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 "TranscoderTestUtils.h"
33
34 namespace android {
35
36 class PassthroughTrackTranscoderTests : public ::testing::Test {
37 public:
PassthroughTrackTranscoderTests()38 PassthroughTrackTranscoderTests() { LOG(DEBUG) << "PassthroughTrackTranscoderTests created"; }
39
SetUp()40 void SetUp() override { LOG(DEBUG) << "PassthroughTrackTranscoderTests set up"; }
41
initSourceAndExtractor()42 void initSourceAndExtractor() {
43 const char* sourcePath =
44 "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
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
TearDown()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
~PassthroughTrackTranscoderTests()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:
SampleID(const uint8_t * sampleData,ssize_t sampleSize)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
operator ==(const SampleID & rhs) const110 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 */
TEST_F(PassthroughTrackTranscoderTests,SampleEquality)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 auto callback = std::make_shared<TestTrackTranscoderCallback>();
156 PassthroughTrackTranscoder transcoder{callback};
157
158 std::shared_ptr<MediaSampleReader> mediaSampleReader =
159 MediaSampleReaderNDK::createFromFd(mSourceFd, 0, mSourceFileSize);
160 EXPECT_NE(mediaSampleReader, nullptr);
161
162 EXPECT_EQ(mediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
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 bool eos = false;
169 uint64_t sampleCount = 0;
170 transcoder.setSampleConsumer(
171 [&sampleCount, &sampleChecksums, &eos](const std::shared_ptr<MediaSample>& sample) {
172 ASSERT_NE(sample, nullptr);
173 EXPECT_FALSE(eos);
174
175 if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) {
176 eos = true;
177 } else {
178 SampleID sampleId{sample->buffer, static_cast<ssize_t>(sample->info.size)};
179 EXPECT_TRUE(sampleId == sampleChecksums[sampleCount]);
180 ++sampleCount;
181 }
182 });
183
184 callback->waitUntilFinished();
185 EXPECT_EQ(sampleCount, sampleChecksums.size());
186 }
187
188 /** Class for testing PassthroughTrackTranscoder's built in buffer pool. */
189 class BufferPoolTests : public ::testing::Test {
190 public:
191 static constexpr int kMaxBuffers = 5;
192
SetUp()193 void SetUp() override {
194 LOG(DEBUG) << "BufferPoolTests set up";
195 mBufferPool = std::make_shared<PassthroughTrackTranscoder::BufferPool>(kMaxBuffers);
196 }
197
TearDown()198 void TearDown() override {
199 LOG(DEBUG) << "BufferPoolTests tear down";
200 mBufferPool.reset();
201 }
202
203 std::shared_ptr<PassthroughTrackTranscoder::BufferPool> mBufferPool;
204 };
205
TEST_F(BufferPoolTests,BufferReuse)206 TEST_F(BufferPoolTests, BufferReuse) {
207 LOG(DEBUG) << "Testing BufferReuse";
208
209 uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
210 EXPECT_NE(buffer1, nullptr);
211
212 uint8_t* buffer2 = mBufferPool->getBufferWithSize(10);
213 EXPECT_NE(buffer2, nullptr);
214 EXPECT_NE(buffer2, buffer1);
215
216 mBufferPool->returnBuffer(buffer1);
217
218 uint8_t* buffer3 = mBufferPool->getBufferWithSize(10);
219 EXPECT_NE(buffer3, nullptr);
220 EXPECT_NE(buffer3, buffer2);
221 EXPECT_EQ(buffer3, buffer1);
222
223 mBufferPool->returnBuffer(buffer2);
224
225 uint8_t* buffer4 = mBufferPool->getBufferWithSize(10);
226 EXPECT_NE(buffer4, nullptr);
227 EXPECT_NE(buffer4, buffer1);
228 EXPECT_EQ(buffer4, buffer2);
229 }
230
TEST_F(BufferPoolTests,SmallestAvailableBuffer)231 TEST_F(BufferPoolTests, SmallestAvailableBuffer) {
232 LOG(DEBUG) << "Testing SmallestAvailableBuffer";
233
234 uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
235 EXPECT_NE(buffer1, nullptr);
236
237 uint8_t* buffer2 = mBufferPool->getBufferWithSize(15);
238 EXPECT_NE(buffer2, nullptr);
239 EXPECT_NE(buffer2, buffer1);
240
241 uint8_t* buffer3 = mBufferPool->getBufferWithSize(20);
242 EXPECT_NE(buffer3, nullptr);
243 EXPECT_NE(buffer3, buffer1);
244 EXPECT_NE(buffer3, buffer2);
245
246 mBufferPool->returnBuffer(buffer1);
247 mBufferPool->returnBuffer(buffer2);
248 mBufferPool->returnBuffer(buffer3);
249
250 uint8_t* buffer4 = mBufferPool->getBufferWithSize(11);
251 EXPECT_NE(buffer4, nullptr);
252 EXPECT_EQ(buffer4, buffer2);
253
254 uint8_t* buffer5 = mBufferPool->getBufferWithSize(11);
255 EXPECT_NE(buffer5, nullptr);
256 EXPECT_EQ(buffer5, buffer3);
257 }
258
TEST_F(BufferPoolTests,AddAfterAbort)259 TEST_F(BufferPoolTests, AddAfterAbort) {
260 LOG(DEBUG) << "Testing AddAfterAbort";
261
262 uint8_t* buffer1 = mBufferPool->getBufferWithSize(10);
263 EXPECT_NE(buffer1, nullptr);
264 mBufferPool->returnBuffer(buffer1);
265
266 mBufferPool->abort();
267 uint8_t* buffer2 = mBufferPool->getBufferWithSize(10);
268 EXPECT_EQ(buffer2, nullptr);
269 }
270
TEST_F(BufferPoolTests,MaximumBuffers)271 TEST_F(BufferPoolTests, MaximumBuffers) {
272 LOG(DEBUG) << "Testing MaximumBuffers";
273
274 static constexpr size_t kBufferBaseSize = 10;
275 std::unordered_map<uint8_t*, size_t> addressSizeMap;
276
277 // Get kMaxBuffers * 2 new buffers with increasing size.
278 // (Note: Once kMaxBuffers have been allocated, the pool will delete old buffers to accommodate
279 // new ones making the deleted buffers free to be reused by the system's heap memory allocator.
280 // So we cannot test that each new pointer is unique here.)
281 for (int i = 0; i < kMaxBuffers * 2; i++) {
282 size_t size = kBufferBaseSize + i;
283 uint8_t* buffer = mBufferPool->getBufferWithSize(size);
284 EXPECT_NE(buffer, nullptr);
285 addressSizeMap[buffer] = size;
286 mBufferPool->returnBuffer(buffer);
287 }
288
289 // Verify that the pool now contains the kMaxBuffers largest buffers allocated above and that
290 // the buffer of matching size is returned.
291 for (int i = kMaxBuffers; i < kMaxBuffers * 2; i++) {
292 size_t size = kBufferBaseSize + i;
293 uint8_t* buffer = mBufferPool->getBufferWithSize(size);
294 EXPECT_NE(buffer, nullptr);
295
296 auto it = addressSizeMap.find(buffer);
297 ASSERT_NE(it, addressSizeMap.end());
298 EXPECT_EQ(it->second, size);
299 mBufferPool->returnBuffer(buffer);
300 }
301 }
302
303 } // namespace android
304
main(int argc,char ** argv)305 int main(int argc, char** argv) {
306 ::testing::InitGoogleTest(&argc, argv);
307 return RUN_ALL_TESTS();
308 }
309