Linus Nilsson | c6221db | 2020-03-18 14:46:22 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | // Unit Test for PassthroughTrackTranscoder |
| 18 | |
| 19 | // #define LOG_NDEBUG 0 |
| 20 | #define LOG_TAG "PassthroughTrackTranscoderTests" |
| 21 | |
| 22 | #include <android-base/logging.h> |
| 23 | #include <fcntl.h> |
| 24 | #include <gtest/gtest.h> |
| 25 | #include <media/MediaSampleReaderNDK.h> |
| 26 | #include <media/NdkMediaExtractor.h> |
| 27 | #include <media/PassthroughTrackTranscoder.h> |
| 28 | #include <openssl/md5.h> |
| 29 | |
| 30 | #include <vector> |
| 31 | |
| 32 | #include "TrackTranscoderTestUtils.h" |
| 33 | |
| 34 | namespace android { |
| 35 | |
| 36 | class PassthroughTrackTranscoderTests : public ::testing::Test { |
| 37 | public: |
| 38 | PassthroughTrackTranscoderTests() { LOG(DEBUG) << "PassthroughTrackTranscoderTests created"; } |
| 39 | |
| 40 | void SetUp() override { LOG(DEBUG) << "PassthroughTrackTranscoderTests set up"; } |
| 41 | |
| 42 | void initSourceAndExtractor() { |
| 43 | const char* sourcePath = |
hkuang | 2ef2b43 | 2020-06-15 18:33:11 -0700 | [diff] [blame] | 44 | "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4"; |
Linus Nilsson | c6221db | 2020-03-18 14:46:22 -0700 | [diff] [blame] | 45 | |
| 46 | mExtractor = AMediaExtractor_new(); |
| 47 | ASSERT_NE(mExtractor, nullptr); |
| 48 | |
| 49 | mSourceFd = open(sourcePath, O_RDONLY); |
| 50 | ASSERT_GT(mSourceFd, 0); |
| 51 | |
| 52 | mSourceFileSize = lseek(mSourceFd, 0, SEEK_END); |
| 53 | lseek(mSourceFd, 0, SEEK_SET); |
| 54 | |
| 55 | media_status_t status = |
| 56 | AMediaExtractor_setDataSourceFd(mExtractor, mSourceFd, 0, mSourceFileSize); |
| 57 | ASSERT_EQ(status, AMEDIA_OK); |
| 58 | |
| 59 | const size_t trackCount = AMediaExtractor_getTrackCount(mExtractor); |
| 60 | for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) { |
| 61 | AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex); |
| 62 | ASSERT_NE(trackFormat, nullptr); |
| 63 | |
| 64 | const char* mime = nullptr; |
| 65 | AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime); |
| 66 | ASSERT_NE(mime, nullptr); |
| 67 | |
| 68 | if (strncmp(mime, "audio/", 6) == 0) { |
| 69 | mTrackIndex = trackIndex; |
| 70 | AMediaExtractor_selectTrack(mExtractor, trackIndex); |
| 71 | break; |
| 72 | } |
| 73 | |
| 74 | AMediaFormat_delete(trackFormat); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | void TearDown() override { |
| 79 | LOG(DEBUG) << "PassthroughTrackTranscoderTests tear down"; |
| 80 | if (mExtractor != nullptr) { |
| 81 | AMediaExtractor_delete(mExtractor); |
| 82 | mExtractor = nullptr; |
| 83 | } |
| 84 | if (mSourceFd > 0) { |
| 85 | close(mSourceFd); |
| 86 | mSourceFd = -1; |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | ~PassthroughTrackTranscoderTests() { |
| 91 | LOG(DEBUG) << "PassthroughTrackTranscoderTests destroyed"; |
| 92 | } |
| 93 | |
| 94 | int mSourceFd = -1; |
| 95 | size_t mSourceFileSize; |
| 96 | int mTrackIndex; |
| 97 | AMediaExtractor* mExtractor = nullptr; |
| 98 | }; |
| 99 | |
| 100 | /** Helper class for comparing sample data using checksums. */ |
| 101 | class SampleID { |
| 102 | public: |
| 103 | SampleID(const uint8_t* sampleData, ssize_t sampleSize) : mSize{sampleSize} { |
| 104 | MD5_CTX md5Ctx; |
| 105 | MD5_Init(&md5Ctx); |
| 106 | MD5_Update(&md5Ctx, sampleData, sampleSize); |
| 107 | MD5_Final(mChecksum, &md5Ctx); |
| 108 | } |
| 109 | |
| 110 | bool operator==(const SampleID& rhs) const { |
| 111 | return mSize == rhs.mSize && memcmp(mChecksum, rhs.mChecksum, MD5_DIGEST_LENGTH) == 0; |
| 112 | } |
| 113 | |
| 114 | uint8_t mChecksum[MD5_DIGEST_LENGTH]; |
| 115 | ssize_t mSize; |
| 116 | }; |
| 117 | |
| 118 | /** |
| 119 | * Tests that the output samples of PassthroughTrackTranscoder are identical to the source samples |
| 120 | * and in correct order. |
| 121 | */ |
| 122 | TEST_F(PassthroughTrackTranscoderTests, SampleEquality) { |
| 123 | LOG(DEBUG) << "Testing SampleEquality"; |
| 124 | |
| 125 | ssize_t bufferSize = 1024; |
| 126 | auto buffer = std::make_unique<uint8_t[]>(bufferSize); |
| 127 | |
| 128 | initSourceAndExtractor(); |
| 129 | |
| 130 | // Loop through all samples of a track and store size and checksums. |
| 131 | std::vector<SampleID> sampleChecksums; |
| 132 | |
| 133 | int64_t sampleTime = AMediaExtractor_getSampleTime(mExtractor); |
| 134 | while (sampleTime != -1) { |
| 135 | if (AMediaExtractor_getSampleTrackIndex(mExtractor) == mTrackIndex) { |
| 136 | ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor); |
| 137 | if (bufferSize < sampleSize) { |
| 138 | bufferSize = sampleSize; |
| 139 | buffer = std::make_unique<uint8_t[]>(bufferSize); |
| 140 | } |
| 141 | |
| 142 | ssize_t bytesRead = |
| 143 | AMediaExtractor_readSampleData(mExtractor, buffer.get(), bufferSize); |
| 144 | ASSERT_EQ(bytesRead, sampleSize); |
| 145 | |
| 146 | SampleID sampleId{buffer.get(), sampleSize}; |
| 147 | sampleChecksums.push_back(sampleId); |
| 148 | } |
| 149 | |
| 150 | AMediaExtractor_advance(mExtractor); |
| 151 | sampleTime = AMediaExtractor_getSampleTime(mExtractor); |
| 152 | } |
| 153 | |
| 154 | // Create and start the transcoder. |
| 155 | std::shared_ptr<TestCallback> callback = std::make_shared<TestCallback>(); |
| 156 | PassthroughTrackTranscoder transcoder{callback}; |
| 157 | |
| 158 | std::shared_ptr<MediaSampleReader> mediaSampleReader = |
| 159 | MediaSampleReaderNDK::createFromFd(mSourceFd, 0, mSourceFileSize); |
| 160 | EXPECT_NE(mediaSampleReader, nullptr); |
| 161 | |
Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 162 | EXPECT_EQ(mediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK); |
Linus Nilsson | c6221db | 2020-03-18 14:46:22 -0700 | [diff] [blame] | 163 | EXPECT_EQ(transcoder.configure(mediaSampleReader, mTrackIndex, nullptr /* destinationFormat */), |
| 164 | AMEDIA_OK); |
| 165 | ASSERT_TRUE(transcoder.start()); |
| 166 | |
| 167 | // Pull transcoder's output samples and compare against input checksums. |
| 168 | uint64_t sampleCount = 0; |
| 169 | std::shared_ptr<MediaSample> sample; |
Linus Nilsson | cab39d8 | 2020-05-14 16:32:21 -0700 | [diff] [blame] | 170 | std::shared_ptr<MediaSampleQueue> outputQueue = transcoder.getOutputQueue(); |
| 171 | while (!outputQueue->dequeue(&sample)) { |
Linus Nilsson | c6221db | 2020-03-18 14:46:22 -0700 | [diff] [blame] | 172 | ASSERT_NE(sample, nullptr); |
| 173 | |
| 174 | if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) { |
| 175 | break; |
| 176 | } |
| 177 | |
| 178 | SampleID sampleId{sample->buffer, static_cast<ssize_t>(sample->info.size)}; |
| 179 | EXPECT_TRUE(sampleId == sampleChecksums[sampleCount]); |
| 180 | ++sampleCount; |
| 181 | } |
| 182 | |
| 183 | EXPECT_EQ(sampleCount, sampleChecksums.size()); |
| 184 | EXPECT_TRUE(transcoder.stop()); |
| 185 | } |
| 186 | |
| 187 | /** Class for testing PassthroughTrackTranscoder's built in buffer pool. */ |
| 188 | class BufferPoolTests : public ::testing::Test { |
| 189 | public: |
| 190 | static constexpr int kMaxBuffers = 5; |
| 191 | |
| 192 | void SetUp() override { |
| 193 | LOG(DEBUG) << "BufferPoolTests set up"; |
| 194 | mBufferPool = std::make_shared<PassthroughTrackTranscoder::BufferPool>(kMaxBuffers); |
| 195 | } |
| 196 | |
| 197 | void TearDown() override { |
| 198 | LOG(DEBUG) << "BufferPoolTests tear down"; |
| 199 | mBufferPool.reset(); |
| 200 | } |
| 201 | |
| 202 | std::shared_ptr<PassthroughTrackTranscoder::BufferPool> mBufferPool; |
| 203 | }; |
| 204 | |
| 205 | TEST_F(BufferPoolTests, BufferReuse) { |
| 206 | LOG(DEBUG) << "Testing BufferReuse"; |
| 207 | |
| 208 | uint8_t* buffer1 = mBufferPool->getBufferWithSize(10); |
| 209 | EXPECT_NE(buffer1, nullptr); |
| 210 | |
| 211 | uint8_t* buffer2 = mBufferPool->getBufferWithSize(10); |
| 212 | EXPECT_NE(buffer2, nullptr); |
| 213 | EXPECT_NE(buffer2, buffer1); |
| 214 | |
| 215 | mBufferPool->returnBuffer(buffer1); |
| 216 | |
| 217 | uint8_t* buffer3 = mBufferPool->getBufferWithSize(10); |
| 218 | EXPECT_NE(buffer3, nullptr); |
| 219 | EXPECT_NE(buffer3, buffer2); |
| 220 | EXPECT_EQ(buffer3, buffer1); |
| 221 | |
| 222 | mBufferPool->returnBuffer(buffer2); |
| 223 | |
| 224 | uint8_t* buffer4 = mBufferPool->getBufferWithSize(10); |
| 225 | EXPECT_NE(buffer4, nullptr); |
| 226 | EXPECT_NE(buffer4, buffer1); |
| 227 | EXPECT_EQ(buffer4, buffer2); |
| 228 | } |
| 229 | |
| 230 | TEST_F(BufferPoolTests, SmallestAvailableBuffer) { |
| 231 | LOG(DEBUG) << "Testing SmallestAvailableBuffer"; |
| 232 | |
| 233 | uint8_t* buffer1 = mBufferPool->getBufferWithSize(10); |
| 234 | EXPECT_NE(buffer1, nullptr); |
| 235 | |
| 236 | uint8_t* buffer2 = mBufferPool->getBufferWithSize(15); |
| 237 | EXPECT_NE(buffer2, nullptr); |
| 238 | EXPECT_NE(buffer2, buffer1); |
| 239 | |
| 240 | uint8_t* buffer3 = mBufferPool->getBufferWithSize(20); |
| 241 | EXPECT_NE(buffer3, nullptr); |
| 242 | EXPECT_NE(buffer3, buffer1); |
| 243 | EXPECT_NE(buffer3, buffer2); |
| 244 | |
| 245 | mBufferPool->returnBuffer(buffer1); |
| 246 | mBufferPool->returnBuffer(buffer2); |
| 247 | mBufferPool->returnBuffer(buffer3); |
| 248 | |
| 249 | uint8_t* buffer4 = mBufferPool->getBufferWithSize(11); |
| 250 | EXPECT_NE(buffer4, nullptr); |
| 251 | EXPECT_EQ(buffer4, buffer2); |
| 252 | |
| 253 | uint8_t* buffer5 = mBufferPool->getBufferWithSize(11); |
| 254 | EXPECT_NE(buffer5, nullptr); |
| 255 | EXPECT_EQ(buffer5, buffer3); |
| 256 | } |
| 257 | |
| 258 | TEST_F(BufferPoolTests, AddAfterAbort) { |
| 259 | LOG(DEBUG) << "Testing AddAfterAbort"; |
| 260 | |
| 261 | uint8_t* buffer1 = mBufferPool->getBufferWithSize(10); |
| 262 | EXPECT_NE(buffer1, nullptr); |
| 263 | mBufferPool->returnBuffer(buffer1); |
| 264 | |
| 265 | mBufferPool->abort(); |
| 266 | uint8_t* buffer2 = mBufferPool->getBufferWithSize(10); |
| 267 | EXPECT_EQ(buffer2, nullptr); |
| 268 | } |
| 269 | |
| 270 | TEST_F(BufferPoolTests, MaximumBuffers) { |
| 271 | LOG(DEBUG) << "Testing MaximumBuffers"; |
| 272 | |
| 273 | static constexpr size_t kBufferBaseSize = 10; |
| 274 | std::unordered_map<uint8_t*, size_t> addressSizeMap; |
| 275 | |
| 276 | // Get kMaxBuffers * 2 new buffers with increasing size. |
| 277 | // (Note: Once kMaxBuffers have been allocated, the pool will delete old buffers to accommodate |
| 278 | // new ones making the deleted buffers free to be reused by the system's heap memory allocator. |
| 279 | // So we cannot test that each new pointer is unique here.) |
| 280 | for (int i = 0; i < kMaxBuffers * 2; i++) { |
| 281 | size_t size = kBufferBaseSize + i; |
| 282 | uint8_t* buffer = mBufferPool->getBufferWithSize(size); |
| 283 | EXPECT_NE(buffer, nullptr); |
| 284 | addressSizeMap[buffer] = size; |
| 285 | mBufferPool->returnBuffer(buffer); |
| 286 | } |
| 287 | |
| 288 | // Verify that the pool now contains the kMaxBuffers largest buffers allocated above and that |
| 289 | // the buffer of matching size is returned. |
| 290 | for (int i = kMaxBuffers; i < kMaxBuffers * 2; i++) { |
| 291 | size_t size = kBufferBaseSize + i; |
| 292 | uint8_t* buffer = mBufferPool->getBufferWithSize(size); |
| 293 | EXPECT_NE(buffer, nullptr); |
| 294 | |
| 295 | auto it = addressSizeMap.find(buffer); |
| 296 | ASSERT_NE(it, addressSizeMap.end()); |
| 297 | EXPECT_EQ(it->second, size); |
| 298 | mBufferPool->returnBuffer(buffer); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | } // namespace android |
| 303 | |
| 304 | int main(int argc, char** argv) { |
| 305 | ::testing::InitGoogleTest(&argc, argv); |
| 306 | return RUN_ALL_TESTS(); |
| 307 | } |