blob: 64240d4bb2eddcaed34b3e09b765c785e535df7a [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
Linus Nilssonb09aac22020-07-29 11:56:53 -070081 static Event WriteSampleWithPts(size_t trackIndex, int64_t pts) {
82 return {.type = Event::WriteSample, .trackIndex = trackIndex, .info = {0, 0, pts, 0}};
83 }
84
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
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
Linus Nilssona85df7f2020-02-20 16:32:04 -080097 if (mEventQueue.empty()) {
98 mPoppedEvent = NoEvent;
99 } else {
100 mPoppedEvent = *mEventQueue.begin();
101 mEventQueue.pop_front();
102 }
103 return mPoppedEvent;
104 }
105
106private:
107 Event mPoppedEvent;
108 std::list<Event> mEventQueue;
109 ssize_t mTrackCount = 0;
Linus Nilssonb09aac22020-07-29 11:56:53 -0700110 std::mutex mMutex;
111 std::condition_variable mCondition;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800112};
113
114bool 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
119bool operator==(const TestMuxer::Event& lhs, const TestMuxer::Event& rhs) {
120 return lhs.type == rhs.type && lhs.format == rhs.format && lhs.trackIndex == rhs.trackIndex &&
121 lhs.data == rhs.data && lhs.info == rhs.info;
122}
123
124/** Represents a media source file. */
125class TestMediaSource {
126public:
127 void init() {
128 static const char* sourcePath =
hkuang2ef2b432020-06-15 18:33:11 -0700129 "/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4";
Linus Nilssona85df7f2020-02-20 16:32:04 -0800130
131 mExtractor = AMediaExtractor_new();
132 ASSERT_NE(mExtractor, nullptr);
133
134 int sourceFd = open(sourcePath, O_RDONLY);
135 ASSERT_GT(sourceFd, 0);
136
137 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
138 lseek(sourceFd, 0, SEEK_SET);
139
140 media_status_t status = AMediaExtractor_setDataSourceFd(mExtractor, sourceFd, 0, fileSize);
141 ASSERT_EQ(status, AMEDIA_OK);
142 close(sourceFd);
143
144 mTrackCount = AMediaExtractor_getTrackCount(mExtractor);
145 ASSERT_GT(mTrackCount, 1);
146 for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
147 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
148 ASSERT_NE(trackFormat, nullptr);
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700149
150 const char* mime = nullptr;
151 AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
152 if (strncmp(mime, "video/", 6) == 0) {
153 mVideoTrackIndex = trackIndex;
154 } else if (strncmp(mime, "audio/", 6) == 0) {
155 mAudioTrackIndex = trackIndex;
156 }
157
Linus Nilssona85df7f2020-02-20 16:32:04 -0800158 mTrackFormats.push_back(
159 std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete));
160
161 AMediaExtractor_selectTrack(mExtractor, trackIndex);
162 }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700163 EXPECT_GE(mVideoTrackIndex, 0);
164 EXPECT_GE(mAudioTrackIndex, 0);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800165 }
166
167 void reset() const {
168 media_status_t status = AMediaExtractor_seekTo(mExtractor, 0 /* seekPosUs */,
169 AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
170 ASSERT_EQ(status, AMEDIA_OK);
171 }
172
173 AMediaExtractor* mExtractor = nullptr;
174 size_t mTrackCount = 0;
175 std::vector<std::shared_ptr<AMediaFormat>> mTrackFormats;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700176 int mVideoTrackIndex = -1;
177 int mAudioTrackIndex = -1;
178};
179
180class TestCallbacks : public MediaSampleWriter::CallbackInterface {
181public:
182 TestCallbacks(bool expectSuccess = true) : mExpectSuccess(expectSuccess) {}
183
184 bool hasFinished() {
185 std::unique_lock<std::mutex> lock(mMutex);
186 return mFinished;
187 }
188
189 // MediaSampleWriter::CallbackInterface
190 virtual void onFinished(const MediaSampleWriter* writer __unused,
191 media_status_t status) override {
192 std::unique_lock<std::mutex> lock(mMutex);
193 EXPECT_FALSE(mFinished);
194 if (mExpectSuccess) {
195 EXPECT_EQ(status, AMEDIA_OK);
196 } else {
197 EXPECT_NE(status, AMEDIA_OK);
198 }
199 mFinished = true;
200 mCondition.notify_all();
201 }
202
203 virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
204 int32_t progress) override {
205 EXPECT_GT(progress, mLastProgress);
206 EXPECT_GE(progress, 0);
207 EXPECT_LE(progress, 100);
208
209 mLastProgress = progress;
210 mProgressUpdateCount++;
211 }
212 // ~MediaSampleWriter::CallbackInterface
213
214 void waitForWritingFinished() {
215 std::unique_lock<std::mutex> lock(mMutex);
216 while (!mFinished) {
217 mCondition.wait(lock);
218 }
219 }
220
221 uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
222
223private:
224 std::mutex mMutex;
225 std::condition_variable mCondition;
226 bool mFinished = false;
227 bool mExpectSuccess;
228 int32_t mLastProgress = -1;
229 uint32_t mProgressUpdateCount = 0;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800230};
231
232class MediaSampleWriterTests : public ::testing::Test {
233public:
234 MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
235 ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
236
237 static const TestMediaSource& getMediaSource() {
238 static TestMediaSource sMediaSource;
239 static std::once_flag sOnceToken;
240
241 std::call_once(sOnceToken, [] { sMediaSource.init(); });
242
243 sMediaSource.reset();
244 return sMediaSource;
245 }
246
247 static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
248 size_t offset, const uint8_t* buffer) {
249 auto sample = std::make_shared<MediaSample>();
250 sample->info.presentationTimeUs = ptsUs;
251 sample->info.flags = flags;
252 sample->info.size = size;
253 sample->dataOffset = offset;
254 sample->buffer = buffer;
255 return sample;
256 }
257
258 static std::shared_ptr<MediaSample> newSampleEos() {
259 return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
260 }
261
262 static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
263 static uint32_t sampleCount = 0;
264
Linus Nilsson443f16c2020-07-30 13:13:21 -0700265 // Use sampleCount to get a unique mock sample.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800266 uint32_t sampleId = ++sampleCount;
267 return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
268 }
269
Linus Nilssonb09aac22020-07-29 11:56:53 -0700270 static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
271 return newSample(ptsUs, 0, 0, 0, nullptr);
272 }
273
Linus Nilssona85df7f2020-02-20 16:32:04 -0800274 void SetUp() override {
275 LOG(DEBUG) << "MediaSampleWriterTests set up";
276 mTestMuxer = std::make_shared<TestMuxer>();
277 mSampleQueue = std::make_shared<MediaSampleQueue>();
278 }
279
280 void TearDown() override {
281 LOG(DEBUG) << "MediaSampleWriterTests tear down";
282 mTestMuxer.reset();
283 mSampleQueue.reset();
284 }
285
286protected:
287 std::shared_ptr<TestMuxer> mTestMuxer;
288 std::shared_ptr<MediaSampleQueue> mSampleQueue;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700289 std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800290};
291
292TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
293 const TestMediaSource& mediaSource = getMediaSource();
294
295 MediaSampleWriter writer{};
296 EXPECT_FALSE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
297}
298
299TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
300 MediaSampleWriter writer{};
301 EXPECT_FALSE(writer.start());
302}
303
304TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
305 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700306 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800307 EXPECT_FALSE(writer.start());
308 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
309}
310
311TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
312 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700313 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800314
315 EXPECT_FALSE(writer.addTrack(mSampleQueue, nullptr));
316 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
317
318 const TestMediaSource& mediaSource = getMediaSource();
319 EXPECT_FALSE(writer.addTrack(nullptr, mediaSource.mTrackFormats[0]));
320 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
321}
322
323TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
324 MediaSampleWriter writer{};
325
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700326 std::shared_ptr<TestCallbacks> callbacks =
327 std::make_shared<TestCallbacks>(false /* expectSuccess */);
328 EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800329
330 const TestMediaSource& mediaSource = getMediaSource();
331 EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
332 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
333
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700334 ASSERT_TRUE(writer.start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800335 EXPECT_FALSE(writer.start());
336
337 EXPECT_TRUE(writer.stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700338 EXPECT_TRUE(callbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800339 EXPECT_FALSE(writer.stop());
340}
341
342TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
343 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700344 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800345
346 const TestMediaSource& mediaSource = getMediaSource();
347 EXPECT_TRUE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
348 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
349
350 EXPECT_FALSE(writer.stop());
351 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
352}
353
354TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
355 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700356
357 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
358 EXPECT_FALSE(writer.init(mTestMuxer, unassignedWp));
359
360 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
361 EXPECT_FALSE(writer.init(mTestMuxer, unassignedSp));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800362
363 const TestMediaSource& mediaSource = getMediaSource();
364 EXPECT_FALSE(writer.addTrack(mSampleQueue, mediaSource.mTrackFormats[0]));
365 ASSERT_FALSE(writer.start());
366}
367
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700368TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700369 const TestMediaSource& mediaSource = getMediaSource();
370
Linus Nilssonb09aac22020-07-29 11:56:53 -0700371 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700372 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
373
374 std::shared_ptr<AMediaFormat> videoFormat =
375 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
376 AMediaFormat_copy(videoFormat.get(),
377 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
378
379 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
380 EXPECT_TRUE(writer.addTrack(mSampleQueue, videoFormat));
381 ASSERT_TRUE(writer.start());
382
383 for (int64_t pts = 0; pts < 100; ++pts) {
384 mSampleQueue->enqueue(newSampleWithPts(pts));
385 }
386 mSampleQueue->enqueue(newSampleEos());
387 mTestCallbacks->waitForWritingFinished();
388
389 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
390}
391
Linus Nilssona85df7f2020-02-20 16:32:04 -0800392TEST_F(MediaSampleWriterTests, TestInterleaving) {
Linus Nilssonb09aac22020-07-29 11:56:53 -0700393 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700394 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800395
396 // Use two tracks for this test.
397 static constexpr int kNumTracks = 2;
398 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
399 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> interleavedSamples;
400 const TestMediaSource& mediaSource = getMediaSource();
401
402 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
403 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
404
405 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
406 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
407 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
408 }
409
410 // Create samples in the expected interleaved order for easy verification.
411 auto addSampleToTrackWithPts = [&interleavedSamples, &sampleQueues](int trackIndex,
412 int64_t pts) {
413 auto sample = newSampleWithPts(pts);
414 sampleQueues[trackIndex]->enqueue(sample);
415 interleavedSamples.emplace_back(sample, trackIndex);
416 };
417
418 addSampleToTrackWithPts(0, 0);
Linus Nilssonb09aac22020-07-29 11:56:53 -0700419 addSampleToTrackWithPts(1, 4);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800420
Linus Nilssonb09aac22020-07-29 11:56:53 -0700421 addSampleToTrackWithPts(0, 1);
422 addSampleToTrackWithPts(0, 2);
423 addSampleToTrackWithPts(0, 3);
424 addSampleToTrackWithPts(0, 10);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800425
Linus Nilssonb09aac22020-07-29 11:56:53 -0700426 addSampleToTrackWithPts(1, 5);
427 addSampleToTrackWithPts(1, 6);
428 addSampleToTrackWithPts(1, 11);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800429
Linus Nilssonb09aac22020-07-29 11:56:53 -0700430 addSampleToTrackWithPts(0, 12);
431 addSampleToTrackWithPts(1, 13);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800432
433 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
434 sampleQueues[trackIndex]->enqueue(newSampleEos());
435 }
436
437 // Start the writer.
438 ASSERT_TRUE(writer.start());
439
440 // Wait for writer to complete.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700441 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800442 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
443
444 // Verify sample order.
445 for (auto entry : interleavedSamples) {
446 auto sample = entry.first;
447 auto trackIndex = entry.second;
448
449 const TestMuxer::Event& event = mTestMuxer->popEvent();
450 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
451 EXPECT_EQ(event.trackIndex, trackIndex);
452 EXPECT_EQ(event.data, sample->buffer);
453 EXPECT_EQ(event.info.offset, sample->dataOffset);
454 EXPECT_EQ(event.info.size, sample->info.size);
455 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
456 EXPECT_EQ(event.info.flags, sample->info.flags);
457 }
458
Linus Nilsson42a971b2020-07-01 16:41:11 -0700459 // Verify EOS samples.
460 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
461 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
462 int64_t duration = 0;
463 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
464
Linus Nilssonb09aac22020-07-29 11:56:53 -0700465 // EOS timestamp = first sample timestamp + duration.
466 const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
467 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
468
Linus Nilsson42a971b2020-07-01 16:41:11 -0700469 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
470 }
471
Linus Nilssona85df7f2020-02-20 16:32:04 -0800472 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
473 EXPECT_TRUE(writer.stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700474 EXPECT_TRUE(mTestCallbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800475}
476
Linus Nilssonb09aac22020-07-29 11:56:53 -0700477TEST_F(MediaSampleWriterTests, TestMaxDivergence) {
478 static constexpr uint32_t kMaxDivergenceUs = 10;
479
480 MediaSampleWriter writer{kMaxDivergenceUs};
481 EXPECT_TRUE(writer.init(mTestMuxer, mTestCallbacks));
482
483 // Use two tracks for this test.
484 static constexpr int kNumTracks = 2;
485 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
486 const TestMediaSource& mediaSource = getMediaSource();
487
488 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
489 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
490
491 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
492 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
493 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
494 }
495
496 ASSERT_TRUE(writer.start());
497 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::Start());
498
499 // The first samples of each track can be written in any order since the writer does not have
500 // any previous timestamps to compare.
501 sampleQueues[0]->enqueue(newSampleWithPtsOnly(0));
502 sampleQueues[1]->enqueue(newSampleWithPtsOnly(1));
503 mTestMuxer->popEvent(true);
504 mTestMuxer->popEvent(true);
505
506 // The writer will now be waiting on track 0 since it has the lowest previous timestamp.
507 sampleQueues[0]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 1));
508 sampleQueues[0]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 2));
509
510 // The writer should dequeue the first sample above but not the second since track 0 now is too
511 // far ahead. Instead it should wait for track 1.
512 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, kMaxDivergenceUs + 1));
513
514 // Enqueue a sample from track 1 that puts it within acceptable divergence range again. The
515 // writer should dequeue that sample and then go back to track 0 since track 1 is empty.
516 sampleQueues[1]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs));
517 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, kMaxDivergenceUs));
518 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, kMaxDivergenceUs + 2));
519
520 // Both tracks are now empty so the writer should wait for track 1 which is farthest behind.
521 sampleQueues[1]->enqueue(newSampleWithPtsOnly(kMaxDivergenceUs + 3));
522 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, kMaxDivergenceUs + 3));
523
524 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
525 sampleQueues[trackIndex]->enqueue(newSampleEos());
526 }
527
528 // Wait for writer to complete.
529 mTestCallbacks->waitForWritingFinished();
530
531 // Verify EOS samples.
532 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
533 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
534 int64_t duration = 0;
535 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
536
537 // EOS timestamp = first sample timestamp + duration.
538 const int64_t endTime = duration + (trackIndex == 1 ? 1 : 0);
539 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
540 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
541 }
542
543 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
544 EXPECT_TRUE(writer.stop());
545 EXPECT_TRUE(mTestCallbacks->hasFinished());
546}
547
548TEST_F(MediaSampleWriterTests, TestTimestampDivergenceOverflow) {
549 auto testCallbacks = std::make_shared<TestCallbacks>(false /* expectSuccess */);
550 MediaSampleWriter writer{};
551 EXPECT_TRUE(writer.init(mTestMuxer, testCallbacks));
552
553 // Use two tracks for this test.
554 static constexpr int kNumTracks = 2;
555 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
556 const TestMediaSource& mediaSource = getMediaSource();
557
558 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
559 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
560
561 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
562 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
563 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
564 }
565
566 // Prime track 0 with lower end of INT64 range, and track 1 with positive timestamps making the
567 // difference larger than INT64_MAX.
568 sampleQueues[0]->enqueue(newSampleWithPtsOnly(INT64_MIN + 1));
569 sampleQueues[1]->enqueue(newSampleWithPtsOnly(1000));
570 sampleQueues[1]->enqueue(newSampleWithPtsOnly(1001));
571
572 ASSERT_TRUE(writer.start());
573 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::Start());
574
575 // The first sample of each track can be pulled in any order.
576 mTestMuxer->popEvent(true);
577 mTestMuxer->popEvent(true);
578
579 // Wait to make sure the writer compares track 0 empty against track 1 non-empty. The writer
580 // should handle the large timestamp differences and chose to wait for track 0 even though
581 // track 1 has a sample ready.
582 std::this_thread::sleep_for(std::chrono::milliseconds(20));
583
584 sampleQueues[0]->enqueue(newSampleWithPtsOnly(INT64_MIN + 2));
585 sampleQueues[0]->enqueue(newSampleWithPtsOnly(1000)); // <-- Close the gap between the tracks.
586 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, INT64_MIN + 2));
587 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(0, 1000));
588 EXPECT_EQ(mTestMuxer->popEvent(true), TestMuxer::WriteSampleWithPts(1, 1001));
589
590 EXPECT_TRUE(writer.stop());
591 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
592 EXPECT_TRUE(testCallbacks->hasFinished());
593}
594
Linus Nilssona85df7f2020-02-20 16:32:04 -0800595TEST_F(MediaSampleWriterTests, TestAbortInputQueue) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800596 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700597 std::shared_ptr<TestCallbacks> callbacks =
598 std::make_shared<TestCallbacks>(false /* expectSuccess */);
599 EXPECT_TRUE(writer.init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800600
601 // Use two tracks for this test.
602 static constexpr int kNumTracks = 2;
603 std::shared_ptr<MediaSampleQueue> sampleQueues[kNumTracks];
604 const TestMediaSource& mediaSource = getMediaSource();
605
606 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
607 sampleQueues[trackIdx] = std::make_shared<MediaSampleQueue>();
608
609 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
610 EXPECT_TRUE(writer.addTrack(sampleQueues[trackIdx], trackFormat));
611 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
612 }
613
614 // Start the writer.
615 ASSERT_TRUE(writer.start());
616
617 // Abort the input queues and wait for the writer to complete.
618 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
619 sampleQueues[trackIdx]->abort();
620 }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700621
622 callbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800623
624 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
625 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
626 EXPECT_TRUE(writer.stop());
627}
628
629// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
630static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
631 size_t* trackIndexOut) {
632 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
633 if (trackIndex < 0) {
634 return nullptr;
635 }
636
637 if (trackIndexOut != nullptr) {
638 *trackIndexOut = trackIndex;
639 }
640
641 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
642 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
643 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
644
645 size_t bufferSize = static_cast<size_t>(sampleSize);
646 uint8_t* buffer = new uint8_t[bufferSize];
647
648 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
649 EXPECT_EQ(dataRead, sampleSize);
650
651 auto sample = MediaSample::createWithReleaseCallback(
652 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
653 sample->info.size = bufferSize;
654 sample->info.presentationTimeUs = sampleTimeUs;
655 sample->info.flags = flags;
656
657 (void)AMediaExtractor_advance(extractor);
658 return sample;
659}
660
661TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
662 // Write samples straight from an extractor and validate output file.
663 static const char* destinationPath =
664 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
665 const int destinationFd =
666 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
667 ASSERT_GT(destinationFd, 0);
668
669 // Initialize writer.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800670 MediaSampleWriter writer{};
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700671 EXPECT_TRUE(writer.init(destinationFd, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800672 close(destinationFd);
673
674 // Add tracks.
675 const TestMediaSource& mediaSource = getMediaSource();
676 std::vector<std::shared_ptr<MediaSampleQueue>> inputQueues;
677
678 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
679 inputQueues.push_back(std::make_shared<MediaSampleQueue>());
680 EXPECT_TRUE(
681 writer.addTrack(inputQueues[trackIndex], mediaSource.mTrackFormats[trackIndex]));
682 }
683
684 // Start the writer.
685 ASSERT_TRUE(writer.start());
686
687 // Enqueue samples and finally End Of Stream.
688 std::shared_ptr<MediaSample> sample;
689 size_t trackIndex;
690 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
691 inputQueues[trackIndex]->enqueue(sample);
692 }
693 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
694 inputQueues[trackIndex]->enqueue(newSampleEos());
695 }
696
697 // Wait for writer.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700698 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800699 EXPECT_TRUE(writer.stop());
700
701 // Compare output file with source.
702 mediaSource.reset();
703
704 AMediaExtractor* extractor = AMediaExtractor_new();
705 ASSERT_NE(extractor, nullptr);
706
707 int sourceFd = open(destinationPath, O_RDONLY);
708 ASSERT_GT(sourceFd, 0);
709
710 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
711 lseek(sourceFd, 0, SEEK_SET);
712
713 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
714 ASSERT_EQ(status, AMEDIA_OK);
715 close(sourceFd);
716
717 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
718 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
719
720 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
721 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
722 ASSERT_NE(trackFormat, nullptr);
723
724 AMediaExtractor_selectTrack(extractor, trackIndex);
725 }
726
727 // Compare samples.
728 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
729 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
730
731 while (sample1 != nullptr && sample2 != nullptr) {
732 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
733 EXPECT_EQ(sample1->info.size, sample2->info.size);
734 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
735
736 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
737
738 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
739 sample2 = readSampleAndAdvance(extractor, nullptr);
740 }
741 EXPECT_EQ(sample1, nullptr);
742 EXPECT_EQ(sample2, nullptr);
743
744 AMediaExtractor_delete(extractor);
745}
746
747} // namespace android
748
749int main(int argc, char** argv) {
750 ::testing::InitGoogleTest(&argc, argv);
751 return RUN_ALL_TESTS();
752}