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