blob: 46f3e9b8b35e3bff198802a672cd62a6a3a52fb6 [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>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800277 }
278
279 void TearDown() override {
280 LOG(DEBUG) << "MediaSampleWriterTests tear down";
281 mTestMuxer.reset();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800282 }
283
284protected:
285 std::shared_ptr<TestMuxer> mTestMuxer;
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700286 std::shared_ptr<TestCallbacks> mTestCallbacks = std::make_shared<TestCallbacks>();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800287};
288
289TEST_F(MediaSampleWriterTests, TestAddTrackWithoutInit) {
290 const TestMediaSource& mediaSource = getMediaSource();
291
Linus Nilssonc31d2492020-09-23 12:30:00 -0700292 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
293 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800294}
295
296TEST_F(MediaSampleWriterTests, TestStartWithoutInit) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700297 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
298 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800299}
300
301TEST_F(MediaSampleWriterTests, TestStartWithoutTracks) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700302 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
303 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
304 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800305 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
306}
307
308TEST_F(MediaSampleWriterTests, TestAddInvalidTrack) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700309 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
310 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800311
Linus Nilssonc31d2492020-09-23 12:30:00 -0700312 EXPECT_EQ(writer->addTrack(nullptr), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800313 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
314}
315
316TEST_F(MediaSampleWriterTests, TestDoubleStartStop) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700317 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800318
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700319 std::shared_ptr<TestCallbacks> callbacks =
320 std::make_shared<TestCallbacks>(false /* expectSuccess */);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700321 EXPECT_TRUE(writer->init(mTestMuxer, callbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800322
323 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700324 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800325 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
326
Linus Nilssonc31d2492020-09-23 12:30:00 -0700327 ASSERT_TRUE(writer->start());
328 EXPECT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800329
Linus Nilssonc31d2492020-09-23 12:30:00 -0700330 EXPECT_TRUE(writer->stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700331 EXPECT_TRUE(callbacks->hasFinished());
Linus Nilssonc31d2492020-09-23 12:30:00 -0700332 EXPECT_FALSE(writer->stop());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800333}
334
335TEST_F(MediaSampleWriterTests, TestStopWithoutStart) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700336 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
337 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800338
339 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700340 EXPECT_NE(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800341 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(mediaSource.mTrackFormats[0].get()));
342
Linus Nilssonc31d2492020-09-23 12:30:00 -0700343 EXPECT_FALSE(writer->stop());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800344 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::NoEvent);
345}
346
347TEST_F(MediaSampleWriterTests, TestStartWithoutCallback) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700348 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700349
350 std::weak_ptr<MediaSampleWriter::CallbackInterface> unassignedWp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700351 EXPECT_FALSE(writer->init(mTestMuxer, unassignedWp));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700352
353 std::shared_ptr<MediaSampleWriter::CallbackInterface> unassignedSp;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700354 EXPECT_FALSE(writer->init(mTestMuxer, unassignedSp));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800355
356 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700357 EXPECT_EQ(writer->addTrack(mediaSource.mTrackFormats[0]), nullptr);
358 ASSERT_FALSE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800359}
360
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700361TEST_F(MediaSampleWriterTests, TestProgressUpdate) {
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700362 const TestMediaSource& mediaSource = getMediaSource();
363
Linus Nilssonc31d2492020-09-23 12:30:00 -0700364 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
365 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700366
367 std::shared_ptr<AMediaFormat> videoFormat =
368 std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
369 AMediaFormat_copy(videoFormat.get(),
370 mediaSource.mTrackFormats[mediaSource.mVideoTrackIndex].get());
371
372 AMediaFormat_setInt64(videoFormat.get(), AMEDIAFORMAT_KEY_DURATION, 100);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700373 auto sampleConsumer = writer->addTrack(videoFormat);
374 EXPECT_NE(sampleConsumer, nullptr);
375 ASSERT_TRUE(writer->start());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700376
377 for (int64_t pts = 0; pts < 100; ++pts) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700378 sampleConsumer(newSampleWithPts(pts));
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700379 }
Linus Nilssonc31d2492020-09-23 12:30:00 -0700380 sampleConsumer(newSampleEos());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700381 mTestCallbacks->waitForWritingFinished();
382
383 EXPECT_EQ(mTestCallbacks->getProgressUpdateCount(), 100);
384}
385
Linus Nilssona85df7f2020-02-20 16:32:04 -0800386TEST_F(MediaSampleWriterTests, TestInterleaving) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700387 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
388 EXPECT_TRUE(writer->init(mTestMuxer, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800389
390 // Use two tracks for this test.
391 static constexpr int kNumTracks = 2;
Linus Nilssonc31d2492020-09-23 12:30:00 -0700392 MediaSampleWriter::MediaSampleConsumerFunction sampleConsumers[kNumTracks];
393 std::vector<std::pair<std::shared_ptr<MediaSample>, size_t>> addedSamples;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800394 const TestMediaSource& mediaSource = getMediaSource();
395
396 for (int trackIdx = 0; trackIdx < kNumTracks; ++trackIdx) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800397 auto trackFormat = mediaSource.mTrackFormats[trackIdx % mediaSource.mTrackCount];
Linus Nilssonc31d2492020-09-23 12:30:00 -0700398 sampleConsumers[trackIdx] = writer->addTrack(trackFormat);
399 EXPECT_NE(sampleConsumers[trackIdx], nullptr);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800400 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::AddTrack(trackFormat.get()));
401 }
402
403 // Create samples in the expected interleaved order for easy verification.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700404 auto addSampleToTrackWithPts = [&addedSamples, &sampleConsumers](int trackIndex, int64_t pts) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800405 auto sample = newSampleWithPts(pts);
Linus Nilssonc31d2492020-09-23 12:30:00 -0700406 sampleConsumers[trackIndex](sample);
407 addedSamples.emplace_back(sample, trackIndex);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800408 };
409
410 addSampleToTrackWithPts(0, 0);
Linus Nilssonb09aac22020-07-29 11:56:53 -0700411 addSampleToTrackWithPts(1, 4);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800412
Linus Nilssonb09aac22020-07-29 11:56:53 -0700413 addSampleToTrackWithPts(0, 1);
414 addSampleToTrackWithPts(0, 2);
415 addSampleToTrackWithPts(0, 3);
416 addSampleToTrackWithPts(0, 10);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800417
Linus Nilssonb09aac22020-07-29 11:56:53 -0700418 addSampleToTrackWithPts(1, 5);
419 addSampleToTrackWithPts(1, 6);
420 addSampleToTrackWithPts(1, 11);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800421
Linus Nilssonb09aac22020-07-29 11:56:53 -0700422 addSampleToTrackWithPts(0, 12);
423 addSampleToTrackWithPts(1, 13);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800424
425 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700426 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800427 }
428
429 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700430 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800431
432 // Wait for writer to complete.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700433 mTestCallbacks->waitForWritingFinished();
Linus Nilssona85df7f2020-02-20 16:32:04 -0800434 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Start());
435
Linus Nilssonc31d2492020-09-23 12:30:00 -0700436 std::sort(addedSamples.begin(), addedSamples.end(),
437 [](const std::pair<std::shared_ptr<MediaSample>, size_t>& left,
438 const std::pair<std::shared_ptr<MediaSample>, size_t>& right) {
439 return left.first->info.presentationTimeUs < right.first->info.presentationTimeUs;
440 });
441
Linus Nilssona85df7f2020-02-20 16:32:04 -0800442 // Verify sample order.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700443 for (auto entry : addedSamples) {
Linus Nilssona85df7f2020-02-20 16:32:04 -0800444 auto sample = entry.first;
445 auto trackIndex = entry.second;
446
447 const TestMuxer::Event& event = mTestMuxer->popEvent();
448 EXPECT_EQ(event.type, TestMuxer::Event::WriteSample);
449 EXPECT_EQ(event.trackIndex, trackIndex);
450 EXPECT_EQ(event.data, sample->buffer);
451 EXPECT_EQ(event.info.offset, sample->dataOffset);
452 EXPECT_EQ(event.info.size, sample->info.size);
453 EXPECT_EQ(event.info.presentationTimeUs, sample->info.presentationTimeUs);
454 EXPECT_EQ(event.info.flags, sample->info.flags);
455 }
456
Linus Nilsson42a971b2020-07-01 16:41:11 -0700457 // Verify EOS samples.
458 for (int trackIndex = 0; trackIndex < kNumTracks; ++trackIndex) {
459 auto trackFormat = mediaSource.mTrackFormats[trackIndex % mediaSource.mTrackCount];
460 int64_t duration = 0;
461 AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &duration);
462
Linus Nilssonb09aac22020-07-29 11:56:53 -0700463 // EOS timestamp = first sample timestamp + duration.
464 const int64_t endTime = duration + (trackIndex == 1 ? 4 : 0);
465 const AMediaCodecBufferInfo info = {0, 0, endTime, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM};
466
Linus Nilsson42a971b2020-07-01 16:41:11 -0700467 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::WriteSample(trackIndex, nullptr, &info));
468 }
469
Linus Nilssona85df7f2020-02-20 16:32:04 -0800470 EXPECT_EQ(mTestMuxer->popEvent(), TestMuxer::Stop());
Linus Nilssonc31d2492020-09-23 12:30:00 -0700471 EXPECT_TRUE(writer->stop());
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700472 EXPECT_TRUE(mTestCallbacks->hasFinished());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800473}
474
Linus Nilssona85df7f2020-02-20 16:32:04 -0800475// Convenience function for reading a sample from an AMediaExtractor represented as a MediaSample.
476static std::shared_ptr<MediaSample> readSampleAndAdvance(AMediaExtractor* extractor,
477 size_t* trackIndexOut) {
478 int trackIndex = AMediaExtractor_getSampleTrackIndex(extractor);
479 if (trackIndex < 0) {
480 return nullptr;
481 }
482
483 if (trackIndexOut != nullptr) {
484 *trackIndexOut = trackIndex;
485 }
486
487 ssize_t sampleSize = AMediaExtractor_getSampleSize(extractor);
488 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(extractor);
489 uint32_t flags = AMediaExtractor_getSampleFlags(extractor);
490
491 size_t bufferSize = static_cast<size_t>(sampleSize);
492 uint8_t* buffer = new uint8_t[bufferSize];
493
494 ssize_t dataRead = AMediaExtractor_readSampleData(extractor, buffer, bufferSize);
495 EXPECT_EQ(dataRead, sampleSize);
496
497 auto sample = MediaSample::createWithReleaseCallback(
498 buffer, 0 /* offset */, 0 /* id */, [buffer](MediaSample*) { delete[] buffer; });
499 sample->info.size = bufferSize;
500 sample->info.presentationTimeUs = sampleTimeUs;
501 sample->info.flags = flags;
502
503 (void)AMediaExtractor_advance(extractor);
504 return sample;
505}
506
507TEST_F(MediaSampleWriterTests, TestDefaultMuxer) {
508 // Write samples straight from an extractor and validate output file.
509 static const char* destinationPath =
510 "/data/local/tmp/MediaSampleWriterTests_TestDefaultMuxer_output.MP4";
511 const int destinationFd =
512 open(destinationPath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH);
513 ASSERT_GT(destinationFd, 0);
514
515 // Initialize writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700516 std::shared_ptr<MediaSampleWriter> writer = MediaSampleWriter::Create();
517 EXPECT_TRUE(writer->init(destinationFd, mTestCallbacks));
Linus Nilssona85df7f2020-02-20 16:32:04 -0800518 close(destinationFd);
519
520 // Add tracks.
521 const TestMediaSource& mediaSource = getMediaSource();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700522 std::vector<MediaSampleWriter::MediaSampleConsumerFunction> sampleConsumers;
Linus Nilssona85df7f2020-02-20 16:32:04 -0800523
524 for (size_t trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700525 auto consumer = writer->addTrack(mediaSource.mTrackFormats[trackIndex]);
526 sampleConsumers.push_back(consumer);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800527 }
528
529 // Start the writer.
Linus Nilssonc31d2492020-09-23 12:30:00 -0700530 ASSERT_TRUE(writer->start());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800531
532 // Enqueue samples and finally End Of Stream.
533 std::shared_ptr<MediaSample> sample;
534 size_t trackIndex;
535 while ((sample = readSampleAndAdvance(mediaSource.mExtractor, &trackIndex)) != nullptr) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700536 sampleConsumers[trackIndex](sample);
Linus Nilssona85df7f2020-02-20 16:32:04 -0800537 }
538 for (trackIndex = 0; trackIndex < mediaSource.mTrackCount; trackIndex++) {
Linus Nilssonc31d2492020-09-23 12:30:00 -0700539 sampleConsumers[trackIndex](newSampleEos());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800540 }
541
542 // Wait for writer.
Linus Nilssone2cdd1f2020-07-07 17:29:26 -0700543 mTestCallbacks->waitForWritingFinished();
Linus Nilssonc31d2492020-09-23 12:30:00 -0700544 EXPECT_TRUE(writer->stop());
Linus Nilssona85df7f2020-02-20 16:32:04 -0800545
546 // Compare output file with source.
547 mediaSource.reset();
548
549 AMediaExtractor* extractor = AMediaExtractor_new();
550 ASSERT_NE(extractor, nullptr);
551
552 int sourceFd = open(destinationPath, O_RDONLY);
553 ASSERT_GT(sourceFd, 0);
554
555 off_t fileSize = lseek(sourceFd, 0, SEEK_END);
556 lseek(sourceFd, 0, SEEK_SET);
557
558 media_status_t status = AMediaExtractor_setDataSourceFd(extractor, sourceFd, 0, fileSize);
559 ASSERT_EQ(status, AMEDIA_OK);
560 close(sourceFd);
561
562 size_t trackCount = AMediaExtractor_getTrackCount(extractor);
563 EXPECT_EQ(trackCount, mediaSource.mTrackCount);
564
565 for (size_t trackIndex = 0; trackIndex < trackCount; trackIndex++) {
566 AMediaFormat* trackFormat = AMediaExtractor_getTrackFormat(extractor, trackIndex);
567 ASSERT_NE(trackFormat, nullptr);
568
569 AMediaExtractor_selectTrack(extractor, trackIndex);
570 }
571
572 // Compare samples.
573 std::shared_ptr<MediaSample> sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
574 std::shared_ptr<MediaSample> sample2 = readSampleAndAdvance(extractor, nullptr);
575
576 while (sample1 != nullptr && sample2 != nullptr) {
577 EXPECT_EQ(sample1->info.presentationTimeUs, sample2->info.presentationTimeUs);
578 EXPECT_EQ(sample1->info.size, sample2->info.size);
579 EXPECT_EQ(sample1->info.flags, sample2->info.flags);
580
581 EXPECT_EQ(memcmp(sample1->buffer, sample2->buffer, sample1->info.size), 0);
582
583 sample1 = readSampleAndAdvance(mediaSource.mExtractor, nullptr);
584 sample2 = readSampleAndAdvance(extractor, nullptr);
585 }
586 EXPECT_EQ(sample1, nullptr);
587 EXPECT_EQ(sample2, nullptr);
588
589 AMediaExtractor_delete(extractor);
590}
591
592} // namespace android
593
594int main(int argc, char** argv) {
595 ::testing::InitGoogleTest(&argc, argv);
596 return RUN_ALL_TESTS();
597}