| /* |
| * 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_NDEBUG 0 |
| #define LOG_TAG "ESDS" |
| #include <utils/Log.h> |
| |
| #include <media/stagefright/foundation/ByteUtils.h> |
| |
| #include <media/esds/ESDS.h> |
| |
| #include <string.h> |
| |
| namespace android { |
| |
| ESDS::ESDS(const void *data, size_t size) |
| : mData(new uint8_t[size]), |
| mSize(size), |
| mInitCheck(NO_INIT), |
| mDecoderSpecificOffset(0), |
| mDecoderSpecificLength(0), |
| mObjectTypeIndication(0) { |
| memcpy(mData, data, size); |
| |
| mInitCheck = parse(); |
| } |
| |
| ESDS::~ESDS() { |
| delete[] mData; |
| mData = NULL; |
| } |
| |
| status_t ESDS::InitCheck() const { |
| return mInitCheck; |
| } |
| |
| status_t ESDS::getObjectTypeIndication(uint8_t *objectTypeIndication) const { |
| if (mInitCheck != OK) { |
| return mInitCheck; |
| } |
| |
| *objectTypeIndication = mObjectTypeIndication; |
| |
| return OK; |
| } |
| |
| status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const { |
| if (mInitCheck != OK) { |
| return mInitCheck; |
| } |
| |
| *data = &mData[mDecoderSpecificOffset]; |
| *size = mDecoderSpecificLength; |
| |
| return OK; |
| } |
| |
| status_t ESDS::skipDescriptorHeader( |
| size_t offset, size_t size, |
| uint8_t *tag, size_t *data_offset, size_t *data_size) const { |
| if (size == 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| *tag = mData[offset++]; |
| --size; |
| |
| *data_size = 0; |
| bool more; |
| do { |
| if (size == 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| uint8_t x = mData[offset++]; |
| --size; |
| |
| *data_size = (*data_size << 7) | (x & 0x7f); |
| more = (x & 0x80) != 0; |
| } |
| while (more); |
| |
| ALOGV("tag=0x%02x data_size=%zu", *tag, *data_size); |
| |
| if (*data_size > size) { |
| return ERROR_MALFORMED; |
| } |
| |
| *data_offset = offset; |
| |
| return OK; |
| } |
| |
| status_t ESDS::parse() { |
| uint8_t tag; |
| size_t data_offset; |
| size_t data_size; |
| status_t err = |
| skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (tag != kTag_ESDescriptor) { |
| return ERROR_MALFORMED; |
| } |
| |
| return parseESDescriptor(data_offset, data_size); |
| } |
| |
| status_t ESDS::parseESDescriptor(size_t offset, size_t size) { |
| if (size < 3) { |
| return ERROR_MALFORMED; |
| } |
| |
| offset += 2; // skip ES_ID |
| size -= 2; |
| |
| unsigned streamDependenceFlag = mData[offset] & 0x80; |
| unsigned URL_Flag = mData[offset] & 0x40; |
| unsigned OCRstreamFlag = mData[offset] & 0x20; |
| |
| ++offset; |
| --size; |
| |
| if (streamDependenceFlag) { |
| if (size < 2) |
| return ERROR_MALFORMED; |
| offset += 2; |
| size -= 2; |
| } |
| |
| if (URL_Flag) { |
| if (offset >= size) { |
| return ERROR_MALFORMED; |
| } |
| unsigned URLlength = mData[offset]; |
| if (URLlength >= size) |
| return ERROR_MALFORMED; |
| offset += URLlength + 1; |
| size -= URLlength + 1; |
| } |
| |
| if (OCRstreamFlag) { |
| if (size < 2) |
| return ERROR_MALFORMED; |
| offset += 2; |
| size -= 2; |
| |
| if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor) |
| && offset - 2 < size |
| && mData[offset - 2] == kTag_DecoderConfigDescriptor) { |
| // Content found "in the wild" had OCRstreamFlag set but was |
| // missing OCR_ES_Id, the decoder config descriptor immediately |
| // followed instead. |
| offset -= 2; |
| size += 2; |
| |
| ALOGW("Found malformed 'esds' atom, ignoring missing OCR_ES_Id."); |
| } |
| } |
| |
| if (offset >= size) { |
| return ERROR_MALFORMED; |
| } |
| |
| uint8_t tag; |
| size_t sub_offset, sub_size; |
| status_t err = skipDescriptorHeader( |
| offset, size, &tag, &sub_offset, &sub_size); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (tag != kTag_DecoderConfigDescriptor) { |
| return ERROR_MALFORMED; |
| } |
| |
| err = parseDecoderConfigDescriptor(sub_offset, sub_size); |
| |
| return err; |
| } |
| |
| status_t ESDS::getBitRate(uint32_t *brateMax, uint32_t *brateAvg) const { |
| if (mInitCheck != OK) { |
| return mInitCheck; |
| } |
| |
| *brateMax = mBitRateMax; |
| *brateAvg = mBitRateAvg; |
| |
| return OK; |
| }; |
| |
| status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) { |
| if (size < 13) { |
| return ERROR_MALFORMED; |
| } |
| |
| mObjectTypeIndication = mData[offset]; |
| mBitRateMax = U32_AT(mData + offset + 5); |
| mBitRateAvg = U32_AT(mData + offset + 9); |
| |
| offset += 13; |
| size -= 13; |
| |
| if (size == 0) { |
| mDecoderSpecificOffset = 0; |
| mDecoderSpecificLength = 0; |
| return OK; |
| } |
| |
| uint8_t tag; |
| size_t sub_offset, sub_size; |
| status_t err = skipDescriptorHeader( |
| offset, size, &tag, &sub_offset, &sub_size); |
| |
| if (err != OK) { |
| return err; |
| } |
| |
| if (tag != kTag_DecoderSpecificInfo) { |
| return ERROR_MALFORMED; |
| } |
| |
| mDecoderSpecificOffset = sub_offset; |
| mDecoderSpecificLength = sub_size; |
| |
| return OK; |
| } |
| |
| } // namespace android |
| |