/* * Copyright (C) 2009 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 "SampleTable" //#define LOG_NDEBUG 0 #include #include #include "SampleTable.h" #include "SampleIterator.h" #include #include #include #include /* TODO: remove after being merged into other branches */ #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif namespace android { // static const uint32_t SampleTable::kChunkOffsetType32 = FOURCC("stco"); // static const uint32_t SampleTable::kChunkOffsetType64 = FOURCC("co64"); // static const uint32_t SampleTable::kSampleSizeType32 = FOURCC("stsz"); // static const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC("stz2"); //////////////////////////////////////////////////////////////////////////////// const off64_t kMaxOffset = std::numeric_limits::max(); struct SampleTable::CompositionDeltaLookup { CompositionDeltaLookup(); void setEntries( const int32_t *deltaEntries, size_t numDeltaEntries); int32_t getCompositionTimeOffset(uint32_t sampleIndex); private: Mutex mLock; const int32_t *mDeltaEntries; size_t mNumDeltaEntries; size_t mCurrentDeltaEntry; size_t mCurrentEntrySampleIndex; DISALLOW_EVIL_CONSTRUCTORS(CompositionDeltaLookup); }; SampleTable::CompositionDeltaLookup::CompositionDeltaLookup() : mDeltaEntries(NULL), mNumDeltaEntries(0), mCurrentDeltaEntry(0), mCurrentEntrySampleIndex(0) { } void SampleTable::CompositionDeltaLookup::setEntries( const int32_t *deltaEntries, size_t numDeltaEntries) { Mutex::Autolock autolock(mLock); mDeltaEntries = deltaEntries; mNumDeltaEntries = numDeltaEntries; mCurrentDeltaEntry = 0; mCurrentEntrySampleIndex = 0; } int32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset( uint32_t sampleIndex) { Mutex::Autolock autolock(mLock); if (mDeltaEntries == NULL) { return 0; } if (sampleIndex < mCurrentEntrySampleIndex) { mCurrentDeltaEntry = 0; mCurrentEntrySampleIndex = 0; } while (mCurrentDeltaEntry < mNumDeltaEntries) { uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry]; if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) { return mDeltaEntries[2 * mCurrentDeltaEntry + 1]; } mCurrentEntrySampleIndex += sampleCount; ++mCurrentDeltaEntry; } return 0; } //////////////////////////////////////////////////////////////////////////////// SampleTable::SampleTable(DataSourceHelper *source) : mDataSource(source), mChunkOffsetOffset(-1), mChunkOffsetType(0), mNumChunkOffsets(0), mSampleToChunkOffset(-1), mNumSampleToChunkOffsets(0), mSampleSizeOffset(-1), mSampleSizeFieldSize(0), mDefaultSampleSize(0), mNumSampleSizes(0), mHasTimeToSample(false), mTimeToSampleCount(0), mTimeToSample(NULL), mSampleTimeEntries(NULL), mCompositionTimeDeltaEntries(NULL), mNumCompositionTimeDeltaEntries(0), mCompositionDeltaLookup(new CompositionDeltaLookup), mSyncSampleOffset(-1), mNumSyncSamples(0), mSyncSamples(NULL), mLastSyncSampleIndex(0), mSampleToChunkEntries(NULL), mTotalSize(0) { mSampleIterator = new SampleIterator(this); } SampleTable::~SampleTable() { delete[] mSampleToChunkEntries; mSampleToChunkEntries = NULL; delete[] mSyncSamples; mSyncSamples = NULL; delete[] mTimeToSample; mTimeToSample = NULL; delete mCompositionDeltaLookup; mCompositionDeltaLookup = NULL; delete[] mCompositionTimeDeltaEntries; mCompositionTimeDeltaEntries = NULL; delete[] mSampleTimeEntries; mSampleTimeEntries = NULL; delete mSampleIterator; mSampleIterator = NULL; } bool SampleTable::isValid() const { return mChunkOffsetOffset >= 0 && mSampleToChunkOffset >= 0 && mSampleSizeOffset >= 0 && mHasTimeToSample; } status_t SampleTable::setChunkOffsetParams( uint32_t type, off64_t data_offset, size_t data_size) { if (mChunkOffsetOffset >= 0) { return ERROR_MALFORMED; } CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64); mChunkOffsetOffset = data_offset; mChunkOffsetType = type; if (data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } if (U32_AT(header) != 0) { // Expected version = 0, flags = 0. return ERROR_MALFORMED; } mNumChunkOffsets = U32_AT(&header[4]); if (mChunkOffsetType == kChunkOffsetType32) { if ((data_size - 8) / 4 < mNumChunkOffsets) { return ERROR_MALFORMED; } } else { if ((data_size - 8) / 8 < mNumChunkOffsets) { return ERROR_MALFORMED; } } return OK; } status_t SampleTable::setSampleToChunkParams( off64_t data_offset, size_t data_size) { if (mSampleToChunkOffset >= 0) { // already set return ERROR_MALFORMED; } if (data_offset < 0) { return ERROR_MALFORMED; } mSampleToChunkOffset = data_offset; if (data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } if (U32_AT(header) != 0) { // Expected version = 0, flags = 0. return ERROR_MALFORMED; } mNumSampleToChunkOffsets = U32_AT(&header[4]); if ((data_size - 8) / sizeof(SampleToChunkEntry) < mNumSampleToChunkOffsets) { return ERROR_MALFORMED; } if ((uint64_t)kMaxTotalSize / sizeof(SampleToChunkEntry) <= (uint64_t)mNumSampleToChunkOffsets) { ALOGE("Sample-to-chunk table size too large."); return ERROR_OUT_OF_RANGE; } mTotalSize += (uint64_t)mNumSampleToChunkOffsets * sizeof(SampleToChunkEntry); if (mTotalSize > kMaxTotalSize) { ALOGE("Sample-to-chunk table size would make sample table too large.\n" " Requested sample-to-chunk table size = %llu\n" " Eventual sample table size >= %llu\n" " Allowed sample table size = %llu\n", (unsigned long long)mNumSampleToChunkOffsets * sizeof(SampleToChunkEntry), (unsigned long long)mTotalSize, (unsigned long long)kMaxTotalSize); return ERROR_OUT_OF_RANGE; } mSampleToChunkEntries = new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets]; if (!mSampleToChunkEntries) { ALOGE("Cannot allocate sample-to-chunk table with %llu entries.", (unsigned long long)mNumSampleToChunkOffsets); return ERROR_OUT_OF_RANGE; } if (mNumSampleToChunkOffsets == 0) { return OK; } if ((off64_t)(kMaxOffset - 8 - ((mNumSampleToChunkOffsets - 1) * sizeof(SampleToChunkEntry))) < mSampleToChunkOffset) { return ERROR_MALFORMED; } for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) { uint8_t buffer[sizeof(SampleToChunkEntry)]; if (mDataSource->readAt( mSampleToChunkOffset + 8 + i * sizeof(SampleToChunkEntry), buffer, sizeof(buffer)) != (ssize_t)sizeof(buffer)) { return ERROR_IO; } // chunk index is 1 based in the spec. if (U32_AT(buffer) < 1) { ALOGE("b/23534160"); return ERROR_OUT_OF_RANGE; } // 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; } status_t SampleTable::setSampleSizeParams( uint32_t type, off64_t data_offset, size_t data_size) { if (mSampleSizeOffset >= 0) { return ERROR_MALFORMED; } CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact); mSampleSizeOffset = data_offset; if (data_size < 12) { return ERROR_MALFORMED; } uint8_t header[12]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } if (U32_AT(header) != 0) { // Expected version = 0, flags = 0. return ERROR_MALFORMED; } mDefaultSampleSize = U32_AT(&header[4]); mNumSampleSizes = U32_AT(&header[8]); if (mNumSampleSizes > (UINT32_MAX - 12) / 16) { ALOGE("b/23247055, mNumSampleSizes(%u)", mNumSampleSizes); return ERROR_MALFORMED; } if (type == kSampleSizeType32) { mSampleSizeFieldSize = 32; if (mDefaultSampleSize != 0) { return OK; } if (data_size < 12 + mNumSampleSizes * 4) { return ERROR_MALFORMED; } } else { if ((mDefaultSampleSize & 0xffffff00) != 0) { // The high 24 bits are reserved and must be 0. return ERROR_MALFORMED; } mSampleSizeFieldSize = mDefaultSampleSize & 0xff; mDefaultSampleSize = 0; if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8 && mSampleSizeFieldSize != 16) { return ERROR_MALFORMED; } if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) { return ERROR_MALFORMED; } } return OK; } status_t SampleTable::setTimeToSampleParams( off64_t data_offset, size_t data_size) { if (mHasTimeToSample || data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } if (U32_AT(header) != 0) { // Expected version = 0, flags = 0. return ERROR_MALFORMED; } mTimeToSampleCount = U32_AT(&header[4]); if (mTimeToSampleCount > UINT32_MAX / (2 * sizeof(uint32_t))) { // Choose this bound because // 1) 2 * sizeof(uint32_t) is the amount of memory needed for one // time-to-sample entry in the time-to-sample table. // 2) mTimeToSampleCount is the number of entries of the time-to-sample // table. // 3) We hope that the table size does not exceed UINT32_MAX. ALOGE("Time-to-sample table size too large."); return ERROR_OUT_OF_RANGE; } // Note: At this point, we know that mTimeToSampleCount * 2 will not // overflow because of the above condition. uint64_t allocSize = (uint64_t)mTimeToSampleCount * 2 * sizeof(uint32_t); mTotalSize += allocSize; if (mTotalSize > kMaxTotalSize) { ALOGE("Time-to-sample table size would make sample table too large.\n" " Requested time-to-sample table size = %llu\n" " Eventual sample table size >= %llu\n" " Allowed sample table size = %llu\n", (unsigned long long)allocSize, (unsigned long long)mTotalSize, (unsigned long long)kMaxTotalSize); return ERROR_OUT_OF_RANGE; } mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2]; if (!mTimeToSample) { ALOGE("Cannot allocate time-to-sample table with %llu entries.", (unsigned long long)mTimeToSampleCount); return ERROR_OUT_OF_RANGE; } if (mDataSource->readAt(data_offset + 8, mTimeToSample, (size_t)allocSize) < (ssize_t)allocSize) { ALOGE("Incomplete data read for time-to-sample table."); return ERROR_IO; } for (size_t i = 0; i < mTimeToSampleCount * 2; ++i) { mTimeToSample[i] = ntohl(mTimeToSample[i]); } mHasTimeToSample = true; return OK; } // NOTE: per 14996-12, version 0 ctts contains unsigned values, while version 1 // contains signed values, however some software creates version 0 files that // contain signed values, so we're always treating the values as signed, // regardless of version. status_t SampleTable::setCompositionTimeToSampleParams( off64_t data_offset, size_t data_size) { ALOGI("There are reordered frames present."); if (mCompositionTimeDeltaEntries != NULL || data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } uint32_t flags = U32_AT(header); uint32_t version = flags >> 24; flags &= 0xffffff; if ((version != 0 && version != 1) || flags != 0) { // Expected version = 0 or 1, flags = 0. return ERROR_MALFORMED; } size_t numEntries = U32_AT(&header[4]); if (((SIZE_MAX / 8) - 1 < numEntries) || (data_size != (numEntries + 1) * 8)) { return ERROR_MALFORMED; } mNumCompositionTimeDeltaEntries = numEntries; uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(int32_t); if (allocSize > kMaxTotalSize) { ALOGE("Composition-time-to-sample table size too large."); return ERROR_OUT_OF_RANGE; } mTotalSize += allocSize; if (mTotalSize > kMaxTotalSize) { ALOGE("Composition-time-to-sample table would make sample table too large.\n" " Requested composition-time-to-sample table size = %llu\n" " Eventual sample table size >= %llu\n" " Allowed sample table size = %llu\n", (unsigned long long)allocSize, (unsigned long long)mTotalSize, (unsigned long long)kMaxTotalSize); return ERROR_OUT_OF_RANGE; } mCompositionTimeDeltaEntries = new (std::nothrow) int32_t[2 * numEntries]; if (!mCompositionTimeDeltaEntries) { ALOGE("Cannot allocate composition-time-to-sample table with %llu " "entries.", (unsigned long long)numEntries); return ERROR_OUT_OF_RANGE; } if (mDataSource->readAt(data_offset + 8, mCompositionTimeDeltaEntries, (size_t)allocSize) < (ssize_t)allocSize) { delete[] mCompositionTimeDeltaEntries; mCompositionTimeDeltaEntries = NULL; return ERROR_IO; } for (size_t i = 0; i < 2 * numEntries; ++i) { mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]); } mCompositionDeltaLookup->setEntries( mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries); return OK; } status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) { if (mSyncSampleOffset >= 0 || data_size < 8) { return ERROR_MALFORMED; } uint8_t header[8]; if (mDataSource->readAt( data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) { return ERROR_IO; } if (U32_AT(header) != 0) { // Expected version = 0, flags = 0. return ERROR_MALFORMED; } uint32_t numSyncSamples = U32_AT(&header[4]); if (numSyncSamples < 2) { ALOGV("Table of sync samples is empty or has only a single entry!"); } uint64_t allocSize = (uint64_t)numSyncSamples * sizeof(uint32_t); if (allocSize > kMaxTotalSize) { ALOGE("Sync sample table size too large."); return ERROR_OUT_OF_RANGE; } mTotalSize += allocSize; if (mTotalSize > kMaxTotalSize) { ALOGE("Sync sample table size would make sample table too large.\n" " Requested sync sample table size = %llu\n" " Eventual sample table size >= %llu\n" " Allowed sample table size = %llu\n", (unsigned long long)allocSize, (unsigned long long)mTotalSize, (unsigned long long)kMaxTotalSize); return ERROR_OUT_OF_RANGE; } mSyncSamples = new (std::nothrow) uint32_t[numSyncSamples]; if (!mSyncSamples) { ALOGE("Cannot allocate sync sample table with %llu entries.", (unsigned long long)numSyncSamples); return ERROR_OUT_OF_RANGE; } if (mDataSource->readAt(data_offset + 8, mSyncSamples, (size_t)allocSize) != (ssize_t)allocSize) { delete[] mSyncSamples; mSyncSamples = NULL; return ERROR_IO; } for (size_t i = 0; i < numSyncSamples; ++i) { if (mSyncSamples[i] == 0) { ALOGE("b/32423862, unexpected zero value in stss"); continue; } mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1; } mSyncSampleOffset = data_offset; mNumSyncSamples = numSyncSamples; return OK; } uint32_t SampleTable::countChunkOffsets() const { return mNumChunkOffsets; } uint32_t SampleTable::countSamples() const { return mNumSampleSizes; } status_t SampleTable::getMaxSampleSize(size_t *max_size) { Mutex::Autolock autoLock(mLock); *max_size = 0; for (uint32_t i = 0; i < mNumSampleSizes; ++i) { size_t sample_size; status_t err = getSampleSize_l(i, &sample_size); if (err != OK) { return err; } if (sample_size > *max_size) { *max_size = sample_size; } } return OK; } uint32_t abs_difference(uint64_t time1, uint64_t time2) { return time1 > time2 ? time1 - time2 : time2 - time1; } // static int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) { const SampleTimeEntry *a = (const SampleTimeEntry *)_a; const SampleTimeEntry *b = (const SampleTimeEntry *)_b; if (a->mCompositionTime < b->mCompositionTime) { return -1; } else if (a->mCompositionTime > b->mCompositionTime) { return 1; } return 0; } void SampleTable::buildSampleEntriesTable() { Mutex::Autolock autoLock(mLock); if (mSampleTimeEntries != NULL || mNumSampleSizes == 0) { if (mNumSampleSizes == 0) { ALOGE("b/23247055, mNumSampleSizes(%u)", mNumSampleSizes); } return; } mTotalSize += (uint64_t)mNumSampleSizes * sizeof(SampleTimeEntry); if (mTotalSize > kMaxTotalSize) { ALOGE("Sample entry table size would make sample table too large.\n" " Requested sample entry table size = %llu\n" " Eventual sample table size >= %llu\n" " Allowed sample table size = %llu\n", (unsigned long long)mNumSampleSizes * sizeof(SampleTimeEntry), (unsigned long long)mTotalSize, (unsigned long long)kMaxTotalSize); return; } mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes]; if (!mSampleTimeEntries) { ALOGE("Cannot allocate sample entry table with %llu entries.", (unsigned long long)mNumSampleSizes); return; } uint32_t sampleIndex = 0; uint64_t sampleTime = 0; for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; for (uint32_t j = 0; j < n; ++j) { if (sampleIndex < mNumSampleSizes) { // Technically this should always be the case if the file // is well-formed, but you know... there's (gasp) malformed // content out there. mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex; int32_t compTimeDelta = mCompositionDeltaLookup->getCompositionTimeOffset( sampleIndex); if ((compTimeDelta < 0 && sampleTime < (compTimeDelta == INT32_MIN ? INT32_MAX : uint32_t(-compTimeDelta))) || (compTimeDelta > 0 && sampleTime > UINT64_MAX - compTimeDelta)) { ALOGE("%llu + %d would overflow, clamping", (unsigned long long) sampleTime, compTimeDelta); if (compTimeDelta < 0) { sampleTime = 0; } else { sampleTime = UINT64_MAX; } compTimeDelta = 0; } mSampleTimeEntries[sampleIndex].mCompositionTime = compTimeDelta > 0 ? sampleTime + compTimeDelta: sampleTime - (-compTimeDelta); } ++sampleIndex; if (sampleTime > UINT64_MAX - delta) { ALOGE("%llu + %u would overflow, clamping", (unsigned long long) sampleTime, delta); sampleTime = UINT64_MAX; } else { sampleTime += delta; } } } qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry), CompareIncreasingTime); } status_t SampleTable::findSampleAtTime( uint64_t req_time, uint64_t scale_num, uint64_t scale_den, uint32_t *sample_index, uint32_t flags) { buildSampleEntriesTable(); if (mSampleTimeEntries == NULL) { return ERROR_OUT_OF_RANGE; } if (flags == kFlagFrameIndex) { if (req_time >= mNumSampleSizes) { return ERROR_OUT_OF_RANGE; } *sample_index = mSampleTimeEntries[req_time].mSampleIndex; return OK; } uint32_t left = 0; uint32_t right_plus_one = mNumSampleSizes; while (left < right_plus_one) { uint32_t center = left + (right_plus_one - left) / 2; uint64_t centerTime = getSampleTime(center, scale_num, scale_den); if (req_time < centerTime) { right_plus_one = center; } else if (req_time > centerTime) { left = center + 1; } else { *sample_index = mSampleTimeEntries[center].mSampleIndex; return OK; } } uint32_t closestIndex = left; if (closestIndex == mNumSampleSizes) { if (flags == kFlagAfter) { return ERROR_OUT_OF_RANGE; } flags = kFlagBefore; } else if (closestIndex == 0) { if (flags == kFlagBefore) { // normally we should return out of range, but that is // treated as end-of-stream. instead return first sample // // return ERROR_OUT_OF_RANGE; } flags = kFlagAfter; } switch (flags) { case kFlagBefore: { --closestIndex; break; } case kFlagAfter: { // nothing to do break; } default: { CHECK(flags == kFlagClosest); // pick closest based on timestamp. use abs_difference for safety if (abs_difference( getSampleTime(closestIndex, scale_num, scale_den), req_time) > abs_difference( req_time, getSampleTime(closestIndex - 1, scale_num, scale_den))) { --closestIndex; } break; } } *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; return OK; } status_t SampleTable::findSyncSampleNear( uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) { Mutex::Autolock autoLock(mLock); *sample_index = 0; if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = start_sample_index; return OK; } if (mNumSyncSamples == 0) { *sample_index = 0; return OK; } uint32_t left = 0; uint32_t right_plus_one = mNumSyncSamples; while (left < right_plus_one) { uint32_t center = left + (right_plus_one - left) / 2; uint32_t x = mSyncSamples[center]; if (start_sample_index < x) { right_plus_one = center; } else if (start_sample_index > x) { left = center + 1; } else { *sample_index = x; return OK; } } if (left == mNumSyncSamples) { if (flags == kFlagAfter) { ALOGE("tried to find a sync frame after the last one: %d", left); return ERROR_OUT_OF_RANGE; } flags = kFlagBefore; } else if (left == 0) { if (flags == kFlagBefore) { ALOGE("tried to find a sync frame before the first one: %d", left); // normally we should return out of range, but that is // treated as end-of-stream. instead seek to first sync // // return ERROR_OUT_OF_RANGE; } flags = kFlagAfter; } // Now ssi[left - 1] <(=) start_sample_index <= ssi[left] switch (flags) { case kFlagBefore: { --left; break; } case kFlagAfter: { // nothing to do break; } default: { // this route is not used, but implement it nonetheless CHECK(flags == kFlagClosest); status_t err = mSampleIterator->seekTo(start_sample_index); if (err != OK) { return err; } uint64_t sample_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(mSyncSamples[left]); if (err != OK) { return err; } uint64_t upper_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(mSyncSamples[left - 1]); if (err != OK) { return err; } uint64_t lower_time = mSampleIterator->getSampleTime(); // use abs_difference for safety if (abs_difference(upper_time, sample_time) > abs_difference(sample_time, lower_time)) { --left; } break; } } *sample_index = mSyncSamples[left]; return OK; } status_t SampleTable::findThumbnailSample(uint32_t *sample_index) { Mutex::Autolock autoLock(mLock); if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = 0; return OK; } uint32_t bestSampleIndex = 0; size_t maxSampleSize = 0; static const size_t kMaxNumSyncSamplesToScan = 20; // Consider the first kMaxNumSyncSamplesToScan sync samples and // pick the one with the largest (compressed) size as the thumbnail. size_t numSamplesToScan = mNumSyncSamples; if (numSamplesToScan > kMaxNumSyncSamplesToScan) { numSamplesToScan = kMaxNumSyncSamplesToScan; } for (size_t i = 0; i < numSamplesToScan; ++i) { uint32_t x = mSyncSamples[i]; // Now x is a sample index. size_t sampleSize; status_t err = getSampleSize_l(x, &sampleSize); if (err != OK) { return err; } if (i == 0 || sampleSize > maxSampleSize) { bestSampleIndex = x; maxSampleSize = sampleSize; } } *sample_index = bestSampleIndex; return OK; } status_t SampleTable::getSampleSize_l( uint32_t sampleIndex, size_t *sampleSize) { return mSampleIterator->getSampleSizeDirect( sampleIndex, sampleSize); } uint32_t SampleTable::getLastSampleIndexInChunk() { Mutex::Autolock autoLock(mLock); return mSampleIterator->getLastSampleIndexInChunk(); } status_t SampleTable::getMetaDataForSample( uint32_t sampleIndex, off64_t *offset, size_t *size, uint64_t *compositionTime, bool *isSyncSample, uint64_t *sampleDuration) { 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 (compositionTime) { *compositionTime = mSampleIterator->getSampleTime(); } if (isSyncSample) { *isSyncSample = false; if (mSyncSampleOffset < 0) { // Every sample is a sync sample. *isSyncSample = true; } else { size_t i = (mLastSyncSampleIndex < mNumSyncSamples) && (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex) ? mLastSyncSampleIndex : 0; while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) { ++i; } if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) { *isSyncSample = true; } mLastSyncSampleIndex = i; } } if (sampleDuration) { *sampleDuration = mSampleIterator->getSampleDuration(); } return OK; } int32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) { return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex); } } // namespace android