Loading media/libstagefright/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ LOCAL_SRC_FILES += \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ MediaExtractor.cpp \ SampleIterator.cpp \ SampleTable.cpp \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ Loading media/libstagefright/MPEG4Extractor.cpp +7 −10 Original line number Diff line number Diff line Loading @@ -232,8 +232,9 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData( uint32_t sampleIndex; uint32_t sampleTime; if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK && track->sampleTable->getDecodingTime( sampleIndex, &sampleTime) == OK) { && track->sampleTable->getMetaDataForSample( sampleIndex, NULL /* offset */, NULL /* size */, &sampleTime) == OK) { track->meta->setInt64( kKeyThumbnailTime, ((int64_t)sampleTime * 1000000) / track->timescale); Loading Loading @@ -929,20 +930,16 @@ status_t MPEG4Source::read( if (mBuffer == NULL) { newBuffer = true; status_t err = mSampleTable->getSampleOffsetAndSize( mCurrentSampleIndex, &offset, &size); if (err != OK) { return err; } err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts); status_t err = mSampleTable->getMetaDataForSample( mCurrentSampleIndex, &offset, &size, &dts); if (err != OK) { return err; } err = mGroup->acquire_buffer(&mBuffer); if (err != OK) { CHECK_EQ(mBuffer, NULL); return err; Loading media/libstagefright/SampleIterator.cpp 0 → 100644 +310 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "SampleIterator" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include "include/SampleIterator.h" #include <arpa/inet.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/Utils.h> #include "include/SampleTable.h" namespace android { SampleIterator::SampleIterator(SampleTable *table) : mTable(table), mInitialized(false), mTimeToSampleIndex(0), mTTSSampleIndex(0), mTTSSampleTime(0), mTTSCount(0), mTTSDuration(0) { reset(); } void SampleIterator::reset() { mSampleToChunkIndex = 0; mFirstChunk = 0; mFirstChunkSampleIndex = 0; mStopChunk = 0; mStopChunkSampleIndex = 0; mSamplesPerChunk = 0; mChunkDesc = 0; } status_t SampleIterator::seekTo(uint32_t sampleIndex) { LOGV("seekTo(%d)", sampleIndex); if (mTable->mSampleToChunkOffset < 0 || mTable->mChunkOffsetOffset < 0 || mTable->mSampleSizeOffset < 0 || mTable->mTimeToSampleCount == 0) { return ERROR_MALFORMED; } if (mInitialized && mCurrentSampleIndex == sampleIndex) { return OK; } if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) { reset(); } if (sampleIndex >= mStopChunkSampleIndex) { status_t err; if ((err = findChunkRange(sampleIndex)) != OK) { LOGE("findChunkRange failed"); return err; } } CHECK(sampleIndex < mStopChunkSampleIndex); uint32_t chunk = (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk + mFirstChunk; if (!mInitialized || chunk != mCurrentChunkIndex) { mCurrentChunkIndex = chunk; status_t err; if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) { LOGE("getChunkOffset return error"); return err; } mCurrentChunkSampleSizes.clear(); uint32_t firstChunkSampleIndex = mFirstChunkSampleIndex + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk); for (uint32_t i = 0; i < mSamplesPerChunk; ++i) { size_t sampleSize; if ((err = getSampleSizeDirect( firstChunkSampleIndex + i, &sampleSize)) != OK) { LOGE("getSampleSizeDirect return error"); return err; } mCurrentChunkSampleSizes.push(sampleSize); } } uint32_t chunkRelativeSampleIndex = (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; mCurrentSampleOffset = mCurrentChunkOffset; for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) { mCurrentSampleOffset += mCurrentChunkSampleSizes[i]; } mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex]; if (sampleIndex < mTTSSampleIndex) { mTimeToSampleIndex = 0; mTTSSampleIndex = 0; mTTSSampleTime = 0; mTTSCount = 0; mTTSDuration = 0; } status_t err; if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) { LOGE("findSampleTime return error"); return err; } mCurrentSampleIndex = sampleIndex; mInitialized = true; return OK; } status_t SampleIterator::findChunkRange(uint32_t sampleIndex) { CHECK(sampleIndex >= mFirstChunkSampleIndex); while (sampleIndex >= mStopChunkSampleIndex) { if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) { return ERROR_OUT_OF_RANGE; } mFirstChunkSampleIndex = mStopChunkSampleIndex; const SampleTable::SampleToChunkEntry *entry = &mTable->mSampleToChunkEntries[mSampleToChunkIndex]; mFirstChunk = entry->startChunk; mSamplesPerChunk = entry->samplesPerChunk; mChunkDesc = entry->chunkDesc; if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) { mStopChunk = entry[1].startChunk; mStopChunkSampleIndex = mFirstChunkSampleIndex + (mStopChunk - mFirstChunk) * mSamplesPerChunk; } else { mStopChunk = 0xffffffff; mStopChunkSampleIndex = 0xffffffff; } ++mSampleToChunkIndex; } return OK; } status_t SampleIterator::getChunkOffset(uint32_t chunk, off_t *offset) { *offset = 0; if (chunk >= mTable->mNumChunkOffsets) { return ERROR_OUT_OF_RANGE; } if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) { uint32_t offset32; if (mTable->mDataSource->readAt( mTable->mChunkOffsetOffset + 8 + 4 * chunk, &offset32, sizeof(offset32)) < (ssize_t)sizeof(offset32)) { return ERROR_IO; } *offset = ntohl(offset32); } else { CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64); uint64_t offset64; if (mTable->mDataSource->readAt( mTable->mChunkOffsetOffset + 8 + 8 * chunk, &offset64, sizeof(offset64)) < (ssize_t)sizeof(offset64)) { return ERROR_IO; } *offset = ntoh64(offset64); } return OK; } status_t SampleIterator::getSampleSizeDirect( uint32_t sampleIndex, size_t *size) { *size = 0; if (sampleIndex >= mTable->mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } if (mTable->mDefaultSampleSize > 0) { *size = mTable->mDefaultSampleSize; return OK; } switch (mTable->mSampleSizeFieldSize) { case 32: { if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + 4 * sampleIndex, size, sizeof(*size)) < (ssize_t)sizeof(*size)) { return ERROR_IO; } *size = ntohl(*size); break; } case 16: { uint16_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + 2 * sampleIndex, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = ntohs(x); break; } case 8: { uint8_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + sampleIndex, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = x; break; } default: { CHECK_EQ(mTable->mSampleSizeFieldSize, 4); uint8_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + sampleIndex / 2, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = (sampleIndex & 1) ? x & 0x0f : x >> 4; break; } } return OK; } status_t SampleIterator::findSampleTime( uint32_t sampleIndex, uint32_t *time) { if (sampleIndex >= mTable->mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } while (sampleIndex >= mTTSSampleIndex + mTTSCount) { if (mTimeToSampleIndex == mTable->mTimeToSampleCount) { return ERROR_OUT_OF_RANGE; } mTTSSampleIndex += mTTSCount; mTTSSampleTime += mTTSCount * mTTSDuration; mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex]; mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1]; ++mTimeToSampleIndex; } *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); return OK; } } // namespace android media/libstagefright/SampleTable.cpp +79 −244 Original line number Diff line number Diff line Loading @@ -15,9 +15,11 @@ */ #define LOG_TAG "SampleTable" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include "include/SampleTable.h" #include "include/SampleIterator.h" #include <arpa/inet.h> Loading @@ -27,10 +29,16 @@ namespace android { static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); // static const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); // static const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); // static const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); // static const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); //////////////////////////////////////////////////////////////////////////////// SampleTable::SampleTable(const sp<DataSource> &source) : mDataSource(source), Loading @@ -46,12 +54,20 @@ SampleTable::SampleTable(const sp<DataSource> &source) mTimeToSampleCount(0), mTimeToSample(NULL), mSyncSampleOffset(-1), mNumSyncSamples(0) { mNumSyncSamples(0), mSampleToChunkEntries(NULL) { mSampleIterator = new SampleIterator(this); } SampleTable::~SampleTable() { delete[] mSampleToChunkEntries; mSampleToChunkEntries = NULL; delete[] mTimeToSample; mTimeToSample = NULL; delete mSampleIterator; mSampleIterator = NULL; } status_t SampleTable::setChunkOffsetParams( Loading Loading @@ -124,6 +140,25 @@ status_t SampleTable::setSampleToChunkParams( return ERROR_MALFORMED; } mSampleToChunkEntries = new SampleToChunkEntry[mNumSampleToChunkOffsets]; for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) { uint8_t buffer[12]; if (mDataSource->readAt( mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer)) != (ssize_t)sizeof(buffer)) { return ERROR_IO; } CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec. // We want the chunk index to be 0-based. mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1; mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]); mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]); } return OK; } Loading Loading @@ -250,217 +285,10 @@ uint32_t SampleTable::countChunkOffsets() const { return mNumChunkOffsets; } status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) { *offset = 0; if (mChunkOffsetOffset < 0) { return ERROR_MALFORMED; } if (chunk_index >= mNumChunkOffsets) { return ERROR_OUT_OF_RANGE; } if (mChunkOffsetType == kChunkOffsetType32) { uint32_t offset32; if (mDataSource->readAt( mChunkOffsetOffset + 8 + 4 * chunk_index, &offset32, sizeof(offset32)) < (ssize_t)sizeof(offset32)) { return ERROR_IO; } *offset = ntohl(offset32); } else { CHECK_EQ(mChunkOffsetType, kChunkOffsetType64); uint64_t offset64; if (mDataSource->readAt( mChunkOffsetOffset + 8 + 8 * chunk_index, &offset64, sizeof(offset64)) < (ssize_t)sizeof(offset64)) { return ERROR_IO; } *offset = ntoh64(offset64); } return OK; } status_t SampleTable::getChunkForSample( uint32_t sample_index, uint32_t *chunk_index, uint32_t *chunk_relative_sample_index, uint32_t *desc_index) { *chunk_index = 0; *chunk_relative_sample_index = 0; *desc_index = 0; if (mSampleToChunkOffset < 0) { return ERROR_MALFORMED; } if (sample_index >= countSamples()) { return ERROR_END_OF_STREAM; } uint32_t first_chunk = 0; uint32_t samples_per_chunk = 0; uint32_t chunk_desc_index = 0; uint32_t index = 0; while (index < mNumSampleToChunkOffsets) { uint8_t buffer[12]; if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } uint32_t stop_chunk = U32_AT(buffer); if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) { break; } sample_index -= (stop_chunk - first_chunk) * samples_per_chunk; first_chunk = stop_chunk; samples_per_chunk = U32_AT(&buffer[4]); chunk_desc_index = U32_AT(&buffer[8]); ++index; } *chunk_index = sample_index / samples_per_chunk + first_chunk - 1; *chunk_relative_sample_index = sample_index % samples_per_chunk; *desc_index = chunk_desc_index; return OK; } uint32_t SampleTable::countSamples() const { return mNumSampleSizes; } status_t SampleTable::getSampleSize( uint32_t sample_index, size_t *sample_size) { *sample_size = 0; if (mSampleSizeOffset < 0) { return ERROR_MALFORMED; } if (sample_index >= mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } if (mDefaultSampleSize > 0) { *sample_size = mDefaultSampleSize; return OK; } switch (mSampleSizeFieldSize) { case 32: { if (mDataSource->readAt( mSampleSizeOffset + 12 + 4 * sample_index, sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) { return ERROR_IO; } *sample_size = ntohl(*sample_size); break; } case 16: { uint16_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + 2 * sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = ntohs(x); break; } case 8: { uint8_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = x; break; } default: { CHECK_EQ(mSampleSizeFieldSize, 4); uint8_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index / 2, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4; break; } } return OK; } status_t SampleTable::getSampleOffsetAndSize( uint32_t sample_index, off_t *offset, size_t *size) { Mutex::Autolock autoLock(mLock); *offset = 0; *size = 0; uint32_t chunk_index; uint32_t chunk_relative_sample_index; uint32_t desc_index; status_t err = getChunkForSample( sample_index, &chunk_index, &chunk_relative_sample_index, &desc_index); if (err != OK) { return err; } err = getChunkOffset(chunk_index, offset); if (err != OK) { return err; } for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) { size_t sample_size; err = getSampleSize(sample_index - j - 1, &sample_size); if (err != OK) { return err; } *offset += sample_size; } err = getSampleSize(sample_index, size); if (err != OK) { return err; } return OK; } status_t SampleTable::getMaxSampleSize(size_t *max_size) { Mutex::Autolock autoLock(mLock); Loading @@ -468,7 +296,7 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { for (uint32_t i = 0; i < mNumSampleSizes; ++i) { size_t sample_size; status_t err = getSampleSize(i, &sample_size); status_t err = getSampleSize_l(i, &sample_size); if (err != OK) { return err; Loading @@ -482,34 +310,6 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { return OK; } status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) { // XXX FIXME idiotic (for the common use-case) O(n) algorithm below... Mutex::Autolock autoLock(mLock); if (sample_index >= mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } uint32_t cur_sample = 0; *time = 0; for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; if (sample_index < cur_sample + n) { *time += delta * (sample_index - cur_sample); return OK; } *time += delta * n; cur_sample += n; } return ERROR_OUT_OF_RANGE; } uint32_t abs_difference(uint32_t time1, uint32_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } Loading Loading @@ -539,7 +339,7 @@ status_t SampleTable::findClosestSample( } if (flags & kSyncSample_Flag) { return findClosestSyncSample(*sample_index, sample_index); return findClosestSyncSample_l(*sample_index, sample_index); } return OK; Loading @@ -552,7 +352,7 @@ status_t SampleTable::findClosestSample( return ERROR_OUT_OF_RANGE; } status_t SampleTable::findClosestSyncSample( status_t SampleTable::findClosestSyncSample_l( uint32_t start_sample_index, uint32_t *sample_index) { *sample_index = 0; Loading Loading @@ -590,6 +390,8 @@ status_t SampleTable::findClosestSyncSample( } status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { Mutex::Autolock autoLock(mLock); if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = 0; Loading Loading @@ -620,7 +422,7 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { // Now x is a sample index. size_t sampleSize; status_t err = getSampleSize(x, &sampleSize); status_t err = getSampleSize_l(x, &sampleSize); if (err != OK) { return err; } Loading @@ -636,5 +438,38 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { return OK; } status_t SampleTable::getSampleSize_l( uint32_t sampleIndex, size_t *sampleSize) { return mSampleIterator->getSampleSizeDirect( sampleIndex, sampleSize); } status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off_t *offset, size_t *size, uint32_t *decodingTime) { Mutex::Autolock autoLock(mLock); status_t err; if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) { return err; } if (offset) { *offset = mSampleIterator->getSampleOffset(); } if (size) { *size = mSampleIterator->getSampleSize(); } if (decodingTime) { *decodingTime = mSampleIterator->getSampleTime(); } return OK; } } // namespace android media/libstagefright/include/SampleIterator.h 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <utils/Vector.h> namespace android { struct SampleTable; struct SampleIterator { SampleIterator(SampleTable *table); status_t seekTo(uint32_t sampleIndex); uint32_t getChunkIndex() const { return mCurrentChunkIndex; } uint32_t getDescIndex() const { return mChunkDesc; } off_t getSampleOffset() const { return mCurrentSampleOffset; } size_t getSampleSize() const { return mCurrentSampleSize; } uint32_t getSampleTime() const { return mCurrentSampleTime; } status_t getSampleSizeDirect( uint32_t sampleIndex, size_t *size); private: SampleTable *mTable; bool mInitialized; uint32_t mSampleToChunkIndex; uint32_t mFirstChunk; uint32_t mFirstChunkSampleIndex; uint32_t mStopChunk; uint32_t mStopChunkSampleIndex; uint32_t mSamplesPerChunk; uint32_t mChunkDesc; uint32_t mCurrentChunkIndex; off_t mCurrentChunkOffset; Vector<size_t> mCurrentChunkSampleSizes; uint32_t mTimeToSampleIndex; uint32_t mTTSSampleIndex; uint32_t mTTSSampleTime; uint32_t mTTSCount; uint32_t mTTSDuration; uint32_t mCurrentSampleIndex; off_t mCurrentSampleOffset; size_t mCurrentSampleSize; uint32_t mCurrentSampleTime; void reset(); status_t findChunkRange(uint32_t sampleIndex); status_t getChunkOffset(uint32_t chunk, off_t *offset); status_t findSampleTime(uint32_t sampleIndex, uint32_t *time); SampleIterator(const SampleIterator &); SampleIterator &operator=(const SampleIterator &); }; } // namespace android Loading
media/libstagefright/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ LOCAL_SRC_FILES += \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ MediaExtractor.cpp \ SampleIterator.cpp \ SampleTable.cpp \ ShoutcastSource.cpp \ StagefrightMediaScanner.cpp \ Loading
media/libstagefright/MPEG4Extractor.cpp +7 −10 Original line number Diff line number Diff line Loading @@ -232,8 +232,9 @@ sp<MetaData> MPEG4Extractor::getTrackMetaData( uint32_t sampleIndex; uint32_t sampleTime; if (track->sampleTable->findThumbnailSample(&sampleIndex) == OK && track->sampleTable->getDecodingTime( sampleIndex, &sampleTime) == OK) { && track->sampleTable->getMetaDataForSample( sampleIndex, NULL /* offset */, NULL /* size */, &sampleTime) == OK) { track->meta->setInt64( kKeyThumbnailTime, ((int64_t)sampleTime * 1000000) / track->timescale); Loading Loading @@ -929,20 +930,16 @@ status_t MPEG4Source::read( if (mBuffer == NULL) { newBuffer = true; status_t err = mSampleTable->getSampleOffsetAndSize( mCurrentSampleIndex, &offset, &size); if (err != OK) { return err; } err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts); status_t err = mSampleTable->getMetaDataForSample( mCurrentSampleIndex, &offset, &size, &dts); if (err != OK) { return err; } err = mGroup->acquire_buffer(&mBuffer); if (err != OK) { CHECK_EQ(mBuffer, NULL); return err; Loading
media/libstagefright/SampleIterator.cpp 0 → 100644 +310 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "SampleIterator" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include "include/SampleIterator.h" #include <arpa/inet.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/Utils.h> #include "include/SampleTable.h" namespace android { SampleIterator::SampleIterator(SampleTable *table) : mTable(table), mInitialized(false), mTimeToSampleIndex(0), mTTSSampleIndex(0), mTTSSampleTime(0), mTTSCount(0), mTTSDuration(0) { reset(); } void SampleIterator::reset() { mSampleToChunkIndex = 0; mFirstChunk = 0; mFirstChunkSampleIndex = 0; mStopChunk = 0; mStopChunkSampleIndex = 0; mSamplesPerChunk = 0; mChunkDesc = 0; } status_t SampleIterator::seekTo(uint32_t sampleIndex) { LOGV("seekTo(%d)", sampleIndex); if (mTable->mSampleToChunkOffset < 0 || mTable->mChunkOffsetOffset < 0 || mTable->mSampleSizeOffset < 0 || mTable->mTimeToSampleCount == 0) { return ERROR_MALFORMED; } if (mInitialized && mCurrentSampleIndex == sampleIndex) { return OK; } if (!mInitialized || sampleIndex < mFirstChunkSampleIndex) { reset(); } if (sampleIndex >= mStopChunkSampleIndex) { status_t err; if ((err = findChunkRange(sampleIndex)) != OK) { LOGE("findChunkRange failed"); return err; } } CHECK(sampleIndex < mStopChunkSampleIndex); uint32_t chunk = (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk + mFirstChunk; if (!mInitialized || chunk != mCurrentChunkIndex) { mCurrentChunkIndex = chunk; status_t err; if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) { LOGE("getChunkOffset return error"); return err; } mCurrentChunkSampleSizes.clear(); uint32_t firstChunkSampleIndex = mFirstChunkSampleIndex + mSamplesPerChunk * (mCurrentChunkIndex - mFirstChunk); for (uint32_t i = 0; i < mSamplesPerChunk; ++i) { size_t sampleSize; if ((err = getSampleSizeDirect( firstChunkSampleIndex + i, &sampleSize)) != OK) { LOGE("getSampleSizeDirect return error"); return err; } mCurrentChunkSampleSizes.push(sampleSize); } } uint32_t chunkRelativeSampleIndex = (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; mCurrentSampleOffset = mCurrentChunkOffset; for (uint32_t i = 0; i < chunkRelativeSampleIndex; ++i) { mCurrentSampleOffset += mCurrentChunkSampleSizes[i]; } mCurrentSampleSize = mCurrentChunkSampleSizes[chunkRelativeSampleIndex]; if (sampleIndex < mTTSSampleIndex) { mTimeToSampleIndex = 0; mTTSSampleIndex = 0; mTTSSampleTime = 0; mTTSCount = 0; mTTSDuration = 0; } status_t err; if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) { LOGE("findSampleTime return error"); return err; } mCurrentSampleIndex = sampleIndex; mInitialized = true; return OK; } status_t SampleIterator::findChunkRange(uint32_t sampleIndex) { CHECK(sampleIndex >= mFirstChunkSampleIndex); while (sampleIndex >= mStopChunkSampleIndex) { if (mSampleToChunkIndex == mTable->mNumSampleToChunkOffsets) { return ERROR_OUT_OF_RANGE; } mFirstChunkSampleIndex = mStopChunkSampleIndex; const SampleTable::SampleToChunkEntry *entry = &mTable->mSampleToChunkEntries[mSampleToChunkIndex]; mFirstChunk = entry->startChunk; mSamplesPerChunk = entry->samplesPerChunk; mChunkDesc = entry->chunkDesc; if (mSampleToChunkIndex + 1 < mTable->mNumSampleToChunkOffsets) { mStopChunk = entry[1].startChunk; mStopChunkSampleIndex = mFirstChunkSampleIndex + (mStopChunk - mFirstChunk) * mSamplesPerChunk; } else { mStopChunk = 0xffffffff; mStopChunkSampleIndex = 0xffffffff; } ++mSampleToChunkIndex; } return OK; } status_t SampleIterator::getChunkOffset(uint32_t chunk, off_t *offset) { *offset = 0; if (chunk >= mTable->mNumChunkOffsets) { return ERROR_OUT_OF_RANGE; } if (mTable->mChunkOffsetType == SampleTable::kChunkOffsetType32) { uint32_t offset32; if (mTable->mDataSource->readAt( mTable->mChunkOffsetOffset + 8 + 4 * chunk, &offset32, sizeof(offset32)) < (ssize_t)sizeof(offset32)) { return ERROR_IO; } *offset = ntohl(offset32); } else { CHECK_EQ(mTable->mChunkOffsetType, SampleTable::kChunkOffsetType64); uint64_t offset64; if (mTable->mDataSource->readAt( mTable->mChunkOffsetOffset + 8 + 8 * chunk, &offset64, sizeof(offset64)) < (ssize_t)sizeof(offset64)) { return ERROR_IO; } *offset = ntoh64(offset64); } return OK; } status_t SampleIterator::getSampleSizeDirect( uint32_t sampleIndex, size_t *size) { *size = 0; if (sampleIndex >= mTable->mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } if (mTable->mDefaultSampleSize > 0) { *size = mTable->mDefaultSampleSize; return OK; } switch (mTable->mSampleSizeFieldSize) { case 32: { if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + 4 * sampleIndex, size, sizeof(*size)) < (ssize_t)sizeof(*size)) { return ERROR_IO; } *size = ntohl(*size); break; } case 16: { uint16_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + 2 * sampleIndex, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = ntohs(x); break; } case 8: { uint8_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + sampleIndex, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = x; break; } default: { CHECK_EQ(mTable->mSampleSizeFieldSize, 4); uint8_t x; if (mTable->mDataSource->readAt( mTable->mSampleSizeOffset + 12 + sampleIndex / 2, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *size = (sampleIndex & 1) ? x & 0x0f : x >> 4; break; } } return OK; } status_t SampleIterator::findSampleTime( uint32_t sampleIndex, uint32_t *time) { if (sampleIndex >= mTable->mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } while (sampleIndex >= mTTSSampleIndex + mTTSCount) { if (mTimeToSampleIndex == mTable->mTimeToSampleCount) { return ERROR_OUT_OF_RANGE; } mTTSSampleIndex += mTTSCount; mTTSSampleTime += mTTSCount * mTTSDuration; mTTSCount = mTable->mTimeToSample[2 * mTimeToSampleIndex]; mTTSDuration = mTable->mTimeToSample[2 * mTimeToSampleIndex + 1]; ++mTimeToSampleIndex; } *time = mTTSSampleTime + mTTSDuration * (sampleIndex - mTTSSampleIndex); return OK; } } // namespace android
media/libstagefright/SampleTable.cpp +79 −244 Original line number Diff line number Diff line Loading @@ -15,9 +15,11 @@ */ #define LOG_TAG "SampleTable" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include "include/SampleTable.h" #include "include/SampleIterator.h" #include <arpa/inet.h> Loading @@ -27,10 +29,16 @@ namespace android { static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); // static const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); // static const uint32_t SampleTable::kChunkOffsetType64 = FOURCC('c', 'o', '6', '4'); // static const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z'); // static const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); //////////////////////////////////////////////////////////////////////////////// SampleTable::SampleTable(const sp<DataSource> &source) : mDataSource(source), Loading @@ -46,12 +54,20 @@ SampleTable::SampleTable(const sp<DataSource> &source) mTimeToSampleCount(0), mTimeToSample(NULL), mSyncSampleOffset(-1), mNumSyncSamples(0) { mNumSyncSamples(0), mSampleToChunkEntries(NULL) { mSampleIterator = new SampleIterator(this); } SampleTable::~SampleTable() { delete[] mSampleToChunkEntries; mSampleToChunkEntries = NULL; delete[] mTimeToSample; mTimeToSample = NULL; delete mSampleIterator; mSampleIterator = NULL; } status_t SampleTable::setChunkOffsetParams( Loading Loading @@ -124,6 +140,25 @@ status_t SampleTable::setSampleToChunkParams( return ERROR_MALFORMED; } mSampleToChunkEntries = new SampleToChunkEntry[mNumSampleToChunkOffsets]; for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) { uint8_t buffer[12]; if (mDataSource->readAt( mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer)) != (ssize_t)sizeof(buffer)) { return ERROR_IO; } CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec. // We want the chunk index to be 0-based. mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1; mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]); mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]); } return OK; } Loading Loading @@ -250,217 +285,10 @@ uint32_t SampleTable::countChunkOffsets() const { return mNumChunkOffsets; } status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) { *offset = 0; if (mChunkOffsetOffset < 0) { return ERROR_MALFORMED; } if (chunk_index >= mNumChunkOffsets) { return ERROR_OUT_OF_RANGE; } if (mChunkOffsetType == kChunkOffsetType32) { uint32_t offset32; if (mDataSource->readAt( mChunkOffsetOffset + 8 + 4 * chunk_index, &offset32, sizeof(offset32)) < (ssize_t)sizeof(offset32)) { return ERROR_IO; } *offset = ntohl(offset32); } else { CHECK_EQ(mChunkOffsetType, kChunkOffsetType64); uint64_t offset64; if (mDataSource->readAt( mChunkOffsetOffset + 8 + 8 * chunk_index, &offset64, sizeof(offset64)) < (ssize_t)sizeof(offset64)) { return ERROR_IO; } *offset = ntoh64(offset64); } return OK; } status_t SampleTable::getChunkForSample( uint32_t sample_index, uint32_t *chunk_index, uint32_t *chunk_relative_sample_index, uint32_t *desc_index) { *chunk_index = 0; *chunk_relative_sample_index = 0; *desc_index = 0; if (mSampleToChunkOffset < 0) { return ERROR_MALFORMED; } if (sample_index >= countSamples()) { return ERROR_END_OF_STREAM; } uint32_t first_chunk = 0; uint32_t samples_per_chunk = 0; uint32_t chunk_desc_index = 0; uint32_t index = 0; while (index < mNumSampleToChunkOffsets) { uint8_t buffer[12]; if (mDataSource->readAt(mSampleToChunkOffset + 8 + index * 12, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { return ERROR_IO; } uint32_t stop_chunk = U32_AT(buffer); if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) { break; } sample_index -= (stop_chunk - first_chunk) * samples_per_chunk; first_chunk = stop_chunk; samples_per_chunk = U32_AT(&buffer[4]); chunk_desc_index = U32_AT(&buffer[8]); ++index; } *chunk_index = sample_index / samples_per_chunk + first_chunk - 1; *chunk_relative_sample_index = sample_index % samples_per_chunk; *desc_index = chunk_desc_index; return OK; } uint32_t SampleTable::countSamples() const { return mNumSampleSizes; } status_t SampleTable::getSampleSize( uint32_t sample_index, size_t *sample_size) { *sample_size = 0; if (mSampleSizeOffset < 0) { return ERROR_MALFORMED; } if (sample_index >= mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } if (mDefaultSampleSize > 0) { *sample_size = mDefaultSampleSize; return OK; } switch (mSampleSizeFieldSize) { case 32: { if (mDataSource->readAt( mSampleSizeOffset + 12 + 4 * sample_index, sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) { return ERROR_IO; } *sample_size = ntohl(*sample_size); break; } case 16: { uint16_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + 2 * sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = ntohs(x); break; } case 8: { uint8_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = x; break; } default: { CHECK_EQ(mSampleSizeFieldSize, 4); uint8_t x; if (mDataSource->readAt( mSampleSizeOffset + 12 + sample_index / 2, &x, sizeof(x)) < (ssize_t)sizeof(x)) { return ERROR_IO; } *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4; break; } } return OK; } status_t SampleTable::getSampleOffsetAndSize( uint32_t sample_index, off_t *offset, size_t *size) { Mutex::Autolock autoLock(mLock); *offset = 0; *size = 0; uint32_t chunk_index; uint32_t chunk_relative_sample_index; uint32_t desc_index; status_t err = getChunkForSample( sample_index, &chunk_index, &chunk_relative_sample_index, &desc_index); if (err != OK) { return err; } err = getChunkOffset(chunk_index, offset); if (err != OK) { return err; } for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) { size_t sample_size; err = getSampleSize(sample_index - j - 1, &sample_size); if (err != OK) { return err; } *offset += sample_size; } err = getSampleSize(sample_index, size); if (err != OK) { return err; } return OK; } status_t SampleTable::getMaxSampleSize(size_t *max_size) { Mutex::Autolock autoLock(mLock); Loading @@ -468,7 +296,7 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { for (uint32_t i = 0; i < mNumSampleSizes; ++i) { size_t sample_size; status_t err = getSampleSize(i, &sample_size); status_t err = getSampleSize_l(i, &sample_size); if (err != OK) { return err; Loading @@ -482,34 +310,6 @@ status_t SampleTable::getMaxSampleSize(size_t *max_size) { return OK; } status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) { // XXX FIXME idiotic (for the common use-case) O(n) algorithm below... Mutex::Autolock autoLock(mLock); if (sample_index >= mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } uint32_t cur_sample = 0; *time = 0; for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; if (sample_index < cur_sample + n) { *time += delta * (sample_index - cur_sample); return OK; } *time += delta * n; cur_sample += n; } return ERROR_OUT_OF_RANGE; } uint32_t abs_difference(uint32_t time1, uint32_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } Loading Loading @@ -539,7 +339,7 @@ status_t SampleTable::findClosestSample( } if (flags & kSyncSample_Flag) { return findClosestSyncSample(*sample_index, sample_index); return findClosestSyncSample_l(*sample_index, sample_index); } return OK; Loading @@ -552,7 +352,7 @@ status_t SampleTable::findClosestSample( return ERROR_OUT_OF_RANGE; } status_t SampleTable::findClosestSyncSample( status_t SampleTable::findClosestSyncSample_l( uint32_t start_sample_index, uint32_t *sample_index) { *sample_index = 0; Loading Loading @@ -590,6 +390,8 @@ status_t SampleTable::findClosestSyncSample( } status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { Mutex::Autolock autoLock(mLock); if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = 0; Loading Loading @@ -620,7 +422,7 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { // Now x is a sample index. size_t sampleSize; status_t err = getSampleSize(x, &sampleSize); status_t err = getSampleSize_l(x, &sampleSize); if (err != OK) { return err; } Loading @@ -636,5 +438,38 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { return OK; } status_t SampleTable::getSampleSize_l( uint32_t sampleIndex, size_t *sampleSize) { return mSampleIterator->getSampleSizeDirect( sampleIndex, sampleSize); } status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off_t *offset, size_t *size, uint32_t *decodingTime) { Mutex::Autolock autoLock(mLock); status_t err; if ((err = mSampleIterator->seekTo(sampleIndex)) != OK) { return err; } if (offset) { *offset = mSampleIterator->getSampleOffset(); } if (size) { *size = mSampleIterator->getSampleSize(); } if (decodingTime) { *decodingTime = mSampleIterator->getSampleTime(); } return OK; } } // namespace android
media/libstagefright/include/SampleIterator.h 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <utils/Vector.h> namespace android { struct SampleTable; struct SampleIterator { SampleIterator(SampleTable *table); status_t seekTo(uint32_t sampleIndex); uint32_t getChunkIndex() const { return mCurrentChunkIndex; } uint32_t getDescIndex() const { return mChunkDesc; } off_t getSampleOffset() const { return mCurrentSampleOffset; } size_t getSampleSize() const { return mCurrentSampleSize; } uint32_t getSampleTime() const { return mCurrentSampleTime; } status_t getSampleSizeDirect( uint32_t sampleIndex, size_t *size); private: SampleTable *mTable; bool mInitialized; uint32_t mSampleToChunkIndex; uint32_t mFirstChunk; uint32_t mFirstChunkSampleIndex; uint32_t mStopChunk; uint32_t mStopChunkSampleIndex; uint32_t mSamplesPerChunk; uint32_t mChunkDesc; uint32_t mCurrentChunkIndex; off_t mCurrentChunkOffset; Vector<size_t> mCurrentChunkSampleSizes; uint32_t mTimeToSampleIndex; uint32_t mTTSSampleIndex; uint32_t mTTSSampleTime; uint32_t mTTSCount; uint32_t mTTSDuration; uint32_t mCurrentSampleIndex; off_t mCurrentSampleOffset; size_t mCurrentSampleSize; uint32_t mCurrentSampleTime; void reset(); status_t findChunkRange(uint32_t sampleIndex); status_t getChunkOffset(uint32_t chunk, off_t *offset); status_t findSampleTime(uint32_t sampleIndex, uint32_t *time); SampleIterator(const SampleIterator &); SampleIterator &operator=(const SampleIterator &); }; } // namespace android