diff options
Diffstat (limited to 'media/libstagefright/SampleTable.cpp')
-rw-r--r-- | media/libstagefright/SampleTable.cpp | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp new file mode 100644 index 0000000..8f1fa67 --- /dev/null +++ b/media/libstagefright/SampleTable.cpp @@ -0,0 +1,598 @@ +/* + * 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" +#include <utils/Log.h> + +#include <arpa/inet.h> +#include <assert.h> + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/SampleTable.h> +#include <media/stagefright/Utils.h> + +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'); + +SampleTable::SampleTable(DataSource *source) + : mDataSource(source), + mChunkOffsetOffset(-1), + mChunkOffsetType(0), + mNumChunkOffsets(0), + mSampleToChunkOffset(-1), + mNumSampleToChunkOffsets(0), + mSampleSizeOffset(-1), + mSampleSizeFieldSize(0), + mDefaultSampleSize(0), + mNumSampleSizes(0), + mTimeToSampleCount(0), + mTimeToSample(NULL), + mSyncSampleOffset(-1), + mNumSyncSamples(0) { +} + +SampleTable::~SampleTable() { + delete[] mTimeToSample; + mTimeToSample = NULL; +} + +status_t SampleTable::setChunkOffsetParams( + uint32_t type, off_t data_offset, off_t data_size) { + if (mChunkOffsetOffset >= 0) { + return ERROR_MALFORMED; + } + + assert(type == kChunkOffsetType32 || type == kChunkOffsetType64); + + mChunkOffsetOffset = data_offset; + mChunkOffsetType = type; + + if (data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t header[8]; + if (mDataSource->read_at( + 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 + mNumChunkOffsets * 4) { + return ERROR_MALFORMED; + } + } else { + if (data_size < 8 + mNumChunkOffsets * 8) { + return ERROR_MALFORMED; + } + } + + return OK; +} + +status_t SampleTable::setSampleToChunkParams( + off_t data_offset, off_t data_size) { + if (mSampleToChunkOffset >= 0) { + return ERROR_MALFORMED; + } + + mSampleToChunkOffset = data_offset; + + if (data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t header[8]; + if (mDataSource->read_at( + 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 + mNumSampleToChunkOffsets * 12) { + return ERROR_MALFORMED; + } + + return OK; +} + +status_t SampleTable::setSampleSizeParams( + uint32_t type, off_t data_offset, off_t data_size) { + if (mSampleSizeOffset >= 0) { + return ERROR_MALFORMED; + } + + assert(type == kSampleSizeType32 || type == kSampleSizeTypeCompact); + + mSampleSizeOffset = data_offset; + + if (data_size < 12) { + return ERROR_MALFORMED; + } + + uint8_t header[12]; + if (mDataSource->read_at( + 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 (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 & 0xf; + 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( + off_t data_offset, off_t data_size) { + if (mTimeToSample != NULL || data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t header[8]; + if (mDataSource->read_at( + 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]); + mTimeToSample = new uint32_t[mTimeToSampleCount * 2]; + + size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2; + if (mDataSource->read_at( + data_offset + 8, mTimeToSample, size) < (ssize_t)size) { + return ERROR_IO; + } + + for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) { + mTimeToSample[i] = ntohl(mTimeToSample[i]); + } + + return OK; +} + +status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) { + if (mSyncSampleOffset >= 0 || data_size < 8) { + return ERROR_MALFORMED; + } + + mSyncSampleOffset = data_offset; + + uint8_t header[8]; + if (mDataSource->read_at( + 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; + } + + mNumSyncSamples = U32_AT(&header[4]); + + if (mNumSyncSamples < 2) { + LOGW("Table of sync samples is empty or has only a single entry!"); + } + return OK; +} + +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->read_at( + mChunkOffsetOffset + 8 + 4 * chunk_index, + &offset32, + sizeof(offset32)) < (ssize_t)sizeof(offset32)) { + return ERROR_IO; + } + + *offset = ntohl(offset32); + } else { + assert(mChunkOffsetOffset == kChunkOffsetType64); + + uint64_t offset64; + if (mDataSource->read_at( + 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->read_at(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->read_at( + 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->read_at( + 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->read_at( + mSampleSizeOffset + 12 + sample_index, + &x, sizeof(x)) < (ssize_t)sizeof(x)) { + return ERROR_IO; + } + + *sample_size = x; + break; + } + + default: + { + assert(mSampleSizeFieldSize == 4); + + uint8_t x; + if (mDataSource->read_at( + 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); + + *max_size = 0; + + for (uint32_t i = 0; i < mNumSampleSizes; ++i) { + size_t sample_size; + status_t err = getSampleSize(i, &sample_size); + + if (err != OK) { + return err; + } + + if (sample_size > *max_size) { + *max_size = sample_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; +} + +status_t SampleTable::findClosestSample( + uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + Mutex::Autolock autoLock(mLock); + + uint32_t cur_sample = 0; + uint32_t time = 0; + for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { + uint32_t n = mTimeToSample[2 * i]; + uint32_t delta = mTimeToSample[2 * i + 1]; + + if (req_time < time + n * delta) { + int j = (req_time - time) / delta; + + *sample_index = cur_sample + j; + + if (flags & kSyncSample_Flag) { + return findClosestSyncSample(*sample_index, sample_index); + } + + return OK; + } + + time += delta * n; + cur_sample += n; + } + + return ERROR_OUT_OF_RANGE; +} + +status_t SampleTable::findClosestSyncSample( + uint32_t start_sample_index, uint32_t *sample_index) { + *sample_index = 0; + + if (mSyncSampleOffset < 0) { + // All samples are sync-samples. + *sample_index = start_sample_index; + return OK; + } + + uint32_t x; + uint32_t left = 0; + uint32_t right = mNumSyncSamples; + while (left < right) { + uint32_t mid = (left + right) / 2; + if (mDataSource->read_at( + mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) { + return ERROR_IO; + } + + x = ntohl(x); + + if (x < (start_sample_index + 1)) { + left = mid + 1; + } else if (x > (start_sample_index + 1)) { + right = mid; + } else { + break; + } + } + +#if 1 + // Make sure we return a sample at or _after_ the requested one. + // Seeking to a particular time in a media source containing audio and + // video will most likely be able to sync fairly close to the requested + // time in the audio track but may only be able to seek a fair distance + // from the requested time in the video track. + // If we seek the video track to a time earlier than the audio track, + // we'll cause the video track to be late for quite a while, the decoder + // trying to catch up. + // If we seek the video track to a time later than the audio track, + // audio will start playing fine while no video will be output for a + // while, the video decoder will not stress the system. + if (mDataSource->read_at( + mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) { + return ERROR_IO; + } + x = ntohl(x); + assert((x - 1) >= start_sample_index); +#endif + + *sample_index = x - 1; + + return OK; +} + +} // namespace android + |