1 /*
2  * Copyright (C) 2019 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "ExtractorUnitTest"
19 #include <utils/Log.h>
20 
21 #include <inttypes.h>
22 
23 #include <datasource/FileSource.h>
24 #include <media/stagefright/MediaBufferGroup.h>
25 #include <media/stagefright/MediaCodecConstants.h>
26 #include <media/stagefright/MediaDefs.h>
27 #include <media/stagefright/MetaDataUtils.h>
28 #include <media/stagefright/foundation/OpusHeader.h>
29 
30 #include <AACExtractor.h>
31 #include <AMRExtractor.h>
32 #include <FLACExtractor.h>
33 #include <MidiExtractor.h>
34 #include <MatroskaExtractor.h>
35 #include <MP3Extractor.h>
36 #include <MPEG4Extractor.h>
37 #include <SampleTable.h>
38 #include <MPEG2PSExtractor.h>
39 #include <MPEG2TSExtractor.h>
40 #include <OggExtractor.h>
41 #include <WAVExtractor.h>
42 
43 #include "ExtractorUnitTestEnvironment.h"
44 
45 using namespace android;
46 
47 #define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput"
48 
49 constexpr int32_t kMaxCount = 10;
50 constexpr int32_t kAudioDefaultSampleDuration = 20000;                       // 20ms
51 constexpr int32_t kRandomSeekToleranceUs = 2 * kAudioDefaultSampleDuration;  // 40 ms;
52 constexpr int32_t kRandomSeed = 700;
53 constexpr int32_t kUndefined = -1;
54 
55 enum inputID {
56     // audio streams
57     AAC_1,
58     AMR_NB_1,
59     AMR_WB_1,
60     FLAC_1,
61     GSM_1,
62     MIDI_1,
63     MP3_1,
64     OPUS_1,
65     VORBIS_1,
66     // video streams
67     HEVC_1,
68     HEVC_2,
69     MPEG2_PS_1,
70     MPEG2_TS_1,
71     MPEG4_1,
72     VP9_1,
73     UNKNOWN_ID,
74 };
75 
76 // LookUpTable of clips and metadata for component testing
77 static const struct InputData {
78     inputID inpId;
79     string mime;
80     string inputFile;
81     int32_t firstParam;
82     int32_t secondParam;
83     int32_t profile;
84     int32_t frameRate;
85 } kInputData[] = {
86         {AAC_1, MEDIA_MIMETYPE_AUDIO_AAC, "test_mono_44100Hz_aac.aac", 44100, 1, AACObjectLC,
87          kUndefined},
88         {AMR_NB_1, MEDIA_MIMETYPE_AUDIO_AMR_NB, "bbb_mono_8kHz_amrnb.amr", 8000, 1, kUndefined,
89          kUndefined},
90         {AMR_WB_1, MEDIA_MIMETYPE_AUDIO_AMR_WB, "bbb_mono_16kHz_amrwb.amr", 16000, 1, kUndefined,
91          kUndefined},
92         {FLAC_1, MEDIA_MIMETYPE_AUDIO_RAW, "bbb_stereo_48kHz_flac.flac", 48000, 2, kUndefined,
93          kUndefined},
94         {GSM_1, MEDIA_MIMETYPE_AUDIO_MSGSM, "test_mono_8kHz_gsm.wav", 8000, 1, kUndefined,
95          kUndefined},
96         {MIDI_1, MEDIA_MIMETYPE_AUDIO_RAW, "midi_a.mid", 22050, 2, kUndefined, kUndefined},
97         {MP3_1, MEDIA_MIMETYPE_AUDIO_MPEG, "bbb_stereo_48kHz_mp3.mp3", 48000, 2, kUndefined,
98          kUndefined},
99         {OPUS_1, MEDIA_MIMETYPE_AUDIO_OPUS, "test_stereo_48kHz_opus.opus", 48000, 2, kUndefined,
100          kUndefined},
101         {VORBIS_1, MEDIA_MIMETYPE_AUDIO_VORBIS, "bbb_stereo_48kHz_vorbis.ogg", 48000, 2, kUndefined,
102          kUndefined},
103 
104         // Test (b/151677264) for MP4 extractor
105         {HEVC_1, MEDIA_MIMETYPE_VIDEO_HEVC, "crowd_508x240_25fps_hevc.mp4", 508, 240,
106          HEVCProfileMain, 25},
107         {HEVC_2, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, "test3.heic", 820, 460, kUndefined, kUndefined},
108         {MPEG2_PS_1, MEDIA_MIMETYPE_VIDEO_MPEG2, "swirl_144x136_mpeg2.mpg", 144, 136,
109          MPEG2ProfileMain, 12},
110         {MPEG2_TS_1, MEDIA_MIMETYPE_VIDEO_MPEG2, "bbb_cif_768kbps_30fps_mpeg2.ts", 352, 288,
111          MPEG2ProfileMain, 30},
112         {MPEG4_1, MEDIA_MIMETYPE_VIDEO_MPEG4, "bbb_cif_768kbps_30fps_mpeg4.mkv", 352, 288,
113          MPEG4ProfileSimple, 30},
114         {VP9_1, MEDIA_MIMETYPE_VIDEO_VP9, "bbb_340x280_30fps_vp9.webm", 340, 280, VP9Profile0, 30},
115 };
116 
117 static ExtractorUnitTestEnvironment *gEnv = nullptr;
118 
119 class ExtractorUnitTest {
120   public:
ExtractorUnitTest()121     ExtractorUnitTest() : mInputFp(nullptr), mDataSource(nullptr), mExtractor(nullptr) {}
122 
~ExtractorUnitTest()123     ~ExtractorUnitTest() {
124         if (mInputFp) {
125             fclose(mInputFp);
126             mInputFp = nullptr;
127         }
128         if (mDataSource) {
129             mDataSource.clear();
130             mDataSource = nullptr;
131         }
132         if (mExtractor) {
133             delete mExtractor;
134             mExtractor = nullptr;
135         }
136     }
137 
setupExtractor(string writerFormat)138     void setupExtractor(string writerFormat) {
139         mExtractorName = unknown_comp;
140         mDisableTest = false;
141 
142         static const std::map<std::string, standardExtractors> mapExtractor = {
143                 {"aac", AAC},
144                 {"amr", AMR},
145                 {"flac", FLAC},
146                 {"mid", MIDI},
147                 {"midi", MIDI},
148                 {"mkv", MKV},
149                 {"mp3", MP3},
150                 {"mp4", MPEG4},
151                 {"mpeg2ps", MPEG2PS},
152                 {"mpeg2ts", MPEG2TS},
153                 {"mpeg4", MPEG4},
154                 {"mpg", MPEG2PS},
155                 {"ogg", OGG},
156                 {"opus", OGG},
157                 {"ts", MPEG2TS},
158                 {"wav", WAV},
159                 {"webm", MKV}};
160         // Find the component type
161         if (mapExtractor.find(writerFormat) != mapExtractor.end()) {
162             mExtractorName = mapExtractor.at(writerFormat);
163         }
164         if (mExtractorName == standardExtractors::unknown_comp) {
165             cout << "[   WARN   ] Test Skipped. Invalid extractor\n";
166             mDisableTest = true;
167         }
168     }
169 
170     int32_t setDataSource(string inputFileName);
171 
172     int32_t createExtractor();
173 
174     enum standardExtractors {
175         AAC,
176         AMR,
177         FLAC,
178         MIDI,
179         MKV,
180         MP3,
181         MPEG4,
182         MPEG2PS,
183         MPEG2TS,
184         OGG,
185         WAV,
186         unknown_comp,
187     };
188 
189     bool mDisableTest;
190     standardExtractors mExtractorName;
191 
192     FILE *mInputFp;
193     sp<DataSource> mDataSource;
194     MediaExtractorPluginHelper *mExtractor;
195 };
196 
197 class ExtractorFunctionalityTest
198     : public ExtractorUnitTest,
199       public ::testing::TestWithParam<tuple<string /* container */, string /* InputFile */,
200                                             int32_t /* numTracks */, bool /* seekSupported */>> {
201   public:
SetUp()202     virtual void SetUp() override {
203         tuple<string, string, int32_t, bool> params = GetParam();
204         mContainer = get<0>(params);
205         mNumTracks = get<2>(params);
206         setupExtractor(mContainer);
207     }
208     string mContainer;
209     int32_t mNumTracks;
210 };
211 
212 class ConfigParamTest : public ExtractorUnitTest,
213                         public ::testing::TestWithParam<pair<string, inputID>> {
214   public:
SetUp()215     virtual void SetUp() override { setupExtractor(GetParam().first); }
216 
217     struct configFormat {
218         string mime;
219         int32_t width;
220         int32_t height;
221         int32_t sampleRate;
222         int32_t channelCount;
223         int32_t profile;
224         int32_t frameRate;
225     };
226 
227     void getFileProperties(inputID inputId, string &inputFile, configFormat &configParam);
228 };
229 
setDataSource(string inputFileName)230 int32_t ExtractorUnitTest::setDataSource(string inputFileName) {
231     mInputFp = fopen(inputFileName.c_str(), "rb");
232     if (!mInputFp) {
233         ALOGE("Unable to open input file for reading");
234         return -1;
235     }
236     struct stat buf;
237     stat(inputFileName.c_str(), &buf);
238     int32_t fd = fileno(mInputFp);
239     mDataSource = new FileSource(dup(fd), 0, buf.st_size);
240     if (!mDataSource) return -1;
241     return 0;
242 }
243 
createExtractor()244 int32_t ExtractorUnitTest::createExtractor() {
245     switch (mExtractorName) {
246         case AAC:
247             mExtractor = new AACExtractor(new DataSourceHelper(mDataSource->wrap()), 0);
248             break;
249         case AMR:
250             mExtractor = new AMRExtractor(new DataSourceHelper(mDataSource->wrap()));
251             break;
252         case MP3:
253             mExtractor = new MP3Extractor(new DataSourceHelper(mDataSource->wrap()), nullptr);
254             break;
255         case OGG:
256             mExtractor = new OggExtractor(new DataSourceHelper(mDataSource->wrap()));
257             break;
258         case WAV:
259             mExtractor = new WAVExtractor(new DataSourceHelper(mDataSource->wrap()));
260             break;
261         case MKV:
262             mExtractor = new MatroskaExtractor(new DataSourceHelper(mDataSource->wrap()));
263             break;
264         case FLAC:
265             mExtractor = new FLACExtractor(new DataSourceHelper(mDataSource->wrap()));
266             break;
267         case MPEG4:
268             mExtractor = new MPEG4Extractor(new DataSourceHelper(mDataSource->wrap()));
269             break;
270         case MPEG2TS:
271             mExtractor = new MPEG2TSExtractor(new DataSourceHelper(mDataSource->wrap()));
272             break;
273         case MPEG2PS:
274             mExtractor = new MPEG2PSExtractor(new DataSourceHelper(mDataSource->wrap()));
275             break;
276         case MIDI:
277             mExtractor = new MidiExtractor(mDataSource->wrap());
278             break;
279         default:
280             return -1;
281     }
282     if (!mExtractor) return -1;
283     return 0;
284 }
285 
getFileProperties(inputID inputId,string & inputFile,configFormat & configParam)286 void ConfigParamTest::getFileProperties(inputID inputId, string &inputFile,
287                                         configFormat &configParam) {
288     int32_t inputDataSize = sizeof(kInputData) / sizeof(kInputData[0]);
289     int32_t inputIdx = 0;
290     for (; inputIdx < inputDataSize; inputIdx++) {
291         if (inputId == kInputData[inputIdx].inpId) {
292             break;
293         }
294     }
295     if (inputIdx == inputDataSize) {
296         return;
297     }
298     inputFile += kInputData[inputIdx].inputFile;
299     configParam.mime = kInputData[inputIdx].mime;
300     size_t found = configParam.mime.find("audio/");
301     // Check if 'audio/' is present in the begininig of the mime type
302     if (found == 0) {
303         configParam.sampleRate = kInputData[inputIdx].firstParam;
304         configParam.channelCount = kInputData[inputIdx].secondParam;
305     } else {
306         configParam.width = kInputData[inputIdx].firstParam;
307         configParam.height = kInputData[inputIdx].secondParam;
308     }
309     configParam.profile = kInputData[inputIdx].profile;
310     configParam.frameRate = kInputData[inputIdx].frameRate;
311     return;
312 }
313 
randomSeekTest(MediaTrackHelper * track,int64_t clipDuration)314 void randomSeekTest(MediaTrackHelper *track, int64_t clipDuration) {
315     int32_t status = 0;
316     int32_t seekCount = 0;
317     bool hasTimestamp = false;
318     vector<int64_t> seekToTimeStamp;
319     string seekPtsString;
320 
321     srand(kRandomSeed);
322     while (seekCount < kMaxCount) {
323         int64_t timeStamp = ((double)rand() / RAND_MAX) * clipDuration;
324         seekToTimeStamp.push_back(timeStamp);
325         seekPtsString.append(to_string(timeStamp));
326         seekPtsString.append(", ");
327         seekCount++;
328     }
329 
330     for (int64_t seekPts : seekToTimeStamp) {
331         MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
332                 CMediaTrackReadOptions::SEEK_CLOSEST | CMediaTrackReadOptions::SEEK, seekPts);
333         ASSERT_NE(options, nullptr) << "Cannot create read option";
334 
335         MediaBufferHelper *buffer = nullptr;
336         status = track->read(&buffer, options);
337         if (buffer) {
338             AMediaFormat *metaData = buffer->meta_data();
339             int64_t timeStamp = 0;
340             hasTimestamp = AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
341             ASSERT_TRUE(hasTimestamp) << "Extractor didn't set timestamp for the given sample";
342 
343             buffer->release();
344             EXPECT_LE(abs(timeStamp - seekPts), kRandomSeekToleranceUs)
345                     << "Seek unsuccessful. Expected timestamp range ["
346                     << seekPts - kRandomSeekToleranceUs << ", " << seekPts + kRandomSeekToleranceUs
347                     << "] "
348                     << "received " << timeStamp << ", list of input seek timestamps ["
349                     << seekPtsString << "]";
350         }
351         delete options;
352     }
353 }
354 
getSeekablePoints(vector<int64_t> & seekablePoints,MediaTrackHelper * track)355 void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) {
356     int32_t status = 0;
357     if (!seekablePoints.empty()) {
358         seekablePoints.clear();
359     }
360     int64_t timeStamp;
361     while (status != AMEDIA_ERROR_END_OF_STREAM) {
362         MediaBufferHelper *buffer = nullptr;
363         status = track->read(&buffer);
364         if (buffer) {
365             AMediaFormat *metaData = buffer->meta_data();
366             int32_t isSync = 0;
367             AMediaFormat_getInt32(metaData, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, &isSync);
368             if (isSync) {
369                 AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
370                 seekablePoints.push_back(timeStamp);
371             }
372             buffer->release();
373         }
374     }
375 }
376 
TEST_P(ExtractorFunctionalityTest,CreateExtractorTest)377 TEST_P(ExtractorFunctionalityTest, CreateExtractorTest) {
378     if (mDisableTest) return;
379 
380     ALOGV("Checks if a valid extractor is created for a given input file");
381     string inputFileName = gEnv->getRes() + get<1>(GetParam());
382 
383     int32_t status = setDataSource(inputFileName);
384     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
385 
386     status = createExtractor();
387     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
388 
389     int32_t numTracks = mExtractor->countTracks();
390     ASSERT_EQ(numTracks, mNumTracks)
391             << "Extractor reported wrong number of track for the given clip";
392 
393     AMediaFormat *format = AMediaFormat_new();
394     ASSERT_NE(format, nullptr) << "AMediaFormat_new returned null AMediaformat";
395 
396     ASSERT_EQ(mExtractor->getMetaData(format), AMEDIA_OK);
397     AMediaFormat_delete(format);
398 }
399 
TEST_P(ExtractorFunctionalityTest,ExtractorTest)400 TEST_P(ExtractorFunctionalityTest, ExtractorTest) {
401     if (mDisableTest) return;
402 
403     ALOGV("Validates %s Extractor for a given input file", mContainer.c_str());
404     string inputFileName = gEnv->getRes() + get<1>(GetParam());
405 
406     int32_t status = setDataSource(inputFileName);
407     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
408 
409     status = createExtractor();
410     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
411 
412     int32_t numTracks = mExtractor->countTracks();
413     ASSERT_EQ(numTracks, mNumTracks)
414             << "Extractor reported wrong number of track for the given clip";
415 
416     for (int32_t idx = 0; idx < numTracks; idx++) {
417         MediaTrackHelper *track = mExtractor->getTrack(idx);
418         ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
419 
420         CMediaTrack *cTrack = wrap(track);
421         ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
422 
423         MediaBufferGroup *bufferGroup = new MediaBufferGroup();
424         status = cTrack->start(track, bufferGroup->wrap());
425         ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
426 
427         FILE *outFp = fopen((OUTPUT_DUMP_FILE + to_string(idx)).c_str(), "wb");
428         if (!outFp) {
429             ALOGW("Unable to open output file for dumping extracted stream");
430         }
431 
432         while (status != AMEDIA_ERROR_END_OF_STREAM) {
433             MediaBufferHelper *buffer = nullptr;
434             status = track->read(&buffer);
435             ALOGV("track->read Status = %d buffer %p", status, buffer);
436             if (buffer) {
437                 ALOGV("buffer->data %p buffer->size() %zu buffer->range_length() %zu",
438                       buffer->data(), buffer->size(), buffer->range_length());
439                 if (outFp) fwrite(buffer->data(), 1, buffer->range_length(), outFp);
440                 buffer->release();
441             }
442         }
443         if (outFp) fclose(outFp);
444         status = cTrack->stop(track);
445         ASSERT_EQ(OK, status) << "Failed to stop the track";
446         delete bufferGroup;
447         delete track;
448     }
449 }
450 
TEST_P(ExtractorFunctionalityTest,MetaDataComparisonTest)451 TEST_P(ExtractorFunctionalityTest, MetaDataComparisonTest) {
452     if (mDisableTest) return;
453 
454     ALOGV("Validates Extractor's meta data for a given input file");
455     string inputFileName = gEnv->getRes() + get<1>(GetParam());
456 
457     int32_t status = setDataSource(inputFileName);
458     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
459 
460     status = createExtractor();
461     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
462 
463     int32_t numTracks = mExtractor->countTracks();
464     ASSERT_EQ(numTracks, mNumTracks)
465             << "Extractor reported wrong number of track for the given clip";
466 
467     AMediaFormat *extractorFormat = AMediaFormat_new();
468     ASSERT_NE(extractorFormat, nullptr) << "AMediaFormat_new returned null AMediaformat";
469     AMediaFormat *trackFormat = AMediaFormat_new();
470     ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null AMediaformat";
471 
472     for (int32_t idx = 0; idx < numTracks; idx++) {
473         MediaTrackHelper *track = mExtractor->getTrack(idx);
474         ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
475 
476         CMediaTrack *cTrack = wrap(track);
477         ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
478 
479         MediaBufferGroup *bufferGroup = new MediaBufferGroup();
480         status = cTrack->start(track, bufferGroup->wrap());
481         ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
482 
483         status = mExtractor->getTrackMetaData(extractorFormat, idx, 1);
484         ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData";
485 
486         status = track->getFormat(trackFormat);
487         ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
488 
489         const char *extractorMime, *trackMime;
490         AMediaFormat_getString(extractorFormat, AMEDIAFORMAT_KEY_MIME, &extractorMime);
491         AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &trackMime);
492         ASSERT_TRUE(!strcmp(extractorMime, trackMime))
493                 << "Extractor's format doesn't match track format";
494 
495         if (!strncmp(extractorMime, "audio/", 6)) {
496             int32_t exSampleRate, exChannelCount;
497             int32_t trackSampleRate, trackChannelCount;
498             ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
499                                               &exChannelCount));
500             ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE,
501                                               &exSampleRate));
502             ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
503                                               &trackChannelCount));
504             ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE,
505                                               &trackSampleRate));
506             ASSERT_EQ(exChannelCount, trackChannelCount) << "ChannelCount not as expected";
507             ASSERT_EQ(exSampleRate, trackSampleRate) << "SampleRate not as expected";
508         } else if (!strncmp(extractorMime, "video/", 6)) {
509             int32_t exWidth, exHeight;
510             int32_t trackWidth, trackHeight;
511             ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_WIDTH, &exWidth));
512             ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_HEIGHT, &exHeight));
513             ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth));
514             ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
515             ASSERT_EQ(exWidth, trackWidth) << "Width not as expected";
516             ASSERT_EQ(exHeight, trackHeight) << "Height not as expected";
517         } else {
518             ALOGV("non a/v track");
519         }
520         status = cTrack->stop(track);
521         ASSERT_EQ(OK, status) << "Failed to stop the track";
522         delete bufferGroup;
523         delete track;
524     }
525     AMediaFormat_delete(trackFormat);
526     AMediaFormat_delete(extractorFormat);
527 }
528 
TEST_P(ExtractorFunctionalityTest,MultipleStartStopTest)529 TEST_P(ExtractorFunctionalityTest, MultipleStartStopTest) {
530     if (mDisableTest) return;
531 
532     ALOGV("Test %s extractor for multiple start and stop calls", mContainer.c_str());
533     string inputFileName = gEnv->getRes() + get<1>(GetParam());
534 
535     int32_t status = setDataSource(inputFileName);
536     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
537 
538     status = createExtractor();
539     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
540 
541     int32_t numTracks = mExtractor->countTracks();
542     ASSERT_EQ(numTracks, mNumTracks)
543             << "Extractor reported wrong number of track for the given clip";
544 
545     // start/stop the tracks multiple times
546     for (int32_t count = 0; count < kMaxCount; count++) {
547         for (int32_t idx = 0; idx < numTracks; idx++) {
548             MediaTrackHelper *track = mExtractor->getTrack(idx);
549             ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
550 
551             CMediaTrack *cTrack = wrap(track);
552             ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
553 
554             MediaBufferGroup *bufferGroup = new MediaBufferGroup();
555             status = cTrack->start(track, bufferGroup->wrap());
556             ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
557             MediaBufferHelper *buffer = nullptr;
558             status = track->read(&buffer);
559             if (buffer) {
560                 ALOGV("buffer->data %p buffer->size() %zu buffer->range_length() %zu",
561                       buffer->data(), buffer->size(), buffer->range_length());
562                 buffer->release();
563             }
564             status = cTrack->stop(track);
565             ASSERT_EQ(OK, status) << "Failed to stop the track";
566             delete bufferGroup;
567             delete track;
568         }
569     }
570 }
571 
TEST_P(ExtractorFunctionalityTest,SeekTest)572 TEST_P(ExtractorFunctionalityTest, SeekTest) {
573     if (mDisableTest) return;
574 
575     string inputFileName = gEnv->getRes() + get<1>(GetParam());
576     ALOGV("Validates %s Extractor behaviour for different seek modes filename %s",
577           mContainer.c_str(), inputFileName.c_str());
578 
579     int32_t status = setDataSource(inputFileName);
580     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
581 
582     status = createExtractor();
583     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
584 
585     int32_t numTracks = mExtractor->countTracks();
586     ASSERT_EQ(numTracks, mNumTracks)
587             << "Extractor reported wrong number of track for the given clip";
588 
589     uint32_t seekFlag = mExtractor->flags();
590     bool seekSupported = get<3>(GetParam());
591     bool seekable = seekFlag & MediaExtractorPluginHelper::CAN_SEEK;
592     if (!seekable) {
593         ASSERT_FALSE(seekSupported) << mContainer << "Extractor is expected to support seek ";
594         cout << "[   WARN   ] Test Skipped. " << mContainer << " Extractor doesn't support seek\n";
595         return;
596     }
597 
598     vector<int64_t> seekablePoints;
599     for (int32_t idx = 0; idx < numTracks; idx++) {
600         MediaTrackHelper *track = mExtractor->getTrack(idx);
601         ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
602 
603         CMediaTrack *cTrack = wrap(track);
604         ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
605 
606         // Get all the seekable points of a given input
607         MediaBufferGroup *bufferGroup = new MediaBufferGroup();
608         status = cTrack->start(track, bufferGroup->wrap());
609         ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
610 
611         // For Flac, Wav and Midi extractor, all samples are seek points.
612         // We cannot create list of all seekable points for these.
613         // This means that if we pass a seekToTimeStamp between two seek points, we may
614         // end up getting the timestamp of next sample as a seekable timestamp.
615         // This timestamp may/may not be a part of the seekable point vector thereby failing the
616         // test. So we test these extractors using random seek test.
617         if (mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) {
618             AMediaFormat *trackMeta = AMediaFormat_new();
619             ASSERT_NE(trackMeta, nullptr) << "AMediaFormat_new returned null AMediaformat";
620 
621             status = mExtractor->getTrackMetaData(trackMeta, idx, 1);
622             ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData";
623 
624             int64_t clipDuration = 0;
625             AMediaFormat_getInt64(trackMeta, AMEDIAFORMAT_KEY_DURATION, &clipDuration);
626             ASSERT_GT(clipDuration, 0) << "Invalid clip duration ";
627             randomSeekTest(track, clipDuration);
628             AMediaFormat_delete(trackMeta);
629             continue;
630         }
631 
632         AMediaFormat *trackFormat = AMediaFormat_new();
633         ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null format";
634         status = track->getFormat(trackFormat);
635         ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
636 
637         const char *mime;
638         ASSERT_TRUE(AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime))
639                 << "Failed to get mime";
640 
641         // Image formats are not expected to be seekable
642         if (!strncmp(mime, "image/", 6)) continue;
643 
644         // Request seekable points for remaining extractors which will be used to validate the seek
645         // accuracy for the extractors. Depending on SEEK Mode, we expect the extractors to return
646         // the expected sync frame. We don't prefer random seek test for these extractors because
647         // they aren't expected to seek to random samples. MP4 for instance can seek to
648         // next/previous sync frames but not to samples between two sync frames.
649         getSeekablePoints(seekablePoints, track);
650         ASSERT_GT(seekablePoints.size(), 0)
651                 << "Failed to get seekable points for " << mContainer << " extractor";
652 
653         bool isOpus = false;
654         int64_t opusSeekPreRollUs = 0;
655         if (!strcmp(mime, "audio/opus")) {
656             isOpus = true;
657             void *seekPreRollBuf = nullptr;
658             size_t size = 0;
659             if (!AMediaFormat_getBuffer(trackFormat, "csd-2", &seekPreRollBuf, &size)) {
660                 size_t opusHeadSize = 0;
661                 size_t codecDelayBufSize = 0;
662                 size_t seekPreRollBufSize = 0;
663                 void *csdBuffer = nullptr;
664                 void *opusHeadBuf = nullptr;
665                 void *codecDelayBuf = nullptr;
666                 AMediaFormat_getBuffer(trackFormat, "csd-0", &csdBuffer, &size);
667                 ASSERT_NE(csdBuffer, nullptr);
668 
669                 GetOpusHeaderBuffers((uint8_t *)csdBuffer, size, &opusHeadBuf, &opusHeadSize,
670                                      &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf,
671                                      &seekPreRollBufSize);
672             }
673             ASSERT_NE(seekPreRollBuf, nullptr)
674                     << "Invalid track format. SeekPreRoll info missing for Opus file";
675             opusSeekPreRollUs = *((int64_t *)seekPreRollBuf);
676         }
677         AMediaFormat_delete(trackFormat);
678 
679         int32_t seekIdx = 0;
680         size_t seekablePointsSize = seekablePoints.size();
681         for (int32_t mode = CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC;
682              mode <= CMediaTrackReadOptions::SEEK_CLOSEST; mode++) {
683             for (int32_t seekCount = 0; seekCount < kMaxCount; seekCount++) {
684                 seekIdx = rand() % seekablePointsSize + 1;
685                 if (seekIdx >= seekablePointsSize) seekIdx = seekablePointsSize - 1;
686 
687                 int64_t seekToTimeStamp = seekablePoints[seekIdx];
688                 if (seekIdx > 1) {
689                     // pick a time just earlier than this seek point
690                     int64_t prevTimeStamp = seekablePoints[seekIdx - 1];
691                     seekToTimeStamp = seekToTimeStamp - ((seekToTimeStamp - prevTimeStamp) >> 3);
692                 }
693 
694                 // Opus has a seekPreRollUs. TimeStamp returned by the
695                 // extractor is calculated based on (seekPts - seekPreRollUs).
696                 // So we add the preRoll value to the timeStamp we want to seek to.
697                 if (isOpus) {
698                     seekToTimeStamp += opusSeekPreRollUs;
699                 }
700 
701                 MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
702                         mode | CMediaTrackReadOptions::SEEK, seekToTimeStamp);
703                 ASSERT_NE(options, nullptr) << "Cannot create read option";
704 
705                 MediaBufferHelper *buffer = nullptr;
706                 status = track->read(&buffer, options);
707                 if (status == AMEDIA_ERROR_END_OF_STREAM) {
708                     delete options;
709                     continue;
710                 }
711                 if (buffer) {
712                     AMediaFormat *metaData = buffer->meta_data();
713                     int64_t timeStamp;
714                     AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
715                     buffer->release();
716 
717                     // CMediaTrackReadOptions::SEEK is 8. Using mask 0111b to get true modes
718                     switch (mode & 0x7) {
719                         case CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC:
720                             EXPECT_EQ(timeStamp, seekablePoints[seekIdx > 0 ? (seekIdx - 1) : 0]);
721                             break;
722                         case CMediaTrackReadOptions::SEEK_NEXT_SYNC:
723                         case CMediaTrackReadOptions::SEEK_CLOSEST_SYNC:
724                         case CMediaTrackReadOptions::SEEK_CLOSEST:
725                             EXPECT_EQ(timeStamp, seekablePoints[seekIdx]);
726                             break;
727                         default:
728                             break;
729                     }
730                 }
731                 delete options;
732             }
733         }
734         status = cTrack->stop(track);
735         ASSERT_EQ(OK, status) << "Failed to stop the track";
736         delete bufferGroup;
737         delete track;
738     }
739     seekablePoints.clear();
740 }
741 
742 // Tests the extractors for seek beyond range : (0, ClipDuration)
TEST_P(ExtractorFunctionalityTest,MonkeySeekTest)743 TEST_P(ExtractorFunctionalityTest, MonkeySeekTest) {
744     if (mDisableTest) return;
745     // TODO(b/155630778): Enable test for wav extractors
746     if (mExtractorName == WAV) return;
747 
748     string inputFileName = gEnv->getRes() + get<1>(GetParam());
749     ALOGV("Validates %s Extractor behaviour for invalid seek points, filename %s",
750           mContainer.c_str(), inputFileName.c_str());
751 
752     int32_t status = setDataSource(inputFileName);
753     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
754 
755     status = createExtractor();
756     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
757 
758     int32_t numTracks = mExtractor->countTracks();
759     ASSERT_EQ(numTracks, mNumTracks)
760             << "Extractor reported wrong number of track for the given clip";
761 
762     uint32_t seekFlag = mExtractor->flags();
763     bool seekSupported = get<3>(GetParam());
764     bool seekable = seekFlag & MediaExtractorPluginHelper::CAN_SEEK;
765     if (!seekable) {
766         ASSERT_FALSE(seekSupported) << mContainer << "Extractor is expected to support seek ";
767         cout << "[   WARN   ] Test Skipped. " << mContainer << " Extractor doesn't support seek\n";
768         return;
769     }
770 
771     for (int32_t idx = 0; idx < numTracks; idx++) {
772         MediaTrackHelper *track = mExtractor->getTrack(idx);
773         ASSERT_NE(track, nullptr) << "Failed to get track for index " << idx;
774 
775         CMediaTrack *cTrack = wrap(track);
776         ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << idx;
777 
778         MediaBufferGroup *bufferGroup = new MediaBufferGroup();
779         status = cTrack->start(track, bufferGroup->wrap());
780         ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
781 
782         AMediaFormat *trackMeta = AMediaFormat_new();
783         ASSERT_NE(trackMeta, nullptr) << "AMediaFormat_new returned null AMediaformat";
784 
785         status = mExtractor->getTrackMetaData(
786                 trackMeta, idx, MediaExtractorPluginHelper::kIncludeExtensiveMetaData);
787         ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData";
788 
789         const char *mime;
790         ASSERT_TRUE(AMediaFormat_getString(trackMeta, AMEDIAFORMAT_KEY_MIME, &mime))
791                 << "Failed to get mime";
792 
793         int64_t clipDuration = 0;
794         AMediaFormat_getInt64(trackMeta, AMEDIAFORMAT_KEY_DURATION, &clipDuration);
795         // Image formats are not expected to have duration information
796         ASSERT_TRUE(clipDuration > 0 || !strncmp(mime, "image/", 6)) << "Invalid clip duration ";
797         AMediaFormat_delete(trackMeta);
798 
799         int64_t seekToTimeStampUs[] = {-clipDuration, clipDuration / 2, clipDuration,
800                                        clipDuration * 2};
801         for (int32_t mode = CMediaTrackReadOptions::SEEK_PREVIOUS_SYNC;
802              mode <= CMediaTrackReadOptions::SEEK_CLOSEST; mode++) {
803             for (int64_t seekTimeUs : seekToTimeStampUs) {
804                 MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions(
805                         mode | CMediaTrackReadOptions::SEEK, seekTimeUs);
806                 ASSERT_NE(options, nullptr) << "Cannot create read option";
807 
808                 MediaBufferHelper *buffer = nullptr;
809                 status = track->read(&buffer, options);
810                 if (status == AMEDIA_ERROR_END_OF_STREAM) {
811                     delete options;
812                     continue;
813                 }
814                 if (buffer) {
815                     AMediaFormat *metaData = buffer->meta_data();
816                     int64_t timeStamp;
817                     AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp);
818                     ALOGV("Seeked to timestamp : %lld, requested : %lld", (long long)timeStamp,
819                           (long long)seekTimeUs);
820                     buffer->release();
821                 }
822                 delete options;
823             }
824         }
825         status = cTrack->stop(track);
826         ASSERT_EQ(OK, status) << "Failed to stop the track";
827         delete bufferGroup;
828         delete track;
829     }
830 }
831 
832 // Tests extractors for invalid tracks
TEST_P(ExtractorFunctionalityTest,SanityTest)833 TEST_P(ExtractorFunctionalityTest, SanityTest) {
834     if (mDisableTest) return;
835     // TODO(b/155626946): Enable test for MPEG2 TS/PS extractors
836     if (mExtractorName == MPEG2TS || mExtractorName == MPEG2PS) return;
837 
838     string inputFileName = gEnv->getRes() + get<1>(GetParam());
839     ALOGV("Validates %s Extractor behaviour for invalid tracks - file %s",
840           mContainer.c_str(), inputFileName.c_str());
841 
842     int32_t status = setDataSource(inputFileName);
843     ASSERT_EQ(status, 0) << "SetDataSource failed for" << mContainer << "extractor";
844 
845     status = createExtractor();
846     ASSERT_EQ(status, 0) << "Extractor creation failed for" << mContainer << "extractor";
847 
848     int32_t numTracks = mExtractor->countTracks();
849     ASSERT_EQ(numTracks, mNumTracks)
850             << "Extractor reported wrong number of track for the given clip";
851 
852     int32_t trackIdx[] = {-1, numTracks};
853     for (int32_t idx : trackIdx) {
854         MediaTrackHelper *track = mExtractor->getTrack(idx);
855         ASSERT_EQ(track, nullptr) << "Failed to get track for index " << idx << "\n";
856 
857         AMediaFormat *extractorFormat = AMediaFormat_new();
858         ASSERT_NE(extractorFormat, nullptr) << "AMediaFormat_new returned null AMediaformat";
859 
860         status = mExtractor->getTrackMetaData(
861                 extractorFormat, idx, MediaExtractorPluginHelper::kIncludeExtensiveMetaData);
862         ASSERT_NE(OK, status) << "getTrackMetaData should return error for invalid index " << idx;
863         AMediaFormat_delete(extractorFormat);
864     }
865 
866     // Validate Extractor's getTrackMetaData for null format
867     AMediaFormat *mediaFormat = nullptr;
868     status = mExtractor->getTrackMetaData(mediaFormat, 0,
869                                           MediaExtractorPluginHelper::kIncludeExtensiveMetaData);
870     ASSERT_NE(OK, status) << "getTrackMetaData should return error for null Media format";
871 }
872 
873 // This test validates config params for a given input file.
874 // For this test we only take single track files since the focus of this test is
875 // to validate the file properties reported by Extractor and not multi-track behavior
TEST_P(ConfigParamTest,ConfigParamValidation)876 TEST_P(ConfigParamTest, ConfigParamValidation) {
877     if (mDisableTest) return;
878 
879     const int trackNumber = 0;
880 
881     string container = GetParam().first;
882     string inputFileName = gEnv->getRes();
883     inputID inputFileId = GetParam().second;
884     configFormat configParam;
885     getFileProperties(inputFileId, inputFileName, configParam);
886 
887     ALOGV("Validates %s Extractor for input's file properties, file %s",
888           container.c_str(), inputFileName.c_str());
889 
890     int32_t status = setDataSource(inputFileName);
891     ASSERT_EQ(status, 0) << "SetDataSource failed for " << container << "extractor";
892 
893     status = createExtractor();
894     ASSERT_EQ(status, 0) << "Extractor creation failed for " << container << "extractor";
895 
896     int32_t numTracks = mExtractor->countTracks();
897     ASSERT_GT(numTracks, 0) << "Extractor didn't find any track for the given clip";
898 
899     MediaTrackHelper *track = mExtractor->getTrack(trackNumber);
900     ASSERT_NE(track, nullptr) << "Failed to get track for index 0";
901 
902     AMediaFormat *trackFormat = AMediaFormat_new();
903     ASSERT_NE(trackFormat, nullptr) << "AMediaFormat_new returned null format";
904 
905     status = track->getFormat(trackFormat);
906     ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
907 
908     const char *trackMime;
909     bool valueFound = AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &trackMime);
910     ASSERT_TRUE(valueFound) << "Mime type not set by extractor";
911     ASSERT_STREQ(configParam.mime.c_str(), trackMime) << "Invalid track format";
912 
913     if (!strncmp(trackMime, "audio/", 6)) {
914         int32_t trackSampleRate, trackChannelCount;
915         ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
916                                           &trackChannelCount));
917         ASSERT_TRUE(
918                 AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &trackSampleRate));
919         ASSERT_EQ(configParam.sampleRate, trackSampleRate) << "SampleRate not as expected";
920         ASSERT_EQ(configParam.channelCount, trackChannelCount) << "ChannelCount not as expected";
921     } else if (!strncmp(trackMime, "video/", 6)) {
922         int32_t trackWidth, trackHeight;
923         ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_WIDTH, &trackWidth));
924         ASSERT_TRUE(AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_HEIGHT, &trackHeight));
925         ASSERT_EQ(configParam.width, trackWidth) << "Width not as expected";
926         ASSERT_EQ(configParam.height, trackHeight) << "Height not as expected";
927 
928         if (configParam.frameRate != kUndefined) {
929             int32_t frameRate;
930             ASSERT_TRUE(
931                     AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_FRAME_RATE, &frameRate));
932             ASSERT_EQ(configParam.frameRate, frameRate) << "frameRate not as expected";
933         }
934     }
935     // validate the profile for the input clip
936     int32_t profile;
937     if (configParam.profile != kUndefined) {
938         if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_PROFILE, &profile)) {
939             ASSERT_EQ(configParam.profile, profile) << "profile not as expected";
940         } else if (mExtractorName == AAC &&
941                    AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_AAC_PROFILE, &profile)) {
942             ASSERT_EQ(configParam.profile, profile) << "profile not as expected";
943         } else {
944             ASSERT_TRUE(false) << "profile not returned in extractor";
945         }
946     }
947 
948     delete track;
949     AMediaFormat_delete(trackFormat);
950 }
951 
952 class ExtractorComparison
953     : public ExtractorUnitTest,
954       public ::testing::TestWithParam<pair<string /* InputFile0 */, string /* InputFile1 */>> {
955   public:
~ExtractorComparison()956     ~ExtractorComparison() {
957         for (int8_t *extractorOp : mExtractorOutput) {
958             if (extractorOp != nullptr) {
959                 free(extractorOp);
960             }
961         }
962     }
963 
964     int8_t *mExtractorOutput[2]{};
965     size_t mExtractorOuputSize[2]{};
966 };
967 
allocateOutputBuffers(string inputFileName,AMediaFormat * extractorFormat)968 size_t allocateOutputBuffers(string inputFileName, AMediaFormat *extractorFormat) {
969     size_t bufferSize = 0u;
970     // allocating the buffer size as sampleRate * channelCount * clipDuration since
971     // some extractors like flac, midi and wav decodes the file. These extractors
972     // advertise the mime type as raw.
973     const char *mime;
974     AMediaFormat_getString(extractorFormat, AMEDIAFORMAT_KEY_MIME, &mime);
975     if (!strcmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
976         int64_t clipDurationUs = -1;
977         int32_t channelCount = -1;
978         int32_t sampleRate = -1;
979         int32_t bitsPerSampple = -1;
980         if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
981                                    &channelCount) || channelCount <= 0) {
982             ALOGE("Invalid channelCount for input file : %s", inputFileName.c_str());
983             return 0;
984         }
985         if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate) ||
986             sampleRate <= 0) {
987             ALOGE("Invalid sampleRate for input file : %s", inputFileName.c_str());
988             return 0;
989         }
990         if (!AMediaFormat_getInt64(extractorFormat, AMEDIAFORMAT_KEY_DURATION, &clipDurationUs) ||
991             clipDurationUs <= 0) {
992             ALOGE("Invalid clip duration for input file : %s", inputFileName.c_str());
993             return 0;
994         }
995         if (!AMediaFormat_getInt32(extractorFormat, AMEDIAFORMAT_KEY_PCM_ENCODING,
996                                    &bitsPerSampple) || bitsPerSampple <= 0) {
997             ALOGE("Invalid bits per sample for input file : %s", inputFileName.c_str());
998             return 0;
999         }
1000         bufferSize = bitsPerSampple * channelCount * sampleRate * (clipDurationUs / 1000000 + 1);
1001     } else {
1002         struct stat buf;
1003         int32_t status = stat(inputFileName.c_str(), &buf);
1004         if (status != 0) {
1005             ALOGE("Unable to get file properties for: %s", inputFileName.c_str());
1006             return 0;
1007         }
1008         bufferSize = buf.st_size;
1009     }
1010     return bufferSize;
1011 }
1012 
1013 // Compare output of two extractors for identical content
TEST_P(ExtractorComparison,ExtractorComparisonTest)1014 TEST_P(ExtractorComparison, ExtractorComparisonTest) {
1015     vector<string> inputFileNames = {GetParam().first, GetParam().second};
1016     size_t extractedOutputSize[2]{};
1017     AMediaFormat *extractorFormat[2]{};
1018     int32_t status = OK;
1019 
1020     for (int32_t idx = 0; idx < inputFileNames.size(); idx++) {
1021         string containerFormat = inputFileNames[idx].substr(inputFileNames[idx].find(".") + 1);
1022         setupExtractor(containerFormat);
1023         if (mDisableTest) {
1024             ALOGV("Unknown extractor %s. Skipping the test", containerFormat.c_str());
1025             return;
1026         }
1027 
1028         ALOGV("Validates %s Extractor for %s", containerFormat.c_str(),
1029               inputFileNames[idx].c_str());
1030         string inputFileName = gEnv->getRes() + inputFileNames[idx];
1031 
1032         status = setDataSource(inputFileName);
1033         ASSERT_EQ(status, 0) << "SetDataSource failed for" << containerFormat << "extractor";
1034 
1035         status = createExtractor();
1036         ASSERT_EQ(status, 0) << "Extractor creation failed for " << containerFormat << " extractor";
1037 
1038         int32_t numTracks = mExtractor->countTracks();
1039         ASSERT_EQ(numTracks, 1) << "This test expects inputs with one track only";
1040 
1041         int32_t trackIdx = 0;
1042         MediaTrackHelper *track = mExtractor->getTrack(trackIdx);
1043         ASSERT_NE(track, nullptr) << "Failed to get track for index " << trackIdx;
1044 
1045         extractorFormat[idx] = AMediaFormat_new();
1046         ASSERT_NE(extractorFormat[idx], nullptr) << "AMediaFormat_new returned null AMediaformat";
1047 
1048         status = track->getFormat(extractorFormat[idx]);
1049         ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data";
1050 
1051         CMediaTrack *cTrack = wrap(track);
1052         ASSERT_NE(cTrack, nullptr) << "Failed to get track wrapper for index " << trackIdx;
1053 
1054         mExtractorOuputSize[idx] = allocateOutputBuffers(inputFileName, extractorFormat[idx]);
1055         ASSERT_GT(mExtractorOuputSize[idx], 0u) << " Invalid size for output buffers";
1056 
1057         mExtractorOutput[idx] = (int8_t *)calloc(1, mExtractorOuputSize[idx]);
1058         ASSERT_NE(mExtractorOutput[idx], nullptr)
1059                 << "Unable to allocate memory for writing extractor's output";
1060 
1061         MediaBufferGroup *bufferGroup = new MediaBufferGroup();
1062         status = cTrack->start(track, bufferGroup->wrap());
1063         ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track";
1064 
1065         int32_t offset = 0;
1066         while (status != AMEDIA_ERROR_END_OF_STREAM) {
1067             MediaBufferHelper *buffer = nullptr;
1068             status = track->read(&buffer);
1069             ALOGV("track->read Status = %d buffer %p", status, buffer);
1070             if (buffer) {
1071                 ASSERT_LE(offset + buffer->range_length(), mExtractorOuputSize[idx])
1072                         << "Memory overflow. Extracted output size more than expected";
1073 
1074                 memcpy(mExtractorOutput[idx] + offset, buffer->data(), buffer->range_length());
1075                 extractedOutputSize[idx] += buffer->range_length();
1076                 offset += buffer->range_length();
1077                 buffer->release();
1078             }
1079         }
1080         status = cTrack->stop(track);
1081         ASSERT_EQ(OK, status) << "Failed to stop the track";
1082 
1083         fclose(mInputFp);
1084         delete bufferGroup;
1085         delete track;
1086         mDataSource.clear();
1087         delete mExtractor;
1088         mInputFp = nullptr;
1089         mExtractor = nullptr;
1090     }
1091 
1092     // Compare the meta data from both the extractors
1093     const char *mime[2];
1094     AMediaFormat_getString(extractorFormat[0], AMEDIAFORMAT_KEY_MIME, &mime[0]);
1095     AMediaFormat_getString(extractorFormat[1], AMEDIAFORMAT_KEY_MIME, &mime[1]);
1096     ASSERT_STREQ(mime[0], mime[1]) << "Mismatch between extractor's format";
1097 
1098     if (!strncmp(mime[0], "audio/", 6)) {
1099         int32_t channelCount0, channelCount1;
1100         int32_t sampleRate0, sampleRate1;
1101         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[0], AMEDIAFORMAT_KEY_CHANNEL_COUNT,
1102                                           &channelCount0));
1103         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[0], AMEDIAFORMAT_KEY_SAMPLE_RATE,
1104                                           &sampleRate0));
1105         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[1], AMEDIAFORMAT_KEY_CHANNEL_COUNT,
1106                                           &channelCount1));
1107         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[1], AMEDIAFORMAT_KEY_SAMPLE_RATE,
1108                                           &sampleRate1));
1109         ASSERT_EQ(channelCount0, channelCount1) << "Mismatch between extractor's channelCount";
1110         ASSERT_EQ(sampleRate0, sampleRate1) << "Mismatch between extractor's sampleRate";
1111     } else if (!strncmp(mime[0], "video/", 6)) {
1112         int32_t width0, height0;
1113         int32_t width1, height1;
1114         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[0], AMEDIAFORMAT_KEY_WIDTH, &width0));
1115         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[0], AMEDIAFORMAT_KEY_HEIGHT, &height0));
1116         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[1], AMEDIAFORMAT_KEY_WIDTH, &width1));
1117         ASSERT_TRUE(AMediaFormat_getInt32(extractorFormat[1], AMEDIAFORMAT_KEY_HEIGHT, &height1));
1118         ASSERT_EQ(width0, width1) << "Mismatch between extractor's width";
1119         ASSERT_EQ(height0, height1) << "Mismatch between extractor's height";
1120     } else {
1121         ASSERT_TRUE(false) << "Invalid mime type " << mime[0];
1122     }
1123 
1124     for (AMediaFormat *exFormat : extractorFormat) {
1125         AMediaFormat_delete(exFormat);
1126     }
1127 
1128     // Compare the extracted outputs of both extractor
1129     ASSERT_EQ(extractedOutputSize[0], extractedOutputSize[1])
1130             << "Extractor's output size doesn't match between " << inputFileNames[0] << "and "
1131             << inputFileNames[1] << " extractors";
1132     status = memcmp(mExtractorOutput[0], mExtractorOutput[1], extractedOutputSize[0]);
1133     ASSERT_EQ(status, 0) << "Extracted content mismatch between " << inputFileNames[0] << "and "
1134                          << inputFileNames[1] << " extractors";
1135 }
1136 
1137 INSTANTIATE_TEST_SUITE_P(
1138         ExtractorComparisonAll, ExtractorComparison,
1139         ::testing::Values(make_pair("swirl_144x136_vp9.mp4", "swirl_144x136_vp9.webm"),
1140                           make_pair("video_480x360_mp4_vp9_333kbps_25fps.mp4",
1141                                     "video_480x360_webm_vp9_333kbps_25fps.webm"),
1142                           make_pair("video_1280x720_av1_hdr_static_3mbps.mp4",
1143                                     "video_1280x720_av1_hdr_static_3mbps.webm"),
1144                           make_pair("swirl_132x130_mpeg4.3gp", "swirl_132x130_mpeg4.mkv"),
1145                           make_pair("swirl_144x136_avc.mkv", "swirl_144x136_avc.mp4"),
1146                           make_pair("swirl_132x130_mpeg4.mp4", "swirl_132x130_mpeg4.mkv"),
1147                           make_pair("crowd_508x240_25fps_hevc.mp4","crowd_508x240_25fps_hevc.mkv"),
1148                           make_pair("bbb_cif_768kbps_30fps_mpeg2.mp4",
1149                                     "bbb_cif_768kbps_30fps_mpeg2.ts"),
1150 
1151                           make_pair("loudsoftaac.aac", "loudsoftaac.mkv"),
1152                           make_pair("sinesweepflacmkv.mkv", "sinesweepflacmp4.mp4"),
1153                           make_pair("sinesweepmp3lame.mp3", "sinesweepmp3lame.mkv"),
1154                           make_pair("sinesweepoggmp4.mp4", "sinesweepogg.ogg"),
1155                           make_pair("sinesweepvorbis.mp4", "sinesweepvorbis.ogg"),
1156                           make_pair("sinesweepvorbis.mkv", "sinesweepvorbis.ogg"),
1157                           make_pair("testopus.mkv", "testopus.mp4"),
1158                           make_pair("testopus.mp4", "testopus.opus"),
1159 
1160                           make_pair("loudsoftaac.aac", "loudsoftaac.aac"),
1161                           make_pair("testamr.amr", "testamr.amr"),
1162                           make_pair("sinesweepflac.flac", "sinesweepflac.flac"),
1163                           make_pair("midi_a.mid", "midi_a.mid"),
1164                           make_pair("sinesweepvorbis.mkv", "sinesweepvorbis.mkv"),
1165                           make_pair("sinesweepmp3lame.mp3", "sinesweepmp3lame.mp3"),
1166                           make_pair("sinesweepoggmp4.mp4", "sinesweepoggmp4.mp4"),
1167                           make_pair("testopus.opus", "testopus.opus"),
1168                           make_pair("john_cage.ogg", "john_cage.ogg"),
1169                           make_pair("monotestgsm.wav", "monotestgsm.wav"),
1170 
1171                           make_pair("swirl_144x136_mpeg2.mpg", "swirl_144x136_mpeg2.mpg"),
1172                           make_pair("swirl_132x130_mpeg4.mp4", "swirl_132x130_mpeg4.mp4"),
1173                           make_pair("swirl_144x136_vp9.webm", "swirl_144x136_vp9.webm"),
1174                           make_pair("swirl_144x136_vp8.webm", "swirl_144x136_vp8.webm")));
1175 
1176 INSTANTIATE_TEST_SUITE_P(ConfigParamTestAll, ConfigParamTest,
1177                          ::testing::Values(make_pair("aac", AAC_1),
1178                                            make_pair("amr", AMR_NB_1),
1179                                            make_pair("amr", AMR_WB_1),
1180                                            make_pair("flac", FLAC_1),
1181                                            make_pair("wav", GSM_1),
1182                                            make_pair("midi", MIDI_1),
1183                                            make_pair("mp3", MP3_1),
1184                                            make_pair("ogg", OPUS_1),
1185                                            make_pair("ogg", VORBIS_1),
1186 
1187                                            make_pair("mpeg4", HEVC_1),
1188                                            make_pair("mpeg4", HEVC_2),
1189                                            make_pair("mpeg2ps", MPEG2_PS_1),
1190                                            make_pair("mpeg2ts", MPEG2_TS_1),
1191                                            make_pair("mkv", MPEG4_1),
1192                                            make_pair("mkv", VP9_1)));
1193 
1194 // Validate extractors for container format, input file, no. of tracks and supports seek flag
1195 INSTANTIATE_TEST_SUITE_P(
1196         ExtractorUnitTestAll, ExtractorFunctionalityTest,
1197         ::testing::Values(
1198                 make_tuple("aac", "loudsoftaac.aac", 1, true),
1199                 make_tuple("amr", "testamr.amr", 1, true),
1200                 make_tuple("amr", "amrwb.wav", 1, true),
1201                 make_tuple("flac", "sinesweepflac.flac", 1, true),
1202                 make_tuple("midi", "midi_a.mid", 1, true),
1203                 make_tuple("mkv", "sinesweepvorbis.mkv", 1, true),
1204                 make_tuple("mkv", "sinesweepmp3lame.mkv", 1, true),
1205                 make_tuple("mkv", "loudsoftaac.mkv", 1, true),
1206                 make_tuple("mp3", "sinesweepmp3lame.mp3", 1, true),
1207                 make_tuple("mp3", "id3test10.mp3", 1, true),
1208                 make_tuple("mpeg2ts", "segment000001.ts", 2, false),
1209                 make_tuple("mpeg2ts", "testac3ts.ts", 1, false),
1210                 make_tuple("mpeg2ts", "testac4ts.ts", 1, false),
1211                 make_tuple("mpeg2ts", "testeac3ts.ts", 1, false),
1212                 make_tuple("mpeg4", "audio_aac_mono_70kbs_44100hz.mp4", 2, true),
1213                 make_tuple("mpeg4", "multi0_ac4.mp4", 1, true),
1214                 make_tuple("mpeg4", "noise_6ch_44khz_aot5_dr_sbr_sig2_mp4.m4a", 1, true),
1215                 make_tuple("mpeg4", "sinesweepalac.mov", 1, true),
1216                 make_tuple("mpeg4", "sinesweepflacmp4.mp4", 1, true),
1217                 make_tuple("mpeg4", "sinesweepm4a.m4a", 1, true),
1218                 make_tuple("mpeg4", "sinesweepoggmp4.mp4", 1, true),
1219                 make_tuple("mpeg4", "sinesweepopusmp4.mp4", 1, true),
1220                 make_tuple("mpeg4", "testac3mp4.mp4", 1, true),
1221                 make_tuple("mpeg4", "testeac3mp4.mp4", 1, true),
1222                 make_tuple("ogg", "john_cage.ogg", 1, true),
1223                 make_tuple("ogg", "testopus.opus", 1, true),
1224                 make_tuple("ogg", "sinesweepoggalbumart.ogg", 1, true),
1225                 make_tuple("wav", "loudsoftwav.wav", 1, true),
1226                 make_tuple("wav", "monotestgsm.wav", 1, true),
1227                 make_tuple("wav", "noise_5ch_44khz_aot2_wave.wav", 1, true),
1228                 make_tuple("wav", "sine1khzm40db_alaw.wav", 1, true),
1229                 make_tuple("wav", "sine1khzm40db_f32le.wav", 1, true),
1230                 make_tuple("wav", "sine1khzm40db_mulaw.wav", 1, true),
1231 
1232                 make_tuple("mkv", "swirl_144x136_avc.mkv", 1, true),
1233                 make_tuple("mkv", "withoutcues.mkv", 2, true),
1234                 make_tuple("mkv", "swirl_144x136_vp9.webm", 1, true),
1235                 make_tuple("mkv", "swirl_144x136_vp8.webm", 1, true),
1236                 make_tuple("mpeg2ps", "swirl_144x136_mpeg2.mpg", 1, false),
1237                 make_tuple("mpeg2ps", "programstream.mpeg", 2, false),
1238                 make_tuple("mpeg4", "color_176x144_bt601_525_lr_sdr_h264.mp4", 1, true),
1239                 make_tuple("mpeg4", "heifwriter_input.heic", 4, false),
1240                 make_tuple("mpeg4", "psshtest.mp4", 1, true),
1241                 make_tuple("mpeg4", "swirl_132x130_mpeg4.mp4", 1, true),
1242                 make_tuple("mpeg4", "testvideo.3gp", 4, true),
1243                 make_tuple("mpeg4", "testvideo_with_2_timedtext_tracks.3gp", 4, true),
1244                 make_tuple("mpeg4",
1245                            "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz_"
1246                            "metadata_gyro_compliant.3gp",
1247                            3, true),
1248                 make_tuple(
1249                         "mpeg4",
1250                         "video_1920x1080_mp4_mpeg2_12000kbps_30fps_aac_stereo_128kbps_48000hz.mp4",
1251                         2, true),
1252                 make_tuple("mpeg4",
1253                            "video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4", 2,
1254                            true),
1255                 make_tuple(
1256                         "mpeg4",
1257                         "video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4",
1258                         2, true)));
1259 
main(int argc,char ** argv)1260 int main(int argc, char **argv) {
1261     gEnv = new ExtractorUnitTestEnvironment();
1262     ::testing::AddGlobalTestEnvironment(gEnv);
1263     ::testing::InitGoogleTest(&argc, argv);
1264     int status = gEnv->initFromOptions(argc, argv);
1265     if (status == 0) {
1266         status = RUN_ALL_TESTS();
1267         ALOGV("Test result = %d\n", status);
1268     }
1269     return status;
1270 }
1271