1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // Unit Test for MediaTrackTranscoder
18 
19 // #define LOG_NDEBUG 0
20 #define LOG_TAG "MediaTrackTranscoderTests"
21 
22 #include <android-base/logging.h>
23 #include <android/binder_process.h>
24 #include <fcntl.h>
25 #include <gtest/gtest.h>
26 #include <media/MediaSampleReaderNDK.h>
27 #include <media/MediaTrackTranscoder.h>
28 #include <media/PassthroughTrackTranscoder.h>
29 #include <media/VideoTrackTranscoder.h>
30 
31 #include "TranscoderTestUtils.h"
32 
33 namespace android {
34 
35 /** TrackTranscoder types to test. */
36 enum TrackTranscoderType {
37     VIDEO,
38     PASSTHROUGH,
39 };
40 
41 class MediaTrackTranscoderTests : public ::testing::TestWithParam<TrackTranscoderType> {
42 public:
MediaTrackTranscoderTests()43     MediaTrackTranscoderTests() { LOG(DEBUG) << "MediaTrackTranscoderTests created"; }
44 
SetUp()45     void SetUp() override {
46         LOG(DEBUG) << "MediaTrackTranscoderTests set up";
47 
48         // Need to start a thread pool to prevent AMediaExtractor binder calls from starving
49         // (b/155663561).
50         ABinderProcess_startThreadPool();
51 
52         mCallback = std::make_shared<TestTrackTranscoderCallback>();
53 
54         switch (GetParam()) {
55         case VIDEO:
56             mTranscoder = VideoTrackTranscoder::create(mCallback);
57             break;
58         case PASSTHROUGH:
59             mTranscoder = std::make_shared<PassthroughTrackTranscoder>(mCallback);
60             break;
61         }
62         ASSERT_NE(mTranscoder, nullptr);
63 
64         initSampleReader("/data/local/tmp/TranscodingTestAssets/cubicle_avc_480x240_aac_24KHz.mp4");
65     }
66 
initSampleReader(const char * sourcePath)67     void initSampleReader(const char* sourcePath) {
68         const int sourceFd = open(sourcePath, O_RDONLY);
69         ASSERT_GT(sourceFd, 0);
70 
71         const size_t fileSize = lseek(sourceFd, 0, SEEK_END);
72         lseek(sourceFd, 0, SEEK_SET);
73 
74         mMediaSampleReader = MediaSampleReaderNDK::createFromFd(sourceFd, 0 /* offset */, fileSize);
75         ASSERT_NE(mMediaSampleReader, nullptr);
76         close(sourceFd);
77 
78         for (size_t trackIndex = 0; trackIndex < mMediaSampleReader->getTrackCount();
79              ++trackIndex) {
80             AMediaFormat* trackFormat = mMediaSampleReader->getTrackFormat(trackIndex);
81             ASSERT_NE(trackFormat, nullptr);
82 
83             const char* mime = nullptr;
84             AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime);
85             ASSERT_NE(mime, nullptr);
86 
87             if (GetParam() == VIDEO && strncmp(mime, "video/", 6) == 0) {
88                 mTrackIndex = trackIndex;
89 
90                 mSourceFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
91                 ASSERT_NE(mSourceFormat, nullptr);
92 
93                 mDestinationFormat =
94                         TrackTranscoderTestUtils::getDefaultVideoDestinationFormat(trackFormat);
95                 ASSERT_NE(mDestinationFormat, nullptr);
96                 break;
97             } else if (GetParam() == PASSTHROUGH && strncmp(mime, "audio/", 6) == 0) {
98                 // TODO(lnilsson): Test metadata track passthrough after hkuang@ provides sample.
99                 mTrackIndex = trackIndex;
100 
101                 mSourceFormat = std::shared_ptr<AMediaFormat>(trackFormat, &AMediaFormat_delete);
102                 ASSERT_NE(mSourceFormat, nullptr);
103                 break;
104             }
105 
106             AMediaFormat_delete(trackFormat);
107         }
108 
109         ASSERT_NE(mSourceFormat, nullptr);
110         EXPECT_EQ(mMediaSampleReader->selectTrack(mTrackIndex), AMEDIA_OK);
111     }
112 
113     // Drains the transcoder's output queue in a loop.
drainOutputSamples(int numSamplesToSave=0)114     void drainOutputSamples(int numSamplesToSave = 0) {
115         mTranscoder->setSampleConsumer(
116                 [this, numSamplesToSave](const std::shared_ptr<MediaSample>& sample) {
117                     ASSERT_NE(sample, nullptr);
118 
119                     mGotEndOfStream = (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) != 0;
120 
121                     if (mSavedSamples.size() < numSamplesToSave) {
122                         mSavedSamples.push_back(sample);
123                     }
124 
125                     if (mSavedSamples.size() == numSamplesToSave || mGotEndOfStream) {
126                         mSamplesSavedSemaphore.signal();
127                     }
128                 });
129     }
130 
TearDown()131     void TearDown() override { LOG(DEBUG) << "MediaTrackTranscoderTests tear down"; }
132 
~MediaTrackTranscoderTests()133     ~MediaTrackTranscoderTests() { LOG(DEBUG) << "MediaTrackTranscoderTests destroyed"; }
134 
135 protected:
136     std::shared_ptr<MediaTrackTranscoder> mTranscoder;
137     std::shared_ptr<TestTrackTranscoderCallback> mCallback;
138 
139     std::shared_ptr<MediaSampleReader> mMediaSampleReader;
140     int mTrackIndex;
141 
142     std::shared_ptr<AMediaFormat> mSourceFormat;
143     std::shared_ptr<AMediaFormat> mDestinationFormat;
144 
145     std::vector<std::shared_ptr<MediaSample>> mSavedSamples;
146     OneShotSemaphore mSamplesSavedSemaphore;
147     bool mGotEndOfStream = false;
148 };
149 
TEST_P(MediaTrackTranscoderTests,WaitNormalOperation)150 TEST_P(MediaTrackTranscoderTests, WaitNormalOperation) {
151     LOG(DEBUG) << "Testing WaitNormalOperation";
152     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
153               AMEDIA_OK);
154     ASSERT_TRUE(mTranscoder->start());
155     drainOutputSamples();
156     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
157     EXPECT_TRUE(mCallback->transcodingFinished());
158     EXPECT_TRUE(mGotEndOfStream);
159 }
160 
TEST_P(MediaTrackTranscoderTests,StopNormalOperation)161 TEST_P(MediaTrackTranscoderTests, StopNormalOperation) {
162     LOG(DEBUG) << "Testing StopNormalOperation";
163 
164     // Use a longer test asset to make sure that transcoding can be stopped.
165     initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
166 
167     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
168               AMEDIA_OK);
169     EXPECT_TRUE(mTranscoder->start());
170     mCallback->waitUntilTrackFormatAvailable();
171     mTranscoder->stop();
172     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
173     EXPECT_TRUE(mCallback->transcodingWasStopped());
174 }
175 
TEST_P(MediaTrackTranscoderTests,StartWithoutConfigure)176 TEST_P(MediaTrackTranscoderTests, StartWithoutConfigure) {
177     LOG(DEBUG) << "Testing StartWithoutConfigure";
178     EXPECT_FALSE(mTranscoder->start());
179 }
180 
TEST_P(MediaTrackTranscoderTests,StopWithoutStart)181 TEST_P(MediaTrackTranscoderTests, StopWithoutStart) {
182     LOG(DEBUG) << "Testing StopWithoutStart";
183     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
184               AMEDIA_OK);
185     mTranscoder->stop();
186 }
187 
TEST_P(MediaTrackTranscoderTests,DoubleStartStop)188 TEST_P(MediaTrackTranscoderTests, DoubleStartStop) {
189     LOG(DEBUG) << "Testing DoubleStartStop";
190 
191     // Use a longer test asset to make sure that transcoding can be stopped.
192     initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
193 
194     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
195               AMEDIA_OK);
196     EXPECT_TRUE(mTranscoder->start());
197     EXPECT_FALSE(mTranscoder->start());
198     mTranscoder->stop();
199     mTranscoder->stop();
200     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
201     EXPECT_TRUE(mCallback->transcodingWasStopped());
202 }
203 
TEST_P(MediaTrackTranscoderTests,DoubleConfigure)204 TEST_P(MediaTrackTranscoderTests, DoubleConfigure) {
205     LOG(DEBUG) << "Testing DoubleConfigure";
206     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
207               AMEDIA_OK);
208     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
209               AMEDIA_ERROR_UNSUPPORTED);
210 }
211 
TEST_P(MediaTrackTranscoderTests,ConfigureAfterFail)212 TEST_P(MediaTrackTranscoderTests, ConfigureAfterFail) {
213     LOG(DEBUG) << "Testing ConfigureAfterFail";
214     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, -1, mDestinationFormat),
215               AMEDIA_ERROR_INVALID_PARAMETER);
216     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
217               AMEDIA_OK);
218 }
219 
TEST_P(MediaTrackTranscoderTests,RestartAfterStop)220 TEST_P(MediaTrackTranscoderTests, RestartAfterStop) {
221     LOG(DEBUG) << "Testing RestartAfterStop";
222     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
223               AMEDIA_OK);
224     EXPECT_TRUE(mTranscoder->start());
225     mTranscoder->stop();
226     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
227     EXPECT_FALSE(mTranscoder->start());
228 }
229 
TEST_P(MediaTrackTranscoderTests,RestartAfterFinish)230 TEST_P(MediaTrackTranscoderTests, RestartAfterFinish) {
231     LOG(DEBUG) << "Testing RestartAfterFinish";
232     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
233               AMEDIA_OK);
234     ASSERT_TRUE(mTranscoder->start());
235     drainOutputSamples();
236     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
237     mTranscoder->stop();
238     EXPECT_FALSE(mTranscoder->start());
239     EXPECT_TRUE(mGotEndOfStream);
240 }
241 
TEST_P(MediaTrackTranscoderTests,HoldSampleAfterTranscoderRelease)242 TEST_P(MediaTrackTranscoderTests, HoldSampleAfterTranscoderRelease) {
243     LOG(DEBUG) << "Testing HoldSampleAfterTranscoderRelease";
244     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
245               AMEDIA_OK);
246     ASSERT_TRUE(mTranscoder->start());
247     drainOutputSamples(1 /* numSamplesToSave */);
248     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
249     mTranscoder->stop();
250     EXPECT_TRUE(mGotEndOfStream);
251 
252     mTranscoder.reset();
253 
254     std::this_thread::sleep_for(std::chrono::milliseconds(20));
255     mSavedSamples.clear();
256 }
257 
TEST_P(MediaTrackTranscoderTests,HoldSampleAfterTranscoderStop)258 TEST_P(MediaTrackTranscoderTests, HoldSampleAfterTranscoderStop) {
259     LOG(DEBUG) << "Testing HoldSampleAfterTranscoderStop";
260     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
261               AMEDIA_OK);
262     ASSERT_TRUE(mTranscoder->start());
263     drainOutputSamples(1 /* numSamplesToSave */);
264     mSamplesSavedSemaphore.wait();
265     mTranscoder->stop();
266     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
267 
268     std::this_thread::sleep_for(std::chrono::milliseconds(20));
269     mSavedSamples.clear();
270 }
271 
TEST_P(MediaTrackTranscoderTests,NullSampleReader)272 TEST_P(MediaTrackTranscoderTests, NullSampleReader) {
273     LOG(DEBUG) << "Testing NullSampleReader";
274     std::shared_ptr<MediaSampleReader> nullSampleReader;
275     EXPECT_NE(mTranscoder->configure(nullSampleReader, mTrackIndex, mDestinationFormat), AMEDIA_OK);
276     ASSERT_FALSE(mTranscoder->start());
277 }
278 
TEST_P(MediaTrackTranscoderTests,InvalidTrackIndex)279 TEST_P(MediaTrackTranscoderTests, InvalidTrackIndex) {
280     LOG(DEBUG) << "Testing InvalidTrackIndex";
281     EXPECT_NE(mTranscoder->configure(mMediaSampleReader, -1, mDestinationFormat), AMEDIA_OK);
282     EXPECT_NE(mTranscoder->configure(mMediaSampleReader, mMediaSampleReader->getTrackCount(),
283                                      mDestinationFormat),
284               AMEDIA_OK);
285 }
286 
TEST_P(MediaTrackTranscoderTests,StopOnSync)287 TEST_P(MediaTrackTranscoderTests, StopOnSync) {
288     LOG(DEBUG) << "Testing StopOnSync";
289 
290     // Use a longer test asset to make sure there is a GOP to finish.
291     initSampleReader("/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4");
292 
293     EXPECT_EQ(mTranscoder->configure(mMediaSampleReader, mTrackIndex, mDestinationFormat),
294               AMEDIA_OK);
295 
296     bool lastSampleWasEos = false;
297     bool lastRealSampleWasSync = false;
298     OneShotSemaphore samplesReceivedSemaphore;
299     uint32_t sampleCount = 0;
300 
301     mTranscoder->setSampleConsumer([&](const std::shared_ptr<MediaSample>& sample) {
302         ASSERT_NE(sample, nullptr);
303 
304         if ((lastSampleWasEos = sample->info.flags & SAMPLE_FLAG_END_OF_STREAM)) {
305             samplesReceivedSemaphore.signal();
306             return;
307         }
308         lastRealSampleWasSync = sample->info.flags & SAMPLE_FLAG_SYNC_SAMPLE;
309 
310         if (++sampleCount >= 10) {  // Wait for a few samples before stopping.
311             samplesReceivedSemaphore.signal();
312         }
313     });
314 
315     ASSERT_TRUE(mTranscoder->start());
316     samplesReceivedSemaphore.wait();
317     mTranscoder->stop(true /* stopOnSync */);
318     EXPECT_EQ(mCallback->waitUntilFinished(), AMEDIA_OK);
319 
320     EXPECT_TRUE(lastSampleWasEos);
321     EXPECT_TRUE(lastRealSampleWasSync);
322     EXPECT_TRUE(mCallback->transcodingWasStopped());
323 }
324 
325 };  // namespace android
326 
327 using namespace android;
328 
329 INSTANTIATE_TEST_SUITE_P(MediaTrackTranscoderTestsAll, MediaTrackTranscoderTests,
330                          ::testing::Values(VIDEO, PASSTHROUGH));
331 
main(int argc,char ** argv)332 int main(int argc, char** argv) {
333     ::testing::InitGoogleTest(&argc, argv);
334     return RUN_ALL_TESTS();
335 }
336