blob: 8b3905c409b5d15a58e1ee988f2a3e6e8e002fda [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 }
Chong Zhang457c6892021-02-01 15:34:20 -0800213
214 virtual void onHeartBeat(const MediaSampleWriter* writer __unused) override {}
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700215 // ~MediaSampleWriter::CallbackInterface
216
217 void waitForWritingFinished() {
218 std::unique_lock<std::mutex> lock(mMutex);
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700219 while (!mFinished && !mStopped) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700220 mCondition.wait(lock);
221 }
222 }
223
224 uint32_t getProgressUpdateCount() const { return mProgressUpdateCount; }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700225 bool wasStopped() const { return mStopped; }
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700226
227private:
228 std::mutex mMutex;
229 std::condition_variable mCondition;
230 bool mFinished = false;
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700231 bool mStopped = false;
232 media_status_t mStatus = AMEDIA_OK;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700233 int32_t mLastProgress = -1;
234 uint32_t mProgressUpdateCount = 0;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800235};
236
237class MediaSampleWriterTests : public ::testing::Test {
238public:
239 MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests created"; }
240 ~MediaSampleWriterTests() { LOG(DEBUG) << "MediaSampleWriterTests destroyed"; }
241
242 static const TestMediaSource& getMediaSource() {
243 static TestMediaSource sMediaSource;
244 static std::once_flag sOnceToken;
245
246 std::call_once(sOnceToken, [] { sMediaSource.init(); });
247
248 sMediaSource.reset();
249 return sMediaSource;
250 }
251
252 static std::shared_ptr<MediaSample> newSample(int64_t ptsUs, uint32_t flags, size_t size,
253 size_t offset, const uint8_t* buffer) {
254 auto sample = std::make_shared<MediaSample>();
255 sample->info.presentationTimeUs = ptsUs;
256 sample->info.flags = flags;
257 sample->info.size = size;
258 sample->dataOffset = offset;
259 sample->buffer = buffer;
260 return sample;
261 }
262
263 static std::shared_ptr<MediaSample> newSampleEos() {
264 return newSample(0, SAMPLE_FLAG_END_OF_STREAM, 0, 0, nullptr);
265 }
266
267 static std::shared_ptr<MediaSample> newSampleWithPts(int64_t ptsUs) {
268 static uint32_t sampleCount = 0;
269
Linus Nilsson443f16c2020-07-30 13:13:21 -0700270 // Use sampleCount to get a unique mock sample.
Linus Nilssona85df7f2020-02-20 16:32:04 -0800271 uint32_t sampleId = ++sampleCount;
272 return newSample(ptsUs, 0, sampleId, sampleId, reinterpret_cast<const uint8_t*>(sampleId));
273 }
274
Linus Nilssonb09aac22020-07-29 11:56:53 -0700275 static std::shared_ptr<MediaSample> newSampleWithPtsOnly(int64_t ptsUs) {
276 return newSample(ptsUs, 0, 0, 0, nullptr);
277 }
278
Linus Nilssona85df7f2020-02-20 16:32:04 -0800279 void SetUp() override {
280 LOG(DEBUG) << "MediaSampleWriterTests set up";
281 mTestMuxer = std::make_shared<TestMuxer>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800282 }
283
284 void TearDown() override {
285 LOG(DEBUG) << "MediaSampleWriterTests tear down";
286 mTestMuxer.reset();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800287 }
288
289protected:
290 std::shared_ptr<TestMuxer> mTestMuxer;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700291 std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800292};
293
294TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
295 const TestMediaSource& mediaSource = getMediaSource();
296
Linus Nilssonc31d2492020-09-23 12:30:00 -0700297 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
298 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800299}
300
301TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700302 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
303 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800304}
305
306TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700307 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
308 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
309 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800310 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
311}
312
313TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700314 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
315 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800316
Linus Nilssonc31d2492020-09-23 12:30:00 -0700317 EXPECT_EQ(writer->addTrack(nullptr), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800318 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
319}
320
321TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700322 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800323
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700324 std::shared_ptr<TestCallbacks> callbacks = std::make_shared<TestCallbacks>();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700325 EXPECT_TRUE(writer->init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800326
327 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700328 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800329 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
330
Linus Nilssonc31d2492020-09-23 12:30:00 -0700331 ASSERT_TRUE(writer->start());
332 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800333
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700334 writer->stop();
335 writer->stop();
336 callbacks->waitForWritingFinished();
337 EXPECT_TRUE(callbacks->wasStopped());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800338}
339
340TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700341 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
342 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800343
344 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700345 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800346 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
347
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700348 writer->stop();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800349 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
350}
351
352TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700353 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700354
355 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700356 EXPECT_FALSE(writer->init(mTestMuxer, unassignedWp));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700357
358 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700359 EXPECT_FALSE(writer->init(mTestMuxer, unassignedSp));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800360
361 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700362 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
363 ASSERT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800364}
365
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700366TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700367 const TestMediaSource& mediaSource = getMediaSource();
368
Linus Nilssonc31d2492020-09-23 12:30:00 -0700369 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
370 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700371
372 std::shared_ptr<AMediaFormat> videoFormat =
373 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
374 AMediaFormat_copy(videoFormat.get(),
375 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
376
377 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700378 auto sampleConsumer = writer->addTrack(videoFormat);
379 EXPECT_NE(sampleConsumer, nullptr);
380 ASSERT_TRUE(writer->start());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700381
382 for (int64_t pts = 0; pts < 100; ++pts) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700383 sampleConsumer(newSampleWithPts(pts));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700384 }
Linus Nilssonc31d2492020-09-23 12:30:00 -0700385 sampleConsumer(newSampleEos());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700386 mTestCallbacks->waitForWritingFinished();
387
388 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
389}
390
Linus Nilssona85df7f2020-02-20 16:32:04 -0800391TEST_F(MediaSampleWriterTests, TestInterleaving) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700392 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
393 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800394
395 // Use two tracks for this test.
396 static constexpr int kNumTracks = 2;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700397 MediaSampleWriter::MediaSampleConsumerFunction sampleConsumers[kNumTracks];
398 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> addedSamples;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800399 const TestMediaSource& mediaSource = getMediaSource();
400
401 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800402 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
Linus Nilssonc31d2492020-09-23 12:30:00 -0700403 sampleConsumers[trackIdx] = writer->addTrack(trackFormat);
404 EXPECT_NE(sampleConsumers[trackIdx], nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800405 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
406 }
407
408 // Create samples in the expected interleaved order for easy verification.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700409 auto addSampleToTrackWithPts = [&addedSamples, &sampleConsumers](int trackIndex, int64_t pts) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800410 auto sample = newSampleWithPts(pts);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700411 sampleConsumers[trackIndex](sample);
412 addedSamples.emplace_back(sample, trackIndex);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800413 };
414
415 addSampleToTrackWithPts(0, 0);
Linus Nilssonb09aac22020-07-29 11:56:53 -0700416 addSampleToTrackWithPts(1, 4);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800417
Linus Nilssonb09aac22020-07-29 11:56:53 -0700418 addSampleToTrackWithPts(0, 1);
419 addSampleToTrackWithPts(0, 2);
420 addSampleToTrackWithPts(0, 3);
421 addSampleToTrackWithPts(0, 10);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800422
Linus Nilssonb09aac22020-07-29 11:56:53 -0700423 addSampleToTrackWithPts(1, 5);
424 addSampleToTrackWithPts(1, 6);
425 addSampleToTrackWithPts(1, 11);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800426
Linus Nilssonb09aac22020-07-29 11:56:53 -0700427 addSampleToTrackWithPts(0, 12);
428 addSampleToTrackWithPts(1, 13);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800429
430 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700431 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800432 }
433
434 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700435 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800436
437 // Wait for writer to complete.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700438 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800439 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
440
Linus Nilssonc31d2492020-09-23 12:30:00 -0700441 std::sort(addedSamples.begin(), addedSamples.end(),
442 [](const std::pair<std::shared_ptr<MediaSample>, size_t>& left,
443 const std::pair<std::shared_ptr<MediaSample>, size_t>& right) {
444 return left.first->info.presentationTimeUs < right.first->info.presentationTimeUs;
445 });
446
Linus Nilssona85df7f2020-02-20 16:32:04 -0800447 // Verify sample order.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700448 for (auto entry : addedSamples) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800449 auto sample = entry.first;
450 auto trackIndex = entry.second;
451
452 const TestMuxer::Event& event = mTestMuxer->popEvent();
453 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
454 EXPECT_EQ(event.trackIndex, trackIndex);
455 EXPECT_EQ(event.data, sample->buffer);
456 EXPECT_EQ(event.info.offset, sample->dataOffset);
457 EXPECT_EQ(event.info.size, sample->info.size);
458 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
459 EXPECT_EQ(event.info.flags, sample->info.flags);
460 }
461
Linus Nilsson42a971b2020-07-01 16:41:11 -0700462 // Verify EOS samples.
463 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
464 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
465 int64_t duration = 0;
466 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
467
Linus Nilssonb09aac22020-07-29 11:56:53 -0700468 // EOS timestamp = first sample timestamp + duration.
469 const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
470 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
471
Linus Nilsson42a971b2020-07-01 16:41:11 -0700472 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
473 }
474
Linus Nilssona85df7f2020-02-20 16:32:04 -0800475 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700476 EXPECT_TRUE(mTestCallbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800477}
478
Linus Nilssona85df7f2020-02-20 16:32:04 -0800479// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
480static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
481 size_t* trackIndexOut) {
482 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
483 if (trackIndex < 0) {
484 return nullptr;
485 }
486
487 if (trackIndexOut != nullptr) {
488 *trackIndexOut = trackIndex;
489 }
490
491 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
492 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
493 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
494
495 size_t bufferSize = static_cast<size_t>(sampleSize);
496 uint8_t* buffer = new uint8_t[bufferSize];
497
498 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
499 EXPECT_EQ(dataRead, sampleSize);
500
501 auto sample = MediaSample::createWithReleaseCallback(
502 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
503 sample->info.size = bufferSize;
504 sample->info.presentationTimeUs = sampleTimeUs;
505 sample->info.flags = flags;
506
507 (void)AMediaExtractor_advance(extractor);
508 return sample;
509}
510
511TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
512 // Write samples straight from an extractor and validate output file.
513 static const char* destinationPath =
514 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
515 const int destinationFd =
516 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
517 ASSERT_GT(destinationFd, 0);
518
519 // Initialize writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700520 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
521 EXPECT_TRUE(writer->init(destinationFd, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800522 close(destinationFd);
523
524 // Add tracks.
525 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700526 std::vector<MediaSampleWriter::MediaSampleConsumerFunction> sampleConsumers;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800527
528 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700529 auto consumer = writer->addTrack(mediaSource.mTrackFormats[trackIndex]);
530 sampleConsumers.push_back(consumer);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800531 }
532
533 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700534 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800535
536 // Enqueue samples and finally End Of Stream.
537 std::shared_ptr<MediaSample> sample;
538 size_t trackIndex;
539 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700540 sampleConsumers[trackIndex](sample);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800541 }
542 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700543 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800544 }
545
546 // Wait for writer.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700547 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800548
549 // Compare output file with source.
550 mediaSource.reset();
551
552 AMediaExtractor* extractor = AMediaExtractor_new();
553 ASSERT_NE(extractor, nullptr);
554
555 int sourceFd = open(destinationPath, O_RDONLY);
556 ASSERT_GT(sourceFd, 0);
557
558 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
559 lseek(sourceFd, 0, SEEK_SET);
560
561 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
562 ASSERT_EQ(status, AMEDIA_OK);
563 close(sourceFd);
564
565 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
566 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
567
568 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
569 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
570 ASSERT_NE(trackFormat, nullptr);
571
572 AMediaExtractor_selectTrack(extractor, trackIndex);
573 }
574
575 // Compare samples.
576 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
577 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
578
579 while (sample1 != nullptr && sample2 != nullptr) {
580 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
581 EXPECT_EQ(sample1->info.size, sample2->info.size);
582 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
583
584 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
585
586 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
587 sample2 = readSampleAndAdvance(extractor, nullptr);
588 }
589 EXPECT_EQ(sample1, nullptr);
590 EXPECT_EQ(sample2, nullptr);
591
592 AMediaExtractor_delete(extractor);
593}
594
595} // namespace android
596
597int main(int argc, char** argv) {
598 ::testing::InitGoogleTest(&argc, argv);
599 return RUN_ALL_TESTS();
600}