Loading media/extractors/tests/ExtractorUnitTest.cpp +96 −8 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaDataUtils.h> #include <media/stagefright/MetaDataUtils.h> #include <media/stagefright/foundation/OpusHeader.h> #include "aac/AACExtractor.h" #include "aac/AACExtractor.h" #include "amr/AMRExtractor.h" #include "amr/AMRExtractor.h" Loading @@ -43,7 +44,9 @@ using namespace android; #define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput" #define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput" constexpr int32_t kMaxCount = 10; constexpr int32_t kMaxCount = 10; constexpr int32_t kOpusSeekPreRollUs = 80000; // 80 ms; constexpr int32_t kAudioDefaultSampleDuration = 20000; // 20ms constexpr int32_t kRandomSeekToleranceUs = 2 * kAudioDefaultSampleDuration; // 40 ms; constexpr int32_t kRandomSeed = 700; static ExtractorUnitTestEnvironment *gEnv = nullptr; static ExtractorUnitTestEnvironment *gEnv = nullptr; Loading Loading @@ -168,6 +171,47 @@ int32_t ExtractorUnitTest::createExtractor() { return 0; return 0; } } void randomSeekTest(MediaTrackHelper *track, int64_t clipDuration) { int32_t status = 0; int32_t seekCount = 0; bool hasTimestamp = false; vector<int64_t> seekToTimeStamp; string seekPtsString; srand(kRandomSeed); while (seekCount < kMaxCount) { int64_t timeStamp = ((double)rand() / RAND_MAX) * clipDuration; seekToTimeStamp.push_back(timeStamp); seekPtsString.append(to_string(timeStamp)); seekPtsString.append(", "); seekCount++; } for (int64_t seekPts : seekToTimeStamp) { MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( CMediaTrackReadOptions::SEEK_CLOSEST | CMediaTrackReadOptions::SEEK, seekPts); ASSERT_NE(options, nullptr) << "Cannot create read option"; MediaBufferHelper *buffer = nullptr; status = track->read(&buffer, options); if (buffer) { AMediaFormat *metaData = buffer->meta_data(); int64_t timeStamp = 0; hasTimestamp = AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp); ASSERT_TRUE(hasTimestamp) << "Extractor didn't set timestamp for the given sample"; buffer->release(); EXPECT_LE(abs(timeStamp - seekPts), kRandomSeekToleranceUs) << "Seek unsuccessful. Expected timestamp range [" << seekPts - kRandomSeekToleranceUs << ", " << seekPts + kRandomSeekToleranceUs << "] " << "received " << timeStamp << ", list of input seek timestamps [" << seekPtsString << "]"; } delete options; } } void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) { void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) { int32_t status = 0; int32_t status = 0; if (!seekablePoints.empty()) { if (!seekablePoints.empty()) { Loading Loading @@ -380,11 +424,7 @@ TEST_P(ExtractorUnitTest, MultipleStartStopTest) { } } TEST_P(ExtractorUnitTest, SeekTest) { TEST_P(ExtractorUnitTest, SeekTest) { // Flac, Midi and Wav extractor can give samples from any pts and mark the given sample as if (mDisableTest) return; // sync frame. So, this seek test is not applicable to these extractors if (mDisableTest || mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) { return; } ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str()); ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str()); string inputFileName = gEnv->getRes() + GetParam().second; string inputFileName = gEnv->getRes() + GetParam().second; Loading Loading @@ -417,6 +457,32 @@ TEST_P(ExtractorUnitTest, SeekTest) { MediaBufferGroup *bufferGroup = new MediaBufferGroup(); MediaBufferGroup *bufferGroup = new MediaBufferGroup(); status = cTrack->start(track, bufferGroup->wrap()); status = cTrack->start(track, bufferGroup->wrap()); ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track"; ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track"; // For Flac, Wav and Midi extractor, all samples are seek points. // We cannot create list of all seekable points for these. // This means that if we pass a seekToTimeStamp between two seek points, we may // end up getting the timestamp of next sample as a seekable timestamp. // This timestamp may/may not be a part of the seekable point vector thereby failing the // test. So we test these extractors using random seek test. if (mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) { AMediaFormat *trackMeta = AMediaFormat_new(); ASSERT_NE(trackMeta, nullptr) << "AMediaFormat_new returned null AMediaformat"; status = mExtractor->getTrackMetaData(trackMeta, idx, 1); ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData"; int64_t clipDuration = 0; AMediaFormat_getInt64(trackMeta, AMEDIAFORMAT_KEY_DURATION, &clipDuration); ASSERT_GT(clipDuration, 0) << "Invalid clip duration "; randomSeekTest(track, clipDuration); AMediaFormat_delete(trackMeta); continue; } // Request seekable points for remaining extractors which will be used to validate the seek // accuracy for the extractors. Depending on SEEK Mode, we expect the extractors to return // the expected sync frame. We don't prefer random seek test for these extractors because // they aren't expected to seek to random samples. MP4 for instance can seek to // next/previous sync frames but not to samples between two sync frames. getSeekablePoints(seekablePoints, track); getSeekablePoints(seekablePoints, track); ASSERT_GT(seekablePoints.size(), 0) ASSERT_GT(seekablePoints.size(), 0) << "Failed to get seekable points for " << GetParam().first << " extractor"; << "Failed to get seekable points for " << GetParam().first << " extractor"; Loading @@ -427,9 +493,31 @@ TEST_P(ExtractorUnitTest, SeekTest) { ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data"; ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data"; bool isOpus = false; bool isOpus = false; int64_t opusSeekPreRollUs = 0; const char *mime; const char *mime; AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime); AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime); if (!strcmp(mime, "audio/opus")) isOpus = true; if (!strcmp(mime, "audio/opus")) { isOpus = true; void *seekPreRollBuf = nullptr; size_t size = 0; if (!AMediaFormat_getBuffer(trackFormat, "csd-2", &seekPreRollBuf, &size)) { size_t opusHeadSize = 0; size_t codecDelayBufSize = 0; size_t seekPreRollBufSize = 0; void *csdBuffer = nullptr; void *opusHeadBuf = nullptr; void *codecDelayBuf = nullptr; AMediaFormat_getBuffer(trackFormat, "csd-0", &csdBuffer, &size); ASSERT_NE(csdBuffer, nullptr); GetOpusHeaderBuffers((uint8_t *)csdBuffer, size, &opusHeadBuf, &opusHeadSize, &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf, &seekPreRollBufSize); } ASSERT_NE(seekPreRollBuf, nullptr) << "Invalid track format. SeekPreRoll info missing for Opus file"; opusSeekPreRollUs = *((int64_t *)seekPreRollBuf); } AMediaFormat_delete(trackFormat); AMediaFormat_delete(trackFormat); int32_t seekIdx = 0; int32_t seekIdx = 0; Loading @@ -450,7 +538,7 @@ TEST_P(ExtractorUnitTest, SeekTest) { // extractor is calculated based on (seekPts - seekPreRollUs). // extractor is calculated based on (seekPts - seekPreRollUs). // So we add the preRoll value to the timeStamp we want to seek to. // So we add the preRoll value to the timeStamp we want to seek to. if (isOpus) { if (isOpus) { seekToTimeStamp += kOpusSeekPreRollUs; seekToTimeStamp += opusSeekPreRollUs; } } MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( Loading Loading
media/extractors/tests/ExtractorUnitTest.cpp +96 −8 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaDataUtils.h> #include <media/stagefright/MetaDataUtils.h> #include <media/stagefright/foundation/OpusHeader.h> #include "aac/AACExtractor.h" #include "aac/AACExtractor.h" #include "amr/AMRExtractor.h" #include "amr/AMRExtractor.h" Loading @@ -43,7 +44,9 @@ using namespace android; #define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput" #define OUTPUT_DUMP_FILE "/data/local/tmp/extractorOutput" constexpr int32_t kMaxCount = 10; constexpr int32_t kMaxCount = 10; constexpr int32_t kOpusSeekPreRollUs = 80000; // 80 ms; constexpr int32_t kAudioDefaultSampleDuration = 20000; // 20ms constexpr int32_t kRandomSeekToleranceUs = 2 * kAudioDefaultSampleDuration; // 40 ms; constexpr int32_t kRandomSeed = 700; static ExtractorUnitTestEnvironment *gEnv = nullptr; static ExtractorUnitTestEnvironment *gEnv = nullptr; Loading Loading @@ -168,6 +171,47 @@ int32_t ExtractorUnitTest::createExtractor() { return 0; return 0; } } void randomSeekTest(MediaTrackHelper *track, int64_t clipDuration) { int32_t status = 0; int32_t seekCount = 0; bool hasTimestamp = false; vector<int64_t> seekToTimeStamp; string seekPtsString; srand(kRandomSeed); while (seekCount < kMaxCount) { int64_t timeStamp = ((double)rand() / RAND_MAX) * clipDuration; seekToTimeStamp.push_back(timeStamp); seekPtsString.append(to_string(timeStamp)); seekPtsString.append(", "); seekCount++; } for (int64_t seekPts : seekToTimeStamp) { MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( CMediaTrackReadOptions::SEEK_CLOSEST | CMediaTrackReadOptions::SEEK, seekPts); ASSERT_NE(options, nullptr) << "Cannot create read option"; MediaBufferHelper *buffer = nullptr; status = track->read(&buffer, options); if (buffer) { AMediaFormat *metaData = buffer->meta_data(); int64_t timeStamp = 0; hasTimestamp = AMediaFormat_getInt64(metaData, AMEDIAFORMAT_KEY_TIME_US, &timeStamp); ASSERT_TRUE(hasTimestamp) << "Extractor didn't set timestamp for the given sample"; buffer->release(); EXPECT_LE(abs(timeStamp - seekPts), kRandomSeekToleranceUs) << "Seek unsuccessful. Expected timestamp range [" << seekPts - kRandomSeekToleranceUs << ", " << seekPts + kRandomSeekToleranceUs << "] " << "received " << timeStamp << ", list of input seek timestamps [" << seekPtsString << "]"; } delete options; } } void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) { void getSeekablePoints(vector<int64_t> &seekablePoints, MediaTrackHelper *track) { int32_t status = 0; int32_t status = 0; if (!seekablePoints.empty()) { if (!seekablePoints.empty()) { Loading Loading @@ -380,11 +424,7 @@ TEST_P(ExtractorUnitTest, MultipleStartStopTest) { } } TEST_P(ExtractorUnitTest, SeekTest) { TEST_P(ExtractorUnitTest, SeekTest) { // Flac, Midi and Wav extractor can give samples from any pts and mark the given sample as if (mDisableTest) return; // sync frame. So, this seek test is not applicable to these extractors if (mDisableTest || mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) { return; } ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str()); ALOGV("Validates %s Extractor behaviour for different seek modes", GetParam().first.c_str()); string inputFileName = gEnv->getRes() + GetParam().second; string inputFileName = gEnv->getRes() + GetParam().second; Loading Loading @@ -417,6 +457,32 @@ TEST_P(ExtractorUnitTest, SeekTest) { MediaBufferGroup *bufferGroup = new MediaBufferGroup(); MediaBufferGroup *bufferGroup = new MediaBufferGroup(); status = cTrack->start(track, bufferGroup->wrap()); status = cTrack->start(track, bufferGroup->wrap()); ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track"; ASSERT_EQ(OK, (media_status_t)status) << "Failed to start the track"; // For Flac, Wav and Midi extractor, all samples are seek points. // We cannot create list of all seekable points for these. // This means that if we pass a seekToTimeStamp between two seek points, we may // end up getting the timestamp of next sample as a seekable timestamp. // This timestamp may/may not be a part of the seekable point vector thereby failing the // test. So we test these extractors using random seek test. if (mExtractorName == FLAC || mExtractorName == WAV || mExtractorName == MIDI) { AMediaFormat *trackMeta = AMediaFormat_new(); ASSERT_NE(trackMeta, nullptr) << "AMediaFormat_new returned null AMediaformat"; status = mExtractor->getTrackMetaData(trackMeta, idx, 1); ASSERT_EQ(OK, (media_status_t)status) << "Failed to get trackMetaData"; int64_t clipDuration = 0; AMediaFormat_getInt64(trackMeta, AMEDIAFORMAT_KEY_DURATION, &clipDuration); ASSERT_GT(clipDuration, 0) << "Invalid clip duration "; randomSeekTest(track, clipDuration); AMediaFormat_delete(trackMeta); continue; } // Request seekable points for remaining extractors which will be used to validate the seek // accuracy for the extractors. Depending on SEEK Mode, we expect the extractors to return // the expected sync frame. We don't prefer random seek test for these extractors because // they aren't expected to seek to random samples. MP4 for instance can seek to // next/previous sync frames but not to samples between two sync frames. getSeekablePoints(seekablePoints, track); getSeekablePoints(seekablePoints, track); ASSERT_GT(seekablePoints.size(), 0) ASSERT_GT(seekablePoints.size(), 0) << "Failed to get seekable points for " << GetParam().first << " extractor"; << "Failed to get seekable points for " << GetParam().first << " extractor"; Loading @@ -427,9 +493,31 @@ TEST_P(ExtractorUnitTest, SeekTest) { ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data"; ASSERT_EQ(OK, (media_status_t)status) << "Failed to get track meta data"; bool isOpus = false; bool isOpus = false; int64_t opusSeekPreRollUs = 0; const char *mime; const char *mime; AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime); AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime); if (!strcmp(mime, "audio/opus")) isOpus = true; if (!strcmp(mime, "audio/opus")) { isOpus = true; void *seekPreRollBuf = nullptr; size_t size = 0; if (!AMediaFormat_getBuffer(trackFormat, "csd-2", &seekPreRollBuf, &size)) { size_t opusHeadSize = 0; size_t codecDelayBufSize = 0; size_t seekPreRollBufSize = 0; void *csdBuffer = nullptr; void *opusHeadBuf = nullptr; void *codecDelayBuf = nullptr; AMediaFormat_getBuffer(trackFormat, "csd-0", &csdBuffer, &size); ASSERT_NE(csdBuffer, nullptr); GetOpusHeaderBuffers((uint8_t *)csdBuffer, size, &opusHeadBuf, &opusHeadSize, &codecDelayBuf, &codecDelayBufSize, &seekPreRollBuf, &seekPreRollBufSize); } ASSERT_NE(seekPreRollBuf, nullptr) << "Invalid track format. SeekPreRoll info missing for Opus file"; opusSeekPreRollUs = *((int64_t *)seekPreRollBuf); } AMediaFormat_delete(trackFormat); AMediaFormat_delete(trackFormat); int32_t seekIdx = 0; int32_t seekIdx = 0; Loading @@ -450,7 +538,7 @@ TEST_P(ExtractorUnitTest, SeekTest) { // extractor is calculated based on (seekPts - seekPreRollUs). // extractor is calculated based on (seekPts - seekPreRollUs). // So we add the preRoll value to the timeStamp we want to seek to. // So we add the preRoll value to the timeStamp we want to seek to. if (isOpus) { if (isOpus) { seekToTimeStamp += kOpusSeekPreRollUs; seekToTimeStamp += opusSeekPreRollUs; } } MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( MediaTrackHelper::ReadOptions *options = new MediaTrackHelper::ReadOptions( Loading