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