blob: e6f9535d65bbc19e45dc88ad4874c9a7cf70d432 [file] [log] [blame]
Linus Nilssona85df7f2020-02-20 16:32:04 -08001/*
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 MediaSampleWriter
18
19// #define LOG_NDEBUG 0
20#define LOG_TAG "MediaSampleWriterTests"
21
22#include <android-base/logging.h>
23#include <fcntl.h>
24#include <gtest/gtest.h>
25#include <media/MediaSampleQueue.h>
26#include <media/MediaSampleWriter.h>
27#include <media/NdkMediaExtractor.h>
28
29#include <condition_variable>
30#include <list>
31#include <mutex>
32
33namespace android {
34
Linus Nilssona85df7f2020-02-20 16:32:04 -080035/** Muxer interface to enable MediaSampleWriter testing. */
36class TestMuxer : public MediaSampleWriterMuxerInterface {
37public:
38 // MuxerInterface
Chong Zhangd6e4aec2020-06-22 14:13:07 -070039 ssize_t addTrack(AMediaFormat* trackFormat) override {
Linus Nilssona85df7f2020-02-20 16:32:04 -080040 mEventQueue.push_back(AddTrack(trackFormat));
41 return mTrackCount++;
42 }
43 media_status_t start() override {
44 mEventQueue.push_back(Start());
45 return AMEDIA_OK;
46 }
47
48 media_status_t writeSampleData(size_t trackIndex, const uint8_t* data,
49 const AMediaCodecBufferInfo* info) override {
50 mEventQueue.push_back(WriteSample(trackIndex, data, info));
51 return AMEDIA_OK;
52 }
53 media_status_t stop() override {
54 mEventQueue.push_back(Stop());
55 return AMEDIA_OK;
56 }
57 // ~MuxerInterface
58
59 struct Event {
60 enum { NoEvent, AddTrack, Start, WriteSample, Stop } type = NoEvent;
61 const AMediaFormat* format = nullptr;
62 size_t trackIndex = 0;
63 const uint8_t* data = nullptr;
64 AMediaCodecBufferInfo info{};
65 };
66
67 static constexpr Event NoEvent = {Event::NoEvent, nullptr, 0, nullptr, {}};
68
69 static Event AddTrack(const AMediaFormat* format) {
70 return {.type = Event::AddTrack, .format = format};
71 }
72
73 static Event Start() { return {.type = Event::Start}; }
74 static Event Stop() { return {.type = Event::Stop}; }
75
76 static Event WriteSample(size_t trackIndex, const uint8_t* data,
77 const AMediaCodecBufferInfo* info) {
78 return {.type = Event::WriteSample, .trackIndex = trackIndex, .data = data, .info = *info};
79 }
80
81 const Event& popEvent() {
82 if (mEventQueue.empty()) {
83 mPoppedEvent = NoEvent;
84 } else {
85 mPoppedEvent = *mEventQueue.begin();
86 mEventQueue.pop_front();
87 }
88 return mPoppedEvent;
89 }
90
91private:
92 Event mPoppedEvent;
93 std::list<Event> mEventQueue;
94 ssize_t mTrackCount = 0;
95};
96
97bool operator==(const AMediaCodecBufferInfo& lhs, const AMediaCodecBufferInfo& rhs) {
98 return lhs.offset == rhs.offset && lhs.size == rhs.size &&
99 lhs.presentationTimeUs == rhs.presentationTimeUs && lhs.flags == rhs.flags;
100}
101
102bool operator==(const TestMuxer::Event& lhs, const TestMuxer::Event& rhs) {
103 return lhs.type == rhs.type && lhs.format == rhs.format && lhs.trackIndex == rhs.trackIndex &&
104 lhs.data == rhs.data && lhs.info == rhs.info;
105}
106
107/** Represents a media source file. */
108class TestMediaSource {
109public:
110 void init() {
111 static const char* sourcePath =
hkuang2ef2b432020-06-15 18:33:11 -0700112 "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
Linus Nilssona85df7f2020-02-20 16:32:04 -0800113
114 mExtractor = AMediaExtractor_new();
115 ASSERT_NE(mExtractor, nullptr);
116
117 int sourceFd = open(sourcePath, O_RDONLY);
118 ASSERT_GT(sourceFd, 0);
119
120 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
121 lseek(sourceFd, 0, SEEK_SET);
122
123 media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, sourceFd, 0, fileSize);
124 ASSERT_EQ(status, AMEDIA_OK);
125 close(sourceFd);
126
127 mTrackCount = AMediaExtractor_getTrackCount(mExtractor);
128 ASSERT_GT(mTrackCount, 1);
129 for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
130 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
131 ASSERT_NE(trackFormat, nullptr);
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700132
133 const char* mime = nullptr;
134 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
135 if (strncmp(mime, "video/", 6) == 0) {
136 mVideoTrackIndex = trackIndex;
137 } else if (strncmp(mime, "audio/", 6) == 0) {
138 mAudioTrackIndex = trackIndex;
139 }
140
Linus Nilssona85df7f2020-02-20 16:32:04 -0800141 mTrackFormats.push_back(
142 std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete));
143
144 AMediaExtractor_selectTrack(mExtractor, trackIndex);
145 }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700146 EXPECT_GE(mVideoTrackIndex, 0);
147 EXPECT_GE(mAudioTrackIndex, 0);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800148 }
149
150 void reset() const {
151 media_status_t status = AMediaExtractor_seekTo(mExtractor, 0 /* seekPosUs */,
152 AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
153 ASSERT_EQ(status, AMEDIA_OK);
154 }
155
156 AMediaExtractor* mExtractor = nullptr;
157 size_t mTrackCount = 0;
158 std::vector<std::shared_ptr<AMediaFormat>> mTrackFormats;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700159 int mVideoTrackIndex = -1;
160 int mAudioTrackIndex = -1;
161};
162
163class TestCallbacks : public MediaSampleWriter::CallbackInterface {
164public:
165 TestCallbacks(bool expectSuccess = true) : mExpectSuccess(expectSuccess) {}
166
167 bool hasFinished() {
168 std::unique_lock<std::mutex> lock(mMutex);
169 return mFinished;
170 }
171
172 // MediaSampleWriter::CallbackInterface
173 virtual void onFinished(const MediaSampleWriter* writer __unused,
174 media_status_t status) override {
175 std::unique_lock<std::mutex> lock(mMutex);
176 EXPECT_FALSE(mFinished);
177 if (mExpectSuccess) {
178 EXPECT_EQ(status, AMEDIA_OK);
179 } else {
180 EXPECT_NE(status, AMEDIA_OK);
181 }
182 mFinished = true;
183 mCondition.notify_all();
184 }
185
186 virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
187 int32_t progress) override {
188 EXPECT_GT(progress, mLastProgress);
189 EXPECT_GE(progress, 0);
190 EXPECT_LE(progress, 100);
191
192 mLastProgress = progress;
193 mProgressUpdateCount++;
194 }
195 // ~MediaSampleWriter::CallbackInterface
196
197 void waitForWritingFinished() {
198 std::unique_lock<std::mutex> lock(mMutex);
199 while (!mFinished) {
200 mCondition.wait(lock);
201 }
202 }
203
204 uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
205
206private:
207 std::mutex mMutex;
208 std::condition_variable mCondition;
209 bool mFinished = false;
210 bool mExpectSuccess;
211 int32_t mLastProgress = -1;
212 uint32_t mProgressUpdateCount = 0;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800213};
214
215class MediaSampleWriterTests : public ::testing::Test {
216public:
217 MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
218 ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
219
220 static const TestMediaSource& getMediaSource() {
221 static TestMediaSource sMediaSource;
222 static std::once_flag sOnceToken;
223
224 std::call_once(sOnceToken, [] { sMediaSource.init(); });
225
226 sMediaSource.reset();
227 return sMediaSource;
228 }
229
230 static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
231 size_t offset, const uint8_t* buffer) {
232 auto sample = std::make_shared<MediaSample>();
233 sample->info.presentationTimeUs = ptsUs;
234 sample->info.flags = flags;
235 sample->info.size = size;
236 sample->dataOffset = offset;
237 sample->buffer = buffer;
238 return sample;
239 }
240
241 static std::shared_ptr<MediaSample> newSampleEos() {
242 return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
243 }
244
245 static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
246 static uint32_t sampleCount = 0;
247
Linus Nilsson443f16c2020-07-30 13:13:21 -0700248 // Use sampleCount to get a unique mock sample.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800249 uint32_t sampleId = ++sampleCount;
250 return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
251 }
252
253 void SetUp() override {
254 LOG(DEBUG) << "MediaSampleWriterTests set up";
255 mTestMuxer = std::make_shared<TestMuxer>();
256 mSampleQueue = std::make_shared<MediaSampleQueue>();
257 }
258
259 void TearDown() override {
260 LOG(DEBUG) << "MediaSampleWriterTests tear down";
261 mTestMuxer.reset();
262 mSampleQueue.reset();
263 }
264
265protected:
266 std::shared_ptr<TestMuxer> mTestMuxer;
267 std::shared_ptr<MediaSampleQueue> mSampleQueue;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700268 std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800269};
270
271TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
272 const TestMediaSource& mediaSource = getMediaSource();
273
274 MediaSampleWriter writer{};
275 EXPECT_FALSE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
276}
277
278TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
279 MediaSampleWriter writer{};
280 EXPECT_FALSE(writer.start());
281}
282
283TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
284 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700285 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800286 EXPECT_FALSE(writer.start());
287 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
288}
289
290TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
291 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700292 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800293
294 EXPECT_FALSE(writer.addTrack(mSampleQueue, nullptr));
295 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
296
297 const TestMediaSource& mediaSource = getMediaSource();
298 EXPECT_FALSE(writer.addTrack(nullptr, mediaSource.mTrackFormats[0]));
299 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
300}
301
302TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
303 MediaSampleWriter writer{};
304
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700305 std::shared_ptr<TestCallbacks> callbacks =
306 std::make_shared<TestCallbacks>(false /* expectSuccess */);
307 EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800308
309 const TestMediaSource& mediaSource = getMediaSource();
310 EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
311 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
312
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700313 ASSERT_TRUE(writer.start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800314 EXPECT_FALSE(writer.start());
315
316 EXPECT_TRUE(writer.stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700317 EXPECT_TRUE(callbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800318 EXPECT_FALSE(writer.stop());
319}
320
321TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
322 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700323 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800324
325 const TestMediaSource& mediaSource = getMediaSource();
326 EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
327 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
328
329 EXPECT_FALSE(writer.stop());
330 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
331}
332
333TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
334 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700335
336 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
337 EXPECT_FALSE(writer.init(mTestMuxer, unassignedWp));
338
339 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
340 EXPECT_FALSE(writer.init(mTestMuxer, unassignedSp));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800341
342 const TestMediaSource& mediaSource = getMediaSource();
343 EXPECT_FALSE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
344 ASSERT_FALSE(writer.start());
345}
346
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700347TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
348 static constexpr uint32_t kSegmentLengthUs = 1;
349 const TestMediaSource& mediaSource = getMediaSource();
350
351 MediaSampleWriter writer{kSegmentLengthUs};
352 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
353
354 std::shared_ptr<AMediaFormat> videoFormat =
355 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
356 AMediaFormat_copy(videoFormat.get(),
357 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
358
359 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
360 EXPECT_TRUE(writer.addTrack(mSampleQueue, videoFormat));
361 ASSERT_TRUE(writer.start());
362
363 for (int64_t pts = 0; pts < 100; ++pts) {
364 mSampleQueue->enqueue(newSampleWithPts(pts));
365 }
366 mSampleQueue->enqueue(newSampleEos());
367 mTestCallbacks->waitForWritingFinished();
368
369 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
370}
371
Linus Nilssona85df7f2020-02-20 16:32:04 -0800372TEST_F(MediaSampleWriterTests, TestInterleaving) {
373 static constexpr uint32_t kSegmentLength = MediaSampleWriter::kDefaultTrackSegmentLengthUs;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800374
375 MediaSampleWriter writer{kSegmentLength};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700376 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800377
378 // Use two tracks for this test.
379 static constexpr int kNumTracks = 2;
380 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
381 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> interleavedSamples;
382 const TestMediaSource& mediaSource = getMediaSource();
383
384 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
385 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
386
387 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
388 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
389 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
390 }
391
392 // Create samples in the expected interleaved order for easy verification.
393 auto addSampleToTrackWithPts = [&interleavedSamples, &sampleQueues](int trackIndex,
394 int64_t pts) {
395 auto sample = newSampleWithPts(pts);
396 sampleQueues[trackIndex]->enqueue(sample);
397 interleavedSamples.emplace_back(sample, trackIndex);
398 };
399
400 addSampleToTrackWithPts(0, 0);
401 addSampleToTrackWithPts(0, kSegmentLength / 2);
402 addSampleToTrackWithPts(0, kSegmentLength); // Track 0 reached 1st segment end
403
404 addSampleToTrackWithPts(1, 0);
405 addSampleToTrackWithPts(1, kSegmentLength); // Track 1 reached 1st segment end
406
407 addSampleToTrackWithPts(0, kSegmentLength * 2); // Track 0 reached 2nd segment end
408
409 addSampleToTrackWithPts(1, kSegmentLength + 1);
410 addSampleToTrackWithPts(1, kSegmentLength * 2); // Track 1 reached 2nd segment end
411
412 addSampleToTrackWithPts(0, kSegmentLength * 2 + 1);
413
414 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
415 sampleQueues[trackIndex]->enqueue(newSampleEos());
416 }
417
418 // Start the writer.
419 ASSERT_TRUE(writer.start());
420
421 // Wait for writer to complete.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700422 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800423 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
424
425 // Verify sample order.
426 for (auto entry : interleavedSamples) {
427 auto sample = entry.first;
428 auto trackIndex = entry.second;
429
430 const TestMuxer::Event& event = mTestMuxer->popEvent();
431 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
432 EXPECT_EQ(event.trackIndex, trackIndex);
433 EXPECT_EQ(event.data, sample->buffer);
434 EXPECT_EQ(event.info.offset, sample->dataOffset);
435 EXPECT_EQ(event.info.size, sample->info.size);
436 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
437 EXPECT_EQ(event.info.flags, sample->info.flags);
438 }
439
Linus Nilsson42a971b2020-07-01 16:41:11 -0700440 // Verify EOS samples.
441 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
442 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
443 int64_t duration = 0;
444 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
445
446 const AMediaCodecBufferInfo info = {0, 0, duration, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
447 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
448 }
449
Linus Nilssona85df7f2020-02-20 16:32:04 -0800450 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
451 EXPECT_TRUE(writer.stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700452 EXPECT_TRUE(mTestCallbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800453}
454
455TEST_F(MediaSampleWriterTests, TestAbortInputQueue) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800456 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700457 std::shared_ptr<TestCallbacks> callbacks =
458 std::make_shared<TestCallbacks>(false /* expectSuccess */);
459 EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800460
461 // Use two tracks for this test.
462 static constexpr int kNumTracks = 2;
463 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
464 const TestMediaSource& mediaSource = getMediaSource();
465
466 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
467 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
468
469 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
470 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
471 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
472 }
473
474 // Start the writer.
475 ASSERT_TRUE(writer.start());
476
477 // Abort the input queues and wait for the writer to complete.
478 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
479 sampleQueues[trackIdx]->abort();
480 }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700481
482 callbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800483
484 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
485 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
486 EXPECT_TRUE(writer.stop());
487}
488
489// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
490static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
491 size_t* trackIndexOut) {
492 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
493 if (trackIndex < 0) {
494 return nullptr;
495 }
496
497 if (trackIndexOut != nullptr) {
498 *trackIndexOut = trackIndex;
499 }
500
501 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
502 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
503 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
504
505 size_t bufferSize = static_cast<size_t>(sampleSize);
506 uint8_t* buffer = new uint8_t[bufferSize];
507
508 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
509 EXPECT_EQ(dataRead, sampleSize);
510
511 auto sample = MediaSample::createWithReleaseCallback(
512 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
513 sample->info.size = bufferSize;
514 sample->info.presentationTimeUs = sampleTimeUs;
515 sample->info.flags = flags;
516
517 (void)AMediaExtractor_advance(extractor);
518 return sample;
519}
520
521TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
522 // Write samples straight from an extractor and validate output file.
523 static const char* destinationPath =
524 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
525 const int destinationFd =
526 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
527 ASSERT_GT(destinationFd, 0);
528
529 // Initialize writer.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800530 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700531 EXPECT_TRUE(writer.init(destinationFd, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800532 close(destinationFd);
533
534 // Add tracks.
535 const TestMediaSource& mediaSource = getMediaSource();
536 std::vector<std::shared_ptr<MediaSampleQueue>> inputQueues;
537
538 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
539 inputQueues.push_back(std::make_shared<MediaSampleQueue>());
540 EXPECT_TRUE(
541 writer.addTrack(inputQueues[trackIndex], mediaSource.mTrackFormats[trackIndex]));
542 }
543
544 // Start the writer.
545 ASSERT_TRUE(writer.start());
546
547 // Enqueue samples and finally End Of Stream.
548 std::shared_ptr<MediaSample> sample;
549 size_t trackIndex;
550 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
551 inputQueues[trackIndex]->enqueue(sample);
552 }
553 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
554 inputQueues[trackIndex]->enqueue(newSampleEos());
555 }
556
557 // Wait for writer.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700558 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800559 EXPECT_TRUE(writer.stop());
560
561 // Compare output file with source.
562 mediaSource.reset();
563
564 AMediaExtractor* extractor = AMediaExtractor_new();
565 ASSERT_NE(extractor, nullptr);
566
567 int sourceFd = open(destinationPath, O_RDONLY);
568 ASSERT_GT(sourceFd, 0);
569
570 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
571 lseek(sourceFd, 0, SEEK_SET);
572
573 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
574 ASSERT_EQ(status, AMEDIA_OK);
575 close(sourceFd);
576
577 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
578 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
579
580 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
581 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
582 ASSERT_NE(trackFormat, nullptr);
583
584 AMediaExtractor_selectTrack(extractor, trackIndex);
585 }
586
587 // Compare samples.
588 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
589 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
590
591 while (sample1 != nullptr && sample2 != nullptr) {
592 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
593 EXPECT_EQ(sample1->info.size, sample2->info.size);
594 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
595
596 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
597
598 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
599 sample2 = readSampleAndAdvance(extractor, nullptr);
600 }
601 EXPECT_EQ(sample1, nullptr);
602 EXPECT_EQ(sample2, nullptr);
603
604 AMediaExtractor_delete(extractor);
605}
606
607} // namespace android
608
609int main(int argc, char** argv) {
610 ::testing::InitGoogleTest(&argc, argv);
611 return RUN_ALL_TESTS();
612}