blob: 0a41b00b147703d05cf026a731b855dd757742ad [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:
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700182 bool hasFinished() {
183 std::unique_lock<std::mutex> lock(mMutex);
184 return mFinished;
185 }
186
187 // MediaSampleWriter::CallbackInterface
188 virtual void onFinished(const MediaSampleWriter* writer __unused,
189 media_status_t status) override {
190 std::unique_lock<std::mutex> lock(mMutex);
191 EXPECT_FALSE(mFinished);
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700192 mFinished = true;
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700193 mStatus = status;
194 mCondition.notify_all();
195 }
196
197 virtual void onStopped(const MediaSampleWriter* writer __unused) {
198 std::unique_lock<std::mutex> lock(mMutex);
199 EXPECT_FALSE(mFinished);
200 mStopped = true;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700201 mCondition.notify_all();
202 }
203
204 virtual void onProgressUpdate(const MediaSampleWriter* writer __unused,
205 int32_t progress) override {
206 EXPECT_GT(progress, mLastProgress);
207 EXPECT_GE(progress, 0);
208 EXPECT_LE(progress, 100);
209
210 mLastProgress = progress;
211 mProgressUpdateCount++;
212 }
213 // ~MediaSampleWriter::CallbackInterface
214
215 void waitForWritingFinished() {
216 std::unique_lock<std::mutex> lock(mMutex);
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700217 while (!mFinished && !mStopped) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700218 mCondition.wait(lock);
219 }
220 }
221
222 uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700223 bool wasStopped() const { return mStopped; }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700224
225private:
226 std::mutex mMutex;
227 std::condition_variable mCondition;
228 bool mFinished = false;
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700229 bool mStopped = false;
230 media_status_t mStatus = AMEDIA_OK;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700231 int32_t mLastProgress = -1;
232 uint32_t mProgressUpdateCount = 0;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800233};
234
235class MediaSampleWriterTests : public ::testing::Test {
236public:
237 MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
238 ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
239
240 static const TestMediaSource& getMediaSource() {
241 static TestMediaSource sMediaSource;
242 static std::once_flag sOnceToken;
243
244 std::call_once(sOnceToken, [] { sMediaSource.init(); });
245
246 sMediaSource.reset();
247 return sMediaSource;
248 }
249
250 static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
251 size_t offset, const uint8_t* buffer) {
252 auto sample = std::make_shared<MediaSample>();
253 sample->info.presentationTimeUs = ptsUs;
254 sample->info.flags = flags;
255 sample->info.size = size;
256 sample->dataOffset = offset;
257 sample->buffer = buffer;
258 return sample;
259 }
260
261 static std::shared_ptr<MediaSample> newSampleEos() {
262 return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
263 }
264
265 static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
266 static uint32_t sampleCount = 0;
267
Linus Nilsson443f16c2020-07-30 13:13:21 -0700268 // Use sampleCount to get a unique mock sample.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800269 uint32_t sampleId = ++sampleCount;
270 return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
271 }
272
Linus Nilssonb09aac22020-07-29 11:56:53 -0700273 static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
274 return newSample(ptsUs, 0, 0, 0, nullptr);
275 }
276
Linus Nilssona85df7f2020-02-20 16:32:04 -0800277 void SetUp() override {
278 LOG(DEBUG) << "MediaSampleWriterTests set up";
279 mTestMuxer = std::make_shared<TestMuxer>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800280 }
281
282 void TearDown() override {
283 LOG(DEBUG) << "MediaSampleWriterTests tear down";
284 mTestMuxer.reset();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800285 }
286
287protected:
288 std::shared_ptr<TestMuxer> mTestMuxer;
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
Linus Nilssonc31d2492020-09-23 12:30:00 -0700295 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
296 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800297}
298
299TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700300 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
301 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800302}
303
304TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700305 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
306 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
307 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800308 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
309}
310
311TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700312 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
313 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800314
Linus Nilssonc31d2492020-09-23 12:30:00 -0700315 EXPECT_EQ(writer->addTrack(nullptr), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800316 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
317}
318
319TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700320 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800321
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700322 std::shared_ptr<TestCallbacks> callbacks = std::make_shared<TestCallbacks>();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700323 EXPECT_TRUE(writer->init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800324
325 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700326 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800327 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
328
Linus Nilssonc31d2492020-09-23 12:30:00 -0700329 ASSERT_TRUE(writer->start());
330 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800331
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700332 writer->stop();
333 writer->stop();
334 callbacks->waitForWritingFinished();
335 EXPECT_TRUE(callbacks->wasStopped());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800336}
337
338TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700339 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
340 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800341
342 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700343 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800344 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
345
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700346 writer->stop();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800347 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
348}
349
350TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700351 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700352
353 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700354 EXPECT_FALSE(writer->init(mTestMuxer, unassignedWp));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700355
356 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700357 EXPECT_FALSE(writer->init(mTestMuxer, unassignedSp));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800358
359 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700360 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
361 ASSERT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800362}
363
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700364TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700365 const TestMediaSource& mediaSource = getMediaSource();
366
Linus Nilssonc31d2492020-09-23 12:30:00 -0700367 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
368 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700369
370 std::shared_ptr<AMediaFormat> videoFormat =
371 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
372 AMediaFormat_copy(videoFormat.get(),
373 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
374
375 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700376 auto sampleConsumer = writer->addTrack(videoFormat);
377 EXPECT_NE(sampleConsumer, nullptr);
378 ASSERT_TRUE(writer->start());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700379
380 for (int64_t pts = 0; pts < 100; ++pts) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700381 sampleConsumer(newSampleWithPts(pts));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700382 }
Linus Nilssonc31d2492020-09-23 12:30:00 -0700383 sampleConsumer(newSampleEos());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700384 mTestCallbacks->waitForWritingFinished();
385
386 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
387}
388
Linus Nilssona85df7f2020-02-20 16:32:04 -0800389TEST_F(MediaSampleWriterTests, TestInterleaving) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700390 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
391 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800392
393 // Use two tracks for this test.
394 static constexpr int kNumTracks = 2;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700395 MediaSampleWriter::MediaSampleConsumerFunction sampleConsumers[kNumTracks];
396 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> addedSamples;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800397 const TestMediaSource& mediaSource = getMediaSource();
398
399 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800400 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
Linus Nilssonc31d2492020-09-23 12:30:00 -0700401 sampleConsumers[trackIdx] = writer->addTrack(trackFormat);
402 EXPECT_NE(sampleConsumers[trackIdx], nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800403 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
404 }
405
406 // Create samples in the expected interleaved order for easy verification.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700407 auto addSampleToTrackWithPts = [&addedSamples, &sampleConsumers](int trackIndex, int64_t pts) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800408 auto sample = newSampleWithPts(pts);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700409 sampleConsumers[trackIndex](sample);
410 addedSamples.emplace_back(sample, trackIndex);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800411 };
412
413 addSampleToTrackWithPts(0, 0);
Linus Nilssonb09aac22020-07-29 11:56:53 -0700414 addSampleToTrackWithPts(1, 4);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800415
Linus Nilssonb09aac22020-07-29 11:56:53 -0700416 addSampleToTrackWithPts(0, 1);
417 addSampleToTrackWithPts(0, 2);
418 addSampleToTrackWithPts(0, 3);
419 addSampleToTrackWithPts(0, 10);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800420
Linus Nilssonb09aac22020-07-29 11:56:53 -0700421 addSampleToTrackWithPts(1, 5);
422 addSampleToTrackWithPts(1, 6);
423 addSampleToTrackWithPts(1, 11);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800424
Linus Nilssonb09aac22020-07-29 11:56:53 -0700425 addSampleToTrackWithPts(0, 12);
426 addSampleToTrackWithPts(1, 13);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800427
428 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700429 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800430 }
431
432 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700433 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800434
435 // Wait for writer to complete.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700436 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800437 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
438
Linus Nilssonc31d2492020-09-23 12:30:00 -0700439 std::sort(addedSamples.begin(), addedSamples.end(),
440 [](const std::pair<std::shared_ptr<MediaSample>, size_t>& left,
441 const std::pair<std::shared_ptr<MediaSample>, size_t>& right) {
442 return left.first->info.presentationTimeUs < right.first->info.presentationTimeUs;
443 });
444
Linus Nilssona85df7f2020-02-20 16:32:04 -0800445 // Verify sample order.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700446 for (auto entry : addedSamples) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800447 auto sample = entry.first;
448 auto trackIndex = entry.second;
449
450 const TestMuxer::Event& event = mTestMuxer->popEvent();
451 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
452 EXPECT_EQ(event.trackIndex, trackIndex);
453 EXPECT_EQ(event.data, sample->buffer);
454 EXPECT_EQ(event.info.offset, sample->dataOffset);
455 EXPECT_EQ(event.info.size, sample->info.size);
456 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
457 EXPECT_EQ(event.info.flags, sample->info.flags);
458 }
459
Linus Nilsson42a971b2020-07-01 16:41:11 -0700460 // Verify EOS samples.
461 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
462 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
463 int64_t duration = 0;
464 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
465
Linus Nilssonb09aac22020-07-29 11:56:53 -0700466 // EOS timestamp = first sample timestamp + duration.
467 const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
468 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
469
Linus Nilsson42a971b2020-07-01 16:41:11 -0700470 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
471 }
472
Linus Nilssona85df7f2020-02-20 16:32:04 -0800473 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700474 EXPECT_TRUE(mTestCallbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800475}
476
Linus Nilssona85df7f2020-02-20 16:32:04 -0800477// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
478static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
479 size_t* trackIndexOut) {
480 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
481 if (trackIndex < 0) {
482 return nullptr;
483 }
484
485 if (trackIndexOut != nullptr) {
486 *trackIndexOut = trackIndex;
487 }
488
489 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
490 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
491 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
492
493 size_t bufferSize = static_cast<size_t>(sampleSize);
494 uint8_t* buffer = new uint8_t[bufferSize];
495
496 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
497 EXPECT_EQ(dataRead, sampleSize);
498
499 auto sample = MediaSample::createWithReleaseCallback(
500 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
501 sample->info.size = bufferSize;
502 sample->info.presentationTimeUs = sampleTimeUs;
503 sample->info.flags = flags;
504
505 (void)AMediaExtractor_advance(extractor);
506 return sample;
507}
508
509TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
510 // Write samples straight from an extractor and validate output file.
511 static const char* destinationPath =
512 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
513 const int destinationFd =
514 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
515 ASSERT_GT(destinationFd, 0);
516
517 // Initialize writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700518 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
519 EXPECT_TRUE(writer->init(destinationFd, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800520 close(destinationFd);
521
522 // Add tracks.
523 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700524 std::vector<MediaSampleWriter::MediaSampleConsumerFunction> sampleConsumers;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800525
526 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700527 auto consumer = writer->addTrack(mediaSource.mTrackFormats[trackIndex]);
528 sampleConsumers.push_back(consumer);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800529 }
530
531 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700532 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800533
534 // Enqueue samples and finally End Of Stream.
535 std::shared_ptr<MediaSample> sample;
536 size_t trackIndex;
537 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700538 sampleConsumers[trackIndex](sample);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800539 }
540 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700541 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800542 }
543
544 // Wait for writer.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700545 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800546
547 // Compare output file with source.
548 mediaSource.reset();
549
550 AMediaExtractor* extractor = AMediaExtractor_new();
551 ASSERT_NE(extractor, nullptr);
552
553 int sourceFd = open(destinationPath, O_RDONLY);
554 ASSERT_GT(sourceFd, 0);
555
556 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
557 lseek(sourceFd, 0, SEEK_SET);
558
559 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
560 ASSERT_EQ(status, AMEDIA_OK);
561 close(sourceFd);
562
563 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
564 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
565
566 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
567 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
568 ASSERT_NE(trackFormat, nullptr);
569
570 AMediaExtractor_selectTrack(extractor, trackIndex);
571 }
572
573 // Compare samples.
574 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
575 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
576
577 while (sample1 != nullptr && sample2 != nullptr) {
578 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
579 EXPECT_EQ(sample1->info.size, sample2->info.size);
580 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
581
582 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
583
584 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
585 sample2 = readSampleAndAdvance(extractor, nullptr);
586 }
587 EXPECT_EQ(sample1, nullptr);
588 EXPECT_EQ(sample2, nullptr);
589
590 AMediaExtractor_delete(extractor);
591}
592
593} // namespace android
594
595int main(int argc, char** argv) {
596 ::testing::InitGoogleTest(&argc, argv);
597 return RUN_ALL_TESTS();
598}