diff options
Diffstat (limited to 'media/libstagefright/MPEG4Extractor.cpp')
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 194 |
1 files changed, 184 insertions, 10 deletions
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index d49696a..2e94a12 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -611,6 +611,14 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } chunk_size = ntoh64(chunk_size); data_offset += 8; + + if (chunk_size < 16) { + // The smallest valid chunk is 16 bytes long in this case. + return ERROR_MALFORMED; + } + } else if (chunk_size < 8) { + // The smallest valid chunk is 8 bytes long. + return ERROR_MALFORMED; } char chunk[5]; @@ -667,14 +675,15 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { case FOURCC('m', 'o', 'o', 'f'): case FOURCC('t', 'r', 'a', 'f'): case FOURCC('m', 'f', 'r', 'a'): - case FOURCC('s', 'k', 'i' ,'p'): case FOURCC('u', 'd', 't', 'a'): case FOURCC('i', 'l', 's', 't'): { if (chunk_type == FOURCC('s', 't', 'b', 'l')) { LOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size); - if (mDataSource->flags() & DataSource::kWantsPrefetching) { + if (mDataSource->flags() + & (DataSource::kWantsPrefetching + | DataSource::kIsCachingDataSource)) { sp<MPEG4DataSource> cachedSource = new MPEG4DataSource(mDataSource); @@ -1053,7 +1062,11 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) { } size_t max_size; - CHECK_EQ(mLastTrack->sampleTable->getMaxSampleSize(&max_size), OK); + err = mLastTrack->sampleTable->getMaxSampleSize(&max_size); + + if (err != OK) { + return err; + } // Assume that a given buffer only contains at most 10 fragments, // each fragment originally prefixed with a 2 byte length will @@ -1468,6 +1481,14 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( hexdump(csd, csd_size); #endif + if (csd_size == 0) { + // There's no further information, i.e. no codec specific data + // Let's assume that the information provided in the mpeg4 headers + // is accurate and hope for the best. + + return OK; + } + if (csd_size < 2) { return ERROR_MALFORMED; } @@ -1662,12 +1683,45 @@ status_t MPEG4Source::read( *out = NULL; + int64_t targetSampleTimeUs = -1; + int64_t seekTimeUs; - if (options && options->getSeekTo(&seekTimeUs)) { + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + uint32_t findFlags = 0; + switch (mode) { + case ReadOptions::SEEK_PREVIOUS_SYNC: + findFlags = SampleTable::kFlagBefore; + break; + case ReadOptions::SEEK_NEXT_SYNC: + findFlags = SampleTable::kFlagAfter; + break; + case ReadOptions::SEEK_CLOSEST_SYNC: + case ReadOptions::SEEK_CLOSEST: + findFlags = SampleTable::kFlagClosest; + break; + default: + CHECK(!"Should not be here."); + break; + } + uint32_t sampleIndex; - status_t err = mSampleTable->findClosestSample( + status_t err = mSampleTable->findSampleAtTime( seekTimeUs * mTimescale / 1000000, - &sampleIndex, SampleTable::kSyncSample_Flag); + &sampleIndex, findFlags); + + if (mode == ReadOptions::SEEK_CLOSEST) { + // We found the closest sample already, now we want the sync + // sample preceding it (or the sample itself of course), even + // if the subsequent sync sample is closer. + findFlags = SampleTable::kFlagBefore; + } + + uint32_t syncSampleIndex; + if (err == OK) { + err = mSampleTable->findSyncSampleNear( + sampleIndex, &syncSampleIndex, findFlags); + } if (err != OK) { if (err == ERROR_OUT_OF_RANGE) { @@ -1681,7 +1735,27 @@ status_t MPEG4Source::read( return err; } - mCurrentSampleIndex = sampleIndex; + uint32_t sampleTime; + CHECK_EQ(OK, mSampleTable->getMetaDataForSample( + sampleIndex, NULL, NULL, &sampleTime)); + + if (mode == ReadOptions::SEEK_CLOSEST) { + targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale; + } + +#if 0 + uint32_t syncSampleTime; + CHECK_EQ(OK, mSampleTable->getMetaDataForSample( + syncSampleIndex, NULL, NULL, &syncSampleTime)); + + LOGI("seek to time %lld us => sample at time %lld us, " + "sync sample at time %lld us", + seekTimeUs, + sampleTime * 1000000ll / mTimescale, + syncSampleTime * 1000000ll / mTimescale); +#endif + + mCurrentSampleIndex = syncSampleIndex; if (mBuffer != NULL) { mBuffer->release(); mBuffer = NULL; @@ -1693,13 +1767,14 @@ status_t MPEG4Source::read( off_t offset; size_t size; uint32_t dts; + bool isSyncSample; bool newBuffer = false; if (mBuffer == NULL) { newBuffer = true; status_t err = mSampleTable->getMetaDataForSample( - mCurrentSampleIndex, &offset, &size, &dts); + mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample); if (err != OK) { return err; @@ -1730,6 +1805,16 @@ status_t MPEG4Source::read( mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data()->setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } + ++mCurrentSampleIndex; } @@ -1838,6 +1923,16 @@ status_t MPEG4Source::read( mBuffer->meta_data()->clear(); mBuffer->meta_data()->setInt64( kKeyTime, ((int64_t)dts * 1000000) / mTimescale); + + if (targetSampleTimeUs >= 0) { + mBuffer->meta_data()->setInt64( + kKeyTargetTime, targetSampleTimeUs); + } + + if (isSyncSample) { + mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); + } + ++mCurrentSampleIndex; *out = mBuffer; @@ -1847,7 +1942,7 @@ status_t MPEG4Source::read( } } -bool SniffMPEG4( +static bool LegacySniffMPEG4( const sp<DataSource> &source, String8 *mimeType, float *confidence) { uint8_t header[8]; @@ -1863,8 +1958,87 @@ bool SniffMPEG4( || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8) || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) { *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.1; + *confidence = 0.4; + + return true; + } + + return false; +} + +static bool isCompatibleBrand(uint32_t fourcc) { + static const uint32_t kCompatibleBrands[] = { + FOURCC('i', 's', 'o', 'm'), + FOURCC('i', 's', 'o', '2'), + FOURCC('a', 'v', 'c', '1'), + FOURCC('3', 'g', 'p', '4'), + FOURCC('m', 'p', '4', '1'), + FOURCC('m', 'p', '4', '2'), + }; + + for (size_t i = 0; + i < sizeof(kCompatibleBrands) / sizeof(kCompatibleBrands[0]); + ++i) { + if (kCompatibleBrands[i] == fourcc) { + return true; + } + } + + return false; +} + +// Attempt to actually parse the 'ftyp' atom and determine if a suitable +// compatible brand is present. +static bool BetterSniffMPEG4( + const sp<DataSource> &source, String8 *mimeType, float *confidence) { + uint8_t header[12]; + if (source->readAt(0, header, 12) != 12 + || memcmp("ftyp", &header[4], 4)) { + return false; + } + + size_t atomSize = U32_AT(&header[0]); + if (atomSize < 16 || (atomSize % 4) != 0) { + return false; + } + + bool success = false; + if (isCompatibleBrand(U32_AT(&header[8]))) { + success = true; + } else { + size_t numCompatibleBrands = (atomSize - 16) / 4; + for (size_t i = 0; i < numCompatibleBrands; ++i) { + uint8_t tmp[4]; + if (source->readAt(16 + i * 4, tmp, 4) != 4) { + return false; + } + + if (isCompatibleBrand(U32_AT(&tmp[0]))) { + success = true; + break; + } + } + } + + if (!success) { + return false; + } + + *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; + *confidence = 0.4f; + + return true; +} + +bool SniffMPEG4( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { + if (BetterSniffMPEG4(source, mimeType, confidence)) { + return true; + } + if (LegacySniffMPEG4(source, mimeType, confidence)) { + LOGW("Identified supported mpeg4 through LegacySniffMPEG4."); return true; } |