blob: a2ffbe4ce0c57ba7705ed6d255fe3421c9398e14 [file] [log] [blame]
Linus Nilssonc6221db2020-03-18 14:46:22 -07001/*
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
34namespace android {
35
36class PassthroughTrackTranscoderTests : public ::testing::Test {
37public:
38 PassthroughTrackTranscoderTests() { LOG(DEBUG) << "PassthroughTrackTranscoderTests created"; }
39
40 void SetUp() override { LOG(DEBUG) << "PassthroughTrackTranscoderTests set up"; }
41
42 void initSourceAndExtractor() {
43 const char* sourcePath =
hkuang2ef2b432020-06-15 18:33:11 -070044 "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
Linus Nilssonc6221db2020-03-18 14:46:22 -070045
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. */
101class SampleID {
102public:
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 */
122TEST_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 Nilsson6233fed2020-08-13 15:15:14 -0700162 EXPECT_EQ(mediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
Linus Nilssonc6221db2020-03-18 14:46:22 -0700163 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 Nilssoncab39d82020-05-14 16:32:21 -0700170 std::shared_ptr<MediaSampleQueue> outputQueue = transcoder.getOutputQueue();
171 while (!outputQueue->dequeue(&sample)) {
Linus Nilssonc6221db2020-03-18 14:46:22 -0700172 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. */
188class BufferPoolTests : public ::testing::Test {
189public:
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
205TEST_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
230TEST_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
258TEST_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
270TEST_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
304int main(int argc, char** argv) {
305 ::testing::InitGoogleTest(&argc, argv);
306 return RUN_ALL_TESTS();
307}