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 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
33 namespace android {
34
35 /** Muxer interface to enable MediaSampleWriter testing. */
36 class TestMuxer : public MediaSampleWriterMuxerInterface {
37 public:
38 // MuxerInterface
addTrack(AMediaFormat * trackFormat)39 ssize_t addTrack(AMediaFormat* trackFormat) override {
40 mEventQueue.push_back(AddTrack(trackFormat));
41 return mTrackCount++;
42 }
start()43 media_status_t start() override {
44 mEventQueue.push_back(Start());
45 return AMEDIA_OK;
46 }
47
writeSampleData(size_t trackIndex,const uint8_t * data,const AMediaCodecBufferInfo * info)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 }
stop()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
AddTrack(const AMediaFormat * format)69 static Event AddTrack(const AMediaFormat* format) {
70 return {.type = Event::AddTrack, .format = format};
71 }
72
Start()73 static Event Start() { return {.type = Event::Start}; }
Stop()74 static Event Stop() { return {.type = Event::Stop}; }
75
WriteSample(size_t trackIndex,const uint8_t * data,const AMediaCodecBufferInfo * info)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
WriteSampleWithPts(size_t trackIndex,int64_t pts)81 static Event WriteSampleWithPts(size_t trackIndex, int64_t pts) {
82 return {.type = Event::WriteSample, .trackIndex = trackIndex, .info = {0, 0, pts, 0}};
83 }
84
pushEvent(const Event & e)85 void pushEvent(const Event& e) {
86 std::unique_lock<std::mutex> lock(mMutex);
87 mEventQueue.push_back(e);
88 mCondition.notify_one();
89 }
90
popEvent(bool wait=false)91 const Event& popEvent(bool wait = false) {
92 std::unique_lock<std::mutex> lock(mMutex);
93 while (wait && mEventQueue.empty()) {
94 mCondition.wait_for(lock, std::chrono::milliseconds(200));
95 }
96
97 if (mEventQueue.empty()) {
98 mPoppedEvent = NoEvent;
99 } else {
100 mPoppedEvent = *mEventQueue.begin();
101 mEventQueue.pop_front();
102 }
103 return mPoppedEvent;
104 }
105
106 private:
107 Event mPoppedEvent;
108 std::list<Event> mEventQueue;
109 ssize_t mTrackCount = 0;
110 std::mutex mMutex;
111 std::condition_variable mCondition;
112 };
113
operator ==(const AMediaCodecBufferInfo & lhs,const AMediaCodecBufferInfo & rhs)114 bool operator==(const AMediaCodecBufferInfo& lhs, const AMediaCodecBufferInfo& rhs) {
115 return lhs.offset == rhs.offset && lhs.size == rhs.size &&
116 lhs.presentationTimeUs == rhs.presentationTimeUs && lhs.flags == rhs.flags;
117 }
118
operator ==(const TestMuxer::Event & lhs,const TestMuxer::Event & rhs)119 bool operator==(const TestMuxer::Event& lhs, const TestMuxer::Event& rhs) {
120 // Don't test format pointer equality since the writer can make a copy.
121 return lhs.type == rhs.type /*&& lhs.format == rhs.format*/ &&
122 lhs.trackIndex == rhs.trackIndex && lhs.data == rhs.data && lhs.info == rhs.info;
123 }
124
125 /** Represents a media source file. */
126 class TestMediaSource {
127 public:
init()128 void init() {
129 static const char* sourcePath =
130 "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
131
132 mExtractor = AMediaExtractor_new();
133 ASSERT_NE(mExtractor, nullptr);
134
135 int sourceFd = open(sourcePath, O_RDONLY);
136 ASSERT_GT(sourceFd, 0);
137
138 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
139 lseek(sourceFd, 0, SEEK_SET);
140
141 media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, sourceFd, 0, fileSize);
142 ASSERT_EQ(status, AMEDIA_OK);
143 close(sourceFd);
144
145 mTrackCount = AMediaExtractor_getTrackCount(mExtractor);
146 ASSERT_GT(mTrackCount, 1);
147 for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
148 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
149 ASSERT_NE(trackFormat, nullptr);
150
151 const char* mime = nullptr;
152 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
153 if (strncmp(mime, "video/", 6) == 0) {
154 mVideoTrackIndex = trackIndex;
155 } else if (strncmp(mime, "audio/", 6) == 0) {
156 mAudioTrackIndex = trackIndex;
157 }
158
159 mTrackFormats.push_back(
160 std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete));
161
162 AMediaExtractor_selectTrack(mExtractor, trackIndex);
163 }
164 EXPECT_GE(mVideoTrackIndex, 0);
165 EXPECT_GE(mAudioTrackIndex, 0);
166 }
167
reset() const168 void reset() const {
169 media_status_t status = AMediaExtractor_seekTo(mExtractor, 0 /* seekPosUs */,
170 AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
171 ASSERT_EQ(status, AMEDIA_OK);
172 }
173
174 AMediaExtractor* mExtractor = nullptr;
175 size_t mTrackCount = 0;
176 std::vector<std::shared_ptr<AMediaFormat>> mTrackFormats;
177 int mVideoTrackIndex = -1;
178 int mAudioTrackIndex = -1;
179 };
180
181 class TestCallbacks : public MediaSampleWriter::CallbackInterface {
182 public:
hasFinished()183 bool hasFinished() {
184 std::unique_lock<std::mutex> lock(mMutex);
185 return mFinished;
186 }
187
188 // MediaSampleWriter::CallbackInterface
onFinished(const MediaSampleWriter * writer __unused,media_status_t status)189 virtual void onFinished(const MediaSampleWriter* writer __unused,
190 media_status_t status) override {
191 std::unique_lock<std::mutex> lock(mMutex);
192 EXPECT_FALSE(mFinished);
193 mFinished = true;
194 mStatus = status;
195 mCondition.notify_all();
196 }
197
onStopped(const MediaSampleWriter * writer __unused)198 virtual void onStopped(const MediaSampleWriter* writer __unused) {
199 std::unique_lock<std::mutex> lock(mMutex);
200 EXPECT_FALSE(mFinished);
201 mStopped = true;
202 mCondition.notify_all();
203 }
204
onProgressUpdate(const MediaSampleWriter * writer __unused,int32_t progress)205 virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
206 int32_t progress) override {
207 EXPECT_GT(progress, mLastProgress);
208 EXPECT_GE(progress, 0);
209 EXPECT_LE(progress, 100);
210
211 mLastProgress = progress;
212 mProgressUpdateCount++;
213 }
214
onHeartBeat(const MediaSampleWriter * writer __unused)215 virtual void onHeartBeat(const MediaSampleWriter* writer __unused) override {}
216 // ~MediaSampleWriter::CallbackInterface
217
waitForWritingFinished()218 void waitForWritingFinished() {
219 std::unique_lock<std::mutex> lock(mMutex);
220 while (!mFinished && !mStopped) {
221 mCondition.wait(lock);
222 }
223 }
224
getProgressUpdateCount() const225 uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
wasStopped() const226 bool wasStopped() const { return mStopped; }
227
228 private:
229 std::mutex mMutex;
230 std::condition_variable mCondition;
231 bool mFinished = false;
232 bool mStopped = false;
233 media_status_t mStatus = AMEDIA_OK;
234 int32_t mLastProgress = -1;
235 uint32_t mProgressUpdateCount = 0;
236 };
237
238 class MediaSampleWriterTests : public ::testing::Test {
239 public:
MediaSampleWriterTests()240 MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
~MediaSampleWriterTests()241 ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
242
getMediaSource()243 static const TestMediaSource& getMediaSource() {
244 static TestMediaSource sMediaSource;
245 static std::once_flag sOnceToken;
246
247 std::call_once(sOnceToken, [] { sMediaSource.init(); });
248
249 sMediaSource.reset();
250 return sMediaSource;
251 }
252
newSample(int64_t ptsUs,uint32_t flags,size_t size,size_t offset,const uint8_t * buffer)253 static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
254 size_t offset, const uint8_t* buffer) {
255 auto sample = std::make_shared<MediaSample>();
256 sample->info.presentationTimeUs = ptsUs;
257 sample->info.flags = flags;
258 sample->info.size = size;
259 sample->dataOffset = offset;
260 sample->buffer = buffer;
261 return sample;
262 }
263
newSampleEos()264 static std::shared_ptr<MediaSample> newSampleEos() {
265 return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
266 }
267
newSampleWithPts(int64_t ptsUs)268 static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
269 static uint32_t sampleCount = 0;
270
271 // Use sampleCount to get a unique mock sample.
272 uint32_t sampleId = ++sampleCount;
273 return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
274 }
275
newSampleWithPtsOnly(int64_t ptsUs)276 static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
277 return newSample(ptsUs, 0, 0, 0, nullptr);
278 }
279
SetUp()280 void SetUp() override {
281 LOG(DEBUG) << "MediaSampleWriterTests set up";
282 mTestMuxer = std::make_shared<TestMuxer>();
283 }
284
TearDown()285 void TearDown() override {
286 LOG(DEBUG) << "MediaSampleWriterTests tear down";
287 mTestMuxer.reset();
288 }
289
290 protected:
291 std::shared_ptr<TestMuxer> mTestMuxer;
292 std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
293 };
294
TEST_F(MediaSampleWriterTests,TestAddTrackWithoutInit)295 TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
296 const TestMediaSource& mediaSource = getMediaSource();
297
298 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
299 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
300 }
301
TEST_F(MediaSampleWriterTests,TestStartWithoutInit)302 TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
303 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
304 EXPECT_FALSE(writer->start());
305 }
306
TEST_F(MediaSampleWriterTests,TestStartWithoutTracks)307 TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
308 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
309 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
310 EXPECT_FALSE(writer->start());
311 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
312 }
313
TEST_F(MediaSampleWriterTests,TestAddInvalidTrack)314 TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
315 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
316 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
317
318 EXPECT_EQ(writer->addTrack(nullptr), nullptr);
319 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
320 }
321
TEST_F(MediaSampleWriterTests,TestDoubleStartStop)322 TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
323 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
324
325 std::shared_ptr<TestCallbacks> callbacks = std::make_shared<TestCallbacks>();
326 EXPECT_TRUE(writer->init(mTestMuxer, callbacks));
327
328 const TestMediaSource& mediaSource = getMediaSource();
329 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
330 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
331
332 ASSERT_TRUE(writer->start());
333 EXPECT_FALSE(writer->start());
334
335 writer->stop();
336 writer->stop();
337 callbacks->waitForWritingFinished();
338 EXPECT_TRUE(callbacks->wasStopped());
339 }
340
TEST_F(MediaSampleWriterTests,TestStopWithoutStart)341 TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
342 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
343 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
344
345 const TestMediaSource& mediaSource = getMediaSource();
346 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
347 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
348
349 writer->stop();
350 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
351 }
352
TEST_F(MediaSampleWriterTests,TestStartWithoutCallback)353 TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
354 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
355
356 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
357 EXPECT_FALSE(writer->init(mTestMuxer, unassignedWp));
358
359 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
360 EXPECT_FALSE(writer->init(mTestMuxer, unassignedSp));
361
362 const TestMediaSource& mediaSource = getMediaSource();
363 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
364 ASSERT_FALSE(writer->start());
365 }
366
TEST_F(MediaSampleWriterTests,TestProgressUpdate)367 TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
368 const TestMediaSource& mediaSource = getMediaSource();
369
370 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
371 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
372
373 std::shared_ptr<AMediaFormat> videoFormat =
374 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
375 AMediaFormat_copy(videoFormat.get(),
376 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
377
378 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
379 auto sampleConsumer = writer->addTrack(videoFormat);
380 EXPECT_NE(sampleConsumer, nullptr);
381 ASSERT_TRUE(writer->start());
382
383 for (int64_t pts = 0; pts < 100; ++pts) {
384 sampleConsumer(newSampleWithPts(pts));
385 }
386 sampleConsumer(newSampleEos());
387 mTestCallbacks->waitForWritingFinished();
388
389 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
390 }
391
TEST_F(MediaSampleWriterTests,TestInterleaving)392 TEST_F(MediaSampleWriterTests, TestInterleaving) {
393 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
394 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
395
396 // Use two tracks for this test.
397 static constexpr int kNumTracks = 2;
398 MediaSampleWriter::MediaSampleConsumerFunction sampleConsumers[kNumTracks];
399 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> addedSamples;
400 const TestMediaSource& mediaSource = getMediaSource();
401
402 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
403 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
404 sampleConsumers[trackIdx] = writer->addTrack(trackFormat);
405 EXPECT_NE(sampleConsumers[trackIdx], nullptr);
406 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
407 }
408
409 // Create samples in the expected interleaved order for easy verification.
410 auto addSampleToTrackWithPts = [&addedSamples, &sampleConsumers](int trackIndex, int64_t pts) {
411 auto sample = newSampleWithPts(pts);
412 sampleConsumers[trackIndex](sample);
413 addedSamples.emplace_back(sample, trackIndex);
414 };
415
416 addSampleToTrackWithPts(0, 0);
417 addSampleToTrackWithPts(1, 4);
418
419 addSampleToTrackWithPts(0, 1);
420 addSampleToTrackWithPts(0, 2);
421 addSampleToTrackWithPts(0, 3);
422 addSampleToTrackWithPts(0, 10);
423
424 addSampleToTrackWithPts(1, 5);
425 addSampleToTrackWithPts(1, 6);
426 addSampleToTrackWithPts(1, 11);
427
428 addSampleToTrackWithPts(0, 12);
429 addSampleToTrackWithPts(1, 13);
430
431 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
432 sampleConsumers[trackIndex](newSampleEos());
433 }
434
435 // Start the writer.
436 ASSERT_TRUE(writer->start());
437
438 // Wait for writer to complete.
439 mTestCallbacks->waitForWritingFinished();
440 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
441
442 std::sort(addedSamples.begin(), addedSamples.end(),
443 [](const std::pair<std::shared_ptr<MediaSample>, size_t>& left,
444 const std::pair<std::shared_ptr<MediaSample>, size_t>& right) {
445 return left.first->info.presentationTimeUs < right.first->info.presentationTimeUs;
446 });
447
448 // Verify sample order.
449 for (auto entry : addedSamples) {
450 auto sample = entry.first;
451 auto trackIndex = entry.second;
452
453 const TestMuxer::Event& event = mTestMuxer->popEvent();
454 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
455 EXPECT_EQ(event.trackIndex, trackIndex);
456 EXPECT_EQ(event.data, sample->buffer);
457 EXPECT_EQ(event.info.offset, sample->dataOffset);
458 EXPECT_EQ(event.info.size, sample->info.size);
459 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
460 EXPECT_EQ(event.info.flags, sample->info.flags);
461 }
462
463 // Verify EOS samples.
464 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
465 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
466 int64_t duration = 0;
467 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
468
469 // EOS timestamp = first sample timestamp + duration.
470 const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
471 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
472
473 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
474 }
475
476 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
477 EXPECT_TRUE(mTestCallbacks->hasFinished());
478 }
479
480 // Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
readSampleAndAdvance(AMediaExtractor * extractor,size_t * trackIndexOut)481 static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
482 size_t* trackIndexOut) {
483 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
484 if (trackIndex < 0) {
485 return nullptr;
486 }
487
488 if (trackIndexOut != nullptr) {
489 *trackIndexOut = trackIndex;
490 }
491
492 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
493 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
494 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
495
496 size_t bufferSize = static_cast<size_t>(sampleSize);
497 uint8_t* buffer = new uint8_t[bufferSize];
498
499 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
500 EXPECT_EQ(dataRead, sampleSize);
501
502 auto sample = MediaSample::createWithReleaseCallback(
503 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
504 sample->info.size = bufferSize;
505 sample->info.presentationTimeUs = sampleTimeUs;
506 sample->info.flags = flags;
507
508 (void)AMediaExtractor_advance(extractor);
509 return sample;
510 }
511
TEST_F(MediaSampleWriterTests,TestDefaultMuxer)512 TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
513 // Write samples straight from an extractor and validate output file.
514 static const char* destinationPath =
515 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
516 const int destinationFd =
517 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
518 ASSERT_GT(destinationFd, 0);
519
520 // Initialize writer.
521 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
522 EXPECT_TRUE(writer->init(destinationFd, mTestCallbacks));
523 close(destinationFd);
524
525 // Add tracks.
526 const TestMediaSource& mediaSource = getMediaSource();
527 std::vector<MediaSampleWriter::MediaSampleConsumerFunction> sampleConsumers;
528
529 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
530 auto consumer = writer->addTrack(mediaSource.mTrackFormats[trackIndex]);
531 sampleConsumers.push_back(consumer);
532 }
533
534 // Start the writer.
535 ASSERT_TRUE(writer->start());
536
537 // Enqueue samples and finally End Of Stream.
538 std::shared_ptr<MediaSample> sample;
539 size_t trackIndex;
540 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
541 sampleConsumers[trackIndex](sample);
542 }
543 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
544 sampleConsumers[trackIndex](newSampleEos());
545 }
546
547 // Wait for writer.
548 mTestCallbacks->waitForWritingFinished();
549
550 // Compare output file with source.
551 mediaSource.reset();
552
553 AMediaExtractor* extractor = AMediaExtractor_new();
554 ASSERT_NE(extractor, nullptr);
555
556 int sourceFd = open(destinationPath, O_RDONLY);
557 ASSERT_GT(sourceFd, 0);
558
559 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
560 lseek(sourceFd, 0, SEEK_SET);
561
562 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
563 ASSERT_EQ(status, AMEDIA_OK);
564 close(sourceFd);
565
566 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
567 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
568
569 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
570 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
571 ASSERT_NE(trackFormat, nullptr);
572
573 AMediaExtractor_selectTrack(extractor, trackIndex);
574 }
575
576 // Compare samples.
577 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
578 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
579
580 while (sample1 != nullptr && sample2 != nullptr) {
581 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
582 EXPECT_EQ(sample1->info.size, sample2->info.size);
583 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
584
585 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
586
587 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
588 sample2 = readSampleAndAdvance(extractor, nullptr);
589 }
590 EXPECT_EQ(sample1, nullptr);
591 EXPECT_EQ(sample2, nullptr);
592
593 AMediaExtractor_delete(extractor);
594 }
595
596 } // namespace android
597
main(int argc,char ** argv)598 int main(int argc, char** argv) {
599 ::testing::InitGoogleTest(&argc, argv);
600 return RUN_ALL_TESTS();
601 }
602