diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /media/libstagefright | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'media/libstagefright')
192 files changed, 35673 insertions, 0 deletions
diff --git a/media/libstagefright/additional_headers b/media/libstagefright/additional_headers new file mode 100644 index 000000000..1357302f6 --- /dev/null +++ b/media/libstagefright/additional_headers @@ -0,0 +1,55 @@ +frameworks/av/include/media/stagefright/foundation/AAtomizer.h +frameworks/av/include/media/stagefright/foundation/ABase.h +frameworks/av/include/media/stagefright/foundation/ABitReader.h +frameworks/av/include/media/stagefright/foundation/ABuffer.h +frameworks/av/include/media/stagefright/foundation/ADebug.h +frameworks/av/include/media/stagefright/foundation/AHandler.h +frameworks/av/include/media/stagefright/foundation/AString.h +frameworks/av/include/media/stagefright/foundation/hexdump.h +frameworks/av/include/media/stagefright/MediaDefs.h +frameworks/av/include/media/stagefright/MediaErrors.h +frameworks/av/include/media/stagefright/MediaExtractor.h +frameworks/av/include/media/stagefright/MediaSource.h +frameworks/av/include/media/stagefright/MetaData.h +frameworks/av/include/media/stagefright/MetaData.h +frameworks/av/media/libstagefright/include/ESDS.h +frameworks/av/media/libstagefright/include/MPEG4Extractor.h +frameworks/av/media/libstagefright/include/SampleTable.h +frameworks/av/media/libstagefright/include/SampleTable.h +system/core/debuggerd/backtrace.h +system/core/include/android/log.h +system/core/include/corkscrew/backtrace.h +system/core/include/corkscrew/map_info.h +system/core/include/corkscrew/ptrace.h +system/core/include/corkscrew/symbol_table.h +system/core/include/cutils/jstring.h +system/core/include/cutils/log.h +system/core/include/cutils/sched_policy.h +system/core/include/log/event_tag_map.h +system/core/include/log/logd.h +system/core/include/log/logger.h +system/core/include/log/logprint.h +system/core/include/log/uio.h +system/core/include/system/graphics.h +system/core/include/sysutils/List.h +system/core/include/utils/Atomic.h +system/core/include/utils/CallStack.h +system/core/include/utils/Condition.h +system/core/include/utils/Debug.h +system/core/include/utils/Errors.h +system/core/include/utils/KeyedVector.h +system/core/include/utils/List.h +system/core/include/utils/Log.h +system/core/include/utils/Mutex.h +system/core/include/utils/RWLock.h +system/core/include/utils/SortedVector.h +system/core/include/utils/String16.h +system/core/include/utils/String8.h +system/core/include/utils/Timers.h +system/core/include/utils/Vector.h +system/core/include/utils/VectorImpl.h +system/core/libpixelflinger/codeflinger/tinyutils/Errors.h +system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h +system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h +system/core/libpixelflinger/codeflinger/tinyutils/Vector.h +system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h diff --git a/media/libstagefright/binding/Adts.cpp b/media/libstagefright/binding/Adts.cpp new file mode 100644 index 000000000..4146ff008 --- /dev/null +++ b/media/libstagefright/binding/Adts.cpp @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/Adts.h" +#include "MediaData.h" +#include "mozilla/Array.h" +#include "mozilla/ArrayUtils.h" +#include "nsAutoPtr.h" + +using namespace mozilla; + +namespace mp4_demuxer +{ + +int8_t +Adts::GetFrequencyIndex(uint32_t aSamplesPerSecond) +{ + static const uint32_t freq_lookup[] = { 96000, 88200, 64000, 48000, 44100, + 32000, 24000, 22050, 16000, 12000, + 11025, 8000, 7350, 0}; + + int8_t i = 0; + while (freq_lookup[i] && aSamplesPerSecond < freq_lookup[i]) { + i++; + } + + if (!freq_lookup[i]) { + return -1; + } + + return i; +} + +bool +Adts::ConvertSample(uint16_t aChannelCount, int8_t aFrequencyIndex, + int8_t aProfile, MediaRawData* aSample) +{ + static const int kADTSHeaderSize = 7; + + size_t newSize = aSample->Size() + kADTSHeaderSize; + + // ADTS header uses 13 bits for packet size. + if (newSize >= (1 << 13) || aChannelCount > 15 || + aFrequencyIndex < 0 || aProfile < 1 || aProfile > 4) { + return false; + } + + Array<uint8_t, kADTSHeaderSize> header; + header[0] = 0xff; + header[1] = 0xf1; + header[2] = + ((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2); + header[3] = ((aChannelCount & 0x3) << 6) + (newSize >> 11); + header[4] = (newSize & 0x7ff) >> 3; + header[5] = ((newSize & 7) << 5) + 0x1f; + header[6] = 0xfc; + + nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter()); + if (!writer->Prepend(&header[0], ArrayLength(header))) { + return false; + } + + if (aSample->mCrypto.mValid) { + if (aSample->mCrypto.mPlainSizes.Length() == 0) { + writer->mCrypto.mPlainSizes.AppendElement(kADTSHeaderSize); + writer->mCrypto.mEncryptedSizes.AppendElement(aSample->Size() - kADTSHeaderSize); + } else { + writer->mCrypto.mPlainSizes[0] += kADTSHeaderSize; + } + } + + return true; +} +} diff --git a/media/libstagefright/binding/AnnexB.cpp b/media/libstagefright/binding/AnnexB.cpp new file mode 100644 index 000000000..2ca355757 --- /dev/null +++ b/media/libstagefright/binding/AnnexB.cpp @@ -0,0 +1,410 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/EndianUtils.h" +#include "mp4_demuxer/AnnexB.h" +#include "mp4_demuxer/ByteReader.h" +#include "mp4_demuxer/ByteWriter.h" +#include "MediaData.h" +#include "nsAutoPtr.h" + +using namespace mozilla; + +namespace mp4_demuxer +{ + +static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 }; + +bool +AnnexB::ConvertSampleToAnnexB(mozilla::MediaRawData* aSample, bool aAddSPS) +{ + MOZ_ASSERT(aSample); + + if (!IsAVCC(aSample)) { + return true; + } + MOZ_ASSERT(aSample->Data()); + + if (!ConvertSampleTo4BytesAVCC(aSample)) { + return false; + } + + if (aSample->Size() < 4) { + // Nothing to do, it's corrupted anyway. + return true; + } + + ByteReader reader(aSample->Data(), aSample->Size()); + + mozilla::Vector<uint8_t> tmp; + ByteWriter writer(tmp); + + while (reader.Remaining() >= 4) { + uint32_t nalLen = reader.ReadU32(); + const uint8_t* p = reader.Read(nalLen); + + if (!writer.Write(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter))) { + return false; + } + if (!p) { + break; + } + if (!writer.Write(p, nalLen)) { + return false; + } + } + + nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter()); + + if (!samplewriter->Replace(tmp.begin(), tmp.length())) { + return false; + } + + // Prepend the Annex B NAL with SPS and PPS tables to keyframes. + if (aAddSPS && aSample->mKeyframe) { + RefPtr<MediaByteBuffer> annexB = + ConvertExtraDataToAnnexB(aSample->mExtraData); + if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) { + return false; + } + } + + return true; +} + +already_AddRefed<mozilla::MediaByteBuffer> +AnnexB::ConvertExtraDataToAnnexB(const mozilla::MediaByteBuffer* aExtraData) +{ + // AVCC 6 byte header looks like: + // +------+------+------+------+------+------+------+------+ + // [0] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | + // +------+------+------+------+------+------+------+------+ + // [1] | profile | + // +------+------+------+------+------+------+------+------+ + // [2] | compatiblity | + // +------+------+------+------+------+------+------+------+ + // [3] | level | + // +------+------+------+------+------+------+------+------+ + // [4] | unused | nalLenSiz-1 | + // +------+------+------+------+------+------+------+------+ + // [5] | unused | numSps | + // +------+------+------+------+------+------+------+------+ + + RefPtr<mozilla::MediaByteBuffer> annexB = new mozilla::MediaByteBuffer; + + ByteReader reader(*aExtraData); + const uint8_t* ptr = reader.Read(5); + if (ptr && ptr[0] == 1) { + // Append SPS then PPS + ConvertSPSOrPPS(reader, reader.ReadU8() & 31, annexB); + ConvertSPSOrPPS(reader, reader.ReadU8(), annexB); + + // MP4Box adds extra bytes that we ignore. I don't know what they do. + } + + return annexB.forget(); +} + +void +AnnexB::ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount, + mozilla::MediaByteBuffer* aAnnexB) +{ + for (int i = 0; i < aCount; i++) { + uint16_t length = aReader.ReadU16(); + + const uint8_t* ptr = aReader.Read(length); + if (!ptr) { + MOZ_ASSERT(false); + return; + } + aAnnexB->AppendElements(kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter)); + aAnnexB->AppendElements(ptr, length); + } +} + +static bool +FindStartCodeInternal(ByteReader& aBr) { + size_t offset = aBr.Offset(); + + for (uint32_t i = 0; i < aBr.Align() && aBr.Remaining() >= 3; i++) { + if (aBr.PeekU24() == 0x000001) { + return true; + } + aBr.Read(1); + } + + while (aBr.Remaining() >= 6) { + uint32_t x32 = aBr.PeekU32(); + if ((x32 - 0x01010101) & (~x32) & 0x80808080) { + if ((x32 >> 8) == 0x000001) { + return true; + } + if (x32 == 0x000001) { + aBr.Read(1); + return true; + } + if ((x32 & 0xff) == 0) { + const uint8_t* p = aBr.Peek(1); + if ((x32 & 0xff00) == 0 && p[4] == 1) { + aBr.Read(2); + return true; + } + if (p[4] == 0 && p[5] == 1) { + aBr.Read(3); + return true; + } + } + } + aBr.Read(4); + } + + while (aBr.Remaining() >= 3) { + if (aBr.PeekU24() == 0x000001) { + return true; + } + aBr.Read(1); + } + + // No start code were found; Go back to the beginning. + aBr.Seek(offset); + return false; +} + +static bool +FindStartCode(ByteReader& aBr, size_t& aStartSize) +{ + if (!FindStartCodeInternal(aBr)) { + aStartSize = 0; + return false; + } + + aStartSize = 3; + if (aBr.Offset()) { + // Check if it's 4-bytes start code + aBr.Rewind(1); + if (aBr.ReadU8() == 0) { + aStartSize = 4; + } + } + aBr.Read(3); + return true; +} + +static bool +ParseNALUnits(ByteWriter& aBw, ByteReader& aBr) +{ + size_t startSize; + + bool rv = FindStartCode(aBr, startSize); + if (rv) { + size_t startOffset = aBr.Offset(); + while (FindStartCode(aBr, startSize)) { + size_t offset = aBr.Offset(); + size_t sizeNAL = offset - startOffset - startSize; + aBr.Seek(startOffset); + if (!aBw.WriteU32(sizeNAL) + || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) { + return false; + } + aBr.Read(startSize); + startOffset = offset; + } + } + size_t sizeNAL = aBr.Remaining(); + if (sizeNAL) { + if (!aBw.WriteU32(sizeNAL) + || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) { + return false; + } + } + return true; +} + +bool +AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample) +{ + if (IsAVCC(aSample)) { + return ConvertSampleTo4BytesAVCC(aSample); + } + if (!IsAnnexB(aSample)) { + // Not AnnexB, nothing to convert. + return true; + } + + mozilla::Vector<uint8_t> nalu; + ByteWriter writer(nalu); + ByteReader reader(aSample->Data(), aSample->Size()); + + if (!ParseNALUnits(writer, reader)) { + return false; + } + nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter()); + return samplewriter->Replace(nalu.begin(), nalu.length()); +} + +already_AddRefed<mozilla::MediaByteBuffer> +AnnexB::ExtractExtraData(const mozilla::MediaRawData* aSample) +{ + RefPtr<mozilla::MediaByteBuffer> extradata = new mozilla::MediaByteBuffer; + if (HasSPS(aSample->mExtraData)) { + // We already have an explicit extradata, re-use it. + extradata = aSample->mExtraData; + return extradata.forget(); + } + + if (IsAnnexB(aSample)) { + // We can't extract data from AnnexB. + return extradata.forget(); + } + + // SPS content + mozilla::Vector<uint8_t> sps; + ByteWriter spsw(sps); + int numSps = 0; + // PPS content + mozilla::Vector<uint8_t> pps; + ByteWriter ppsw(pps); + int numPps = 0; + + int nalLenSize; + if (IsAVCC(aSample)) { + nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1; + } else { + // We do not have an extradata, assume it's AnnexB converted to AVCC via + // ConvertSampleToAVCC. + nalLenSize = 4; + } + ByteReader reader(aSample->Data(), aSample->Size()); + + // Find SPS and PPS NALUs in AVCC data + while (reader.Remaining() > nalLenSize) { + uint32_t nalLen; + switch (nalLenSize) { + case 1: nalLen = reader.ReadU8(); break; + case 2: nalLen = reader.ReadU16(); break; + case 3: nalLen = reader.ReadU24(); break; + case 4: nalLen = reader.ReadU32(); break; + } + uint8_t nalType = reader.PeekU8() & 0x1f; + const uint8_t* p = reader.Read(nalLen); + if (!p) { + return extradata.forget(); + } + + if (nalType == 0x7) { /* SPS */ + numSps++; + if (!spsw.WriteU16(nalLen) + || !spsw.Write(p, nalLen)) { + return extradata.forget(); + } + } else if (nalType == 0x8) { /* PPS */ + numPps++; + if (!ppsw.WriteU16(nalLen) + || !ppsw.Write(p, nalLen)) { + return extradata.forget(); + } + } + } + + if (numSps && sps.length() > 5) { + extradata->AppendElement(1); // version + extradata->AppendElement(sps[3]); // profile + extradata->AppendElement(sps[4]); // profile compat + extradata->AppendElement(sps[5]); // level + extradata->AppendElement(0xfc | 3); // nal size - 1 + extradata->AppendElement(0xe0 | numSps); + extradata->AppendElements(sps.begin(), sps.length()); + extradata->AppendElement(numPps); + if (numPps) { + extradata->AppendElements(pps.begin(), pps.length()); + } + } + + return extradata.forget(); +} + +bool +AnnexB::HasSPS(const mozilla::MediaRawData* aSample) +{ + return HasSPS(aSample->mExtraData); +} + +bool +AnnexB::HasSPS(const mozilla::MediaByteBuffer* aExtraData) +{ + if (!aExtraData) { + return false; + } + + ByteReader reader(aExtraData); + const uint8_t* ptr = reader.Read(5); + if (!ptr || !reader.CanRead8()) { + return false; + } + uint8_t numSps = reader.ReadU8() & 0x1f; + + return numSps > 0; +} + +bool +AnnexB::ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample) +{ + MOZ_ASSERT(IsAVCC(aSample)); + + int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1; + + if (nalLenSize == 4) { + return true; + } + mozilla::Vector<uint8_t> dest; + ByteWriter writer(dest); + ByteReader reader(aSample->Data(), aSample->Size()); + while (reader.Remaining() > nalLenSize) { + uint32_t nalLen; + switch (nalLenSize) { + case 1: nalLen = reader.ReadU8(); break; + case 2: nalLen = reader.ReadU16(); break; + case 3: nalLen = reader.ReadU24(); break; + case 4: nalLen = reader.ReadU32(); break; + } + const uint8_t* p = reader.Read(nalLen); + if (!p) { + return true; + } + if (!writer.WriteU32(nalLen) + || !writer.Write(p, nalLen)) { + return false; + } + } + nsAutoPtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter()); + return samplewriter->Replace(dest.begin(), dest.length()); +} + +bool +AnnexB::IsAVCC(const mozilla::MediaRawData* aSample) +{ + return aSample->Size() >= 3 && aSample->mExtraData && + aSample->mExtraData->Length() >= 7 && (*aSample->mExtraData)[0] == 1; +} + +bool +AnnexB::IsAnnexB(const mozilla::MediaRawData* aSample) +{ + if (aSample->Size() < 4) { + return false; + } + uint32_t header = mozilla::BigEndian::readUint32(aSample->Data()); + return header == 0x00000001 || (header >> 8) == 0x000001; +} + +bool +AnnexB::CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1, + const mozilla::MediaByteBuffer* aExtraData2) +{ + // Very crude comparison. + return aExtraData1 == aExtraData2 || *aExtraData1 == *aExtraData2; +} + +} // namespace mp4_demuxer diff --git a/media/libstagefright/binding/BitReader.cpp b/media/libstagefright/binding/BitReader.cpp new file mode 100644 index 000000000..4bf80caba --- /dev/null +++ b/media/libstagefright/binding/BitReader.cpp @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/BitReader.h" +#include <media/stagefright/foundation/ABitReader.h> + +using namespace mozilla; +using namespace stagefright; + +namespace mp4_demuxer +{ + +BitReader::BitReader(const mozilla::MediaByteBuffer* aBuffer) + : mBitReader(new ABitReader(aBuffer->Elements(), aBuffer->Length())) + , mSize(aBuffer->Length()) {} + +BitReader::BitReader(const uint8_t* aBuffer, size_t aLength) + : mBitReader(new ABitReader(aBuffer, aLength)) + , mSize(aLength) {} + +BitReader::~BitReader() {} + +uint32_t +BitReader::ReadBits(size_t aNum) +{ + MOZ_ASSERT(aNum <= 32); + if (mBitReader->numBitsLeft() < aNum) { + return 0; + } + return mBitReader->getBits(aNum); +} + +// Read unsigned integer Exp-Golomb-coded. +uint32_t +BitReader::ReadUE() +{ + uint32_t i = 0; + + while (ReadBit() == 0 && i < 32) { + i++; + } + if (i == 32) { + // This can happen if the data is invalid, or if it's + // short, since ReadBit() will return 0 when it runs + // off the end of the buffer. + NS_WARNING("Invalid H.264 data"); + return 0; + } + uint32_t r = ReadBits(i); + r += (1 << i) - 1; + return r; +} + +// Read signed integer Exp-Golomb-coded. +int32_t +BitReader::ReadSE() +{ + int32_t r = ReadUE(); + if (r & 1) { + return (r+1) / 2; + } else { + return -r / 2; + } +} + +uint64_t +BitReader::ReadU64() +{ + uint64_t hi = ReadU32(); + uint32_t lo = ReadU32(); + return (hi << 32) | lo; +} + +uint64_t +BitReader::ReadUTF8() +{ + int64_t val = ReadBits(8); + uint32_t top = (val & 0x80) >> 1; + + if ((val & 0xc0) == 0x80 || val >= 0xFE) { + // error. + return -1; + } + while (val & top) { + int tmp = ReadBits(8) - 128; + if (tmp >> 6) { + // error. + return -1; + } + val = (val << 6) + tmp; + top <<= 5; + } + val &= (top << 1) - 1; + return val; +} + +size_t +BitReader::BitCount() const +{ + return mSize * 8 - mBitReader->numBitsLeft(); +} + +} // namespace mp4_demuxer diff --git a/media/libstagefright/binding/Box.cpp b/media/libstagefright/binding/Box.cpp new file mode 100644 index 000000000..4bef10816 --- /dev/null +++ b/media/libstagefright/binding/Box.cpp @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/Box.h" +#include "mp4_demuxer/Stream.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/Unused.h" +#include <algorithm> + +using namespace mozilla; + +namespace mp4_demuxer { + +// Limit reads to 32MiB max. +// static +const uint64_t Box::kMAX_BOX_READ = 32 * 1024 * 1024; + +// Returns the offset from the start of the body of a box of type |aType| +// to the start of its first child. +static uint32_t +BoxOffset(AtomType aType) +{ + const uint32_t FULLBOX_OFFSET = 4; + + if (aType == AtomType("mp4a") || aType == AtomType("enca")) { + // AudioSampleEntry; ISO 14496-12, section 8.16 + return 28; + } else if (aType == AtomType("mp4v") || aType == AtomType("encv")) { + // VideoSampleEntry; ISO 14496-12, section 8.16 + return 78; + } else if (aType == AtomType("stsd")) { + // SampleDescriptionBox; ISO 14496-12, section 8.16 + // This is a FullBox, and contains a |count| member before its child + // boxes. + return FULLBOX_OFFSET + 4; + } + + return 0; +} + +Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent) + : mContext(aContext), mParent(aParent) +{ + uint8_t header[8]; + + if (aOffset > INT64_MAX - sizeof(header)) { + return; + } + + MediaByteRange headerRange(aOffset, aOffset + sizeof(header)); + if (mParent && !mParent->mRange.Contains(headerRange)) { + return; + } + + const MediaByteRange* byteRange; + for (int i = 0; ; i++) { + if (i == mContext->mByteRanges.Length()) { + return; + } + + byteRange = static_cast<const MediaByteRange*>(&mContext->mByteRanges[i]); + if (byteRange->Contains(headerRange)) { + break; + } + } + + size_t bytes; + if (!mContext->mSource->CachedReadAt(aOffset, header, sizeof(header), + &bytes) || + bytes != sizeof(header)) { + return; + } + + uint64_t size = BigEndian::readUint32(header); + if (size == 1) { + uint8_t bigLength[8]; + if (aOffset > INT64_MAX - sizeof(header) - sizeof(bigLength)) { + return; + } + MediaByteRange bigLengthRange(headerRange.mEnd, + headerRange.mEnd + sizeof(bigLength)); + if ((mParent && !mParent->mRange.Contains(bigLengthRange)) || + !byteRange->Contains(bigLengthRange) || + !mContext->mSource->CachedReadAt(aOffset + sizeof(header), bigLength, + sizeof(bigLength), &bytes) || + bytes != sizeof(bigLength)) { + return; + } + size = BigEndian::readUint64(bigLength); + mBodyOffset = bigLengthRange.mEnd; + } else if (size == 0) { + // box extends to end of file. + size = mContext->mByteRanges.LastInterval().mEnd - aOffset; + mBodyOffset = headerRange.mEnd; + } else { + mBodyOffset = headerRange.mEnd; + } + + if (size > INT64_MAX) { + return; + } + int64_t end = static_cast<int64_t>(aOffset) + static_cast<int64_t>(size); + if (end < static_cast<int64_t>(aOffset)) { + // Overflowed. + return; + } + + mType = BigEndian::readUint32(&header[4]); + mChildOffset = mBodyOffset + BoxOffset(mType); + + MediaByteRange boxRange(aOffset, end); + if (mChildOffset > boxRange.mEnd || + (mParent && !mParent->mRange.Contains(boxRange)) || + !byteRange->Contains(boxRange)) { + return; + } + + mRange = boxRange; +} + +Box::Box() + : mContext(nullptr) +{} + +Box +Box::Next() const +{ + MOZ_ASSERT(IsAvailable()); + return Box(mContext, mRange.mEnd, mParent); +} + +Box +Box::FirstChild() const +{ + MOZ_ASSERT(IsAvailable()); + if (mChildOffset == mRange.mEnd) { + return Box(); + } + return Box(mContext, mChildOffset, this); +} + +nsTArray<uint8_t> +Box::Read() +{ + nsTArray<uint8_t> out; + Unused << Read(&out, mRange); + return out; +} + +bool +Box::Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange) +{ + int64_t length; + if (!mContext->mSource->Length(&length)) { + // The HTTP server didn't give us a length to work with. + // Limit the read to kMAX_BOX_READ max. + length = std::min(aRange.mEnd - mChildOffset, kMAX_BOX_READ); + } else { + length = aRange.mEnd - mChildOffset; + } + aDest->SetLength(length); + size_t bytes; + if (!mContext->mSource->CachedReadAt(mChildOffset, aDest->Elements(), + aDest->Length(), &bytes) || + bytes != aDest->Length()) { + // Byte ranges are being reported incorrectly + NS_WARNING("Read failed in mp4_demuxer::Box::Read()"); + aDest->Clear(); + return false; + } + return true; +} +} diff --git a/media/libstagefright/binding/BufferStream.cpp b/media/libstagefright/binding/BufferStream.cpp new file mode 100644 index 000000000..6f6a58398 --- /dev/null +++ b/media/libstagefright/binding/BufferStream.cpp @@ -0,0 +1,77 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/BufferStream.h" +#include "MediaData.h" +#include "MediaResource.h" +#include <algorithm> + +using namespace mozilla; + +namespace mp4_demuxer { + +BufferStream::BufferStream() + : mStartOffset(0) + , mData(new mozilla::MediaByteBuffer) +{ +} + +BufferStream::BufferStream(mozilla::MediaByteBuffer* aBuffer) + : mStartOffset(0) + , mData(aBuffer) +{ +} + +BufferStream::~BufferStream() +{ +} + +/*virtual*/ bool +BufferStream::ReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) +{ + if (aOffset < mStartOffset || aOffset > mStartOffset + mData->Length()) { + return false; + } + *aBytesRead = + std::min(aLength, size_t(mStartOffset + mData->Length() - aOffset)); + memcpy(aData, mData->Elements() + aOffset - mStartOffset, *aBytesRead); + return true; +} + +/*virtual*/ bool +BufferStream::CachedReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) +{ + return ReadAt(aOffset, aData, aLength, aBytesRead); +} + +/*virtual*/ bool +BufferStream::Length(int64_t* aLength) +{ + *aLength = mStartOffset + mData->Length(); + return true; +} + +/* virtual */ void +BufferStream::DiscardBefore(int64_t aOffset) +{ + if (aOffset > mStartOffset) { + mData->RemoveElementsAt(0, aOffset - mStartOffset); + mStartOffset = aOffset; + } +} + +bool +BufferStream::AppendBytes(const uint8_t* aData, size_t aLength) +{ + return mData->AppendElements(aData, aLength, fallible); +} + +MediaByteRange +BufferStream::GetByteRange() +{ + return MediaByteRange(mStartOffset, mStartOffset + mData->Length()); +} +} diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp new file mode 100644 index 000000000..aaf2fb32c --- /dev/null +++ b/media/libstagefright/binding/DecoderData.cpp @@ -0,0 +1,217 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/Adts.h" +#include "mp4_demuxer/AnnexB.h" +#include "mp4_demuxer/ByteReader.h" +#include "mp4_demuxer/DecoderData.h" +#include <media/stagefright/foundation/ABitReader.h> +#include "media/stagefright/MetaData.h" +#include "media/stagefright/MediaDefs.h" +#include "media/stagefright/Utils.h" +#include "mozilla/ArrayUtils.h" +#include "include/ESDS.h" + +#ifdef MOZ_RUST_MP4PARSE +#include "mp4parse.h" +#endif + +using namespace stagefright; + +namespace mp4_demuxer +{ + +static int32_t +FindInt32(const MetaData* mMetaData, uint32_t mKey) +{ + int32_t value; + if (!mMetaData->findInt32(mKey, &value)) + return 0; + return value; +} + +static int64_t +FindInt64(const MetaData* mMetaData, uint32_t mKey) +{ + int64_t value; + if (!mMetaData->findInt64(mKey, &value)) + return 0; + return value; +} + +template <typename T, size_t N> +static bool +FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::Vector<T, N>* aDest) +{ + const void* data; + size_t size; + uint32_t type; + + aDest->clear(); + // There's no point in checking that the type matches anything because it + // isn't set consistently in the MPEG4Extractor. + if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) { + return false; + } + + aDest->append(reinterpret_cast<const T*>(data), size / sizeof(T)); + return true; +} + +template <typename T> +static bool +FindData(const MetaData* aMetaData, uint32_t aKey, nsTArray<T>* aDest) +{ + const void* data; + size_t size; + uint32_t type; + + aDest->Clear(); + // There's no point in checking that the type matches anything because it + // isn't set consistently in the MPEG4Extractor. + if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) { + return false; + } + + aDest->AppendElements(reinterpret_cast<const T*>(data), size / sizeof(T)); + return true; +} + +static bool +FindData(const MetaData* aMetaData, uint32_t aKey, mozilla::MediaByteBuffer* aDest) +{ + return FindData(aMetaData, aKey, static_cast<nsTArray<uint8_t>*>(aDest)); +} + +bool +CryptoFile::DoUpdate(const uint8_t* aData, size_t aLength) +{ + ByteReader reader(aData, aLength); + while (reader.Remaining()) { + PsshInfo psshInfo; + if (!reader.ReadArray(psshInfo.uuid, 16)) { + return false; + } + + if (!reader.CanReadType<uint32_t>()) { + return false; + } + auto length = reader.ReadType<uint32_t>(); + + if (!reader.ReadArray(psshInfo.data, length)) { + return false; + } + pssh.AppendElement(psshInfo); + } + return true; +} + +static void +UpdateTrackInfo(mozilla::TrackInfo& aConfig, + const MetaData* aMetaData, + const char* aMimeType) +{ + mozilla::CryptoTrack& crypto = aConfig.mCrypto; + aConfig.mMimeType = aMimeType; + aConfig.mDuration = FindInt64(aMetaData, kKeyDuration); + aConfig.mMediaTime = FindInt64(aMetaData, kKeyMediaTime); + aConfig.mTrackId = FindInt32(aMetaData, kKeyTrackID); + aConfig.mCrypto.mValid = aMetaData->findInt32(kKeyCryptoMode, &crypto.mMode) && + aMetaData->findInt32(kKeyCryptoDefaultIVSize, &crypto.mIVSize) && + FindData(aMetaData, kKeyCryptoKey, &crypto.mKeyId); +} + +void +MP4AudioInfo::Update(const MetaData* aMetaData, + const char* aMimeType) +{ + UpdateTrackInfo(*this, aMetaData, aMimeType); + mChannels = FindInt32(aMetaData, kKeyChannelCount); + mBitDepth = FindInt32(aMetaData, kKeySampleSize); + mRate = FindInt32(aMetaData, kKeySampleRate); + mProfile = FindInt32(aMetaData, kKeyAACProfile); + + if (FindData(aMetaData, kKeyESDS, mExtraData)) { + ESDS esds(mExtraData->Elements(), mExtraData->Length()); + + const void* data; + size_t size; + if (esds.getCodecSpecificInfo(&data, &size) == OK) { + const uint8_t* cdata = reinterpret_cast<const uint8_t*>(data); + mCodecSpecificConfig->AppendElements(cdata, size); + if (size > 1) { + ABitReader br(cdata, size); + mExtendedProfile = br.getBits(5); + + if (mExtendedProfile == 31) { // AAC-ELD => additional 6 bits + mExtendedProfile = 32 + br.getBits(6); + } + } + } + } +} + +bool +MP4AudioInfo::IsValid() const +{ + return mChannels > 0 && mRate > 0 && + // Accept any mime type here, but if it's aac, validate the profile. + (!mMimeType.Equals(MEDIA_MIMETYPE_AUDIO_AAC) || + mProfile > 0 || mExtendedProfile > 0); +} + +void +MP4VideoInfo::Update(const MetaData* aMetaData, const char* aMimeType) +{ + UpdateTrackInfo(*this, aMetaData, aMimeType); + mDisplay.width = FindInt32(aMetaData, kKeyDisplayWidth); + mDisplay.height = FindInt32(aMetaData, kKeyDisplayHeight); + mImage.width = FindInt32(aMetaData, kKeyWidth); + mImage.height = FindInt32(aMetaData, kKeyHeight); + mRotation = VideoInfo::ToSupportedRotation(FindInt32(aMetaData, kKeyRotation)); + + FindData(aMetaData, kKeyAVCC, mExtraData); + if (!mExtraData->Length()) { + if (FindData(aMetaData, kKeyESDS, mExtraData)) { + ESDS esds(mExtraData->Elements(), mExtraData->Length()); + + const void* data; + size_t size; + if (esds.getCodecSpecificInfo(&data, &size) == OK) { + const uint8_t* cdata = reinterpret_cast<const uint8_t*>(data); + mCodecSpecificConfig->AppendElements(cdata, size); + } + } + } + +} + +#ifdef MOZ_RUST_MP4PARSE +void +MP4VideoInfo::Update(const mp4parse_track_info* track, + const mp4parse_track_video_info* video) +{ + if (track->codec == MP4PARSE_CODEC_AVC) { + mMimeType = MEDIA_MIMETYPE_VIDEO_AVC; + } else if (track->codec == MP4PARSE_CODEC_VP9) { + mMimeType = NS_LITERAL_CSTRING("video/vp9"); + } + mTrackId = track->track_id; + mDuration = track->duration; + mMediaTime = track->media_time; + mDisplay.width = video->display_width; + mDisplay.height = video->display_height; + mImage.width = video->image_width; + mImage.height = video->image_height; +} +#endif + +bool +MP4VideoInfo::IsValid() const +{ + return (mDisplay.width > 0 && mDisplay.height > 0) || + (mImage.width > 0 && mImage.height > 0); +} + +} diff --git a/media/libstagefright/binding/H264.cpp b/media/libstagefright/binding/H264.cpp new file mode 100644 index 000000000..a34ae2d3a --- /dev/null +++ b/media/libstagefright/binding/H264.cpp @@ -0,0 +1,528 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "mozilla/PodOperations.h" +#include "mp4_demuxer/AnnexB.h" +#include "mp4_demuxer/BitReader.h" +#include "mp4_demuxer/ByteReader.h" +#include "mp4_demuxer/ByteWriter.h" +#include "mp4_demuxer/H264.h" +#include <media/stagefright/foundation/ABitReader.h> +#include <limits> + +using namespace mozilla; + +namespace mp4_demuxer +{ + +SPSData::SPSData() +{ + PodZero(this); + // Default values when they aren't defined as per ITU-T H.264 (2014/02). + chroma_format_idc = 1; + video_format = 5; + colour_primaries = 2; + transfer_characteristics = 2; + sample_ratio = 1.0; +} + +/* static */ already_AddRefed<mozilla::MediaByteBuffer> +H264::DecodeNALUnit(const mozilla::MediaByteBuffer* aNAL) +{ + MOZ_ASSERT(aNAL); + + if (aNAL->Length() < 4) { + return nullptr; + } + + RefPtr<mozilla::MediaByteBuffer> rbsp = new mozilla::MediaByteBuffer; + ByteReader reader(aNAL); + uint8_t nal_unit_type = reader.ReadU8() & 0x1f; + uint32_t nalUnitHeaderBytes = 1; + if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) { + bool svc_extension_flag = false; + bool avc_3d_extension_flag = false; + if (nal_unit_type != 21) { + svc_extension_flag = reader.PeekU8() & 0x80; + } else { + avc_3d_extension_flag = reader.PeekU8() & 0x80; + } + if (svc_extension_flag) { + nalUnitHeaderBytes += 3; + } else if (avc_3d_extension_flag) { + nalUnitHeaderBytes += 2; + } else { + nalUnitHeaderBytes += 3; + } + } + if (!reader.Read(nalUnitHeaderBytes - 1)) { + return nullptr; + } + uint32_t lastbytes = 0xffff; + while (reader.Remaining()) { + uint8_t byte = reader.ReadU8(); + if ((lastbytes & 0xffff) == 0 && byte == 0x03) { + // reset last two bytes, to detect the 0x000003 sequence again. + lastbytes = 0xffff; + } else { + rbsp->AppendElement(byte); + } + lastbytes = (lastbytes << 8) | byte; + } + return rbsp.forget(); +} + +static int32_t +ConditionDimension(float aValue) +{ + // This will exclude NaNs and too-big values. + if (aValue > 1.0 && aValue <= INT32_MAX) + return int32_t(aValue); + return 0; +} + +/* static */ bool +H264::DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest) +{ + if (!aSPS) { + return false; + } + BitReader br(aSPS); + + int32_t lastScale; + int32_t nextScale; + int32_t deltaScale; + + aDest.profile_idc = br.ReadBits(8); + aDest.constraint_set0_flag = br.ReadBit(); + aDest.constraint_set1_flag = br.ReadBit(); + aDest.constraint_set2_flag = br.ReadBit(); + aDest.constraint_set3_flag = br.ReadBit(); + aDest.constraint_set4_flag = br.ReadBit(); + aDest.constraint_set5_flag = br.ReadBit(); + br.ReadBits(2); // reserved_zero_2bits + aDest.level_idc = br.ReadBits(8); + aDest.seq_parameter_set_id = br.ReadUE(); + if (aDest.profile_idc == 100 || aDest.profile_idc == 110 || + aDest.profile_idc == 122 || aDest.profile_idc == 244 || + aDest.profile_idc == 44 || aDest.profile_idc == 83 || + aDest.profile_idc == 86 || aDest.profile_idc == 118 || + aDest.profile_idc == 128 || aDest.profile_idc == 138 || + aDest.profile_idc == 139 || aDest.profile_idc == 134) { + if ((aDest.chroma_format_idc = br.ReadUE()) == 3) { + aDest.separate_colour_plane_flag = br.ReadBit(); + } + br.ReadUE(); // bit_depth_luma_minus8 + br.ReadUE(); // bit_depth_chroma_minus8 + br.ReadBit(); // qpprime_y_zero_transform_bypass_flag + if (br.ReadBit()) { // seq_scaling_matrix_present_flag + for (int idx = 0; idx < ((aDest.chroma_format_idc != 3) ? 8 : 12); ++idx) { + if (br.ReadBit()) { // Scaling list present + lastScale = nextScale = 8; + int sl_n = (idx < 6) ? 16 : 64; + for (int sl_i = 0; sl_i < sl_n; sl_i++) { + if (nextScale) { + deltaScale = br.ReadSE(); + nextScale = (lastScale + deltaScale + 256) % 256; + } + lastScale = (nextScale == 0) ? lastScale : nextScale; + } + } + } + } + } else if (aDest.profile_idc == 183) { + aDest.chroma_format_idc = 0; + } else { + // default value if chroma_format_idc isn't set. + aDest.chroma_format_idc = 1; + } + aDest.log2_max_frame_num = br.ReadUE() + 4; + aDest.pic_order_cnt_type = br.ReadUE(); + if (aDest.pic_order_cnt_type == 0) { + aDest.log2_max_pic_order_cnt_lsb = br.ReadUE() + 4; + } else if (aDest.pic_order_cnt_type == 1) { + aDest.delta_pic_order_always_zero_flag = br.ReadBit(); + aDest.offset_for_non_ref_pic = br.ReadSE(); + aDest.offset_for_top_to_bottom_field = br.ReadSE(); + uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ReadUE(); + for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + br.ReadSE(); // offset_for_ref_frame[i] + } + } + aDest.max_num_ref_frames = br.ReadUE(); + aDest.gaps_in_frame_num_allowed_flag = br.ReadBit(); + aDest.pic_width_in_mbs = br.ReadUE() + 1; + aDest.pic_height_in_map_units = br.ReadUE() + 1; + aDest.frame_mbs_only_flag = br.ReadBit(); + if (!aDest.frame_mbs_only_flag) { + aDest.pic_height_in_map_units *= 2; + aDest.mb_adaptive_frame_field_flag = br.ReadBit(); + } + br.ReadBit(); // direct_8x8_inference_flag + aDest.frame_cropping_flag = br.ReadBit(); + if (aDest.frame_cropping_flag) { + aDest.frame_crop_left_offset = br.ReadUE(); + aDest.frame_crop_right_offset = br.ReadUE(); + aDest.frame_crop_top_offset = br.ReadUE(); + aDest.frame_crop_bottom_offset = br.ReadUE(); + } + + aDest.sample_ratio = 1.0f; + aDest.vui_parameters_present_flag = br.ReadBit(); + if (aDest.vui_parameters_present_flag) { + vui_parameters(br, aDest); + } + + // Calculate common values. + + uint8_t ChromaArrayType = + aDest.separate_colour_plane_flag ? 0 : aDest.chroma_format_idc; + // Calculate width. + uint32_t CropUnitX = 1; + uint32_t SubWidthC = aDest.chroma_format_idc == 3 ? 1 : 2; + if (ChromaArrayType != 0) { + CropUnitX = SubWidthC; + } + + // Calculate Height + uint32_t CropUnitY = 2 - aDest.frame_mbs_only_flag; + uint32_t SubHeightC = aDest.chroma_format_idc <= 1 ? 2 : 1; + if (ChromaArrayType != 0) { + CropUnitY *= SubHeightC; + } + + uint32_t width = aDest.pic_width_in_mbs * 16; + uint32_t height = aDest.pic_height_in_map_units * 16; + if (aDest.frame_crop_left_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitX && + aDest.frame_crop_right_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitX && + aDest.frame_crop_top_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitY && + aDest.frame_crop_bottom_offset <= std::numeric_limits<int32_t>::max() / 4 / CropUnitY && + (aDest.frame_crop_left_offset + aDest.frame_crop_right_offset) * CropUnitX < width && + (aDest.frame_crop_top_offset + aDest.frame_crop_bottom_offset) * CropUnitY < height) { + aDest.crop_left = aDest.frame_crop_left_offset * CropUnitX; + aDest.crop_right = aDest.frame_crop_right_offset * CropUnitX; + aDest.crop_top = aDest.frame_crop_top_offset * CropUnitY; + aDest.crop_bottom = aDest.frame_crop_bottom_offset * CropUnitY; + } else { + // Nonsensical value, ignore them. + aDest.crop_left = aDest.crop_right = aDest.crop_top = aDest.crop_bottom = 0; + } + + aDest.pic_width = width - aDest.crop_left - aDest.crop_right; + aDest.pic_height = height - aDest.crop_top - aDest.crop_bottom; + + aDest.interlaced = !aDest.frame_mbs_only_flag; + + // Determine display size. + if (aDest.sample_ratio > 1.0) { + // Increase the intrinsic width + aDest.display_width = + ConditionDimension(aDest.pic_width * aDest.sample_ratio); + aDest.display_height = aDest.pic_height; + } else { + // Increase the intrinsic height + aDest.display_width = aDest.pic_width; + aDest.display_height = + ConditionDimension(aDest.pic_height / aDest.sample_ratio); + } + + return true; +} + +/* static */ void +H264::vui_parameters(BitReader& aBr, SPSData& aDest) +{ + aDest.aspect_ratio_info_present_flag = aBr.ReadBit(); + if (aDest.aspect_ratio_info_present_flag) { + aDest.aspect_ratio_idc = aBr.ReadBits(8); + aDest.sar_width = aDest.sar_height = 0; + + // From E.2.1 VUI parameters semantics (ITU-T H.264 02/2014) + switch (aDest.aspect_ratio_idc) { + case 0: + // Unspecified + break; + case 1: + /* + 1:1 + 7680x4320 16:9 frame without horizontal overscan + 3840x2160 16:9 frame without horizontal overscan + 1280x720 16:9 frame without horizontal overscan + 1920x1080 16:9 frame without horizontal overscan (cropped from 1920x1088) + 640x480 4:3 frame without horizontal overscan + */ + aDest.sample_ratio = 1.0f; + break; + case 2: + /* + 12:11 + 720x576 4:3 frame with horizontal overscan + 352x288 4:3 frame without horizontal overscan + */ + aDest.sample_ratio = 12.0 / 11.0; + break; + case 3: + /* + 10:11 + 720x480 4:3 frame with horizontal overscan + 352x240 4:3 frame without horizontal overscan + */ + aDest.sample_ratio = 10.0 / 11.0; + break; + case 4: + /* + 16:11 + 720x576 16:9 frame with horizontal overscan + 528x576 4:3 frame without horizontal overscan + */ + aDest.sample_ratio = 16.0 / 11.0; + break; + case 5: + /* + 40:33 + 720x480 16:9 frame with horizontal overscan + 528x480 4:3 frame without horizontal overscan + */ + aDest.sample_ratio = 40.0 / 33.0; + break; + case 6: + /* + 24:11 + 352x576 4:3 frame without horizontal overscan + 480x576 16:9 frame with horizontal overscan + */ + aDest.sample_ratio = 24.0 / 11.0; + break; + case 7: + /* + 20:11 + 352x480 4:3 frame without horizontal overscan + 480x480 16:9 frame with horizontal overscan + */ + aDest.sample_ratio = 20.0 / 11.0; + break; + case 8: + /* + 32:11 + 352x576 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 32.0 / 11.0; + break; + case 9: + /* + 80:33 + 352x480 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 80.0 / 33.0; + break; + case 10: + /* + 18:11 + 480x576 4:3 frame with horizontal overscan + */ + aDest.sample_ratio = 18.0 / 11.0; + break; + case 11: + /* + 15:11 + 480x480 4:3 frame with horizontal overscan + */ + aDest.sample_ratio = 15.0 / 11.0; + break; + case 12: + /* + 64:33 + 528x576 16:9 frame with horizontal overscan + */ + aDest.sample_ratio = 64.0 / 33.0; + break; + case 13: + /* + 160:99 + 528x480 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 160.0 / 99.0; + break; + case 14: + /* + 4:3 + 1440x1080 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 4.0 / 3.0; + break; + case 15: + /* + 3:2 + 1280x1080 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 3.2 / 2.0; + break; + case 16: + /* + 2:1 + 960x1080 16:9 frame without horizontal overscan + */ + aDest.sample_ratio = 2.0 / 1.0; + break; + case 255: + /* Extended_SAR */ + aDest.sar_width = aBr.ReadBits(16); + aDest.sar_height = aBr.ReadBits(16); + if (aDest.sar_width && aDest.sar_height) { + aDest.sample_ratio = float(aDest.sar_width) / float(aDest.sar_height); + } + break; + default: + break; + } + } + + if (aBr.ReadBit()) { //overscan_info_present_flag + aDest.overscan_appropriate_flag = aBr.ReadBit(); + } + + if (aBr.ReadBit()) { // video_signal_type_present_flag + aDest.video_format = aBr.ReadBits(3); + aDest.video_full_range_flag = aBr.ReadBit(); + aDest.colour_description_present_flag = aBr.ReadBit(); + if (aDest.colour_description_present_flag) { + aDest.colour_primaries = aBr.ReadBits(8); + aDest.transfer_characteristics = aBr.ReadBits(8); + aDest.matrix_coefficients = aBr.ReadBits(8); + } + } + + aDest.chroma_loc_info_present_flag = aBr.ReadBit(); + if (aDest.chroma_loc_info_present_flag) { + aDest.chroma_sample_loc_type_top_field = aBr.ReadUE(); + aDest.chroma_sample_loc_type_bottom_field = aBr.ReadUE(); + } + + aDest.timing_info_present_flag = aBr.ReadBit(); + if (aDest.timing_info_present_flag ) { + aDest.num_units_in_tick = aBr.ReadBits(32); + aDest.time_scale = aBr.ReadBits(32); + aDest.fixed_frame_rate_flag = aBr.ReadBit(); + } +} + +/* static */ bool +H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData& aDest) +{ + if (!AnnexB::HasSPS(aExtraData)) { + return false; + } + ByteReader reader(aExtraData); + + if (!reader.Read(5)) { + return false; + } + + if (!(reader.ReadU8() & 0x1f)) { + // No SPS. + return false; + } + uint16_t length = reader.ReadU16(); + + if ((reader.PeekU8() & 0x1f) != 7) { + // Not a SPS NAL type. + return false; + } + + const uint8_t* ptr = reader.Read(length); + if (!ptr) { + return false; + } + + RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer; + rawNAL->AppendElements(ptr, length); + + RefPtr<mozilla::MediaByteBuffer> sps = DecodeNALUnit(rawNAL); + + if (!sps) { + return false; + } + + return DecodeSPS(sps, aDest); +} + +/* static */ bool +H264::EnsureSPSIsSane(SPSData& aSPS) +{ + bool valid = true; + static const float default_aspect = 4.0f / 3.0f; + if (aSPS.sample_ratio <= 0.0f || aSPS.sample_ratio > 6.0f) { + if (aSPS.pic_width && aSPS.pic_height) { + aSPS.sample_ratio = + (float) aSPS.pic_width / (float) aSPS.pic_height; + } else { + aSPS.sample_ratio = default_aspect; + } + aSPS.display_width = aSPS.pic_width; + aSPS.display_height = aSPS.pic_height; + valid = false; + } + if (aSPS.max_num_ref_frames > 16) { + aSPS.max_num_ref_frames = 16; + valid = false; + } + return valid; +} + +/* static */ uint32_t +H264::ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData) +{ + uint32_t maxRefFrames = 4; + // Retrieve video dimensions from H264 SPS NAL. + SPSData spsdata; + if (DecodeSPSFromExtraData(aExtraData, spsdata)) { + // max_num_ref_frames determines the size of the sliding window + // we need to queue that many frames in order to guarantee proper + // pts frames ordering. Use a minimum of 4 to ensure proper playback of + // non compliant videos. + maxRefFrames = + std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u); + } + return maxRefFrames; +} + +/* static */ H264::FrameType +H264::GetFrameType(const mozilla::MediaRawData* aSample) +{ + if (!AnnexB::IsAVCC(aSample)) { + // We must have a valid AVCC frame with extradata. + return FrameType::INVALID; + } + MOZ_ASSERT(aSample->Data()); + + int nalLenSize = ((*aSample->mExtraData)[4] & 3) + 1; + + ByteReader reader(aSample->Data(), aSample->Size()); + + while (reader.Remaining() >= nalLenSize) { + uint32_t nalLen; + switch (nalLenSize) { + case 1: nalLen = reader.ReadU8(); break; + case 2: nalLen = reader.ReadU16(); break; + case 3: nalLen = reader.ReadU24(); break; + case 4: nalLen = reader.ReadU32(); break; + } + if (!nalLen) { + continue; + } + const uint8_t* p = reader.Read(nalLen); + if (!p) { + return FrameType::INVALID; + } + if ((p[0] & 0x1f) == 5) { + // IDR NAL. + return FrameType::I_FRAME; + } + } + + return FrameType::OTHER; +} + +} // namespace mp4_demuxer diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp new file mode 100644 index 000000000..9d6ab5028 --- /dev/null +++ b/media/libstagefright/binding/Index.cpp @@ -0,0 +1,533 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/ByteReader.h" +#include "mp4_demuxer/Index.h" +#include "mp4_demuxer/Interval.h" +#include "mp4_demuxer/SinfParser.h" +#include "nsAutoPtr.h" +#include "mozilla/RefPtr.h" + +#include <algorithm> +#include <limits> + +using namespace stagefright; +using namespace mozilla; +using namespace mozilla::media; + +namespace mp4_demuxer +{ + +class MOZ_STACK_CLASS RangeFinder +{ +public: + // Given that we're processing this in order we don't use a binary search + // to find the apropriate time range. Instead we search linearly from the + // last used point. + explicit RangeFinder(const MediaByteRangeSet& ranges) + : mRanges(ranges), mIndex(0) + { + // Ranges must be normalised for this to work + } + + bool Contains(MediaByteRange aByteRange); + +private: + const MediaByteRangeSet& mRanges; + size_t mIndex; +}; + +bool +RangeFinder::Contains(MediaByteRange aByteRange) +{ + if (!mRanges.Length()) { + return false; + } + + if (mRanges[mIndex].ContainsStrict(aByteRange)) { + return true; + } + + if (aByteRange.mStart < mRanges[mIndex].mStart) { + // Search backwards + do { + if (!mIndex) { + return false; + } + --mIndex; + if (mRanges[mIndex].ContainsStrict(aByteRange)) { + return true; + } + } while (aByteRange.mStart < mRanges[mIndex].mStart); + + return false; + } + + while (aByteRange.mEnd > mRanges[mIndex].mEnd) { + if (mIndex == mRanges.Length() - 1) { + return false; + } + ++mIndex; + if (mRanges[mIndex].ContainsStrict(aByteRange)) { + return true; + } + } + + return false; +} + +SampleIterator::SampleIterator(Index* aIndex) + : mIndex(aIndex) + , mCurrentMoof(0) + , mCurrentSample(0) +{ + mIndex->RegisterIterator(this); +} + +SampleIterator::~SampleIterator() +{ + mIndex->UnregisterIterator(this); +} + +already_AddRefed<MediaRawData> SampleIterator::GetNext() +{ + Sample* s(Get()); + if (!s) { + return nullptr; + } + + int64_t length = std::numeric_limits<int64_t>::max(); + mIndex->mSource->Length(&length); + if (s->mByteRange.mEnd > length) { + // We don't have this complete sample. + return nullptr; + } + + RefPtr<MediaRawData> sample = new MediaRawData(); + sample->mTimecode= s->mDecodeTime; + sample->mTime = s->mCompositionRange.start; + sample->mDuration = s->mCompositionRange.Length(); + sample->mOffset = s->mByteRange.mStart; + sample->mKeyframe = s->mSync; + + nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); + // Do the blocking read + if (!writer->SetSize(s->mByteRange.Length())) { + return nullptr; + } + + size_t bytesRead; + if (!mIndex->mSource->ReadAt(sample->mOffset, writer->Data(), sample->Size(), + &bytesRead) || bytesRead != sample->Size()) { + return nullptr; + } + + if (!s->mCencRange.IsEmpty()) { + MoofParser* parser = mIndex->mMoofParser.get(); + + if (!parser || !parser->mSinf.IsValid()) { + return nullptr; + } + + uint8_t ivSize = parser->mSinf.mDefaultIVSize; + + // The size comes from an 8 bit field + AutoTArray<uint8_t, 256> cenc; + cenc.SetLength(s->mCencRange.Length()); + if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, cenc.Elements(), cenc.Length(), + &bytesRead) || bytesRead != cenc.Length()) { + return nullptr; + } + ByteReader reader(cenc); + writer->mCrypto.mValid = true; + writer->mCrypto.mIVSize = ivSize; + + if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) { + return nullptr; + } + + if (reader.CanRead16()) { + uint16_t count = reader.ReadU16(); + + if (reader.Remaining() < count * 6) { + return nullptr; + } + + for (size_t i = 0; i < count; i++) { + writer->mCrypto.mPlainSizes.AppendElement(reader.ReadU16()); + writer->mCrypto.mEncryptedSizes.AppendElement(reader.ReadU32()); + } + } else { + // No subsample information means the entire sample is encrypted. + writer->mCrypto.mPlainSizes.AppendElement(0); + writer->mCrypto.mEncryptedSizes.AppendElement(sample->Size()); + } + } + + Next(); + + return sample.forget(); +} + +Sample* SampleIterator::Get() +{ + if (!mIndex->mMoofParser) { + MOZ_ASSERT(!mCurrentMoof); + return mCurrentSample < mIndex->mIndex.Length() + ? &mIndex->mIndex[mCurrentSample] + : nullptr; + } + + nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs(); + while (true) { + if (mCurrentMoof == moofs.Length()) { + if (!mIndex->mMoofParser->BlockingReadNextMoof()) { + return nullptr; + } + MOZ_ASSERT(mCurrentMoof < moofs.Length()); + } + if (mCurrentSample < moofs[mCurrentMoof].mIndex.Length()) { + break; + } + mCurrentSample = 0; + ++mCurrentMoof; + } + return &moofs[mCurrentMoof].mIndex[mCurrentSample]; +} + +void SampleIterator::Next() +{ + ++mCurrentSample; +} + +void SampleIterator::Seek(Microseconds aTime) +{ + size_t syncMoof = 0; + size_t syncSample = 0; + mCurrentMoof = 0; + mCurrentSample = 0; + Sample* sample; + while (!!(sample = Get())) { + if (sample->mCompositionRange.start > aTime) { + break; + } + if (sample->mSync) { + syncMoof = mCurrentMoof; + syncSample = mCurrentSample; + } + if (sample->mCompositionRange.start == aTime) { + break; + } + Next(); + } + mCurrentMoof = syncMoof; + mCurrentSample = syncSample; +} + +Microseconds +SampleIterator::GetNextKeyframeTime() +{ + SampleIterator itr(*this); + Sample* sample; + while (!!(sample = itr.Get())) { + if (sample->mSync) { + return sample->mCompositionRange.start; + } + itr.Next(); + } + return -1; +} + +Index::Index(const nsTArray<Indice>& aIndex, + Stream* aSource, + uint32_t aTrackId, + bool aIsAudio) + : mSource(aSource) + , mIsAudio(aIsAudio) +{ + if (aIndex.IsEmpty()) { + mMoofParser = new MoofParser(aSource, aTrackId, aIsAudio); + } else { + if (!mIndex.SetCapacity(aIndex.Length(), fallible)) { + // OOM. + return; + } + media::IntervalSet<int64_t> intervalTime; + MediaByteRange intervalRange; + bool haveSync = false; + bool progressive = true; + int64_t lastOffset = 0; + for (size_t i = 0; i < aIndex.Length(); i++) { + const Indice& indice = aIndex[i]; + if (indice.sync || mIsAudio) { + haveSync = true; + } + if (!haveSync) { + continue; + } + + Sample sample; + sample.mByteRange = MediaByteRange(indice.start_offset, + indice.end_offset); + sample.mCompositionRange = Interval<Microseconds>(indice.start_composition, + indice.end_composition); + sample.mDecodeTime = indice.start_decode; + sample.mSync = indice.sync || mIsAudio; + // FIXME: Make this infallible after bug 968520 is done. + MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible)); + if (indice.start_offset < lastOffset) { + NS_WARNING("Chunks in MP4 out of order, expect slow down"); + progressive = false; + } + lastOffset = indice.end_offset; + + // Pack audio samples in group of 128. + if (sample.mSync && progressive && (!mIsAudio || !(i % 128))) { + if (mDataOffset.Length()) { + auto& last = mDataOffset.LastElement(); + last.mEndOffset = intervalRange.mEnd; + NS_ASSERTION(intervalTime.Length() == 1, "Discontinuous samples between keyframes"); + last.mTime.start = intervalTime.GetStart(); + last.mTime.end = intervalTime.GetEnd(); + } + if (!mDataOffset.AppendElement(MP4DataOffset(mIndex.Length() - 1, + indice.start_offset), + fallible)) { + // OOM. + return; + } + intervalTime = media::IntervalSet<int64_t>(); + intervalRange = MediaByteRange(); + } + intervalTime += media::Interval<int64_t>(sample.mCompositionRange.start, + sample.mCompositionRange.end); + intervalRange = intervalRange.Span(sample.mByteRange); + } + + if (mDataOffset.Length() && progressive) { + auto& last = mDataOffset.LastElement(); + last.mEndOffset = aIndex.LastElement().end_offset; + last.mTime = Interval<int64_t>(intervalTime.GetStart(), intervalTime.GetEnd()); + } else { + mDataOffset.Clear(); + } + } +} + +Index::~Index() {} + +void +Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges) +{ + UpdateMoofIndex(aByteRanges, false); +} + +void +Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges, bool aCanEvict) +{ + if (!mMoofParser) { + return; + } + size_t moofs = mMoofParser->Moofs().Length(); + bool canEvict = aCanEvict && moofs > 1; + if (canEvict) { + // Check that we can trim the mMoofParser. We can only do so if all + // iterators have demuxed all possible samples. + for (const SampleIterator* iterator : mIterators) { + if ((iterator->mCurrentSample == 0 && iterator->mCurrentMoof == moofs) || + iterator->mCurrentMoof == moofs - 1) { + continue; + } + canEvict = false; + break; + } + } + mMoofParser->RebuildFragmentedIndex(aByteRanges, &canEvict); + if (canEvict) { + // The moofparser got trimmed. Adjust all registered iterators. + for (SampleIterator* iterator : mIterators) { + iterator->mCurrentMoof -= moofs - 1; + } + } +} + +Microseconds +Index::GetEndCompositionIfBuffered(const MediaByteRangeSet& aByteRanges) +{ + FallibleTArray<Sample>* index; + if (mMoofParser) { + if (!mMoofParser->ReachedEnd() || mMoofParser->Moofs().IsEmpty()) { + return 0; + } + index = &mMoofParser->Moofs().LastElement().mIndex; + } else { + index = &mIndex; + } + + Microseconds lastComposition = 0; + RangeFinder rangeFinder(aByteRanges); + for (size_t i = index->Length(); i--;) { + const Sample& sample = (*index)[i]; + if (!rangeFinder.Contains(sample.mByteRange)) { + return 0; + } + lastComposition = std::max(lastComposition, sample.mCompositionRange.end); + if (sample.mSync) { + return lastComposition; + } + } + return 0; +} + +TimeIntervals +Index::ConvertByteRangesToTimeRanges(const MediaByteRangeSet& aByteRanges) +{ + if (aByteRanges == mLastCachedRanges) { + return mLastBufferedRanges; + } + mLastCachedRanges = aByteRanges; + + if (mDataOffset.Length()) { + TimeIntervals timeRanges; + for (const auto& range : aByteRanges) { + uint32_t start = mDataOffset.IndexOfFirstElementGt(range.mStart - 1); + if (!mIsAudio && start == mDataOffset.Length()) { + continue; + } + uint32_t end = mDataOffset.IndexOfFirstElementGt(range.mEnd, MP4DataOffset::EndOffsetComparator()); + if (!mIsAudio && end < start) { + continue; + } + if (mIsAudio && start && + range.Intersects(MediaByteRange(mDataOffset[start-1].mStartOffset, + mDataOffset[start-1].mEndOffset))) { + // Check if previous audio data block contains some available samples. + for (size_t i = mDataOffset[start-1].mIndex; i < mIndex.Length(); i++) { + if (range.ContainsStrict(mIndex[i].mByteRange)) { + timeRanges += + TimeInterval(TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start), + TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end)); + } + } + } + if (end > start) { + timeRanges += + TimeInterval(TimeUnit::FromMicroseconds(mDataOffset[start].mTime.start), + TimeUnit::FromMicroseconds(mDataOffset[end-1].mTime.end)); + } + if (end < mDataOffset.Length()) { + // Find samples in partial block contained in the byte range. + for (size_t i = mDataOffset[end].mIndex; + i < mIndex.Length() && range.ContainsStrict(mIndex[i].mByteRange); + i++) { + timeRanges += + TimeInterval(TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.start), + TimeUnit::FromMicroseconds(mIndex[i].mCompositionRange.end)); + } + } + } + mLastBufferedRanges = timeRanges; + return timeRanges; + } + + RangeFinder rangeFinder(aByteRanges); + nsTArray<Interval<Microseconds>> timeRanges; + nsTArray<FallibleTArray<Sample>*> indexes; + if (mMoofParser) { + // We take the index out of the moof parser and move it into a local + // variable so we don't get concurrency issues. It gets freed when we + // exit this function. + for (int i = 0; i < mMoofParser->Moofs().Length(); i++) { + Moof& moof = mMoofParser->Moofs()[i]; + + // We need the entire moof in order to play anything + if (rangeFinder.Contains(moof.mRange)) { + if (rangeFinder.Contains(moof.mMdatRange)) { + Interval<Microseconds>::SemiNormalAppend(timeRanges, moof.mTimeRange); + } else { + indexes.AppendElement(&moof.mIndex); + } + } + } + } else { + indexes.AppendElement(&mIndex); + } + + bool hasSync = false; + for (size_t i = 0; i < indexes.Length(); i++) { + FallibleTArray<Sample>* index = indexes[i]; + for (size_t j = 0; j < index->Length(); j++) { + const Sample& sample = (*index)[j]; + if (!rangeFinder.Contains(sample.mByteRange)) { + // We process the index in decode order so we clear hasSync when we hit + // a range that isn't buffered. + hasSync = false; + continue; + } + + hasSync |= sample.mSync; + if (!hasSync) { + continue; + } + + Interval<Microseconds>::SemiNormalAppend(timeRanges, + sample.mCompositionRange); + } + } + + // This fixes up when the compositon order differs from the byte range order + nsTArray<Interval<Microseconds>> timeRangesNormalized; + Interval<Microseconds>::Normalize(timeRanges, &timeRangesNormalized); + // convert timeRanges. + media::TimeIntervals ranges; + for (size_t i = 0; i < timeRangesNormalized.Length(); i++) { + ranges += + media::TimeInterval(media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].start), + media::TimeUnit::FromMicroseconds(timeRangesNormalized[i].end)); + } + mLastBufferedRanges = ranges; + return ranges; +} + +uint64_t +Index::GetEvictionOffset(Microseconds aTime) +{ + uint64_t offset = std::numeric_limits<uint64_t>::max(); + if (mMoofParser) { + // We need to keep the whole moof if we're keeping any of it because the + // parser doesn't keep parsed moofs. + for (int i = 0; i < mMoofParser->Moofs().Length(); i++) { + Moof& moof = mMoofParser->Moofs()[i]; + + if (moof.mTimeRange.Length() && moof.mTimeRange.end > aTime) { + offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart, + moof.mMdatRange.mStart))); + } + } + } else { + // We've already parsed and stored the moov so we don't need to keep it. + // All we need to keep is the sample data itself. + for (size_t i = 0; i < mIndex.Length(); i++) { + const Sample& sample = mIndex[i]; + if (aTime >= sample.mCompositionRange.end) { + offset = std::min(offset, uint64_t(sample.mByteRange.mEnd)); + } + } + } + return offset; +} + +void +Index::RegisterIterator(SampleIterator* aIterator) +{ + mIterators.AppendElement(aIterator); +} + +void +Index::UnregisterIterator(SampleIterator* aIterator) +{ + mIterators.RemoveElement(aIterator); +} + +} diff --git a/media/libstagefright/binding/MP4Metadata.cpp b/media/libstagefright/binding/MP4Metadata.cpp new file mode 100644 index 000000000..eb5350704 --- /dev/null +++ b/media/libstagefright/binding/MP4Metadata.cpp @@ -0,0 +1,880 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "include/MPEG4Extractor.h" +#include "media/stagefright/DataSource.h" +#include "media/stagefright/MediaDefs.h" +#include "media/stagefright/MediaSource.h" +#include "media/stagefright/MetaData.h" +#include "mozilla/Assertions.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/UniquePtr.h" +#include "VideoUtils.h" +#include "mp4_demuxer/MoofParser.h" +#include "mp4_demuxer/MP4Metadata.h" +#include "mp4_demuxer/Stream.h" +#include "MediaPrefs.h" + +#include <limits> +#include <stdint.h> +#include <vector> + +#ifdef MOZ_RUST_MP4PARSE +// OpusDecoder header is really needed only by MP4 in rust +#include "OpusDecoder.h" +#include "mp4parse.h" + +struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } }; +#endif + +using namespace stagefright; + +namespace mp4_demuxer +{ + +class DataSourceAdapter : public DataSource +{ +public: + explicit DataSourceAdapter(Stream* aSource) : mSource(aSource) {} + + ~DataSourceAdapter() {} + + virtual status_t initCheck() const { return NO_ERROR; } + + virtual ssize_t readAt(off64_t offset, void* data, size_t size) + { + MOZ_ASSERT(((ssize_t)size) >= 0); + size_t bytesRead; + if (!mSource->ReadAt(offset, data, size, &bytesRead)) + return ERROR_IO; + + if (bytesRead == 0) + return ERROR_END_OF_STREAM; + + MOZ_ASSERT(((ssize_t)bytesRead) > 0); + return bytesRead; + } + + virtual status_t getSize(off64_t* size) + { + if (!mSource->Length(size)) + return ERROR_UNSUPPORTED; + return NO_ERROR; + } + + virtual uint32_t flags() { return kWantsPrefetching | kIsHTTPBasedSource; } + + virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; } + +private: + RefPtr<Stream> mSource; +}; + +class MP4MetadataStagefright +{ +public: + explicit MP4MetadataStagefright(Stream* aSource); + ~MP4MetadataStagefright(); + + static bool HasCompleteMetadata(Stream* aSource); + static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource); + uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const; + mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const; + bool CanSeek() const; + + const CryptoFile& Crypto() const; + + bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID); + +private: + int32_t GetTrackNumber(mozilla::TrackID aTrackID); + void UpdateCrypto(const stagefright::MetaData* aMetaData); + mozilla::UniquePtr<mozilla::TrackInfo> CheckTrack(const char* aMimeType, + stagefright::MetaData* aMetaData, + int32_t aIndex) const; + CryptoFile mCrypto; + RefPtr<Stream> mSource; + sp<MediaExtractor> mMetadataExtractor; + bool mCanSeek; +}; + +#ifdef MOZ_RUST_MP4PARSE + +// Wrap an mp4_demuxer::Stream to remember the read offset. + +class RustStreamAdaptor { +public: + explicit RustStreamAdaptor(Stream* aSource) + : mSource(aSource) + , mOffset(0) + { + } + + ~RustStreamAdaptor() {} + + bool Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read); + +private: + Stream* mSource; + CheckedInt<size_t> mOffset; +}; + +class MP4MetadataRust +{ +public: + explicit MP4MetadataRust(Stream* aSource); + ~MP4MetadataRust(); + + static bool HasCompleteMetadata(Stream* aSource); + static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource); + uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const; + mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const; + bool CanSeek() const; + + const CryptoFile& Crypto() const; + + bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID); + +private: + Maybe<uint32_t> TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const; + + CryptoFile mCrypto; + RefPtr<Stream> mSource; + RustStreamAdaptor mRustSource; + mozilla::UniquePtr<mp4parse_parser, FreeMP4Parser> mRustParser; +}; +#endif + +MP4Metadata::MP4Metadata(Stream* aSource) + : mStagefright(MakeUnique<MP4MetadataStagefright>(aSource)) +#ifdef MOZ_RUST_MP4PARSE + , mRust(MakeUnique<MP4MetadataRust>(aSource)) + , mPreferRust(false) + , mReportedAudioTrackTelemetry(false) + , mReportedVideoTrackTelemetry(false) +#ifndef RELEASE_OR_BETA + , mRustTestMode(MediaPrefs::RustTestMode()) +#endif +#endif +{ +} + +MP4Metadata::~MP4Metadata() +{ +} + +/*static*/ bool +MP4Metadata::HasCompleteMetadata(Stream* aSource) +{ + return MP4MetadataStagefright::HasCompleteMetadata(aSource); +} + +/*static*/ already_AddRefed<mozilla::MediaByteBuffer> +MP4Metadata::Metadata(Stream* aSource) +{ + return MP4MetadataStagefright::Metadata(aSource); +} + +static const char * +TrackTypeToString(mozilla::TrackInfo::TrackType aType) +{ + switch (aType) { + case mozilla::TrackInfo::kAudioTrack: + return "audio"; + case mozilla::TrackInfo::kVideoTrack: + return "video"; + default: + return "unknown"; + } +} + +uint32_t +MP4Metadata::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const +{ + static LazyLogModule sLog("MP4Metadata"); + + uint32_t numTracks = mStagefright->GetNumberTracks(aType); + +#ifdef MOZ_RUST_MP4PARSE + if (!mRust) { + return numTracks; + } + + uint32_t numTracksRust = mRust->GetNumberTracks(aType); + MOZ_LOG(sLog, LogLevel::Info, ("%s tracks found: stagefright=%u rust=%u", + TrackTypeToString(aType), numTracks, numTracksRust)); + + bool numTracksMatch = numTracks == numTracksRust; + + if (aType == mozilla::TrackInfo::kAudioTrack && !mReportedAudioTrackTelemetry) { + Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_AUDIO, + numTracksMatch); + mReportedAudioTrackTelemetry = true; + } else if (aType == mozilla::TrackInfo::kVideoTrack && !mReportedVideoTrackTelemetry) { + Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_TRACK_MATCH_VIDEO, + numTracksMatch); + mReportedVideoTrackTelemetry = true; + } + + if (mPreferRust || ShouldPreferRust()) { + MOZ_LOG(sLog, LogLevel::Info, ("Preferring rust demuxer")); + mPreferRust = true; + return numTracksRust; + } +#endif // MOZ_RUST_MP4PARSE + + return numTracks; +} + +#ifdef MOZ_RUST_MP4PARSE +bool MP4Metadata::ShouldPreferRust() const { + if (!mRust) { + return false; + } + // See if there's an Opus track. + uint32_t numTracks = mRust->GetNumberTracks(TrackInfo::kAudioTrack); + for (auto i = 0; i < numTracks; i++) { + auto info = mRust->GetTrackInfo(TrackInfo::kAudioTrack, i); + if (!info) { + return false; + } + if (info->mMimeType.EqualsASCII("audio/opus") || + info->mMimeType.EqualsASCII("audio/flac")) { + return true; + } + } + + numTracks = mRust->GetNumberTracks(TrackInfo::kVideoTrack); + for (auto i = 0; i < numTracks; i++) { + auto info = mRust->GetTrackInfo(TrackInfo::kVideoTrack, i); + if (!info) { + return false; + } + if (info->mMimeType.EqualsASCII("video/vp9")) { + return true; + } + } + // Otherwise, fall back. + return false; +} +#endif // MOZ_RUST_MP4PARSE + +mozilla::UniquePtr<mozilla::TrackInfo> +MP4Metadata::GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const +{ + mozilla::UniquePtr<mozilla::TrackInfo> info = + mStagefright->GetTrackInfo(aType, aTrackNumber); + +#ifdef MOZ_RUST_MP4PARSE + if (!mRust) { + return info; + } + + mozilla::UniquePtr<mozilla::TrackInfo> infoRust = + mRust->GetTrackInfo(aType, aTrackNumber); + +#ifndef RELEASE_OR_BETA + if (mRustTestMode && info) { + MOZ_DIAGNOSTIC_ASSERT(infoRust); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mId == info->mId); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mKind == info->mKind); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mLabel == info->mLabel); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mLanguage == info->mLanguage); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mEnabled == info->mEnabled); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mTrackId == info->mTrackId); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mMimeType == info->mMimeType); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mDuration == info->mDuration); + MOZ_DIAGNOSTIC_ASSERT(infoRust->mMediaTime == info->mMediaTime); + switch (aType) { + case mozilla::TrackInfo::kAudioTrack: { + AudioInfo *audioRust = infoRust->GetAsAudioInfo(), *audio = info->GetAsAudioInfo(); + MOZ_DIAGNOSTIC_ASSERT(audioRust->mRate == audio->mRate); + MOZ_DIAGNOSTIC_ASSERT(audioRust->mChannels == audio->mChannels); + MOZ_DIAGNOSTIC_ASSERT(audioRust->mBitDepth == audio->mBitDepth); + // TODO: These fields aren't implemented in the Rust demuxer yet. + //MOZ_DIAGNOSTIC_ASSERT(audioRust->mProfile != audio->mProfile); + //MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile != audio->mExtendedProfile); + break; + } + case mozilla::TrackInfo::kVideoTrack: { + VideoInfo *videoRust = infoRust->GetAsVideoInfo(), *video = info->GetAsVideoInfo(); + MOZ_DIAGNOSTIC_ASSERT(videoRust->mDisplay == video->mDisplay); + MOZ_DIAGNOSTIC_ASSERT(videoRust->mImage == video->mImage); + break; + } + default: + break; + } + } +#endif + + if (!mPreferRust) { + return info; + } + MOZ_ASSERT(infoRust); + return infoRust; +#endif + + return info; +} + +bool +MP4Metadata::CanSeek() const +{ + return mStagefright->CanSeek(); +} + +const CryptoFile& +MP4Metadata::Crypto() const +{ + return mStagefright->Crypto(); +} + +bool +MP4Metadata::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID) +{ +#ifdef MOZ_RUST_MP4PARSE + if (mRust && mPreferRust && mRust->ReadTrackIndex(aDest, aTrackID)) { + return true; + } + aDest.Clear(); +#endif + return mStagefright->ReadTrackIndex(aDest, aTrackID); +} + +static inline bool +ConvertIndex(FallibleTArray<Index::Indice>& aDest, + const nsTArray<stagefright::MediaSource::Indice>& aIndex, + int64_t aMediaTime) +{ + if (!aDest.SetCapacity(aIndex.Length(), mozilla::fallible)) { + return false; + } + for (size_t i = 0; i < aIndex.Length(); i++) { + Index::Indice indice; + const stagefright::MediaSource::Indice& s_indice = aIndex[i]; + indice.start_offset = s_indice.start_offset; + indice.end_offset = s_indice.end_offset; + indice.start_composition = s_indice.start_composition - aMediaTime; + indice.end_composition = s_indice.end_composition - aMediaTime; + indice.start_decode = s_indice.start_decode; + indice.sync = s_indice.sync; + // FIXME: Make this infallible after bug 968520 is done. + MOZ_ALWAYS_TRUE(aDest.AppendElement(indice, mozilla::fallible)); + } + return true; +} + +MP4MetadataStagefright::MP4MetadataStagefright(Stream* aSource) + : mSource(aSource) + , mMetadataExtractor(new MPEG4Extractor(new DataSourceAdapter(mSource))) + , mCanSeek(mMetadataExtractor->flags() & MediaExtractor::CAN_SEEK) +{ + sp<MetaData> metaData = mMetadataExtractor->getMetaData(); + + if (metaData.get()) { + UpdateCrypto(metaData.get()); + } +} + +MP4MetadataStagefright::~MP4MetadataStagefright() +{ +} + +uint32_t +MP4MetadataStagefright::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const +{ + size_t tracks = mMetadataExtractor->countTracks(); + uint32_t total = 0; + for (size_t i = 0; i < tracks; i++) { + sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i); + + const char* mimeType; + if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) { + continue; + } + switch (aType) { + case mozilla::TrackInfo::kAudioTrack: + if (!strncmp(mimeType, "audio/", 6) && + CheckTrack(mimeType, metaData.get(), i)) { + total++; + } + break; + case mozilla::TrackInfo::kVideoTrack: + if (!strncmp(mimeType, "video/", 6) && + CheckTrack(mimeType, metaData.get(), i)) { + total++; + } + break; + default: + break; + } + } + return total; +} + +mozilla::UniquePtr<mozilla::TrackInfo> +MP4MetadataStagefright::GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const +{ + size_t tracks = mMetadataExtractor->countTracks(); + if (!tracks) { + return nullptr; + } + int32_t index = -1; + const char* mimeType; + sp<MetaData> metaData; + + size_t i = 0; + while (i < tracks) { + metaData = mMetadataExtractor->getTrackMetaData(i); + + if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) { + continue; + } + switch (aType) { + case mozilla::TrackInfo::kAudioTrack: + if (!strncmp(mimeType, "audio/", 6) && + CheckTrack(mimeType, metaData.get(), i)) { + index++; + } + break; + case mozilla::TrackInfo::kVideoTrack: + if (!strncmp(mimeType, "video/", 6) && + CheckTrack(mimeType, metaData.get(), i)) { + index++; + } + break; + default: + break; + } + if (index == aTrackNumber) { + break; + } + i++; + } + if (index < 0) { + return nullptr; + } + + UniquePtr<mozilla::TrackInfo> e = CheckTrack(mimeType, metaData.get(), index); + + if (e) { + metaData = mMetadataExtractor->getMetaData(); + int64_t movieDuration; + if (!e->mDuration && + metaData->findInt64(kKeyMovieDuration, &movieDuration)) { + // No duration in track, use movie extend header box one. + e->mDuration = movieDuration; + } + } + + return e; +} + +mozilla::UniquePtr<mozilla::TrackInfo> +MP4MetadataStagefright::CheckTrack(const char* aMimeType, + stagefright::MetaData* aMetaData, + int32_t aIndex) const +{ + sp<MediaSource> track = mMetadataExtractor->getTrack(aIndex); + if (!track.get()) { + return nullptr; + } + + UniquePtr<mozilla::TrackInfo> e; + + if (!strncmp(aMimeType, "audio/", 6)) { + auto info = mozilla::MakeUnique<MP4AudioInfo>(); + info->Update(aMetaData, aMimeType); + e = Move(info); + } else if (!strncmp(aMimeType, "video/", 6)) { + auto info = mozilla::MakeUnique<MP4VideoInfo>(); + info->Update(aMetaData, aMimeType); + e = Move(info); + } + + if (e && e->IsValid()) { + return e; + } + + return nullptr; +} + +bool +MP4MetadataStagefright::CanSeek() const +{ + return mCanSeek; +} + +const CryptoFile& +MP4MetadataStagefright::Crypto() const +{ + return mCrypto; +} + +void +MP4MetadataStagefright::UpdateCrypto(const MetaData* aMetaData) +{ + const void* data; + size_t size; + uint32_t type; + + // There's no point in checking that the type matches anything because it + // isn't set consistently in the MPEG4Extractor. + if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) { + return; + } + mCrypto.Update(reinterpret_cast<const uint8_t*>(data), size); +} + +bool +MP4MetadataStagefright::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID) +{ + size_t numTracks = mMetadataExtractor->countTracks(); + int32_t trackNumber = GetTrackNumber(aTrackID); + if (trackNumber < 0) { + return false; + } + sp<MediaSource> track = mMetadataExtractor->getTrack(trackNumber); + if (!track.get()) { + return false; + } + sp<MetaData> metadata = mMetadataExtractor->getTrackMetaData(trackNumber); + int64_t mediaTime; + if (!metadata->findInt64(kKeyMediaTime, &mediaTime)) { + mediaTime = 0; + } + bool rv = ConvertIndex(aDest, track->exportIndex(), mediaTime); + + return rv; +} + +int32_t +MP4MetadataStagefright::GetTrackNumber(mozilla::TrackID aTrackID) +{ + size_t numTracks = mMetadataExtractor->countTracks(); + for (size_t i = 0; i < numTracks; i++) { + sp<MetaData> metaData = mMetadataExtractor->getTrackMetaData(i); + if (!metaData.get()) { + continue; + } + int32_t value; + if (metaData->findInt32(kKeyTrackID, &value) && value == aTrackID) { + return i; + } + } + return -1; +} + +/*static*/ bool +MP4MetadataStagefright::HasCompleteMetadata(Stream* aSource) +{ + auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false); + return parser->HasMetadata(); +} + +/*static*/ already_AddRefed<mozilla::MediaByteBuffer> +MP4MetadataStagefright::Metadata(Stream* aSource) +{ + auto parser = mozilla::MakeUnique<MoofParser>(aSource, 0, false); + return parser->Metadata(); +} + +#ifdef MOZ_RUST_MP4PARSE +bool +RustStreamAdaptor::Read(uint8_t* buffer, uintptr_t size, size_t* bytes_read) +{ + if (!mOffset.isValid()) { + static LazyLogModule sLog("MP4Metadata"); + MOZ_LOG(sLog, LogLevel::Error, ("Overflow in source stream offset")); + return false; + } + bool rv = mSource->ReadAt(mOffset.value(), buffer, size, bytes_read); + if (rv) { + mOffset += *bytes_read; + } + return rv; +} + +// Wrapper to allow rust to call our read adaptor. +static intptr_t +read_source(uint8_t* buffer, uintptr_t size, void* userdata) +{ + MOZ_ASSERT(buffer); + MOZ_ASSERT(userdata); + + auto source = reinterpret_cast<RustStreamAdaptor*>(userdata); + size_t bytes_read = 0; + bool rv = source->Read(buffer, size, &bytes_read); + if (!rv) { + static LazyLogModule sLog("MP4Metadata"); + MOZ_LOG(sLog, LogLevel::Warning, ("Error reading source data")); + return -1; + } + return bytes_read; +} + +MP4MetadataRust::MP4MetadataRust(Stream* aSource) + : mSource(aSource) + , mRustSource(aSource) +{ + mp4parse_io io = { read_source, &mRustSource }; + mRustParser.reset(mp4parse_new(&io)); + MOZ_ASSERT(mRustParser); + + static LazyLogModule sLog("MP4Metadata"); + mp4parse_error rv = mp4parse_read(mRustParser.get()); + MOZ_LOG(sLog, LogLevel::Debug, ("rust parser returned %d\n", rv)); + Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_SUCCESS, + rv == MP4PARSE_OK); + if (rv != MP4PARSE_OK) { + MOZ_ASSERT(rv > 0); + Telemetry::Accumulate(Telemetry::MEDIA_RUST_MP4PARSE_ERROR_CODE, rv); + } +} + +MP4MetadataRust::~MP4MetadataRust() +{ +} + +bool +TrackTypeEqual(TrackInfo::TrackType aLHS, mp4parse_track_type aRHS) +{ + switch (aLHS) { + case TrackInfo::kAudioTrack: + return aRHS == MP4PARSE_TRACK_TYPE_AUDIO; + case TrackInfo::kVideoTrack: + return aRHS == MP4PARSE_TRACK_TYPE_VIDEO; + default: + return false; + } +} + +uint32_t +MP4MetadataRust::GetNumberTracks(mozilla::TrackInfo::TrackType aType) const +{ + static LazyLogModule sLog("MP4Metadata"); + + uint32_t tracks; + auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks); + if (rv != MP4PARSE_OK) { + MOZ_LOG(sLog, LogLevel::Warning, + ("rust parser error %d counting tracks", rv)); + return 0; + } + MOZ_LOG(sLog, LogLevel::Info, ("rust parser found %u tracks", tracks)); + + uint32_t total = 0; + for (uint32_t i = 0; i < tracks; ++i) { + mp4parse_track_info track_info; + rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info); + if (rv != MP4PARSE_OK) { + continue; + } + if (TrackTypeEqual(aType, track_info.track_type)) { + total += 1; + } + } + + return total; +} + +Maybe<uint32_t> +MP4MetadataRust::TrackTypeToGlobalTrackIndex(mozilla::TrackInfo::TrackType aType, size_t aTrackNumber) const +{ + uint32_t tracks; + auto rv = mp4parse_get_track_count(mRustParser.get(), &tracks); + if (rv != MP4PARSE_OK) { + return Nothing(); + } + + /* The MP4Metadata API uses a per-TrackType index of tracks, but mp4parse + (and libstagefright) use a global track index. Convert the index by + counting the tracks of the requested type and returning the global + track index when a match is found. */ + uint32_t perType = 0; + for (uint32_t i = 0; i < tracks; ++i) { + mp4parse_track_info track_info; + rv = mp4parse_get_track_info(mRustParser.get(), i, &track_info); + if (rv != MP4PARSE_OK) { + continue; + } + if (TrackTypeEqual(aType, track_info.track_type)) { + if (perType == aTrackNumber) { + return Some(i); + } + perType += 1; + } + } + + return Nothing(); +} + +mozilla::UniquePtr<mozilla::TrackInfo> +MP4MetadataRust::GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const +{ + static LazyLogModule sLog("MP4Metadata"); + + Maybe<uint32_t> trackIndex = TrackTypeToGlobalTrackIndex(aType, aTrackNumber); + if (trackIndex.isNothing()) { + return nullptr; + } + + mp4parse_track_info info; + auto rv = mp4parse_get_track_info(mRustParser.get(), trackIndex.value(), &info); + if (rv != MP4PARSE_OK) { + MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_info returned %d", rv)); + return nullptr; + } +#ifdef DEBUG + const char* codec_string = "unrecognized"; + switch (info.codec) { + case MP4PARSE_CODEC_UNKNOWN: codec_string = "unknown"; break; + case MP4PARSE_CODEC_AAC: codec_string = "aac"; break; + case MP4PARSE_CODEC_OPUS: codec_string = "opus"; break; + case MP4PARSE_CODEC_FLAC: codec_string = "flac"; break; + case MP4PARSE_CODEC_AVC: codec_string = "h.264"; break; + case MP4PARSE_CODEC_VP9: codec_string = "vp9"; break; + case MP4PARSE_CODEC_MP3: codec_string = "mp3"; break; + } + MOZ_LOG(sLog, LogLevel::Debug, ("track codec %s (%u)\n", + codec_string, info.codec)); +#endif + + // This specialization interface is crazy. + UniquePtr<mozilla::TrackInfo> e; + switch (aType) { + case TrackInfo::TrackType::kAudioTrack: { + mp4parse_track_audio_info audio; + auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio); + if (rv != MP4PARSE_OK) { + MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv)); + return nullptr; + } + auto track = mozilla::MakeUnique<mozilla::AudioInfo>(); + if (info.codec == MP4PARSE_CODEC_OPUS) { + track->mMimeType = NS_LITERAL_CSTRING("audio/opus"); + // The Opus decoder expects the container's codec delay or + // pre-skip value, in microseconds, as a 64-bit int at the + // start of the codec-specific config blob. + MOZ_ASSERT(audio.codec_specific_config.data); + MOZ_ASSERT(audio.codec_specific_config.length >= 12); + uint16_t preskip = + LittleEndian::readUint16(audio.codec_specific_config.data + 10); + MOZ_LOG(sLog, LogLevel::Debug, + ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip)); + OpusDataDecoder::AppendCodecDelay(track->mCodecSpecificConfig, + mozilla::FramesToUsecs(preskip, 48000).value()); + } else if (info.codec == MP4PARSE_CODEC_AAC) { + track->mMimeType = MEDIA_MIMETYPE_AUDIO_AAC; + } else if (info.codec == MP4PARSE_CODEC_FLAC) { + track->mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC; + } else if (info.codec == MP4PARSE_CODEC_MP3) { + track->mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG; + } + track->mCodecSpecificConfig->AppendElements( + audio.codec_specific_config.data, + audio.codec_specific_config.length); + track->mRate = audio.sample_rate; + track->mChannels = audio.channels; + track->mBitDepth = audio.bit_depth; + track->mDuration = info.duration; + track->mMediaTime = info.media_time; + track->mTrackId = info.track_id; + e = Move(track); + } + break; + case TrackInfo::TrackType::kVideoTrack: { + mp4parse_track_video_info video; + auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video); + if (rv != MP4PARSE_OK) { + MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv)); + return nullptr; + } + auto track = mozilla::MakeUnique<MP4VideoInfo>(); + track->Update(&info, &video); + e = Move(track); + } + break; + default: + MOZ_LOG(sLog, LogLevel::Warning, ("unhandled track type %d", aType)); + return nullptr; + break; + } + + // No duration in track, use fragment_duration. + if (e && !e->mDuration) { + mp4parse_fragment_info info; + auto rv = mp4parse_get_fragment_info(mRustParser.get(), &info); + if (rv == MP4PARSE_OK) { + e->mDuration = info.fragment_duration; + } + } + + if (e && e->IsValid()) { + return e; + } + MOZ_LOG(sLog, LogLevel::Debug, ("TrackInfo didn't validate")); + + return nullptr; +} + +bool +MP4MetadataRust::CanSeek() const +{ + MOZ_ASSERT(false, "Not yet implemented"); + return false; +} + +const CryptoFile& +MP4MetadataRust::Crypto() const +{ + MOZ_ASSERT(false, "Not yet implemented"); + return mCrypto; +} + +bool +MP4MetadataRust::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID) +{ + uint8_t fragmented = false; + auto rv = mp4parse_is_fragmented(mRustParser.get(), aTrackID, &fragmented); + if (rv != MP4PARSE_OK) { + return false; + } + + if (fragmented) { + return true; + } + + // For non-fragmented mp4. + NS_WARNING("Not yet implemented"); + + return false; +} + +/*static*/ bool +MP4MetadataRust::HasCompleteMetadata(Stream* aSource) +{ + MOZ_ASSERT(false, "Not yet implemented"); + return false; +} + +/*static*/ already_AddRefed<mozilla::MediaByteBuffer> +MP4MetadataRust::Metadata(Stream* aSource) +{ + MOZ_ASSERT(false, "Not yet implemented"); + return nullptr; +} +#endif + +} // namespace mp4_demuxer diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp new file mode 100644 index 000000000..ced054282 --- /dev/null +++ b/media/libstagefright/binding/MoofParser.cpp @@ -0,0 +1,925 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/MoofParser.h" +#include "mp4_demuxer/Box.h" +#include "mp4_demuxer/SinfParser.h" +#include <limits> +#include "Intervals.h" + +#include "mozilla/CheckedInt.h" +#include "mozilla/Logging.h" + +#if defined(MOZ_FMP4) +extern mozilla::LogModule* GetDemuxerLog(); + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define LOG(name, arg, ...) MOZ_LOG(GetDemuxerLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#else +#define LOG(...) +#endif + +namespace mp4_demuxer +{ + +using namespace stagefright; +using namespace mozilla; + +bool +MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges) +{ + BoxContext context(mSource, aByteRanges); + return RebuildFragmentedIndex(context); +} + +bool +MoofParser::RebuildFragmentedIndex( + const MediaByteRangeSet& aByteRanges, bool* aCanEvict) +{ + MOZ_ASSERT(aCanEvict); + if (*aCanEvict && mMoofs.Length() > 1) { + MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length()); + mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1); + mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1); + *aCanEvict = true; + } else { + *aCanEvict = false; + } + return RebuildFragmentedIndex(aByteRanges); +} + +bool +MoofParser::RebuildFragmentedIndex(BoxContext& aContext) +{ + bool foundValidMoof = false; + bool foundMdat = false; + + for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) { + if (box.IsType("moov") && mInitRange.IsEmpty()) { + mInitRange = MediaByteRange(0, box.Range().mEnd); + ParseMoov(box); + } else if (box.IsType("moof")) { + Moof moof(box, mTrex, mMvhd, mMdhd, mEdts, mSinf, &mLastDecodeTime, mIsAudio); + + if (!moof.IsValid() && !box.Next().IsAvailable()) { + // Moof isn't valid abort search for now. + break; + } + + if (!mMoofs.IsEmpty()) { + // Stitch time ranges together in the case of a (hopefully small) time + // range gap between moofs. + mMoofs.LastElement().FixRounding(moof); + } + + mMoofs.AppendElement(moof); + mMediaRanges.AppendElement(moof.mRange); + foundValidMoof = true; + } else if (box.IsType("mdat") && !Moofs().IsEmpty()) { + // Check if we have all our data from last moof. + Moof& moof = Moofs().LastElement(); + media::Interval<int64_t> datarange(moof.mMdatRange.mStart, moof.mMdatRange.mEnd, 0); + media::Interval<int64_t> mdat(box.Range().mStart, box.Range().mEnd, 0); + if (datarange.Intersects(mdat)) { + mMediaRanges.LastElement() = + mMediaRanges.LastElement().Span(box.Range()); + } + } + mOffset = box.NextOffset(); + } + return foundValidMoof; +} + +MediaByteRange +MoofParser::FirstCompleteMediaHeader() +{ + if (Moofs().IsEmpty()) { + return MediaByteRange(); + } + return Moofs()[0].mRange; +} + +MediaByteRange +MoofParser::FirstCompleteMediaSegment() +{ + for (uint32_t i = 0 ; i < mMediaRanges.Length(); i++) { + if (mMediaRanges[i].Contains(Moofs()[i].mMdatRange)) { + return mMediaRanges[i]; + } + } + return MediaByteRange(); +} + +class BlockingStream : public Stream { +public: + explicit BlockingStream(Stream* aStream) : mStream(aStream) + { + } + + bool ReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read) + override + { + return mStream->ReadAt(offset, data, size, bytes_read); + } + + bool CachedReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read) + override + { + return mStream->ReadAt(offset, data, size, bytes_read); + } + + virtual bool Length(int64_t* size) override + { + return mStream->Length(size); + } + +private: + RefPtr<Stream> mStream; +}; + +bool +MoofParser::BlockingReadNextMoof() +{ + int64_t length = std::numeric_limits<int64_t>::max(); + mSource->Length(&length); + MediaByteRangeSet byteRanges; + byteRanges += MediaByteRange(0, length); + RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource); + + BoxContext context(stream, byteRanges); + for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) { + if (box.IsType("moof")) { + byteRanges.Clear(); + byteRanges += MediaByteRange(mOffset, box.Range().mEnd); + return RebuildFragmentedIndex(context); + } + } + return false; +} + +void +MoofParser::ScanForMetadata(mozilla::MediaByteRange& aFtyp, + mozilla::MediaByteRange& aMoov) +{ + int64_t length = std::numeric_limits<int64_t>::max(); + mSource->Length(&length); + MediaByteRangeSet byteRanges; + byteRanges += MediaByteRange(0, length); + RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource); + + BoxContext context(stream, byteRanges); + for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) { + if (box.IsType("ftyp")) { + aFtyp = box.Range(); + continue; + } + if (box.IsType("moov")) { + aMoov = box.Range(); + break; + } + } + mInitRange = aFtyp.Span(aMoov); +} + +bool +MoofParser::HasMetadata() +{ + MediaByteRange ftyp; + MediaByteRange moov; + ScanForMetadata(ftyp, moov); + return !!ftyp.Length() && !!moov.Length(); +} + +already_AddRefed<mozilla::MediaByteBuffer> +MoofParser::Metadata() +{ + MediaByteRange ftyp; + MediaByteRange moov; + ScanForMetadata(ftyp, moov); + CheckedInt<MediaByteBuffer::size_type> ftypLength = ftyp.Length(); + CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length(); + if (!ftypLength.isValid() || !moovLength.isValid() + || !ftypLength.value() || !moovLength.value()) { + // No ftyp or moov, or they cannot be used as array size. + return nullptr; + } + CheckedInt<MediaByteBuffer::size_type> totalLength = ftypLength + moovLength; + if (!totalLength.isValid()) { + // Addition overflow, or sum cannot be used as array size. + return nullptr; + } + RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer(); + if (!metadata->SetLength(totalLength.value(), fallible)) { + // OOM + return nullptr; + } + + RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource); + size_t read; + bool rv = + stream->ReadAt(ftyp.mStart, metadata->Elements(), ftypLength.value(), &read); + if (!rv || read != ftypLength.value()) { + return nullptr; + } + rv = + stream->ReadAt(moov.mStart, metadata->Elements() + ftypLength.value(), moovLength.value(), &read); + if (!rv || read != moovLength.value()) { + return nullptr; + } + return metadata.forget(); +} + +Interval<Microseconds> +MoofParser::GetCompositionRange(const MediaByteRangeSet& aByteRanges) +{ + Interval<Microseconds> compositionRange; + BoxContext context(mSource, aByteRanges); + for (size_t i = 0; i < mMoofs.Length(); i++) { + Moof& moof = mMoofs[i]; + Box box(&context, moof.mRange.mStart); + if (box.IsAvailable()) { + compositionRange = compositionRange.Extents(moof.mTimeRange); + } + } + return compositionRange; +} + +bool +MoofParser::ReachedEnd() +{ + int64_t length; + return mSource->Length(&length) && mOffset == length; +} + +void +MoofParser::ParseMoov(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("mvhd")) { + mMvhd = Mvhd(box); + } else if (box.IsType("trak")) { + ParseTrak(box); + } else if (box.IsType("mvex")) { + ParseMvex(box); + } + } +} + +void +MoofParser::ParseTrak(Box& aBox) +{ + Tkhd tkhd; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tkhd")) { + tkhd = Tkhd(box); + } else if (box.IsType("mdia")) { + if (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId) { + ParseMdia(box, tkhd); + } + } else if (box.IsType("edts") && + (!mTrex.mTrackId || tkhd.mTrackId == mTrex.mTrackId)) { + mEdts = Edts(box); + } + } +} + +void +MoofParser::ParseMdia(Box& aBox, Tkhd& aTkhd) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("mdhd")) { + mMdhd = Mdhd(box); + } else if (box.IsType("minf")) { + ParseMinf(box); + } + } +} + +void +MoofParser::ParseMvex(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("trex")) { + Trex trex = Trex(box); + if (!mTrex.mTrackId || trex.mTrackId == mTrex.mTrackId) { + auto trackId = mTrex.mTrackId; + mTrex = trex; + // Keep the original trackId, as should it be 0 we want to continue + // parsing all tracks. + mTrex.mTrackId = trackId; + } + } + } +} + +void +MoofParser::ParseMinf(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("stbl")) { + ParseStbl(box); + } + } +} + +void +MoofParser::ParseStbl(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("stsd")) { + ParseStsd(box); + } + } +} + +void +MoofParser::ParseStsd(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("encv") || box.IsType("enca")) { + ParseEncrypted(box); + } + } +} + +void +MoofParser::ParseEncrypted(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + // Some MP4 files have been found to have multiple sinf boxes in the same + // enc* box. This does not match spec anyway, so just choose the first + // one that parses properly. + if (box.IsType("sinf")) { + mSinf = Sinf(box); + + if (mSinf.IsValid()) { + break; + } + } + } +} + +class CtsComparator +{ +public: + bool Equals(Sample* const aA, Sample* const aB) const + { + return aA->mCompositionRange.start == aB->mCompositionRange.start; + } + bool + LessThan(Sample* const aA, Sample* const aB) const + { + return aA->mCompositionRange.start < aB->mCompositionRange.start; + } +}; + +Moof::Moof(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio) + : mRange(aBox.Range()) + , mMaxRoundingError(35000) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("traf")) { + ParseTraf(box, aTrex, aMvhd, aMdhd, aEdts, aSinf, aDecodeTime, aIsAudio); + } + } + if (IsValid()) { + if (mIndex.Length()) { + // Ensure the samples are contiguous with no gaps. + nsTArray<Sample*> ctsOrder; + for (auto& sample : mIndex) { + ctsOrder.AppendElement(&sample); + } + ctsOrder.Sort(CtsComparator()); + + for (size_t i = 1; i < ctsOrder.Length(); i++) { + ctsOrder[i-1]->mCompositionRange.end = ctsOrder[i]->mCompositionRange.start; + } + + // In MP4, the duration of a sample is defined as the delta between two decode + // timestamps. The operation above has updated the duration of each sample + // as a Sample's duration is mCompositionRange.end - mCompositionRange.start + // MSE's TrackBuffersManager expects dts that increased by the sample's + // duration, so we rewrite the dts accordingly. + int64_t presentationDuration = + ctsOrder.LastElement()->mCompositionRange.end + - ctsOrder[0]->mCompositionRange.start; + int64_t endDecodeTime = + aMdhd.ToMicroseconds((int64_t)*aDecodeTime - aEdts.mMediaStart) + + aMvhd.ToMicroseconds(aEdts.mEmptyOffset); + int64_t decodeDuration = endDecodeTime - mIndex[0].mDecodeTime; + double adjust = (double)decodeDuration / presentationDuration; + int64_t dtsOffset = mIndex[0].mDecodeTime; + int64_t compositionDuration = 0; + // Adjust the dts, ensuring that the new adjusted dts will never be greater + // than decodeTime (the next moof's decode start time). + for (auto& sample : mIndex) { + sample.mDecodeTime = dtsOffset + int64_t(compositionDuration * adjust); + compositionDuration += sample.mCompositionRange.Length(); + } + mTimeRange = Interval<Microseconds>(ctsOrder[0]->mCompositionRange.start, + ctsOrder.LastElement()->mCompositionRange.end); + } + ProcessCenc(); + } +} + +bool +Moof::GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges) +{ + aByteRanges->Clear(); + + Saiz* saiz = nullptr; + for (int i = 0; ; i++) { + if (i == mSaizs.Length()) { + return false; + } + if (mSaizs[i].mAuxInfoType == aType) { + saiz = &mSaizs[i]; + break; + } + } + Saio* saio = nullptr; + for (int i = 0; ; i++) { + if (i == mSaios.Length()) { + return false; + } + if (mSaios[i].mAuxInfoType == aType) { + saio = &mSaios[i]; + break; + } + } + + if (saio->mOffsets.Length() == 1) { + aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length()); + uint64_t offset = mRange.mStart + saio->mOffsets[0]; + for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) { + aByteRanges->AppendElement( + MediaByteRange(offset, offset + saiz->mSampleInfoSize[i])); + offset += saiz->mSampleInfoSize[i]; + } + return true; + } + + if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) { + aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length()); + for (size_t i = 0; i < saio->mOffsets.Length(); i++) { + uint64_t offset = mRange.mStart + saio->mOffsets[i]; + aByteRanges->AppendElement( + MediaByteRange(offset, offset + saiz->mSampleInfoSize[i])); + } + return true; + } + + return false; +} + +bool +Moof::ProcessCenc() +{ + nsTArray<MediaByteRange> cencRanges; + if (!GetAuxInfo(AtomType("cenc"), &cencRanges) || + cencRanges.Length() != mIndex.Length()) { + return false; + } + for (int i = 0; i < cencRanges.Length(); i++) { + mIndex[i].mCencRange = cencRanges[i]; + } + return true; +} + +void +Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio) +{ + MOZ_ASSERT(aDecodeTime); + Tfhd tfhd(aTrex); + Tfdt tfdt; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tfhd")) { + tfhd = Tfhd(box, aTrex); + } else if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) { + if (box.IsType("tfdt")) { + tfdt = Tfdt(box); + } else if (box.IsType("saiz")) { + mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType)); + } else if (box.IsType("saio")) { + mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType)); + } + } + } + if (aTrex.mTrackId && tfhd.mTrackId != aTrex.mTrackId) { + return; + } + // Now search for TRUN boxes. + uint64_t decodeTime = + tfdt.IsValid() ? tfdt.mBaseMediaDecodeTime : *aDecodeTime; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("trun")) { + if (ParseTrun(box, tfhd, aMvhd, aMdhd, aEdts, &decodeTime, aIsAudio)) { + mValid = true; + } else { + mValid = false; + break; + } + } + } + *aDecodeTime = decodeTime; +} + +void +Moof::FixRounding(const Moof& aMoof) { + Microseconds gap = aMoof.mTimeRange.start - mTimeRange.end; + if (gap > 0 && gap <= mMaxRoundingError) { + mTimeRange.end = aMoof.mTimeRange.start; + } +} + +bool +Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio) +{ + if (!aTfhd.IsValid() || !aMvhd.IsValid() || !aMdhd.IsValid() || + !aEdts.IsValid()) { + LOG(Moof, "Invalid dependencies: aTfhd(%d) aMvhd(%d) aMdhd(%d) aEdts(%d)", + aTfhd.IsValid(), aMvhd.IsValid(), aMdhd.IsValid(), !aEdts.IsValid()); + return false; + } + + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Moof, "Incomplete Box (missing flags)"); + return false; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + + if (!reader->CanReadType<uint32_t>()) { + LOG(Moof, "Incomplete Box (missing sampleCount)"); + return false; + } + uint32_t sampleCount = reader->ReadU32(); + if (sampleCount == 0) { + return true; + } + + size_t need = + ((flags & 1) ? sizeof(uint32_t) : 0) + + ((flags & 4) ? sizeof(uint32_t) : 0); + uint16_t flag[] = { 0x100, 0x200, 0x400, 0x800, 0 }; + for (size_t i = 0; flag[i]; i++) { + if (flags & flag[i]) { + need += sizeof(uint32_t) * sampleCount; + } + } + if (reader->Remaining() < need) { + LOG(Moof, "Incomplete Box (have:%lld need:%lld)", + reader->Remaining(), need); + return false; + } + + uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0); + uint32_t firstSampleFlags = + flags & 4 ? reader->ReadU32() : aTfhd.mDefaultSampleFlags; + uint64_t decodeTime = *aDecodeTime; + nsTArray<Interval<Microseconds>> timeRanges; + + if (!mIndex.SetCapacity(sampleCount, fallible)) { + LOG(Moof, "Out of Memory"); + return false; + } + + for (size_t i = 0; i < sampleCount; i++) { + uint32_t sampleDuration = + flags & 0x100 ? reader->ReadU32() : aTfhd.mDefaultSampleDuration; + uint32_t sampleSize = + flags & 0x200 ? reader->ReadU32() : aTfhd.mDefaultSampleSize; + uint32_t sampleFlags = + flags & 0x400 ? reader->ReadU32() + : i ? aTfhd.mDefaultSampleFlags : firstSampleFlags; + int32_t ctsOffset = 0; + if (flags & 0x800) { + ctsOffset = reader->Read32(); + } + + Sample sample; + sample.mByteRange = MediaByteRange(offset, offset + sampleSize); + offset += sampleSize; + + sample.mDecodeTime = + aMdhd.ToMicroseconds((int64_t)decodeTime - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset); + sample.mCompositionRange = Interval<Microseconds>( + aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset), + aMdhd.ToMicroseconds((int64_t)decodeTime + ctsOffset + sampleDuration - aEdts.mMediaStart) + aMvhd.ToMicroseconds(aEdts.mEmptyOffset)); + decodeTime += sampleDuration; + + // Sometimes audio streams don't properly mark their samples as keyframes, + // because every audio sample is a keyframe. + sample.mSync = !(sampleFlags & 0x1010000) || aIsAudio; + + // FIXME: Make this infallible after bug 968520 is done. + MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible)); + + mMdatRange = mMdatRange.Span(sample.mByteRange); + } + mMaxRoundingError += aMdhd.ToMicroseconds(sampleCount); + + *aDecodeTime = decodeTime; + + return true; +} + +Tkhd::Tkhd(Box& aBox) +{ + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Tkhd, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = + 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + 2*sizeof(int32_t); + if (reader->Remaining() < need) { + LOG(Tkhd, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + if (version == 0) { + mCreationTime = reader->ReadU32(); + mModificationTime = reader->ReadU32(); + mTrackId = reader->ReadU32(); + uint32_t reserved = reader->ReadU32(); + NS_ASSERTION(!reserved, "reserved should be 0"); + mDuration = reader->ReadU32(); + } else if (version == 1) { + mCreationTime = reader->ReadU64(); + mModificationTime = reader->ReadU64(); + mTrackId = reader->ReadU32(); + uint32_t reserved = reader->ReadU32(); + NS_ASSERTION(!reserved, "reserved should be 0"); + mDuration = reader->ReadU64(); + } + // We don't care about whatever else may be in the box. + mValid = true; +} + +Mvhd::Mvhd(Box& aBox) +{ + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Mdhd, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = + 3*(version ? sizeof(int64_t) : sizeof(int32_t)) + sizeof(uint32_t); + if (reader->Remaining() < need) { + LOG(Mvhd, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + + if (version == 0) { + mCreationTime = reader->ReadU32(); + mModificationTime = reader->ReadU32(); + mTimescale = reader->ReadU32(); + mDuration = reader->ReadU32(); + } else if (version == 1) { + mCreationTime = reader->ReadU64(); + mModificationTime = reader->ReadU64(); + mTimescale = reader->ReadU32(); + mDuration = reader->ReadU64(); + } else { + return; + } + // We don't care about whatever else may be in the box. + if (mTimescale) { + mValid = true; + } +} + +Mdhd::Mdhd(Box& aBox) + : Mvhd(aBox) +{ +} + +Trex::Trex(Box& aBox) +{ + BoxReader reader(aBox); + if (reader->Remaining() < 6*sizeof(uint32_t)) { + LOG(Trex, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)6*sizeof(uint32_t)); + return; + } + mFlags = reader->ReadU32(); + mTrackId = reader->ReadU32(); + mDefaultSampleDescriptionIndex = reader->ReadU32(); + mDefaultSampleDuration = reader->ReadU32(); + mDefaultSampleSize = reader->ReadU32(); + mDefaultSampleFlags = reader->ReadU32(); + mValid = true; +} + +Tfhd::Tfhd(Box& aBox, Trex& aTrex) + : Trex(aTrex) +{ + MOZ_ASSERT(aBox.IsType("tfhd")); + MOZ_ASSERT(aBox.Parent()->IsType("traf")); + MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof")); + + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Tfhd, "Incomplete Box (missing flags)"); + return; + } + mFlags = reader->ReadU32(); + size_t need = sizeof(uint32_t) /* trackid */; + uint8_t flag[] = { 1, 2, 8, 0x10, 0x20, 0 }; + uint8_t flagSize[] = { sizeof(uint64_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) }; + for (size_t i = 0; flag[i]; i++) { + if (mFlags & flag[i]) { + need += flagSize[i]; + } + } + if (reader->Remaining() < need) { + LOG(Tfhd, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + mTrackId = reader->ReadU32(); + mBaseDataOffset = + mFlags & 1 ? reader->ReadU64() : aBox.Parent()->Parent()->Offset(); + if (mFlags & 2) { + mDefaultSampleDescriptionIndex = reader->ReadU32(); + } + if (mFlags & 8) { + mDefaultSampleDuration = reader->ReadU32(); + } + if (mFlags & 0x10) { + mDefaultSampleSize = reader->ReadU32(); + } + if (mFlags & 0x20) { + mDefaultSampleFlags = reader->ReadU32(); + } + mValid = true; +} + +Tfdt::Tfdt(Box& aBox) +{ + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Tfdt, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = version ? sizeof(uint64_t) : sizeof(uint32_t) ; + if (reader->Remaining() < need) { + LOG(Tfdt, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + if (version == 0) { + mBaseMediaDecodeTime = reader->ReadU32(); + } else if (version == 1) { + mBaseMediaDecodeTime = reader->ReadU64(); + } + mValid = true; +} + +Edts::Edts(Box& aBox) + : mMediaStart(0) + , mEmptyOffset(0) +{ + Box child = aBox.FirstChild(); + if (!child.IsType("elst")) { + return; + } + + BoxReader reader(child); + if (!reader->CanReadType<uint32_t>()) { + LOG(Edts, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = + sizeof(uint32_t) + 2*(version ? sizeof(int64_t) : sizeof(uint32_t)); + if (reader->Remaining() < need) { + LOG(Edts, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + bool emptyEntry = false; + uint32_t entryCount = reader->ReadU32(); + for (uint32_t i = 0; i < entryCount; i++) { + uint64_t segment_duration; + int64_t media_time; + if (version == 1) { + segment_duration = reader->ReadU64(); + media_time = reader->Read64(); + } else { + segment_duration = reader->ReadU32(); + media_time = reader->Read32(); + } + if (media_time == -1 && i) { + LOG(Edts, "Multiple empty edit, not handled"); + } else if (media_time == -1) { + mEmptyOffset = segment_duration; + emptyEntry = true; + } else if (i > 1 || (i > 0 && !emptyEntry)) { + LOG(Edts, "More than one edit entry, not handled. A/V sync will be wrong"); + break; + } else { + mMediaStart = media_time; + } + reader->ReadU32(); // media_rate_integer and media_rate_fraction + } +} + +Saiz::Saiz(Box& aBox, AtomType aDefaultType) + : mAuxInfoType(aDefaultType) + , mAuxInfoTypeParameter(0) +{ + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Saiz, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = + ((flags & 1) ? 2*sizeof(uint32_t) : 0) + sizeof(uint8_t) + sizeof(uint32_t); + if (reader->Remaining() < need) { + LOG(Saiz, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + if (flags & 1) { + mAuxInfoType = reader->ReadU32(); + mAuxInfoTypeParameter = reader->ReadU32(); + } + uint8_t defaultSampleInfoSize = reader->ReadU8(); + uint32_t count = reader->ReadU32(); + if (defaultSampleInfoSize) { + if (!mSampleInfoSize.SetLength(count, fallible)) { + LOG(Saiz, "OOM"); + return; + } + memset(mSampleInfoSize.Elements(), defaultSampleInfoSize, mSampleInfoSize.Length()); + } else { + if (!reader->ReadArray(mSampleInfoSize, count)) { + LOG(Saiz, "Incomplete Box (OOM or missing count:%u)", count); + return; + } + } + mValid = true; +} + +Saio::Saio(Box& aBox, AtomType aDefaultType) + : mAuxInfoType(aDefaultType) + , mAuxInfoTypeParameter(0) +{ + BoxReader reader(aBox); + if (!reader->CanReadType<uint32_t>()) { + LOG(Saio, "Incomplete Box (missing flags)"); + return; + } + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + size_t need = ((flags & 1) ? (2*sizeof(uint32_t)) : 0) + sizeof(uint32_t); + if (reader->Remaining() < need) { + LOG(Saio, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + if (flags & 1) { + mAuxInfoType = reader->ReadU32(); + mAuxInfoTypeParameter = reader->ReadU32(); + } + size_t count = reader->ReadU32(); + need = (version ? sizeof(uint64_t) : sizeof(uint32_t)) * count; + if (reader->Remaining() < need) { + LOG(Saio, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + if (!mOffsets.SetCapacity(count, fallible)) { + LOG(Saiz, "OOM"); + return; + } + if (version == 0) { + for (size_t i = 0; i < count; i++) { + MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU32(), fallible)); + } + } else { + for (size_t i = 0; i < count; i++) { + MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU64(), fallible)); + } + } + mValid = true; +} + +#undef LOG +} diff --git a/media/libstagefright/binding/ResourceStream.cpp b/media/libstagefright/binding/ResourceStream.cpp new file mode 100644 index 000000000..2fd042cf4 --- /dev/null +++ b/media/libstagefright/binding/ResourceStream.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mp4_demuxer/ResourceStream.h" + +namespace mp4_demuxer { + +ResourceStream::ResourceStream(mozilla::MediaResource* aResource) + : mResource(aResource) + , mPinCount(0) +{ + MOZ_ASSERT(aResource); +} + +ResourceStream::~ResourceStream() +{ + MOZ_ASSERT(mPinCount == 0); +} + +bool +ResourceStream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount, + size_t* aBytesRead) +{ + uint32_t sum = 0; + uint32_t bytesRead = 0; + do { + uint64_t offset = aOffset + sum; + char* buffer = reinterpret_cast<char*>(aBuffer) + sum; + uint32_t toRead = aCount - sum; + nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead); + if (NS_FAILED(rv)) { + return false; + } + sum += bytesRead; + } while (sum < aCount && bytesRead > 0); + + *aBytesRead = sum; + return true; +} + +bool +ResourceStream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount, + size_t* aBytesRead) +{ + nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer), + aOffset, aCount); + if (NS_FAILED(rv)) { + *aBytesRead = 0; + return false; + } + *aBytesRead = aCount; + return true; +} + +bool +ResourceStream::Length(int64_t* aSize) +{ + if (mResource->GetLength() < 0) + return false; + *aSize = mResource->GetLength(); + return true; +} + +} // namespace mp4_demuxer diff --git a/media/libstagefright/binding/SinfParser.cpp b/media/libstagefright/binding/SinfParser.cpp new file mode 100644 index 000000000..5cf3aa553 --- /dev/null +++ b/media/libstagefright/binding/SinfParser.cpp @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Unused.h" +#include "mp4_demuxer/SinfParser.h" +#include "mp4_demuxer/AtomType.h" +#include "mp4_demuxer/Box.h" + +namespace mp4_demuxer { + +Sinf::Sinf(Box& aBox) + : mDefaultIVSize(0) + , mDefaultEncryptionType() +{ + SinfParser parser(aBox); + if (parser.GetSinf().IsValid()) { + *this = parser.GetSinf(); + } +} + +SinfParser::SinfParser(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("schm")) { + ParseSchm(box); + } else if (box.IsType("schi")) { + ParseSchi(box); + } + } +} + +void +SinfParser::ParseSchm(Box& aBox) +{ + BoxReader reader(aBox); + + if (reader->Remaining() < 8) { + return; + } + + mozilla::Unused << reader->ReadU32(); // flags -- ignore + mSinf.mDefaultEncryptionType = reader->ReadU32(); +} + +void +SinfParser::ParseSchi(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tenc")) { + ParseTenc(box); + } + } +} + +void +SinfParser::ParseTenc(Box& aBox) +{ + BoxReader reader(aBox); + + if (reader->Remaining() < 24) { + return; + } + + mozilla::Unused << reader->ReadU32(); // flags -- ignore + + uint32_t isEncrypted = reader->ReadU24(); + mSinf.mDefaultIVSize = reader->ReadU8(); + memcpy(mSinf.mDefaultKeyID, reader->Read(16), 16); +} + +} diff --git a/media/libstagefright/binding/include/demuxer/TrackDemuxer.h b/media/libstagefright/binding/include/demuxer/TrackDemuxer.h new file mode 100644 index 000000000..c3f72648b --- /dev/null +++ b/media/libstagefright/binding/include/demuxer/TrackDemuxer.h @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TRACK_DEMUXER_H_ +#define TRACK_DEMUXER_H_ + +template <class T> struct already_AddRefed; + +namespace mozilla { + +class MediaRawData; +class MediaByteRange; + +class TrackDemuxer { +public: + typedef int64_t Microseconds; + + TrackDemuxer() {} + virtual ~TrackDemuxer() {} + + virtual void Seek(Microseconds aTime) = 0; + + // DemuxSample returns nullptr on end of stream or error. + virtual already_AddRefed<MediaRawData> DemuxSample() = 0; + + // Returns timestamp of next keyframe, or -1 if demuxer can't + // report this. + virtual Microseconds GetNextKeyframeTime() = 0; +}; + +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/Adts.h b/media/libstagefright/binding/include/mp4_demuxer/Adts.h new file mode 100644 index 000000000..8d03beb4b --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Adts.h @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ADTS_H_ +#define ADTS_H_ + +#include <stdint.h> + +namespace mozilla { +class MediaRawData; +} + +namespace mp4_demuxer +{ + +class Adts +{ +public: + static int8_t GetFrequencyIndex(uint32_t aSamplesPerSecond); + static bool ConvertSample(uint16_t aChannelCount, int8_t aFrequencyIndex, + int8_t aProfile, mozilla::MediaRawData* aSample); +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h new file mode 100644 index 000000000..879e40c84 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MP4_DEMUXER_ANNEX_B_H_ +#define MP4_DEMUXER_ANNEX_B_H_ + +template <class T> struct already_AddRefed; + +namespace mozilla { +class MediaRawData; +class MediaByteBuffer; +} +namespace mp4_demuxer +{ +class ByteReader; + +class AnnexB +{ +public: + // All conversions assume size of NAL length field is 4 bytes. + // Convert a sample from AVCC format to Annex B. + static bool ConvertSampleToAnnexB(mozilla::MediaRawData* aSample, bool aAddSPS = true); + // Convert a sample from Annex B to AVCC. + // an AVCC extradata must not be set. + static bool ConvertSampleToAVCC(mozilla::MediaRawData* aSample); + static bool ConvertSampleTo4BytesAVCC(mozilla::MediaRawData* aSample); + + // Parse an AVCC extradata and construct the Annex B sample header. + static already_AddRefed<mozilla::MediaByteBuffer> ConvertExtraDataToAnnexB( + const mozilla::MediaByteBuffer* aExtraData); + // Extract SPS and PPS NALs from aSample, aSample must be in AVCC format. + // If aSample already contains an extradata with an SPS, it will be returned + // otherwise the SPS/PPS NALs are searched in-band. + static already_AddRefed<mozilla::MediaByteBuffer> ExtractExtraData( + const mozilla::MediaRawData* aSample); + static bool HasSPS(const mozilla::MediaRawData* aSample); + static bool HasSPS(const mozilla::MediaByteBuffer* aExtraData); + // Returns true if format is AVCC and sample has valid extradata. + static bool IsAVCC(const mozilla::MediaRawData* aSample); + // Returns true if format is AnnexB. + static bool IsAnnexB(const mozilla::MediaRawData* aSample); + // Return true if both extradata are equal. + static bool CompareExtraData(const mozilla::MediaByteBuffer* aExtraData1, + const mozilla::MediaByteBuffer* aExtraData2); + +private: + // AVCC box parser helper. + static void ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount, + mozilla::MediaByteBuffer* aAnnexB); +}; + +} // namespace mp4_demuxer + +#endif // MP4_DEMUXER_ANNEX_B_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/Atom.h b/media/libstagefright/binding/include/mp4_demuxer/Atom.h new file mode 100644 index 000000000..48f2878d4 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Atom.h @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ATOM_H_ +#define ATOM_H_ + +namespace mp4_demuxer { + +class Atom +{ +public: + Atom() + : mValid(false) + { + } + virtual bool IsValid() + { + return mValid; + } +protected: + bool mValid; +}; + +} + +#endif // ATOM_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/AtomType.h b/media/libstagefright/binding/include/mp4_demuxer/AtomType.h new file mode 100644 index 000000000..95baedfe7 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/AtomType.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ATOM_TYPE_H_ +#define ATOM_TYPE_H_ + +#include <stdint.h> +#include "mozilla/EndianUtils.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +class AtomType +{ +public: + AtomType() : mType(0) { } + MOZ_IMPLICIT AtomType(uint32_t aType) : mType(aType) { } + MOZ_IMPLICIT AtomType(const char* aType) : mType(BigEndian::readUint32(aType)) { } + bool operator==(const AtomType& aType) const { return mType == aType.mType; } + bool operator!() const { return !mType; } + +private: + uint32_t mType; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/BitReader.h b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h new file mode 100644 index 000000000..27a10338f --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/BitReader.h @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BIT_READER_H_ +#define BIT_READER_H_ + +#include "nsAutoPtr.h" +#include "MediaData.h" + +namespace stagefright { class ABitReader; } + +namespace mp4_demuxer +{ + +class BitReader +{ +public: + explicit BitReader(const mozilla::MediaByteBuffer* aBuffer); + BitReader(const uint8_t* aBuffer, size_t aLength); + ~BitReader(); + uint32_t ReadBits(size_t aNum); + uint32_t ReadBit() { return ReadBits(1); } + uint32_t ReadU32() { return ReadBits(32); } + uint64_t ReadU64(); + + // Read the UTF-8 sequence and convert it to its 64-bit UCS-4 encoded form. + // Return 0xfffffffffffffff if sequence was invalid. + uint64_t ReadUTF8(); + // Read unsigned integer Exp-Golomb-coded. + uint32_t ReadUE(); + // Read signed integer Exp-Golomb-coded. + int32_t ReadSE(); + + // Return the number of bits parsed so far; + size_t BitCount() const; + +private: + nsAutoPtr<stagefright::ABitReader> mBitReader; + const size_t mSize; +}; + +} // namespace mp4_demuxer + +#endif // BIT_READER_H_
\ No newline at end of file diff --git a/media/libstagefright/binding/include/mp4_demuxer/Box.h b/media/libstagefright/binding/include/mp4_demuxer/Box.h new file mode 100644 index 000000000..f53404a1d --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Box.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BOX_H_ +#define BOX_H_ + +#include <stdint.h> +#include "nsTArray.h" +#include "MediaResource.h" +#include "mozilla/EndianUtils.h" +#include "mp4_demuxer/AtomType.h" +#include "mp4_demuxer/ByteReader.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +class Stream; + +class BoxContext +{ +public: + BoxContext(Stream* aSource, const MediaByteRangeSet& aByteRanges) + : mSource(aSource), mByteRanges(aByteRanges) + { + } + + RefPtr<Stream> mSource; + const MediaByteRangeSet& mByteRanges; +}; + +class Box +{ +public: + Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent = nullptr); + Box(); + + bool IsAvailable() const { return !mRange.IsEmpty(); } + uint64_t Offset() const { return mRange.mStart; } + uint64_t Length() const { return mRange.mEnd - mRange.mStart; } + uint64_t NextOffset() const { return mRange.mEnd; } + const MediaByteRange& Range() const { return mRange; } + const Box* Parent() const { return mParent; } + bool IsType(const char* aType) const { return mType == AtomType(aType); } + + Box Next() const; + Box FirstChild() const; + nsTArray<uint8_t> Read(); + bool Read(nsTArray<uint8_t>* aDest, const MediaByteRange& aRange); + + static const uint64_t kMAX_BOX_READ; + +private: + bool Contains(MediaByteRange aRange) const; + BoxContext* mContext; + mozilla::MediaByteRange mRange; + uint64_t mBodyOffset; + uint64_t mChildOffset; + AtomType mType; + const Box* mParent; +}; + +// BoxReader takes a copy of a box contents and serves through an AutoByteReader. +MOZ_RAII +class BoxReader +{ +public: + explicit BoxReader(Box& aBox) + : mBuffer(aBox.Read()) + , mReader(mBuffer.Elements(), mBuffer.Length()) + { + } + ByteReader* operator->() { return &mReader; } + +private: + nsTArray<uint8_t> mBuffer; + ByteReader mReader; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h b/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h new file mode 100644 index 000000000..bb703db5d --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/BufferStream.h @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BUFFER_STREAM_H_ +#define BUFFER_STREAM_H_ + +#include "mp4_demuxer/Stream.h" +#include "nsTArray.h" +#include "MediaResource.h" + +namespace mozilla { +class MediaByteBuffer; +} + +namespace mp4_demuxer { + +class BufferStream : public Stream +{ +public: + /* BufferStream does not take ownership of aData nor does it make a copy. + * Therefore BufferStream shouldn't get used after aData is destroyed. + */ + BufferStream(); + explicit BufferStream(mozilla::MediaByteBuffer* aBuffer); + + virtual bool ReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) override; + virtual bool CachedReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) override; + virtual bool Length(int64_t* aLength) override; + + virtual void DiscardBefore(int64_t aOffset) override; + + bool AppendBytes(const uint8_t* aData, size_t aLength); + + mozilla::MediaByteRange GetByteRange(); + +private: + ~BufferStream(); + int64_t mStartOffset; + RefPtr<mozilla::MediaByteBuffer> mData; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h new file mode 100644 index 000000000..9c7df04bd --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h @@ -0,0 +1,349 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BYTE_READER_H_ +#define BYTE_READER_H_ + +#include "mozilla/EndianUtils.h" +#include "mozilla/Vector.h" +#include "nsTArray.h" +#include "MediaData.h" + +namespace mp4_demuxer { + +class MOZ_RAII ByteReader +{ +public: + ByteReader() : mPtr(nullptr), mRemaining(0) {} + explicit ByteReader(const mozilla::Vector<uint8_t>& aData) + : mPtr(aData.begin()), mRemaining(aData.length()), mLength(aData.length()) + { + } + ByteReader(const uint8_t* aData, size_t aSize) + : mPtr(aData), mRemaining(aSize), mLength(aSize) + { + } + template<size_t S> + explicit ByteReader(const AutoTArray<uint8_t, S>& aData) + : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length()) + { + } + explicit ByteReader(const nsTArray<uint8_t>& aData) + : mPtr(aData.Elements()), mRemaining(aData.Length()), mLength(aData.Length()) + { + } + explicit ByteReader(const mozilla::MediaByteBuffer* aData) + : mPtr(aData->Elements()), mRemaining(aData->Length()), mLength(aData->Length()) + { + } + + void SetData(const nsTArray<uint8_t>& aData) + { + MOZ_ASSERT(!mPtr && !mRemaining); + mPtr = aData.Elements(); + mRemaining = aData.Length(); + mLength = mRemaining; + } + + ~ByteReader() + { + } + + size_t Offset() + { + return mLength - mRemaining; + } + + size_t Remaining() const { return mRemaining; } + + bool CanRead8() { return mRemaining >= 1; } + + uint8_t ReadU8() + { + auto ptr = Read(1); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return *ptr; + } + + bool CanRead16() { return mRemaining >= 2; } + + uint16_t ReadU16() + { + auto ptr = Read(2); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint16(ptr); + } + + int16_t ReadLE16() + { + auto ptr = Read(2); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::LittleEndian::readInt16(ptr); + } + + uint32_t ReadU24() + { + auto ptr = Read(3); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; + } + + uint32_t Read24() + { + return (uint32_t)ReadU24(); + } + + int32_t ReadLE24() + { + auto ptr = Read(3); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + int32_t result = int32_t(ptr[2] << 16 | ptr[1] << 8 | ptr[0]); + if (result & 0x00800000u) { + result -= 0x1000000; + } + return result; + } + + bool CanRead32() { return mRemaining >= 4; } + + uint32_t ReadU32() + { + auto ptr = Read(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint32(ptr); + } + + int32_t Read32() + { + auto ptr = Read(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readInt32(ptr); + } + + uint64_t ReadU64() + { + auto ptr = Read(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint64(ptr); + } + + int64_t Read64() + { + auto ptr = Read(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readInt64(ptr); + } + + const uint8_t* Read(size_t aCount) + { + if (aCount > mRemaining) { + mRemaining = 0; + return nullptr; + } + mRemaining -= aCount; + + const uint8_t* result = mPtr; + mPtr += aCount; + + return result; + } + + const uint8_t* Rewind(size_t aCount) + { + MOZ_ASSERT(aCount <= Offset()); + size_t rewind = Offset(); + if (aCount < rewind) { + rewind = aCount; + } + mRemaining += rewind; + mPtr -= rewind; + return mPtr; + } + + uint8_t PeekU8() + { + auto ptr = Peek(1); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return *ptr; + } + + uint16_t PeekU16() + { + auto ptr = Peek(2); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint16(ptr); + } + + uint32_t PeekU24() + { + auto ptr = Peek(3); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; + } + + uint32_t Peek24() + { + return (uint32_t)PeekU24(); + } + + uint32_t PeekU32() + { + auto ptr = Peek(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint32(ptr); + } + + int32_t Peek32() + { + auto ptr = Peek(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readInt32(ptr); + } + + uint64_t PeekU64() + { + auto ptr = Peek(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint64(ptr); + } + + int64_t Peek64() + { + auto ptr = Peek(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readInt64(ptr); + } + + const uint8_t* Peek(size_t aCount) + { + if (aCount > mRemaining) { + return nullptr; + } + return mPtr; + } + + const uint8_t* Seek(size_t aOffset) + { + if (aOffset >= mLength) { + MOZ_ASSERT(false); + return nullptr; + } + + mPtr = mPtr - Offset() + aOffset; + mRemaining = mLength - aOffset; + return mPtr; + } + + const uint8_t* Reset() + { + mPtr -= Offset(); + mRemaining = mLength; + return mPtr; + } + + uint32_t Align() + { + return 4 - ((intptr_t)mPtr & 3); + } + + template <typename T> bool CanReadType() { return mRemaining >= sizeof(T); } + + template <typename T> T ReadType() + { + auto ptr = Read(sizeof(T)); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return *reinterpret_cast<const T*>(ptr); + } + + template <typename T> + MOZ_MUST_USE bool ReadArray(nsTArray<T>& aDest, size_t aLength) + { + auto ptr = Read(aLength * sizeof(T)); + if (!ptr) { + return false; + } + + aDest.Clear(); + aDest.AppendElements(reinterpret_cast<const T*>(ptr), aLength); + return true; + } + + template <typename T> + MOZ_MUST_USE bool ReadArray(FallibleTArray<T>& aDest, size_t aLength) + { + auto ptr = Read(aLength * sizeof(T)); + if (!ptr) { + return false; + } + + aDest.Clear(); + if (!aDest.SetCapacity(aLength, mozilla::fallible)) { + return false; + } + MOZ_ALWAYS_TRUE(aDest.AppendElements(reinterpret_cast<const T*>(ptr), + aLength, + mozilla::fallible)); + return true; + } + +private: + const uint8_t* mPtr; + size_t mRemaining; + size_t mLength; +}; + +} // namespace mp4_demuxer + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h b/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h new file mode 100644 index 000000000..48ebdd460 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/ByteWriter.h @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BYTE_WRITER_H_ +#define BYTE_WRITER_H_ + +#include "mozilla/EndianUtils.h" +#include "mozilla/Vector.h" +#include "nsTArray.h" + +namespace mp4_demuxer { + +class ByteWriter +{ +public: + explicit ByteWriter(mozilla::Vector<uint8_t>& aData) + : mPtr(aData) + { + } + ~ByteWriter() + { + } + + MOZ_MUST_USE bool WriteU8(uint8_t aByte) + { + return Write(&aByte, 1); + } + + MOZ_MUST_USE bool WriteU16(uint16_t aShort) + { + uint8_t c[2]; + mozilla::BigEndian::writeUint16(&c[0], aShort); + return Write(&c[0], 2); + } + + MOZ_MUST_USE bool WriteU32(uint32_t aLong) + { + uint8_t c[4]; + mozilla::BigEndian::writeUint32(&c[0], aLong); + return Write(&c[0], 4); + } + + MOZ_MUST_USE bool Write32(int32_t aLong) + { + uint8_t c[4]; + mozilla::BigEndian::writeInt32(&c[0], aLong); + return Write(&c[0], 4); + } + + MOZ_MUST_USE bool WriteU64(uint64_t aLongLong) + { + uint8_t c[8]; + mozilla::BigEndian::writeUint64(&c[0], aLongLong); + return Write(&c[0], 8); + } + + MOZ_MUST_USE bool Write64(int64_t aLongLong) + { + uint8_t c[8]; + mozilla::BigEndian::writeInt64(&c[0], aLongLong); + return Write(&c[0], 8); + } + + MOZ_MUST_USE bool Write(const uint8_t* aSrc, size_t aCount) + { + return mPtr.append(aSrc, aCount); + } + +private: + mozilla::Vector<uint8_t>& mPtr; +}; + +} // namespace mp4_demuxer + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h new file mode 100644 index 000000000..87262c26a --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DECODER_DATA_H_ +#define DECODER_DATA_H_ + +#include "MediaData.h" +#include "MediaInfo.h" +#include "mozilla/Types.h" +#include "mozilla/Vector.h" +#include "mozilla/RefPtr.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsString.h" + +namespace stagefright +{ +class MetaData; +} + +#ifdef MOZ_RUST_MP4PARSE +extern "C" { +typedef struct mp4parse_track_info mp4parse_track_info; +typedef struct mp4parse_track_audio_info mp4parse_track_audio_info; +typedef struct mp4parse_track_video_info mp4parse_track_video_info; +} +#endif + +namespace mp4_demuxer +{ + +class MP4Demuxer; + +struct PsshInfo +{ + PsshInfo() {} + PsshInfo(const PsshInfo& aOther) : uuid(aOther.uuid), data(aOther.data) {} + nsTArray<uint8_t> uuid; + nsTArray<uint8_t> data; +}; + +class CryptoFile +{ +public: + CryptoFile() : valid(false) {} + CryptoFile(const CryptoFile& aCryptoFile) : valid(aCryptoFile.valid) + { + pssh.AppendElements(aCryptoFile.pssh); + } + + void Update(const uint8_t* aData, size_t aLength) + { + valid = DoUpdate(aData, aLength); + } + + bool valid; + nsTArray<PsshInfo> pssh; + +private: + bool DoUpdate(const uint8_t* aData, size_t aLength); +}; + +class MP4AudioInfo : public mozilla::AudioInfo +{ +public: + MP4AudioInfo() = default; + + void Update(const stagefright::MetaData* aMetaData, + const char* aMimeType); + + virtual bool IsValid() const override; +}; + +class MP4VideoInfo : public mozilla::VideoInfo +{ +public: + MP4VideoInfo() = default; + + void Update(const stagefright::MetaData* aMetaData, + const char* aMimeType); + +#ifdef MOZ_RUST_MP4PARSE + void Update(const mp4parse_track_info* track, + const mp4parse_track_video_info* video); +#endif + + virtual bool IsValid() const override; +}; + +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/H264.h b/media/libstagefright/binding/include/mp4_demuxer/H264.h new file mode 100644 index 000000000..2aa710f61 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h @@ -0,0 +1,372 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MP4_DEMUXER_H264_H_ +#define MP4_DEMUXER_H264_H_ + +#include "mp4_demuxer/DecoderData.h" + +namespace mp4_demuxer +{ + +class BitReader; + +struct SPSData +{ + /* Decoded Members */ + /* + pic_width is the decoded width according to: + pic_width = ((pic_width_in_mbs_minus1 + 1) * 16) + - (frame_crop_left_offset + frame_crop_right_offset) * 2 + */ + uint32_t pic_width; + /* + pic_height is the decoded height according to: + pic_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16) + - (frame_crop_top_offset + frame_crop_bottom_offset) * 2 + */ + uint32_t pic_height; + + bool interlaced; + + /* + Displayed size. + display_width and display_height are adjusted according to the display + sample aspect ratio. + */ + uint32_t display_width; + uint32_t display_height; + + float sample_ratio; + + uint32_t crop_left; + uint32_t crop_right; + uint32_t crop_top; + uint32_t crop_bottom; + + /* + H264 decoding parameters according to ITU-T H.264 (T-REC-H.264-201402-I/en) + http://www.itu.int/rec/T-REC-H.264-201402-I/en + */ + + bool constraint_set0_flag; + bool constraint_set1_flag; + bool constraint_set2_flag; + bool constraint_set3_flag; + bool constraint_set4_flag; + bool constraint_set5_flag; + + /* + profile_idc and level_idc indicate the profile and level to which the coded + video sequence conforms when the SVC sequence parameter set is the active + SVC sequence parameter set. + */ + uint8_t profile_idc; + uint8_t level_idc; + + /* + seq_parameter_set_id identifies the sequence parameter set that is referred + to by the picture parameter set. The value of seq_parameter_set_id shall be + in the range of 0 to 31, inclusive. + */ + uint8_t seq_parameter_set_id; + + /* + chroma_format_idc specifies the chroma sampling relative to the luma + sampling as specified in clause 6.2. The value of chroma_format_idc shall be + in the range of 0 to 3, inclusive. When chroma_format_idc is not present, + it shall be inferred to be equal to 1 (4:2:0 chroma format). + When profile_idc is equal to 183, chroma_format_idc shall be equal to 0 + (4:0:0 chroma format). + */ + uint8_t chroma_format_idc; + + /* + separate_colour_plane_flag equal to 1 specifies that the three colour + components of the 4:4:4 chroma format are coded separately. + separate_colour_plane_flag equal to 0 specifies that the colour components + are not coded separately. When separate_colour_plane_flag is not present, + it shall be inferred to be equal to 0. When separate_colour_plane_flag is + equal to 1, the primary coded picture consists of three separate components, + each of which consists of coded samples of one colour plane (Y, Cb or Cr) + that each use the monochrome coding syntax. In this case, each colour plane + is associated with a specific colour_plane_id value. + */ + bool separate_colour_plane_flag; + + /* + log2_max_frame_num_minus4 specifies the value of the variable + MaxFrameNum that is used in frame_num related derivations as + follows: + + MaxFrameNum = 2( log2_max_frame_num_minus4 + 4 ). The value of + log2_max_frame_num_minus4 shall be in the range of 0 to 12, inclusive. + */ + uint8_t log2_max_frame_num; + + /* + pic_order_cnt_type specifies the method to decode picture order + count (as specified in subclause 8.2.1). The value of + pic_order_cnt_type shall be in the range of 0 to 2, inclusive. + */ + uint8_t pic_order_cnt_type; + + /* + log2_max_pic_order_cnt_lsb_minus4 specifies the value of the + variable MaxPicOrderCntLsb that is used in the decoding + process for picture order count as specified in subclause + 8.2.1 as follows: + + MaxPicOrderCntLsb = 2( log2_max_pic_order_cnt_lsb_minus4 + 4 ) + + The value of log2_max_pic_order_cnt_lsb_minus4 shall be in + the range of 0 to 12, inclusive. + */ + uint8_t log2_max_pic_order_cnt_lsb; + + /* + delta_pic_order_always_zero_flag equal to 1 specifies that + delta_pic_order_cnt[ 0 ] and delta_pic_order_cnt[ 1 ] are + not present in the slice headers of the sequence and shall + be inferred to be equal to 0. + */ + bool delta_pic_order_always_zero_flag; + + /* + offset_for_non_ref_pic is used to calculate the picture + order count of a non-reference picture as specified in + 8.2.1. The value of offset_for_non_ref_pic shall be in the + range of -231 to 231 - 1, inclusive. + */ + int8_t offset_for_non_ref_pic; + + /* + offset_for_top_to_bottom_field is used to calculate the + picture order count of a bottom field as specified in + subclause 8.2.1. The value of offset_for_top_to_bottom_field + shall be in the range of -231 to 231 - 1, inclusive. + */ + int8_t offset_for_top_to_bottom_field; + + /* + max_num_ref_frames specifies the maximum number of short-term and + long-term reference frames, complementary reference field pairs, + and non-paired reference fields that may be used by the decoding + process for inter prediction of any picture in the + sequence. max_num_ref_frames also determines the size of the sliding + window operation as specified in subclause 8.2.5.3. The value of + max_num_ref_frames shall be in the range of 0 to MaxDpbSize (as + specified in subclause A.3.1 or A.3.2), inclusive. + */ + uint32_t max_num_ref_frames; + + /* + gaps_in_frame_num_value_allowed_flag specifies the allowed + values of frame_num as specified in subclause 7.4.3 and the + decoding process in case of an inferred gap between values of + frame_num as specified in subclause 8.2.5.2. + */ + bool gaps_in_frame_num_allowed_flag; + + /* + pic_width_in_mbs_minus1 plus 1 specifies the width of each + decoded picture in units of macroblocks. 16 macroblocks in a row + */ + uint32_t pic_width_in_mbs; + + /* + pic_height_in_map_units_minus1 plus 1 specifies the height in + slice group map units of a decoded frame or field. 16 + macroblocks in each column. + */ + uint32_t pic_height_in_map_units; + + /* + frame_mbs_only_flag equal to 0 specifies that coded pictures of + the coded video sequence may either be coded fields or coded + frames. frame_mbs_only_flag equal to 1 specifies that every + coded picture of the coded video sequence is a coded frame + containing only frame macroblocks. + */ + bool frame_mbs_only_flag; + + /* + mb_adaptive_frame_field_flag equal to 0 specifies no + switching between frame and field macroblocks within a + picture. mb_adaptive_frame_field_flag equal to 1 specifies + the possible use of switching between frame and field + macroblocks within frames. When mb_adaptive_frame_field_flag + is not present, it shall be inferred to be equal to 0. + */ + bool mb_adaptive_frame_field_flag; + + /* + frame_cropping_flag equal to 1 specifies that the frame cropping + offset parameters follow next in the sequence parameter + set. frame_cropping_flag equal to 0 specifies that the frame + cropping offset parameters are not present. + */ + bool frame_cropping_flag; + uint32_t frame_crop_left_offset;; + uint32_t frame_crop_right_offset; + uint32_t frame_crop_top_offset; + uint32_t frame_crop_bottom_offset; + + // VUI Parameters + + /* + vui_parameters_present_flag equal to 1 specifies that the + vui_parameters( ) syntax structure as specified in Annex E is + present. vui_parameters_present_flag equal to 0 specifies that + the vui_parameters( ) syntax structure as specified in Annex E + is not present. + */ + bool vui_parameters_present_flag; + + /* + aspect_ratio_info_present_flag equal to 1 specifies that + aspect_ratio_idc is present. aspect_ratio_info_present_flag + equal to 0 specifies that aspect_ratio_idc is not present. + */ + bool aspect_ratio_info_present_flag; + + /* + aspect_ratio_idc specifies the value of the sample aspect + ratio of the luma samples. Table E-1 shows the meaning of + the code. When aspect_ratio_idc indicates Extended_SAR, the + sample aspect ratio is represented by sar_width and + sar_height. When the aspect_ratio_idc syntax element is not + present, aspect_ratio_idc value shall be inferred to be + equal to 0. + */ + uint8_t aspect_ratio_idc; + uint32_t sar_width; + uint32_t sar_height; + + /* + video_signal_type_present_flag equal to 1 specifies that video_format, + video_full_range_flag and colour_description_present_flag are present. + video_signal_type_present_flag equal to 0, specify that video_format, + video_full_range_flag and colour_description_present_flag are not present. + */ + bool video_signal_type_present_flag; + + /* + overscan_info_present_flag equal to1 specifies that the + overscan_appropriate_flag is present. When overscan_info_present_flag is + equal to 0 or is not present, the preferred display method for the video + signal is unspecified (Unspecified). + */ + bool overscan_info_present_flag; + /* + overscan_appropriate_flag equal to 1 indicates that the cropped decoded + pictures output are suitable for display using overscan. + overscan_appropriate_flag equal to 0 indicates that the cropped decoded + pictures output contain visually important information in the entire region + out to the edges of the cropping rectangle of the picture + */ + bool overscan_appropriate_flag; + + /* + video_format indicates the representation of the pictures as specified in + Table E-2, before being coded in accordance with this + Recommendation | International Standard. When the video_format syntax element + is not present, video_format value shall be inferred to be equal to 5. + (Unspecified video format) + */ + uint8_t video_format; + + /* + video_full_range_flag indicates the black level and range of the luma and + chroma signals as derived from E′Y, E′PB, and E′PR or E′R, E′G, and E′B + real-valued component signals. + When the video_full_range_flag syntax element is not present, the value of + video_full_range_flag shall be inferred to be equal to 0. + */ + bool video_full_range_flag; + + /* + colour_description_present_flag equal to1 specifies that colour_primaries, + transfer_characteristics and matrix_coefficients are present. + colour_description_present_flag equal to 0 specifies that colour_primaries, + transfer_characteristics and matrix_coefficients are not present. + */ + bool colour_description_present_flag; + + /* + colour_primaries indicates the chromaticity coordinates of the source + primaries as specified in Table E-3 in terms of the CIE 1931 definition of + x and y as specified by ISO 11664-1. + When the colour_primaries syntax element is not present, the value of + colour_primaries shall be inferred to be equal to 2 (the chromaticity is + unspecified or is determined by the application). + */ + uint8_t colour_primaries; + + /* + transfer_characteristics indicates the opto-electronic transfer + characteristic of the source picture as specified in Table E-4 as a function + of a linear optical intensity input Lc with a nominal real-valued range of 0 + to 1. + When the transfer_characteristics syntax element is not present, the value + of transfer_characteristics shall be inferred to be equal to 2 + (the transfer characteristics are unspecified or are determined by the + application). + */ + uint8_t transfer_characteristics; + + uint8_t matrix_coefficients; + bool chroma_loc_info_present_flag; + uint32_t chroma_sample_loc_type_top_field; + uint32_t chroma_sample_loc_type_bottom_field; + bool timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + bool fixed_frame_rate_flag; + + SPSData(); +}; + +class H264 +{ +public: + static bool DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData& aDest); + + /* Extract RAW BYTE SEQUENCE PAYLOAD from NAL content. + Returns nullptr if invalid content. + This is compliant to ITU H.264 7.3.1 Syntax in tabular form NAL unit syntax + */ + static already_AddRefed<mozilla::MediaByteBuffer> DecodeNALUnit(const mozilla::MediaByteBuffer* aNAL); + + /* Decode SPS NAL RBSP and fill SPSData structure */ + static bool DecodeSPS(const mozilla::MediaByteBuffer* aSPS, SPSData& aDest); + + // Ensure that SPS data makes sense, Return true if SPS data was, and false + // otherwise. If false, then content will be adjusted accordingly. + static bool EnsureSPSIsSane(SPSData& aSPS); + + // If the given aExtraData is valid, return the aExtraData.max_num_ref_frames + // clamped to be in the range of [4, 16]; otherwise return 4. + static uint32_t ComputeMaxRefFrames(const mozilla::MediaByteBuffer* aExtraData); + + enum class FrameType + { + I_FRAME, + OTHER, + INVALID, + }; + + // Returns the frame type. Returns I_FRAME if the sample is an IDR + // (Instantaneous Decoding Refresh) Picture. + static FrameType GetFrameType(const mozilla::MediaRawData* aSample); + +private: + static void vui_parameters(BitReader& aBr, SPSData& aDest); + // Read HRD parameters, all data is ignored. + static void hrd_parameters(BitReader& aBr); +}; + +} // namespace mp4_demuxer + +#endif // MP4_DEMUXER_H264_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h new file mode 100644 index 000000000..d566c9459 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h @@ -0,0 +1,128 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef INDEX_H_ +#define INDEX_H_ + +#include "MediaData.h" +#include "MediaResource.h" +#include "TimeUnits.h" +#include "mp4_demuxer/MoofParser.h" +#include "mp4_demuxer/Interval.h" +#include "mp4_demuxer/Stream.h" +#include "nsISupportsImpl.h" + +template<class T> class nsAutoPtr; + +namespace mp4_demuxer +{ + +class Index; + +typedef int64_t Microseconds; + +class SampleIterator +{ +public: + explicit SampleIterator(Index* aIndex); + ~SampleIterator(); + already_AddRefed<mozilla::MediaRawData> GetNext(); + void Seek(Microseconds aTime); + Microseconds GetNextKeyframeTime(); +private: + Sample* Get(); + void Next(); + RefPtr<Index> mIndex; + friend class Index; + size_t mCurrentMoof; + size_t mCurrentSample; +}; + +class Index +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Index) + + struct Indice + { + uint64_t start_offset; + uint64_t end_offset; + uint64_t start_composition; + uint64_t end_composition; + uint64_t start_decode; + bool sync; + }; + + struct MP4DataOffset + { + MP4DataOffset(uint32_t aIndex, int64_t aStartOffset) + : mIndex(aIndex) + , mStartOffset(aStartOffset) + , mEndOffset(0) + {} + + bool operator==(int64_t aStartOffset) const { + return mStartOffset == aStartOffset; + } + + bool operator!=(int64_t aStartOffset) const { + return mStartOffset != aStartOffset; + } + + bool operator<(int64_t aStartOffset) const { + return mStartOffset < aStartOffset; + } + + struct EndOffsetComparator { + bool Equals(const MP4DataOffset& a, const int64_t& b) const { + return a.mEndOffset == b; + } + + bool LessThan(const MP4DataOffset& a, const int64_t& b) const { + return a.mEndOffset < b; + } + }; + + uint32_t mIndex; + int64_t mStartOffset; + int64_t mEndOffset; + Interval<Microseconds> mTime; + }; + + Index(const nsTArray<Indice>& aIndex, + Stream* aSource, + uint32_t aTrackId, + bool aIsAudio); + + void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges, + bool aCanEvict); + void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges); + Microseconds GetEndCompositionIfBuffered( + const mozilla::MediaByteRangeSet& aByteRanges); + mozilla::media::TimeIntervals ConvertByteRangesToTimeRanges( + const mozilla::MediaByteRangeSet& aByteRanges); + uint64_t GetEvictionOffset(Microseconds aTime); + bool IsFragmented() { return mMoofParser; } + + friend class SampleIterator; + +private: + ~Index(); + void RegisterIterator(SampleIterator* aIterator); + void UnregisterIterator(SampleIterator* aIterator); + + Stream* mSource; + FallibleTArray<Sample> mIndex; + FallibleTArray<MP4DataOffset> mDataOffset; + nsAutoPtr<MoofParser> mMoofParser; + nsTArray<SampleIterator*> mIterators; + + // ConvertByteRangesToTimeRanges cache + mozilla::MediaByteRangeSet mLastCachedRanges; + mozilla::media::TimeIntervals mLastBufferedRanges; + bool mIsAudio; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/Interval.h b/media/libstagefright/binding/include/mp4_demuxer/Interval.h new file mode 100644 index 000000000..2c3d0f8af --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Interval.h @@ -0,0 +1,147 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef INTERVAL_H_ +#define INTERVAL_H_ + +#include "nsTArray.h" +#include <algorithm> + +namespace mp4_demuxer +{ + +template <typename T> +struct Interval +{ + Interval() : start(0), end(0) {} + Interval(T aStart, T aEnd) : start(aStart), end(aEnd) + { + MOZ_ASSERT(aStart <= aEnd); + } + T Length() { return end - start; } + Interval Intersection(const Interval& aOther) const + { + T s = start > aOther.start ? start : aOther.start; + T e = end < aOther.end ? end : aOther.end; + if (s > e) { + return Interval(); + } + return Interval(s, e); + } + bool Contains(const Interval& aOther) const + { + return aOther.start >= start && aOther.end <= end; + } + bool operator==(const Interval& aOther) const + { + return start == aOther.start && end == aOther.end; + } + bool operator!=(const Interval& aOther) const { return !(*this == aOther); } + bool IsNull() const + { + return end == start; + } + Interval Extents(const Interval& aOther) const + { + if (IsNull()) { + return aOther; + } + return Interval(std::min(start, aOther.start), + std::max(end, aOther.end)); + } + + T start; + T end; + + static void SemiNormalAppend(nsTArray<Interval<T>>& aIntervals, + Interval<T> aInterval) + { + if (!aIntervals.IsEmpty() && + aIntervals.LastElement().end == aInterval.start) { + aIntervals.LastElement().end = aInterval.end; + } else { + aIntervals.AppendElement(aInterval); + } + } + + static void Normalize(const nsTArray<Interval<T>>& aIntervals, + nsTArray<Interval<T>>* aNormalized) + { + if (!aNormalized || !aIntervals.Length()) { + MOZ_ASSERT(aNormalized); + return; + } + MOZ_ASSERT(aNormalized->IsEmpty()); + + nsTArray<Interval<T>> sorted; + sorted = aIntervals; + sorted.Sort(Compare()); + + Interval<T> current = sorted[0]; + for (size_t i = 1; i < sorted.Length(); i++) { + MOZ_ASSERT(sorted[i].start <= sorted[i].end); + if (current.Contains(sorted[i])) { + continue; + } + if (current.end >= sorted[i].start) { + current.end = sorted[i].end; + } else { + aNormalized->AppendElement(current); + current = sorted[i]; + } + } + aNormalized->AppendElement(current); + } + + static void Intersection(const nsTArray<Interval<T>>& a0, + const nsTArray<Interval<T>>& a1, + nsTArray<Interval<T>>* aIntersection) + { + MOZ_ASSERT(IsNormalized(a0)); + MOZ_ASSERT(IsNormalized(a1)); + size_t i0 = 0; + size_t i1 = 0; + while (i0 < a0.Length() && i1 < a1.Length()) { + Interval i = a0[i0].Intersection(a1[i1]); + if (i.Length()) { + aIntersection->AppendElement(i); + } + if (a0[i0].end < a1[i1].end) { + i0++; + // Assert that the array is sorted + MOZ_ASSERT(i0 == a0.Length() || a0[i0 - 1].start < a0[i0].start); + } else { + i1++; + // Assert that the array is sorted + MOZ_ASSERT(i1 == a1.Length() || a1[i1 - 1].start < a1[i1].start); + } + } + } + + static bool IsNormalized(const nsTArray<Interval<T>>& aIntervals) + { + for (size_t i = 1; i < aIntervals.Length(); i++) { + if (aIntervals[i - 1].end >= aIntervals[i].start) { + return false; + } + } + return true; + } + + struct Compare + { + bool Equals(const Interval<T>& a0, const Interval<T>& a1) const + { + return a0.start == a1.start && a0.end == a1.end; + } + + bool LessThan(const Interval<T>& a0, const Interval<T>& a1) const + { + return a0.start < a1.start; + } + }; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h new file mode 100644 index 000000000..83eca69d3 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/MP4Metadata.h @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MP4METADATA_H_ +#define MP4METADATA_H_ + +#include "mozilla/UniquePtr.h" +#include "mp4_demuxer/DecoderData.h" +#include "mp4_demuxer/Index.h" +#include "MediaData.h" +#include "MediaInfo.h" +#include "Stream.h" + +namespace mp4_demuxer +{ + +class MP4MetadataStagefright; +class MP4MetadataRust; + +class MP4Metadata +{ +public: + explicit MP4Metadata(Stream* aSource); + ~MP4Metadata(); + + static bool HasCompleteMetadata(Stream* aSource); + static already_AddRefed<mozilla::MediaByteBuffer> Metadata(Stream* aSource); + uint32_t GetNumberTracks(mozilla::TrackInfo::TrackType aType) const; + mozilla::UniquePtr<mozilla::TrackInfo> GetTrackInfo(mozilla::TrackInfo::TrackType aType, + size_t aTrackNumber) const; + bool CanSeek() const; + + const CryptoFile& Crypto() const; + + bool ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID); + +private: + UniquePtr<MP4MetadataStagefright> mStagefright; +#ifdef MOZ_RUST_MP4PARSE + UniquePtr<MP4MetadataRust> mRust; + mutable bool mPreferRust; + mutable bool mReportedAudioTrackTelemetry; + mutable bool mReportedVideoTrackTelemetry; +#ifndef RELEASE_OR_BETA + mutable bool mRustTestMode; +#endif + bool ShouldPreferRust() const; +#endif +}; + +} // namespace mp4_demuxer + +#endif // MP4METADATA_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h new file mode 100644 index 000000000..814f806fc --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -0,0 +1,260 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MOOF_PARSER_H_ +#define MOOF_PARSER_H_ + +#include "mp4_demuxer/Atom.h" +#include "mp4_demuxer/AtomType.h" +#include "mp4_demuxer/SinfParser.h" +#include "mp4_demuxer/Stream.h" +#include "mp4_demuxer/Interval.h" +#include "MediaResource.h" + +namespace mp4_demuxer { +typedef int64_t Microseconds; + +class Box; +class BoxContext; +class Moof; + +class Mvhd : public Atom +{ +public: + Mvhd() + : mCreationTime(0) + , mModificationTime(0) + , mTimescale(0) + , mDuration(0) + { + } + explicit Mvhd(Box& aBox); + + Microseconds ToMicroseconds(int64_t aTimescaleUnits) + { + int64_t major = aTimescaleUnits / mTimescale; + int64_t remainder = aTimescaleUnits % mTimescale; + return major * 1000000ll + remainder * 1000000ll / mTimescale; + } + + uint64_t mCreationTime; + uint64_t mModificationTime; + uint32_t mTimescale; + uint64_t mDuration; +}; + +class Tkhd : public Mvhd +{ +public: + Tkhd() + : mTrackId(0) + { + } + explicit Tkhd(Box& aBox); + + uint32_t mTrackId; +}; + +class Mdhd : public Mvhd +{ +public: + Mdhd() = default; + explicit Mdhd(Box& aBox); +}; + +class Trex : public Atom +{ +public: + explicit Trex(uint32_t aTrackId) + : mFlags(0) + , mTrackId(aTrackId) + , mDefaultSampleDescriptionIndex(0) + , mDefaultSampleDuration(0) + , mDefaultSampleSize(0) + , mDefaultSampleFlags(0) + { + } + + explicit Trex(Box& aBox); + + uint32_t mFlags; + uint32_t mTrackId; + uint32_t mDefaultSampleDescriptionIndex; + uint32_t mDefaultSampleDuration; + uint32_t mDefaultSampleSize; + uint32_t mDefaultSampleFlags; +}; + +class Tfhd : public Trex +{ +public: + explicit Tfhd(Trex& aTrex) + : Trex(aTrex) + , mBaseDataOffset(0) + { + mValid = aTrex.IsValid(); + } + Tfhd(Box& aBox, Trex& aTrex); + + uint64_t mBaseDataOffset; +}; + +class Tfdt : public Atom +{ +public: + Tfdt() + : mBaseMediaDecodeTime(0) + { + } + explicit Tfdt(Box& aBox); + + uint64_t mBaseMediaDecodeTime; +}; + +class Edts : public Atom +{ +public: + Edts() + : mMediaStart(0) + , mEmptyOffset(0) + { + } + explicit Edts(Box& aBox); + virtual bool IsValid() + { + // edts is optional + return true; + } + + int64_t mMediaStart; + int64_t mEmptyOffset; +}; + +struct Sample +{ + mozilla::MediaByteRange mByteRange; + mozilla::MediaByteRange mCencRange; + Microseconds mDecodeTime; + Interval<Microseconds> mCompositionRange; + bool mSync; +}; + +class Saiz final : public Atom +{ +public: + Saiz(Box& aBox, AtomType aDefaultType); + + AtomType mAuxInfoType; + uint32_t mAuxInfoTypeParameter; + FallibleTArray<uint8_t> mSampleInfoSize; +}; + +class Saio final : public Atom +{ +public: + Saio(Box& aBox, AtomType aDefaultType); + + AtomType mAuxInfoType; + uint32_t mAuxInfoTypeParameter; + FallibleTArray<uint64_t> mOffsets; +}; + +class AuxInfo { +public: + AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio); + +private: + int64_t mMoofOffset; + Saiz& mSaiz; + Saio& mSaio; +}; + +class Moof final : public Atom +{ +public: + Moof(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecoderTime, bool aIsAudio); + bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges); + void FixRounding(const Moof& aMoof); + + mozilla::MediaByteRange mRange; + mozilla::MediaByteRange mMdatRange; + Interval<Microseconds> mTimeRange; + FallibleTArray<Sample> mIndex; + + nsTArray<Saiz> mSaizs; + nsTArray<Saio> mSaios; + +private: + // aDecodeTime is updated to the end of the parsed TRAF on return. + void ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio); + // aDecodeTime is updated to the end of the parsed TRUN on return. + bool ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio); + void ParseSaiz(Box& aBox); + void ParseSaio(Box& aBox); + bool ProcessCenc(); + uint64_t mMaxRoundingError; +}; + +class MoofParser +{ +public: + MoofParser(Stream* aSource, uint32_t aTrackId, bool aIsAudio) + : mSource(aSource) + , mOffset(0) + , mTrex(aTrackId) + , mIsAudio(aIsAudio) + , mLastDecodeTime(0) + { + // Setting the mTrex.mTrackId to 0 is a nasty work around for calculating + // the composition range for MSE. We need an array of tracks. + } + bool RebuildFragmentedIndex( + const mozilla::MediaByteRangeSet& aByteRanges); + // If *aCanEvict is set to true. then will remove all moofs already parsed + // from index then rebuild the index. *aCanEvict is set to true upon return if + // some moofs were removed. + bool RebuildFragmentedIndex( + const mozilla::MediaByteRangeSet& aByteRanges, bool* aCanEvict); + bool RebuildFragmentedIndex(BoxContext& aContext); + Interval<Microseconds> GetCompositionRange( + const mozilla::MediaByteRangeSet& aByteRanges); + bool ReachedEnd(); + void ParseMoov(Box& aBox); + void ParseTrak(Box& aBox); + void ParseMdia(Box& aBox, Tkhd& aTkhd); + void ParseMvex(Box& aBox); + + void ParseMinf(Box& aBox); + void ParseStbl(Box& aBox); + void ParseStsd(Box& aBox); + void ParseEncrypted(Box& aBox); + void ParseSinf(Box& aBox); + + bool BlockingReadNextMoof(); + bool HasMetadata(); + already_AddRefed<mozilla::MediaByteBuffer> Metadata(); + MediaByteRange FirstCompleteMediaSegment(); + MediaByteRange FirstCompleteMediaHeader(); + + mozilla::MediaByteRange mInitRange; + RefPtr<Stream> mSource; + uint64_t mOffset; + Mvhd mMvhd; + Mdhd mMdhd; + Trex mTrex; + Tfdt mTfdt; + Edts mEdts; + Sinf mSinf; + nsTArray<Moof>& Moofs() { return mMoofs; } +private: + void ScanForMetadata(mozilla::MediaByteRange& aFtyp, + mozilla::MediaByteRange& aMoov); + nsTArray<Moof> mMoofs; + nsTArray<MediaByteRange> mMediaRanges; + bool mIsAudio; + uint64_t mLastDecodeTime; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h b/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h new file mode 100644 index 000000000..219465f17 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/ResourceStream.h @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef RESOURCESTREAM_H_ +#define RESOURCESTREAM_H_ + +#include "MediaResource.h" +#include "mp4_demuxer/Stream.h" +#include "mozilla/RefPtr.h" + +namespace mp4_demuxer +{ + +class ResourceStream : public Stream +{ +public: + explicit ResourceStream(mozilla::MediaResource* aResource); + + virtual bool ReadAt(int64_t offset, void* aBuffer, size_t aCount, + size_t* aBytesRead) override; + virtual bool CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount, + size_t* aBytesRead) override; + virtual bool Length(int64_t* size) override; + + void Pin() + { + mResource->Pin(); + ++mPinCount; + } + + void Unpin() + { + mResource->Unpin(); + MOZ_ASSERT(mPinCount); + --mPinCount; + } + +protected: + virtual ~ResourceStream(); + +private: + RefPtr<mozilla::MediaResource> mResource; + uint32_t mPinCount; +}; + +} + +#endif // RESOURCESTREAM_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h b/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h new file mode 100644 index 000000000..782a87907 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/SinfParser.h @@ -0,0 +1,51 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#ifndef SINF_PARSER_H_ +#define SINF_PARSER_H_ + +#include "mp4_demuxer/Atom.h" +#include "mp4_demuxer/AtomType.h" + +namespace mp4_demuxer { + +class Box; + +class Sinf : public Atom +{ +public: + Sinf() + : mDefaultIVSize(0) + , mDefaultEncryptionType() + {} + explicit Sinf(Box& aBox); + + virtual bool IsValid() override + { + return !!mDefaultIVSize && !!mDefaultEncryptionType; + } + + uint8_t mDefaultIVSize; + AtomType mDefaultEncryptionType; + uint8_t mDefaultKeyID[16]; +}; + +class SinfParser +{ +public: + explicit SinfParser(Box& aBox); + + Sinf& GetSinf() { return mSinf; } +private: + void ParseSchm(Box& aBox); + void ParseSchi(Box& aBox); + void ParseTenc(Box& aBox); + + Sinf mSinf; +}; + +} + +#endif // SINF_PARSER_H_ diff --git a/media/libstagefright/binding/include/mp4_demuxer/Stream.h b/media/libstagefright/binding/include/mp4_demuxer/Stream.h new file mode 100644 index 000000000..379478ec7 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Stream.h @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef STREAM_H_ +#define STREAM_H_ + +#include "nsISupportsImpl.h" + +namespace mp4_demuxer +{ + +class Stream +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Stream); + + virtual bool ReadAt(int64_t offset, void* data, size_t size, + size_t* bytes_read) = 0; + virtual bool CachedReadAt(int64_t offset, void* data, size_t size, + size_t* bytes_read) = 0; + virtual bool Length(int64_t* size) = 0; + + virtual void DiscardBefore(int64_t offset) {} + +protected: + virtual ~Stream() {} +}; + +} + +#endif diff --git a/media/libstagefright/binding/include/mp4parse.h b/media/libstagefright/binding/include/mp4parse.h new file mode 100644 index 000000000..6650661cb --- /dev/null +++ b/media/libstagefright/binding/include/mp4parse.h @@ -0,0 +1,113 @@ + +#ifndef cheddar_generated_mp4parse_h +#define cheddar_generated_mp4parse_h + + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> + +// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +typedef enum mp4parse_error { + MP4PARSE_OK = 0, + MP4PARSE_ERROR_BADARG = 1, + MP4PARSE_ERROR_INVALID = 2, + MP4PARSE_ERROR_UNSUPPORTED = 3, + MP4PARSE_ERROR_EOF = 4, + MP4PARSE_ERROR_IO = 5, +} mp4parse_error; + +typedef enum mp4parse_track_type { + MP4PARSE_TRACK_TYPE_VIDEO = 0, + MP4PARSE_TRACK_TYPE_AUDIO = 1, +} mp4parse_track_type; + +typedef enum mp4parse_codec { + MP4PARSE_CODEC_UNKNOWN, + MP4PARSE_CODEC_AAC, + MP4PARSE_CODEC_FLAC, + MP4PARSE_CODEC_OPUS, + MP4PARSE_CODEC_AVC, + MP4PARSE_CODEC_VP9, + MP4PARSE_CODEC_MP3, +} mp4parse_codec; + +typedef struct mp4parse_track_info { + mp4parse_track_type track_type; + mp4parse_codec codec; + uint32_t track_id; + uint64_t duration; + int64_t media_time; +} mp4parse_track_info; + +typedef struct mp4parse_codec_specific_config { + uint32_t length; + uint8_t const* data; +} mp4parse_codec_specific_config; + +typedef struct mp4parse_track_audio_info { + uint16_t channels; + uint16_t bit_depth; + uint32_t sample_rate; + mp4parse_codec_specific_config codec_specific_config; +} mp4parse_track_audio_info; + +typedef struct mp4parse_track_video_info { + uint32_t display_width; + uint32_t display_height; + uint16_t image_width; + uint16_t image_height; +} mp4parse_track_video_info; + +typedef struct mp4parse_fragment_info { + uint64_t fragment_duration; +} mp4parse_fragment_info; + +typedef struct mp4parse_parser mp4parse_parser; + +typedef struct mp4parse_io { + intptr_t (*read)(uint8_t* buffer, uintptr_t size, void* userdata); + void* userdata; +} mp4parse_io; + +/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`. +mp4parse_parser* mp4parse_new(mp4parse_io const* io); + +/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`. +void mp4parse_free(mp4parse_parser* parser); + +/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error. +mp4parse_error mp4parse_read(mp4parse_parser* parser); + +/// Return the number of tracks parsed by previous `mp4parse_read()` call. +mp4parse_error mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count); + +/// Fill the supplied `mp4parse_track_info` with metadata for `track`. +mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info); + +/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`. +mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info); + +/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. +mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info); + +mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info); + +mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented); + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/media/libstagefright/binding/mp4parse-cargo.patch b/media/libstagefright/binding/mp4parse-cargo.patch new file mode 100644 index 000000000..9b0ca7134 --- /dev/null +++ b/media/libstagefright/binding/mp4parse-cargo.patch @@ -0,0 +1,45 @@ +diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml +index ff9422c..814c4c6 100644 +--- a/media/libstagefright/binding/mp4parse/Cargo.toml ++++ b/media/libstagefright/binding/mp4parse/Cargo.toml +@@ -18,17 +18,11 @@ exclude = [ + ] + + [dependencies] +-byteorder = "0.5.0" +-afl = { version = "0.1.1", optional = true } +-afl-plugin = { version = "0.1.1", optional = true } +-abort_on_panic = { version = "1.0.0", optional = true } ++byteorder = "0.5.0" + + [dev-dependencies] + test-assembler = "0.1.2" + +-[features] +-fuzz = ["afl", "afl-plugin", "abort_on_panic"] +- + # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. + [profile.release] + debug-assertions = true +diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml +index aeeebc65..5c0836a 100644 +--- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml ++++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml +@@ -18,17 +18,9 @@ exclude = [ + "*.mp4", + ] + +-build = "build.rs" +- + [dependencies] + "mp4parse" = {version = "0.6.0", path = "../mp4parse"} + +-[build-dependencies] +-rusty-cheddar = "0.3.2" +- +-[features] +-fuzz = ["mp4parse/fuzz"] +- + # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. + [profile.release] + debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml new file mode 100644 index 000000000..affcef72b --- /dev/null +++ b/media/libstagefright/binding/mp4parse/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "mp4parse" +version = "0.6.0" +authors = [ + "Ralph Giles <giles@mozilla.com>", + "Matthew Gregan <kinetik@flim.org>", + "Alfredo Yang <ayang@mozilla.com>", +] + +description = "Parser for ISO base media file format (mp4)" +documentation = "https://mp4parse-docs.surge.sh/mp4parse/" +license = "MPL-2.0" + +repository = "https://github.com/mozilla/mp4parse-rust" + +# Avoid complaints about trying to package test files. +exclude = [ + "*.mp4", +] + +[dependencies] +byteorder = "0.5.0" + +[dev-dependencies] +test-assembler = "0.1.2" + +# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. +[profile.release] +debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse/src/boxes.rs b/media/libstagefright/binding/mp4parse/src/boxes.rs new file mode 100644 index 000000000..689439ade --- /dev/null +++ b/media/libstagefright/binding/mp4parse/src/boxes.rs @@ -0,0 +1,62 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +macro_rules! box_database { + ($($boxenum:ident $boxtype:expr),*,) => { + #[derive(Debug, Clone, Copy, PartialEq)] + pub enum BoxType { + $($boxenum),*, + UnknownBox(u32), + } + + impl From<u32> for BoxType { + fn from(t: u32) -> BoxType { + use self::BoxType::*; + match t { + $($boxtype => $boxenum),*, + _ => UnknownBox(t), + } + } + } + } +} + +box_database!( + FileTypeBox 0x66747970, // "ftyp" + MovieBox 0x6d6f6f76, // "moov" + MovieHeaderBox 0x6d766864, // "mvhd" + TrackBox 0x7472616b, // "trak" + TrackHeaderBox 0x746b6864, // "tkhd" + EditBox 0x65647473, // "edts" + MediaBox 0x6d646961, // "mdia" + EditListBox 0x656c7374, // "elst" + MediaHeaderBox 0x6d646864, // "mdhd" + HandlerBox 0x68646c72, // "hdlr" + MediaInformationBox 0x6d696e66, // "minf" + SampleTableBox 0x7374626c, // "stbl" + SampleDescriptionBox 0x73747364, // "stsd" + TimeToSampleBox 0x73747473, // "stts" + SampleToChunkBox 0x73747363, // "stsc" + SampleSizeBox 0x7374737a, // "stsz" + ChunkOffsetBox 0x7374636f, // "stco" + ChunkLargeOffsetBox 0x636f3634, // "co64" + SyncSampleBox 0x73747373, // "stss" + AVCSampleEntry 0x61766331, // "avc1" + AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec. + AVCConfigurationBox 0x61766343, // "avcC" + MP4AudioSampleEntry 0x6d703461, // "mp4a" + ESDBox 0x65736473, // "esds" + VP8SampleEntry 0x76703038, // "vp08" + VP9SampleEntry 0x76703039, // "vp09" + VPCodecConfigurationBox 0x76706343, // "vpcC" + FLACSampleEntry 0x664c6143, // "fLaC" + FLACSpecificBox 0x64664c61, // "dfLa" + OpusSampleEntry 0x4f707573, // "Opus" + OpusSpecificBox 0x644f7073, // "dOps" + ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec. + ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec. + MovieExtendsBox 0x6d766578, // "mvex" + MovieExtendsHeaderBox 0x6d656864, // "mehd" + QTWaveAtom 0x77617665, // "wave" - quicktime atom +); diff --git a/media/libstagefright/binding/mp4parse/src/lib.rs b/media/libstagefright/binding/mp4parse/src/lib.rs new file mode 100644 index 000000000..37606a5e2 --- /dev/null +++ b/media/libstagefright/binding/mp4parse/src/lib.rs @@ -0,0 +1,1704 @@ +//! Module for parsing ISO Base Media Format aka video/mp4 streams. + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +#![cfg_attr(feature = "fuzz", feature(plugin))] +#![cfg_attr(feature = "fuzz", plugin(afl_plugin))] +#[cfg(feature = "fuzz")] +extern crate afl; + +extern crate byteorder; +use byteorder::ReadBytesExt; +use std::io::{Read, Take}; +use std::io::Cursor; +use std::cmp; + +mod boxes; +use boxes::BoxType; + +// Unit tests. +#[cfg(test)] +mod tests; + +// Arbitrary buffer size limit used for raw read_bufs on a box. +const BUF_SIZE_LIMIT: u64 = 1024 * 1024; + +static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT; + +pub fn set_debug_mode(mode: bool) { + DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst); +} + +#[inline(always)] +fn get_debug_mode() -> bool { + DEBUG_MODE.load(std::sync::atomic::Ordering::Relaxed) +} + +macro_rules! log { + ($($args:tt)*) => ( + if get_debug_mode() { + println!( $( $args )* ); + } + ) +} + +/// Describes parser failures. +/// +/// This enum wraps the standard `io::Error` type, unified with +/// our own parser error states and those of crates we use. +#[derive(Debug)] +pub enum Error { + /// Parse error caused by corrupt or malformed data. + InvalidData(&'static str), + /// Parse error caused by limited parser support rather than invalid data. + Unsupported(&'static str), + /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data. + UnexpectedEOF, + /// Propagate underlying errors from `std::io`. + Io(std::io::Error), + /// read_mp4 terminated without detecting a moov box. + NoMoov, +} + +impl From<std::io::Error> for Error { + fn from(err: std::io::Error) -> Error { + match err.kind() { + std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEOF, + _ => Error::Io(err), + } + } +} + +impl From<std::string::FromUtf8Error> for Error { + fn from(_: std::string::FromUtf8Error) -> Error { + Error::InvalidData("invalid utf8") + } +} + +/// Result shorthand using our Error enum. +pub type Result<T> = std::result::Result<T, Error>; + +/// Basic ISO box structure. +/// +/// mp4 files are a sequence of possibly-nested 'box' structures. Each box +/// begins with a header describing the length of the box's data and a +/// four-byte box type which identifies the type of the box. Together these +/// are enough to interpret the contents of that section of the file. +#[derive(Debug, Clone, Copy)] +struct BoxHeader { + /// Box type. + name: BoxType, + /// Size of the box in bytes. + size: u64, + /// Offset to the start of the contained data (or header size). + offset: u64, +} + +/// File type box 'ftyp'. +#[derive(Debug)] +struct FileTypeBox { + major_brand: u32, + minor_version: u32, + compatible_brands: Vec<u32>, +} + +/// Movie header box 'mvhd'. +#[derive(Debug)] +struct MovieHeaderBox { + pub timescale: u32, + duration: u64, +} + +/// Track header box 'tkhd' +#[derive(Debug, Clone)] +pub struct TrackHeaderBox { + track_id: u32, + pub disabled: bool, + pub duration: u64, + pub width: u32, + pub height: u32, +} + +/// Edit list box 'elst' +#[derive(Debug)] +struct EditListBox { + edits: Vec<Edit>, +} + +#[derive(Debug)] +struct Edit { + segment_duration: u64, + media_time: i64, + media_rate_integer: i16, + media_rate_fraction: i16, +} + +/// Media header box 'mdhd' +#[derive(Debug)] +struct MediaHeaderBox { + timescale: u32, + duration: u64, +} + +// Chunk offset box 'stco' or 'co64' +#[derive(Debug)] +struct ChunkOffsetBox { + offsets: Vec<u64>, +} + +// Sync sample box 'stss' +#[derive(Debug)] +struct SyncSampleBox { + samples: Vec<u32>, +} + +// Sample to chunk box 'stsc' +#[derive(Debug)] +struct SampleToChunkBox { + samples: Vec<SampleToChunk>, +} + +#[derive(Debug)] +struct SampleToChunk { + first_chunk: u32, + samples_per_chunk: u32, + sample_description_index: u32, +} + +// Sample size box 'stsz' +#[derive(Debug)] +struct SampleSizeBox { + sample_size: u32, + sample_sizes: Vec<u32>, +} + +// Time to sample box 'stts' +#[derive(Debug)] +struct TimeToSampleBox { + samples: Vec<Sample>, +} + +#[derive(Debug)] +struct Sample { + sample_count: u32, + sample_delta: u32, +} + +// Handler reference box 'hdlr' +#[derive(Debug)] +struct HandlerBox { + handler_type: u32, +} + +// Sample description box 'stsd' +#[derive(Debug)] +struct SampleDescriptionBox { + descriptions: Vec<SampleEntry>, +} + +#[derive(Debug, Clone)] +pub enum SampleEntry { + Audio(AudioSampleEntry), + Video(VideoSampleEntry), + Unknown, +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub struct ES_Descriptor { + pub audio_codec: CodecType, + pub audio_sample_rate: Option<u32>, + pub audio_channel_count: Option<u16>, + pub codec_specific_config: Vec<u8>, +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Clone)] +pub enum AudioCodecSpecific { + ES_Descriptor(ES_Descriptor), + FLACSpecificBox(FLACSpecificBox), + OpusSpecificBox(OpusSpecificBox), +} + +#[derive(Debug, Clone)] +pub struct AudioSampleEntry { + data_reference_index: u16, + pub channelcount: u16, + pub samplesize: u16, + pub samplerate: u32, + pub codec_specific: AudioCodecSpecific, +} + +#[derive(Debug, Clone)] +pub enum VideoCodecSpecific { + AVCConfig(Vec<u8>), + VPxConfig(VPxConfigBox), +} + +#[derive(Debug, Clone)] +pub struct VideoSampleEntry { + data_reference_index: u16, + pub width: u16, + pub height: u16, + pub codec_specific: VideoCodecSpecific, +} + +/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9). +#[derive(Debug, Clone)] +pub struct VPxConfigBox { + profile: u8, + level: u8, + pub bit_depth: u8, + pub color_space: u8, // Really an enum + pub chroma_subsampling: u8, + transfer_function: u8, + video_full_range: bool, + pub codec_init: Vec<u8>, // Empty for vp8/vp9. +} + +#[derive(Debug, Clone)] +pub struct FLACMetadataBlock { + pub block_type: u8, + pub data: Vec<u8>, +} + +/// Represet a FLACSpecificBox 'dfLa' +#[derive(Debug, Clone)] +pub struct FLACSpecificBox { + version: u8, + pub blocks: Vec<FLACMetadataBlock>, +} + +#[derive(Debug, Clone)] +struct ChannelMappingTable { + stream_count: u8, + coupled_count: u8, + channel_mapping: Vec<u8>, +} + +/// Represent an OpusSpecificBox 'dOps' +#[derive(Debug, Clone)] +pub struct OpusSpecificBox { + pub version: u8, + output_channel_count: u8, + pre_skip: u16, + input_sample_rate: u32, + output_gain: i16, + channel_mapping_family: u8, + channel_mapping_table: Option<ChannelMappingTable>, +} + +#[derive(Debug)] +pub struct MovieExtendsBox { + pub fragment_duration: Option<MediaScaledTime>, +} + +/// Internal data structures. +#[derive(Debug, Default)] +pub struct MediaContext { + pub timescale: Option<MediaTimeScale>, + /// Tracks found in the file. + pub tracks: Vec<Track>, + pub mvex: Option<MovieExtendsBox>, +} + +impl MediaContext { + pub fn new() -> MediaContext { + Default::default() + } +} + +#[derive(Debug, PartialEq)] +pub enum TrackType { + Audio, + Video, + Unknown, +} + +impl Default for TrackType { + fn default() -> Self { TrackType::Unknown } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CodecType { + Unknown, + MP3, + AAC, + FLAC, + Opus, + H264, + VP9, + VP8, + EncryptedVideo, + EncryptedAudio, +} + +impl Default for CodecType { + fn default() -> Self { CodecType::Unknown } +} + +/// The media's global (mvhd) timescale in units per second. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct MediaTimeScale(pub u64); + +/// A time to be scaled by the media's global (mvhd) timescale. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct MediaScaledTime(pub u64); + +/// The track's local (mdhd) timescale. +/// Members are timescale units per second and the track id. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct TrackTimeScale(pub u64, pub usize); + +/// A time to be scaled by the track's local (mdhd) timescale. +/// Members are time in scale units and the track id. +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct TrackScaledTime(pub u64, pub usize); + +/// A fragmented file contains no sample data in stts, stsc, and stco. +#[derive(Debug, Default)] +pub struct EmptySampleTableBoxes { + pub empty_stts : bool, + pub empty_stsc : bool, + pub empty_stco : bool, +} + +/// Check boxes contain data. +impl EmptySampleTableBoxes { + pub fn all_empty(&self) -> bool { + self.empty_stts & self.empty_stsc & self.empty_stco + } +} + +#[derive(Debug, Default)] +pub struct Track { + id: usize, + pub track_type: TrackType, + pub empty_duration: Option<MediaScaledTime>, + pub media_time: Option<TrackScaledTime>, + pub timescale: Option<TrackTimeScale>, + pub duration: Option<TrackScaledTime>, + pub track_id: Option<u32>, + pub codec_type: CodecType, + pub empty_sample_boxes: EmptySampleTableBoxes, + pub data: Option<SampleEntry>, + pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this. +} + +impl Track { + fn new(id: usize) -> Track { + Track { id: id, ..Default::default() } + } +} + +struct BMFFBox<'a, T: 'a + Read> { + head: BoxHeader, + content: Take<&'a mut T>, +} + +struct BoxIter<'a, T: 'a + Read> { + src: &'a mut T, +} + +impl<'a, T: Read> BoxIter<'a, T> { + fn new(src: &mut T) -> BoxIter<T> { + BoxIter { src: src } + } + + fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> { + let r = read_box_header(self.src); + match r { + Ok(h) => Ok(Some(BMFFBox { + head: h, + content: self.src.take(h.size - h.offset), + })), + Err(Error::UnexpectedEOF) => Ok(None), + Err(e) => Err(e), + } + } +} + +impl<'a, T: Read> Read for BMFFBox<'a, T> { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { + self.content.read(buf) + } +} + +impl<'a, T: Read> BMFFBox<'a, T> { + fn bytes_left(&self) -> usize { + self.content.limit() as usize + } + + fn get_header(&self) -> &BoxHeader { + &self.head + } + + fn box_iter<'b>(&'b mut self) -> BoxIter<BMFFBox<'a, T>> { + BoxIter::new(self) + } +} + +/// Read and parse a box header. +/// +/// Call this first to determine the type of a particular mp4 box +/// and its length. Used internally for dispatching to specific +/// parsers for the internal content, or to get the length to +/// skip unknown or uninteresting boxes. +fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> { + let size32 = try!(be_u32(src)); + let name = BoxType::from(try!(be_u32(src))); + let size = match size32 { + // valid only for top-level box and indicates it's the last box in the file. usually mdat. + 0 => return Err(Error::Unsupported("unknown sized box")), + 1 => { + let size64 = try!(be_u64(src)); + if size64 < 16 { + return Err(Error::InvalidData("malformed wide size")); + } + size64 + } + 2...7 => return Err(Error::InvalidData("malformed size")), + _ => size32 as u64, + }; + let offset = match size32 { + 1 => 4 + 4 + 8, + _ => 4 + 4, + }; + assert!(offset <= size); + Ok(BoxHeader { + name: name, + size: size, + offset: offset, + }) +} + +/// Parse the extra header fields for a full box. +fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> { + let version = try!(src.read_u8()); + let flags_a = try!(src.read_u8()); + let flags_b = try!(src.read_u8()); + let flags_c = try!(src.read_u8()); + Ok((version, + (flags_a as u32) << 16 | (flags_b as u32) << 8 | (flags_c as u32))) +} + +/// Skip over the entire contents of a box. +fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> { + // Skip the contents of unknown chunks. + let to_skip = { + let header = src.get_header(); + log!("{:?} (skipped)", header); + (header.size - header.offset) as usize + }; + assert!(to_skip == src.bytes_left()); + skip(src, to_skip) +} + +/// Skip over the remain data of a box. +fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> { + let remain = { + let header = src.get_header(); + let len = src.bytes_left(); + log!("remain {} (skipped) in {:?}", len, header); + len + }; + skip(src, remain) +} + +macro_rules! check_parser_state { + ( $src:expr ) => { + if $src.limit() > 0 { + log!("bad parser state: {} content bytes left", $src.limit()); + return Err(Error::InvalidData("unread box content or bad parser sync")); + } + } +} + +/// Read the contents of a box, including sub boxes. +/// +/// Metadata is accumulated in the passed-through `MediaContext` struct, +/// which can be examined later. +pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> { + let mut found_ftyp = false; + let mut found_moov = false; + // TODO(kinetik): Top-level parsing should handle zero-sized boxes + // rather than throwing an error. + let mut iter = BoxIter::new(f); + while let Some(mut b) = try!(iter.next_box()) { + // box ordering: ftyp before any variable length box (inc. moov), + // but may not be first box in file if file signatures etc. present + // fragmented mp4 order: ftyp, moov, pairs of moof/mdat (1-multiple), mfra + + // "special": uuid, wide (= 8 bytes) + // isom: moov, mdat, free, skip, udta, ftyp, moof, mfra + // iso2: pdin, meta + // iso3: meco + // iso5: styp, sidx, ssix, prft + // unknown, maybe: id32 + + // qt: pnot + + // possibly allow anything where all printable and/or all lowercase printable + // "four printable characters from the ISO 8859-1 character set" + match b.head.name { + BoxType::FileTypeBox => { + let ftyp = try!(read_ftyp(&mut b)); + found_ftyp = true; + log!("{:?}", ftyp); + } + BoxType::MovieBox => { + try!(read_moov(&mut b, context)); + found_moov = true; + } + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + if found_moov { + log!("found moov {}, could stop pure 'moov' parser now", if found_ftyp { + "and ftyp" + } else { + "but no ftyp" + }); + } + } + + // XXX(kinetik): This isn't perfect, as a "moov" with no contents is + // treated as okay but we haven't found anything useful. Needs more + // thought for clearer behaviour here. + if found_moov { + Ok(()) + } else { + Err(Error::NoMoov) + } +} + +fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<(MovieHeaderBox, Option<MediaTimeScale>)> { + let mvhd = try!(read_mvhd(f)); + if mvhd.timescale == 0 { + return Err(Error::InvalidData("zero timescale in mdhd")); + } + let timescale = Some(MediaTimeScale(mvhd.timescale as u64)); + Ok((mvhd, timescale)) +} + +fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::MovieHeaderBox => { + let (mvhd, timescale) = try!(parse_mvhd(&mut b)); + context.timescale = timescale; + log!("{:?}", mvhd); + } + BoxType::TrackBox => { + let mut track = Track::new(context.tracks.len()); + try!(read_trak(&mut b, &mut track)); + context.tracks.push(track); + } + BoxType::MovieExtendsBox => { + let mvex = try!(read_mvex(&mut b)); + log!("{:?}", mvex); + context.mvex = Some(mvex); + } + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> { + let mut iter = src.box_iter(); + let mut fragment_duration = None; + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::MovieExtendsHeaderBox => { + let duration = try!(read_mehd(&mut b)); + fragment_duration = Some(duration); + }, + _ => try!(skip_box_content(&mut b)), + } + } + Ok(MovieExtendsBox { + fragment_duration: fragment_duration, + }) +} + +fn read_mehd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaScaledTime> { + let (version, _) = try!(read_fullbox_extra(src)); + let fragment_duration = match version { + 1 => try!(be_u64(src)), + 0 => try!(be_u32(src)) as u64, + _ => return Err(Error::InvalidData("unhandled mehd version")), + }; + Ok(MediaScaledTime(fragment_duration)) +} + +fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::TrackHeaderBox => { + let tkhd = try!(read_tkhd(&mut b)); + track.track_id = Some(tkhd.track_id); + track.tkhd = Some(tkhd.clone()); + log!("{:?}", tkhd); + } + BoxType::EditBox => try!(read_edts(&mut b, track)), + BoxType::MediaBox => try!(read_mdia(&mut b, track)), + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::EditListBox => { + let elst = try!(read_elst(&mut b)); + let mut empty_duration = 0; + let mut idx = 0; + if elst.edits.len() > 2 { + return Err(Error::Unsupported("more than two edits")); + } + if elst.edits[idx].media_time == -1 { + empty_duration = elst.edits[idx].segment_duration; + if elst.edits.len() < 2 { + return Err(Error::InvalidData("expected additional edit")); + } + idx += 1; + } + track.empty_duration = Some(MediaScaledTime(empty_duration)); + if elst.edits[idx].media_time < 0 { + return Err(Error::InvalidData("unexpected negative media time in edit")); + } + track.media_time = Some(TrackScaledTime(elst.edits[idx].media_time as u64, + track.id)); + log!("{:?}", elst); + } + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +fn parse_mdhd<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<(MediaHeaderBox, Option<TrackScaledTime>, Option<TrackTimeScale>)> { + let mdhd = try!(read_mdhd(f)); + let duration = match mdhd.duration { + std::u64::MAX => None, + duration => Some(TrackScaledTime(duration, track.id)), + }; + if mdhd.timescale == 0 { + return Err(Error::InvalidData("zero timescale in mdhd")); + } + let timescale = Some(TrackTimeScale(mdhd.timescale as u64, track.id)); + Ok((mdhd, duration, timescale)) +} + +fn read_mdia<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::MediaHeaderBox => { + let (mdhd, duration, timescale) = try!(parse_mdhd(&mut b, track)); + track.duration = duration; + track.timescale = timescale; + log!("{:?}", mdhd); + } + BoxType::HandlerBox => { + let hdlr = try!(read_hdlr(&mut b)); + match hdlr.handler_type { + 0x76696465 /* 'vide' */ => track.track_type = TrackType::Video, + 0x736f756e /* 'soun' */ => track.track_type = TrackType::Audio, + _ => (), + } + log!("{:?}", hdlr); + } + BoxType::MediaInformationBox => try!(read_minf(&mut b, track)), + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +fn read_minf<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::SampleTableBox => try!(read_stbl(&mut b, track)), + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> { + let mut iter = f.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::SampleDescriptionBox => { + let stsd = try!(read_stsd(&mut b, track)); + log!("{:?}", stsd); + } + BoxType::TimeToSampleBox => { + let stts = try!(read_stts(&mut b)); + track.empty_sample_boxes.empty_stts = stts.samples.is_empty(); + log!("{:?}", stts); + } + BoxType::SampleToChunkBox => { + let stsc = try!(read_stsc(&mut b)); + track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty(); + log!("{:?}", stsc); + } + BoxType::SampleSizeBox => { + let stsz = try!(read_stsz(&mut b)); + log!("{:?}", stsz); + } + BoxType::ChunkOffsetBox => { + let stco = try!(read_stco(&mut b)); + track.empty_sample_boxes.empty_stco = stco.offsets.is_empty(); + log!("{:?}", stco); + } + BoxType::ChunkLargeOffsetBox => { + let co64 = try!(read_co64(&mut b)); + log!("{:?}", co64); + } + BoxType::SyncSampleBox => { + let stss = try!(read_stss(&mut b)); + log!("{:?}", stss); + } + _ => try!(skip_box_content(&mut b)), + }; + check_parser_state!(b.content); + } + Ok(()) +} + +/// Parse an ftyp box. +fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> { + let major = try!(be_u32(src)); + let minor = try!(be_u32(src)); + let bytes_left = src.bytes_left(); + if bytes_left % 4 != 0 { + return Err(Error::InvalidData("invalid ftyp size")); + } + // Is a brand_count of zero valid? + let brand_count = bytes_left / 4; + let mut brands = Vec::new(); + for _ in 0..brand_count { + brands.push(try!(be_u32(src))); + } + Ok(FileTypeBox { + major_brand: major, + minor_version: minor, + compatible_brands: brands, + }) +} + +/// Parse an mvhd box. +fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> { + let (version, _) = try!(read_fullbox_extra(src)); + match version { + // 64 bit creation and modification times. + 1 => { + try!(skip(src, 16)); + } + // 32 bit creation and modification times. + 0 => { + try!(skip(src, 8)); + } + _ => return Err(Error::InvalidData("unhandled mvhd version")), + } + let timescale = try!(be_u32(src)); + let duration = match version { + 1 => try!(be_u64(src)), + 0 => { + let d = try!(be_u32(src)); + if d == std::u32::MAX { + std::u64::MAX + } else { + d as u64 + } + } + _ => return Err(Error::InvalidData("unhandled mvhd version")), + }; + // Skip remaining fields. + try!(skip(src, 80)); + Ok(MovieHeaderBox { + timescale: timescale, + duration: duration, + }) +} + +/// Parse a tkhd box. +fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> { + let (version, flags) = try!(read_fullbox_extra(src)); + let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0; + match version { + // 64 bit creation and modification times. + 1 => { + try!(skip(src, 16)); + } + // 32 bit creation and modification times. + 0 => { + try!(skip(src, 8)); + } + _ => return Err(Error::InvalidData("unhandled tkhd version")), + } + let track_id = try!(be_u32(src)); + try!(skip(src, 4)); + let duration = match version { + 1 => try!(be_u64(src)), + 0 => try!(be_u32(src)) as u64, + _ => return Err(Error::InvalidData("unhandled tkhd version")), + }; + // Skip uninteresting fields. + try!(skip(src, 52)); + let width = try!(be_u32(src)); + let height = try!(be_u32(src)); + Ok(TrackHeaderBox { + track_id: track_id, + disabled: disabled, + duration: duration, + width: width, + height: height, + }) +} + +/// Parse a elst box. +fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> { + let (version, _) = try!(read_fullbox_extra(src)); + let edit_count = try!(be_u32(src)); + if edit_count == 0 { + return Err(Error::InvalidData("invalid edit count")); + } + let mut edits = Vec::new(); + for _ in 0..edit_count { + let (segment_duration, media_time) = match version { + 1 => { + // 64 bit segment duration and media times. + (try!(be_u64(src)), try!(be_i64(src))) + } + 0 => { + // 32 bit segment duration and media times. + (try!(be_u32(src)) as u64, try!(be_i32(src)) as i64) + } + _ => return Err(Error::InvalidData("unhandled elst version")), + }; + let media_rate_integer = try!(be_i16(src)); + let media_rate_fraction = try!(be_i16(src)); + edits.push(Edit { + segment_duration: segment_duration, + media_time: media_time, + media_rate_integer: media_rate_integer, + media_rate_fraction: media_rate_fraction, + }) + } + + Ok(EditListBox { + edits: edits, + }) +} + +/// Parse a mdhd box. +fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> { + let (version, _) = try!(read_fullbox_extra(src)); + let (timescale, duration) = match version { + 1 => { + // Skip 64-bit creation and modification times. + try!(skip(src, 16)); + + // 64 bit duration. + (try!(be_u32(src)), try!(be_u64(src))) + } + 0 => { + // Skip 32-bit creation and modification times. + try!(skip(src, 8)); + + // 32 bit duration. + let timescale = try!(be_u32(src)); + let duration = { + // Since we convert the 32-bit duration to 64-bit by + // upcasting, we need to preserve the special all-1s + // ("unknown") case by hand. + let d = try!(be_u32(src)); + if d == std::u32::MAX { + std::u64::MAX + } else { + d as u64 + } + }; + (timescale, duration) + } + _ => return Err(Error::InvalidData("unhandled mdhd version")), + }; + + // Skip uninteresting fields. + try!(skip(src, 4)); + + Ok(MediaHeaderBox { + timescale: timescale, + duration: duration, + }) +} + +/// Parse a stco box. +fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let offset_count = try!(be_u32(src)); + let mut offsets = Vec::new(); + for _ in 0..offset_count { + offsets.push(try!(be_u32(src)) as u64); + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(ChunkOffsetBox { + offsets: offsets, + }) +} + +/// Parse a co64 box. +fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let offset_count = try!(be_u32(src)); + let mut offsets = Vec::new(); + for _ in 0..offset_count { + offsets.push(try!(be_u64(src))); + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(ChunkOffsetBox { + offsets: offsets, + }) +} + +/// Parse a stss box. +fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let sample_count = try!(be_u32(src)); + let mut samples = Vec::new(); + for _ in 0..sample_count { + samples.push(try!(be_u32(src))); + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(SyncSampleBox { + samples: samples, + }) +} + +/// Parse a stsc box. +fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let sample_count = try!(be_u32(src)); + let mut samples = Vec::new(); + for _ in 0..sample_count { + let first_chunk = try!(be_u32(src)); + let samples_per_chunk = try!(be_u32(src)); + let sample_description_index = try!(be_u32(src)); + samples.push(SampleToChunk { + first_chunk: first_chunk, + samples_per_chunk: samples_per_chunk, + sample_description_index: sample_description_index, + }); + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(SampleToChunkBox { + samples: samples, + }) +} + +/// Parse a stsz box. +fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let sample_size = try!(be_u32(src)); + let sample_count = try!(be_u32(src)); + let mut sample_sizes = Vec::new(); + if sample_size == 0 { + for _ in 0..sample_count { + sample_sizes.push(try!(be_u32(src))); + } + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(SampleSizeBox { + sample_size: sample_size, + sample_sizes: sample_sizes, + }) +} + +/// Parse a stts box. +fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> { + let (_, _) = try!(read_fullbox_extra(src)); + let sample_count = try!(be_u32(src)); + let mut samples = Vec::new(); + for _ in 0..sample_count { + let sample_count = try!(be_u32(src)); + let sample_delta = try!(be_u32(src)); + samples.push(Sample { + sample_count: sample_count, + sample_delta: sample_delta, + }); + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(TimeToSampleBox { + samples: samples, + }) +} + +/// Parse a VPx Config Box. +fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> { + let (version, _) = try!(read_fullbox_extra(src)); + if version != 0 { + return Err(Error::Unsupported("unknown vpcC version")); + } + + let profile = try!(src.read_u8()); + let level = try!(src.read_u8()); + let (bit_depth, color_space) = { + let byte = try!(src.read_u8()); + ((byte >> 4) & 0x0f, byte & 0x0f) + }; + let (chroma_subsampling, transfer_function, video_full_range) = { + let byte = try!(src.read_u8()); + ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1) + }; + + let codec_init_size = try!(be_u16(src)); + let codec_init = try!(read_buf(src, codec_init_size as usize)); + + // TODO(rillian): validate field value ranges. + Ok(VPxConfigBox { + profile: profile, + level: level, + bit_depth: bit_depth, + color_space: color_space, + chroma_subsampling: chroma_subsampling, + transfer_function: transfer_function, + video_full_range: video_full_range, + codec_init: codec_init, + }) +} + +fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> { + let temp = try!(src.read_u8()); + let block_type = temp & 0x7f; + let length = try!(be_u24(src)); + if length as usize > src.bytes_left() { + return Err(Error::InvalidData( + "FLACMetadataBlock larger than parent box")); + } + let data = try!(read_buf(src, length as usize)); + Ok(FLACMetadataBlock { + block_type: block_type, + data: data, + }) +} + +fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> { + // Tags for elementary stream description + const ESDESCR_TAG: u8 = 0x03; + const DECODER_CONFIG_TAG: u8 = 0x04; + const DECODER_SPECIFIC_TAG: u8 = 0x05; + + let frequency_table = + vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000), + (0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050), + (0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000), + (0xc, 7350)]; + + let (_, _) = try!(read_fullbox_extra(src)); + + let esds_size = src.head.size - src.head.offset - 4; + if esds_size > BUF_SIZE_LIMIT { + return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT")); + } + let esds_array = try!(read_buf(src, esds_size as usize)); + + // Parsing DecoderConfig descriptor to get the object_profile_indicator + // for correct codec type, audio sample rate and channel counts. + let (object_profile_indicator, sample_frequency, channels) = { + let mut object_profile: u8 = 0; + let mut sample_frequency = None; + let mut channels = None; + + // clone a esds cursor for parsing. + let esds = &mut Cursor::new(&esds_array); + let next_tag = try!(esds.read_u8()); + + if next_tag != ESDESCR_TAG { + return Err(Error::Unsupported("fail to parse ES descriptor")); + } + + let esds_extend = try!(esds.read_u8()); + // extension tag start from 0x80. + let esds_end = if esds_extend >= 0x80 { + // skip remaining extension. + try!(skip(esds, 2)); + esds.position() + try!(esds.read_u8()) as u64 + } else { + esds.position() + esds_extend as u64 + }; + try!(skip(esds, 2)); + + let esds_flags = try!(esds.read_u8()); + + // Stream dependency flag, first bit from left most. + if esds_flags & 0x80 > 0 { + // Skip uninteresting fields. + try!(skip(esds, 2)); + } + + // Url flag, second bit from left most. + if esds_flags & 0x40 > 0 { + // Skip uninteresting fields. + let skip_es_len: usize = try!(esds.read_u8()) as usize + 2; + try!(skip(esds, skip_es_len)); + } + + // find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG) + if esds_end > esds.position() { + let next_tag = try!(esds.read_u8()); + if next_tag == DECODER_CONFIG_TAG { + let dcds_extend = try!(esds.read_u8()); + // extension tag start from 0x80. + if dcds_extend >= 0x80 { + // skip remains extension and length. + try!(skip(esds, 3)); + } + + object_profile = try!(esds.read_u8()); + + // Skip uninteresting fields. + try!(skip(esds, 12)); + } + } + + + // find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG) + if esds_end > esds.position() { + let next_tag = try!(esds.read_u8()); + if next_tag == DECODER_SPECIFIC_TAG { + let dsds_extend = try!(esds.read_u8()); + // extension tag start from 0x80. + if dsds_extend >= 0x80 { + // skip remains extension and length. + try!(skip(esds, 3)); + } + + let audio_specific_config = try!(be_u16(esds)); + + let sample_index = (audio_specific_config & 0x07FF) >> 7; + + let channel_counts = (audio_specific_config & 0x007F) >> 3; + + sample_frequency = + frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1); + + channels = Some(channel_counts); + } + } + + (object_profile, sample_frequency, channels) + }; + + let codec = match object_profile_indicator { + 0x40 | 0x41 => CodecType::AAC, + 0x6B => CodecType::MP3, + _ => CodecType::Unknown, + }; + + if codec == CodecType::Unknown { + return Err(Error::Unsupported("unknown audio codec")); + } + + Ok(ES_Descriptor { + audio_codec: codec, + audio_sample_rate: sample_frequency, + audio_channel_count: channels, + codec_specific_config: esds_array, + }) +} + +/// Parse `FLACSpecificBox`. +fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> { + let (version, flags) = try!(read_fullbox_extra(src)); + if version != 0 { + return Err(Error::Unsupported("unknown dfLa (FLAC) version")); + } + if flags != 0 { + return Err(Error::InvalidData("no-zero dfLa (FLAC) flags")); + } + let mut blocks = Vec::new(); + while src.bytes_left() > 0 { + let block = try!(read_flac_metadata(src)); + blocks.push(block); + } + // The box must have at least one meta block, and the first block + // must be the METADATA_BLOCK_STREAMINFO + if blocks.is_empty() { + return Err(Error::InvalidData("FLACSpecificBox missing metadata")); + } else if blocks[0].block_type != 0 { + println!("flac metadata block:\n {:?}", blocks[0]); + return Err(Error::InvalidData( + "FLACSpecificBox must have STREAMINFO metadata first")); + } else if blocks[0].data.len() != 34 { + return Err(Error::InvalidData( + "FLACSpecificBox STREAMINFO block is the wrong size")); + } + Ok(FLACSpecificBox { + version: version, + blocks: blocks, + }) +} + +/// Parse `OpusSpecificBox`. +fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> { + let version = try!(src.read_u8()); + if version != 0 { + return Err(Error::Unsupported("unknown dOps (Opus) version")); + } + + let output_channel_count = try!(src.read_u8()); + let pre_skip = try!(be_u16(src)); + let input_sample_rate = try!(be_u32(src)); + let output_gain = try!(be_i16(src)); + let channel_mapping_family = try!(src.read_u8()); + + let channel_mapping_table = if channel_mapping_family == 0 { + None + } else { + let stream_count = try!(src.read_u8()); + let coupled_count = try!(src.read_u8()); + let channel_mapping = try!(read_buf(src, output_channel_count as usize)); + + Some(ChannelMappingTable { + stream_count: stream_count, + coupled_count: coupled_count, + channel_mapping: channel_mapping, + }) + }; + + // TODO(kinetik): validate field value ranges. + Ok(OpusSpecificBox { + version: version, + output_channel_count: output_channel_count, + pre_skip: pre_skip, + input_sample_rate: input_sample_rate, + output_gain: output_gain, + channel_mapping_family: channel_mapping_family, + channel_mapping_table: channel_mapping_table, + }) +} + +/// Re-serialize the Opus codec-specific config data as an `OpusHead` packet. +/// +/// Some decoders expect the initialization data in the format used by the +/// Ogg and WebM encapsulations. To support this we prepend the `OpusHead` +/// tag and byte-swap the data from big- to little-endian relative to the +/// dOps box. +pub fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> { + match dst.write(b"OpusHead") { + Err(e) => return Err(Error::from(e)), + Ok(bytes) => { + if bytes != 8 { + return Err(Error::InvalidData("Couldn't write OpusHead tag.")); + } + } + } + // In mp4 encapsulation, the version field is 0, but in ogg + // it is 1. While decoders generally accept zero as well, write + // out the version of the header we're supporting rather than + // whatever we parsed out of mp4. + try!(dst.write_u8(1)); + try!(dst.write_u8(opus.output_channel_count)); + try!(dst.write_u16::<byteorder::LittleEndian>(opus.pre_skip)); + try!(dst.write_u32::<byteorder::LittleEndian>(opus.input_sample_rate)); + try!(dst.write_i16::<byteorder::LittleEndian>(opus.output_gain)); + try!(dst.write_u8(opus.channel_mapping_family)); + match opus.channel_mapping_table { + None => {} + Some(ref table) => { + try!(dst.write_u8(table.stream_count)); + try!(dst.write_u8(table.coupled_count)); + match dst.write(&table.channel_mapping) { + Err(e) => return Err(Error::from(e)), + Ok(bytes) => { + if bytes != table.channel_mapping.len() { + return Err(Error::InvalidData("Couldn't write channel mapping table data.")); + } + } + } + } + }; + Ok(()) +} + +/// Parse a hdlr box. +fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> { + let (_, _) = try!(read_fullbox_extra(src)); + + // Skip uninteresting fields. + try!(skip(src, 4)); + + let handler_type = try!(be_u32(src)); + + // Skip uninteresting fields. + try!(skip(src, 12)); + + let bytes_left = src.bytes_left(); + let _name = try!(read_null_terminated_string(src, bytes_left)); + + Ok(HandlerBox { + handler_type: handler_type, + }) +} + +/// Parse an video description inside an stsd box. +fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> { + let name = src.get_header().name; + track.codec_type = match name { + BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264, + BoxType::VP8SampleEntry => CodecType::VP8, + BoxType::VP9SampleEntry => CodecType::VP9, + BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo, + _ => CodecType::Unknown, + }; + + // Skip uninteresting fields. + try!(skip(src, 6)); + + let data_reference_index = try!(be_u16(src)); + + // Skip uninteresting fields. + try!(skip(src, 16)); + + let width = try!(be_u16(src)); + let height = try!(be_u16(src)); + + // Skip uninteresting fields. + try!(skip(src, 14)); + + let _compressorname = try!(read_fixed_length_pascal_string(src, 32)); + + // Skip uninteresting fields. + try!(skip(src, 4)); + + // Skip clap/pasp/etc. for now. + let mut codec_specific = None; + let mut iter = src.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::AVCConfigurationBox => { + if (name != BoxType::AVCSampleEntry && + name != BoxType::AVC3SampleEntry && + name != BoxType::ProtectedVisualSampleEntry) || + codec_specific.is_some() { + return Err(Error::InvalidData("malformed video sample entry")); + } + let avcc_size = b.head.size - b.head.offset; + if avcc_size > BUF_SIZE_LIMIT { + return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT")); + } + let avcc = try!(read_buf(&mut b.content, avcc_size as usize)); + // TODO(kinetik): Parse avcC box? For now we just stash the data. + codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc)); + } + BoxType::VPCodecConfigurationBox => { // vpcC + if (name != BoxType::VP8SampleEntry && + name != BoxType::VP9SampleEntry) || + codec_specific.is_some() { + return Err(Error::InvalidData("malformed video sample entry")); + } + let vpcc = try!(read_vpcc(&mut b)); + codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc)); + } + _ => try!(skip_box_content(&mut b)), + } + check_parser_state!(b.content); + } + + codec_specific + .map(|codec_specific| SampleEntry::Video(VideoSampleEntry { + data_reference_index: data_reference_index, + width: width, + height: height, + codec_specific: codec_specific, + })) + .ok_or_else(|| Error::InvalidData("malformed video sample entry")) +} + +fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> { + let mut codec_specific = None; + let mut iter = src.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::ESDBox => { + let esds = try!(read_esds(&mut b)); + codec_specific = Some(esds); + }, + _ => try!(skip_box_content(&mut b)), + } + } + + codec_specific.ok_or_else(|| Error::InvalidData("malformed audio sample entry")) +} + +/// Parse an audio description inside an stsd box. +fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> { + let name = src.get_header().name; + track.codec_type = match name { + // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg). + BoxType::MP4AudioSampleEntry => CodecType::AAC, + BoxType::FLACSampleEntry => CodecType::FLAC, + BoxType::OpusSampleEntry => CodecType::Opus, + BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio, + _ => CodecType::Unknown, + }; + + // Skip uninteresting fields. + try!(skip(src, 6)); + + let data_reference_index = try!(be_u16(src)); + + // XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant + // uses it, need to work out if we have to support it. Without checking + // here and reading extra fields after samplerate (or bailing with an + // error), the parser loses sync completely. + let version = try!(be_u16(src)); + + // Skip uninteresting fields. + try!(skip(src, 6)); + + let channelcount = try!(be_u16(src)); + let samplesize = try!(be_u16(src)); + + // Skip uninteresting fields. + try!(skip(src, 4)); + + let samplerate = try!(be_u32(src)); + + match version { + 0 => (), + 1 => { + // Quicktime sound sample description version 1. + // Skip uninteresting fields. + try!(skip(src, 16)); + }, + _ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")), + } + + // Skip chan/etc. for now. + let mut codec_specific = None; + let mut iter = src.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + match b.head.name { + BoxType::ESDBox => { + if (name != BoxType::MP4AudioSampleEntry && + name != BoxType::ProtectedAudioSampleEntry) || + codec_specific.is_some() { + return Err(Error::InvalidData("malformed audio sample entry")); + } + + let esds = try!(read_esds(&mut b)); + track.codec_type = esds.audio_codec; + codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds)); + } + BoxType::FLACSpecificBox => { + if name != BoxType::FLACSampleEntry || + codec_specific.is_some() { + return Err(Error::InvalidData("malformed audio sample entry")); + } + let dfla = try!(read_dfla(&mut b)); + track.codec_type = CodecType::FLAC; + codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla)); + } + BoxType::OpusSpecificBox => { + if name != BoxType::OpusSampleEntry || + codec_specific.is_some() { + return Err(Error::InvalidData("malformed audio sample entry")); + } + let dops = try!(read_dops(&mut b)); + track.codec_type = CodecType::Opus; + codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops)); + } + BoxType::QTWaveAtom => { + let qt_esds = try!(read_qt_wave_atom(&mut b)); + track.codec_type = qt_esds.audio_codec; + codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds)); + } + _ => try!(skip_box_content(&mut b)), + } + check_parser_state!(b.content); + } + + codec_specific + .map(|codec_specific| SampleEntry::Audio(AudioSampleEntry { + data_reference_index: data_reference_index, + channelcount: channelcount, + samplesize: samplesize, + samplerate: samplerate, + codec_specific: codec_specific, + })) + .ok_or_else(|| Error::InvalidData("malformed audio sample entry")) +} + +/// Parse a stsd box. +fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> { + let (_, _) = try!(read_fullbox_extra(src)); + + let description_count = try!(be_u32(src)); + let mut descriptions = Vec::new(); + + { + // TODO(kinetik): check if/when more than one desc per track? do we need to support? + let mut iter = src.box_iter(); + while let Some(mut b) = try!(iter.next_box()) { + let description = match track.track_type { + TrackType::Video => read_video_sample_entry(&mut b, track), + TrackType::Audio => read_audio_sample_entry(&mut b, track), + TrackType::Unknown => Err(Error::Unsupported("unknown track type")), + }; + let description = match description { + Ok(desc) => desc, + Err(Error::Unsupported(_)) => { + // read_{audio,video}_desc may have returned Unsupported + // after partially reading the box content, so we can't + // simply use skip_box_content here. + let to_skip = b.bytes_left(); + try!(skip(&mut b, to_skip)); + SampleEntry::Unknown + } + Err(e) => return Err(e), + }; + if track.data.is_none() { + track.data = Some(description.clone()); + } else { + log!("** don't know how to handle multiple descriptions **"); + } + descriptions.push(description); + check_parser_state!(b.content); + if descriptions.len() == description_count as usize { + break; + } + } + } + + // Padding could be added in some contents. + try!(skip_box_remain(src)); + + Ok(SampleDescriptionBox { + descriptions: descriptions, + }) +} + +/// Skip a number of bytes that we don't care to parse. +fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> { + const BUF_SIZE: usize = 64 * 1024; + let mut buf = vec![0; BUF_SIZE]; + while bytes > 0 { + let buf_size = cmp::min(bytes, BUF_SIZE); + let len = try!(src.take(buf_size as u64).read(&mut buf)); + if len == 0 { + return Err(Error::UnexpectedEOF); + } + bytes -= len; + } + Ok(()) +} + +/// Read size bytes into a Vector or return error. +fn read_buf<T: ReadBytesExt>(src: &mut T, size: usize) -> Result<Vec<u8>> { + let mut buf = vec![0; size]; + let r = try!(src.read(&mut buf)); + if r != size { + return Err(Error::InvalidData("failed buffer read")); + } + Ok(buf) +} + +// TODO(kinetik): Find a copy of ISO/IEC 14496-1 to confirm various string encodings. +// XXX(kinetik): definition of "null-terminated" string is fuzzy, we have: +// - zero or more byte strings, with a single null terminating the string. +// - zero byte strings with no null terminator (i.e. zero space in the box for the string) +// - length-prefixed strings with no null terminator (e.g. bear_rotate_0.mp4) +fn read_null_terminated_string<T: ReadBytesExt>(src: &mut T, mut size: usize) -> Result<String> { + let mut buf = Vec::new(); + while size > 0 { + let c = try!(src.read_u8()); + if c == 0 { + break; + } + buf.push(c); + size -= 1; + } + String::from_utf8(buf).map_err(From::from) +} + +#[allow(dead_code)] +fn read_pascal_string<T: ReadBytesExt>(src: &mut T) -> Result<String> { + let len = try!(src.read_u8()); + let buf = try!(read_buf(src, len as usize)); + String::from_utf8(buf).map_err(From::from) +} + +// Weird string encoding with a length prefix and a fixed sized buffer which +// contains padding if the string doesn't fill the buffer. +fn read_fixed_length_pascal_string<T: Read>(src: &mut T, size: usize) -> Result<String> { + assert!(size > 0); + let len = cmp::min(try!(src.read_u8()) as usize, size - 1); + let buf = try!(read_buf(src, len)); + try!(skip(src, size - 1 - buf.len())); + String::from_utf8(buf).map_err(From::from) +} + +fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> { + src.read_i16::<byteorder::BigEndian>().map_err(From::from) +} + +fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> { + src.read_i32::<byteorder::BigEndian>().map_err(From::from) +} + +fn be_i64<T: ReadBytesExt>(src: &mut T) -> Result<i64> { + src.read_i64::<byteorder::BigEndian>().map_err(From::from) +} + +fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> { + src.read_u16::<byteorder::BigEndian>().map_err(From::from) +} + +fn be_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> { + src.read_uint::<byteorder::BigEndian>(3) + .map(|v| v as u32) + .map_err(From::from) +} + +fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> { + src.read_u32::<byteorder::BigEndian>().map_err(From::from) +} + +fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> { + src.read_u64::<byteorder::BigEndian>().map_err(From::from) +} diff --git a/media/libstagefright/binding/mp4parse/src/tests.rs b/media/libstagefright/binding/mp4parse/src/tests.rs new file mode 100644 index 000000000..7063b3790 --- /dev/null +++ b/media/libstagefright/binding/mp4parse/src/tests.rs @@ -0,0 +1,860 @@ +//! Module for parsing ISO Base Media Format aka video/mp4 streams. +//! Internal unit tests. + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::io::Cursor; +use super::read_mp4; +use super::MediaContext; +use super::Error; +extern crate test_assembler; +use self::test_assembler::*; + +use boxes::BoxType; + +enum BoxSize { + Short(u32), + Long(u64), + UncheckedShort(u32), + UncheckedLong(u64), + Auto, +} + +fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>> + where F: Fn(Section) -> Section +{ + let mut section = Section::new(); + let box_size = Label::new(); + section = match size { + BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size), + BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1), + BoxSize::Auto => section.B32(&box_size), + }; + section = section.append_bytes(name); + section = match size { + // The spec allows the 32-bit size to be 0 to indicate unknown + // length streams. It's not clear if this is valid when using a + // 64-bit size, so prohibit it for now. + BoxSize::Long(size) => { + assert!(size > 0); + section.B64(size) + } + BoxSize::UncheckedLong(size) => section.B64(size), + _ => section, + }; + section = func(section); + match size { + BoxSize::Short(size) => { + if size > 0 { + assert_eq!(size as u64, section.size()) + } + } + BoxSize::Long(size) => assert_eq!(size, section.size()), + BoxSize::Auto => { + assert!(section.size() <= u32::max_value() as u64, + "Tried to use a long box with BoxSize::Auto"); + box_size.set_const(section.size()); + } + // Skip checking BoxSize::Unchecked* cases. + _ => (), + } + Cursor::new(section.get_contents().unwrap()) +} + +fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>> + where F: Fn(Section) -> Section +{ + make_box(size, name, |s| { + func(s.B8(version) + .B8(0) + .B8(0) + .B8(0)) + }) +} + +#[test] +fn read_box_header_short() { + let mut stream = make_box(BoxSize::Short(8), b"test", |s| s); + let header = super::read_box_header(&mut stream).unwrap(); + assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" + assert_eq!(header.size, 8); +} + +#[test] +fn read_box_header_long() { + let mut stream = make_box(BoxSize::Long(16), b"test", |s| s); + let header = super::read_box_header(&mut stream).unwrap(); + assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" + assert_eq!(header.size, 16); +} + +#[test] +fn read_box_header_short_unknown_size() { + let mut stream = make_box(BoxSize::Short(0), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"), + _ => panic!("unexpected result reading box with unknown size"), + }; +} + +#[test] +fn read_box_header_short_invalid_size() { + let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, "malformed size"), + _ => panic!("unexpected result reading box with invalid size"), + }; +} + +#[test] +fn read_box_header_long_invalid_size() { + let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s); + match super::read_box_header(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, "malformed wide size"), + _ => panic!("unexpected result reading box with invalid size"), + }; +} + +#[test] +fn read_ftyp() { + let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| { + s.append_bytes(b"mp42") + .B32(0) // minor version + .append_bytes(b"isom") + .append_bytes(b"mp42") + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FileTypeBox); + assert_eq!(stream.head.size, 24); + let parsed = super::read_ftyp(&mut stream).unwrap(); + assert_eq!(parsed.major_brand, 0x6d703432); // mp42 + assert_eq!(parsed.minor_version, 0); + assert_eq!(parsed.compatible_brands.len(), 2); + assert_eq!(parsed.compatible_brands[0], 0x69736f6d); // isom + assert_eq!(parsed.compatible_brands[1], 0x6d703432); // mp42 +} + +#[test] +fn read_truncated_ftyp() { + // We declare a 24 byte box, but only write 20 bytes. + let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| { + s.append_bytes(b"mp42") + .B32(0) // minor version + .append_bytes(b"isom") + }); + let mut context = MediaContext::new(); + match read_mp4(&mut stream, &mut context) { + Err(Error::UnexpectedEOF) => (), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +#[test] +fn read_ftyp_case() { + // Brands in BMFF are represented as a u32, so it would seem clear that + // 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some + // demuxers treat these as case-insensitive strings, e.g. street.mp4's + // major brand is "MP42". I haven't seen case-insensitive + // compatible_brands (which we also test here), but it doesn't seem + // unlikely given the major_brand behaviour. + let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| { + s.append_bytes(b"MP42") + .B32(0) // minor version + .append_bytes(b"ISOM") + .append_bytes(b"MP42") + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FileTypeBox); + assert_eq!(stream.head.size, 24); + let parsed = super::read_ftyp(&mut stream).unwrap(); + assert_eq!(parsed.major_brand, 0x4d503432); + assert_eq!(parsed.minor_version, 0); + assert_eq!(parsed.compatible_brands.len(), 2); + assert_eq!(parsed.compatible_brands[0], 0x49534f4d); // ISOM + assert_eq!(parsed.compatible_brands[1], 0x4d503432); // MP42 +} + +#[test] +fn read_elst_v0() { + let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| { + s.B32(1) // list count + // first entry + .B32(1234) // duration + .B32(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::EditListBox); + assert_eq!(stream.head.size, 28); + let parsed = super::read_elst(&mut stream).unwrap(); + assert_eq!(parsed.edits.len(), 1); + assert_eq!(parsed.edits[0].segment_duration, 1234); + assert_eq!(parsed.edits[0].media_time, 5678); + assert_eq!(parsed.edits[0].media_rate_integer, 12); + assert_eq!(parsed.edits[0].media_rate_fraction, 34); +} + +#[test] +fn read_elst_v1() { + let mut stream = make_fullbox(BoxSize::Short(56), b"elst", 1, |s| { + s.B32(2) // list count + // first entry + .B64(1234) // duration + .B64(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + // second entry + .B64(1234) // duration + .B64(5678) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::EditListBox); + assert_eq!(stream.head.size, 56); + let parsed = super::read_elst(&mut stream).unwrap(); + assert_eq!(parsed.edits.len(), 2); + assert_eq!(parsed.edits[1].segment_duration, 1234); + assert_eq!(parsed.edits[1].media_time, 5678); + assert_eq!(parsed.edits[1].media_rate_integer, 12); + assert_eq!(parsed.edits[1].media_rate_fraction, 34); +} + +#[test] +fn read_mdhd_v0() { + let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) // timescale + .B32(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mdhd_v1() { + let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(1234) // timescale + .B64(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 44); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mdhd_unknown_duration() { + let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) // timescale + .B32(::std::u32::MAX) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_mdhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, ::std::u64::MAX); +} + +#[test] +fn read_mdhd_invalid_timescale() { + let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(0) // timescale + .B64(5678) // duration + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MediaHeaderBox); + assert_eq!(stream.head.size, 44); + let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0)); + assert_eq!(r.is_err(), true); +} + +#[test] +fn read_mvhd_v0() { + let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) + .B32(5678) + .append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 108); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mvhd_v1() { + let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(1234) + .B64(5678) + .append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 120); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, 5678); +} + +#[test] +fn read_mvhd_invalid_timescale() { + let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { + s.B64(0) + .B64(0) + .B32(0) + .B64(5678) + .append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 120); + let r = super::parse_mvhd(&mut stream); + assert_eq!(r.is_err(), true); +} + +#[test] +fn read_mvhd_unknown_duration() { + let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { + s.B32(0) + .B32(0) + .B32(1234) + .B32(::std::u32::MAX) + .append_repeated(0, 80) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::MovieHeaderBox); + assert_eq!(stream.head.size, 108); + let parsed = super::read_mvhd(&mut stream).unwrap(); + assert_eq!(parsed.timescale, 1234); + assert_eq!(parsed.duration, ::std::u64::MAX); +} + +#[test] +fn read_vpcc() { + let data_length = 12u16; + let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| { + s.B8(2) + .B8(0) + .B8(0x82) + .B8(0) + .B16(data_length) + .append_repeated(42, data_length as usize) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox); + let r = super::read_vpcc(&mut stream); + assert!(r.is_ok()); +} + +#[test] +fn read_hdlr() { + let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| { + s.B32(0) + .append_bytes(b"vide") + .B32(0) + .B32(0) + .B32(0) + .append_bytes(b"VideoHandler") + .B8(0) // null-terminate string + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 45); + let parsed = super::read_hdlr(&mut stream).unwrap(); + assert_eq!(parsed.handler_type, 0x76696465); // vide +} + +#[test] +fn read_hdlr_short_name() { + let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| { + s.B32(0) + .append_bytes(b"vide") + .B32(0) + .B32(0) + .B32(0) + .B8(0) // null-terminate string + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 33); + let parsed = super::read_hdlr(&mut stream).unwrap(); + assert_eq!(parsed.handler_type, 0x76696465); // vide +} + +#[test] +fn read_hdlr_zero_length_name() { + let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { + s.B32(0) + .append_bytes(b"vide") + .B32(0) + .B32(0) + .B32(0) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::HandlerBox); + assert_eq!(stream.head.size, 32); + let parsed = super::read_hdlr(&mut stream).unwrap(); + assert_eq!(parsed.handler_type, 0x76696465); // vide +} + +fn flac_streaminfo() -> Vec<u8> { + vec![ + 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00, + 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0xc9, + 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3, + 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, 0x05, 0x00, + 0x33, 0xad, + ] +} + +#[test] +fn read_flac() { + let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| { + s.append_repeated(0, 6) // reserved + .B16(1) // data reference index + .B32(0) // reserved + .B32(0) // reserved + .B16(2) // channel count + .B16(16) // bits per sample + .B16(0) // pre_defined + .B16(0) // reserved + .B32(44100 << 16) // Sample rate + .append_bytes(&make_dfla(FlacBlockType::StreamInfo, true, + &flac_streaminfo(), FlacBlockLength::Correct) + .into_inner()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + let r = super::read_audio_sample_entry(&mut stream, &mut track); + r.unwrap(); +} + +#[derive(Clone, Copy)] +enum FlacBlockType { + StreamInfo = 0, + _Padding = 1, + _Application = 2, + _Seektable = 3, + _Comment = 4, + _Cuesheet = 5, + _Picture = 6, + _Reserved, + _Invalid = 127, +} + +enum FlacBlockLength { + Correct, + Incorrect(usize), +} + +fn make_dfla(block_type: FlacBlockType, last: bool, data: &Vec<u8>, + data_length: FlacBlockLength) -> Cursor<Vec<u8>> { + assert!(data.len() < 1<<24); + make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| { + let flag = match last { + true => 1, + false => 0, + }; + let size = match data_length { + FlacBlockLength::Correct => (data.len() as u32) & 0xffffff, + FlacBlockLength::Incorrect(size) => { + assert!(size < 1<<24); + (size as u32) & 0xffffff + } + }; + let block_type = (block_type as u32) & 0x7f; + s.B32(flag << 31 | block_type << 24 | size) + .append_bytes(data) + }) +} + +#[test] +fn read_dfla() { + let mut stream = make_dfla(FlacBlockType::StreamInfo, true, + &flac_streaminfo(), FlacBlockLength::Correct); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FLACSpecificBox); + let dfla = super::read_dfla(&mut stream).unwrap(); + assert_eq!(dfla.version, 0); +} + +#[test] +fn long_flac_metadata() { + let streaminfo = flac_streaminfo(); + let mut stream = make_dfla(FlacBlockType::StreamInfo, true, + &streaminfo, + FlacBlockLength::Incorrect(streaminfo.len() + 4)); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::FLACSpecificBox); + let r = super::read_dfla(&mut stream); + assert!(r.is_err()); +} + +#[test] +fn read_opus() { + let mut stream = make_box(BoxSize::Auto, b"Opus", |s| { + s.append_repeated(0, 6) + .B16(1) // data reference index + .B32(0) + .B32(0) + .B16(2) // channel count + .B16(16) // bits per sample + .B16(0) + .B16(0) + .B32(48000 << 16) // Sample rate is always 48 kHz for Opus. + .append_bytes(&make_dops().into_inner()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + let r = super::read_audio_sample_entry(&mut stream, &mut track); + assert!(r.is_ok()); +} + +fn make_dops() -> Cursor<Vec<u8>> { + make_box(BoxSize::Auto, b"dOps", |s| { + s.B8(0) // version + .B8(2) // channel count + .B16(348) // pre-skip + .B32(44100) // original sample rate + .B16(0) // gain + .B8(0) // channel mapping + }) +} + +#[test] +fn read_dops() { + let mut stream = make_dops(); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + assert_eq!(stream.head.name, BoxType::OpusSpecificBox); + let r = super::read_dops(&mut stream); + assert!(r.is_ok()); +} + +#[test] +fn serialize_opus_header() { + let opus = super::OpusSpecificBox { + version: 0, + output_channel_count: 1, + pre_skip: 342, + input_sample_rate: 24000, + output_gain: 0, + channel_mapping_family: 0, + channel_mapping_table: None, + }; + let mut v = Vec::<u8>::new(); + super::serialize_opus_header(&opus, &mut v).unwrap(); + assert!(v.len() == 19); + assert!(v == vec![ + 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64, + 0x01, 0x01, 0x56, 0x01, + 0xc0, 0x5d, 0x00, 0x00, + 0x00, 0x00, 0x00, + ]); + let opus = super::OpusSpecificBox { + version: 0, + output_channel_count: 6, + pre_skip: 152, + input_sample_rate: 48000, + output_gain: 0, + channel_mapping_family: 1, + channel_mapping_table: Some(super::ChannelMappingTable { + stream_count: 4, + coupled_count: 2, + channel_mapping: vec![0, 4, 1, 2, 3, 5], + }), + }; + let mut v = Vec::<u8>::new(); + super::serialize_opus_header(&opus, &mut v).unwrap(); + assert!(v.len() == 27); + assert!(v == vec![ + 0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64, + 0x01, 0x06, 0x98, 0x00, + 0x80, 0xbb, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x04, 0x02, + 0x00, 0x04, 0x01, 0x02, 0x03, 0x05, + ]); +} + +#[test] +fn avcc_limit() { + let mut stream = make_box(BoxSize::Auto, b"avc1", |s| { + s.append_repeated(0, 6) + .B16(1) + .append_repeated(0, 16) + .B16(320) + .B16(240) + .append_repeated(0, 14) + .append_repeated(0, 32) + .append_repeated(0, 4) + .B32(0xffffffff) + .append_bytes(b"avcC") + .append_repeated(0, 100) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + match super::read_video_sample_entry(&mut stream, &mut track) { + Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +#[test] +fn esds_limit() { + let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { + s.append_repeated(0, 6) + .B16(1) + .B32(0) + .B32(0) + .B16(2) + .B16(16) + .B16(0) + .B16(0) + .B32(48000 << 16) + .B32(0xffffffff) + .append_bytes(b"esds") + .append_repeated(0, 100) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + match super::read_audio_sample_entry(&mut stream, &mut track) { + Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +#[test] +fn esds_limit_2() { + let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { + s.append_repeated(0, 6) + .B16(1) + .B32(0) + .B32(0) + .B16(2) + .B16(16) + .B16(0) + .B16(0) + .B32(48000 << 16) + .B32(8) + .append_bytes(b"esds") + .append_repeated(0, 4) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + match super::read_audio_sample_entry(&mut stream, &mut track) { + Err(Error::UnexpectedEOF) => (), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +#[test] +fn read_elst_zero_entries() { + let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| { + s.B32(0) + .B16(12) + .B16(34) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match super::read_elst(&mut stream) { + Err(Error::InvalidData(s)) => assert_eq!(s, "invalid edit count"), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +fn make_elst() -> Cursor<Vec<u8>> { + make_fullbox(BoxSize::Auto, b"elst", 1, |s| { + s.B32(1) + // first entry + .B64(1234) // duration + .B64(0xffffffffffffffff) // time + .B16(12) // rate integer + .B16(34) // rate fraction + }) +} + +#[test] +fn read_edts_bogus() { + // First edit list entry has a media_time of -1, so we expect a second + // edit list entry to be present to provide a valid media_time. + let mut stream = make_box(BoxSize::Auto, b"edts", |s| { + s.append_bytes(&make_elst().into_inner()) + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + match super::read_edts(&mut stream, &mut track) { + Err(Error::InvalidData(s)) => assert_eq!(s, "expected additional edit"), + Ok(_) => assert!(false, "expected an error result"), + _ => assert!(false, "expected a different error result"), + } +} + +#[test] +fn invalid_pascal_string() { + // String claims to be 32 bytes long (we provide 33 bytes to account for + // the 1 byte length prefix). + let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + let mut stream = Cursor::new(pstr); + // Reader wants to limit the total read length to 32 bytes, so any + // returned string must be no longer than 31 bytes. + let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap(); + assert_eq!(s.len(), 31); +} + +#[test] +fn skip_padding_in_boxes() { + // Padding data could be added in the end of these boxes. Parser needs to skip + // them instead of returning error. + let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"]; + + for name in box_names { + let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| { + s.append_repeated(0, 100) // add padding data + }); + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + match name { + b"stts" => { + super::read_stts(&mut stream).expect("fail to skip padding: stts"); + }, + b"stsc" => { + super::read_stsc(&mut stream).expect("fail to skip padding: stsc"); + }, + b"stsz" => { + super::read_stsz(&mut stream).expect("fail to skip padding: stsz"); + }, + b"stco" => { + super::read_stco(&mut stream).expect("fail to skip padding: stco"); + }, + b"co64" => { + super::read_co64(&mut stream).expect("fail to skip padding: co64"); + }, + b"stss" => { + super::read_stss(&mut stream).expect("fail to skip padding: stss"); + }, + _ => (), + } + } +} + +#[test] +fn skip_padding_in_stsd() { + // Padding data could be added in the end of stsd boxes. Parser needs to skip + // them instead of returning error. + let avc = make_box(BoxSize::Auto, b"avc1", |s| { + s.append_repeated(0, 6) + .B16(1) + .append_repeated(0, 16) + .B16(320) + .B16(240) + .append_repeated(0, 14) + .append_repeated(0, 32) + .append_repeated(0, 4) + .B32(0xffffffff) + .append_bytes(b"avcC") + .append_repeated(0, 100) + }).into_inner(); + let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| { + s.B32(1) + .append_bytes(avc.as_slice()) + .append_repeated(0, 100) // add padding data + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + super::read_stsd(&mut stream, &mut super::Track::new(0)) + .expect("fail to skip padding: stsd"); +} + +#[test] +fn read_qt_wave_atom() { + let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| { + s.B8(0x03) // elementary stream descriptor tag + .B8(0x0b) // esds length + .append_repeated(0, 2) + .B8(0x00) // flags + .B8(0x04) // decoder config descriptor tag + .B8(0x0d) // dcds length + .B8(0x6b) // mp3 + .append_repeated(0, 12) + }).into_inner(); + let wave = make_box(BoxSize::Auto, b"wave", |s| { + s.append_bytes(esds.as_slice()) + }).into_inner(); + let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { + s.append_repeated(0, 6) + .B16(1) // data_reference_count + .B16(1) // verion: qt -> 1 + .append_repeated(0, 6) + .B16(2) + .B16(16) + .append_repeated(0, 4) + .B32(48000 << 16) + .append_repeated(0, 16) + .append_bytes(wave.as_slice()) + }); + + let mut iter = super::BoxIter::new(&mut stream); + let mut stream = iter.next_box().unwrap().unwrap(); + let mut track = super::Track::new(0); + super::read_audio_sample_entry(&mut stream, &mut track) + .expect("fail to read qt wave atom"); + assert_eq!(track.codec_type, super::CodecType::MP3); +} diff --git a/media/libstagefright/binding/mp4parse/tests/afl.rs b/media/libstagefright/binding/mp4parse/tests/afl.rs new file mode 100644 index 000000000..d4ffec0df --- /dev/null +++ b/media/libstagefright/binding/mp4parse/tests/afl.rs @@ -0,0 +1,53 @@ +/// Regression tests from American Fuzzy Lop test cases. + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +/// These all caused panics at some point during development. + +extern crate mp4parse; + +use std::io::Cursor; + +/// https://github.com/mozilla/mp4parse-rust/issues/2 +/// +/// Test a box with 4-byte size, smaller than the smallest header. +#[test] +fn fuzz_2() { + let mut c = Cursor::new(b"\x00\x00\x00\x04\xa6\x00\x04\xa6".to_vec()); + let mut context = mp4parse::MediaContext::new(); + let _ = mp4parse::read_mp4(&mut c, &mut context); +} + +/// https://github.com/mozilla/mp4parse-rust/issues/4 +/// +/// Test a large (64 bit) box header with zero declared size. +#[test] +fn fuzz_4() { + let mut c = Cursor::new(b"\x00\x00\x00\x01\x30\x30\x30\x30\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()); + let mut context = mp4parse::MediaContext::new(); + let _ = mp4parse::read_mp4(&mut c, &mut context); +} + +/// https://github.com/mozilla/mp4parse-rust/issues/5 +/// +/// Declares 202116104 compatible brands but does not supply them, +/// verifying read is properly bounded at the end of the stream. +#[test] +fn fuzz_5() { + let mut c = Cursor::new(b"\x30\x30\x30\x30\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); + let mut context = mp4parse::MediaContext::new(); + let _ = mp4parse::read_mp4(&mut c, &mut context); +} + +/// https://github.com/mozilla/mp4parse-rust/issues/6 +/// +/// Declares an ftyp box with a single invalid (short - 3 byte) compatible +/// brand and excludes the extra 3 bytes from the stream. +#[test] +fn fuzz_6() { + let mut c = Cursor::new(b"\x00\x00\x00\x13\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); + let mut context = mp4parse::MediaContext::new(); + let _ = mp4parse::read_mp4(&mut c, &mut context); +} diff --git a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 b/media/libstagefright/binding/mp4parse/tests/minimal.mp4 Binary files differnew file mode 100644 index 000000000..9fe1e6722 --- /dev/null +++ b/media/libstagefright/binding/mp4parse/tests/minimal.mp4 diff --git a/media/libstagefright/binding/mp4parse/tests/public.rs b/media/libstagefright/binding/mp4parse/tests/public.rs new file mode 100644 index 000000000..1d36f5f5a --- /dev/null +++ b/media/libstagefright/binding/mp4parse/tests/public.rs @@ -0,0 +1,97 @@ +/// Check if needed fields are still public. + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +extern crate mp4parse as mp4; + +use std::io::{Cursor, Read}; +use std::fs::File; + +// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53 +#[test] +fn public_api() { + let mut fd = File::open("tests/minimal.mp4").expect("Unknown file"); + let mut buf = Vec::new(); + fd.read_to_end(&mut buf).expect("File error"); + + let mut c = Cursor::new(&buf); + let mut context = mp4::MediaContext::new(); + mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed"); + assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000))); + for track in context.tracks { + match track.data { + Some(mp4::SampleEntry::Video(v)) => { + // track part + assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0))); + assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); + assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0))); + assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12800, 0))); + assert_eq!(v.width, 320); + assert_eq!(v.height, 240); + + // track.tkhd part + let tkhd = track.tkhd.unwrap(); + assert_eq!(tkhd.disabled, false); + assert_eq!(tkhd.duration, 40); + assert_eq!(tkhd.width, 20971520); + assert_eq!(tkhd.height, 15728640); + + // track.data part + assert_eq!(match v.codec_specific { + mp4::VideoCodecSpecific::AVCConfig(v) => { + assert!(v.len() > 0); + "AVC" + } + mp4::VideoCodecSpecific::VPxConfig(vpx) => { + // We don't enter in here, we just check if fields are public. + assert!(vpx.bit_depth > 0); + assert!(vpx.color_space > 0); + assert!(vpx.chroma_subsampling > 0); + assert!(vpx.codec_init.len() > 0); + "VPx" + } + }, "AVC"); + } + Some(mp4::SampleEntry::Audio(a)) => { + // track part + assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1))); + assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); + assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1))); + assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1))); + + // track.tkhd part + let tkhd = track.tkhd.unwrap(); + assert_eq!(tkhd.disabled, false); + assert_eq!(tkhd.duration, 62); + assert_eq!(tkhd.width, 0); + assert_eq!(tkhd.height, 0); + + // track.data part + assert_eq!(match a.codec_specific { + mp4::AudioCodecSpecific::ES_Descriptor(esds) => { + assert_eq!(esds.audio_codec, mp4::CodecType::AAC); + assert_eq!(esds.audio_sample_rate.unwrap(), 48000); + "ES" + } + mp4::AudioCodecSpecific::FLACSpecificBox(flac) => { + // STREAMINFO block must be present and first. + assert!(flac.blocks.len() > 0); + assert!(flac.blocks[0].block_type == 0); + assert!(flac.blocks[0].data.len() == 34); + "FLAC" + } + mp4::AudioCodecSpecific::OpusSpecificBox(opus) => { + // We don't enter in here, we just check if fields are public. + assert!(opus.version > 0); + "Opus" + } + }, "ES"); + assert!(a.samplesize > 0); + assert!(a.samplerate > 0); + } + Some(mp4::SampleEntry::Unknown) | None => {} + } + } +} diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml new file mode 100644 index 000000000..5c0836abe --- /dev/null +++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "mp4parse_capi" +version = "0.6.0" +authors = [ + "Ralph Giles <giles@mozilla.com>", + "Matthew Gregan <kinetik@flim.org>", + "Alfredo Yang <ayang@mozilla.com>", +] + +description = "Parser for ISO base media file format (mp4)" +documentation = "https://mp4parse-docs.surge.sh/mp4parse/" +license = "MPL-2.0" + +repository = "https://github.com/mozilla/mp4parse-rust" + +# Avoid complaints about trying to package test files. +exclude = [ + "*.mp4", +] + +[dependencies] +"mp4parse" = {version = "0.6.0", path = "../mp4parse"} + +# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. +[profile.release] +debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse_capi/build.rs b/media/libstagefright/binding/mp4parse_capi/build.rs new file mode 100644 index 000000000..29f2a85ce --- /dev/null +++ b/media/libstagefright/binding/mp4parse_capi/build.rs @@ -0,0 +1,12 @@ +extern crate cheddar; + +fn main() { + println!("cargo:rerun-if-changed=src/lib.rs"); + // Generate mp4parse.h. + cheddar::Cheddar::new().expect("could not read manifest") + .insert_code("// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT\n\n") + .insert_code("// This Source Code Form is subject to the terms of the Mozilla Public\n") + .insert_code("// License, v. 2.0. If a copy of the MPL was not distributed with this\n") + .insert_code("// file, You can obtain one at https://mozilla.org/MPL/2.0/.") + .run_build("include/mp4parse.h"); +} diff --git a/media/libstagefright/binding/mp4parse_capi/src/lib.rs b/media/libstagefright/binding/mp4parse_capi/src/lib.rs new file mode 100644 index 000000000..f52d8b169 --- /dev/null +++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs @@ -0,0 +1,870 @@ +//! C API for mp4parse module. +//! +//! Parses ISO Base Media Format aka video/mp4 streams. +//! +//! # Examples +//! +//! ```rust +//! extern crate mp4parse_capi; +//! use std::io::Read; +//! +//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize { +//! let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) }; +//! let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) }; +//! match input.read(&mut buf) { +//! Ok(n) => n as isize, +//! Err(_) => -1, +//! } +//! } +//! +//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); +//! let io = mp4parse_capi::mp4parse_io { +//! read: buf_read, +//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void +//! }; +//! unsafe { +//! let parser = mp4parse_capi::mp4parse_new(&io); +//! let rv = mp4parse_capi::mp4parse_read(parser); +//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK); +//! mp4parse_capi::mp4parse_free(parser); +//! } +//! ``` + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +extern crate mp4parse; + +use std::io::Read; +use std::collections::HashMap; + +// Symbols we need from our rust api. +use mp4parse::MediaContext; +use mp4parse::TrackType; +use mp4parse::read_mp4; +use mp4parse::Error; +use mp4parse::SampleEntry; +use mp4parse::AudioCodecSpecific; +use mp4parse::VideoCodecSpecific; +use mp4parse::MediaTimeScale; +use mp4parse::MediaScaledTime; +use mp4parse::TrackTimeScale; +use mp4parse::TrackScaledTime; +use mp4parse::serialize_opus_header; +use mp4parse::CodecType; + +// rusty-cheddar's C enum generation doesn't namespace enum members by +// prefixing them, so we're forced to do it in our member names until +// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing +// the members into the module namespace avoids doubling up on the +// namespacing on the Rust side. +use mp4parse_error::*; +use mp4parse_track_type::*; + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub enum mp4parse_error { + MP4PARSE_OK = 0, + MP4PARSE_ERROR_BADARG = 1, + MP4PARSE_ERROR_INVALID = 2, + MP4PARSE_ERROR_UNSUPPORTED = 3, + MP4PARSE_ERROR_EOF = 4, + MP4PARSE_ERROR_IO = 5, +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub enum mp4parse_track_type { + MP4PARSE_TRACK_TYPE_VIDEO = 0, + MP4PARSE_TRACK_TYPE_AUDIO = 1, +} + +#[repr(C)] +#[derive(PartialEq, Debug)] +pub enum mp4parse_codec { + MP4PARSE_CODEC_UNKNOWN, + MP4PARSE_CODEC_AAC, + MP4PARSE_CODEC_FLAC, + MP4PARSE_CODEC_OPUS, + MP4PARSE_CODEC_AVC, + MP4PARSE_CODEC_VP9, + MP4PARSE_CODEC_MP3, +} + +#[repr(C)] +pub struct mp4parse_track_info { + pub track_type: mp4parse_track_type, + pub codec: mp4parse_codec, + pub track_id: u32, + pub duration: u64, + pub media_time: i64, // wants to be u64? understand how elst adjustment works + // TODO(kinetik): include crypto guff +} + +#[repr(C)] +pub struct mp4parse_codec_specific_config { + pub length: u32, + pub data: *const u8, +} + +impl Default for mp4parse_codec_specific_config { + fn default() -> Self { + mp4parse_codec_specific_config { + length: 0, + data: std::ptr::null_mut(), + } + } +} + +#[derive(Default)] +#[repr(C)] +pub struct mp4parse_track_audio_info { + pub channels: u16, + pub bit_depth: u16, + pub sample_rate: u32, + // TODO(kinetik): + // int32_t profile; + // int32_t extended_profile; // check types + codec_specific_config: mp4parse_codec_specific_config, +} + +#[repr(C)] +pub struct mp4parse_track_video_info { + pub display_width: u32, + pub display_height: u32, + pub image_width: u16, + pub image_height: u16, + // TODO(kinetik): + // extra_data + // codec_specific_config +} + +#[repr(C)] +pub struct mp4parse_fragment_info { + pub fragment_duration: u64, + // TODO: + // info in trex box. +} + +// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us +// use more than one member, so we introduce *another* wrapper. +struct Wrap { + context: MediaContext, + io: mp4parse_io, + poisoned: bool, + opus_header: HashMap<u32, Vec<u8>>, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +pub struct mp4parse_parser(Wrap); + +impl mp4parse_parser { + fn context(&self) -> &MediaContext { + &self.0.context + } + + fn context_mut(&mut self) -> &mut MediaContext { + &mut self.0.context + } + + fn io_mut(&mut self) -> &mut mp4parse_io { + &mut self.0.io + } + + fn poisoned(&self) -> bool { + self.0.poisoned + } + + fn set_poisoned(&mut self, poisoned: bool) { + self.0.poisoned = poisoned; + } + + fn opus_header_mut(&mut self) -> &mut HashMap<u32, Vec<u8>> { + &mut self.0.opus_header + } +} + +#[repr(C)] +#[derive(Clone)] +pub struct mp4parse_io { + pub read: extern fn(buffer: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize, + pub userdata: *mut std::os::raw::c_void, +} + +impl Read for mp4parse_io { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { + if buf.len() > isize::max_value() as usize { + return Err(std::io::Error::new(std::io::ErrorKind::Other, "buf length overflow in mp4parse_io Read impl")); + } + let rv = (self.read)(buf.as_mut_ptr(), buf.len(), self.userdata); + if rv >= 0 { + Ok(rv as usize) + } else { + Err(std::io::Error::new(std::io::ErrorKind::Other, "I/O error in mp4parse_io Read impl")) + } + } +} + +// C API wrapper functions. + +/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`. +#[no_mangle] +pub unsafe extern fn mp4parse_new(io: *const mp4parse_io) -> *mut mp4parse_parser { + if io.is_null() || (*io).userdata.is_null() { + return std::ptr::null_mut(); + } + // is_null() isn't available on a fn type because it can't be null (in + // Rust) by definition. But since this value is coming from the C API, + // it *could* be null. Ideally, we'd wrap it in an Option to represent + // reality, but this causes rusty-cheddar to emit the wrong type + // (https://github.com/Sean1708/rusty-cheddar/issues/30). + if ((*io).read as *mut std::os::raw::c_void).is_null() { + return std::ptr::null_mut(); + } + let parser = Box::new(mp4parse_parser(Wrap { + context: MediaContext::new(), + io: (*io).clone(), + poisoned: false, + opus_header: HashMap::new(), + })); + Box::into_raw(parser) +} + +/// Free an `mp4parse_parser*` allocated by `mp4parse_new()`. +#[no_mangle] +pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) { + assert!(!parser.is_null()); + let _ = Box::from_raw(parser); +} + +/// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error. +#[no_mangle] +pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error { + // Validate arguments from C. + if parser.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let mut context = (*parser).context_mut(); + let mut io = (*parser).io_mut(); + + let r = read_mp4(io, context); + match r { + Ok(_) => MP4PARSE_OK, + Err(Error::NoMoov) | Err(Error::InvalidData(_)) => { + // Block further calls. We've probable lost sync. + (*parser).set_poisoned(true); + MP4PARSE_ERROR_INVALID + } + Err(Error::Unsupported(_)) => MP4PARSE_ERROR_UNSUPPORTED, + Err(Error::UnexpectedEOF) => MP4PARSE_ERROR_EOF, + Err(Error::Io(_)) => { + // Block further calls after a read failure. + // Getting std::io::ErrorKind::UnexpectedEof is normal + // but our From trait implementation should have converted + // those to our Error::UnexpectedEOF variant. + (*parser).set_poisoned(true); + MP4PARSE_ERROR_IO + } + } +} + +/// Return the number of tracks parsed by previous `mp4parse_read()` call. +#[no_mangle] +pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, count: *mut u32) -> mp4parse_error { + // Validate arguments from C. + if parser.is_null() || count.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + let context = (*parser).context(); + + // Make sure the track count fits in a u32. + if context.tracks.len() > u32::max_value() as usize { + return MP4PARSE_ERROR_INVALID; + } + *count = context.tracks.len() as u32; + MP4PARSE_OK +} + +/// Calculate numerator * scale / denominator, if possible. +/// +/// Applying the associativity of integer arithmetic, we divide first +/// and add the remainder after multiplying each term separately +/// to preserve precision while leaving more headroom. That is, +/// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d. +/// +/// Return None on overflow or if the denominator is zero. +fn rational_scale(numerator: u64, denominator: u64, scale: u64) -> Option<u64> { + if denominator == 0 { + return None; + } + let integer = numerator / denominator; + let remainder = numerator % denominator; + match integer.checked_mul(scale) { + Some(integer) => remainder.checked_mul(scale) + .and_then(|remainder| (remainder/denominator).checked_add(integer)), + None => None, + } +} + +fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> { + let microseconds_per_second = 1000000; + rational_scale(time.0, scale.0, microseconds_per_second) +} + +fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> { + assert!(time.1 == scale.1); + let microseconds_per_second = 1000000; + rational_scale(time.0, scale.0, microseconds_per_second) +} + +/// Fill the supplied `mp4parse_track_info` with metadata for `track`. +#[no_mangle] +pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error { + if parser.is_null() || info.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let context = (*parser).context_mut(); + let track_index: usize = track_index as usize; + let info: &mut mp4parse_track_info = &mut *info; + + if track_index >= context.tracks.len() { + return MP4PARSE_ERROR_BADARG; + } + + info.track_type = match context.tracks[track_index].track_type { + TrackType::Video => MP4PARSE_TRACK_TYPE_VIDEO, + TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO, + TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED, + }; + + info.codec = match context.tracks[track_index].data { + Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific { + AudioCodecSpecific::OpusSpecificBox(_) => + mp4parse_codec::MP4PARSE_CODEC_OPUS, + AudioCodecSpecific::FLACSpecificBox(_) => + mp4parse_codec::MP4PARSE_CODEC_FLAC, + AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC => + mp4parse_codec::MP4PARSE_CODEC_AAC, + AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 => + mp4parse_codec::MP4PARSE_CODEC_MP3, + AudioCodecSpecific::ES_Descriptor(_) => + mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + }, + Some(SampleEntry::Video(ref video)) => match video.codec_specific { + VideoCodecSpecific::VPxConfig(_) => + mp4parse_codec::MP4PARSE_CODEC_VP9, + VideoCodecSpecific::AVCConfig(_) => + mp4parse_codec::MP4PARSE_CODEC_AVC, + }, + _ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + }; + + let track = &context.tracks[track_index]; + + if let (Some(track_timescale), + Some(context_timescale)) = (track.timescale, + context.timescale) { + let media_time = + match track.media_time.map_or(Some(0), |media_time| { + track_time_to_us(media_time, track_timescale) }) { + Some(time) => time as i64, + None => return MP4PARSE_ERROR_INVALID, + }; + let empty_duration = + match track.empty_duration.map_or(Some(0), |empty_duration| { + media_time_to_us(empty_duration, context_timescale) }) { + Some(time) => time as i64, + None => return MP4PARSE_ERROR_INVALID, + }; + info.media_time = media_time - empty_duration; + + if let Some(track_duration) = track.duration { + match track_time_to_us(track_duration, track_timescale) { + Some(duration) => info.duration = duration, + None => return MP4PARSE_ERROR_INVALID, + } + } else { + // Duration unknown; stagefright returns 0 for this. + info.duration = 0 + } + } else { + return MP4PARSE_ERROR_INVALID + } + + info.track_id = match track.track_id { + Some(track_id) => track_id, + None => return MP4PARSE_ERROR_INVALID, + }; + + MP4PARSE_OK +} + +/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`. +#[no_mangle] +pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error { + if parser.is_null() || info.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let context = (*parser).context_mut(); + + if track_index as usize >= context.tracks.len() { + return MP4PARSE_ERROR_BADARG; + } + + let track = &context.tracks[track_index as usize]; + + match track.track_type { + TrackType::Audio => {} + _ => return MP4PARSE_ERROR_INVALID, + }; + + let audio = match track.data { + Some(ref data) => data, + None => return MP4PARSE_ERROR_INVALID, + }; + + let audio = match *audio { + SampleEntry::Audio(ref x) => x, + _ => return MP4PARSE_ERROR_INVALID, + }; + + (*info).channels = audio.channelcount; + (*info).bit_depth = audio.samplesize; + (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point + + match audio.codec_specific { + AudioCodecSpecific::ES_Descriptor(ref v) => { + if v.codec_specific_config.len() > std::u32::MAX as usize { + return MP4PARSE_ERROR_INVALID; + } + (*info).codec_specific_config.length = v.codec_specific_config.len() as u32; + (*info).codec_specific_config.data = v.codec_specific_config.as_ptr(); + if let Some(rate) = v.audio_sample_rate { + (*info).sample_rate = rate; + } + if let Some(channels) = v.audio_channel_count { + (*info).channels = channels; + } + } + AudioCodecSpecific::FLACSpecificBox(ref flac) => { + // Return the STREAMINFO metadata block in the codec_specific. + let streaminfo = &flac.blocks[0]; + if streaminfo.block_type != 0 || streaminfo.data.len() != 34 { + return MP4PARSE_ERROR_INVALID; + } + (*info).codec_specific_config.length = streaminfo.data.len() as u32; + (*info).codec_specific_config.data = streaminfo.data.as_ptr(); + } + AudioCodecSpecific::OpusSpecificBox(ref opus) => { + let mut v = Vec::new(); + match serialize_opus_header(opus, &mut v) { + Err(_) => { + return MP4PARSE_ERROR_INVALID; + } + Ok(_) => { + let header = (*parser).opus_header_mut(); + header.insert(track_index, v); + match header.get(&track_index) { + None => {} + Some(v) => { + if v.len() > std::u32::MAX as usize { + return MP4PARSE_ERROR_INVALID; + } + (*info).codec_specific_config.length = v.len() as u32; + (*info).codec_specific_config.data = v.as_ptr(); + } + } + } + } + } + } + + MP4PARSE_OK +} + +/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`. +#[no_mangle] +pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error { + if parser.is_null() || info.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let context = (*parser).context_mut(); + + if track_index as usize >= context.tracks.len() { + return MP4PARSE_ERROR_BADARG; + } + + let track = &context.tracks[track_index as usize]; + + match track.track_type { + TrackType::Video => {} + _ => return MP4PARSE_ERROR_INVALID, + }; + + let video = match track.data { + Some(ref data) => data, + None => return MP4PARSE_ERROR_INVALID, + }; + + let video = match *video { + SampleEntry::Video(ref x) => x, + _ => return MP4PARSE_ERROR_INVALID, + }; + + if let Some(ref tkhd) = track.tkhd { + (*info).display_width = tkhd.width >> 16; // 16.16 fixed point + (*info).display_height = tkhd.height >> 16; // 16.16 fixed point + } else { + return MP4PARSE_ERROR_INVALID; + } + (*info).image_width = video.width; + (*info).image_height = video.height; + + MP4PARSE_OK +} + +#[no_mangle] +pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error { + if parser.is_null() || info.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let context = (*parser).context(); + let info: &mut mp4parse_fragment_info = &mut *info; + + info.fragment_duration = 0; + + let duration = match context.mvex { + Some(ref mvex) => mvex.fragment_duration, + None => return MP4PARSE_ERROR_INVALID, + }; + + if let (Some(time), Some(scale)) = (duration, context.timescale) { + info.fragment_duration = match media_time_to_us(time, scale) { + Some(time_us) => time_us as u64, + None => return MP4PARSE_ERROR_INVALID, + } + } + + MP4PARSE_OK +} + +// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes. +#[no_mangle] +pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error { + if parser.is_null() || (*parser).poisoned() { + return MP4PARSE_ERROR_BADARG; + } + + let context = (*parser).context_mut(); + let tracks = &context.tracks; + (*fragmented) = false as u8; + + if context.mvex.is_none() { + return MP4PARSE_OK; + } + + // check sample tables. + let mut iter = tracks.iter(); + match iter.find(|track| track.track_id == Some(track_id)) { + Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8, + Some(_) => {}, + None => return MP4PARSE_ERROR_BADARG, + } + + MP4PARSE_OK +} + +#[cfg(test)] +extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { + panic!("panic_read shouldn't be called in these tests"); +} + +#[cfg(test)] +extern fn error_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize { + -1 +} + +#[cfg(test)] +extern fn valid_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize { + let mut input: &mut std::fs::File = unsafe { &mut *(userdata as *mut _) }; + + let mut buf = unsafe { std::slice::from_raw_parts_mut(buf, size) }; + match input.read(&mut buf) { + Ok(n) => n as isize, + Err(_) => -1, + } +} + +#[test] +fn new_parser() { + let mut dummy_value: u32 = 42; + let io = mp4parse_io { + read: panic_read, + userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, + }; + unsafe { + let parser = mp4parse_new(&io); + assert!(!parser.is_null()); + mp4parse_free(parser); + } +} + +#[test] +#[should_panic(expected = "assertion failed")] +fn free_null_parser() { + unsafe { + mp4parse_free(std::ptr::null_mut()); + } +} + +#[test] +fn get_track_count_null_parser() { + unsafe { + let mut count: u32 = 0; + let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut()); + assert!(rv == MP4PARSE_ERROR_BADARG); + let rv = mp4parse_get_track_count(std::ptr::null(), &mut count); + assert!(rv == MP4PARSE_ERROR_BADARG); + } +} + +#[test] +fn arg_validation() { + unsafe { + // Passing a null mp4parse_io is an error. + let parser = mp4parse_new(std::ptr::null()); + assert!(parser.is_null()); + + let null_mut: *mut std::os::raw::c_void = std::ptr::null_mut(); + + // Passing an mp4parse_io with null members is an error. + let io = mp4parse_io { read: std::mem::transmute(null_mut), + userdata: null_mut }; + let parser = mp4parse_new(&io); + assert!(parser.is_null()); + + let io = mp4parse_io { read: panic_read, + userdata: null_mut }; + let parser = mp4parse_new(&io); + assert!(parser.is_null()); + + let mut dummy_value = 42; + let io = mp4parse_io { + read: std::mem::transmute(null_mut), + userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, + }; + let parser = mp4parse_new(&io); + assert!(parser.is_null()); + + // Passing a null mp4parse_parser is an error. + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(std::ptr::null_mut())); + + let mut dummy_info = mp4parse_track_info { + track_type: MP4PARSE_TRACK_TYPE_VIDEO, + codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + track_id: 0, + duration: 0, + media_time: 0, + }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info)); + + let mut dummy_video = mp4parse_track_video_info { + display_width: 0, + display_height: 0, + image_width: 0, + image_height: 0, + }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video)); + + let mut dummy_audio = Default::default(); + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio)); + } +} + +#[test] +fn arg_validation_with_parser() { + unsafe { + let mut dummy_value = 42; + let io = mp4parse_io { + read: error_read, + userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, + }; + let parser = mp4parse_new(&io); + assert!(!parser.is_null()); + + // Our mp4parse_io read should simply fail with an error. + assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); + + // The parser is now poisoned and unusable. + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_read(parser)); + + // Null info pointers are an error. + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, std::ptr::null_mut())); + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, std::ptr::null_mut())); + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, std::ptr::null_mut())); + + let mut dummy_info = mp4parse_track_info { + track_type: MP4PARSE_TRACK_TYPE_VIDEO, + codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + track_id: 0, + duration: 0, + media_time: 0, + }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info)); + + let mut dummy_video = mp4parse_track_video_info { + display_width: 0, + display_height: 0, + image_width: 0, + image_height: 0, + }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video)); + + let mut dummy_audio = Default::default(); + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio)); + + mp4parse_free(parser); + } +} + +#[test] +fn get_track_count_poisoned_parser() { + unsafe { + let mut dummy_value = 42; + let io = mp4parse_io { + read: error_read, + userdata: &mut dummy_value as *mut _ as *mut std::os::raw::c_void, + }; + let parser = mp4parse_new(&io); + assert!(!parser.is_null()); + + // Our mp4parse_io read should simply fail with an error. + assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser)); + + let mut count: u32 = 0; + let rv = mp4parse_get_track_count(parser, &mut count); + assert!(rv == MP4PARSE_ERROR_BADARG); + } +} + +#[test] +fn arg_validation_with_data() { + unsafe { + let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap(); + let io = mp4parse_io { read: valid_read, + userdata: &mut file as *mut _ as *mut std::os::raw::c_void }; + let parser = mp4parse_new(&io); + assert!(!parser.is_null()); + + assert_eq!(MP4PARSE_OK, mp4parse_read(parser)); + + let mut count: u32 = 0; + assert_eq!(MP4PARSE_OK, mp4parse_get_track_count(parser, &mut count)); + assert_eq!(2, count); + + let mut info = mp4parse_track_info { + track_type: MP4PARSE_TRACK_TYPE_VIDEO, + codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + track_id: 0, + duration: 0, + media_time: 0, + }; + assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 0, &mut info)); + assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); + assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AVC); + assert_eq!(info.track_id, 1); + assert_eq!(info.duration, 40000); + assert_eq!(info.media_time, 0); + + assert_eq!(MP4PARSE_OK, mp4parse_get_track_info(parser, 1, &mut info)); + assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_AUDIO); + assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_AAC); + assert_eq!(info.track_id, 2); + assert_eq!(info.duration, 61333); + assert_eq!(info.media_time, 21333); + + let mut video = mp4parse_track_video_info { + display_width: 0, + display_height: 0, + image_width: 0, + image_height: 0, + }; + assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video)); + assert_eq!(video.display_width, 320); + assert_eq!(video.display_height, 240); + assert_eq!(video.image_width, 320); + assert_eq!(video.image_height, 240); + + let mut audio = Default::default(); + assert_eq!(MP4PARSE_OK, mp4parse_get_track_audio_info(parser, 1, &mut audio)); + assert_eq!(audio.channels, 1); + assert_eq!(audio.bit_depth, 16); + assert_eq!(audio.sample_rate, 48000); + + // Test with an invalid track number. + let mut info = mp4parse_track_info { + track_type: MP4PARSE_TRACK_TYPE_VIDEO, + codec: mp4parse_codec::MP4PARSE_CODEC_UNKNOWN, + track_id: 0, + duration: 0, + media_time: 0, + }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 3, &mut info)); + assert_eq!(info.track_type, MP4PARSE_TRACK_TYPE_VIDEO); + assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN); + assert_eq!(info.track_id, 0); + assert_eq!(info.duration, 0); + assert_eq!(info.media_time, 0); + + let mut video = mp4parse_track_video_info { display_width: 0, + display_height: 0, + image_width: 0, + image_height: 0 }; + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video)); + assert_eq!(video.display_width, 0); + assert_eq!(video.display_height, 0); + assert_eq!(video.image_width, 0); + assert_eq!(video.image_height, 0); + + let mut audio = Default::default(); + assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio)); + assert_eq!(audio.channels, 0); + assert_eq!(audio.bit_depth, 0); + assert_eq!(audio.sample_rate, 0); + + mp4parse_free(parser); + } +} + +#[test] +fn rational_scale_overflow() { + assert_eq!(rational_scale(17, 3, 1000), Some(5666)); + let large = 0x4000_0000_0000_0000; + assert_eq!(rational_scale(large, 2, 2), Some(large)); + assert_eq!(rational_scale(large, 4, 4), Some(large)); + assert_eq!(rational_scale(large, 2, 8), None); + assert_eq!(rational_scale(large, 8, 4), Some(large/2)); + assert_eq!(rational_scale(large + 1, 4, 4), Some(large+1)); + assert_eq!(rational_scale(large, 40, 1000), None); +} + +#[test] +fn media_time_overflow() { + let scale = MediaTimeScale(90000); + let duration = MediaScaledTime(9007199254710000); + assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000)); +} + +#[test] +fn track_time_overflow() { + let scale = TrackTimeScale(44100, 0); + let duration = TrackScaledTime(4413527634807900, 0); + assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000)); +} diff --git a/media/libstagefright/binding/update-rust.sh b/media/libstagefright/binding/update-rust.sh new file mode 100755 index 000000000..a8a462f6d --- /dev/null +++ b/media/libstagefright/binding/update-rust.sh @@ -0,0 +1,56 @@ +#!/bin/sh -e +# Script to update mp4parse-rust sources to latest upstream + +# Default version. +VER=v0.6.0 + +# Accept version or commit from the command line. +if test -n "$1"; then + VER=$1 +fi + +echo "Fetching sources..." +rm -rf _upstream +git clone https://github.com/mozilla/mp4parse-rust _upstream/mp4parse +pushd _upstream/mp4parse +git checkout ${VER} +echo "Verifying sources..." +pushd mp4parse +cargo test +popd +echo "Constructing C api header..." +pushd mp4parse_capi +cargo build +echo "Verifying sources..." +cargo test +popd +popd +rm -rf mp4parse +mkdir -p mp4parse/src +cp _upstream/mp4parse/mp4parse/Cargo.toml mp4parse/ +cp _upstream/mp4parse/mp4parse/src/*.rs mp4parse/src/ +mkdir -p mp4parse/tests +cp _upstream/mp4parse/mp4parse/tests/*.rs mp4parse/tests/ +cp _upstream/mp4parse/mp4parse/tests/*.mp4 mp4parse/tests/ +rm -rf mp4parse_capi +mkdir -p mp4parse_capi/src +cp _upstream/mp4parse/mp4parse_capi/Cargo.toml mp4parse_capi/ +cp _upstream/mp4parse/mp4parse_capi/build.rs mp4parse_capi/ +cp _upstream/mp4parse/mp4parse_capi/include/mp4parse.h include/ +cp _upstream/mp4parse/mp4parse_capi/src/*.rs mp4parse_capi/src/ + +echo "Applying patches..." +patch -p4 < mp4parse-cargo.patch + +echo "Cleaning up..." +rm -rf _upstream + +echo "Updating gecko Cargo.lock..." +pushd ../../../toolkit/library/rust/ +cargo update --package mp4parse_capi +popd +pushd ../../../toolkit/library/gtest/rust/ +cargo update --package mp4parse_capi +popd + +echo "Updated to ${VER}." diff --git a/media/libstagefright/checkout.sh b/media/libstagefright/checkout.sh new file mode 100755 index 000000000..42dfb87fd --- /dev/null +++ b/media/libstagefright/checkout.sh @@ -0,0 +1,52 @@ +#!/bin/bash -e +set -o pipefail +abort () { + errcode=$? + echo "Error: That didn't work..." + exit $errcode +} +trap abort ERR + +cd `dirname "$0"` + +SITE=https://android.googlesource.com/platform +for TAGFILE in `find patches -name \*.tag` +do + REPO=${TAGFILE:8:-4} + DEST=android/${REPO} + if [[ ! -e ${DEST} ]] + then + mkdir -p `dirname ${DEST}` + echo Cloning from ${SITE}/${REPO} + git clone ${SITE}/${REPO} ${DEST} + fi + + rm -fR ${REPO} + TAG=`cat $TAGFILE` + (cd $DEST && git reset --hard 2>&1 && git checkout ${TAG} 2>&1) > /dev/null +done + +FILES=`python files.py` +HEADERS=`cat additional_headers` +for FILE in $FILES $HEADERS frameworks/av/media/libstagefright/include/AMRExtractor.h +do + echo Copying ${FILE} + mkdir -p `dirname ${FILE}` + cp android/${FILE} ${FILE} +done + +for PATCH in `find patches -name \*.patch` +do + REPO=${PATCH:8:-6} + echo Patching repo ${REPO} + for FILE in `grep -- '--- a/' ${PATCH} | colrm 1 6` + do + if [[ ! -e ${FILE} ]] + then + echo Copying ${REPO}/${FILE} + mkdir -p `dirname ${REPO}/${FILE}` + cp android/${REPO}/${FILE} ${REPO}/${FILE} + fi + done + (cd ${REPO} && patch -p1 || true) < $PATCH +done diff --git a/media/libstagefright/files.py b/media/libstagefright/files.py new file mode 100644 index 000000000..4d6775fa4 --- /dev/null +++ b/media/libstagefright/files.py @@ -0,0 +1,29 @@ +#!/usr/bin/python + +import os +import sys + +DEFINES={} +CONFIG={} +CXXFLAGS=[] +CONFIG['_MSC_VER'] = 0 +CONFIG['OS_TARGET'] = 0 + +class Exports(object): + def __init__(self): + self.mp4_demuxer=[] + +EXPORTS=Exports() + +SOURCES=[] +UNIFIED_SOURCES=[] +LOCAL_INCLUDES=[] + +try: + execfile('moz.build') +except: + sys.exit(1) + +for f in SOURCES+UNIFIED_SOURCES: + if not f.startswith('binding/'): + print f diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h new file mode 100644 index 000000000..f6b30b0e8 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/DataSource.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#ifndef DATA_SOURCE_H_ + +#define DATA_SOURCE_H_ + +#include <sys/types.h> + +#include <media/stagefright/MediaErrors.h> +#include <utils/RefBase.h> + +namespace stagefright { + +class String8; + +class DataSource : public RefBase { +public: + enum Flags { + kWantsPrefetching = 1, + kStreamedFromLocalHost = 2, + kIsCachingDataSource = 4, + kIsHTTPBasedSource = 8, + }; + + DataSource() {} + + virtual status_t initCheck() const = 0; + + virtual ssize_t readAt(off64_t offset, void *data, size_t size) = 0; + + // Convenience methods: + bool getUInt16(off64_t offset, uint16_t *x); + bool getUInt24(off64_t offset, uint32_t *x); // 3 byte int, returned as a 32-bit int + bool getUInt32(off64_t offset, uint32_t *x); + bool getUInt64(off64_t offset, uint64_t *x); + + // May return ERROR_UNSUPPORTED. + virtual status_t getSize(off64_t *size); + + virtual uint32_t flags() { + return 0; + } + + virtual status_t reconnectAtOffset(off64_t offset) { + return ERROR_UNSUPPORTED; + } + +#if 0 + //////////////////////////////////////////////////////////////////////////// + + bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta); + + // The sniffer can optionally fill in "meta" with an AMessage containing + // a dictionary of values that helps the corresponding extractor initialize + // its state without duplicating effort already exerted by the sniffer. + typedef bool (*SnifferFunc)( + const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *meta); + + static void RegisterSniffer(SnifferFunc func); + static void RegisterDefaultSniffers(); + + // for DRM + virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL) { + return NULL; + } + virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {}; + + virtual String8 getUri() { + return String8(); + } +#endif + + virtual String8 getMIMEType() const; + +protected: + virtual ~DataSource() {} + +private: + DataSource(const DataSource &); + DataSource &operator=(const DataSource &); +}; + +} // namespace stagefright + +#endif // DATA_SOURCE_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h new file mode 100644 index 000000000..ffbfcfc79 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaBuffer.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#ifndef MEDIA_BUFFER_H_ + +#define MEDIA_BUFFER_H_ + +#include <pthread.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include "nsTArray.h" + +namespace stagefright { + +struct ABuffer; +class GraphicBuffer; +class MediaBuffer; +class MediaBufferObserver; +class MetaData; + +class MediaBufferObserver { +public: + MediaBufferObserver() {} + virtual ~MediaBufferObserver() {} + + virtual void signalBufferReturned(MediaBuffer *buffer) = 0; + +private: + MediaBufferObserver(const MediaBufferObserver &); + MediaBufferObserver &operator=(const MediaBufferObserver &); +}; + +class MediaBuffer { +public: + // The underlying data remains the responsibility of the caller! + MediaBuffer(void *data, size_t size); + + MediaBuffer(size_t size); + + MediaBuffer(const sp<GraphicBuffer>& graphicBuffer); + + MediaBuffer(const sp<ABuffer> &buffer); + + // Decrements the reference count and deletes it if the reference + // count drops to 0. + void release(); + + // Increments the reference count. + void add_ref(); + + void *data() const; + size_t size() const; + + size_t range_offset() const; + size_t range_length() const; + + void set_range(size_t offset, size_t length); + + sp<GraphicBuffer> graphicBuffer() const; + + sp<MetaData> meta_data(); + + // Clears meta data and resets the range to the full extent. + void reset(); + + void setObserver(MediaBufferObserver *group); + + // Returns a clone of this MediaBuffer increasing its reference count. + // The clone references the same data but has its own range and + // MetaData. + MediaBuffer *clone(); + + int refcount() const; + + bool ensuresize(size_t length); + +protected: + virtual ~MediaBuffer(); + +private: + friend class MediaBufferGroup; + friend class OMXDecoder; + + // For use by OMXDecoder, reference count must be 1, drop reference + // count to 0 without signalling the observer. + void claim(); + + MediaBufferObserver *mObserver; + MediaBuffer *mNextBuffer; + int mRefCount; + + void *mData; + size_t mSize, mRangeOffset, mRangeLength; + sp<GraphicBuffer> mGraphicBuffer; + sp<ABuffer> mBuffer; + + bool mOwnsData; + + sp<MetaData> mMetaData; + + MediaBuffer *mOriginal; + + void setNextBuffer(MediaBuffer *buffer); + MediaBuffer *nextBuffer(); + + MediaBuffer(const MediaBuffer &); + MediaBuffer &operator=(const MediaBuffer &); + + FallibleTArray<uint8_t> mBufferBackend; +}; + +} // namespace stagefright + +#endif // MEDIA_BUFFER_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h new file mode 100644 index 000000000..7ac6db8d5 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaDefs.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef MEDIA_DEFS_H_ + +#define MEDIA_DEFS_H_ + +namespace stagefright { + +extern const char *MEDIA_MIMETYPE_IMAGE_JPEG; + +extern const char *MEDIA_MIMETYPE_VIDEO_VP6; +extern const char *MEDIA_MIMETYPE_VIDEO_VP8; +extern const char *MEDIA_MIMETYPE_VIDEO_VP9; +extern const char *MEDIA_MIMETYPE_VIDEO_AVC; +extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4; +extern const char *MEDIA_MIMETYPE_VIDEO_H263; +extern const char *MEDIA_MIMETYPE_VIDEO_MPEG2; +extern const char *MEDIA_MIMETYPE_VIDEO_RAW; + +extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB; +extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB; +extern const char *MEDIA_MIMETYPE_AUDIO_MPEG; // layer III +extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I; +extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II; +extern const char *MEDIA_MIMETYPE_AUDIO_AAC; +extern const char *MEDIA_MIMETYPE_AUDIO_QCELP; +extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS; +extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW; +extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW; +extern const char *MEDIA_MIMETYPE_AUDIO_RAW; +extern const char *MEDIA_MIMETYPE_AUDIO_FLAC; +extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; +extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM; + +extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; +extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; +extern const char *MEDIA_MIMETYPE_CONTAINER_OGG; +extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA; +extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS; +extern const char *MEDIA_MIMETYPE_CONTAINER_AVI; +extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS; + +extern const char *MEDIA_MIMETYPE_CONTAINER_WVM; + +extern const char *MEDIA_MIMETYPE_TEXT_3GPP; +extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP; + +} // namespace stagefright + +#endif // MEDIA_DEFS_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h new file mode 100644 index 000000000..df2c1a50b --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaErrors.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef MEDIA_ERRORS_H_ + +#define MEDIA_ERRORS_H_ + +#include <utils/Errors.h> + +namespace stagefright { + +#define MEDIA_ERROR_BASE (-1000) + +#define ERROR_ALREADY_CONNECTED (MEDIA_ERROR_BASE) +#define ERROR_NOT_CONNECTED (MEDIA_ERROR_BASE - 1) +#define ERROR_UNKNOWN_HOST (MEDIA_ERROR_BASE - 2) +#define ERROR_CANNOT_CONNECT (MEDIA_ERROR_BASE - 3) +#define ERROR_IO (MEDIA_ERROR_BASE - 4) +#define ERROR_CONNECTION_LOST (MEDIA_ERROR_BASE - 5) +#define ERROR_MALFORMED (MEDIA_ERROR_BASE - 7) +#define ERROR_OUT_OF_RANGE (MEDIA_ERROR_BASE - 8) +#define ERROR_BUFFER_TOO_SMALL (MEDIA_ERROR_BASE - 9) +#define ERROR_UNSUPPORTED (MEDIA_ERROR_BASE - 10) +#define ERROR_END_OF_STREAM (MEDIA_ERROR_BASE - 11) + +// Not technically an error. +#define INFO_FORMAT_CHANGED (MEDIA_ERROR_BASE - 12) +#define INFO_DISCONTINUITY (MEDIA_ERROR_BASE - 13) +#define INFO_OUTPUT_BUFFERS_CHANGED (MEDIA_ERROR_BASE - 14) + +// The following constant values should be in sync with +// drm/drm_framework_common.h +#define DRM_ERROR_BASE (-2000) + +#define ERROR_DRM_UNKNOWN (DRM_ERROR_BASE) +#define ERROR_DRM_NO_LICENSE (DRM_ERROR_BASE - 1) +#define ERROR_DRM_LICENSE_EXPIRED (DRM_ERROR_BASE - 2) +#define ERROR_DRM_SESSION_NOT_OPENED (DRM_ERROR_BASE - 3) +#define ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED (DRM_ERROR_BASE - 4) +#define ERROR_DRM_DECRYPT (DRM_ERROR_BASE - 5) +#define ERROR_DRM_CANNOT_HANDLE (DRM_ERROR_BASE - 6) +#define ERROR_DRM_TAMPER_DETECTED (DRM_ERROR_BASE - 7) +#define ERROR_DRM_NOT_PROVISIONED (DRM_ERROR_BASE - 8) +#define ERROR_DRM_DEVICE_REVOKED (DRM_ERROR_BASE - 9) +#define ERROR_DRM_RESOURCE_BUSY (DRM_ERROR_BASE - 10) + +#define ERROR_DRM_VENDOR_MAX (DRM_ERROR_BASE - 500) +#define ERROR_DRM_VENDOR_MIN (DRM_ERROR_BASE - 999) + +// Heartbeat Error Codes +#define HEARTBEAT_ERROR_BASE (-3000) +#define ERROR_HEARTBEAT_TERMINATE_REQUESTED (HEARTBEAT_ERROR_BASE) + +} // namespace stagefright + +#endif // MEDIA_ERRORS_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h new file mode 100644 index 000000000..25ed8e18b --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaExtractor.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef MEDIA_EXTRACTOR_H_ + +#define MEDIA_EXTRACTOR_H_ + +#include <utils/RefBase.h> + +namespace stagefright { + +class DataSource; +struct MediaSource; +class MetaData; + +class MediaExtractor : public RefBase { +public: + static sp<MediaExtractor> Create( + const sp<DataSource> &source, const char *mime = NULL); + + virtual size_t countTracks() = 0; + virtual sp<MediaSource> getTrack(size_t index) = 0; + + enum GetTrackMetaDataFlags { + kIncludeExtensiveMetaData = 1 + }; + virtual sp<MetaData> getTrackMetaData( + size_t index, uint32_t flags = 0) = 0; + + // Return container specific meta-data. The default implementation + // returns an empty metadata object. + virtual sp<MetaData> getMetaData() = 0; + + enum Flags { + CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" + CAN_SEEK_FORWARD = 2, // the "seek 10secs forward button" + CAN_PAUSE = 4, + CAN_SEEK = 8, // the "seek bar" + }; + + // If subclasses do _not_ override this, the default is + // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE + virtual uint32_t flags() const = 0; + + // for DRM + void setDrmFlag(bool flag) { + mIsDrm = flag; + }; + bool getDrmFlag() { + return mIsDrm; + } + virtual char* getDrmTrackInfo(size_t trackID, int *len) { + return NULL; + } + +protected: + MediaExtractor() : mIsDrm(false) {} + virtual ~MediaExtractor() {} + +private: + bool mIsDrm; + + MediaExtractor(const MediaExtractor &); + MediaExtractor &operator=(const MediaExtractor &); +}; + +} // namespace stagefright + +#endif // MEDIA_EXTRACTOR_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h new file mode 100644 index 000000000..913fca6d9 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef MEDIA_SOURCE_H_ + +#define MEDIA_SOURCE_H_ + +#include <sys/types.h> + +#include <media/stagefright/MediaErrors.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> +#include "nsTArray.h" + +namespace stagefright { + +class MediaBuffer; +class MetaData; + +struct MediaSource : public virtual RefBase { + MediaSource(); + + // Returns the format of the data output by this media source. + virtual sp<MetaData> getFormat() = 0; + + struct Indice + { + uint64_t start_offset; + uint64_t end_offset; + uint64_t start_composition; + uint64_t end_composition; + uint64_t start_decode; + bool sync; + }; + + virtual nsTArray<Indice> exportIndex() = 0; + +protected: + virtual ~MediaSource(); + +private: + MediaSource(const MediaSource &) = delete; + MediaSource &operator=(const MediaSource &) = delete; +}; + +} // namespace stagefright + +#endif // MEDIA_SOURCE_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h new file mode 100644 index 000000000..3cd6a22b4 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h @@ -0,0 +1,275 @@ +/* + * 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. + */ + +#ifndef META_DATA_H_ + +#define META_DATA_H_ + +#include <sys/types.h> + +#include <stdint.h> + +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> + +namespace stagefright { + +// The following keys map to int32_t data unless indicated otherwise. +enum { + kKeyMIMEType = 'mime', // cstring + kKeyWidth = 'widt', // int32_t, image pixel + kKeyHeight = 'heig', // int32_t, image pixel + kKeyDisplayWidth = 'dWid', // int32_t, display/presentation + kKeyDisplayHeight = 'dHgt', // int32_t, display/presentation + kKeySARWidth = 'sarW', // int32_t, sampleAspectRatio width + kKeySARHeight = 'sarH', // int32_t, sampleAspectRatio height + + // a rectangle, if absent assumed to be (0, 0, width - 1, height - 1) + kKeyCropRect = 'crop', + + kKeyRotation = 'rotA', // int32_t (angle in degrees) + kKeyIFramesInterval = 'ifiv', // int32_t + kKeyStride = 'strd', // int32_t + kKeySliceHeight = 'slht', // int32_t + kKeyChannelCount = '#chn', // int32_t + kKeyChannelMask = 'chnm', // int32_t + kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz) + kKeySampleSize = 'ssiz', // int32_t (sample size in bits) + kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) + kKeyBitRate = 'brte', // int32_t (bps) + kKeyESDS = 'esds', // raw data + kKeyAACProfile = 'aacp', // int32_t + kKeyAVCC = 'avcc', // raw data + kKeyD263 = 'd263', // raw data + kKeyVorbisInfo = 'vinf', // raw data + kKeyVorbisBooks = 'vboo', // raw data + kKeyWantsNALFragments = 'NALf', + kKeyIsSyncFrame = 'sync', // int32_t (bool) + kKeyIsCodecConfig = 'conf', // int32_t (bool) + kKeyTime = 'time', // int64_t (usecs) + kKeyDecodingTime = 'decT', // int64_t (decoding timestamp in usecs) + kKeyNTPTime = 'ntpT', // uint64_t (ntp-timestamp) + kKeyTargetTime = 'tarT', // int64_t (usecs) + kKeyDriftTime = 'dftT', // int64_t (usecs) + kKeyAnchorTime = 'ancT', // int64_t (usecs) + kKeyDuration = 'dura', // int64_t (usecs) + kKeyMovieDuration = 'mdur', // int64_t (usecs) + kKeyColorFormat = 'colf', + kKeyPlatformPrivate = 'priv', // pointer + kKeyDecoderComponent = 'decC', // cstring + kKeyBufferID = 'bfID', + kKeyMaxInputSize = 'inpS', + kKeyThumbnailTime = 'thbT', // int64_t (usecs) + kKeyTrackID = 'trID', + kKeyIsDRM = 'idrm', // int32_t (bool) + kKeyEncoderDelay = 'encd', // int32_t (frames) + kKeyEncoderPadding = 'encp', // int32_t (frames) + kKeyMediaTime = 'mtme', // int64_t (usecs) + + kKeyAlbum = 'albu', // cstring + kKeyArtist = 'arti', // cstring + kKeyAlbumArtist = 'aart', // cstring + kKeyComposer = 'comp', // cstring + kKeyGenre = 'genr', // cstring + kKeyTitle = 'titl', // cstring + kKeyYear = 'year', // cstring + kKeyAlbumArt = 'albA', // compressed image data + kKeyAlbumArtMIME = 'alAM', // cstring + kKeyAuthor = 'auth', // cstring + kKeyCDTrackNumber = 'cdtr', // cstring + kKeyDiscNumber = 'dnum', // cstring + kKeyDate = 'date', // cstring + kKeyWriter = 'writ', // cstring + kKeyCompilation = 'cpil', // cstring + kKeyLocation = 'loc ', // cstring + kKeyTimeScale = 'tmsl', // int32_t + + // video profile and level + kKeyVideoProfile = 'vprf', // int32_t + kKeyVideoLevel = 'vlev', // int32_t + + // Set this key to enable authoring files in 64-bit offset + kKey64BitFileOffset = 'fobt', // int32_t (bool) + kKey2ByteNalLength = '2NAL', // int32_t (bool) + + // Identify the file output format for authoring + // Please see <media/mediarecorder.h> for the supported + // file output formats. + kKeyFileType = 'ftyp', // int32_t + + // Track authoring progress status + // kKeyTrackTimeStatus is used to track progress in elapsed time + kKeyTrackTimeStatus = 'tktm', // int64_t + + kKeyRealTimeRecording = 'rtrc', // bool (int32_t) + kKeyNumBuffers = 'nbbf', // int32_t + + // Ogg files can be tagged to be automatically looping... + kKeyAutoLoop = 'autL', // bool (int32_t) + + kKeyValidSamples = 'valD', // int32_t + + kKeyIsUnreadable = 'unre', // bool (int32_t) + + // An indication that a video buffer has been rendered. + kKeyRendered = 'rend', // bool (int32_t) + + // The language code for this media + kKeyMediaLanguage = 'lang', // cstring + + // To store the timed text format data + kKeyTextFormatData = 'text', // raw data + + kKeyRequiresSecureBuffers = 'secu', // bool (int32_t) + + kKeyIsADTS = 'adts', // bool (int32_t) + kKeyAACAOT = 'aaot', // int32_t + + // If a MediaBuffer's data represents (at least partially) encrypted + // data, the following fields aid in decryption. + // The data can be thought of as pairs of plain and encrypted data + // fragments, i.e. plain and encrypted data alternate. + // The first fragment is by convention plain data (if that's not the + // case, simply specify plain fragment size of 0). + // kKeyEncryptedSizes and kKeyPlainSizes each map to an array of + // size_t values. The sum total of all size_t values of both arrays + // must equal the amount of data (i.e. MediaBuffer's range_length()). + // If both arrays are present, they must be of the same size. + // If only encrypted sizes are present it is assumed that all + // plain sizes are 0, i.e. all fragments are encrypted. + // To programmatically set these array, use the MetaData::setData API, i.e. + // const size_t encSizes[]; + // meta->setData( + // kKeyEncryptedSizes, 0 /* type */, encSizes, sizeof(encSizes)); + // A plain sizes array by itself makes no sense. + kKeyEncryptedSizes = 'encr', // size_t[] + kKeyPlainSizes = 'plai', // size_t[] + kKeyCryptoKey = 'cryK', // uint8_t[16] + kKeyCryptoIV = 'cryI', // uint8_t[16] + kKeyCryptoMode = 'cryM', // int32_t + + kKeyCryptoDefaultIVSize = 'cryS', // int32_t + + kKeyPssh = 'pssh', // raw data +}; + +enum { + kTypeESDS = 'esds', + kTypeAVCC = 'avcc', + kTypeD263 = 'd263', +}; + +class MetaData : public RefBase { +public: + MetaData(); + MetaData(const MetaData &from); + + enum Type { + TYPE_NONE = 'none', + TYPE_C_STRING = 'cstr', + TYPE_INT32 = 'in32', + TYPE_INT64 = 'in64', + TYPE_FLOAT = 'floa', + TYPE_POINTER = 'ptr ', + TYPE_RECT = 'rect', + }; + + void clear(); + bool remove(uint32_t key); + + bool setCString(uint32_t key, const char *value); + bool setInt32(uint32_t key, int32_t value); + bool setInt64(uint32_t key, int64_t value); + bool setFloat(uint32_t key, float value); + bool setPointer(uint32_t key, void *value); + + bool setRect( + uint32_t key, + int32_t left, int32_t top, + int32_t right, int32_t bottom); + + bool findCString(uint32_t key, const char **value) const; + bool findInt32(uint32_t key, int32_t *value) const; + bool findInt64(uint32_t key, int64_t *value) const; + bool findFloat(uint32_t key, float *value) const; + bool findPointer(uint32_t key, void **value) const; + + bool findRect( + uint32_t key, + int32_t *left, int32_t *top, + int32_t *right, int32_t *bottom) const; + + bool setData(uint32_t key, uint32_t type, const void *data, size_t size); + + bool findData(uint32_t key, uint32_t *type, + const void **data, size_t *size) const; + + void dumpToLog() const; + +protected: + virtual ~MetaData(); + +private: + struct typed_data { + typed_data(); + ~typed_data(); + + typed_data(const MetaData::typed_data &); + typed_data &operator=(const MetaData::typed_data &); + + void clear(); + void setData(uint32_t type, const void *data, size_t size); + void getData(uint32_t *type, const void **data, size_t *size) const; + String8 asString() const; + + private: + uint32_t mType; + size_t mSize; + + union { + void *ext_data; + float reservoir; + } u; + + bool usesReservoir() const { + return mSize <= sizeof(u.reservoir); + } + + bool allocateStorage(size_t size); + void freeStorage(); + + void *storage() { + return usesReservoir() ? &u.reservoir : u.ext_data; + } + + const void *storage() const { + return usesReservoir() ? &u.reservoir : u.ext_data; + } + }; + + struct Rect { + int32_t mLeft, mTop, mRight, mBottom; + }; + + KeyedVector<uint32_t, typed_data> mItems; + + // MetaData &operator=(const MetaData &); +}; + +} // namespace stagefright + +#endif // META_DATA_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h b/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h new file mode 100644 index 000000000..ae2e6efa8 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/Utils.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef UTILS_H_ + +#define UTILS_H_ + +#include <media/stagefright/foundation/AString.h> +#include <stdint.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <system/audio.h> +#include <media/MediaPlayerInterface.h> + +namespace stagefright { + +#define FOURCC(c1, c2, c3, c4) \ + ((uint32_t) c1 << 24 | c2 << 16 | c3 << 8 | c4) + +uint16_t U16_AT(const uint8_t *ptr); +uint32_t U32_AT(const uint8_t *ptr); +uint64_t U64_AT(const uint8_t *ptr); + +uint16_t U16LE_AT(const uint8_t *ptr); +uint32_t U32LE_AT(const uint8_t *ptr); +uint64_t U64LE_AT(const uint8_t *ptr); + +uint64_t ntoh64(uint64_t x); +uint64_t hton64(uint64_t x); + +class MetaData; +struct AMessage; +status_t convertMetaDataToMessage( + const sp<MetaData> &meta, sp<AMessage> *format); +void convertMessageToMetaData( + const sp<AMessage> &format, sp<MetaData> &meta); + +AString MakeUserAgent(); + +} // namespace stagefright + +#endif // UTILS_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h new file mode 100644 index 000000000..c20ea07b0 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AAtomizer.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef A_ATOMIZER_H_ + +#define A_ATOMIZER_H_ + +#include <stdint.h> + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AString.h> +#include <utils/List.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace stagefright { + +struct AAtomizer { + static const char *Atomize(const char *name); + +private: + static AAtomizer gAtomizer; + + Mutex mLock; + Vector<List<AString> > mAtoms; + + AAtomizer(); + + const char *atomize(const char *name); + + static uint32_t Hash(const char *s); + + DISALLOW_EVIL_CONSTRUCTORS(AAtomizer); +}; + +} // namespace stagefright + +#endif // A_ATOMIZER_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h new file mode 100644 index 000000000..9eceea3fd --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABase.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#ifndef A_BASE_H_ + +#define A_BASE_H_ + +#define DISALLOW_EVIL_CONSTRUCTORS(name) \ + name(const name &); \ + name &operator=(const name &) + +#endif // A_BASE_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h new file mode 100644 index 000000000..589e5fe92 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABitReader.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef A_BIT_READER_H_ + +#define A_BIT_READER_H_ + +#include <media/stagefright/foundation/ABase.h> + +#include <sys/types.h> +#include <stdint.h> + +namespace stagefright { + +struct ABitReader { + ABitReader(const uint8_t *data, size_t size); + + uint32_t getBits(size_t n); + void skipBits(size_t n); + + void putBits(uint32_t x, size_t n); + + size_t numBitsLeft() const; + + const uint8_t *data() const; + +private: + const uint8_t *mData; + size_t mSize; + + uint32_t mReservoir; // left-aligned bits + size_t mNumBitsLeft; + + void fillReservoir(); + + DISALLOW_EVIL_CONSTRUCTORS(ABitReader); +}; + +} // namespace stagefright + +#endif // A_BIT_READER_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h new file mode 100644 index 000000000..4fa908e93 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ABuffer.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef A_BUFFER_H_ + +#define A_BUFFER_H_ + +#include <sys/types.h> +#include <stdint.h> + +#include <media/stagefright/foundation/ABase.h> +#include <utils/RefBase.h> + +namespace stagefright { + +struct AMessage; + +struct ABuffer : public RefBase { + ABuffer(size_t capacity); + ABuffer(void *data, size_t capacity); + + void setFarewellMessage(const sp<AMessage> msg); + + uint8_t *base() { return (uint8_t *)mData; } + uint8_t *data() { return (uint8_t *)mData + mRangeOffset; } + size_t capacity() const { return mCapacity; } + size_t size() const { return mRangeLength; } + size_t offset() const { return mRangeOffset; } + + void setRange(size_t offset, size_t size); + + void setInt32Data(int32_t data) { mInt32Data = data; } + int32_t int32Data() const { return mInt32Data; } + + sp<AMessage> meta(); + +protected: + virtual ~ABuffer(); + +private: + sp<AMessage> mFarewell; + sp<AMessage> mMeta; + + void *mData; + size_t mCapacity; + size_t mRangeOffset; + size_t mRangeLength; + + int32_t mInt32Data; + + bool mOwnsData; + + DISALLOW_EVIL_CONSTRUCTORS(ABuffer); +}; + +} // namespace stagefright + +#endif // A_BUFFER_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h new file mode 100644 index 000000000..a58d951d5 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/ADebug.h @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#ifndef A_DEBUG_H_ + +#define A_DEBUG_H_ + +#include <string.h> + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AString.h> +#include <utils/Log.h> + +namespace stagefright { + +#define LITERAL_TO_STRING_INTERNAL(x) #x +#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x) + +#define CHECK(condition) \ + LOG_ALWAYS_FATAL_IF( \ + !(condition), \ + "%s", \ + __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ + " CHECK(" #condition ") failed.") + +#define MAKE_COMPARATOR(suffix,op) \ + template<class A, class B> \ + AString Compare_##suffix(const A &a, const B &b) { \ + AString res; \ + if (!(a op b)) { \ + res.append(a); \ + res.append(" vs. "); \ + res.append(b); \ + } \ + return res; \ + } + +MAKE_COMPARATOR(EQ,==) +MAKE_COMPARATOR(NE,!=) +MAKE_COMPARATOR(LE,<=) +MAKE_COMPARATOR(GE,>=) +MAKE_COMPARATOR(LT,<) +MAKE_COMPARATOR(GT,>) + +#define CHECK_OP(x,y,suffix,op) \ + do { \ + AString ___res = Compare_##suffix(x, y); \ + if (!___res.empty()) { \ + AString ___full = \ + __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ + " CHECK_" #suffix "( " #x "," #y ") failed: "; \ + ___full.append(___res); \ + \ + LOG_ALWAYS_FATAL("%s", ___full.c_str()); \ + } \ + } while (false) + +#define CHECK_EQ(x,y) CHECK_OP(x,y,EQ,==) +#define CHECK_NE(x,y) CHECK_OP(x,y,NE,!=) +#define CHECK_LE(x,y) CHECK_OP(x,y,LE,<=) +#define CHECK_LT(x,y) CHECK_OP(x,y,LT,<) +#define CHECK_GE(x,y) CHECK_OP(x,y,GE,>=) +#define CHECK_GT(x,y) CHECK_OP(x,y,GT,>) + +#define TRESPASS() \ + LOG_ALWAYS_FATAL( \ + __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ + " Should not be here."); + +} // namespace stagefright + +#endif // A_DEBUG_H_ + diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h new file mode 100644 index 000000000..5a5ecc203 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AHandler.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef A_HANDLER_H_ + +#define A_HANDLER_H_ + +#include <media/stagefright/foundation/ALooper.h> +#include <utils/RefBase.h> + +namespace stagefright { + +struct AMessage; + +struct AHandler : public RefBase { + AHandler() + : mID(0) { + } + + ALooper::handler_id id() const { + return mID; + } + + sp<ALooper> looper(); + +protected: + virtual void onMessageReceived(const sp<AMessage> &msg) = 0; + +private: + friend struct ALooperRoster; + + ALooper::handler_id mID; + + void setID(ALooper::handler_id id) { + mID = id; + } + + DISALLOW_EVIL_CONSTRUCTORS(AHandler); +}; + +} // namespace stagefright + +#endif // A_HANDLER_H_ diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h new file mode 100644 index 000000000..8f97ed4e2 --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/AString.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef A_STRING_H_ + +#define A_STRING_H_ + +#include <sys/types.h> + +namespace stagefright { + +struct AString { + AString(); + AString(const char *s); + AString(const char *s, size_t size); + AString(const AString &from); + AString(const AString &from, size_t offset, size_t n); + ~AString(); + + AString &operator=(const AString &from); + void setTo(const char *s); + void setTo(const char *s, size_t size); + void setTo(const AString &from, size_t offset, size_t n); + + size_t size() const; + const char *c_str() const; + + bool empty() const; + + void clear(); + void trim(); + void erase(size_t start, size_t n); + + void append(char c) { append(&c, 1); } + void append(const char *s); + void append(const char *s, size_t size); + void append(const AString &from); + void append(const AString &from, size_t offset, size_t n); + void append(int x); + void append(unsigned x); + void append(long x); + void append(unsigned long x); + void append(long long x); + void append(unsigned long long x); + void append(float x); + void append(double x); + void append(void *x); + + void insert(const AString &from, size_t insertionPos); + void insert(const char *from, size_t size, size_t insertionPos); + + ssize_t find(const char *substring, size_t start = 0) const; + + size_t hash() const; + + bool operator==(const AString &other) const; + bool operator<(const AString &other) const; + bool operator>(const AString &other) const; + + int compare(const AString &other) const; + + bool startsWith(const char *prefix) const; + bool endsWith(const char *suffix) const; + + void tolower(); + +private: + static const char *kEmptyString; + + char *mData; + size_t mSize; + size_t mAllocSize; + + void makeMutable(); +}; + +AString StringPrintf(const char *format, ...); + +} // namespace stagefright + +#endif // A_STRING_H_ + diff --git a/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h new file mode 100644 index 000000000..18172f7eb --- /dev/null +++ b/media/libstagefright/frameworks/av/include/media/stagefright/foundation/hexdump.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef HEXDUMP_H_ + +#define HEXDUMP_H_ + +#include <sys/types.h> + +namespace stagefright { + +struct AString; + +void hexdump( + const void *_data, size_t size, + size_t indent = 0, AString *appendTo = NULL); + +} // namespace stagefright + +#endif // HEXDUMP_H_ diff --git a/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp b/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp new file mode 100644 index 000000000..c65a69e48 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/DataSource.cpp @@ -0,0 +1,219 @@ +/* + * 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. + */ + +#include "include/AMRExtractor.h" + +#if CHROMIUM_AVAILABLE +#include "include/chromium_http_stub.h" +#endif + +#include "include/MPEG4Extractor.h" + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaErrors.h> + +namespace stagefright { + +bool DataSource::getUInt16(off64_t offset, uint16_t *x) { + *x = 0; + + uint8_t byte[2]; + if (readAt(offset, byte, 2) != 2) { + return false; + } + + *x = (byte[0] << 8) | byte[1]; + + return true; +} + +bool DataSource::getUInt24(off64_t offset, uint32_t *x) { + *x = 0; + + uint8_t byte[3]; + if (readAt(offset, byte, 3) != 3) { + return false; + } + + *x = (byte[0] << 16) | (byte[1] << 8) | byte[2]; + + return true; +} + +bool DataSource::getUInt32(off64_t offset, uint32_t *x) { + *x = 0; + + uint32_t tmp; + if (readAt(offset, &tmp, 4) != 4) { + return false; + } + + *x = ntohl(tmp); + + return true; +} + +bool DataSource::getUInt64(off64_t offset, uint64_t *x) { + *x = 0; + + uint64_t tmp; + if (readAt(offset, &tmp, 8) != 8) { + return false; + } + + *x = ntoh64(tmp); + + return true; +} + +status_t DataSource::getSize(off64_t *size) { + *size = 0; + + return ERROR_UNSUPPORTED; +} + +//////////////////////////////////////////////////////////////////////////////// + +#if 0 + +Mutex DataSource::gSnifferMutex; +List<DataSource::SnifferFunc> DataSource::gSniffers; + +bool DataSource::sniff( + String8 *mimeType, float *confidence, sp<AMessage> *meta) { + *mimeType = ""; + *confidence = 0.0f; + meta->clear(); + + Mutex::Autolock autoLock(gSnifferMutex); + for (List<SnifferFunc>::iterator it = gSniffers.begin(); + it != gSniffers.end(); ++it) { + String8 newMimeType; + float newConfidence; + sp<AMessage> newMeta; + if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) { + if (newConfidence > *confidence) { + *mimeType = newMimeType; + *confidence = newConfidence; + *meta = newMeta; + } + } + } + + return *confidence > 0.0; +} + +// static +void DataSource::RegisterSniffer(SnifferFunc func) { + Mutex::Autolock autoLock(gSnifferMutex); + + for (List<SnifferFunc>::iterator it = gSniffers.begin(); + it != gSniffers.end(); ++it) { + if (*it == func) { + return; + } + } + + gSniffers.push_back(func); +} + +// static +void DataSource::RegisterDefaultSniffers() { + RegisterSniffer(SniffMPEG4); + RegisterSniffer(SniffMatroska); + RegisterSniffer(SniffOgg); + RegisterSniffer(SniffWAV); + RegisterSniffer(SniffFLAC); + RegisterSniffer(SniffAMR); + RegisterSniffer(SniffMPEG2TS); + RegisterSniffer(SniffMP3); + RegisterSniffer(SniffAAC); + RegisterSniffer(SniffMPEG2PS); + RegisterSniffer(SniffWVM); + + char value[PROPERTY_VALUE_MAX]; + if (property_get("drm.service.enabled", value, NULL) + && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { + RegisterSniffer(SniffDRM); + } +} + +// static +sp<DataSource> DataSource::CreateFromURI( + const char *uri, const KeyedVector<String8, String8> *headers) { + bool isWidevine = !strncasecmp("widevine://", uri, 11); + + sp<DataSource> source; + if (!strncasecmp("file://", uri, 7)) { + source = new FileSource(uri + 7); + } else if (!strncasecmp("http://", uri, 7) + || !strncasecmp("https://", uri, 8) + || isWidevine) { + sp<HTTPBase> httpSource = HTTPBase::Create(); + + String8 tmp; + if (isWidevine) { + tmp = String8("http://"); + tmp.append(uri + 11); + + uri = tmp.string(); + } + + if (httpSource->connect(uri, headers) != OK) { + return NULL; + } + + if (!isWidevine) { + String8 cacheConfig; + bool disconnectAtHighwatermark; + if (headers != NULL) { + KeyedVector<String8, String8> copy = *headers; + NuCachedSource2::RemoveCacheSpecificHeaders( + ©, &cacheConfig, &disconnectAtHighwatermark); + } + + source = new NuCachedSource2( + httpSource, + cacheConfig.isEmpty() ? NULL : cacheConfig.string()); + } else { + // We do not want that prefetching, caching, datasource wrapper + // in the widevine:// case. + source = httpSource; + } + +# if CHROMIUM_AVAILABLE + } else if (!strncasecmp("data:", uri, 5)) { + source = createDataUriSource(uri); +#endif + } else { + // Assume it's a filename. + source = new FileSource(uri); + } + + if (source == NULL || source->initCheck() != OK) { + return NULL; + } + + return source; +} + +#endif + +String8 DataSource::getMIMEType() const { + return String8("application/octet-stream"); +} + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp new file mode 100644 index 000000000..1e64fd505 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/ESDS.cpp @@ -0,0 +1,244 @@ +/* + * 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 +#undef LOG_TAG +#define LOG_TAG "ESDS" +#include <utils/Log.h> + +#include "include/ESDS.h" + +#include <string.h> + +namespace stagefright { + +ESDS::ESDS(const void *data, size_t size) + : mData(new (mozilla::fallible) uint8_t[size]), + mSize(size), + mInitCheck(NO_INIT), + mDecoderSpecificOffset(0), + mDecoderSpecificLength(0), + mObjectTypeIndication(0) { + if (!mData) { + mInitCheck = ERROR_BUFFER_TOO_SMALL; + return; + } + + 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=%d", *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) { + offset += 2; + if (size <= 2) { + return ERROR_MALFORMED; + } + size -= 2; + } + + if (URL_Flag) { + if (offset >= size) { + return ERROR_MALFORMED; + } + unsigned URLlength = mData[offset]; + offset += URLlength + 1; + if (size <= URLlength + 1) { + return ERROR_MALFORMED; + } + size -= URLlength + 1; + } + + if (OCRstreamFlag) { + offset += 2; + if (size <= 2) { + return ERROR_MALFORMED; + } + size -= 2; + + if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor) + && offset >= 2 + && 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::parseDecoderConfigDescriptor(size_t offset, size_t size) { + if (size < 13) { + return ERROR_MALFORMED; + } + + mObjectTypeIndication = mData[offset]; + + 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 stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp new file mode 100644 index 000000000..e881e0262 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -0,0 +1,2972 @@ +/* + * 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 +#undef LOG_TAG +#define LOG_TAG "MPEG4Extractor" +#include <utils/Log.h> + +#include "include/MPEG4Extractor.h" +#include "include/SampleTable.h" +#include "include/ESDS.h" + +#include <algorithm> +#include <ctype.h> +#include <limits> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <type_traits> + +#include <media/stagefright/foundation/ABitReader.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +static const uint32_t kMAX_ALLOCATION = + (SIZE_MAX < INT32_MAX ? SIZE_MAX : INT32_MAX) - 128; + +namespace stagefright { + +static const int64_t OVERFLOW_ERROR = -INT64_MAX; + +// Calculate units*1,000,000/hz, trying to avoid overflow. +// Return OVERFLOW_ERROR in case of unavoidable overflow, or div by hz==0. +int64_t unitsToUs(int64_t units, int64_t hz) { + if (hz == 0) { + return OVERFLOW_ERROR; + } + const int64_t MAX_S = INT64_MAX / 1000000; + if (std::abs(units) <= MAX_S) { + return units * 1000000 / hz; + } + // Hard case, avoid overflow-inducing 'units*1M' by calculating: + // (units / hz) * 1M + ((units % hz) * 1M) / hz. + // ^-- ^-- ^-- overflows still possible + int64_t units_div_hz = units / hz; + int64_t units_rem_hz = units % hz; + if (std::abs(units_div_hz) > MAX_S || std::abs(units_rem_hz) > MAX_S) { + return OVERFLOW_ERROR; + } + int64_t quot_us = units_div_hz * 1000000; + int64_t rem_us = (units_rem_hz * 1000000) / hz; + if (std::abs(quot_us) > INT64_MAX - std::abs(rem_us)) { + return OVERFLOW_ERROR; + } + return quot_us + rem_us; +} + +class MPEG4Source : public MediaSource { +public: + MPEG4Source(const sp<MetaData> &format, + uint32_t timeScale, + const sp<SampleTable> &sampleTable); + + sp<MetaData> getFormat() override; + + nsTArray<Indice> exportIndex() override; + +protected: + virtual ~MPEG4Source(); + +private: + sp<MetaData> mFormat; + uint32_t mTimescale; + sp<SampleTable> mSampleTable; + + MPEG4Source(const MPEG4Source &) = delete; + MPEG4Source &operator=(const MPEG4Source &) = delete; +}; + +// This custom data source wraps an existing one and satisfies requests +// falling entirely within a cached range from the cache while forwarding +// all remaining requests to the wrapped datasource. +// This is used to cache the full sampletable metadata for a single track, +// possibly wrapping multiple times to cover all tracks, i.e. +// Each MPEG4DataSource caches the sampletable metadata for a single track. + +struct MPEG4DataSource : public DataSource { + MPEG4DataSource(const sp<DataSource> &source); + + status_t initCheck() const override; + ssize_t readAt(off64_t offset, void *data, size_t size) override; + status_t getSize(off64_t *size) override; + uint32_t flags() override; + + status_t setCachedRange(off64_t offset, size_t size); + +protected: + virtual ~MPEG4DataSource(); + +private: + Mutex mLock; + + sp<DataSource> mSource; + off64_t mCachedOffset; + size_t mCachedSize; + uint8_t *mCache; + + void clearCache(); + + MPEG4DataSource(const MPEG4DataSource &) = delete; + MPEG4DataSource &operator=(const MPEG4DataSource &) = delete; +}; + +MPEG4DataSource::MPEG4DataSource(const sp<DataSource> &source) + : mSource(source), + mCachedOffset(0), + mCachedSize(0), + mCache(NULL) { +} + +MPEG4DataSource::~MPEG4DataSource() { + clearCache(); +} + +void MPEG4DataSource::clearCache() { + if (mCache) { + free(mCache); + mCache = NULL; + } + + mCachedOffset = 0; + mCachedSize = 0; +} + +status_t MPEG4DataSource::initCheck() const { + return mSource->initCheck(); +} + +ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) { + Mutex::Autolock autoLock(mLock); + + if (offset >= mCachedOffset + && offset + size <= mCachedOffset + mCachedSize) { + memcpy(data, &mCache[offset - mCachedOffset], size); + return size; + } + + return mSource->readAt(offset, data, size); +} + +status_t MPEG4DataSource::getSize(off64_t *size) { + return mSource->getSize(size); +} + +uint32_t MPEG4DataSource::flags() { + return mSource->flags(); +} + +status_t MPEG4DataSource::setCachedRange(off64_t offset, size_t size) { + Mutex::Autolock autoLock(mLock); + + clearCache(); + + mCache = (uint8_t *)malloc(size); + + if (mCache == NULL) { + return -ENOMEM; + } + + mCachedOffset = offset; + mCachedSize = size; + + ssize_t err = mSource->readAt(mCachedOffset, mCache, mCachedSize); + + if (err < (ssize_t)size) { + clearCache(); + + return ERROR_IO; + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +static void hexdump(const void *_data, size_t size) { + const uint8_t *data = (const uint8_t *)_data; + size_t offset = 0; + while (offset < size) { + printf("0x%04x ", offset); + + size_t n = size - offset; + if (n > 16) { + n = 16; + } + + for (size_t i = 0; i < 16; ++i) { + if (i == 8) { + printf(" "); + } + + if (offset + i < size) { + printf("%02x ", data[offset + i]); + } else { + printf(" "); + } + } + + printf(" "); + + for (size_t i = 0; i < n; ++i) { + if (isprint(data[offset + i])) { + printf("%c", data[offset + i]); + } else { + printf("."); + } + } + + printf("\n"); + + offset += 16; + } +} + +static const char *FourCC2MIME(uint32_t fourcc) { + switch (fourcc) { + case FOURCC('m', 'p', '4', 'a'): + return MEDIA_MIMETYPE_AUDIO_AAC; + + case FOURCC('s', 'a', 'm', 'r'): + return MEDIA_MIMETYPE_AUDIO_AMR_NB; + + case FOURCC('s', 'a', 'w', 'b'): + return MEDIA_MIMETYPE_AUDIO_AMR_WB; + + case FOURCC('.', 'm', 'p', '3'): + return MEDIA_MIMETYPE_AUDIO_MPEG; + + case FOURCC('m', 'p', '4', 'v'): + return MEDIA_MIMETYPE_VIDEO_MPEG4; + + case FOURCC('s', '2', '6', '3'): + case FOURCC('h', '2', '6', '3'): + case FOURCC('H', '2', '6', '3'): + return MEDIA_MIMETYPE_VIDEO_H263; + + case FOURCC('a', 'v', 'c', '1'): + case FOURCC('a', 'v', 'c', '3'): + return MEDIA_MIMETYPE_VIDEO_AVC; + + case FOURCC('V', 'P', '6', 'F'): + return MEDIA_MIMETYPE_VIDEO_VP6; + + default: + ALOGE("Unknown MIME type %08x", fourcc); + return NULL; + } +} + +static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t *rate) { + const char* mime = FourCC2MIME(fourcc); + if (!mime) { + return false; + } + if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { + // AMR NB audio is always mono, 8kHz + *channels = 1; + *rate = 8000; + return true; + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { + // AMR WB audio is always mono, 16kHz + *channels = 1; + *rate = 16000; + return true; + } + return false; +} + +MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source) + : mSidxDuration(0), + mDataSource(source), + mInitCheck(NO_INIT), + mHasVideo(false), + mHeaderTimescale(0), + mFirstTrack(NULL), + mLastTrack(NULL), + mFileMetaData(new MetaData), + mFirstSINF(NULL), + mIsDrm(false), + mDrmScheme(0) +{ +} + +MPEG4Extractor::~MPEG4Extractor() { + Track *track = mFirstTrack; + while (track) { + Track *next = track->next; + + delete track; + track = next; + } + mFirstTrack = mLastTrack = NULL; + + SINF *sinf = mFirstSINF; + while (sinf) { + SINF *next = sinf->next; + delete[] sinf->IPMPData; + delete sinf; + sinf = next; + } + mFirstSINF = NULL; + + for (size_t i = 0; i < mPssh.Length(); i++) { + delete [] mPssh[i].data; + } +} + +uint32_t MPEG4Extractor::flags() const { + return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK; +} + +sp<MetaData> MPEG4Extractor::getMetaData() { + status_t err; + if ((err = readMetaData()) != OK) { + return NULL; + } + + return mFileMetaData; +} + +size_t MPEG4Extractor::countTracks() { + status_t err; + if ((err = readMetaData()) != OK) { + ALOGV("MPEG4Extractor::countTracks: no tracks"); + return 0; + } + + size_t n = 0; + Track *track = mFirstTrack; + while (track) { + ++n; + track = track->next; + } + + ALOGV("MPEG4Extractor::countTracks: %d tracks", n); + return n; +} + +sp<MetaData> MPEG4Extractor::getTrackMetaData( + size_t index, uint32_t flags) { + status_t err; + if ((err = readMetaData()) != OK) { + return NULL; + } + + Track *track = mFirstTrack; + while (index > 0) { + if (track == NULL) { + return NULL; + } + + track = track->next; + --index; + } + + if (track == NULL) { + return NULL; + } + + return track->meta; +} + +static void MakeFourCCString(uint32_t x, char *s) { + s[0] = x >> 24; + s[1] = (x >> 16) & 0xff; + s[2] = (x >> 8) & 0xff; + s[3] = x & 0xff; + s[4] = '\0'; +} + +status_t MPEG4Extractor::readMetaData() { + if (mInitCheck != NO_INIT) { + return mInitCheck; + } + + off64_t offset = 0; + status_t err = NO_INIT; + while (!mFirstTrack) { + err = parseChunk(&offset, 0); + // The parseChunk function returns UNKNOWN_ERROR to skip + // some boxes we don't want to handle. Filter that error + // code but return others so e.g. I/O errors propagate. + if (err != OK && err != (status_t) UNKNOWN_ERROR) { + ALOGW("Error %d parsing chunck at offset %lld looking for first track", + err, (long long)offset); + break; + } + } + + if (mInitCheck == OK) { + if (mHasVideo) { + mFileMetaData->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4); + } else { + mFileMetaData->setCString(kKeyMIMEType, "audio/mp4"); + } + + mInitCheck = OK; + } else { + mInitCheck = err; + } + + CHECK_NE(err, (status_t)NO_INIT); + + // copy pssh data into file metadata + uint64_t psshsize = 0; + for (size_t i = 0; i < mPssh.Length(); i++) { + psshsize += 20 + mPssh[i].datalen; + if (mPssh[i].datalen > kMAX_ALLOCATION - 20 || + psshsize > kMAX_ALLOCATION) { + return ERROR_MALFORMED; + } + } + if (psshsize) { + char *buf = (char*)malloc(psshsize); + if (!buf) { + return -ENOMEM; + } + char *ptr = buf; + for (size_t i = 0; i < mPssh.Length(); i++) { + memcpy(ptr, mPssh[i].uuid, 20); // uuid + length + memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen); + ptr += (20 + mPssh[i].datalen); + } + mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize); + free(buf); + } + return mInitCheck; +} + +char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) { + if (mFirstSINF == NULL) { + return NULL; + } + + SINF *sinf = mFirstSINF; + while (sinf && (trackID != sinf->trackID)) { + sinf = sinf->next; + } + + if (sinf == NULL) { + return NULL; + } + + *len = sinf->len; + return sinf->IPMPData; +} + +// Reads an encoded integer 7 bits at a time until it encounters the high bit clear. +static int32_t readSize(off64_t offset, + const sp<DataSource> DataSource, uint8_t *numOfBytes) { + uint32_t size = 0; + uint8_t data; + bool moreData = true; + *numOfBytes = 0; + + while (moreData) { + if (DataSource->readAt(offset, &data, 1) < 1) { + return -1; + } + offset ++; + moreData = (data >= 128) ? true : false; + size = (size << 7) | (data & 0x7f); // Take last 7 bits + (*numOfBytes) ++; + } + + return size; +} + +status_t MPEG4Extractor::parseDrmSINF(off64_t *offset, off64_t data_offset) { + uint8_t updateIdTag; + if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) { + return ERROR_IO; + } + data_offset ++; + + if (0x01/*OBJECT_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) { + return ERROR_MALFORMED; + } + + uint8_t numOfBytes; + int32_t size = readSize(data_offset, mDataSource, &numOfBytes); + if (size < 0) { + return ERROR_IO; + } + int32_t classSize = size; + data_offset += numOfBytes; + + while(size >= 11 ) { + uint8_t descriptorTag; + if (mDataSource->readAt(data_offset, &descriptorTag, 1) < 1) { + return ERROR_IO; + } + data_offset ++; + + if (0x11/*OBJECT_DESCRIPTOR_ID_TAG*/ != descriptorTag) { + return ERROR_MALFORMED; + } + + uint8_t buffer[8]; + //ObjectDescriptorID and ObjectDescriptor url flag + if (mDataSource->readAt(data_offset, buffer, 2) < 2) { + return ERROR_IO; + } + data_offset += 2; + + if ((buffer[1] >> 5) & 0x0001) { //url flag is set + return ERROR_MALFORMED; + } + + if (mDataSource->readAt(data_offset, buffer, 8) < 8) { + return ERROR_IO; + } + data_offset += 8; + + if ((0x0F/*ES_ID_REF_TAG*/ != buffer[1]) + || ( 0x0A/*IPMP_DESCRIPTOR_POINTER_ID_TAG*/ != buffer[5])) { + return ERROR_MALFORMED; + } + + SINF *sinf = new SINF; + sinf->trackID = U16_AT(&buffer[3]); + sinf->IPMPDescriptorID = buffer[7]; + sinf->next = mFirstSINF; + mFirstSINF = sinf; + + size -= (8 + 2 + 1); + } + + if (size != 0) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt(data_offset, &updateIdTag, 1) < 1) { + return ERROR_IO; + } + data_offset ++; + + if(0x05/*IPMP_DESCRIPTOR_UPDATE_ID_TAG*/ != updateIdTag) { + return ERROR_MALFORMED; + } + + size = readSize(data_offset, mDataSource, &numOfBytes); + if (size < 0) { + return ERROR_IO; + } + classSize = size; + data_offset += numOfBytes; + + while (size > 0) { + uint8_t tag; + int32_t dataLen; + if (mDataSource->readAt(data_offset, &tag, 1) < 1) { + return ERROR_IO; + } + data_offset ++; + + if (0x0B/*IPMP_DESCRIPTOR_ID_TAG*/ == tag) { + uint8_t id; + dataLen = readSize(data_offset, mDataSource, &numOfBytes); + if (dataLen < 0) { + return ERROR_IO; + } else if (dataLen < 4) { + return ERROR_MALFORMED; + } + data_offset += numOfBytes; + + if (mDataSource->readAt(data_offset, &id, 1) < 1) { + return ERROR_IO; + } + data_offset ++; + + SINF *sinf = mFirstSINF; + while (sinf && (sinf->IPMPDescriptorID != id)) { + sinf = sinf->next; + } + if (sinf == NULL) { + return ERROR_MALFORMED; + } + sinf->len = dataLen - 3; + sinf->IPMPData = new (fallible) char[sinf->len]; + if (!sinf->IPMPData) { + return -ENOMEM; + } + + if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) { + return ERROR_IO; + } + data_offset += sinf->len; + + size -= (dataLen + numOfBytes + 1); + } + } + + if (size != 0) { + return ERROR_MALFORMED; + } + + return UNKNOWN_ERROR; // Return a dummy error. +} + +struct PathAdder { + PathAdder(nsTArray<uint32_t> *path, uint32_t chunkType) + : mPath(path) { + mPath->AppendElement(chunkType); + } + + ~PathAdder() { + mPath->RemoveElementAt(mPath->Length() - 1); + } + +private: + nsTArray<uint32_t> *mPath; + + PathAdder(const PathAdder &); + PathAdder &operator=(const PathAdder &); +}; + +static bool underMetaDataPath(const nsTArray<uint32_t> &path) { + return path.Length() >= 5 + && path[0] == FOURCC('m', 'o', 'o', 'v') + && path[1] == FOURCC('u', 'd', 't', 'a') + && path[2] == FOURCC('m', 'e', 't', 'a') + && path[3] == FOURCC('i', 'l', 's', 't'); +} + +static bool ValidInputSize(int32_t size) { + // Reject compressed samples larger than an uncompressed UHD + // frame. This is a reasonable cut-off for a lossy codec, + // combined with the current Firefox limit to 5k video. + return (size > 0 && size <= 4 * (1920 * 1080) * 3 / 2); +} + +status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { + ALOGV("entering parseChunk %lld/%d", *offset, depth); + uint32_t hdr[2]; + if (mDataSource->readAt(*offset, hdr, 4) < 4) { + return ERROR_IO; + } + if (!hdr[0]) { + *offset += 4; + return OK; + } + if (mDataSource->readAt(*offset + 4, hdr + 1, 4) < 4) { + return ERROR_IO; + } + uint64_t chunk_size = ntohl(hdr[0]); + uint32_t chunk_type = ntohl(hdr[1]); + off64_t data_offset = *offset + 8; + + if (chunk_size == 1) { + if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) { + return ERROR_IO; + } + 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; + } + + if (chunk_size >= kMAX_ALLOCATION) { + // Could cause an overflow later. Abort. + return ERROR_MALFORMED; + } + + char chunk[5]; + MakeFourCCString(chunk_type, chunk); + ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth); + +#if 0 + static const char kWhitespace[] = " "; + const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth]; + printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size); + + char buffer[256]; + size_t n = chunk_size; + if (n > sizeof(buffer)) { + n = sizeof(buffer); + } + if (mDataSource->readAt(*offset, buffer, n) + < (ssize_t)n) { + return ERROR_IO; + } + + hexdump(buffer, n); +#endif + + PathAdder autoAdder(&mPath, chunk_type); + + off64_t chunk_data_size = *offset + chunk_size - data_offset; + + if (chunk_type != FOURCC('c', 'p', 'r', 't') + && chunk_type != FOURCC('c', 'o', 'v', 'r') + && mPath.Length() == 5 && underMetaDataPath(mPath)) { + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset; + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + + return OK; + } + + switch(chunk_type) { + case FOURCC('m', 'o', 'o', 'v'): + case FOURCC('t', 'r', 'a', 'k'): + case FOURCC('m', 'd', 'i', 'a'): + case FOURCC('m', 'i', 'n', 'f'): + case FOURCC('d', 'i', 'n', 'f'): + case FOURCC('s', 't', 'b', 'l'): + case FOURCC('m', 'v', 'e', 'x'): + case FOURCC('m', 'o', 'o', 'f'): + case FOURCC('t', 'r', 'a', 'f'): + case FOURCC('m', 'f', 'r', 'a'): + case FOURCC('u', 'd', 't', 'a'): + case FOURCC('i', 'l', 's', 't'): + case FOURCC('s', 'i', 'n', 'f'): + case FOURCC('s', 'c', 'h', 'i'): + case FOURCC('e', 'd', 't', 's'): + { + if (chunk_type == FOURCC('s', 't', 'b', 'l')) { + ALOGV("sampleTable chunk is %d bytes long.", (size_t)chunk_size); + + if (mDataSource->flags() + & (DataSource::kWantsPrefetching + | DataSource::kIsCachingDataSource)) { + sp<MPEG4DataSource> cachedSource = + new MPEG4DataSource(mDataSource); + + if (cachedSource->setCachedRange(*offset, chunk_size) == OK) { + mDataSource = cachedSource; + } + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->sampleTable = new SampleTable(mDataSource); + } + + bool isTrack = false; + if (chunk_type == FOURCC('t', 'r', 'a', 'k')) { + isTrack = true; + + Track *track = new Track; + track->next = NULL; + if (mLastTrack) { + mLastTrack->next = track; + } else { + mFirstTrack = track; + } + mLastTrack = track; + + track->meta = new MetaData; + track->includes_expensive_metadata = false; + track->skipTrack = false; + track->timescale = 0; + track->empty_duration = 0; + track->segment_duration = 0; + track->media_time = 0; + track->meta->setCString(kKeyMIMEType, "application/octet-stream"); + } + + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset; + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + + if (isTrack) { + if (mLastTrack->skipTrack) { + Track *cur = mFirstTrack; + + if (cur == mLastTrack) { + delete cur; + mFirstTrack = mLastTrack = NULL; + } else { + while (cur && cur->next != mLastTrack) { + cur = cur->next; + } + cur->next = NULL; + delete mLastTrack; + mLastTrack = cur; + } + + return OK; + } + + status_t err = verifyTrack(mLastTrack); + + if (err != OK) { + return err; + } + } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) { + mInitCheck = OK; + + if (!mIsDrm) { + return UNKNOWN_ERROR; // Return a dummy error. + } else { + return OK; + } + } + break; + } + + case FOURCC('e', 'l', 's', 't'): + { + // See 14496-12 8.6.6 + uint8_t version; + if (mDataSource->readAt(data_offset, &version, 1) < 1) { + return ERROR_IO; + } + + uint32_t entry_count; + if (!mDataSource->getUInt32(data_offset + 4, &entry_count)) { + return ERROR_IO; + } + + off64_t entriesoffset = data_offset + 8; + bool nonEmptyCount = false; + for (uint32_t i = 0; i < entry_count; i++) { + if (mHeaderTimescale == 0) { + ALOGW("ignoring edit list because timescale is 0"); + break; + } + if (entriesoffset - data_offset > chunk_size) { + ALOGW("invalid edit list size"); + break; + } + uint64_t segment_duration; + int64_t media_time; + if (version == 1) { + if (!mDataSource->getUInt64(entriesoffset, &segment_duration) || + !mDataSource->getUInt64(entriesoffset + 8, (uint64_t*)&media_time)) { + return ERROR_IO; + } + entriesoffset += 16; + } else if (version == 0) { + uint32_t sd; + int32_t mt; + if (!mDataSource->getUInt32(entriesoffset, &sd) || + !mDataSource->getUInt32(entriesoffset + 4, (uint32_t*)&mt)) { + return ERROR_IO; + } + entriesoffset += 8; + segment_duration = sd; + media_time = mt; + } else { + return ERROR_IO; + } + entriesoffset += 4; // ignore media_rate_integer and media_rate_fraction. + if (media_time == -1 && i) { + ALOGW("ignoring invalid empty edit", i); + break; + } else if (media_time == -1) { + // Starting offsets for tracks (streams) are represented by an initial empty edit. + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->empty_duration = segment_duration; + continue; + } else if (nonEmptyCount) { + // we only support a single non-empty entry at the moment, for gapless playback + ALOGW("multiple edit list entries, A/V sync will be wrong"); + break; + } else { + nonEmptyCount = true; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->segment_duration = segment_duration; + mLastTrack->media_time = media_time; + } + storeEditList(); + *offset += chunk_size; + break; + } + + case FOURCC('f', 'r', 'm', 'a'): + { + uint32_t original_fourcc; + if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) { + return ERROR_IO; + } + original_fourcc = ntohl(original_fourcc); + ALOGV("read original format: %d", original_fourcc); + if (!mLastTrack) { + return ERROR_MALFORMED; + } + const char* mime = FourCC2MIME(original_fourcc); + if (!mime) { + return ERROR_UNSUPPORTED; + } + mLastTrack->meta->setCString(kKeyMIMEType, mime); + uint32_t num_channels = 0; + uint32_t sample_rate = 0; + if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) { + mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); + mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); + } + *offset += chunk_size; + break; + } + + case FOURCC('s', 'c', 'h', 'm'): + { + if (!mDataSource->getUInt32(data_offset, &mDrmScheme)) { + return ERROR_IO; + } + + *offset += chunk_size; + break; + } + + case FOURCC('t', 'e', 'n', 'c'): + { + if (chunk_size < 32) { + return ERROR_MALFORMED; + } + + // tenc box contains 1 byte version, 3 byte flags, 3 byte default algorithm id, one byte + // default IV size, 16 bytes default KeyID + // (ISO 23001-7) + char buf[4]; + memset(buf, 0, 4); + if (mDataSource->readAt(data_offset + 4, buf + 1, 3) < 3) { + return ERROR_IO; + } + uint32_t defaultAlgorithmId = ntohl(*((int32_t*)buf)); + if (defaultAlgorithmId > 1) { + // only 0 (clear) and 1 (AES-128) are valid + return ERROR_MALFORMED; + } + + memset(buf, 0, 4); + if (mDataSource->readAt(data_offset + 7, buf + 3, 1) < 1) { + return ERROR_IO; + } + uint32_t defaultIVSize = ntohl(*((int32_t*)buf)); + + if ((defaultAlgorithmId == 0 && defaultIVSize != 0) || + (defaultAlgorithmId != 0 && defaultIVSize == 0)) { + // only unencrypted data must have 0 IV size + return ERROR_MALFORMED; + } else if (defaultIVSize != 0 && + defaultIVSize != 8 && + defaultIVSize != 16) { + // only supported sizes are 0, 8 and 16 + return ERROR_MALFORMED; + } + + uint8_t defaultKeyId[16]; + + if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) { + return ERROR_IO; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId); + mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize); + mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16); + *offset += chunk_size; + break; + } + + case FOURCC('t', 'k', 'h', 'd'): + { + status_t err; + if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('p', 's', 's', 'h'): + { + PsshInfo pssh; + + // We need the contents of the box header before data_offset. Make + // sure we don't underflow somehow. + CHECK(data_offset >= 8); + + uint32_t version = 0; + if (mDataSource->readAt(data_offset, &version, 4) < 4) { + return ERROR_IO; + } + + if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) { + return ERROR_IO; + } + + // Copy the contents of the box (including header) verbatim. + pssh.datalen = chunk_data_size + 8; + pssh.data = new (fallible) uint8_t[pssh.datalen]; + if (!pssh.data) { + return -ENOMEM; + } + if (mDataSource->readAt(data_offset - 8, pssh.data, pssh.datalen) < pssh.datalen) { + return ERROR_IO; + } + + mPssh.AppendElement(pssh); + + *offset += chunk_size; + break; + } + + case FOURCC('m', 'd', 'h', 'd'): + { + if (chunk_data_size < 4) { + return ERROR_MALFORMED; + } + + uint8_t version; + if (mDataSource->readAt( + data_offset, &version, sizeof(version)) + < (ssize_t)sizeof(version)) { + return ERROR_IO; + } + + off64_t timescale_offset; + + if (version == 1) { + timescale_offset = data_offset + 4 + 16; + } else if (version == 0) { + timescale_offset = data_offset + 4 + 8; + } else { + return ERROR_IO; + } + + uint32_t timescale; + if (mDataSource->readAt( + timescale_offset, ×cale, sizeof(timescale)) + < (ssize_t)sizeof(timescale)) { + return ERROR_IO; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->timescale = ntohl(timescale); + if (!mLastTrack->timescale) { + return ERROR_MALFORMED; + } + + // Now that we've parsed the media timescale, we can interpret + // the edit list data. + storeEditList(); + + int64_t duration = 0; + if (version == 1) { + if (mDataSource->readAt( + timescale_offset + 4, &duration, sizeof(duration)) + < (ssize_t)sizeof(duration)) { + return ERROR_IO; + } + // Avoid duration sets to -1, which is incorrect. + if (duration != -1) { + duration = ntoh64(duration); + } else { + duration = 0; + } + } else { + uint32_t duration32; + if (mDataSource->readAt( + timescale_offset + 4, &duration32, sizeof(duration32)) + < (ssize_t)sizeof(duration32)) { + return ERROR_IO; + } + // ffmpeg sets duration to -1, which is incorrect. + if (duration32 != 0xffffffff) { + duration = ntohl(duration32); + } else { + duration = 0; + } + } + if (duration < 0) { + return ERROR_MALFORMED; + } + int64_t duration_us = unitsToUs(duration, mLastTrack->timescale); + if (duration_us == OVERFLOW_ERROR) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setInt64(kKeyDuration, duration_us); + + uint8_t lang[2]; + off64_t lang_offset; + if (version == 1) { + lang_offset = timescale_offset + 4 + 8; + } else if (version == 0) { + lang_offset = timescale_offset + 4 + 4; + } else { + return ERROR_IO; + } + + if (mDataSource->readAt(lang_offset, &lang, sizeof(lang)) + < (ssize_t)sizeof(lang)) { + return ERROR_IO; + } + + // To get the ISO-639-2/T three character language code + // 1 bit pad followed by 3 5-bits characters. Each character + // is packed as the difference between its ASCII value and 0x60. + char lang_code[4]; + lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60; + lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60; + lang_code[2] = (lang[1] & 0x1f) + 0x60; + lang_code[3] = '\0'; + + mLastTrack->meta->setCString( + kKeyMediaLanguage, lang_code); + + *offset += chunk_size; + break; + } + + case FOURCC('s', 't', 's', 'd'): + { + if (chunk_data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t buffer[8]; + if (chunk_data_size < (off64_t)sizeof(buffer)) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, 8) < 8) { + return ERROR_IO; + } + + if (U32_AT(buffer) != 0) { + // Should be version 0, flags 0. + return ERROR_MALFORMED; + } + + uint32_t entry_count = U32_AT(&buffer[4]); + + if (entry_count > 1) { + // For 3GPP timed text, there could be multiple tx3g boxes contain + // multiple text display formats. These formats will be used to + // display the timed text. + // For encrypted files, there may also be more than one entry. + const char *mime; + if (!mLastTrack) { + return ERROR_MALFORMED; + } + CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) && + strcasecmp(mime, "application/octet-stream")) { + // For now we only support a single type of media per track. + mLastTrack->skipTrack = true; + *offset += chunk_size; + break; + } + } + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset + 8; + for (uint32_t i = 0; i < entry_count; ++i) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + // Some muxers add some padding after the stsd content. Skip it. + *offset = stop_offset; + break; + } + + case FOURCC('m', 'p', '4', 'a'): + case FOURCC('.', 'm', 'p', '3'): + case FOURCC('e', 'n', 'c', 'a'): + case FOURCC('s', 'a', 'm', 'r'): + case FOURCC('s', 'a', 'w', 'b'): + { + // QT's MP4 may have an empty MP4A atom within a MP4A atom. + // Ignore it. + if (chunk_data_size == 4) { + *offset += chunk_size; + break; + } + uint8_t buffer[8 + 20]; + if (chunk_data_size < (ssize_t)sizeof(buffer)) { + // Basic AudioSampleEntry size. + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { + return ERROR_IO; + } + + uint16_t data_ref_index = U16_AT(&buffer[6]); + uint16_t qt_version = U16_AT(&buffer[8]); + uint32_t num_channels = U16_AT(&buffer[16]); + + uint16_t sample_size = U16_AT(&buffer[18]); + uint32_t sample_rate = U32_AT(&buffer[24]) >> 16; + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + if (chunk_type != FOURCC('e', 'n', 'c', 'a')) { + // if the chunk type is enca, we'll get the type from the sinf/frma box later + const char* mime = FourCC2MIME(chunk_type); + if (!mime) { + return ERROR_UNSUPPORTED; + } + mLastTrack->meta->setCString(kKeyMIMEType, mime); + AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate); + } + + uint64_t skip = 0; + if (qt_version == 1) { + // Skip QTv1 extension + // uint32_t SamplesPerPacket + // uint32_t BytesPerPacket + // uint32_t BytesPerFrame + // uint32_t BytesPerSample + skip = 16; + } else if (qt_version == 2) { + // Skip QTv2 extension + // uint32_t Qt V2 StructSize + // double SampleRate + // uint32_t ChannelCount + // uint32_t Reserved + // uint32_t BitsPerChannel + // uint32_t LPCMFormatSpecificFlags + // uint32_t BytesPerAudioPacket + // uint32_t LPCMFramesPerAudioPacket + // if (Qt V2 StructSize > 72) { + // StructSize-72: Qt V2 extension + // } + uint32_t structSize32; + if (mDataSource->readAt( + data_offset + 28, &structSize32, sizeof(structSize32)) + < (ssize_t)sizeof(structSize32)) { + return ERROR_IO; + } + uint32_t structSize = ntohl(structSize32); + // Read SampleRate. + uint64_t sample_rate64; + if (mDataSource->readAt( + data_offset + 32, &sample_rate64, sizeof(sample_rate64)) + < (ssize_t)sizeof(sample_rate64)) { + return ERROR_IO; + } + uint64_t i_value = ntoh64(sample_rate64); + void* v_value = reinterpret_cast<void*>(&i_value); + sample_rate = uint32_t(*reinterpret_cast<double*>(v_value)); + // Read ChannelCount. + uint32_t channel_count32; + if (mDataSource->readAt( + data_offset + 40, &channel_count32, sizeof(channel_count32)) + < (ssize_t)sizeof(channel_count32)) { + return ERROR_IO; + } + num_channels = ntohl(channel_count32); + + skip += 36; + if (structSize > 72) { + skip += structSize - 72; + } + } + ALOGV("*** coding='%s' %d channels, size %d, rate %d\n", + chunk, num_channels, sample_size, sample_rate); + mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); + mLastTrack->meta->setInt32(kKeySampleSize, sample_size); + mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); + + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset + sizeof(buffer) + skip; + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + break; + } + + case FOURCC('m', 'p', '4', 'v'): + case FOURCC('e', 'n', 'c', 'v'): + case FOURCC('s', '2', '6', '3'): + case FOURCC('H', '2', '6', '3'): + case FOURCC('h', '2', '6', '3'): + case FOURCC('a', 'v', 'c', '1'): + case FOURCC('a', 'v', 'c', '3'): + case FOURCC('V', 'P', '6', 'F'): + { + mHasVideo = true; + + uint8_t buffer[78]; + if (chunk_data_size < (ssize_t)sizeof(buffer)) { + // Basic VideoSampleEntry size. + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) { + return ERROR_IO; + } + + uint16_t data_ref_index = U16_AT(&buffer[6]); + uint16_t width = U16_AT(&buffer[6 + 18]); + uint16_t height = U16_AT(&buffer[6 + 20]); + + // The video sample is not standard-compliant if it has invalid dimension. + // Use some default width and height value, and + // let the decoder figure out the actual width and height (and thus + // be prepared for INFO_FOMRAT_CHANGED event). + if (width == 0) width = 352; + if (height == 0) height = 288; + + // printf("*** coding='%s' width=%d height=%d\n", + // chunk, width, height); + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + if (chunk_type != FOURCC('e', 'n', 'c', 'v')) { + // if the chunk type is encv, we'll get the type from the sinf/frma box later + const char* mime = FourCC2MIME(chunk_type); + if (!mime) { + return ERROR_UNSUPPORTED; + } + mLastTrack->meta->setCString(kKeyMIMEType, mime); + } + mLastTrack->meta->setInt32(kKeyWidth, width); + mLastTrack->meta->setInt32(kKeyHeight, height); + + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset + sizeof(buffer); + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + // Some Apple QuickTime muxed videos appear to have some padding. + // Ignore it and assume we've reached the end. + if (stop_offset - *offset < 8) { + *offset = stop_offset; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + break; + } + + case FOURCC('s', 't', 'c', 'o'): + case FOURCC('c', 'o', '6', '4'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setChunkOffsetParams( + chunk_type, data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('s', 't', 's', 'c'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setSampleToChunkParams( + data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('s', 't', 's', 'z'): + case FOURCC('s', 't', 'z', '2'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setSampleSizeParams( + chunk_type, data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + size_t max_size; + err = mLastTrack->sampleTable->getMaxSampleSize(&max_size); + + if (err != OK) { + return err; + } + + if (max_size != 0) { + // Assume that a given buffer only contains at most 10 chunks, + // each chunk originally prefixed with a 2 byte length will + // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion, + // and thus will grow by 2 bytes per chunk. + mLastTrack->meta->setInt32(kKeyMaxInputSize, max_size + 10 * 2); + } else { + // No size was specified. Pick a conservatively large size. + int32_t width, height; + if (mLastTrack->meta->findInt32(kKeyWidth, &width) && + mLastTrack->meta->findInt32(kKeyHeight, &height)) { + mLastTrack->meta->setInt32(kKeyMaxInputSize, width * height * 3 / 2); + } else { + ALOGV("No width or height, assuming worst case 1080p"); + mLastTrack->meta->setInt32(kKeyMaxInputSize, 3110400); + } + } + *offset += chunk_size; + + // Calculate average frame rate. + const char *mime; + CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); + if (!strncasecmp("video/", mime, 6)) { + size_t nSamples = mLastTrack->sampleTable->countSamples(); + int64_t durationUs; + if (mLastTrack->meta->findInt64(kKeyDuration, &durationUs)) { + if (durationUs > 0) { + int32_t frameRate = (nSamples * 1000000LL + + (durationUs >> 1)) / durationUs; + mLastTrack->meta->setInt32(kKeyFrameRate, frameRate); + } + } + } + + break; + } + + case FOURCC('s', 't', 't', 's'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setTimeToSampleParams( + data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('c', 't', 't', 's'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setCompositionTimeToSampleParams( + data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('s', 't', 's', 's'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setSyncSampleParams( + data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('s', 'a', 'i', 'z'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setSampleAuxiliaryInformationSizeParams( + data_offset, chunk_data_size, mDrmScheme); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + case FOURCC('s', 'a', 'i', 'o'): + { + if (!mLastTrack || !mLastTrack->sampleTable.get()) { + return ERROR_MALFORMED; + } + status_t err = + mLastTrack->sampleTable->setSampleAuxiliaryInformationOffsetParams( + data_offset, chunk_data_size, mDrmScheme); + + if (err != OK) { + return err; + } + + *offset += chunk_size; + break; + } + + // @xyz + case FOURCC('\xA9', 'x', 'y', 'z'): + { + // Best case the total data length inside "@xyz" box + // would be 8, for instance "@xyz" + "\x00\x04\x15\xc7" + "0+0/", + // where "\x00\x04" is the text string length with value = 4, + // "\0x15\xc7" is the language code = en, and "0+0" is a + // location (string) value with longitude = 0 and latitude = 0. + if (chunk_data_size < 8) { + return ERROR_MALFORMED; + } + + // Worst case the location string length would be 18, + // for instance +90.0000-180.0000, without the trailing "/" and + // the string length + language code. + char buffer[18]; + + // Substracting 5 from the data size is because the text string length + + // language code takes 4 bytes, and the trailing slash "/" takes 1 byte. + off64_t location_length = chunk_data_size - 5; + if (location_length >= (off64_t) sizeof(buffer)) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset + 4, buffer, location_length) < location_length) { + return ERROR_IO; + } + + buffer[location_length] = '\0'; + mFileMetaData->setCString(kKeyLocation, buffer); + *offset += chunk_size; + break; + } + + case FOURCC('e', 's', 'd', 's'): + { + if (chunk_data_size < 4) { + return ERROR_MALFORMED; + } + + uint8_t buffer[256]; + if (chunk_data_size > (off64_t)sizeof(buffer)) { + return ERROR_BUFFER_TOO_SMALL; + } + + if (mDataSource->readAt( + data_offset, buffer, chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + + if (U32_AT(buffer) != 0) { + // Should be version 0, flags 0. + return ERROR_MALFORMED; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setData( + kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4); + + if (mPath.Length() >= 2 + && (mPath[mPath.Length() - 2] == FOURCC('m', 'p', '4', 'a') || + (mPath[mPath.Length() - 2] == FOURCC('e', 'n', 'c', 'a')))) { + // Information from the ESDS must be relied on for proper + // setup of sample rate and channel count for MPEG4 Audio. + // The generic header appears to only contain generic + // information... + + status_t err = updateAudioTrackInfoFromESDS_MPEG4Audio( + &buffer[4], chunk_data_size - 4); + + if (err != OK) { + return err; + } + } + + *offset += chunk_size; + break; + } + + case FOURCC('a', 'v', 'c', 'C'): + { + if (chunk_data_size < 7) { + ALOGE("short avcC chunk (%d bytes)", chunk_data_size); + return ERROR_MALFORMED; + } + + sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size); + if (!buffer.get() || !buffer->data()) { + return -ENOMEM; + } + + if (mDataSource->readAt( + data_offset, buffer->data(), chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setData( + kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size); + + *offset += chunk_size; + break; + } + + case FOURCC('d', '2', '6', '3'): + { + /* + * d263 contains a fixed 7 bytes part: + * vendor - 4 bytes + * version - 1 byte + * level - 1 byte + * profile - 1 byte + * optionally, "d263" box itself may contain a 16-byte + * bit rate box (bitr) + * average bit rate - 4 bytes + * max bit rate - 4 bytes + */ + char buffer[23]; + if (chunk_data_size != 7 && + chunk_data_size != 23) { + ALOGE("Incorrect D263 box size %lld", chunk_data_size); + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, chunk_data_size) < chunk_data_size) { + return ERROR_IO; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size); + + *offset += chunk_size; + break; + } + + case FOURCC('m', 'e', 't', 'a'): + { + uint8_t buffer[4]; + if (chunk_data_size < (off64_t)sizeof(buffer)) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, 4) < 4) { + return ERROR_IO; + } + + if (U32_AT(buffer) != 0) { + // Should be version 0, flags 0. + + // If it's not, let's assume this is one of those + // apparently malformed chunks that don't have flags + // and completely different semantics than what's + // in the MPEG4 specs and skip it. + *offset += chunk_size; + return OK; + } + + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset + sizeof(buffer); + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + break; + } + + case FOURCC('m', 'e', 'a', 'n'): + case FOURCC('n', 'a', 'm', 'e'): + case FOURCC('d', 'a', 't', 'a'): + { + if (mPath.Length() == 6 && underMetaDataPath(mPath)) { + status_t err = parseMetaData(data_offset, chunk_data_size); + + if (err != OK) { + return err; + } + } + + *offset += chunk_size; + break; + } + + case FOURCC('m', 'v', 'h', 'd'): + { + if (chunk_data_size < 24) { + return ERROR_MALFORMED; + } + + uint8_t header[24]; + if (mDataSource->readAt( + data_offset, header, sizeof(header)) + < (ssize_t)sizeof(header)) { + return ERROR_IO; + } + + if (header[0] == 1) { + mHeaderTimescale = U32_AT(&header[20]); + } else if (header[0] != 0) { + return ERROR_MALFORMED; + } else { + mHeaderTimescale = U32_AT(&header[12]); + } + + *offset += chunk_size; + break; + } + + case FOURCC('m', 'e', 'h', 'd'): + { + if (chunk_data_size < 8) { + return ERROR_MALFORMED; + } + + uint8_t version; + if (mDataSource->readAt( + data_offset, &version, sizeof(version)) + < (ssize_t)sizeof(version)) { + return ERROR_IO; + } + if (version > 1) { + break; + } + int64_t duration = 0; + if (version == 1) { + if (mDataSource->readAt( + data_offset + 4, &duration, sizeof(duration)) + < (ssize_t)sizeof(duration)) { + return ERROR_IO; + } + duration = ntoh64(duration); + } else { + uint32_t duration32; + if (mDataSource->readAt( + data_offset + 4, &duration32, sizeof(duration32)) + < (ssize_t)sizeof(duration32)) { + return ERROR_IO; + } + duration = ntohl(duration32); + } + if (duration < 0) { + return ERROR_MALFORMED; + } + int64_t duration_us = unitsToUs(duration, mHeaderTimescale); + if (duration_us == OVERFLOW_ERROR) { + return ERROR_MALFORMED; + } + if (duration && mHeaderTimescale) { + mFileMetaData->setInt64(kKeyMovieDuration, duration_us); + } + + *offset += chunk_size; + break; + } + + case FOURCC('m', 'd', 'a', 't'): + { + ALOGV("mdat chunk, drm: %d", mIsDrm); + if (!mIsDrm) { + *offset += chunk_size; + break; + } + + if (chunk_size < 8) { + return ERROR_MALFORMED; + } + + return parseDrmSINF(offset, data_offset); + } + + case FOURCC('h', 'd', 'l', 'r'): + { + uint32_t buffer; + if (mDataSource->readAt( + data_offset + 8, &buffer, 4) < 4) { + return ERROR_IO; + } + + uint32_t type = ntohl(buffer); + // For the 3GPP file format, the handler-type within the 'hdlr' box + // shall be 'text'. We also want to support 'sbtl' handler type + // for a practical reason as various MPEG4 containers use it. + if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) { + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP); + } + + *offset += chunk_size; + break; + } + + case FOURCC('t', 'x', '3', 'g'): + { + if (!mLastTrack) { + return ERROR_MALFORMED; + } + uint32_t type; + const void *data; + size_t size = 0; + if (!mLastTrack->meta->findData( + kKeyTextFormatData, &type, &data, &size)) { + size = 0; + } + + // Make sure (size + chunk_size) isn't going to overflow. + if (size >= kMAX_ALLOCATION - chunk_size) { + return ERROR_MALFORMED; + } + uint8_t *buffer = new (fallible) uint8_t[size + chunk_size]; + if (!buffer) { + return -ENOMEM; + } + + if (size > 0) { + memcpy(buffer, data, size); + } + + if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size)) + < chunk_size) { + delete[] buffer; + buffer = NULL; + + return ERROR_IO; + } + + mLastTrack->meta->setData( + kKeyTextFormatData, 0, buffer, size + chunk_size); + + delete[] buffer; + + *offset += chunk_size; + break; + } + + case FOURCC('c', 'o', 'v', 'r'): + { + if (mFileMetaData != NULL) { + ALOGV("chunk_data_size = %lld and data_offset = %lld", + chunk_data_size, data_offset); + const int kSkipBytesOfDataBox = 16; + if (chunk_data_size <= kSkipBytesOfDataBox) { + return ERROR_MALFORMED; + } + sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size + 1); + if (!buffer.get() || !buffer->data()) { + return -ENOMEM; + } + if (mDataSource->readAt( + data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) { + return ERROR_IO; + } + mFileMetaData->setData( + kKeyAlbumArt, MetaData::TYPE_NONE, + buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox); + } + + *offset += chunk_size; + break; + } + + case FOURCC('-', '-', '-', '-'): + { + mLastCommentMean.clear(); + mLastCommentName.clear(); + mLastCommentData.clear(); + *offset += chunk_size; + break; + } + + case FOURCC('s', 'i', 'd', 'x'): + { + parseSegmentIndex(data_offset, chunk_data_size); + *offset += chunk_size; + return UNKNOWN_ERROR; // stop parsing after sidx + } + + case FOURCC('w', 'a', 'v', 'e'): + { + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset; + while (*offset < stop_offset) { + status_t err = parseChunk(offset, depth + 1); + if (err != OK) { + return err; + } + } + + if (*offset != stop_offset) { + return ERROR_MALFORMED; + } + break; + } + + default: + { + *offset += chunk_size; + break; + } + } + + return OK; +} + +void MPEG4Extractor::storeEditList() +{ + if (mHeaderTimescale == 0 || + !mLastTrack || + mLastTrack->timescale == 0) { + return; + } + + if (mLastTrack->segment_duration > uint64_t(INT64_MAX) || + mLastTrack->empty_duration > uint64_t(INT64_MAX)) { + return; + } + uint64_t segment_duration = + uint64_t(unitsToUs(mLastTrack->segment_duration, mHeaderTimescale)); + // media_time is measured in media time scale units. + int64_t media_time = unitsToUs(mLastTrack->media_time, mLastTrack->timescale); + // empty_duration is in the Movie Header Box's timescale. + int64_t empty_duration = unitsToUs(mLastTrack->empty_duration, mHeaderTimescale); + if (segment_duration == OVERFLOW_ERROR || + media_time == OVERFLOW_ERROR || + empty_duration == OVERFLOW_ERROR) { + return; + } + media_time -= empty_duration; + mLastTrack->meta->setInt64(kKeyMediaTime, media_time); + + int64_t duration; + int32_t samplerate; + if (mLastTrack->meta->findInt64(kKeyDuration, &duration) && + mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) { + + int64_t delay = (media_time * samplerate + 500000) / 1000000; + mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); + + int64_t paddingus = duration - (segment_duration + media_time); + int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000; + mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples); + } +} + +status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { + ALOGV("MPEG4Extractor::parseSegmentIndex"); + + if (size < 12) { + return -EINVAL; + } + + uint32_t flags; + if (!mDataSource->getUInt32(offset, &flags)) { + return ERROR_MALFORMED; + } + + uint32_t version = flags >> 24; + flags &= 0xffffff; + + ALOGV("sidx version %d", version); + + uint32_t referenceId; + if (!mDataSource->getUInt32(offset + 4, &referenceId)) { + return ERROR_MALFORMED; + } + + uint32_t timeScale; + if (!mDataSource->getUInt32(offset + 8, &timeScale)) { + return ERROR_MALFORMED; + } + if (!timeScale) { + return ERROR_MALFORMED; + } + ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale); + + uint64_t earliestPresentationTime; + uint64_t firstOffset; + + offset += 12; + size -= 12; + + if (version == 0) { + if (size < 8) { + return -EINVAL; + } + uint32_t tmp; + if (!mDataSource->getUInt32(offset, &tmp)) { + return ERROR_MALFORMED; + } + earliestPresentationTime = tmp; + if (!mDataSource->getUInt32(offset + 4, &tmp)) { + return ERROR_MALFORMED; + } + firstOffset = tmp; + offset += 8; + size -= 8; + } else { + if (size < 16) { + return -EINVAL; + } + if (!mDataSource->getUInt64(offset, &earliestPresentationTime)) { + return ERROR_MALFORMED; + } + if (!mDataSource->getUInt64(offset + 8, &firstOffset)) { + return ERROR_MALFORMED; + } + offset += 16; + size -= 16; + } + ALOGV("sidx pres/off: %Ld/%Ld", earliestPresentationTime, firstOffset); + + if (size < 4) { + return -EINVAL; + } + + uint16_t referenceCount; + if (!mDataSource->getUInt16(offset + 2, &referenceCount)) { + return ERROR_MALFORMED; + } + offset += 4; + size -= 4; + ALOGV("refcount: %d", referenceCount); + + if (size < referenceCount * 12) { + return -EINVAL; + } + + int64_t total_duration = 0; + for (unsigned int i = 0; i < referenceCount; i++) { + uint32_t d1, d2, d3; + + if (!mDataSource->getUInt32(offset, &d1) || // size + !mDataSource->getUInt32(offset + 4, &d2) || // duration + !mDataSource->getUInt32(offset + 8, &d3)) { // flags + return ERROR_MALFORMED; + } + + if (d1 & 0x80000000) { + ALOGW("sub-sidx boxes not supported yet"); + } + bool sap = d3 & 0x80000000; + uint32_t saptype = (d3 >> 28) & 0x3; + if (!sap || saptype > 2) { + ALOGW("not a stream access point, or unsupported type"); + } + total_duration += d2; + offset += 12; + ALOGV(" item %d, %08x %08x %08x", i, d1, d2, d3); + SidxEntry se; + se.mSize = d1 & 0x7fffffff; + int64_t durationUs = unitsToUs(d2, timeScale); + if (durationUs == OVERFLOW_ERROR || durationUs > int64_t(UINT32_MAX)) { + return ERROR_MALFORMED; + } + se.mDurationUs = uint32_t(durationUs); + mSidxEntries.AppendElement(se); + } + + mSidxDuration = unitsToUs(total_duration, timeScale); + if (mSidxDuration == OVERFLOW_ERROR) { + return ERROR_MALFORMED; + } + ALOGV("duration: %lld", mSidxDuration); + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + int64_t metaDuration; + if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) { + mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration); + } + return OK; +} + +status_t MPEG4Extractor::parseTrackHeader( + off64_t data_offset, off64_t data_size) { + if (data_size < 4) { + return ERROR_MALFORMED; + } + + uint8_t version; + if (mDataSource->readAt(data_offset, &version, 1) < 1) { + return ERROR_IO; + } + + size_t dynSize = (version == 1) ? 36 : 24; + + uint8_t buffer[36 + 60]; + + if (data_size != (off64_t)dynSize + 60) { + return ERROR_MALFORMED; + } + + if (mDataSource->readAt( + data_offset, buffer, data_size) < (ssize_t)data_size) { + return ERROR_IO; + } + + uint64_t ctime, mtime, duration; + int32_t id; + + if (version == 1) { + ctime = U64_AT(&buffer[4]); + mtime = U64_AT(&buffer[12]); + id = U32_AT(&buffer[20]); + duration = U64_AT(&buffer[28]); + } else if (version == 0) { + ctime = U32_AT(&buffer[4]); + mtime = U32_AT(&buffer[8]); + id = U32_AT(&buffer[12]); + duration = U32_AT(&buffer[20]); + } else { + return ERROR_UNSUPPORTED; + } + + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setInt32(kKeyTrackID, id); + + size_t matrixOffset = dynSize + 16; + int32_t a00 = U32_AT(&buffer[matrixOffset]); + int32_t a01 = U32_AT(&buffer[matrixOffset + 4]); + int32_t dx = U32_AT(&buffer[matrixOffset + 8]); + int32_t a10 = U32_AT(&buffer[matrixOffset + 12]); + int32_t a11 = U32_AT(&buffer[matrixOffset + 16]); + int32_t dy = U32_AT(&buffer[matrixOffset + 20]); + +#if 0 + ALOGI("x' = %.2f * x + %.2f * y + %.2f", + a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f); + ALOGI("y' = %.2f * x + %.2f * y + %.2f", + a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f); +#endif + + uint32_t rotationDegrees; + + static const int32_t kFixedOne = 0x10000; + if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) { + // Identity, no rotation + rotationDegrees = 0; + } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) { + rotationDegrees = 90; + } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) { + rotationDegrees = 270; + } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) { + rotationDegrees = 180; + } else { + ALOGW("We only support 0,90,180,270 degree rotation matrices"); + rotationDegrees = 0; + } + + if (rotationDegrees != 0) { + mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees); + } + + // Handle presentation display size, which could be different + // from the image size indicated by kKeyWidth and kKeyHeight. + uint32_t width = U32_AT(&buffer[dynSize + 52]); + uint32_t height = U32_AT(&buffer[dynSize + 56]); + mLastTrack->meta->setInt32(kKeyDisplayWidth, width >> 16); + mLastTrack->meta->setInt32(kKeyDisplayHeight, height >> 16); + + return OK; +} + +status_t MPEG4Extractor::parseMetaData(off64_t offset, size_t size) { + if (size < 4) { + return ERROR_MALFORMED; + } + + FallibleTArray<uint8_t> bufferBackend; + if (!bufferBackend.SetLength(size + 1, mozilla::fallible)) { + // OOM ignore metadata. + return OK; + } + + uint8_t *buffer = bufferBackend.Elements(); + if (mDataSource->readAt( + offset, buffer, size) != (ssize_t)size) { + return ERROR_IO; + } + + uint32_t flags = U32_AT(buffer); + + uint32_t metadataKey = 0; + char chunk[5]; + MakeFourCCString(mPath[4], chunk); + ALOGV("meta: %s @ %lld", chunk, offset); + switch (mPath[4]) { + case FOURCC(0xa9, 'a', 'l', 'b'): + { + metadataKey = kKeyAlbum; + break; + } + case FOURCC(0xa9, 'A', 'R', 'T'): + { + metadataKey = kKeyArtist; + break; + } + case FOURCC('a', 'A', 'R', 'T'): + { + metadataKey = kKeyAlbumArtist; + break; + } + case FOURCC(0xa9, 'd', 'a', 'y'): + { + metadataKey = kKeyYear; + break; + } + case FOURCC(0xa9, 'n', 'a', 'm'): + { + metadataKey = kKeyTitle; + break; + } + case FOURCC(0xa9, 'w', 'r', 't'): + { + metadataKey = kKeyWriter; + break; + } + case FOURCC('c', 'o', 'v', 'r'): + { + metadataKey = kKeyAlbumArt; + break; + } + case FOURCC('g', 'n', 'r', 'e'): + { + metadataKey = kKeyGenre; + break; + } + case FOURCC(0xa9, 'g', 'e', 'n'): + { + metadataKey = kKeyGenre; + break; + } + case FOURCC('c', 'p', 'i', 'l'): + { + if (size == 9 && flags == 21) { + char tmp[16]; + sprintf(tmp, "%d", + (int)buffer[size - 1]); + + mFileMetaData->setCString(kKeyCompilation, tmp); + } + break; + } + case FOURCC('t', 'r', 'k', 'n'): + { + if (size == 16 && flags == 0) { + char tmp[16]; + uint16_t* pTrack = (uint16_t*)&buffer[10]; + uint16_t* pTotalTracks = (uint16_t*)&buffer[12]; + sprintf(tmp, "%d/%d", ntohs(*pTrack), ntohs(*pTotalTracks)); + + mFileMetaData->setCString(kKeyCDTrackNumber, tmp); + } + break; + } + case FOURCC('d', 'i', 's', 'k'): + { + if ((size == 14 || size == 16) && flags == 0) { + char tmp[16]; + uint16_t* pDisc = (uint16_t*)&buffer[10]; + uint16_t* pTotalDiscs = (uint16_t*)&buffer[12]; + sprintf(tmp, "%d/%d", ntohs(*pDisc), ntohs(*pTotalDiscs)); + + mFileMetaData->setCString(kKeyDiscNumber, tmp); + } + break; + } + case FOURCC('-', '-', '-', '-'): + { + buffer[size] = '\0'; + switch (mPath[5]) { + case FOURCC('m', 'e', 'a', 'n'): + mLastCommentMean.setTo((const char *)buffer + 4); + break; + case FOURCC('n', 'a', 'm', 'e'): + mLastCommentName.setTo((const char *)buffer + 4); + break; + case FOURCC('d', 'a', 't', 'a'): + mLastCommentData.setTo((const char *)buffer + 8); + break; + } + + // Once we have a set of mean/name/data info, go ahead and process + // it to see if its something we are interested in. Whether or not + // were are interested in the specific tag, make sure to clear out + // the set so we can be ready to process another tuple should one + // show up later in the file. + if ((mLastCommentMean.length() != 0) && + (mLastCommentName.length() != 0) && + (mLastCommentData.length() != 0)) { + + if (mLastCommentMean == "com.apple.iTunes" + && mLastCommentName == "iTunSMPB") { + int32_t delay, padding; + if (sscanf(mLastCommentData, + " %*x %x %x %*x", &delay, &padding) == 2) { + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); + mLastTrack->meta->setInt32(kKeyEncoderPadding, padding); + } + } + + mLastCommentMean.clear(); + mLastCommentName.clear(); + mLastCommentData.clear(); + } + break; + } + + default: + break; + } + + if (size >= 8 && metadataKey) { + if (metadataKey == kKeyAlbumArt) { + mFileMetaData->setData( + kKeyAlbumArt, MetaData::TYPE_NONE, + buffer + 8, size - 8); + } else if (metadataKey == kKeyGenre) { + if (flags == 0) { + // uint8_t genre code, iTunes genre codes are + // the standard id3 codes, except they start + // at 1 instead of 0 (e.g. Pop is 14, not 13) + // We use standard id3 numbering, so subtract 1. + int genrecode = (int)buffer[size - 1]; + genrecode--; + if (genrecode < 0) { + genrecode = 255; // reserved for 'unknown genre' + } + char genre[10]; + sprintf(genre, "%d", genrecode); + + mFileMetaData->setCString(metadataKey, genre); + } else if (flags == 1) { + // custom genre string + buffer[size] = '\0'; + + mFileMetaData->setCString( + metadataKey, (const char *)buffer + 8); + } + } else { + buffer[size] = '\0'; + + mFileMetaData->setCString( + metadataKey, (const char *)buffer + 8); + } + } + + return OK; +} + +sp<MediaSource> MPEG4Extractor::getTrack(size_t index) { + status_t err; + if ((err = readMetaData()) != OK) { + return NULL; + } + + Track *track = mFirstTrack; + while (index > 0) { + if (track == NULL) { + return NULL; + } + + track = track->next; + --index; + } + + if (track == NULL) { + return NULL; + } + + ALOGV("getTrack called, pssh: %d", mPssh.Length()); + + return new MPEG4Source(track->meta, track->timescale, track->sampleTable); +} + +// static +status_t MPEG4Extractor::verifyTrack(Track *track) { + int32_t trackId; + if (!track->meta->findInt32(kKeyTrackID, &trackId)) { + return ERROR_MALFORMED; + } + + const char *mime; + if (!track->meta->findCString(kKeyMIMEType, &mime)) { + return ERROR_MALFORMED; + } + + uint32_t type; + const void *data; + size_t size; + if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + if (!track->meta->findData(kKeyAVCC, &type, &data, &size) + || type != kTypeAVCC + || size < 7 + // configurationVersion == 1? + || reinterpret_cast<const uint8_t*>(data)[0] != 1) { + return ERROR_MALFORMED; + } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) + || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + if (!track->meta->findData(kKeyESDS, &type, &data, &size) + || type != kTypeESDS) { + return ERROR_MALFORMED; + } + } + + if (!track->sampleTable.get() || !track->sampleTable->isValid()) { + // Make sure we have all the metadata we need. + return ERROR_MALFORMED; + } + + uint32_t keytype; + const void *key; + size_t keysize; + if (track->meta->findData(kKeyCryptoKey, &keytype, &key, &keysize)) { + if (keysize > 16) { + return ERROR_MALFORMED; + } + } + + return OK; +} + +typedef enum { + //AOT_NONE = -1, + //AOT_NULL_OBJECT = 0, + //AOT_AAC_MAIN = 1, /**< Main profile */ + AOT_AAC_LC = 2, /**< Low Complexity object */ + //AOT_AAC_SSR = 3, + //AOT_AAC_LTP = 4, + AOT_SBR = 5, + //AOT_AAC_SCAL = 6, + //AOT_TWIN_VQ = 7, + //AOT_CELP = 8, + //AOT_HVXC = 9, + //AOT_RSVD_10 = 10, /**< (reserved) */ + //AOT_RSVD_11 = 11, /**< (reserved) */ + //AOT_TTSI = 12, /**< TTSI Object */ + //AOT_MAIN_SYNTH = 13, /**< Main Synthetic object */ + //AOT_WAV_TAB_SYNTH = 14, /**< Wavetable Synthesis object */ + //AOT_GEN_MIDI = 15, /**< General MIDI object */ + //AOT_ALG_SYNTH_AUD_FX = 16, /**< Algorithmic Synthesis and Audio FX object */ + AOT_ER_AAC_LC = 17, /**< Error Resilient(ER) AAC Low Complexity */ + //AOT_RSVD_18 = 18, /**< (reserved) */ + //AOT_ER_AAC_LTP = 19, /**< Error Resilient(ER) AAC LTP object */ + AOT_ER_AAC_SCAL = 20, /**< Error Resilient(ER) AAC Scalable object */ + //AOT_ER_TWIN_VQ = 21, /**< Error Resilient(ER) TwinVQ object */ + AOT_ER_BSAC = 22, /**< Error Resilient(ER) BSAC object */ + AOT_ER_AAC_LD = 23, /**< Error Resilient(ER) AAC LowDelay object */ + //AOT_ER_CELP = 24, /**< Error Resilient(ER) CELP object */ + //AOT_ER_HVXC = 25, /**< Error Resilient(ER) HVXC object */ + //AOT_ER_HILN = 26, /**< Error Resilient(ER) HILN object */ + //AOT_ER_PARA = 27, /**< Error Resilient(ER) Parametric object */ + //AOT_RSVD_28 = 28, /**< might become SSC */ + AOT_PS = 29, /**< PS, Parametric Stereo (includes SBR) */ + //AOT_MPEGS = 30, /**< MPEG Surround */ + + AOT_ESCAPE = 31, /**< Signal AOT uses more than 5 bits */ + + //AOT_MP3ONMP4_L1 = 32, /**< MPEG-Layer1 in mp4 */ + //AOT_MP3ONMP4_L2 = 33, /**< MPEG-Layer2 in mp4 */ + //AOT_MP3ONMP4_L3 = 34, /**< MPEG-Layer3 in mp4 */ + //AOT_RSVD_35 = 35, /**< might become DST */ + //AOT_RSVD_36 = 36, /**< might become ALS */ + //AOT_AAC_SLS = 37, /**< AAC + SLS */ + //AOT_SLS = 38, /**< SLS */ + //AOT_ER_AAC_ELD = 39, /**< AAC Enhanced Low Delay */ + + //AOT_USAC = 42, /**< USAC */ + //AOT_SAOC = 43, /**< SAOC */ + //AOT_LD_MPEGS = 44, /**< Low Delay MPEG Surround */ + + //AOT_RSVD50 = 50, /**< Interim AOT for Rsvd50 */ +} AUDIO_OBJECT_TYPE; + +status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( + const void *esds_data, size_t esds_size) { + ESDS esds(esds_data, esds_size); + + uint8_t objectTypeIndication; + if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) { + return ERROR_MALFORMED; + } + + if (objectTypeIndication == 0xe1) { + // This isn't MPEG4 audio at all, it's QCELP 14k... + if (mLastTrack == NULL) + return ERROR_MALFORMED; + + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP); + return OK; + } + + if (objectTypeIndication == 0x6b || objectTypeIndication == 0x69) { + // The media subtype is MP3 audio + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + } + + const uint8_t *csd; + size_t csd_size; + if (esds.getCodecSpecificInfo( + (const void **)&csd, &csd_size) != OK) { + return ERROR_MALFORMED; + } + +#if 0 + if (kUseHexDump) { + printf("ESD of size %zu\n", csd_size); + 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; + } + + static uint32_t kSamplingRate[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350 + }; + + ABitReader br(csd, csd_size); + if (br.numBitsLeft() < 5) { + return ERROR_MALFORMED; + } + uint32_t objectType = br.getBits(5); + + if (objectType == 31) { // AAC-ELD => additional 6 bits + if (br.numBitsLeft() < 6) { + return ERROR_MALFORMED; + } + objectType = 32 + br.getBits(6); + } + + if (mLastTrack == NULL) + return ERROR_MALFORMED; + + if (objectType >= 1 && objectType <= 4) { + mLastTrack->meta->setInt32(kKeyAACProfile, objectType); + } + + //keep AOT type + mLastTrack->meta->setInt32(kKeyAACAOT, objectType); + + if (br.numBitsLeft() < 4) { + return ERROR_MALFORMED; + } + uint32_t freqIndex = br.getBits(4); + + int32_t sampleRate = 0; + int32_t numChannels = 0; + if (freqIndex == 15) { + if (br.numBitsLeft() < 28) return ERROR_MALFORMED; + sampleRate = br.getBits(24); + numChannels = br.getBits(4); + } else { + if (br.numBitsLeft() < 4) return ERROR_MALFORMED; + numChannels = br.getBits(4); + + if (freqIndex == 13 || freqIndex == 14) { + return ERROR_MALFORMED; + } + + sampleRate = kSamplingRate[freqIndex]; + } + + if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13 + if (br.numBitsLeft() < 4) return ERROR_MALFORMED; + uint32_t extFreqIndex = br.getBits(4); + int32_t extSampleRate; + if (extFreqIndex == 15) { + if (csd_size < 8) { + return ERROR_MALFORMED; + } + if (br.numBitsLeft() < 24) return ERROR_MALFORMED; + extSampleRate = br.getBits(24); + } else { + if (extFreqIndex == 13 || extFreqIndex == 14) { + return ERROR_MALFORMED; + } + extSampleRate = kSamplingRate[extFreqIndex]; + } + //TODO: save the extension sampling rate value in meta data => + // mLastTrack->meta->setInt32(kKeyExtSampleRate, extSampleRate); + } + + switch (numChannels) { + // values defined in 14496-3_2009 amendment-4 Table 1.19 - Channel Configuration + case 0: + case 1:// FC + case 2:// FL FR + case 3:// FC, FL FR + case 4:// FC, FL FR, RC + case 5:// FC, FL FR, SL SR + case 6:// FC, FL FR, SL SR, LFE + //numChannels already contains the right value + break; + case 11:// FC, FL FR, SL SR, RC, LFE + numChannels = 7; + break; + case 7: // FC, FCL FCR, FL FR, SL SR, LFE + case 12:// FC, FL FR, SL SR, RL RR, LFE + case 14:// FC, FL FR, SL SR, LFE, FHL FHR + numChannels = 8; + break; + default: + return ERROR_UNSUPPORTED; + } + + { + if (objectType == AOT_SBR || objectType == AOT_PS) { + if (br.numBitsLeft() < 5) return ERROR_MALFORMED; + objectType = br.getBits(5); + + if (objectType == AOT_ESCAPE) { + if (br.numBitsLeft() < 6) return ERROR_MALFORMED; + objectType = 32 + br.getBits(6); + } + } + if (objectType == AOT_AAC_LC || objectType == AOT_ER_AAC_LC || + objectType == AOT_ER_AAC_LD || objectType == AOT_ER_AAC_SCAL || + objectType == AOT_ER_BSAC) { + if (br.numBitsLeft() < 2) return ERROR_MALFORMED; + const int32_t frameLengthFlag = br.getBits(1); + + const int32_t dependsOnCoreCoder = br.getBits(1); + + if (dependsOnCoreCoder ) { + if (br.numBitsLeft() < 14) return ERROR_MALFORMED; + const int32_t coreCoderDelay = br.getBits(14); + } + + int32_t extensionFlag = -1; + if (br.numBitsLeft() > 0) { + extensionFlag = br.getBits(1); + } else { + switch (objectType) { + // 14496-3 4.5.1.1 extensionFlag + case AOT_AAC_LC: + extensionFlag = 0; + break; + case AOT_ER_AAC_LC: + case AOT_ER_AAC_SCAL: + case AOT_ER_BSAC: + case AOT_ER_AAC_LD: + extensionFlag = 1; + break; + default: + return ERROR_MALFORMED; + break; + } + ALOGW("csd missing extension flag; assuming %d for object type %u.", + extensionFlag, objectType); + } + + if (numChannels == 0) { + int32_t channelsEffectiveNum = 0; + int32_t channelsNum = 0; + if (br.numBitsLeft() < 32) { + return ERROR_MALFORMED; + } + const int32_t ElementInstanceTag = br.getBits(4); + const int32_t Profile = br.getBits(2); + const int32_t SamplingFrequencyIndex = br.getBits(4); + const int32_t NumFrontChannelElements = br.getBits(4); + const int32_t NumSideChannelElements = br.getBits(4); + const int32_t NumBackChannelElements = br.getBits(4); + const int32_t NumLfeChannelElements = br.getBits(2); + const int32_t NumAssocDataElements = br.getBits(3); + const int32_t NumValidCcElements = br.getBits(4); + + const int32_t MonoMixdownPresent = br.getBits(1); + + if (MonoMixdownPresent != 0) { + if (br.numBitsLeft() < 4) return ERROR_MALFORMED; + const int32_t MonoMixdownElementNumber = br.getBits(4); + } + + if (br.numBitsLeft() < 1) return ERROR_MALFORMED; + const int32_t StereoMixdownPresent = br.getBits(1); + if (StereoMixdownPresent != 0) { + if (br.numBitsLeft() < 4) return ERROR_MALFORMED; + const int32_t StereoMixdownElementNumber = br.getBits(4); + } + + if (br.numBitsLeft() < 1) return ERROR_MALFORMED; + const int32_t MatrixMixdownIndexPresent = br.getBits(1); + if (MatrixMixdownIndexPresent != 0) { + if (br.numBitsLeft() < 3) return ERROR_MALFORMED; + const int32_t MatrixMixdownIndex = br.getBits(2); + const int32_t PseudoSurroundEnable = br.getBits(1); + } + + int i; + for (i=0; i < NumFrontChannelElements; i++) { + if (br.numBitsLeft() < 5) return ERROR_MALFORMED; + const int32_t FrontElementIsCpe = br.getBits(1); + const int32_t FrontElementTagSelect = br.getBits(4); + channelsNum += FrontElementIsCpe ? 2 : 1; + } + + for (i=0; i < NumSideChannelElements; i++) { + if (br.numBitsLeft() < 5) return ERROR_MALFORMED; + const int32_t SideElementIsCpe = br.getBits(1); + const int32_t SideElementTagSelect = br.getBits(4); + channelsNum += SideElementIsCpe ? 2 : 1; + } + + for (i=0; i < NumBackChannelElements; i++) { + if (br.numBitsLeft() < 5) return ERROR_MALFORMED; + const int32_t BackElementIsCpe = br.getBits(1); + const int32_t BackElementTagSelect = br.getBits(4); + channelsNum += BackElementIsCpe ? 2 : 1; + } + channelsEffectiveNum = channelsNum; + + for (i=0; i < NumLfeChannelElements; i++) { + if (br.numBitsLeft() < 4) return ERROR_MALFORMED; + const int32_t LfeElementTagSelect = br.getBits(4); + channelsNum += 1; + } + ALOGV("mpeg4 audio channelsNum = %d", channelsNum); + ALOGV("mpeg4 audio channelsEffectiveNum = %d", channelsEffectiveNum); + numChannels = channelsNum; + } + } + } + + if (numChannels == 0) { + return ERROR_UNSUPPORTED; + } + + if (mLastTrack == NULL) + return ERROR_MALFORMED; + + int32_t prevSampleRate; + CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate)); + + if (prevSampleRate != sampleRate) { + ALOGV("mpeg4 audio sample rate different from previous setting. " + "was: %d, now: %d", prevSampleRate, sampleRate); + } + + mLastTrack->meta->setInt32(kKeySampleRate, sampleRate); + + int32_t prevChannelCount; + CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount)); + + if (prevChannelCount != numChannels) { + ALOGV("mpeg4 audio channel count different from previous setting. " + "was: %d, now: %d", prevChannelCount, numChannels); + } + + mLastTrack->meta->setInt32(kKeyChannelCount, numChannels); + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +MPEG4Source::MPEG4Source( + const sp<MetaData> &format, + uint32_t timeScale, + const sp<SampleTable> &sampleTable) + : mFormat(format), + mTimescale(timeScale), + mSampleTable(sampleTable) { +} + +MPEG4Source::~MPEG4Source() { +} + +sp<MetaData> MPEG4Source::getFormat() { + return mFormat; +} + +class CompositionSorter +{ +public: + bool LessThan(MediaSource::Indice* aFirst, MediaSource::Indice* aSecond) const + { + return aFirst->start_composition < aSecond->start_composition; + } + + bool Equals(MediaSource::Indice* aFirst, MediaSource::Indice* aSecond) const + { + return aFirst->start_composition == aSecond->start_composition; + } +}; + +nsTArray<MediaSource::Indice> MPEG4Source::exportIndex() +{ + nsTArray<MediaSource::Indice> index; + if (!mTimescale || !mSampleTable.get()) { + return index; + } + + if (!index.SetCapacity(mSampleTable->countSamples(), mozilla::fallible)) { + return index; + } + for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples(); + sampleIndex++) { + off64_t offset; + size_t size; + uint32_t compositionTime; + uint32_t duration; + bool isSyncSample; + uint32_t decodeTime; + if (mSampleTable->getMetaDataForSample(sampleIndex, &offset, &size, + &compositionTime, &duration, + &isSyncSample, &decodeTime) != OK) { + ALOGE("Unexpected sample table problem"); + continue; + } + + Indice indice; + indice.start_offset = offset; + indice.end_offset = offset + size; + indice.start_composition = (compositionTime * 1000000ll) / mTimescale; + // end_composition is overwritten everywhere except the last frame, where + // the presentation duration is equal to the sample duration. + indice.end_composition = + (compositionTime * 1000000ll + duration * 1000000ll) / mTimescale; + indice.sync = isSyncSample; + indice.start_decode = (decodeTime * 1000000ll) / mTimescale; + index.AppendElement(indice); + } + + // Fix up composition durations so we don't end up with any unsightly gaps. + if (index.Length() != 0) { + nsTArray<Indice*> composition_order; + if (!composition_order.SetCapacity(index.Length(), mozilla::fallible)) { + return index; + } + for (uint32_t i = 0; i < index.Length(); i++) { + composition_order.AppendElement(&index[i]); + } + + composition_order.Sort(CompositionSorter()); + for (uint32_t i = 0; i + 1 < composition_order.Length(); i++) { + composition_order[i]->end_composition = + composition_order[i + 1]->start_composition; + } + } + + return index; +} + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp new file mode 100644 index 000000000..e41afcc76 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaBuffer.cpp @@ -0,0 +1,215 @@ +/* + * 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 "MediaBuffer" +#include <utils/Log.h> + +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MetaData.h> + +#include <ui/GraphicBuffer.h> +#include <sys/atomics.h> + +namespace stagefright { + +MediaBuffer::MediaBuffer(void *data, size_t size) + : mObserver(NULL), + mNextBuffer(NULL), + mRefCount(0), + mData(data), + mSize(size), + mRangeOffset(0), + mRangeLength(size), + mOwnsData(false), + mMetaData(new MetaData), + mOriginal(NULL) { +} + +MediaBuffer::MediaBuffer(size_t size) + : mObserver(NULL), + mNextBuffer(NULL), + mRefCount(0), + mData(NULL), + mSize(size), + mRangeOffset(0), + mRangeLength(size), + mOwnsData(true), + mMetaData(new MetaData), + mOriginal(NULL) { + ensuresize(size); +} + +MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer) + : mObserver(NULL), + mNextBuffer(NULL), + mRefCount(0), + mData(NULL), + mSize(1), + mRangeOffset(0), + mRangeLength(mSize), + mGraphicBuffer(graphicBuffer), + mOwnsData(false), + mMetaData(new MetaData), + mOriginal(NULL) { +} + +MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer) + : mObserver(NULL), + mNextBuffer(NULL), + mRefCount(0), + mData(buffer->data()), + mSize(buffer->size()), + mRangeOffset(0), + mRangeLength(mSize), + mBuffer(buffer), + mOwnsData(false), + mMetaData(new MetaData), + mOriginal(NULL) { +} + +void MediaBuffer::release() { + if (mObserver == NULL) { + CHECK_EQ(mRefCount, 0); + delete this; + return; + } + + int prevCount = __atomic_dec(&mRefCount); + if (prevCount == 1) { + if (mObserver == NULL) { + delete this; + return; + } + + mObserver->signalBufferReturned(this); + } + CHECK(prevCount > 0); +} + +void MediaBuffer::claim() { + CHECK(mObserver != NULL); + CHECK_EQ(mRefCount, 1); + + mRefCount = 0; +} + +void MediaBuffer::add_ref() { + (void) __atomic_inc(&mRefCount); +} + +void *MediaBuffer::data() const { + CHECK(mGraphicBuffer == NULL); + return mData; +} + +size_t MediaBuffer::size() const { + CHECK(mGraphicBuffer == NULL); + return mSize; +} + +size_t MediaBuffer::range_offset() const { + return mRangeOffset; +} + +size_t MediaBuffer::range_length() const { + return mRangeLength; +} + +void MediaBuffer::set_range(size_t offset, size_t length) { + if ((mGraphicBuffer == NULL) && (offset + length > mSize)) { + ALOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize); + } + CHECK((mGraphicBuffer != NULL) || (offset + length <= mSize)); + + mRangeOffset = offset; + mRangeLength = length; +} + +sp<GraphicBuffer> MediaBuffer::graphicBuffer() const { + return mGraphicBuffer; +} + +sp<MetaData> MediaBuffer::meta_data() { + return mMetaData; +} + +void MediaBuffer::reset() { + mMetaData->clear(); + set_range(0, mSize); +} + +MediaBuffer::~MediaBuffer() { + CHECK(mObserver == NULL); + + if (mOriginal != NULL) { + mOriginal->release(); + mOriginal = NULL; + } +} + +void MediaBuffer::setObserver(MediaBufferObserver *observer) { + CHECK(observer == NULL || mObserver == NULL); + mObserver = observer; +} + +void MediaBuffer::setNextBuffer(MediaBuffer *buffer) { + mNextBuffer = buffer; +} + +MediaBuffer *MediaBuffer::nextBuffer() { + return mNextBuffer; +} + +int MediaBuffer::refcount() const { + return mRefCount; +} + +MediaBuffer *MediaBuffer::clone() { + CHECK(mGraphicBuffer == NULL); + + MediaBuffer *buffer = new MediaBuffer(mData, mSize); + buffer->set_range(mRangeOffset, mRangeLength); + buffer->mMetaData = new MetaData(*mMetaData.get()); + + add_ref(); + buffer->mOriginal = this; + + return buffer; +} + +bool MediaBuffer::ensuresize(size_t length) { + if (mBufferBackend.Length() >= length) { + return true; + } + // Can't reallocate data we don't owned or shared with another. + if (!mOwnsData || refcount()) { + return false; + } + if (!mBufferBackend.SetLength(length, mozilla::fallible)) { + return false; + } + mData = mBufferBackend.Elements(); + mSize = length; + return true; +} + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp new file mode 100644 index 000000000..a1b520b10 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaDefs.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include <media/stagefright/MediaDefs.h> + +namespace stagefright { + +const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg"; + +const char *MEDIA_MIMETYPE_VIDEO_VP6 = "video/x-vnd.on2.vp6"; +const char *MEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; +const char *MEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; +const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc"; +const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es"; +const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp"; +const char *MEDIA_MIMETYPE_VIDEO_MPEG2 = "video/mpeg2"; +const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw"; + +const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp"; +const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb"; +const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg"; +const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I = "audio/mpeg-L1"; +const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II = "audio/mpeg-L2"; +const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; +const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp"; +const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; +const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw"; +const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw"; +const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; +const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac"; +const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; +const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; + +const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; +const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; +const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg"; +const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska"; +const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts"; +const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi"; +const char *MEDIA_MIMETYPE_CONTAINER_MPEG2PS = "video/mp2p"; + +const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm"; + +const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt"; +const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp new file mode 100644 index 000000000..581a26472 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/MediaSource.cpp @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#include <media/stagefright/MediaSource.h> + +namespace stagefright { + +MediaSource::MediaSource() {} + +MediaSource::~MediaSource() {} + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp new file mode 100644 index 000000000..987541758 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp @@ -0,0 +1,367 @@ +/* + * 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 +#undef LOG_TAG +#define LOG_TAG "MetaData" +#include <utils/Log.h> + +#include <stdlib.h> +#include <string.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MetaData.h> + +namespace stagefright { + +MetaData::MetaData() { +} + +MetaData::MetaData(const MetaData &from) + : RefBase(), + mItems(from.mItems) { +} + +MetaData::~MetaData() { + clear(); +} + +void MetaData::clear() { + mItems.clear(); +} + +bool MetaData::remove(uint32_t key) { + ssize_t i = mItems.indexOfKey(key); + + if (i < 0) { + return false; + } + + mItems.removeItemsAt(i); + + return true; +} + +bool MetaData::setCString(uint32_t key, const char *value) { + return setData(key, TYPE_C_STRING, value, strlen(value) + 1); +} + +bool MetaData::setInt32(uint32_t key, int32_t value) { + return setData(key, TYPE_INT32, &value, sizeof(value)); +} + +bool MetaData::setInt64(uint32_t key, int64_t value) { + return setData(key, TYPE_INT64, &value, sizeof(value)); +} + +bool MetaData::setFloat(uint32_t key, float value) { + return setData(key, TYPE_FLOAT, &value, sizeof(value)); +} + +bool MetaData::setPointer(uint32_t key, void *value) { + return setData(key, TYPE_POINTER, &value, sizeof(value)); +} + +bool MetaData::setRect( + uint32_t key, + int32_t left, int32_t top, + int32_t right, int32_t bottom) { + Rect r; + r.mLeft = left; + r.mTop = top; + r.mRight = right; + r.mBottom = bottom; + + return setData(key, TYPE_RECT, &r, sizeof(r)); +} + +bool MetaData::findCString(uint32_t key, const char **value) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) { + return false; + } + + *value = (const char *)data; + + return true; +} + +bool MetaData::findInt32(uint32_t key, int32_t *value) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_INT32) { + return false; + } + + CHECK_EQ(size, sizeof(*value)); + + *value = *(int32_t *)data; + + return true; +} + +bool MetaData::findInt64(uint32_t key, int64_t *value) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_INT64) { + return false; + } + + CHECK_EQ(size, sizeof(*value)); + + *value = *(int64_t *)data; + + return true; +} + +bool MetaData::findFloat(uint32_t key, float *value) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) { + return false; + } + + CHECK_EQ(size, sizeof(*value)); + + *value = *(float *)data; + + return true; +} + +bool MetaData::findPointer(uint32_t key, void **value) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) { + return false; + } + + CHECK_EQ(size, sizeof(*value)); + + *value = *(void **)data; + + return true; +} + +bool MetaData::findRect( + uint32_t key, + int32_t *left, int32_t *top, + int32_t *right, int32_t *bottom) const { + uint32_t type; + const void *data; + size_t size; + if (!findData(key, &type, &data, &size) || type != TYPE_RECT) { + return false; + } + + CHECK_EQ(size, sizeof(Rect)); + + const Rect *r = (const Rect *)data; + *left = r->mLeft; + *top = r->mTop; + *right = r->mRight; + *bottom = r->mBottom; + + return true; +} + +bool MetaData::setData( + uint32_t key, uint32_t type, const void *data, size_t size) { + bool overwrote_existing = true; + + ssize_t i = mItems.indexOfKey(key); + if (i < 0) { + typed_data item; + i = mItems.add(key, item); + + overwrote_existing = false; + } + + typed_data &item = mItems.editValueAt(i); + + item.setData(type, data, size); + + return overwrote_existing; +} + +bool MetaData::findData(uint32_t key, uint32_t *type, + const void **data, size_t *size) const { + ssize_t i = mItems.indexOfKey(key); + + if (i < 0) { + return false; + } + + const typed_data &item = mItems.valueAt(i); + + item.getData(type, data, size); + + return true; +} + +MetaData::typed_data::typed_data() + : mType(TYPE_NONE), + mSize(0) { +} + +MetaData::typed_data::~typed_data() { + clear(); +} + +MetaData::typed_data::typed_data(const typed_data &from) + : mType(from.mType), + mSize(0) { + if (allocateStorage(from.mSize)) { + memcpy(storage(), from.storage(), mSize); + } +} + +MetaData::typed_data &MetaData::typed_data::operator=( + const MetaData::typed_data &from) { + if (this != &from) { + clear(); + if (allocateStorage(from.mSize)) { + mType = from.mType; + memcpy(storage(), from.storage(), mSize); + } + } + + return *this; +} + +void MetaData::typed_data::clear() { + freeStorage(); + + mType = TYPE_NONE; +} + +void MetaData::typed_data::setData( + uint32_t type, const void *data, size_t size) { + clear(); + + if (allocateStorage(size)) { + mType = type; + memcpy(storage(), data, size); + } +} + +void MetaData::typed_data::getData( + uint32_t *type, const void **data, size_t *size) const { + *type = mType; + *size = mSize; + *data = storage(); +} + +bool MetaData::typed_data::allocateStorage(size_t size) { + // Update mSize now, as it is needed by usesReservoir() below. + // (mSize will be reset if the allocation fails further below.) + mSize = size; + + if (usesReservoir()) { + return true; + } + + u.ext_data = malloc(mSize); + if (!u.ext_data) { + mType = TYPE_NONE; + mSize = 0; + return false; + } + return true; +} + +void MetaData::typed_data::freeStorage() { + if (!usesReservoir()) { + if (u.ext_data) { + free(u.ext_data); + u.ext_data = NULL; + } + } + + mSize = 0; +} + +String8 MetaData::typed_data::asString() const { + String8 out; + const void *data = storage(); + switch(mType) { + case TYPE_NONE: + out = String8::format("no type, size %d)", mSize); + break; + case TYPE_C_STRING: + out = String8::format("(char*) %s", (const char *)data); + break; + case TYPE_INT32: + out = String8::format("(int32_t) %d", *(int32_t *)data); + break; + case TYPE_INT64: + out = String8::format("(int64_t) %lld", *(int64_t *)data); + break; + case TYPE_FLOAT: + out = String8::format("(float) %f", *(float *)data); + break; + case TYPE_POINTER: + out = String8::format("(void*) %p", *(void **)data); + break; + case TYPE_RECT: + { + const Rect *r = (const Rect *)data; + out = String8::format("Rect(%d, %d, %d, %d)", + r->mLeft, r->mTop, r->mRight, r->mBottom); + break; + } + + default: + out = String8::format("(unknown type %d, size %d)", mType, mSize); + if (mSize <= 48) { // if it's less than three lines of hex data, dump it + AString foo; + hexdump(data, mSize, 0, &foo); + out.append("\n"); + out.append(foo.c_str()); + } + break; + } + return out; +} + +static void MakeFourCCString(uint32_t x, char *s) { + s[0] = x >> 24; + s[1] = (x >> 16) & 0xff; + s[2] = (x >> 8) & 0xff; + s[3] = x & 0xff; + s[4] = '\0'; +} + +void MetaData::dumpToLog() const { + for (int i = mItems.size(); --i >= 0;) { + int32_t key = mItems.keyAt(i); + char cc[5]; + MakeFourCCString(key, cc); + const typed_data &item = mItems.valueAt(i); + ALOGI("%s: %s", cc, item.asString().string()); + } +} + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp new file mode 100644 index 000000000..37bb2b7a5 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp @@ -0,0 +1,331 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "SampleIterator" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "include/SampleIterator.h" + +#include <arpa/inet.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/Utils.h> + +#include "include/SampleTable.h" + +namespace stagefright { + +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) { + ALOGV("seekTo(%d)", sampleIndex); + + if (sampleIndex >= mTable->mNumSampleSizes) { + return ERROR_END_OF_STREAM; + } + + 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) { + ALOGE("findChunkRange failed"); + return err; + } + } + + if (sampleIndex >= mStopChunkSampleIndex) { + return ERROR_MALFORMED; + } + + uint32_t chunk = + (sampleIndex - mFirstChunkSampleIndex) / mSamplesPerChunk + + mFirstChunk; + + if (!mInitialized || chunk != mCurrentChunkIndex) { + mCurrentChunkIndex = chunk; + + status_t err; + if ((err = getChunkOffset(chunk, &mCurrentChunkOffset)) != OK) { + ALOGE("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) { + ALOGE("getSampleSizeDirect return error"); + return err; + } + + mCurrentChunkSampleSizes.push(sampleSize); + } + } + + if (mCurrentChunkSampleSizes.size() != mSamplesPerChunk) { + return ERROR_MALFORMED; + } + + uint32_t chunkRelativeSampleIndex = + (sampleIndex - mFirstChunkSampleIndex) % mSamplesPerChunk; + + // This can never happen unless % operator is buggy. + CHECK(chunkRelativeSampleIndex < 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) { + ALOGE("findSampleTime return error"); + return err; + } + + // mTTSDuration is set by findSampleTime() + mCurrentSampleDuration = mTTSDuration; + mCurrentSampleDecodeTime = mTTSSampleTime + mTTSDuration * (sampleIndex - + mTTSSampleIndex); + 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 if (mSamplesPerChunk) { + mStopChunk = 0xffffffff; + mStopChunkSampleIndex = 0xffffffff; + } + + ++mSampleToChunkIndex; + } + + return OK; +} + +status_t SampleIterator::getChunkOffset(uint32_t chunk, off64_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); + + *time += mTable->getCompositionTimeOffset(sampleIndex); + + return OK; +} + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp new file mode 100644 index 000000000..bbb2227e7 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp @@ -0,0 +1,1170 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "SampleTable" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "include/SampleTable.h" +#include "include/SampleIterator.h" + +#include <arpa/inet.h> + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/Utils.h> + +#include <stdint.h> + +namespace stagefright { + +// 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'); + +const uint32_t kAuxTypeCenc = FOURCC('c', 'e', 'n', 'c'); + +static const uint32_t kMAX_ALLOCATION = + (SIZE_MAX < INT32_MAX ? SIZE_MAX : INT32_MAX) - 128; + +//////////////////////////////////////////////////////////////////////////////// + +struct SampleTable::CompositionDeltaLookup { + CompositionDeltaLookup(); + + void setEntries( + const uint32_t *deltaEntries, size_t numDeltaEntries); + + uint32_t getCompositionTimeOffset(uint32_t sampleIndex); + +private: + Mutex mLock; + + const uint32_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 uint32_t *deltaEntries, size_t numDeltaEntries) { + Mutex::Autolock autolock(mLock); + + mDeltaEntries = deltaEntries; + mNumDeltaEntries = numDeltaEntries; + mCurrentDeltaEntry = 0; + mCurrentEntrySampleIndex = 0; +} + +uint32_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(const sp<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), + mSampleTimeEntries(NULL), + mCompositionTimeDeltaEntries(NULL), + mNumCompositionTimeDeltaEntries(0), + mCompositionDeltaLookup(new CompositionDeltaLookup), + mSyncSampleOffset(-1), + mNumSyncSamples(0), + mSyncSamples(NULL), + mLastSyncSampleIndex(0), + mSampleToChunkEntries(NULL), + mCencInfo(NULL), + mCencInfoCount(0), + mCencDefaultSize(0) +{ + mSampleIterator = new SampleIterator(this); +} + +SampleTable::~SampleTable() { + delete[] mSampleToChunkEntries; + mSampleToChunkEntries = NULL; + + delete[] mSyncSamples; + mSyncSamples = NULL; + + delete mCompositionDeltaLookup; + mCompositionDeltaLookup = NULL; + + delete[] mCompositionTimeDeltaEntries; + mCompositionTimeDeltaEntries = NULL; + + delete[] mSampleTimeEntries; + mSampleTimeEntries = NULL; + + delete[] mTimeToSample; + mTimeToSample = NULL; + + if (mCencInfo) { + for (uint32_t i = 0; i < mCencInfoCount; i++) { + if (mCencInfo[i].mSubsamples) { + delete[] mCencInfo[i].mSubsamples; + } + } + delete[] mCencInfo; + } + + delete mSampleIterator; + mSampleIterator = NULL; +} + +bool SampleTable::isValid() const { + return mChunkOffsetOffset >= 0 + && mSampleToChunkOffset >= 0 + && mSampleSizeOffset >= 0 + && mTimeToSample != NULL; +} + +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 + (uint64_t)mNumChunkOffsets * 4) { + return ERROR_MALFORMED; + } + } else { + if (data_size < 8 + (uint64_t)mNumChunkOffsets * 8) { + return ERROR_MALFORMED; + } + } + + return OK; +} + +status_t SampleTable::setSampleToChunkParams( + off64_t data_offset, size_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->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 + (uint64_t)mNumSampleToChunkOffsets * 12) { + return ERROR_MALFORMED; + } + + mSampleToChunkEntries = + new (mozilla::fallible) SampleToChunkEntry[mNumSampleToChunkOffsets]; + if (!mSampleToChunkEntries) { + return ERROR_BUFFER_TOO_SMALL; + } + + 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; + } + + if (!U32_AT(buffer)) { + ALOGE("error reading sample to chunk table"); + return ERROR_MALFORMED; // 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; +} + +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 (type == kSampleSizeType32) { + mSampleSizeFieldSize = 32; + + if (mDefaultSampleSize != 0) { + return OK; + } + + if (data_size < 12 + (uint64_t)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 + ((uint64_t)mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) { + return ERROR_MALFORMED; + } + } + + return OK; +} + +status_t SampleTable::setTimeToSampleParams( + off64_t data_offset, size_t data_size) { + if (mTimeToSample != 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; + } + + if (U32_AT(header) != 0) { + // Expected version = 0, flags = 0. + return ERROR_MALFORMED; + } + + mTimeToSampleCount = U32_AT(&header[4]); + if (mTimeToSampleCount > kMAX_ALLOCATION / 2 / sizeof(uint32_t)) { + // Avoid later overflow. + return ERROR_MALFORMED; + } + + size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2; + + mTimeToSample = new (mozilla::fallible) uint32_t[mTimeToSampleCount * 2]; + if (!mTimeToSample) { + return ERROR_BUFFER_TOO_SMALL; + } + + if (mDataSource->readAt( + 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::setCompositionTimeToSampleParams( + off64_t data_offset, size_t data_size) { + ALOGV("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 numEntries = U32_AT(&header[4]); + + if (U32_AT(header) != 0 && numEntries) { + // Expected version = 0, flags = 0. + return ERROR_MALFORMED; + } + + if (data_size < ((uint64_t)numEntries + 1) * 8) { + return ERROR_MALFORMED; + } + + mNumCompositionTimeDeltaEntries = numEntries; + mCompositionTimeDeltaEntries = new (mozilla::fallible) uint32_t[2 * numEntries]; + if (!mCompositionTimeDeltaEntries) { + return ERROR_BUFFER_TOO_SMALL; + } + + if (mDataSource->readAt( + data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8) + < (ssize_t)numEntries * 8) { + 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; + } + + mSyncSampleOffset = data_offset; + + 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; + } + + mNumSyncSamples = U32_AT(&header[4]); + if (mNumSyncSamples > kMAX_ALLOCATION / sizeof(uint32_t)) { + // Avoid later overflow. + return ERROR_MALFORMED; + } + + if (mNumSyncSamples < 2) { + ALOGV("Table of sync samples is empty or has only a single entry!"); + } + + mSyncSamples = new (mozilla::fallible) uint32_t[mNumSyncSamples]; + if (!mSyncSamples) { + return ERROR_BUFFER_TOO_SMALL; + } + size_t size = mNumSyncSamples * sizeof(uint32_t); + if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size) + != (ssize_t)size) { + return ERROR_IO; + } + + for (size_t i = 0; i < mNumSyncSamples; ++i) { + mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1; + } + + return OK; +} + +static status_t +validateCencBoxHeader( + sp<DataSource>& data_source, off64_t& data_offset, + uint8_t* out_version, uint32_t* out_aux_type) { + *out_aux_type = 0; + + if (data_source->readAt(data_offset++, out_version, 1) < 1) { + ALOGE("error reading sample aux info header"); + return ERROR_IO; + } + + uint32_t flags; + if (!data_source->getUInt24(data_offset, &flags)) { + ALOGE("error reading sample aux info flags"); + return ERROR_IO; + } + data_offset += 3; + + if (flags & 1) { + uint32_t aux_type; + uint32_t aux_param; + if (!data_source->getUInt32(data_offset, &aux_type) || + !data_source->getUInt32(data_offset + 4, &aux_param)) { + ALOGE("error reading aux info type"); + return ERROR_IO; + } + data_offset += 8; + *out_aux_type = aux_type; + } + + return OK; +} + +status_t +SampleTable::setSampleAuxiliaryInformationSizeParams( + off64_t data_offset, size_t data_size, uint32_t drm_scheme) { + off64_t data_end = data_offset + data_size; + + uint8_t version; + uint32_t aux_type; + status_t err = validateCencBoxHeader( + mDataSource, data_offset, &version, &aux_type); + if (err != OK) { + return err; + } + + if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) { + // Quietly skip aux types we don't care about. + return OK; + } + + if (!mCencSizes.IsEmpty() || mCencDefaultSize) { + ALOGE("duplicate cenc saiz box"); + return ERROR_MALFORMED; + } + + if (version) { + ALOGV("unsupported cenc saiz version"); + return ERROR_UNSUPPORTED; + } + + if (mDataSource->readAt( + data_offset++, &mCencDefaultSize, sizeof(mCencDefaultSize)) + < sizeof(mCencDefaultSize)) { + return ERROR_IO; + } + + if (!mDataSource->getUInt32(data_offset, &mCencInfoCount)) { + return ERROR_IO; + } + data_offset += 4; + + if (!mCencDefaultSize) { + if (!mCencSizes.InsertElementsAt(0, mCencInfoCount, mozilla::fallible)) { + return ERROR_IO; + } + if (mDataSource->readAt( + data_offset, mCencSizes.Elements(), mCencInfoCount) + < mCencInfoCount) { + return ERROR_IO; + } + data_offset += mCencInfoCount; + } + + if (data_offset != data_end) { + ALOGW("wrong saiz data size, expected %lu, actual %lu", + data_size, data_offset - (data_end - data_size)); + // Continue, assume extra data is not important. + // Parser will skip past the box end. + } + + return parseSampleCencInfo(); +} + +status_t +SampleTable::setSampleAuxiliaryInformationOffsetParams( + off64_t data_offset, size_t data_size, uint32_t drm_scheme) { + off64_t data_end = data_offset + data_size; + + uint8_t version; + uint32_t aux_type; + status_t err = validateCencBoxHeader(mDataSource, data_offset, + &version, &aux_type); + if (err != OK) { + return err; + } + + if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) { + // Quietly skip aux types we don't care about. + return OK; + } + + if (!mCencOffsets.IsEmpty()) { + ALOGE("duplicate cenc saio box"); + return ERROR_MALFORMED; + } + + uint32_t cencOffsetCount; + if (!mDataSource->getUInt32(data_offset, &cencOffsetCount)) { + ALOGE("error reading cenc aux info offset count"); + return ERROR_IO; + } + data_offset += 4; + + if (cencOffsetCount >= kMAX_ALLOCATION) { + return ERROR_MALFORMED; + } + if (!version) { + if (!mCencOffsets.SetCapacity(cencOffsetCount, mozilla::fallible)) { + return ERROR_MALFORMED; + } + for (uint32_t i = 0; i < cencOffsetCount; i++) { + uint32_t tmp; + if (!mDataSource->getUInt32(data_offset, &tmp)) { + ALOGE("error reading cenc aux info offsets"); + return ERROR_IO; + } + // FIXME: Make this infallible after bug 968520 is done. + MOZ_ALWAYS_TRUE(mCencOffsets.AppendElement(tmp, mozilla::fallible)); + data_offset += 4; + } + } else { + if (!mCencOffsets.SetLength(cencOffsetCount, mozilla::fallible)) { + return ERROR_MALFORMED; + } + for (uint32_t i = 0; i < cencOffsetCount; i++) { + if (!mDataSource->getUInt64(data_offset, &mCencOffsets[i])) { + ALOGE("error reading cenc aux info offsets"); + return ERROR_IO; + } + data_offset += 8; + } + } + + if (data_offset != data_end) { + ALOGW("wrong saio data size, expected %lu, actual %lu", + data_size, data_offset - (data_end - data_size)); + // Continue, assume extra data is not important. + // Parser will skip past the box end. + } + + return parseSampleCencInfo(); +} + +status_t +SampleTable::parseSampleCencInfo() { + if ((!mCencDefaultSize && !mCencInfoCount) || mCencOffsets.IsEmpty()) { + // We don't have all the cenc information we need yet. Quietly fail and + // hope we get the data we need later in the track header. + ALOGV("Got half of cenc saio/saiz pair. Deferring parse until we get the other half."); + return OK; + } + + if ((mCencOffsets.Length() > 1 && mCencOffsets.Length() < mCencInfoCount) || + (!mCencDefaultSize && mCencSizes.Length() < mCencInfoCount)) { + return ERROR_MALFORMED; + } + + if (mCencInfoCount > kMAX_ALLOCATION / sizeof(SampleCencInfo)) { + // Avoid future OOM. + return ERROR_MALFORMED; + } + + mCencInfo = new (mozilla::fallible) SampleCencInfo[mCencInfoCount]; + if (!mCencInfo) { + return ERROR_BUFFER_TOO_SMALL; + } + for (uint32_t i = 0; i < mCencInfoCount; i++) { + mCencInfo[i].mSubsamples = NULL; + } + + uint64_t nextOffset = mCencOffsets[0]; + for (uint32_t i = 0; i < mCencInfoCount; i++) { + uint8_t size = mCencDefaultSize ? mCencDefaultSize : mCencSizes[i]; + uint64_t offset = mCencOffsets.Length() == 1 ? nextOffset : mCencOffsets[i]; + nextOffset = offset + size; + + auto& info = mCencInfo[i]; + + if (size < IV_BYTES) { + ALOGE("cenc aux info too small"); + return ERROR_MALFORMED; + } + + if (mDataSource->readAt(offset, info.mIV, IV_BYTES) < IV_BYTES) { + ALOGE("couldn't read init vector"); + return ERROR_IO; + } + offset += IV_BYTES; + + if (size == IV_BYTES) { + info.mSubsampleCount = 0; + continue; + } + + if (size < IV_BYTES + sizeof(info.mSubsampleCount)) { + ALOGE("subsample count overflows sample aux info buffer"); + return ERROR_MALFORMED; + } + + if (!mDataSource->getUInt16(offset, &info.mSubsampleCount)) { + ALOGE("error reading sample cenc info subsample count"); + return ERROR_IO; + } + offset += sizeof(info.mSubsampleCount); + + if (size < IV_BYTES + sizeof(info.mSubsampleCount) + info.mSubsampleCount * 6) { + ALOGE("subsample descriptions overflow sample aux info buffer"); + return ERROR_MALFORMED; + } + + info.mSubsamples = new (mozilla::fallible) SampleCencInfo::SubsampleSizes[info.mSubsampleCount]; + if (!info.mSubsamples) { + return ERROR_BUFFER_TOO_SMALL; + } + for (uint16_t j = 0; j < info.mSubsampleCount; j++) { + auto& subsample = info.mSubsamples[j]; + if (!mDataSource->getUInt16(offset, &subsample.mClearBytes) || + !mDataSource->getUInt32(offset + sizeof(subsample.mClearBytes), + &subsample.mCipherBytes)) { + ALOGE("error reading cenc subsample aux info"); + return ERROR_IO; + } + offset += 6; + } + } + + 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(uint32_t time1, uint32_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; +} + +status_t SampleTable::buildSampleEntriesTable() { + Mutex::Autolock autoLock(mLock); + + if (mSampleTimeEntries != NULL) { + return OK; + } + + mSampleTimeEntries = new (mozilla::fallible) SampleTimeEntry[mNumSampleSizes]; + if (!mSampleTimeEntries) { + return ERROR_BUFFER_TOO_SMALL; + } + + uint32_t sampleIndex = 0; + uint32_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; + + uint32_t compTimeDelta = + mCompositionDeltaLookup->getCompositionTimeOffset( + sampleIndex); + + mSampleTimeEntries[sampleIndex].mCompositionTime = + sampleTime + compTimeDelta; + } + + ++sampleIndex; + sampleTime += delta; + } + } + + qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry), + CompareIncreasingTime); + return OK; +} + +status_t SampleTable::findSampleAtTime( + uint32_t req_time, uint32_t *sample_index, uint32_t flags) { + status_t err = buildSampleEntriesTable(); + if (err != OK) { + return err; + } + + uint32_t left = 0; + uint32_t right = mNumSampleSizes; + while (left < right) { + uint32_t center = (left + right) / 2; + uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; + + if (req_time < centerTime) { + right = center; + } else if (req_time > centerTime) { + left = center + 1; + } else { + left = center; + break; + } + } + + if (left == mNumSampleSizes) { + if (flags == kFlagAfter) { + return ERROR_OUT_OF_RANGE; + } + + --left; + } + + uint32_t closestIndex = left; + + switch (flags) { + case kFlagBefore: + { + while (closestIndex > 0 + && mSampleTimeEntries[closestIndex].mCompositionTime + > req_time) { + --closestIndex; + } + break; + } + + case kFlagAfter: + { + while (closestIndex + 1 < mNumSampleSizes + && mSampleTimeEntries[closestIndex].mCompositionTime + < req_time) { + ++closestIndex; + } + break; + } + + default: + { + CHECK(flags == kFlagClosest); + + if (closestIndex > 0) { + // Check left neighbour and pick closest. + uint32_t absdiff1 = + abs_difference( + mSampleTimeEntries[closestIndex].mCompositionTime, + req_time); + + uint32_t absdiff2 = + abs_difference( + mSampleTimeEntries[closestIndex - 1].mCompositionTime, + req_time); + + if (absdiff1 > absdiff2) { + closestIndex = closestIndex - 1; + } + } + + 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 = mNumSyncSamples; + while (left < right) { + uint32_t center = left + (right - left) / 2; + uint32_t x = mSyncSamples[center]; + + if (start_sample_index < x) { + right = center; + } else if (start_sample_index > x) { + left = center + 1; + } else { + left = center; + break; + } + } + if (left == mNumSyncSamples) { + if (flags == kFlagAfter) { + ALOGE("tried to find a sync frame after the last one: %d", left); + return ERROR_OUT_OF_RANGE; + } + left = left - 1; + } + + // Now ssi[left] is the sync sample index just before (or at) + // start_sample_index. + // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples. + + uint32_t x = mSyncSamples[left]; + + if (left + 1 < mNumSyncSamples) { + uint32_t y = mSyncSamples[left + 1]; + + // our sample lies between sync samples x and y. + + status_t err = mSampleIterator->seekTo(start_sample_index); + if (err != OK) { + return err; + } + + uint32_t sample_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(x); + if (err != OK) { + return err; + } + uint32_t x_time = mSampleIterator->getSampleTime(); + + err = mSampleIterator->seekTo(y); + if (err != OK) { + return err; + } + + uint32_t y_time = mSampleIterator->getSampleTime(); + + if (abs_difference(x_time, sample_time) + > abs_difference(y_time, sample_time)) { + // Pick the sync sample closest (timewise) to the start-sample. + x = y; + ++left; + } + } + + switch (flags) { + case kFlagBefore: + { + if (x > start_sample_index) { + CHECK(left > 0); + + x = mSyncSamples[left - 1]; + + if (x > start_sample_index) { + // The table of sync sample indices was not sorted + // properly. + return ERROR_MALFORMED; + } + } + break; + } + + case kFlagAfter: + { + if (x < start_sample_index) { + if (left + 1 >= mNumSyncSamples) { + return ERROR_OUT_OF_RANGE; + } + + x = mSyncSamples[left + 1]; + + if (x < start_sample_index) { + // The table of sync sample indices was not sorted + // properly. + return ERROR_MALFORMED; + } + } + + break; + } + + default: + break; + } + + *sample_index = x; + + 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); +} + +status_t SampleTable::getMetaDataForSample( + uint32_t sampleIndex, + off64_t *offset, + size_t *size, + uint32_t *compositionTime, + uint32_t *duration, + bool *isSyncSample, + uint32_t *decodeTime) { + 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 (decodeTime) { + *decodeTime = mSampleIterator->getSampleDecodeTime(); + } + + if (duration) { + *duration = mSampleIterator->getSampleDuration(); + } + + 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; + } + } + + return OK; +} + +uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) { + return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex); +} + +status_t +SampleTable::getSampleCencInfo( + uint32_t sample_index, nsTArray<uint16_t>& clear_sizes, + nsTArray<uint32_t>& cipher_sizes, uint8_t iv[]) { + CHECK(clear_sizes.IsEmpty() && cipher_sizes.IsEmpty()); + + if (sample_index >= mCencInfoCount) { + ALOGE("cenc info requested for out of range sample index"); + return ERROR_MALFORMED; + } + + auto& info = mCencInfo[sample_index]; + clear_sizes.SetCapacity(info.mSubsampleCount); + cipher_sizes.SetCapacity(info.mSubsampleCount); + + for (uint32_t i = 0; i < info.mSubsampleCount; i++) { + clear_sizes.AppendElement(info.mSubsamples[i].mClearBytes); + cipher_sizes.AppendElement(info.mSubsamples[i].mCipherBytes); + } + + memcpy(iv, info.mIV, IV_BYTES); + + return OK; +} + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp b/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp new file mode 100644 index 000000000..abc926239 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/Utils.cpp @@ -0,0 +1,619 @@ +/* + * 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 +#undef LOG_TAG +#define LOG_TAG "Utils" +#include <utils/Log.h> + +#include "include/ESDS.h" + +#include <arpa/inet.h> +#include <cutils/properties.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MediaDefs.h> +#include <media/AudioSystem.h> +#include <media/MediaPlayerInterface.h> +#include <hardware/audio.h> +#include <media/stagefright/Utils.h> +#include <media/AudioParameter.h> + +namespace stagefright { + +uint16_t U16_AT(const uint8_t *ptr) { + return ptr[0] << 8 | ptr[1]; +} + +uint32_t U32_AT(const uint8_t *ptr) { + return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; +} + +uint64_t U64_AT(const uint8_t *ptr) { + return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4); +} + +uint16_t U16LE_AT(const uint8_t *ptr) { + return ptr[0] | (ptr[1] << 8); +} + +uint32_t U32LE_AT(const uint8_t *ptr) { + return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0]; +} + +uint64_t U64LE_AT(const uint8_t *ptr) { + return ((uint64_t)U32LE_AT(ptr + 4)) << 32 | U32LE_AT(ptr); +} + +uint64_t ntoh64(uint64_t x) { + return ((x & 0xFF00000000000000ull) >> 56) | + ((x & 0x00FF000000000000ull) >> 40) | + ((x & 0x0000FF0000000000ull) >> 24) | + ((x & 0x000000FF00000000ull) >> 8) | + ((x & 0x00000000FF000000ull) << 8) | + ((x & 0x0000000000FF0000ull) << 24) | + ((x & 0x000000000000FF00ull) << 40) | + ((x & 0x00000000000000FFull) << 56); +} + +// XXX warning: this won't work on big-endian host. +uint64_t hton64(uint64_t x) { + return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32); +} + +#if 0 +status_t convertMetaDataToMessage( + const sp<MetaData> &meta, sp<AMessage> *format) { + format->clear(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + sp<AMessage> msg = new AMessage; + msg->setString("mime", mime); + + int64_t durationUs; + if (meta->findInt64(kKeyDuration, &durationUs)) { + msg->setInt64("durationUs", durationUs); + } + + int32_t isSync; + if (meta->findInt32(kKeyIsSyncFrame, &isSync) && isSync != 0) { + msg->setInt32("is-sync-frame", 1); + } + + if (!strncasecmp("video/", mime, 6)) { + int32_t width, height; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + + msg->setInt32("width", width); + msg->setInt32("height", height); + + int32_t sarWidth, sarHeight; + if (meta->findInt32(kKeySARWidth, &sarWidth) + && meta->findInt32(kKeySARHeight, &sarHeight)) { + msg->setInt32("sar-width", sarWidth); + msg->setInt32("sar-height", sarHeight); + } + } else if (!strncasecmp("audio/", mime, 6)) { + int32_t numChannels, sampleRate; + CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); + CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + + msg->setInt32("channel-count", numChannels); + msg->setInt32("sample-rate", sampleRate); + + int32_t channelMask; + if (meta->findInt32(kKeyChannelMask, &channelMask)) { + msg->setInt32("channel-mask", channelMask); + } + + int32_t delay = 0; + if (meta->findInt32(kKeyEncoderDelay, &delay)) { + msg->setInt32("encoder-delay", delay); + } + int32_t padding = 0; + if (meta->findInt32(kKeyEncoderPadding, &padding)) { + msg->setInt32("encoder-padding", padding); + } + + int32_t isADTS; + if (meta->findInt32(kKeyIsADTS, &isADTS)) { + msg->setInt32("is-adts", true); + } + } + + int32_t maxInputSize; + if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { + msg->setInt32("max-input-size", maxInputSize); + } + + uint32_t type; + const void *data; + size_t size; + if (meta->findData(kKeyAVCC, &type, &data, &size)) { + // Parse the AVCDecoderConfigurationRecord + + const uint8_t *ptr = (const uint8_t *)data; + + CHECK(size >= 7); + CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 + uint8_t profile = ptr[1]; + uint8_t level = ptr[3]; + + // There is decodable content out there that fails the following + // assertion, let's be lenient for now... + // CHECK((ptr[4] >> 2) == 0x3f); // reserved + + size_t lengthSize = 1 + (ptr[4] & 3); + + // commented out check below as H264_QVGA_500_NO_AUDIO.3gp + // violates it... + // CHECK((ptr[5] >> 5) == 7); // reserved + + size_t numSeqParameterSets = ptr[5] & 31; + + ptr += 6; + size -= 6; + + sp<ABuffer> buffer = new ABuffer(1024); + buffer->setRange(0, 0); + + for (size_t i = 0; i < numSeqParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + buffer->size() + 4, ptr, length); + buffer->setRange(0, buffer->size() + 4 + length); + + ptr += length; + size -= length; + } + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + + msg->setBuffer("csd-0", buffer); + + buffer = new ABuffer(1024); + buffer->setRange(0, 0); + + CHECK(size >= 1); + size_t numPictureParameterSets = *ptr; + ++ptr; + --size; + + for (size_t i = 0; i < numPictureParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + buffer->size() + 4, ptr, length); + buffer->setRange(0, buffer->size() + 4 + length); + + ptr += length; + size -= length; + } + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-1", buffer); + } else if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), (status_t)OK); + + const void *codec_specific_data; + size_t codec_specific_data_size; + esds.getCodecSpecificInfo( + &codec_specific_data, &codec_specific_data_size); + + sp<ABuffer> buffer = new ABuffer(codec_specific_data_size); + + memcpy(buffer->data(), codec_specific_data, + codec_specific_data_size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); + } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + sp<ABuffer> buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); + + if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { + return -EINVAL; + } + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-1", buffer); + } + + *format = msg; + + return OK; +} + +static size_t reassembleAVCC(const sp<ABuffer> &csd0, const sp<ABuffer> csd1, char *avcc) { + + avcc[0] = 1; // version + avcc[1] = 0x64; // profile + avcc[2] = 0; // unused (?) + avcc[3] = 0xd; // level + avcc[4] = 0xff; // reserved+size + + size_t i = 0; + int numparams = 0; + int lastparamoffset = 0; + int avccidx = 6; + do { + if (i >= csd0->size() - 4 || + memcmp(csd0->data() + i, "\x00\x00\x00\x01", 4) == 0) { + if (i >= csd0->size() - 4) { + // there can't be another param here, so use all the rest + i = csd0->size(); + } + ALOGV("block at %d, last was %d", i, lastparamoffset); + if (lastparamoffset > 0) { + int size = i - lastparamoffset; + avcc[avccidx++] = size >> 8; + avcc[avccidx++] = size & 0xff; + memcpy(avcc+avccidx, csd0->data() + lastparamoffset, size); + avccidx += size; + numparams++; + } + i += 4; + lastparamoffset = i; + } else { + i++; + } + } while(i < csd0->size()); + ALOGV("csd0 contains %d params", numparams); + + avcc[5] = 0xe0 | numparams; + //and now csd-1 + i = 0; + numparams = 0; + lastparamoffset = 0; + int numpicparamsoffset = avccidx; + avccidx++; + do { + if (i >= csd1->size() - 4 || + memcmp(csd1->data() + i, "\x00\x00\x00\x01", 4) == 0) { + if (i >= csd1->size() - 4) { + // there can't be another param here, so use all the rest + i = csd1->size(); + } + ALOGV("block at %d, last was %d", i, lastparamoffset); + if (lastparamoffset > 0) { + int size = i - lastparamoffset; + avcc[avccidx++] = size >> 8; + avcc[avccidx++] = size & 0xff; + memcpy(avcc+avccidx, csd1->data() + lastparamoffset, size); + avccidx += size; + numparams++; + } + i += 4; + lastparamoffset = i; + } else { + i++; + } + } while(i < csd1->size()); + avcc[numpicparamsoffset] = numparams; + return avccidx; +} + +static void reassembleESDS(const sp<ABuffer> &csd0, char *esds) { + int csd0size = csd0->size(); + esds[0] = 3; // kTag_ESDescriptor; + int esdescriptorsize = 26 + csd0size; + CHECK(esdescriptorsize < 268435456); // 7 bits per byte, so max is 2^28-1 + esds[1] = 0x80 | (esdescriptorsize >> 21); + esds[2] = 0x80 | ((esdescriptorsize >> 14) & 0x7f); + esds[3] = 0x80 | ((esdescriptorsize >> 7) & 0x7f); + esds[4] = (esdescriptorsize & 0x7f); + esds[5] = esds[6] = 0; // es id + esds[7] = 0; // flags + esds[8] = 4; // kTag_DecoderConfigDescriptor + int configdescriptorsize = 18 + csd0size; + esds[9] = 0x80 | (configdescriptorsize >> 21); + esds[10] = 0x80 | ((configdescriptorsize >> 14) & 0x7f); + esds[11] = 0x80 | ((configdescriptorsize >> 7) & 0x7f); + esds[12] = (configdescriptorsize & 0x7f); + esds[13] = 0x40; // objectTypeIndication + esds[14] = 0x15; // not sure what 14-25 mean, they are ignored by ESDS.cpp, + esds[15] = 0x00; // but the actual values here were taken from a real file. + esds[16] = 0x18; + esds[17] = 0x00; + esds[18] = 0x00; + esds[19] = 0x00; + esds[20] = 0xfa; + esds[21] = 0x00; + esds[22] = 0x00; + esds[23] = 0x00; + esds[24] = 0xfa; + esds[25] = 0x00; + esds[26] = 5; // kTag_DecoderSpecificInfo; + esds[27] = 0x80 | (csd0size >> 21); + esds[28] = 0x80 | ((csd0size >> 14) & 0x7f); + esds[29] = 0x80 | ((csd0size >> 7) & 0x7f); + esds[30] = (csd0size & 0x7f); + memcpy((void*)&esds[31], csd0->data(), csd0size); + // data following this is ignored, so don't bother appending it + +} + +void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) { + AString mime; + if (msg->findString("mime", &mime)) { + meta->setCString(kKeyMIMEType, mime.c_str()); + } else { + ALOGW("did not find mime type"); + } + + int64_t durationUs; + if (msg->findInt64("durationUs", &durationUs)) { + meta->setInt64(kKeyDuration, durationUs); + } + + int32_t isSync; + if (msg->findInt32("is-sync-frame", &isSync) && isSync != 0) { + meta->setInt32(kKeyIsSyncFrame, 1); + } + + if (mime.startsWith("video/")) { + int32_t width; + int32_t height; + if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) { + meta->setInt32(kKeyWidth, width); + meta->setInt32(kKeyHeight, height); + } else { + ALOGW("did not find width and/or height"); + } + + int32_t sarWidth, sarHeight; + if (msg->findInt32("sar-width", &sarWidth) + && msg->findInt32("sar-height", &sarHeight)) { + meta->setInt32(kKeySARWidth, sarWidth); + meta->setInt32(kKeySARHeight, sarHeight); + } + } else if (mime.startsWith("audio/")) { + int32_t numChannels; + if (msg->findInt32("channel-count", &numChannels)) { + meta->setInt32(kKeyChannelCount, numChannels); + } + int32_t sampleRate; + if (msg->findInt32("sample-rate", &sampleRate)) { + meta->setInt32(kKeySampleRate, sampleRate); + } + int32_t channelMask; + if (msg->findInt32("channel-mask", &channelMask)) { + meta->setInt32(kKeyChannelMask, channelMask); + } + int32_t delay = 0; + if (msg->findInt32("encoder-delay", &delay)) { + meta->setInt32(kKeyEncoderDelay, delay); + } + int32_t padding = 0; + if (msg->findInt32("encoder-padding", &padding)) { + meta->setInt32(kKeyEncoderPadding, padding); + } + + int32_t isADTS; + if (msg->findInt32("is-adts", &isADTS)) { + meta->setInt32(kKeyIsADTS, isADTS); + } + } + + int32_t maxInputSize; + if (msg->findInt32("max-input-size", &maxInputSize)) { + meta->setInt32(kKeyMaxInputSize, maxInputSize); + } + + // reassemble the csd data into its original form + sp<ABuffer> csd0; + if (msg->findBuffer("csd-0", &csd0)) { + if (mime.startsWith("video/")) { // do we need to be stricter than this? + sp<ABuffer> csd1; + if (msg->findBuffer("csd-1", &csd1)) { + char avcc[1024]; // that oughta be enough, right? + size_t outsize = reassembleAVCC(csd0, csd1, avcc); + meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize); + } + } else if (mime.startsWith("audio/")) { + int csd0size = csd0->size(); + char esds[csd0size + 31]; + reassembleESDS(csd0, esds); + meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); + } + } + + // XXX TODO add whatever other keys there are + +#if 0 + ALOGI("converted %s to:", msg->debugString(0).c_str()); + meta->dumpToLog(); +#endif +} + +AString MakeUserAgent() { + AString ua; + ua.append("stagefright/1.2 (Linux;Android "); + +#if (PROPERTY_VALUE_MAX < 8) +#error "PROPERTY_VALUE_MAX must be at least 8" +#endif + + char value[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.release", value, "Unknown"); + ua.append(value); + ua.append(")"); + + return ua; +} + +status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, + const sp<MetaData>& meta) +{ + int32_t sampleRate = 0; + int32_t bitRate = 0; + int32_t channelMask = 0; + int32_t delaySamples = 0; + int32_t paddingSamples = 0; + + AudioParameter param = AudioParameter(); + + if (meta->findInt32(kKeySampleRate, &sampleRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate); + } + if (meta->findInt32(kKeyChannelMask, &channelMask)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask); + } + if (meta->findInt32(kKeyBitRate, &bitRate)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate); + } + if (meta->findInt32(kKeyEncoderDelay, &delaySamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples); + } + if (meta->findInt32(kKeyEncoderPadding, &paddingSamples)) { + param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples); + } + + ALOGV("sendMetaDataToHal: bitRate %d, sampleRate %d, chanMask %d," + "delaySample %d, paddingSample %d", bitRate, sampleRate, + channelMask, delaySamples, paddingSamples); + + sink->setParameters(param.toString()); + return OK; +} + +struct mime_conv_t { + const char* mime; + audio_format_t format; +}; + +static const struct mime_conv_t mimeLookup[] = { + { MEDIA_MIMETYPE_AUDIO_MPEG, AUDIO_FORMAT_MP3 }, + { MEDIA_MIMETYPE_AUDIO_RAW, AUDIO_FORMAT_PCM_16_BIT }, + { MEDIA_MIMETYPE_AUDIO_AMR_NB, AUDIO_FORMAT_AMR_NB }, + { MEDIA_MIMETYPE_AUDIO_AMR_WB, AUDIO_FORMAT_AMR_WB }, + { MEDIA_MIMETYPE_AUDIO_AAC, AUDIO_FORMAT_AAC }, + { MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS }, + { 0, AUDIO_FORMAT_INVALID } +}; + +status_t mapMimeToAudioFormat( audio_format_t& format, const char* mime ) +{ +const struct mime_conv_t* p = &mimeLookup[0]; + while (p->mime != NULL) { + if (0 == strcasecmp(mime, p->mime)) { + format = p->format; + return OK; + } + ++p; + } + + return BAD_VALUE; +} + +bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) +{ + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + audio_offload_info_t info = AUDIO_INFO_INITIALIZER; + + info.format = AUDIO_FORMAT_INVALID; + if (mapMimeToAudioFormat(info.format, mime) != OK) { + ALOGE(" Couldn't map mime type \"%s\" to a valid AudioSystem::audio_format !", mime); + return false; + } else { + ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info.format); + } + + if (AUDIO_FORMAT_INVALID == info.format) { + // can't offload if we don't know what the source format is + ALOGE("mime type \"%s\" not a known audio format", mime); + return false; + } + + int32_t srate = -1; + if (!meta->findInt32(kKeySampleRate, &srate)) { + ALOGV("track of type '%s' does not publish sample rate", mime); + } + info.sample_rate = srate; + + int32_t cmask = 0; + if (!meta->findInt32(kKeyChannelMask, &cmask)) { + ALOGV("track of type '%s' does not publish channel mask", mime); + + // Try a channel count instead + int32_t channelCount; + if (!meta->findInt32(kKeyChannelCount, &channelCount)) { + ALOGV("track of type '%s' does not publish channel count", mime); + } else { + cmask = audio_channel_out_mask_from_count(channelCount); + } + } + info.channel_mask = cmask; + + int64_t duration = 0; + if (!meta->findInt64(kKeyDuration, &duration)) { + ALOGV("track of type '%s' does not publish duration", mime); + } + info.duration_us = duration; + + int32_t brate = -1; + if (!meta->findInt32(kKeyBitRate, &brate)) { + ALOGV("track of type '%s' does not publish bitrate", mime); + } + info.bit_rate = brate; + + + info.stream_type = AUDIO_STREAM_MUSIC; + info.has_video = hasVideo; + info.is_streaming = isStreaming; + + // Check if offload is possible for given format, stream type, sample rate, + // bit rate, duration, video and streaming + return AudioSystem::isOffloadSupported(info); +} + +#endif + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp new file mode 100644 index 000000000..4210cdaa6 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AAtomizer.cpp @@ -0,0 +1,67 @@ +/* + * 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 <sys/types.h> + +#include "AAtomizer.h" + +namespace stagefright { + +// static +AAtomizer AAtomizer::gAtomizer; + +// static +const char *AAtomizer::Atomize(const char *name) { + return gAtomizer.atomize(name); +} + +AAtomizer::AAtomizer() { + for (size_t i = 0; i < 128; ++i) { + mAtoms.push(List<AString>()); + } +} + +const char *AAtomizer::atomize(const char *name) { + Mutex::Autolock autoLock(mLock); + + const size_t n = mAtoms.size(); + size_t index = AAtomizer::Hash(name) % n; + List<AString> &entry = mAtoms.editItemAt(index); + List<AString>::iterator it = entry.begin(); + while (it != entry.end()) { + if ((*it) == name) { + return (*it).c_str(); + } + ++it; + } + + entry.push_back(AString(name)); + + return (*--entry.end()).c_str(); +} + +// static +uint32_t AAtomizer::Hash(const char *s) { + uint32_t sum = 0; + while (*s != '\0') { + sum = (sum * 31) + *s; + ++s; + } + + return sum; +} + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp new file mode 100644 index 000000000..22850ef02 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABitReader.cpp @@ -0,0 +1,103 @@ +/* + * 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 "ABitReader.h" + +#include <log/log.h> +#include <media/stagefright/foundation/ADebug.h> + +namespace stagefright { + +ABitReader::ABitReader(const uint8_t *data, size_t size) + : mData(data), + mSize(size), + mReservoir(0), + mNumBitsLeft(0) { +} + +void ABitReader::fillReservoir() { + CHECK_GT(mSize, 0u); + + mReservoir = 0; + size_t i; + for (i = 0; mSize > 0 && i < 4; ++i) { + mReservoir = (mReservoir << 8) | *mData; + + ++mData; + --mSize; + } + + mNumBitsLeft = 8 * i; + mReservoir <<= 32 - mNumBitsLeft; +} + +uint32_t ABitReader::getBits(size_t n) { + CHECK_LE(n, 32u); + + uint32_t result = 0; + while (n > 0) { + if (mNumBitsLeft == 0) { + fillReservoir(); + } + + size_t m = n; + if (m > mNumBitsLeft) { + m = mNumBitsLeft; + } + + result = (result << m) | (mReservoir >> (32 - m)); + mReservoir <<= m; + mNumBitsLeft -= m; + + n -= m; + } + + return result; +} + +void ABitReader::skipBits(size_t n) { + while (n > 32) { + getBits(32); + n -= 32; + } + + if (n > 0) { + getBits(n); + } +} + +void ABitReader::putBits(uint32_t x, size_t n) { + CHECK_LE(n, 32u); + + while (mNumBitsLeft + n > 32) { + mNumBitsLeft -= 8; + --mData; + ++mSize; + } + + mReservoir = (mReservoir >> n) | (x << (32 - n)); + mNumBitsLeft += n; +} + +size_t ABitReader::numBitsLeft() const { + return mSize * 8 + mNumBitsLeft; +} + +const uint8_t *ABitReader::data() const { + return mData - (mNumBitsLeft + 7) / 8; +} + +} // namespace stagefright diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp new file mode 100644 index 000000000..53f5ebc69 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/ABuffer.cpp @@ -0,0 +1,76 @@ +/* + * 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 "ABuffer.h" + +#include "ADebug.h" +#include "ALooper.h" +#include "AMessage.h" + +namespace stagefright { + +ABuffer::ABuffer(size_t capacity) + : mData(malloc(capacity)), + mCapacity(capacity), + mRangeOffset(0), + mRangeLength(capacity), + mInt32Data(0), + mOwnsData(true) { +} + +ABuffer::ABuffer(void *data, size_t capacity) + : mData(data), + mCapacity(capacity), + mRangeOffset(0), + mRangeLength(capacity), + mInt32Data(0), + mOwnsData(false) { +} + +ABuffer::~ABuffer() { + if (mOwnsData) { + if (mData != NULL) { + free(mData); + mData = NULL; + } + } + + if (mFarewell != NULL) { + mFarewell->post(); + } +} + +void ABuffer::setRange(size_t offset, size_t size) { + CHECK_LE(offset, mCapacity); + CHECK_LE(offset + size, mCapacity); + + mRangeOffset = offset; + mRangeLength = size; +} + +void ABuffer::setFarewellMessage(const sp<AMessage> msg) { + mFarewell = msg; +} + +sp<AMessage> ABuffer::meta() { + if (mMeta == NULL) { + mMeta = new AMessage; + } + return mMeta; +} + +} // namespace stagefright + diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp new file mode 100644 index 000000000..79c944b58 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/AString.cpp @@ -0,0 +1,347 @@ +/* + * 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 <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "ADebug.h" +#include "AString.h" + +namespace stagefright { + +// static +const char *AString::kEmptyString = ""; + +AString::AString() + : mData((char *)kEmptyString), + mSize(0), + mAllocSize(1) { +} + +AString::AString(const char *s) + : mData(NULL), + mSize(0), + mAllocSize(1) { + setTo(s); +} + +AString::AString(const char *s, size_t size) + : mData(NULL), + mSize(0), + mAllocSize(1) { + setTo(s, size); +} + +AString::AString(const AString &from) + : mData(NULL), + mSize(0), + mAllocSize(1) { + setTo(from, 0, from.size()); +} + +AString::AString(const AString &from, size_t offset, size_t n) + : mData(NULL), + mSize(0), + mAllocSize(1) { + setTo(from, offset, n); +} + +AString::~AString() { + clear(); +} + +AString &AString::operator=(const AString &from) { + if (&from != this) { + setTo(from, 0, from.size()); + } + + return *this; +} + +size_t AString::size() const { + return mSize; +} + +const char *AString::c_str() const { + return mData; +} + +bool AString::empty() const { + return mSize == 0; +} + +void AString::setTo(const char *s) { + setTo(s, strlen(s)); +} + +void AString::setTo(const char *s, size_t size) { + clear(); + append(s, size); +} + +void AString::setTo(const AString &from, size_t offset, size_t n) { + CHECK(&from != this); + + clear(); + setTo(from.mData + offset, n); +} + +void AString::clear() { + if (mData && mData != kEmptyString) { + free(mData); + mData = NULL; + } + + mData = (char *)kEmptyString; + mSize = 0; + mAllocSize = 1; +} + +size_t AString::hash() const { + size_t x = 0; + for (size_t i = 0; i < mSize; ++i) { + x = (x * 31) + mData[i]; + } + + return x; +} + +bool AString::operator==(const AString &other) const { + return mSize == other.mSize && !memcmp(mData, other.mData, mSize); +} + +void AString::trim() { + makeMutable(); + + size_t i = 0; + while (i < mSize && isspace(mData[i])) { + ++i; + } + + size_t j = mSize; + while (j > i && isspace(mData[j - 1])) { + --j; + } + + memmove(mData, &mData[i], j - i); + mSize = j - i; + mData[mSize] = '\0'; +} + +void AString::erase(size_t start, size_t n) { + CHECK_LT(start, mSize); + CHECK_LE(start + n, mSize); + + makeMutable(); + + memmove(&mData[start], &mData[start + n], mSize - start - n); + mSize -= n; + mData[mSize] = '\0'; +} + +void AString::makeMutable() { + if (mData == kEmptyString) { + mData = strdup(kEmptyString); + } +} + +void AString::append(const char *s) { + append(s, strlen(s)); +} + +void AString::append(const char *s, size_t size) { + makeMutable(); + + if (mSize + size + 1 > mAllocSize) { + mAllocSize = (mAllocSize + size + 31) & -32; + mData = (char *)realloc(mData, mAllocSize); + CHECK(mData != NULL); + } + + memcpy(&mData[mSize], s, size); + mSize += size; + mData[mSize] = '\0'; +} + +void AString::append(const AString &from) { + append(from.c_str(), from.size()); +} + +void AString::append(const AString &from, size_t offset, size_t n) { + append(from.c_str() + offset, n); +} + +void AString::append(int x) { + char s[16]; + sprintf(s, "%d", x); + + append(s); +} + +void AString::append(unsigned x) { + char s[16]; + sprintf(s, "%u", x); + + append(s); +} + +void AString::append(long x) { + char s[16]; + sprintf(s, "%ld", x); + + append(s); +} + +void AString::append(unsigned long x) { + char s[16]; + sprintf(s, "%lu", x); + + append(s); +} + +void AString::append(long long x) { + char s[32]; + sprintf(s, "%lld", x); + + append(s); +} + +void AString::append(unsigned long long x) { + char s[32]; + sprintf(s, "%llu", x); + + append(s); +} + +void AString::append(float x) { + char s[16]; + sprintf(s, "%f", x); + + append(s); +} + +void AString::append(double x) { + char s[16]; + sprintf(s, "%f", x); + + append(s); +} + +void AString::append(void *x) { + char s[16]; + sprintf(s, "%p", x); + + append(s); +} + +ssize_t AString::find(const char *substring, size_t start) const { + CHECK_LE(start, size()); + + const char *match = strstr(mData + start, substring); + + if (match == NULL) { + return -1; + } + + return match - mData; +} + +void AString::insert(const AString &from, size_t insertionPos) { + insert(from.c_str(), from.size(), insertionPos); +} + +void AString::insert(const char *from, size_t size, size_t insertionPos) { + CHECK_GE(insertionPos, 0u); + CHECK_LE(insertionPos, mSize); + + makeMutable(); + + if (mSize + size + 1 > mAllocSize) { + mAllocSize = (mAllocSize + size + 31) & -32; + mData = (char *)realloc(mData, mAllocSize); + CHECK(mData != NULL); + } + + memmove(&mData[insertionPos + size], + &mData[insertionPos], mSize - insertionPos + 1); + + memcpy(&mData[insertionPos], from, size); + + mSize += size; +} + +bool AString::operator<(const AString &other) const { + return compare(other) < 0; +} + +bool AString::operator>(const AString &other) const { + return compare(other) > 0; +} + +int AString::compare(const AString &other) const { + return strcmp(mData, other.mData); +} + +void AString::tolower() { + makeMutable(); + + for (size_t i = 0; i < mSize; ++i) { + mData[i] = ::tolower(mData[i]); + } +} + +bool AString::startsWith(const char *prefix) const { + return !strncmp(mData, prefix, strlen(prefix)); +} + +bool AString::endsWith(const char *suffix) const { + size_t suffixLen = strlen(suffix); + + if (mSize < suffixLen) { + return false; + } + + return !strcmp(mData + mSize - suffixLen, suffix); +} + +AString StringPrintf(const char *format, ...) { + va_list ap; + va_start(ap, format); + + char *buffer; +#ifdef WIN32 + int n = vsnprintf(NULL, 0, format, ap); + buffer = new char[n+1]; + vsnprintf(buffer, n+1, format, ap); +#else + vasprintf(&buffer, format, ap); +#endif + + va_end(ap); + + AString result(buffer); + + free(buffer); + buffer = NULL; + + return result; +} + +} // namespace stagefright + diff --git a/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp new file mode 100644 index 000000000..7b566eb23 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/foundation/hexdump.cpp @@ -0,0 +1,96 @@ +/* + * 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_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "hexdump" +#include <utils/Log.h> + +#include "hexdump.h" + +#include "ADebug.h" +#include "AString.h" + +#include <ctype.h> +#include <stdint.h> +#include <stdio.h> + +namespace stagefright { + +static void appendIndent(AString *s, int32_t indent) { + static const char kWhitespace[] = + " " + " "; + + CHECK_LT((size_t)indent, sizeof(kWhitespace)); + + s->append(kWhitespace, indent); +} + +void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) { + const uint8_t *data = (const uint8_t *)_data; + + size_t offset = 0; + while (offset < size) { + AString line; + + appendIndent(&line, indent); + + char tmp[32]; + sprintf(tmp, "%08lx: ", (unsigned long)offset); + + line.append(tmp); + + for (size_t i = 0; i < 16; ++i) { + if (i == 8) { + line.append(' '); + } + if (offset + i >= size) { + line.append(" "); + } else { + sprintf(tmp, "%02x ", data[offset + i]); + line.append(tmp); + } + } + + line.append(' '); + + for (size_t i = 0; i < 16; ++i) { + if (offset + i >= size) { + break; + } + + if (isprint(data[offset + i])) { + line.append((char)data[offset + i]); + } else { + line.append('.'); + } + } + + if (appendTo != NULL) { + appendTo->append(line); + appendTo->append("\n"); + } else { + ALOGI("%s", line.c_str()); + } + + offset += 16; + } +} + +} // namespace stagefright + +#undef LOG_TAG diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h new file mode 100644 index 000000000..cdfc98e88 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/AMRExtractor.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef AMR_EXTRACTOR_H_ + +#define AMR_EXTRACTOR_H_ + +#include <utils/Errors.h> +#include <media/stagefright/MediaExtractor.h> + +namespace stagefright { + +struct AMessage; +class String8; +#define OFFSET_TABLE_LEN 300 + +class AMRExtractor : public MediaExtractor { +public: + AMRExtractor(const sp<DataSource> &source); + + virtual size_t countTracks(); + virtual sp<MediaSource> getTrack(size_t index); + virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); + + virtual sp<MetaData> getMetaData(); + +protected: + virtual ~AMRExtractor(); + +private: + sp<DataSource> mDataSource; + sp<MetaData> mMeta; + status_t mInitCheck; + bool mIsWide; + + off64_t mOffsetTable[OFFSET_TABLE_LEN]; //5 min + size_t mOffsetTableLength; + + AMRExtractor(const AMRExtractor &); + AMRExtractor &operator=(const AMRExtractor &); +}; + +bool SniffAMR( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + +} // namespace stagefright + +#endif // AMR_EXTRACTOR_H_ diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h b/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h new file mode 100644 index 000000000..bc4c5a6e5 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/ESDS.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ESDS_H_ + +#define ESDS_H_ + +#include <stdint.h> + +#include <media/stagefright/MediaErrors.h> + +namespace stagefright { + +class ESDS { +public: + ESDS(const void *data, size_t size); + ~ESDS(); + + status_t InitCheck() const; + + status_t getObjectTypeIndication(uint8_t *objectTypeIndication) const; + status_t getCodecSpecificInfo(const void **data, size_t *size) const; + status_t getCodecSpecificOffset(size_t *offset, size_t *size) const; + status_t getBitRate(uint32_t *brateMax, uint32_t *brateAvg) const; + status_t getStreamType(uint8_t *streamType) const; + +private: + enum { + kTag_ESDescriptor = 0x03, + kTag_DecoderConfigDescriptor = 0x04, + kTag_DecoderSpecificInfo = 0x05 + }; + + uint8_t *mData; + size_t mSize; + + status_t mInitCheck; + + size_t mDecoderSpecificOffset; + size_t mDecoderSpecificLength; + uint8_t mObjectTypeIndication; + uint8_t mStreamType; + uint32_t mBitRateMax; + uint32_t mBitRateAvg; + + status_t skipDescriptorHeader( + size_t offset, size_t size, + uint8_t *tag, size_t *data_offset, size_t *data_size) const; + + status_t parse(); + status_t parseESDescriptor(size_t offset, size_t size); + status_t parseDecoderConfigDescriptor(size_t offset, size_t size); + + ESDS(const ESDS &); + ESDS &operator=(const ESDS &); +}; + +} // namespace stagefright +#endif // ESDS_H_ diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h new file mode 100644 index 000000000..86579d39b --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/MPEG4Extractor.h @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#ifndef MPEG4_EXTRACTOR_H_ + +#define MPEG4_EXTRACTOR_H_ + +#include <arpa/inet.h> + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/Utils.h> +#include <utils/List.h> +#include <utils/String8.h> +#include "nsTArray.h" + +namespace stagefright { + +struct AMessage; +class DataSource; +class SampleTable; +class String8; + +struct SidxEntry { + size_t mSize; + uint32_t mDurationUs; +}; + +class MPEG4Extractor : public MediaExtractor { +public: + MPEG4Extractor(const sp<DataSource> &source); + + size_t countTracks() override; + sp<MediaSource> getTrack(size_t index) override; + sp<MetaData> getTrackMetaData(size_t index, uint32_t flags) override; + + sp<MetaData> getMetaData() override; + uint32_t flags() const override; + + // for DRM + char* getDrmTrackInfo(size_t trackID, int *len) override; + +protected: + virtual ~MPEG4Extractor(); + +private: + + struct PsshInfo { + uint8_t uuid[16]; + uint32_t datalen; + uint8_t *data; + }; + struct Track { + Track *next; + sp<MetaData> meta; + uint32_t timescale; + // Temporary storage for elst until we've + // parsed mdhd and can interpret them. + uint64_t empty_duration; + uint64_t segment_duration; + int64_t media_time; + + sp<SampleTable> sampleTable; + bool includes_expensive_metadata; + bool skipTrack; + }; + + nsTArray<SidxEntry> mSidxEntries; + uint64_t mSidxDuration; + + nsTArray<PsshInfo> mPssh; + + sp<DataSource> mDataSource; + status_t mInitCheck; + bool mHasVideo; + uint32_t mHeaderTimescale; + + Track *mFirstTrack, *mLastTrack; + + sp<MetaData> mFileMetaData; + + nsTArray<uint32_t> mPath; + String8 mLastCommentMean; + String8 mLastCommentName; + String8 mLastCommentData; + + status_t readMetaData(); + status_t parseChunk(off64_t *offset, int depth); + status_t parseMetaData(off64_t offset, size_t size); + + status_t updateAudioTrackInfoFromESDS_MPEG4Audio( + const void *esds_data, size_t esds_size); + + static status_t verifyTrack(Track *track); + + struct SINF { + SINF *next; + uint16_t trackID; + uint8_t IPMPDescriptorID; + ssize_t len; + char *IPMPData; + }; + + SINF *mFirstSINF; + + bool mIsDrm; + uint32_t mDrmScheme; + + status_t parseDrmSINF(off64_t *offset, off64_t data_offset); + + status_t parseTrackHeader(off64_t data_offset, off64_t data_size); + + status_t parseSegmentIndex(off64_t data_offset, size_t data_size); + + void storeEditList(); + + MPEG4Extractor(const MPEG4Extractor &); + MPEG4Extractor &operator=(const MPEG4Extractor &); +}; + +} // namespace stagefright + +#endif // MPEG4_EXTRACTOR_H_ diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h new file mode 100644 index 000000000..907ea39f1 --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleIterator.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef SAMPLE_ITERATOR_H_ +#define SAMPLE_ITERATOR_H_ + +#include <utils/Vector.h> + +namespace stagefright { + +class SampleTable; + +struct SampleIterator { + SampleIterator(SampleTable *table); + + status_t seekTo(uint32_t sampleIndex); + + uint32_t getChunkIndex() const { return mCurrentChunkIndex; } + uint32_t getDescIndex() const { return mChunkDesc; } + off64_t getSampleOffset() const { return mCurrentSampleOffset; } + size_t getSampleSize() const { return mCurrentSampleSize; } + uint32_t getSampleTime() const { return mCurrentSampleTime; } + uint32_t getSampleDecodeTime() const { return mCurrentSampleDecodeTime; } + uint32_t getSampleDuration() const { return mCurrentSampleDuration; } + + 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; + off64_t mCurrentChunkOffset; + Vector<size_t> mCurrentChunkSampleSizes; + + uint32_t mTimeToSampleIndex; + uint32_t mTTSSampleIndex; + uint32_t mTTSSampleTime; + uint32_t mTTSCount; + uint32_t mTTSDuration; + + uint32_t mCurrentSampleIndex; + off64_t mCurrentSampleOffset; + size_t mCurrentSampleSize; + uint32_t mCurrentSampleTime; + uint32_t mCurrentSampleDecodeTime; + uint32_t mCurrentSampleDuration; + + void reset(); + status_t findChunkRange(uint32_t sampleIndex); + status_t getChunkOffset(uint32_t chunk, off64_t *offset); + status_t findSampleTime(uint32_t sampleIndex, uint32_t *time); + + SampleIterator(const SampleIterator &); + SampleIterator &operator=(const SampleIterator &); +}; + +} // namespace stagefright + +#endif diff --git a/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h new file mode 100644 index 000000000..e115c92bb --- /dev/null +++ b/media/libstagefright/frameworks/av/media/libstagefright/include/SampleTable.h @@ -0,0 +1,188 @@ +/* + * 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. + */ + +#ifndef SAMPLE_TABLE_H_ + +#define SAMPLE_TABLE_H_ + +#include <sys/types.h> +#include <stdint.h> + +#include <media/stagefright/MediaErrors.h> +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace stagefright { + +class DataSource; +struct SampleIterator; + +class SampleTable : public RefBase { +public: + SampleTable(const sp<DataSource> &source); + + bool isValid() const; + + // type can be 'stco' or 'co64'. + status_t setChunkOffsetParams( + uint32_t type, off64_t data_offset, size_t data_size); + + status_t setSampleToChunkParams(off64_t data_offset, size_t data_size); + + // type can be 'stsz' or 'stz2'. + status_t setSampleSizeParams( + uint32_t type, off64_t data_offset, size_t data_size); + + status_t setTimeToSampleParams(off64_t data_offset, size_t data_size); + + status_t setCompositionTimeToSampleParams( + off64_t data_offset, size_t data_size); + + status_t setSyncSampleParams(off64_t data_offset, size_t data_size); + + status_t setSampleAuxiliaryInformationSizeParams(off64_t aDataOffset, + size_t aDataSize, + uint32_t aDrmScheme); + + status_t setSampleAuxiliaryInformationOffsetParams(off64_t aDataOffset, + size_t aDataSize, + uint32_t aDrmScheme); + + //////////////////////////////////////////////////////////////////////////// + + uint32_t countChunkOffsets() const; + + uint32_t countSamples() const; + + status_t getMaxSampleSize(size_t *size); + + status_t getMetaDataForSample( + uint32_t sampleIndex, + off64_t *offset, + size_t *size, + uint32_t *compositionTime, + uint32_t *duration = NULL, + bool *isSyncSample = NULL, + uint32_t *decodeTime = NULL); + + enum { + kFlagBefore, + kFlagAfter, + kFlagClosest + }; + status_t findSampleAtTime( + uint32_t req_time, uint32_t *sample_index, uint32_t flags); + + status_t findSyncSampleNear( + uint32_t start_sample_index, uint32_t *sample_index, + uint32_t flags); + + status_t findThumbnailSample(uint32_t *sample_index); + + bool hasCencInfo() const { return !!mCencInfo; } + + status_t getSampleCencInfo(uint32_t aSampleIndex, + nsTArray<uint16_t>& aClearSizes, + nsTArray<uint32_t>& aCipherSizes, + uint8_t aIV[]); + +protected: + ~SampleTable(); + +private: + struct CompositionDeltaLookup; + + static const uint32_t kChunkOffsetType32; + static const uint32_t kChunkOffsetType64; + static const uint32_t kSampleSizeType32; + static const uint32_t kSampleSizeTypeCompact; + + sp<DataSource> mDataSource; + Mutex mLock; + + off64_t mChunkOffsetOffset; + uint32_t mChunkOffsetType; + uint32_t mNumChunkOffsets; + + off64_t mSampleToChunkOffset; + uint32_t mNumSampleToChunkOffsets; + + off64_t mSampleSizeOffset; + uint32_t mSampleSizeFieldSize; + uint32_t mDefaultSampleSize; + uint32_t mNumSampleSizes; + + uint32_t mTimeToSampleCount; + uint32_t *mTimeToSample; + + struct SampleTimeEntry { + uint32_t mSampleIndex; + uint32_t mCompositionTime; + }; + SampleTimeEntry *mSampleTimeEntries; + + uint32_t *mCompositionTimeDeltaEntries; + size_t mNumCompositionTimeDeltaEntries; + CompositionDeltaLookup *mCompositionDeltaLookup; + + off64_t mSyncSampleOffset; + uint32_t mNumSyncSamples; + uint32_t *mSyncSamples; + size_t mLastSyncSampleIndex; + + SampleIterator *mSampleIterator; + + struct SampleToChunkEntry { + uint32_t startChunk; + uint32_t samplesPerChunk; + uint32_t chunkDesc; + }; + SampleToChunkEntry *mSampleToChunkEntries; + + enum { IV_BYTES = 16 }; + struct SampleCencInfo { + uint8_t mIV[IV_BYTES]; + uint16_t mSubsampleCount; + + struct SubsampleSizes { + uint16_t mClearBytes; + uint32_t mCipherBytes; + } * mSubsamples; + } * mCencInfo; + uint32_t mCencInfoCount; + + uint8_t mCencDefaultSize; + FallibleTArray<uint8_t> mCencSizes; + FallibleTArray<uint64_t> mCencOffsets; + + friend struct SampleIterator; + + status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); + uint32_t getCompositionTimeOffset(uint32_t sampleIndex); + + static int CompareIncreasingTime(const void *, const void *); + + status_t buildSampleEntriesTable(); + + status_t parseSampleCencInfo(); + + SampleTable(const SampleTable &); + SampleTable &operator=(const SampleTable &); +}; + +} // namespace stagefright + +#endif // SAMPLE_TABLE_H_ diff --git a/media/libstagefright/gtest/TestInterval.cpp b/media/libstagefright/gtest/TestInterval.cpp new file mode 100644 index 000000000..68aa3b126 --- /dev/null +++ b/media/libstagefright/gtest/TestInterval.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mp4_demuxer/Interval.h" + +using namespace mp4_demuxer; +using namespace mozilla; + +TEST(Interval, Length) +{ + Interval<int> i(15, 25); + EXPECT_EQ(10, i.Length()); +} + +TEST(Interval, Intersection) +{ + Interval<int> i0(10, 20); + Interval<int> i1(15, 25); + Interval<int> i = i0.Intersection(i1); + EXPECT_EQ(15, i.start); + EXPECT_EQ(20, i.end); +} + +TEST(Interval, Equals) +{ + Interval<int> i0(10, 20); + Interval<int> i1(10, 20); + EXPECT_EQ(i0, i1); + + Interval<int> i2(5, 20); + EXPECT_NE(i0, i2); + + Interval<int> i3(10, 15); + EXPECT_NE(i0, i2); +} + +TEST(Interval, IntersectionVector) +{ + nsTArray<Interval<int>> i0; + i0.AppendElement(Interval<int>(5, 10)); + i0.AppendElement(Interval<int>(20, 25)); + i0.AppendElement(Interval<int>(40, 60)); + + nsTArray<Interval<int>> i1; + i1.AppendElement(Interval<int>(7, 15)); + i1.AppendElement(Interval<int>(16, 27)); + i1.AppendElement(Interval<int>(45, 50)); + i1.AppendElement(Interval<int>(53, 57)); + + nsTArray<Interval<int>> i; + Interval<int>::Intersection(i0, i1, &i); + + EXPECT_EQ(4u, i.Length()); + + EXPECT_EQ(7, i[0].start); + EXPECT_EQ(10, i[0].end); + + EXPECT_EQ(20, i[1].start); + EXPECT_EQ(25, i[1].end); + + EXPECT_EQ(45, i[2].start); + EXPECT_EQ(50, i[2].end); + + EXPECT_EQ(53, i[3].start); + EXPECT_EQ(57, i[3].end); +} + +TEST(Interval, Normalize) +{ + nsTArray<Interval<int>> i; + i.AppendElement(Interval<int>(20, 30)); + i.AppendElement(Interval<int>(1, 8)); + i.AppendElement(Interval<int>(5, 10)); + i.AppendElement(Interval<int>(2, 7)); + + nsTArray<Interval<int>> o; + Interval<int>::Normalize(i, &o); + + EXPECT_EQ(2u, o.Length()); + + EXPECT_EQ(1, o[0].start); + EXPECT_EQ(10, o[0].end); + + EXPECT_EQ(20, o[1].start); + EXPECT_EQ(30, o[1].end); +} diff --git a/media/libstagefright/gtest/TestMP4Rust.cpp b/media/libstagefright/gtest/TestMP4Rust.cpp new file mode 100644 index 000000000..a338b5386 --- /dev/null +++ b/media/libstagefright/gtest/TestMP4Rust.cpp @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mp4parse.h" + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <algorithm> +#include <vector> + +static intptr_t +error_reader(uint8_t* buffer, uintptr_t size, void* userdata) +{ + return -1; +} + +struct read_vector { + explicit read_vector(FILE* file, size_t length); + explicit read_vector(size_t length); + + size_t location; + std::vector<uint8_t> buffer; +}; + +read_vector::read_vector(FILE* file, size_t length) + : location(0) +{ + buffer.resize(length); + size_t read = fread(buffer.data(), sizeof(decltype(buffer)::value_type), + buffer.size(), file); + buffer.resize(read); +} + +read_vector::read_vector(size_t length) + : location(0) +{ + buffer.resize(length, 0); +} + +static intptr_t +vector_reader(uint8_t* buffer, uintptr_t size, void* userdata) +{ + if (!buffer || !userdata) { + return -1; + } + + auto source = reinterpret_cast<read_vector*>(userdata); + if (source->location > source->buffer.size()) { + return -1; + } + uintptr_t available = source->buffer.size() - source->location; + uintptr_t length = std::min(available, size); + memcpy(buffer, source->buffer.data() + source->location, length); + source->location += length; + return length; +} + +TEST(rust, MP4MetadataEmpty) +{ + mp4parse_error rv; + mp4parse_io io; + + // Shouldn't be able to read with no context. + rv = mp4parse_read(nullptr); + EXPECT_EQ(rv, MP4PARSE_ERROR_BADARG); + + // Shouldn't be able to wrap an mp4parse_io with null members. + io = { nullptr, nullptr }; + mp4parse_parser* context = mp4parse_new(&io); + EXPECT_EQ(context, nullptr); + + io = { nullptr, &io }; + context = mp4parse_new(&io); + EXPECT_EQ(context, nullptr); + + // FIXME: this should probably be accepted. + io = { error_reader, nullptr }; + context = mp4parse_new(&io); + EXPECT_EQ(context, nullptr); + + // Read method errors should propagate. + io = { error_reader, &io }; + context = mp4parse_new(&io); + ASSERT_NE(context, nullptr); + rv = mp4parse_read(context); + EXPECT_EQ(rv, MP4PARSE_ERROR_IO); + mp4parse_free(context); + + // Short buffers should fail. + read_vector buf(0); + io = { vector_reader, &buf }; + context = mp4parse_new(&io); + ASSERT_NE(context, nullptr); + rv = mp4parse_read(context); + EXPECT_EQ(rv, MP4PARSE_ERROR_INVALID); + mp4parse_free(context); + + buf.buffer.reserve(4097); + context = mp4parse_new(&io); + ASSERT_NE(context, nullptr); + rv = mp4parse_read(context); + EXPECT_EQ(rv, MP4PARSE_ERROR_INVALID); + mp4parse_free(context); + + // Empty buffers should fail. + buf.buffer.resize(4097, 0); + context = mp4parse_new(&io); + rv = mp4parse_read(context); + EXPECT_EQ(rv, MP4PARSE_ERROR_UNSUPPORTED); + mp4parse_free(context); +} + +TEST(rust, MP4Metadata) +{ + FILE* f = fopen("street.mp4", "rb"); + ASSERT_TRUE(f != nullptr); + // Read just the moov header to work around the parser + // treating mid-box eof as an error. + //read_vector reader = read_vector(f, 1061); + struct stat s; + ASSERT_EQ(0, fstat(fileno(f), &s)); + read_vector reader = read_vector(f, s.st_size); + fclose(f); + + mp4parse_io io = { vector_reader, &reader }; + mp4parse_parser* context = mp4parse_new(&io); + ASSERT_NE(nullptr, context); + + mp4parse_error rv = mp4parse_read(context); + EXPECT_EQ(MP4PARSE_OK, rv); + + uint32_t tracks = 0; + rv = mp4parse_get_track_count(context, &tracks); + EXPECT_EQ(MP4PARSE_OK, rv); + EXPECT_EQ(2U, tracks); + + mp4parse_free(context); +} diff --git a/media/libstagefright/gtest/TestParser.cpp b/media/libstagefright/gtest/TestParser.cpp new file mode 100644 index 000000000..caaca6c70 --- /dev/null +++ b/media/libstagefright/gtest/TestParser.cpp @@ -0,0 +1,479 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "MediaData.h" +#include "mozilla/ArrayUtils.h" +#include "mp4_demuxer/BufferStream.h" +#include "mp4_demuxer/MP4Metadata.h" +#include "mp4_demuxer/MoofParser.h" + +using namespace mozilla; +using namespace mp4_demuxer; + +class TestStream : public Stream +{ +public: + TestStream(const uint8_t* aBuffer, size_t aSize) + : mHighestSuccessfulEndOffset(0) + , mBuffer(aBuffer) + , mSize(aSize) + { + } + bool ReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) override + { + if (aOffset < 0 || aOffset > static_cast<int64_t>(mSize)) { + return false; + } + // After the test, 0 <= aOffset <= mSize <= SIZE_MAX, so it's safe to cast to size_t. + size_t offset = static_cast<size_t>(aOffset); + // Don't read past the end (but it's not an error to try). + if (aLength > mSize - offset) { + aLength = mSize - offset; + } + // Now, 0 <= offset <= offset + aLength <= mSize <= SIZE_MAX. + *aBytesRead = aLength; + memcpy(aData, mBuffer + offset, aLength); + if (mHighestSuccessfulEndOffset < offset + aLength) + { + mHighestSuccessfulEndOffset = offset + aLength; + } + return true; + } + bool CachedReadAt(int64_t aOffset, void* aData, size_t aLength, + size_t* aBytesRead) override + { + return ReadAt(aOffset, aData, aLength, aBytesRead); + } + bool Length(int64_t* aLength) override + { + *aLength = mSize; + return true; + } + void DiscardBefore(int64_t aOffset) override + { + } + + // Offset past the last character ever read. 0 when nothing read yet. + size_t mHighestSuccessfulEndOffset; +protected: + virtual ~TestStream() + { + } + + const uint8_t* mBuffer; + size_t mSize; +}; + +TEST(stagefright_MP4Metadata, EmptyStream) +{ + RefPtr<Stream> stream = new TestStream(nullptr, 0); + + EXPECT_FALSE(MP4Metadata::HasCompleteMetadata(stream)); + RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + EXPECT_FALSE(metadataBuffer); + + MP4Metadata metadata(stream); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kVideoTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1))); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0)); + // We can seek anywhere in any MPEG4. + EXPECT_TRUE(metadata.CanSeek()); + EXPECT_FALSE(metadata.Crypto().valid); +} + +TEST(stagefright_MoofParser, EmptyStream) +{ + RefPtr<Stream> stream = new TestStream(nullptr, 0); + + MoofParser parser(stream, 0, false); + EXPECT_EQ(0u, parser.mOffset); + EXPECT_TRUE(parser.ReachedEnd()); + + MediaByteRangeSet byteRanges; + EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges)); + + EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull()); + EXPECT_TRUE(parser.mInitRange.IsEmpty()); + EXPECT_EQ(0u, parser.mOffset); + EXPECT_TRUE(parser.ReachedEnd()); + EXPECT_FALSE(parser.HasMetadata()); + RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); + EXPECT_FALSE(metadataBuffer); + EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty()); + EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsEmpty()); +} + +nsTArray<uint8_t> +ReadTestFile(const char* aFilename) +{ + if (!aFilename) { + return {}; + } + FILE* f = fopen(aFilename, "rb"); + if (!f) { + return {}; + } + + if (fseek(f, 0, SEEK_END) != 0) { + fclose(f); + return {}; + } + long position = ftell(f); + // I know EOF==-1, so this test is made obsolete by '<0', but I don't want + // the code to rely on that. + if (position == 0 || position == EOF || position < 0) { + fclose(f); + return {}; + } + if (fseek(f, 0, SEEK_SET) != 0) { + fclose(f); + return {}; + } + + size_t len = static_cast<size_t>(position); + nsTArray<uint8_t> buffer(len); + buffer.SetLength(len); + size_t read = fread(buffer.Elements(), 1, len, f); + fclose(f); + if (read != len) { + return {}; + } + + return buffer; +} + +struct TestFileData +{ + const char* mFilename; + uint32_t mNumberVideoTracks; + int64_t mVideoDuration; // For first video track, -1 if N/A. + int32_t mWidth; + int32_t mHeight; + uint32_t mNumberAudioTracks; + int64_t mAudioDuration; // For first audio track, -1 if N/A. + bool mHasCrypto; + uint64_t mMoofReachedOffset; // or 0 for the end. + bool mValidMoof; + bool mHeader; +}; +static const TestFileData testFiles[] = { + // filename #V dur w h #A dur crypt off moof headr + { "test_case_1156505.mp4", 0, -1, 0, 0, 0, -1, false, 152, false, false }, + { "test_case_1181213.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1181215.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1181220.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1181223.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1181719.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1185230.mp4", 1, 416666, + 320, 240, 1, 5, false, 0, false, false }, + { "test_case_1187067.mp4", 1, 80000, + 160, 90, 0, -1, false, 0, false, false }, + { "test_case_1200326.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1204580.mp4", 1, 502500, + 320, 180, 0, -1, false, 0, false, false }, + { "test_case_1216748.mp4", 0, -1, 0, 0, 0, -1, false, 152, false, false }, + { "test_case_1296473.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1296532.mp4", 1, 5589333, + 560, 320, 1, 5589333, + true, 0, true, true }, + { "test_case_1301065.mp4", 0, -1, 0, 0, 1, 100079991719000000, + false, 0, false, false }, + { "test_case_1301065-u32max.mp4", 0, -1, 0, 0, 1, 97391548639, + false, 0, false, false }, + { "test_case_1301065-max-ez.mp4", 0, -1, 0, 0, 1, 209146758205306, + false, 0, false, false }, + { "test_case_1301065-harder.mp4", 0, -1, 0, 0, 1, 209146758205328, + false, 0, false, false }, + { "test_case_1301065-max-ok.mp4", 0, -1, 0, 0, 1, 9223372036854775804, + false, 0, false, false }, + { "test_case_1301065-overfl.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1301065-i64max.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1301065-i64min.mp4", 0, -1, 0, 0, 0, -1, false, 0, false, false }, + { "test_case_1301065-u64max.mp4", 0, -1, 0, 0, 1, 0, false, 0, false, false }, + { "test_case_1351094.mp4", 0, -1, 0, 0, 0, -1, false, 0, true, true }, +}; + +TEST(stagefright_MPEG4Metadata, test_case_mp4) +{ + for (size_t test = 0; test < ArrayLength(testFiles); ++test) { + nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename); + ASSERT_FALSE(buffer.IsEmpty()); + RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length()); + + EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream)); + RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + EXPECT_TRUE(metadataBuffer); + + MP4Metadata metadata(stream); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack)); + EXPECT_EQ(testFiles[test].mNumberAudioTracks, + metadata.GetNumberTracks(TrackInfo::kAudioTrack)); + EXPECT_EQ(testFiles[test].mNumberVideoTracks, + metadata.GetNumberTracks(TrackInfo::kVideoTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1))); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0)); + UniquePtr<TrackInfo> trackInfo = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0); + if (testFiles[test].mNumberVideoTracks == 0) { + EXPECT_TRUE(!trackInfo); + } else { + ASSERT_TRUE(!!trackInfo); + const VideoInfo* videoInfo = trackInfo->GetAsVideoInfo(); + ASSERT_TRUE(!!videoInfo); + EXPECT_TRUE(videoInfo->IsValid()); + EXPECT_TRUE(videoInfo->IsVideo()); + EXPECT_EQ(testFiles[test].mVideoDuration, videoInfo->mDuration); + EXPECT_EQ(testFiles[test].mWidth, videoInfo->mDisplay.width); + EXPECT_EQ(testFiles[test].mHeight, videoInfo->mDisplay.height); + FallibleTArray<mp4_demuxer::Index::Indice> indices; + EXPECT_TRUE(metadata.ReadTrackIndex(indices, videoInfo->mTrackId)); + for (const mp4_demuxer::Index::Indice& indice : indices) { + EXPECT_TRUE(indice.start_offset <= indice.end_offset); + EXPECT_TRUE(indice.start_composition <= indice.end_composition); + } + } + trackInfo = metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0); + if (testFiles[test].mNumberAudioTracks == 0) { + EXPECT_TRUE(!trackInfo); + } else { + ASSERT_TRUE(!!trackInfo); + const AudioInfo* audioInfo = trackInfo->GetAsAudioInfo(); + ASSERT_TRUE(!!audioInfo); + EXPECT_TRUE(audioInfo->IsValid()); + EXPECT_TRUE(audioInfo->IsAudio()); + EXPECT_EQ(testFiles[test].mAudioDuration, audioInfo->mDuration); + FallibleTArray<mp4_demuxer::Index::Indice> indices; + EXPECT_TRUE(metadata.ReadTrackIndex(indices, audioInfo->mTrackId)); + for (const mp4_demuxer::Index::Indice& indice : indices) { + EXPECT_TRUE(indice.start_offset <= indice.end_offset); + EXPECT_TRUE(indice.start_composition <= indice.end_composition); + } + } + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0)); + // We can see anywhere in any MPEG4. + EXPECT_TRUE(metadata.CanSeek()); + EXPECT_EQ(testFiles[test].mHasCrypto, metadata.Crypto().valid); + } +} + +// Bug 1224019: This test produces way to much output, disabling for now. +#if 0 +TEST(stagefright_MPEG4Metadata, test_case_mp4_subsets) +{ + static const size_t step = 1u; + for (size_t test = 0; test < ArrayLength(testFiles); ++test) { + nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename); + ASSERT_FALSE(buffer.IsEmpty()); + ASSERT_LE(step, buffer.Length()); + // Just exercizing the parser starting at different points through the file, + // making sure it doesn't crash. + // No checks because results would differ for each position. + for (size_t offset = 0; offset < buffer.Length() - step; offset += step) { + size_t size = buffer.Length() - offset; + while (size > 0) { + RefPtr<TestStream> stream = + new TestStream(buffer.Elements() + offset, size); + + MP4Metadata::HasCompleteMetadata(stream); + RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + MP4Metadata metadata(stream); + + if (stream->mHighestSuccessfulEndOffset <= 0) { + // No successful reads -> Cutting down the size won't change anything. + break; + } + if (stream->mHighestSuccessfulEndOffset < size) { + // Read up to a point before the end -> Resize down to that point. + size = stream->mHighestSuccessfulEndOffset; + } else { + // Read up to the end (or after?!) -> Just cut 1 byte. + size -= 1; + } + } + } + } +} +#endif + +TEST(stagefright_MoofParser, test_case_mp4) +{ + for (size_t test = 0; test < ArrayLength(testFiles); ++test) { + nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename); + ASSERT_FALSE(buffer.IsEmpty()); + RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length()); + + MoofParser parser(stream, 0, false); + EXPECT_EQ(0u, parser.mOffset); + EXPECT_FALSE(parser.ReachedEnd()); + EXPECT_TRUE(parser.mInitRange.IsEmpty()); + + EXPECT_TRUE(parser.HasMetadata()); + RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); + EXPECT_TRUE(metadataBuffer); + + EXPECT_FALSE(parser.mInitRange.IsEmpty()); + const MediaByteRangeSet byteRanges( + MediaByteRange(0, int64_t(buffer.Length()))); + EXPECT_EQ(testFiles[test].mValidMoof, + parser.RebuildFragmentedIndex(byteRanges)); + if (testFiles[test].mMoofReachedOffset == 0) { + EXPECT_EQ(buffer.Length(), parser.mOffset); + EXPECT_TRUE(parser.ReachedEnd()); + } else { + EXPECT_EQ(testFiles[test].mMoofReachedOffset, parser.mOffset); + EXPECT_FALSE(parser.ReachedEnd()); + } + + EXPECT_FALSE(parser.mInitRange.IsEmpty()); + EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull()); + EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsEmpty()); + EXPECT_EQ(testFiles[test].mHeader, + !parser.FirstCompleteMediaHeader().IsEmpty()); + } +} + +// Bug 1224019: This test produces way to much output, disabling for now. +#if 0 +TEST(stagefright_MoofParser, test_case_mp4_subsets) +{ + const size_t step = 1u; + for (size_t test = 0; test < ArrayLength(testFiles); ++test) { + nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename); + ASSERT_FALSE(buffer.IsEmpty()); + ASSERT_LE(step, buffer.Length()); + // Just exercizing the parser starting at different points through the file, + // making sure it doesn't crash. + // No checks because results would differ for each position. + for (size_t offset = 0; offset < buffer.Length() - step; offset += step) { + size_t size = buffer.Length() - offset; + while (size > 0) { + RefPtr<TestStream> stream = + new TestStream(buffer.Elements() + offset, size); + + MoofParser parser(stream, 0, false); + MediaByteRangeSet byteRanges; + EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges)); + parser.GetCompositionRange(byteRanges); + parser.HasMetadata(); + RefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); + parser.FirstCompleteMediaSegment(); + parser.FirstCompleteMediaHeader(); + + if (stream->mHighestSuccessfulEndOffset <= 0) { + // No successful reads -> Cutting down the size won't change anything. + break; + } + if (stream->mHighestSuccessfulEndOffset < size) { + // Read up to a point before the end -> Resize down to that point. + size = stream->mHighestSuccessfulEndOffset; + } else { + // Read up to the end (or after?!) -> Just cut 1 byte. + size -= 1; + } + } + } + } +} +#endif + +uint8_t media_libstagefright_gtest_video_init_mp4[] = { + 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d, + 0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x6d, 0x61, 0x76, 0x63, 0x31, + 0x00, 0x00, 0x02, 0xd1, 0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c, + 0x6d, 0x76, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8, + 0xc8, 0x4a, 0xc5, 0x7a, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, + 0x69, 0x6f, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x80, 0x80, + 0x07, 0x00, 0x4f, 0xff, 0xff, 0x29, 0x15, 0xff, 0x00, 0x00, 0x02, 0x0d, + 0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c, 0x74, 0x6b, 0x68, 0x64, + 0x00, 0x00, 0x00, 0x01, 0xc8, 0x49, 0x73, 0xf8, 0xc8, 0x49, 0x73, 0xf9, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xa9, 0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20, + 0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8, + 0xc8, 0x49, 0x73, 0xf9, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x68, 0x64, 0x6c, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x50, 0x41, 0x43, 0x20, 0x49, 0x53, 0x4f, 0x20, 0x56, 0x69, 0x64, + 0x65, 0x6f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x49, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14, + 0x76, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66, + 0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x73, 0x74, 0x62, 0x6c, + 0x00, 0x00, 0x00, 0xad, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9d, 0x61, 0x76, 0x63, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x80, 0x01, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x33, 0x61, 0x76, + 0x63, 0x43, 0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x64, + 0x00, 0x1f, 0xac, 0x2c, 0xc5, 0x02, 0x80, 0xbf, 0xe5, 0xc0, 0x44, 0x00, + 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xf2, 0x3c, 0x60, 0xc6, + 0x58, 0x01, 0x00, 0x05, 0x68, 0xe9, 0x2b, 0x2c, 0x8b, 0x00, 0x00, 0x00, + 0x14, 0x62, 0x74, 0x72, 0x74, 0x00, 0x01, 0x5a, 0xc2, 0x00, 0x24, 0x74, + 0x38, 0x00, 0x09, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x74, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x63, 0x74, 0x74, 0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x73, + 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6d, 0x76, 0x65, + 0x78, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x68, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x76, 0x18, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00 +}; + +const uint32_t media_libstagefright_gtest_video_init_mp4_len = 745; + +TEST(stagefright_MP4Metadata, EmptyCTTS) +{ + RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_libstagefright_gtest_video_init_mp4_len); + buffer->AppendElements(media_libstagefright_gtest_video_init_mp4, media_libstagefright_gtest_video_init_mp4_len); + RefPtr<BufferStream> stream = new BufferStream(buffer); + + EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream)); + RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + EXPECT_TRUE(metadataBuffer); + + MP4Metadata metadata(stream); + + EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack)); + mozilla::UniquePtr<mozilla::TrackInfo> track = + metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0); + EXPECT_TRUE(track != nullptr); + // We can seek anywhere in any MPEG4. + EXPECT_TRUE(metadata.CanSeek()); + EXPECT_FALSE(metadata.Crypto().valid); +} + diff --git a/media/libstagefright/gtest/moz.build b/media/libstagefright/gtest/moz.build new file mode 100644 index 000000000..15d91dc94 --- /dev/null +++ b/media/libstagefright/gtest/moz.build @@ -0,0 +1,49 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Library('stagefright_gtest') + +SOURCES += [ + 'TestInterval.cpp', + 'TestParser.cpp', +] + +TEST_HARNESS_FILES.gtest += [ + 'test_case_1156505.mp4', + 'test_case_1181213.mp4', + 'test_case_1181215.mp4', + 'test_case_1181220.mp4', + 'test_case_1181223.mp4', + 'test_case_1181719.mp4', + 'test_case_1185230.mp4', + 'test_case_1187067.mp4', + 'test_case_1200326.mp4', + 'test_case_1204580.mp4', + 'test_case_1216748.mp4', + 'test_case_1296473.mp4', + 'test_case_1296532.mp4', + 'test_case_1301065-harder.mp4', + 'test_case_1301065-i64max.mp4', + 'test_case_1301065-i64min.mp4', + 'test_case_1301065-max-ez.mp4', + 'test_case_1301065-max-ok.mp4', + 'test_case_1301065-overfl.mp4', + 'test_case_1301065-u32max.mp4', + 'test_case_1301065-u64max.mp4', + 'test_case_1301065.mp4', + 'test_case_1351094.mp4', +] + +if CONFIG['MOZ_RUST']: + UNIFIED_SOURCES += ['TestMP4Rust.cpp',] + TEST_HARNESS_FILES.gtest += [ + '../../../dom/media/test/street.mp4', + ] + LOCAL_INCLUDES += [ + '../binding/include', + ] + +FINAL_LIBRARY = 'xul-gtest' diff --git a/media/libstagefright/gtest/test_case_1156505.mp4 b/media/libstagefright/gtest/test_case_1156505.mp4 Binary files differnew file mode 100644 index 000000000..687b06ee1 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1156505.mp4 diff --git a/media/libstagefright/gtest/test_case_1181213.mp4 b/media/libstagefright/gtest/test_case_1181213.mp4 Binary files differnew file mode 100644 index 000000000..e2326edb4 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1181213.mp4 diff --git a/media/libstagefright/gtest/test_case_1181215.mp4 b/media/libstagefright/gtest/test_case_1181215.mp4 Binary files differnew file mode 100644 index 000000000..7adba3836 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1181215.mp4 diff --git a/media/libstagefright/gtest/test_case_1181220.mp4 b/media/libstagefright/gtest/test_case_1181220.mp4 Binary files differnew file mode 100644 index 000000000..e4a144c8e --- /dev/null +++ b/media/libstagefright/gtest/test_case_1181220.mp4 diff --git a/media/libstagefright/gtest/test_case_1181223.mp4 b/media/libstagefright/gtest/test_case_1181223.mp4 Binary files differnew file mode 100644 index 000000000..2aa2d5abf --- /dev/null +++ b/media/libstagefright/gtest/test_case_1181223.mp4 diff --git a/media/libstagefright/gtest/test_case_1181719.mp4 b/media/libstagefright/gtest/test_case_1181719.mp4 Binary files differnew file mode 100644 index 000000000..6846edd6e --- /dev/null +++ b/media/libstagefright/gtest/test_case_1181719.mp4 diff --git a/media/libstagefright/gtest/test_case_1185230.mp4 b/media/libstagefright/gtest/test_case_1185230.mp4 Binary files differnew file mode 100644 index 000000000..ac5cbdbe8 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1185230.mp4 diff --git a/media/libstagefright/gtest/test_case_1187067.mp4 b/media/libstagefright/gtest/test_case_1187067.mp4 Binary files differnew file mode 100644 index 000000000..fdb396eeb --- /dev/null +++ b/media/libstagefright/gtest/test_case_1187067.mp4 diff --git a/media/libstagefright/gtest/test_case_1200326.mp4 b/media/libstagefright/gtest/test_case_1200326.mp4 Binary files differnew file mode 100644 index 000000000..5b8b27d50 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1200326.mp4 diff --git a/media/libstagefright/gtest/test_case_1204580.mp4 b/media/libstagefright/gtest/test_case_1204580.mp4 Binary files differnew file mode 100644 index 000000000..4e55b0571 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1204580.mp4 diff --git a/media/libstagefright/gtest/test_case_1216748.mp4 b/media/libstagefright/gtest/test_case_1216748.mp4 Binary files differnew file mode 100755 index 000000000..7072f53be --- /dev/null +++ b/media/libstagefright/gtest/test_case_1216748.mp4 diff --git a/media/libstagefright/gtest/test_case_1296473.mp4 b/media/libstagefright/gtest/test_case_1296473.mp4 Binary files differnew file mode 100644 index 000000000..109eb5106 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1296473.mp4 diff --git a/media/libstagefright/gtest/test_case_1296532.mp4 b/media/libstagefright/gtest/test_case_1296532.mp4 Binary files differnew file mode 100644 index 000000000..5a5669bb8 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1296532.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-harder.mp4 b/media/libstagefright/gtest/test_case_1301065-harder.mp4 Binary files differnew file mode 100644 index 000000000..7d678b7c6 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-harder.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-i64max.mp4 b/media/libstagefright/gtest/test_case_1301065-i64max.mp4 Binary files differnew file mode 100644 index 000000000..5a3572f88 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-i64max.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-i64min.mp4 b/media/libstagefright/gtest/test_case_1301065-i64min.mp4 Binary files differnew file mode 100644 index 000000000..4d3eb366e --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-i64min.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-max-ez.mp4 b/media/libstagefright/gtest/test_case_1301065-max-ez.mp4 Binary files differnew file mode 100644 index 000000000..17fbf411e --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-max-ez.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-max-ok.mp4 b/media/libstagefright/gtest/test_case_1301065-max-ok.mp4 Binary files differnew file mode 100644 index 000000000..a5e1e4610 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-max-ok.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-overfl.mp4 b/media/libstagefright/gtest/test_case_1301065-overfl.mp4 Binary files differnew file mode 100644 index 000000000..1ef24e932 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-overfl.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-u32max.mp4 b/media/libstagefright/gtest/test_case_1301065-u32max.mp4 Binary files differnew file mode 100644 index 000000000..b1d8b6ce7 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-u32max.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065-u64max.mp4 b/media/libstagefright/gtest/test_case_1301065-u64max.mp4 Binary files differnew file mode 100644 index 000000000..419dcba2c --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065-u64max.mp4 diff --git a/media/libstagefright/gtest/test_case_1301065.mp4 b/media/libstagefright/gtest/test_case_1301065.mp4 Binary files differnew file mode 100644 index 000000000..543a4fba3 --- /dev/null +++ b/media/libstagefright/gtest/test_case_1301065.mp4 diff --git a/media/libstagefright/gtest/test_case_1351094.mp4 b/media/libstagefright/gtest/test_case_1351094.mp4 Binary files differnew file mode 100644 index 000000000..2dfd4c35c --- /dev/null +++ b/media/libstagefright/gtest/test_case_1351094.mp4 diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build new file mode 100644 index 000000000..71cc87e5f --- /dev/null +++ b/media/libstagefright/moz.build @@ -0,0 +1,172 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DEFINES['ANDROID_SMP'] = 0 +DEFINES['LOG_NDEBUG'] = 1 + +if CONFIG['OS_TARGET'] != 'WINNT': + DEFINES['_GLIBCXX_OS_DEFINES'] = True + +if CONFIG['OS_TARGET'] == 'WINNT': + if CONFIG['_MSC_VER']: + DEFINES['ssize_t'] = 'intptr_t' + DEFINES['off64_t'] = 'int64_t' + DEFINES['strcasecmp'] = 'stricmp' + DEFINES['strncasecmp'] = 'strnicmp' + DEFINES['HAVE_MS_C_RUNTIME'] = True + DEFINES['__PRETTY_FUNCTION__'] = '__FUNCTION__' + LOCAL_INCLUDES += [ 'ports/win32/include' ] +elif CONFIG['OS_TARGET'] == 'Darwin': + DEFINES['HAVE_SYS_UIO_H'] = True + DEFINES['off64_t'] = 'off_t' + LOCAL_INCLUDES += [ 'ports/darwin/include' ] +elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD', + 'GNU/kFreeBSD'): + if CONFIG['OS_TARGET'] != 'NetBSD': + DEFINES['ENODATA'] = '-0x80000003' + if CONFIG['OS_TARGET'] == 'OpenBSD': + DEFINES['EBADMSG'] = '-0x80000006' + DEFINES['HAVE_SYS_UIO_H'] = True + if CONFIG['OS_TARGET'] != 'GNU/kFreeBSD': + DEFINES['off64_t'] = 'off_t' + LOCAL_INCLUDES += [ 'ports/bsd/include' ] +else: + DEFINES['HAVE_SYS_UIO_H'] = True + +if CONFIG['OS_TARGET'] != 'Android': + DEFINES['FAKE_LOG_DEVICE'] = True + SOURCES += [ + 'system/core/liblog/fake_log_device.c', + ] + UNIFIED_SOURCES += [ + 'system/core/libcutils/strdup16to8.c', + 'system/core/liblog/logd_write.c', + 'system/core/liblog/logprint.c', + ] + +EXPORTS.mp4_demuxer += [ + 'binding/include/mp4_demuxer/Adts.h', + 'binding/include/mp4_demuxer/AnnexB.h', + 'binding/include/mp4_demuxer/Atom.h', + 'binding/include/mp4_demuxer/AtomType.h', + 'binding/include/mp4_demuxer/BitReader.h', + 'binding/include/mp4_demuxer/BufferStream.h', + 'binding/include/mp4_demuxer/ByteReader.h', + 'binding/include/mp4_demuxer/ByteWriter.h', + 'binding/include/mp4_demuxer/DecoderData.h', + 'binding/include/mp4_demuxer/H264.h', + 'binding/include/mp4_demuxer/Index.h', + 'binding/include/mp4_demuxer/Interval.h', + 'binding/include/mp4_demuxer/MoofParser.h', + 'binding/include/mp4_demuxer/MP4Metadata.h', + 'binding/include/mp4_demuxer/ResourceStream.h', + 'binding/include/mp4_demuxer/SinfParser.h', + 'binding/include/mp4_demuxer/Stream.h', +] + +EXPORTS.demuxer += [ + 'binding/include/demuxer/TrackDemuxer.h', +] + +SOURCES += [ + 'frameworks/av/media/libstagefright/foundation/hexdump.cpp', + 'frameworks/av/media/libstagefright/MetaData.cpp', + 'system/core/libutils/RefBase.cpp', + 'system/core/libutils/String16.cpp', + 'system/core/libutils/String8.cpp', + 'system/core/libutils/VectorImpl.cpp', +] + +if CONFIG['MOZ_RUST']: + EXPORTS += [ + 'binding/include/mp4parse.h', + ] + +UNIFIED_SOURCES += [ + 'binding/Adts.cpp', + 'binding/AnnexB.cpp', + 'binding/BitReader.cpp', + 'binding/Box.cpp', + 'binding/BufferStream.cpp', + 'binding/DecoderData.cpp', + 'binding/H264.cpp', + 'binding/Index.cpp', + 'binding/MoofParser.cpp', + 'binding/MP4Metadata.cpp', + 'binding/ResourceStream.cpp', + 'binding/SinfParser.cpp', + 'frameworks/av/media/libstagefright/DataSource.cpp', + 'frameworks/av/media/libstagefright/ESDS.cpp', + 'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp', + 'frameworks/av/media/libstagefright/foundation/ABitReader.cpp', + 'frameworks/av/media/libstagefright/foundation/ABuffer.cpp', + 'frameworks/av/media/libstagefright/foundation/AString.cpp', + 'frameworks/av/media/libstagefright/MediaBuffer.cpp', + 'frameworks/av/media/libstagefright/MediaDefs.cpp', + 'frameworks/av/media/libstagefright/MediaSource.cpp', + 'frameworks/av/media/libstagefright/MPEG4Extractor.cpp', + 'frameworks/av/media/libstagefright/SampleIterator.cpp', + 'frameworks/av/media/libstagefright/SampleTable.cpp', + 'frameworks/av/media/libstagefright/Utils.cpp', + 'system/core/libutils/SharedBuffer.cpp', + 'system/core/libutils/Static.cpp', + 'system/core/libutils/Unicode.cpp', +] + +LOCAL_INCLUDES += [ + 'binding/include', + 'frameworks/av/include', + 'frameworks/av/include/media/stagefright/foundation', + 'frameworks/av/media/libstagefright/', + 'stubs/empty', + 'stubs/include', + 'stubs/include/media/stagefright/foundation', + 'system/core/include', +] + +TEST_DIRS += [ + 'gtest', +] + +# We allow warnings for third-party code that can be updated from upstream. +ALLOW_COMPILER_WARNINGS = True + +FINAL_LIBRARY = 'xul' + +# Suppress warnings in third-party code. +if CONFIG['_MSC_VER']: + CFLAGS += [ + '-wd4013', # 'function' undefined; assuming extern returning int + '-wd4101', # unreferenced local variable + ] + CXXFLAGS += [ + '-wd4018', # '<' : signed/unsigned mismatch + '-wd4275', # non dll-interface class used as base for dll-interface class + '-wd4305', # '=' : truncation from 'double' to 'float' + '-wd4309', # '=' : truncation of constant value + '-wd4355', # 'this' : used in base member initializer list + '-wd4804', # '>' : unsafe use of type 'bool' in operation + '-wd4099', # mismatched class/struct tags + ] +elif CONFIG['GNU_CXX']: + CFLAGS += [ + '-Wno-comment', + '-Wno-sign-compare' + ] + CXXFLAGS += [ + '-Wno-format', + '-Wno-format-security', + '-Wno-multichar', + '-Wno-sign-compare', + '-Wno-unused', + ] + if CONFIG['CLANG_CXX']: + CXXFLAGS += [ + '-Wno-mismatched-tags', + '-Wno-tautological-constant-out-of-range-compare', + '-Wno-unreachable-code-return', + '-Wno-implicit-fallthrough', + ] diff --git a/media/libstagefright/patches/frameworks/av.patch b/media/libstagefright/patches/frameworks/av.patch new file mode 100644 index 000000000..22fb6c580 --- /dev/null +++ b/media/libstagefright/patches/frameworks/av.patch @@ -0,0 +1,1192 @@ +diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h +index 742bc0e..f6b30b0 100644 +--- a/include/media/stagefright/DataSource.h ++++ b/include/media/stagefright/DataSource.h +@@ -21,16 +21,10 @@ + #include <sys/types.h> + + #include <media/stagefright/MediaErrors.h> +-#include <utils/Errors.h> +-#include <utils/KeyedVector.h> +-#include <utils/List.h> + #include <utils/RefBase.h> +-#include <utils/threads.h> +-#include <drm/DrmManagerClient.h> + +-namespace android { ++namespace stagefright { + +-struct AMessage; + class String8; + + class DataSource : public RefBase { +@@ -42,10 +36,6 @@ public: + kIsHTTPBasedSource = 8, + }; + +- static sp<DataSource> CreateFromURI( +- const char *uri, +- const KeyedVector<String8, String8> *headers = NULL); +- + DataSource() {} + + virtual status_t initCheck() const = 0; +@@ -69,6 +59,7 @@ public: + return ERROR_UNSUPPORTED; + } + ++#if 0 + //////////////////////////////////////////////////////////////////////////// + + bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta); +@@ -92,6 +83,7 @@ public: + virtual String8 getUri() { + return String8(); + } ++#endif + + virtual String8 getMIMEType() const; + +@@ -99,13 +91,10 @@ protected: + virtual ~DataSource() {} + + private: +- static Mutex gSnifferMutex; +- static List<SnifferFunc> gSniffers; +- + DataSource(const DataSource &); + DataSource &operator=(const DataSource &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // DATA_SOURCE_H_ +diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h +index 3d79596..4172739 100644 +--- a/include/media/stagefright/MediaBuffer.h ++++ b/include/media/stagefright/MediaBuffer.h +@@ -23,7 +23,7 @@ + #include <utils/Errors.h> + #include <utils/RefBase.h> + +-namespace android { ++namespace stagefright { + + struct ABuffer; + class GraphicBuffer; +@@ -118,6 +118,6 @@ private: + MediaBuffer &operator=(const MediaBuffer &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_BUFFER_H_ +diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h +index 0488292..4a51351 100644 +--- a/include/media/stagefright/MediaBufferGroup.h ++++ b/include/media/stagefright/MediaBufferGroup.h +@@ -22,7 +22,7 @@ + #include <utils/Errors.h> + #include <utils/threads.h> + +-namespace android { ++namespace stagefright { + + class MediaBuffer; + class MetaData; +@@ -53,6 +53,6 @@ private: + MediaBufferGroup &operator=(const MediaBufferGroup &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_BUFFER_GROUP_H_ +diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h +index 85693d4..9897caf 100644 +--- a/include/media/stagefright/MediaDefs.h ++++ b/include/media/stagefright/MediaDefs.h +@@ -18,7 +18,7 @@ + + #define MEDIA_DEFS_H_ + +-namespace android { ++namespace stagefright { + + extern const char *MEDIA_MIMETYPE_IMAGE_JPEG; + +@@ -58,6 +58,6 @@ extern const char *MEDIA_MIMETYPE_CONTAINER_WVM; + extern const char *MEDIA_MIMETYPE_TEXT_3GPP; + extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP; + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_DEFS_H_ +diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h +index 686f286..df2c1a5 100644 +--- a/include/media/stagefright/MediaErrors.h ++++ b/include/media/stagefright/MediaErrors.h +@@ -20,52 +20,50 @@ + + #include <utils/Errors.h> + +-namespace android { ++namespace stagefright { + +-enum { +- MEDIA_ERROR_BASE = -1000, ++#define MEDIA_ERROR_BASE (-1000) + +- ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE, +- ERROR_NOT_CONNECTED = MEDIA_ERROR_BASE - 1, +- ERROR_UNKNOWN_HOST = MEDIA_ERROR_BASE - 2, +- ERROR_CANNOT_CONNECT = MEDIA_ERROR_BASE - 3, +- ERROR_IO = MEDIA_ERROR_BASE - 4, +- ERROR_CONNECTION_LOST = MEDIA_ERROR_BASE - 5, +- ERROR_MALFORMED = MEDIA_ERROR_BASE - 7, +- ERROR_OUT_OF_RANGE = MEDIA_ERROR_BASE - 8, +- ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9, +- ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10, +- ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11, ++#define ERROR_ALREADY_CONNECTED (MEDIA_ERROR_BASE) ++#define ERROR_NOT_CONNECTED (MEDIA_ERROR_BASE - 1) ++#define ERROR_UNKNOWN_HOST (MEDIA_ERROR_BASE - 2) ++#define ERROR_CANNOT_CONNECT (MEDIA_ERROR_BASE - 3) ++#define ERROR_IO (MEDIA_ERROR_BASE - 4) ++#define ERROR_CONNECTION_LOST (MEDIA_ERROR_BASE - 5) ++#define ERROR_MALFORMED (MEDIA_ERROR_BASE - 7) ++#define ERROR_OUT_OF_RANGE (MEDIA_ERROR_BASE - 8) ++#define ERROR_BUFFER_TOO_SMALL (MEDIA_ERROR_BASE - 9) ++#define ERROR_UNSUPPORTED (MEDIA_ERROR_BASE - 10) ++#define ERROR_END_OF_STREAM (MEDIA_ERROR_BASE - 11) + +- // Not technically an error. +- INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12, +- INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13, +- INFO_OUTPUT_BUFFERS_CHANGED = MEDIA_ERROR_BASE - 14, ++// Not technically an error. ++#define INFO_FORMAT_CHANGED (MEDIA_ERROR_BASE - 12) ++#define INFO_DISCONTINUITY (MEDIA_ERROR_BASE - 13) ++#define INFO_OUTPUT_BUFFERS_CHANGED (MEDIA_ERROR_BASE - 14) + +- // The following constant values should be in sync with +- // drm/drm_framework_common.h +- DRM_ERROR_BASE = -2000, ++// The following constant values should be in sync with ++// drm/drm_framework_common.h ++#define DRM_ERROR_BASE (-2000) + +- ERROR_DRM_UNKNOWN = DRM_ERROR_BASE, +- ERROR_DRM_NO_LICENSE = DRM_ERROR_BASE - 1, +- ERROR_DRM_LICENSE_EXPIRED = DRM_ERROR_BASE - 2, +- ERROR_DRM_SESSION_NOT_OPENED = DRM_ERROR_BASE - 3, +- ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 4, +- ERROR_DRM_DECRYPT = DRM_ERROR_BASE - 5, +- ERROR_DRM_CANNOT_HANDLE = DRM_ERROR_BASE - 6, +- ERROR_DRM_TAMPER_DETECTED = DRM_ERROR_BASE - 7, +- ERROR_DRM_NOT_PROVISIONED = DRM_ERROR_BASE - 8, +- ERROR_DRM_DEVICE_REVOKED = DRM_ERROR_BASE - 9, +- ERROR_DRM_RESOURCE_BUSY = DRM_ERROR_BASE - 10, ++#define ERROR_DRM_UNKNOWN (DRM_ERROR_BASE) ++#define ERROR_DRM_NO_LICENSE (DRM_ERROR_BASE - 1) ++#define ERROR_DRM_LICENSE_EXPIRED (DRM_ERROR_BASE - 2) ++#define ERROR_DRM_SESSION_NOT_OPENED (DRM_ERROR_BASE - 3) ++#define ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED (DRM_ERROR_BASE - 4) ++#define ERROR_DRM_DECRYPT (DRM_ERROR_BASE - 5) ++#define ERROR_DRM_CANNOT_HANDLE (DRM_ERROR_BASE - 6) ++#define ERROR_DRM_TAMPER_DETECTED (DRM_ERROR_BASE - 7) ++#define ERROR_DRM_NOT_PROVISIONED (DRM_ERROR_BASE - 8) ++#define ERROR_DRM_DEVICE_REVOKED (DRM_ERROR_BASE - 9) ++#define ERROR_DRM_RESOURCE_BUSY (DRM_ERROR_BASE - 10) + +- ERROR_DRM_VENDOR_MAX = DRM_ERROR_BASE - 500, +- ERROR_DRM_VENDOR_MIN = DRM_ERROR_BASE - 999, ++#define ERROR_DRM_VENDOR_MAX (DRM_ERROR_BASE - 500) ++#define ERROR_DRM_VENDOR_MIN (DRM_ERROR_BASE - 999) + +- // Heartbeat Error Codes +- HEARTBEAT_ERROR_BASE = -3000, +- ERROR_HEARTBEAT_TERMINATE_REQUESTED = HEARTBEAT_ERROR_BASE, +-}; ++// Heartbeat Error Codes ++#define HEARTBEAT_ERROR_BASE (-3000) ++#define ERROR_HEARTBEAT_TERMINATE_REQUESTED (HEARTBEAT_ERROR_BASE) + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_ERRORS_H_ +diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h +index 3076a96..96d9ee1 100644 +--- a/include/media/stagefright/MediaExtractor.h ++++ b/include/media/stagefright/MediaExtractor.h +@@ -20,7 +20,7 @@ + + #include <utils/RefBase.h> + +-namespace android { ++namespace stagefright { + + class DataSource; + class MediaSource; +@@ -42,7 +42,7 @@ public: + + // Return container specific meta-data. The default implementation + // returns an empty metadata object. +- virtual sp<MetaData> getMetaData(); ++ virtual sp<MetaData> getMetaData() = 0; + + enum Flags { + CAN_SEEK_BACKWARD = 1, // the "seek 10secs back button" +@@ -53,7 +53,7 @@ public: + + // If subclasses do _not_ override this, the default is + // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE +- virtual uint32_t flags() const; ++ virtual uint32_t flags() const = 0; + + // for DRM + void setDrmFlag(bool flag) { +@@ -77,6 +77,6 @@ private: + MediaExtractor &operator=(const MediaExtractor &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_EXTRACTOR_H_ +diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h +index 3818e63..9aa6669 100644 +--- a/include/media/stagefright/MediaSource.h ++++ b/include/media/stagefright/MediaSource.h +@@ -24,7 +24,7 @@ + #include <utils/RefBase.h> + #include <utils/Vector.h> + +-namespace android { ++namespace stagefright { + + class MediaBuffer; + class MetaData; +@@ -117,6 +117,6 @@ private: + MediaSource &operator=(const MediaSource &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // MEDIA_SOURCE_H_ +diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h +index de3fc36..30b40c8 100644 +--- a/include/media/stagefright/MetaData.h ++++ b/include/media/stagefright/MetaData.h +@@ -26,7 +26,7 @@ + #include <utils/KeyedVector.h> + #include <utils/String8.h> + +-namespace android { ++namespace stagefright { + + // The following keys map to int32_t data unless indicated otherwise. + enum { +@@ -48,6 +48,7 @@ enum { + kKeyChannelCount = '#chn', // int32_t + kKeyChannelMask = 'chnm', // int32_t + kKeySampleRate = 'srte', // int32_t (audio sampling rate Hz) ++ kKeySampleSize = 'ssiz', // int32_t (sample size in bits) + kKeyFrameRate = 'frmR', // int32_t (video frame rate fps) + kKeyBitRate = 'brte', // int32_t (bps) + kKeyESDS = 'esds', // raw data +@@ -266,6 +267,6 @@ private: + // MetaData &operator=(const MetaData &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // META_DATA_H_ +diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h +index c24f612..bd70252 100644 +--- a/include/media/stagefright/Utils.h ++++ b/include/media/stagefright/Utils.h +@@ -25,10 +25,10 @@ + #include <system/audio.h> + #include <media/MediaPlayerInterface.h> + +-namespace android { ++namespace stagefright { + + #define FOURCC(c1, c2, c3, c4) \ +- (c1 << 24 | c2 << 16 | c3 << 8 | c4) ++ ((uint32_t) c1 << 24 | c2 << 16 | c3 << 8 | c4) + + uint16_t U16_AT(const uint8_t *ptr); + uint32_t U32_AT(const uint8_t *ptr); +@@ -50,15 +50,6 @@ void convertMessageToMetaData( + + AString MakeUserAgent(); + +-// Convert a MIME type to a AudioSystem::audio_format +-status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime); +- +-// Send information from MetaData to the HAL via AudioSink +-status_t sendMetaDataToHal(sp<MediaPlayerBase::AudioSink>& sink, const sp<MetaData>& meta); +- +-// Check whether the stream defined by meta can be offloaded to hardware +-bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming); +- +-} // namespace android ++} // namespace stagefright + + #endif // UTILS_H_ +diff --git a/include/media/stagefright/foundation/AAtomizer.h b/include/media/stagefright/foundation/AAtomizer.h +index 5f3a678..c20ea07 100644 +--- a/include/media/stagefright/foundation/AAtomizer.h ++++ b/include/media/stagefright/foundation/AAtomizer.h +@@ -26,7 +26,7 @@ + #include <utils/Vector.h> + #include <utils/threads.h> + +-namespace android { ++namespace stagefright { + + struct AAtomizer { + static const char *Atomize(const char *name); +@@ -46,6 +46,6 @@ private: + DISALLOW_EVIL_CONSTRUCTORS(AAtomizer); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // A_ATOMIZER_H_ +diff --git a/include/media/stagefright/foundation/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h +index 5510b12..589e5fe 100644 +--- a/include/media/stagefright/foundation/ABitReader.h ++++ b/include/media/stagefright/foundation/ABitReader.h +@@ -23,7 +23,7 @@ + #include <sys/types.h> + #include <stdint.h> + +-namespace android { ++namespace stagefright { + + struct ABitReader { + ABitReader(const uint8_t *data, size_t size); +@@ -49,6 +49,6 @@ private: + DISALLOW_EVIL_CONSTRUCTORS(ABitReader); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // A_BIT_READER_H_ +diff --git a/include/media/stagefright/foundation/ABuffer.h b/include/media/stagefright/foundation/ABuffer.h +index 28f0aed..4fa908e 100644 +--- a/include/media/stagefright/foundation/ABuffer.h ++++ b/include/media/stagefright/foundation/ABuffer.h +@@ -24,7 +24,7 @@ + #include <media/stagefright/foundation/ABase.h> + #include <utils/RefBase.h> + +-namespace android { ++namespace stagefright { + + struct AMessage; + +@@ -66,6 +66,6 @@ private: + DISALLOW_EVIL_CONSTRUCTORS(ABuffer); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // A_BUFFER_H_ +diff --git a/include/media/stagefright/foundation/ADebug.h b/include/media/stagefright/foundation/ADebug.h +index 450dcfe..a58d951 100644 +--- a/include/media/stagefright/foundation/ADebug.h ++++ b/include/media/stagefright/foundation/ADebug.h +@@ -24,7 +24,7 @@ + #include <media/stagefright/foundation/AString.h> + #include <utils/Log.h> + +-namespace android { ++namespace stagefright { + + #define LITERAL_TO_STRING_INTERNAL(x) #x + #define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x) +@@ -80,7 +80,7 @@ MAKE_COMPARATOR(GT,>) + __FILE__ ":" LITERAL_TO_STRING(__LINE__) \ + " Should not be here."); + +-} // namespace android ++} // namespace stagefright + + #endif // A_DEBUG_H_ + +diff --git a/include/media/stagefright/foundation/AHandler.h b/include/media/stagefright/foundation/AHandler.h +index b008b54..5a5ecc2 100644 +--- a/include/media/stagefright/foundation/AHandler.h ++++ b/include/media/stagefright/foundation/AHandler.h +@@ -21,7 +21,7 @@ + #include <media/stagefright/foundation/ALooper.h> + #include <utils/RefBase.h> + +-namespace android { ++namespace stagefright { + + struct AMessage; + +@@ -51,6 +51,6 @@ private: + DISALLOW_EVIL_CONSTRUCTORS(AHandler); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // A_HANDLER_H_ +diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h +index 0f8f1e1..8f97ed4 100644 +--- a/include/media/stagefright/foundation/AString.h ++++ b/include/media/stagefright/foundation/AString.h +@@ -20,7 +20,7 @@ + + #include <sys/types.h> + +-namespace android { ++namespace stagefright { + + struct AString { + AString(); +@@ -89,7 +89,7 @@ private: + + AString StringPrintf(const char *format, ...); + +-} // namespace android ++} // namespace stagefright + + #endif // A_STRING_H_ + +diff --git a/include/media/stagefright/foundation/hexdump.h b/include/media/stagefright/foundation/hexdump.h +index 8360c5a..18172f7 100644 +--- a/include/media/stagefright/foundation/hexdump.h ++++ b/include/media/stagefright/foundation/hexdump.h +@@ -20,7 +20,7 @@ + + #include <sys/types.h> + +-namespace android { ++namespace stagefright { + + struct AString; + +@@ -28,6 +28,6 @@ void hexdump( + const void *_data, size_t size, + size_t indent = 0, AString *appendTo = NULL); + +-} // namespace android ++} // namespace stagefright + + #endif // HEXDUMP_H_ +diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp +index fc6fd9c..c65a69e 100644 +--- a/media/libstagefright/DataSource.cpp ++++ b/media/libstagefright/DataSource.cpp +@@ -20,30 +20,12 @@ + #include "include/chromium_http_stub.h" + #endif + +-#include "include/AACExtractor.h" +-#include "include/DRMExtractor.h" +-#include "include/FLACExtractor.h" +-#include "include/HTTPBase.h" +-#include "include/MP3Extractor.h" +-#include "include/MPEG2PSExtractor.h" +-#include "include/MPEG2TSExtractor.h" + #include "include/MPEG4Extractor.h" +-#include "include/NuCachedSource2.h" +-#include "include/OggExtractor.h" +-#include "include/WAVExtractor.h" +-#include "include/WVMExtractor.h" + +-#include "matroska/MatroskaExtractor.h" +- +-#include <media/stagefright/foundation/AMessage.h> + #include <media/stagefright/DataSource.h> +-#include <media/stagefright/FileSource.h> + #include <media/stagefright/MediaErrors.h> +-#include <utils/String8.h> +- +-#include <cutils/properties.h> + +-namespace android { ++namespace stagefright { + + bool DataSource::getUInt16(off64_t offset, uint16_t *x) { + *x = 0; +@@ -105,6 +87,8 @@ status_t DataSource::getSize(off64_t *size) { + + //////////////////////////////////////////////////////////////////////////////// + ++#if 0 ++ + Mutex DataSource::gSnifferMutex; + List<DataSource::SnifferFunc> DataSource::gSniffers; + +@@ -226,8 +210,10 @@ sp<DataSource> DataSource::CreateFromURI( + return source; + } + ++#endif ++ + String8 DataSource::getMIMEType() const { + return String8("application/octet-stream"); + } + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp +index 4a0c35c..ccf60e3 100644 +--- a/media/libstagefright/ESDS.cpp ++++ b/media/libstagefright/ESDS.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "ESDS" + #include <utils/Log.h> + +@@ -22,7 +23,7 @@ + + #include <string.h> + +-namespace android { ++namespace stagefright { + + ESDS::ESDS(const void *data, size_t size) + : mData(new uint8_t[size]), +@@ -223,5 +224,6 @@ status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) { + return OK; + } + +-} // namespace android ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp +index ad985ee..71a0613 100644 +--- a/media/libstagefright/MPEG4Extractor.cpp ++++ b/media/libstagefright/MPEG4Extractor.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "MPEG4Extractor" + #include <utils/Log.h> + +@@ -38,7 +39,7 @@ + #include <media/stagefright/MetaData.h> + #include <utils/String8.h> + +-namespace android { ++namespace stagefright { + + class MPEG4Source : public MediaSource { + public: +@@ -726,6 +727,11 @@ static bool underMetaDataPath(const Vector<uint32_t> &path) { + static void convertTimeToDate(int64_t time_1904, String8 *s) { + time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600); + ++ if (time_1970 < 0) { ++ s->clear(); ++ return; ++ } ++ + char tmp[32]; + strftime(tmp, sizeof(tmp), "%Y%m%dT%H%M%S.000Z", gmtime(&time_1970)); + +@@ -1248,6 +1254,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { + ALOGV("*** coding='%s' %d channels, size %d, rate %d\n", + chunk, num_channels, sample_size, sample_rate); + mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); ++ mLastTrack->meta->setInt32(kKeySampleSize, sample_size); + mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); + + off64_t stop_offset = *offset + chunk_size; +@@ -1652,8 +1659,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { + + String8 s; + convertTimeToDate(creationTime, &s); +- +- mFileMetaData->setCString(kKeyDate, s.string()); ++ if (s.length()) { ++ mFileMetaData->setCString(kKeyDate, s.string()); ++ } + + *offset += chunk_size; + break; +@@ -2278,6 +2286,10 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( + objectType = 32 + br.getBits(6); + } + ++ if (objectType >= 1 && objectType <= 4) { ++ mLastTrack->meta->setInt32(kKeyAACProfile, objectType); ++ } ++ + uint32_t freqIndex = br.getBits(4); + + int32_t sampleRate = 0; +@@ -3154,6 +3166,7 @@ status_t MPEG4Source::read( + CHECK(mBuffer != NULL); + mBuffer->set_range(0, size); + mBuffer->meta_data()->clear(); ++ mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + +@@ -3276,6 +3289,7 @@ status_t MPEG4Source::read( + } + + mBuffer->meta_data()->clear(); ++ mBuffer->meta_data()->setInt64(kKey64BitFileOffset, offset); + mBuffer->meta_data()->setInt64( + kKeyTime, ((int64_t)cts * 1000000) / mTimescale); + +@@ -3360,6 +3374,18 @@ status_t MPEG4Source::fragmentedRead( + // move to next fragment + Sample lastSample = mCurrentSamples[mCurrentSamples.size() - 1]; + off64_t nextMoof = mNextMoofOffset; // lastSample.offset + lastSample.size; ++ ++ // If we're pointing to a sidx box then we skip it. ++ uint32_t hdr[2]; ++ if (mDataSource->readAt(nextMoof, hdr, 8) < 8) { ++ return ERROR_END_OF_STREAM; ++ } ++ uint64_t chunk_size = ntohl(hdr[0]); ++ uint32_t chunk_type = ntohl(hdr[1]); ++ if (chunk_type == FOURCC('s', 'i', 'd', 'x')) { ++ nextMoof += chunk_size; ++ } ++ + mCurrentMoofOffset = nextMoof; + mCurrentSamples.clear(); + mCurrentSampleIndex = 0; +@@ -3626,6 +3652,7 @@ static bool isCompatibleBrand(uint32_t fourcc) { + return false; + } + ++#if 0 + // Attempt to actually parse the 'ftyp' atom and determine if a suitable + // compatible brand is present. + // Also try to identify where this file's metadata ends +@@ -3756,5 +3783,8 @@ bool SniffMPEG4( + + return false; + } ++#endif ++ ++} // namespace stagefright + +-} // namespace android ++#undef LOG_TAG +diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp +index 11b80bf..72a7174 100644 +--- a/media/libstagefright/MediaBuffer.cpp ++++ b/media/libstagefright/MediaBuffer.cpp +@@ -29,7 +29,7 @@ + #include <ui/GraphicBuffer.h> + #include <sys/atomics.h> + +-namespace android { ++namespace stagefright { + + MediaBuffer::MediaBuffer(void *data, size_t size) + : mObserver(NULL), +@@ -200,4 +200,4 @@ MediaBuffer *MediaBuffer::clone() { + return buffer; + } + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp +index 80aae51..17dc08a 100644 +--- a/media/libstagefright/MediaBufferGroup.cpp ++++ b/media/libstagefright/MediaBufferGroup.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#undef LOG_TAG + #define LOG_TAG "MediaBufferGroup" + #include <utils/Log.h> + +@@ -21,7 +22,7 @@ + #include <media/stagefright/MediaBuffer.h> + #include <media/stagefright/MediaBufferGroup.h> + +-namespace android { ++namespace stagefright { + + MediaBufferGroup::MediaBufferGroup() + : mFirstBuffer(NULL), +@@ -83,4 +84,6 @@ void MediaBufferGroup::signalBufferReturned(MediaBuffer *) { + mCondition.signal(); + } + +-} // namespace android ++} // namespace stagefright ++ ++#undef LOG_TAG +diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp +index b5d4e44..1f17bc0 100644 +--- a/media/libstagefright/MediaDefs.cpp ++++ b/media/libstagefright/MediaDefs.cpp +@@ -16,7 +16,7 @@ + + #include <media/stagefright/MediaDefs.h> + +-namespace android { ++namespace stagefright { + + const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg"; + +@@ -56,4 +56,4 @@ const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm"; + const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt"; + const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp +index fd0e79c..faafe59 100644 +--- a/media/libstagefright/MediaSource.cpp ++++ b/media/libstagefright/MediaSource.cpp +@@ -16,7 +16,7 @@ + + #include <media/stagefright/MediaSource.h> + +-namespace android { ++namespace stagefright { + + MediaSource::MediaSource() {} + +@@ -61,4 +61,4 @@ int64_t MediaSource::ReadOptions::getLateBy() const { + return mLatenessUs; + } + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp +index ae6ae2d..c832c96 100644 +--- a/media/libstagefright/MetaData.cpp ++++ b/media/libstagefright/MetaData.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "MetaData" + #include <utils/Log.h> + +@@ -26,7 +27,7 @@ + #include <media/stagefright/foundation/hexdump.h> + #include <media/stagefright/MetaData.h> + +-namespace android { ++namespace stagefright { + + MetaData::MetaData() { + } +@@ -350,5 +351,6 @@ void MetaData::dumpToLog() const { + } + } + +-} // namespace android ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/SampleIterator.cpp b/media/libstagefright/SampleIterator.cpp +index eae721b..767393a 100644 +--- a/media/libstagefright/SampleIterator.cpp ++++ b/media/libstagefright/SampleIterator.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#undef LOG_TAG + #define LOG_TAG "SampleIterator" + //#define LOG_NDEBUG 0 + #include <utils/Log.h> +@@ -28,7 +29,7 @@ + + #include "include/SampleTable.h" + +-namespace android { ++namespace stagefright { + + SampleIterator::SampleIterator(SampleTable *table) + : mTable(table), +@@ -312,5 +313,6 @@ status_t SampleIterator::findSampleTime( + return OK; + } + +-} // namespace android ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp +index d9858d7..d2099df 100644 +--- a/media/libstagefright/SampleTable.cpp ++++ b/media/libstagefright/SampleTable.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#undef LOG_TAG + #define LOG_TAG "SampleTable" + //#define LOG_NDEBUG 0 + #include <utils/Log.h> +@@ -27,7 +28,7 @@ + #include <media/stagefright/DataSource.h> + #include <media/stagefright/Utils.h> + +-namespace android { ++namespace stagefright { + + // static + const uint32_t SampleTable::kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o'); +@@ -827,5 +828,6 @@ uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) { + return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex); + } + +-} // namespace android ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp +index 4db8e80..7b7c17a 100644 +--- a/media/libstagefright/Utils.cpp ++++ b/media/libstagefright/Utils.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "Utils" + #include <utils/Log.h> + +@@ -33,7 +34,7 @@ + #include <media/stagefright/Utils.h> + #include <media/AudioParameter.h> + +-namespace android { ++namespace stagefright { + + uint16_t U16_AT(const uint8_t *ptr) { + return ptr[0] << 8 | ptr[1]; +@@ -68,6 +69,7 @@ uint64_t hton64(uint64_t x) { + return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32); + } + ++#if 0 + status_t convertMetaDataToMessage( + const sp<MetaData> &meta, sp<AMessage> *format) { + format->clear(); +@@ -603,5 +605,8 @@ bool canOffloadStream(const sp<MetaData>& meta, bool hasVideo, bool isStreaming) + return AudioSystem::isOffloadSupported(info); + } + +-} // namespace android ++#endif ++ ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/foundation/AAtomizer.cpp b/media/libstagefright/foundation/AAtomizer.cpp +index b7b9e9f..4210cda 100644 +--- a/media/libstagefright/foundation/AAtomizer.cpp ++++ b/media/libstagefright/foundation/AAtomizer.cpp +@@ -18,7 +18,7 @@ + + #include "AAtomizer.h" + +-namespace android { ++namespace stagefright { + + // static + AAtomizer AAtomizer::gAtomizer; +@@ -64,4 +64,4 @@ uint32_t AAtomizer::Hash(const char *s) { + return sum; + } + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp +index 5499c32..22850ef 100644 +--- a/media/libstagefright/foundation/ABitReader.cpp ++++ b/media/libstagefright/foundation/ABitReader.cpp +@@ -16,9 +16,10 @@ + + #include "ABitReader.h" + ++#include <log/log.h> + #include <media/stagefright/foundation/ADebug.h> + +-namespace android { ++namespace stagefright { + + ABitReader::ABitReader(const uint8_t *data, size_t size) + : mData(data), +@@ -99,4 +100,4 @@ const uint8_t *ABitReader::data() const { + return mData - (mNumBitsLeft + 7) / 8; + } + +-} // namespace android ++} // namespace stagefright +diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp +index 6173db4..53f5ebc 100644 +--- a/media/libstagefright/foundation/ABuffer.cpp ++++ b/media/libstagefright/foundation/ABuffer.cpp +@@ -20,7 +20,7 @@ + #include "ALooper.h" + #include "AMessage.h" + +-namespace android { ++namespace stagefright { + + ABuffer::ABuffer(size_t capacity) + : mData(malloc(capacity)), +@@ -72,5 +72,5 @@ sp<AMessage> ABuffer::meta() { + return mMeta; + } + +-} // namespace android ++} // namespace stagefright + +diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp +index dee786d..d06110a 100644 +--- a/media/libstagefright/foundation/AString.cpp ++++ b/media/libstagefright/foundation/AString.cpp +@@ -23,7 +23,7 @@ + #include "ADebug.h" + #include "AString.h" + +-namespace android { ++namespace stagefright { + + // static + const char *AString::kEmptyString = ""; +@@ -325,7 +325,13 @@ AString StringPrintf(const char *format, ...) { + va_start(ap, format); + + char *buffer; ++#ifdef _MSC_VER ++ int n = vsnprintf(NULL, 0, format, ap); ++ buffer = new char[n+1]; ++ vsnprintf(buffer, n+1, format, ap); ++#else + vasprintf(&buffer, format, ap); ++#endif + + va_end(ap); + +@@ -337,5 +343,5 @@ AString StringPrintf(const char *format, ...) { + return result; + } + +-} // namespace android ++} // namespace stagefright + +diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp +index a44d832..7b566eb 100644 +--- a/media/libstagefright/foundation/hexdump.cpp ++++ b/media/libstagefright/foundation/hexdump.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "hexdump" + #include <utils/Log.h> + +@@ -27,7 +28,7 @@ + #include <stdint.h> + #include <stdio.h> + +-namespace android { ++namespace stagefright { + + static void appendIndent(AString *s, int32_t indent) { + static const char kWhitespace[] = +@@ -90,5 +91,6 @@ void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) { + } + } + +-} // namespace android ++} // namespace stagefright + ++#undef LOG_TAG +diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp +index 34d671a..3aa395c 100644 +--- a/media/libstagefright/id3/ID3.cpp ++++ b/media/libstagefright/id3/ID3.cpp +@@ -15,6 +15,7 @@ + */ + + //#define LOG_NDEBUG 0 ++#undef LOG_TAG + #define LOG_TAG "ID3" + #include <utils/Log.h> + +@@ -26,7 +27,7 @@ + #include <utils/String8.h> + #include <byteswap.h> + +-namespace android { ++namespace stagefright { + + static const size_t kMaxMetadataSize = 3 * 1024 * 1024; + +@@ -897,4 +898,6 @@ bool ID3::parseV1(const sp<DataSource> &source) { + return true; + } + +-} // namespace android ++} // namespace stagefright ++ ++#undef LOG_TAG +diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h +index 4a1c827..cdfc98e 100644 +--- a/media/libstagefright/include/AMRExtractor.h ++++ b/media/libstagefright/include/AMRExtractor.h +@@ -21,7 +21,7 @@ + #include <utils/Errors.h> + #include <media/stagefright/MediaExtractor.h> + +-namespace android { ++namespace stagefright { + + struct AMessage; + class String8; +@@ -57,6 +57,6 @@ bool SniffAMR( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + +-} // namespace android ++} // namespace stagefright + + #endif // AMR_EXTRACTOR_H_ +diff --git a/media/libstagefright/include/ESDS.h b/media/libstagefright/include/ESDS.h +index 2f40dae..bc4c5a6 100644 +--- a/media/libstagefright/include/ESDS.h ++++ b/media/libstagefright/include/ESDS.h +@@ -22,7 +22,7 @@ + + #include <media/stagefright/MediaErrors.h> + +-namespace android { ++namespace stagefright { + + class ESDS { + public: +@@ -68,5 +68,5 @@ private: + ESDS &operator=(const ESDS &); + }; + +-} // namespace android ++} // namespace stagefright + #endif // ESDS_H_ +diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h +index cca83ab..cbb3bbf 100644 +--- a/media/libstagefright/include/ID3.h ++++ b/media/libstagefright/include/ID3.h +@@ -20,7 +20,7 @@ + + #include <utils/RefBase.h> + +-namespace android { ++namespace stagefright { + + struct DataSource; + struct String8; +@@ -96,7 +96,7 @@ private: + ID3 &operator=(const ID3 &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // ID3_H_ + +diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h +index bbec1c4..98818b8 100644 +--- a/media/libstagefright/include/MPEG4Extractor.h ++++ b/media/libstagefright/include/MPEG4Extractor.h +@@ -27,7 +27,7 @@ + #include <utils/Vector.h> + #include <utils/String8.h> + +-namespace android { ++namespace stagefright { + + struct AMessage; + class DataSource; +@@ -129,6 +129,6 @@ bool SniffMPEG4( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + +-} // namespace android ++} // namespace stagefright + + #endif // MPEG4_EXTRACTOR_H_ +diff --git a/media/libstagefright/include/SampleIterator.h b/media/libstagefright/include/SampleIterator.h +index b5a043c..a4d52fe 100644 +--- a/media/libstagefright/include/SampleIterator.h ++++ b/media/libstagefright/include/SampleIterator.h +@@ -14,9 +14,12 @@ + * limitations under the License. + */ + ++#ifndef SAMPLE_ITERATOR_H_ ++#define SAMPLE_ITERATOR_H_ ++ + #include <utils/Vector.h> + +-namespace android { ++namespace stagefright { + + struct SampleTable; + +@@ -71,5 +74,6 @@ private: + SampleIterator &operator=(const SampleIterator &); + }; + +-} // namespace android ++} // namespace stagefright + ++#endif +diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h +index 847dff7..2a82077 100644 +--- a/media/libstagefright/include/SampleTable.h ++++ b/media/libstagefright/include/SampleTable.h +@@ -25,7 +25,7 @@ + #include <utils/RefBase.h> + #include <utils/threads.h> + +-namespace android { ++namespace stagefright { + + class DataSource; + struct SampleIterator; +@@ -148,6 +148,6 @@ private: + SampleTable &operator=(const SampleTable &); + }; + +-} // namespace android ++} // namespace stagefright + + #endif // SAMPLE_TABLE_H_ diff --git a/media/libstagefright/patches/system/core.patch b/media/libstagefright/patches/system/core.patch new file mode 100644 index 000000000..374afc5f3 --- /dev/null +++ b/media/libstagefright/patches/system/core.patch @@ -0,0 +1,1387 @@ +diff --git a/include/cutils/properties.h b/include/cutils/properties.h +index 2c70165..c380d5d 100644 +--- a/include/cutils/properties.h ++++ b/include/cutils/properties.h +@@ -19,7 +19,6 @@ + + #include <sys/cdefs.h> + #include <stddef.h> +-#include <sys/system_properties.h> + + #ifdef __cplusplus + extern "C" { +diff --git a/include/log/log.h b/include/log/log.h +index 7faddea..6131f01 100644 +--- a/include/log/log.h ++++ b/include/log/log.h +@@ -25,6 +25,16 @@ + // supports O_APPEND. These calls have mutex-protected data structures + // and so are NOT reentrant. Do not use LOG in a signal handler. + // ++ ++/* ++ * This is the local tag used for the following simplified ++ * logging macros. You can change this preprocessor definition ++ * before using the other macros to change the tag. ++ */ ++#ifndef LOG_TAG ++#define LOG_TAG NULL ++#endif ++ + #ifndef _LIBS_LOG_LOG_H + #define _LIBS_LOG_LOG_H + +@@ -40,6 +50,10 @@ + #include <log/uio.h> + #include <log/logd.h> + ++#ifdef _MSC_VER ++#define __builtin_expect(X, Y) (X) ++#endif ++ + #ifdef __cplusplus + extern "C" { + #endif +@@ -59,15 +73,6 @@ extern "C" { + #endif + #endif + +-/* +- * This is the local tag used for the following simplified +- * logging macros. You can change this preprocessor definition +- * before using the other macros to change the tag. +- */ +-#ifndef LOG_TAG +-#define LOG_TAG NULL +-#endif +- + // --------------------------------------------------------------------- + + /* +@@ -498,11 +503,11 @@ typedef enum { + * The stuff in the rest of this file should not be used directly. + */ + +-#define android_printLog(prio, tag, fmt...) \ +- __android_log_print(prio, tag, fmt) ++#define android_printLog(prio, tag, ...) \ ++ __android_log_print(prio, tag, __VA_ARGS__) + +-#define android_vprintLog(prio, cond, tag, fmt...) \ +- __android_log_vprint(prio, tag, fmt) ++#define android_vprintLog(prio, cond, tag, ...) \ ++ __android_log_vprint(prio, tag, __VA_ARGS__) + + /* XXX Macros to work around syntax errors in places where format string + * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF +@@ -519,9 +524,9 @@ typedef enum { + */ + #define __android_rest(first, ...) , ## __VA_ARGS__ + +-#define android_printAssert(cond, tag, fmt...) \ ++#define android_printAssert(cond, tag, ...) \ + __android_log_assert(cond, tag, \ +- __android_second(0, ## fmt, NULL) __android_rest(fmt)) ++ __android_second(0, ## __VA_ARGS__, NULL) __android_rest(__VA_ARGS__)) + + #define android_writeLog(prio, tag, text) \ + __android_log_write(prio, tag, text) +diff --git a/include/log/logprint.h b/include/log/logprint.h +index 481c96e..9b57e0e 100644 +--- a/include/log/logprint.h ++++ b/include/log/logprint.h +@@ -20,7 +20,6 @@ + #include <log/log.h> + #include <log/logger.h> + #include <log/event_tag_map.h> +-#include <pthread.h> + + #ifdef __cplusplus + extern "C" { +diff --git a/include/sysutils/List.h b/include/sysutils/List.h +index 31f7b37..72a9c44 100644 +--- a/include/sysutils/List.h ++++ b/include/sysutils/List.h +@@ -30,7 +30,7 @@ + #include <stddef.h> + #include <stdint.h> + +-namespace android { ++namespace stagefright { + namespace sysutils { + + /* +@@ -329,6 +329,6 @@ List<T>& List<T>::operator=(const List<T>& right) + } + + }; // namespace sysutils +-}; // namespace android ++}; // namespace stagefright + + #endif // _SYSUTILS_LIST_H +diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h +index 61dc832..eb52c8b 100644 +--- a/include/utils/CallStack.h ++++ b/include/utils/CallStack.h +@@ -25,7 +25,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + class CallStack + { +@@ -68,7 +68,7 @@ private: + backtrace_frame_t mStack[MAX_DEPTH]; + }; + +-}; // namespace android ++}; // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/include/utils/Condition.h b/include/utils/Condition.h +index e63ba7e..e8e7ae9 100644 +--- a/include/utils/Condition.h ++++ b/include/utils/Condition.h +@@ -30,7 +30,7 @@ + #include <utils/Timers.h> + + // --------------------------------------------------------------------------- +-namespace android { ++namespace stagefright { + // --------------------------------------------------------------------------- + + /* +@@ -138,10 +138,22 @@ inline void Condition::broadcast() { + pthread_cond_broadcast(&mCond); + } + ++#else ++ ++inline Condition::Condition() {} ++inline Condition::Condition(int type) {} ++inline Condition::~Condition() {} ++inline status_t Condition::wait(Mutex& mutex) { return OK; } ++inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { ++ return OK; ++} ++inline void Condition::signal() {} ++inline void Condition::broadcast() {} ++ + #endif // HAVE_PTHREADS + + // --------------------------------------------------------------------------- +-}; // namespace android ++}; // namespace stagefright + // --------------------------------------------------------------------------- + + #endif // _LIBS_UTILS_CONDITON_H +diff --git a/include/utils/Debug.h b/include/utils/Debug.h +index 08893bd..41f09eb 100644 +--- a/include/utils/Debug.h ++++ b/include/utils/Debug.h +@@ -20,7 +20,7 @@ + #include <stdint.h> + #include <sys/types.h> + +-namespace android { ++namespace stagefright { + // --------------------------------------------------------------------------- + + #ifdef __cplusplus +@@ -43,6 +43,6 @@ struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; }; + #endif + + // --------------------------------------------------------------------------- +-}; // namespace android ++}; // namespace stagefright + + #endif // ANDROID_UTILS_DEBUG_H +diff --git a/include/utils/Errors.h b/include/utils/Errors.h +index 0b75b19..0ceda59 100644 +--- a/include/utils/Errors.h ++++ b/include/utils/Errors.h +@@ -20,7 +20,7 @@ + #include <sys/types.h> + #include <errno.h> + +-namespace android { ++namespace stagefright { + + // use this type to return error codes + #ifdef HAVE_MS_C_RUNTIME +@@ -81,7 +81,7 @@ enum { + # define NO_ERROR 0L + #endif + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h +index c4faae0..ca4bfd6 100644 +--- a/include/utils/KeyedVector.h ++++ b/include/utils/KeyedVector.h +@@ -29,7 +29,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + template <typename KEY, typename VALUE> + class KeyedVector +@@ -217,7 +217,7 @@ const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; + } + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/List.h b/include/utils/List.h +index 403cd7f..f5c110e 100644 +--- a/include/utils/List.h ++++ b/include/utils/List.h +@@ -30,7 +30,7 @@ + #include <stddef.h> + #include <stdint.h> + +-namespace android { ++namespace stagefright { + + /* + * Doubly-linked list. Instantiate with "List<MyClass> myList". +@@ -56,9 +56,11 @@ protected: + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } ++#ifndef _MSC_VER + private: + friend class List; + friend class _ListIterator; ++#endif + T mVal; + _Node* mpPrev; + _Node* mpNext; +@@ -327,6 +329,6 @@ List<T>& List<T>::operator=(const List<T>& right) + return *this; + } + +-}; // namespace android ++}; // namespace stagefright + + #endif // _LIBS_UTILS_LIST_H +diff --git a/include/utils/Log.h b/include/utils/Log.h +index 4259c86..97cc4f3 100644 +--- a/include/utils/Log.h ++++ b/include/utils/Log.h +@@ -33,7 +33,7 @@ + + #ifdef __cplusplus + +-namespace android { ++namespace stagefright { + + /* + * A very simple utility that yells in the log when an operation takes too long. +@@ -62,9 +62,9 @@ private: + * } + */ + #define ALOGD_IF_SLOW(timeoutMillis, message) \ +- android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); ++ stagefright::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); + +-} // namespace android ++} // namespace stagefright + + #endif // __cplusplus + +diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h +index dd201c8..b33efef 100644 +--- a/include/utils/Mutex.h ++++ b/include/utils/Mutex.h +@@ -28,7 +28,7 @@ + #include <utils/Errors.h> + + // --------------------------------------------------------------------------- +-namespace android { ++namespace stagefright { + // --------------------------------------------------------------------------- + + class Condition; +@@ -118,6 +118,17 @@ inline status_t Mutex::tryLock() { + return -pthread_mutex_trylock(&mMutex); + } + ++#else ++ ++inline Mutex::Mutex() {} ++inline Mutex::Mutex(const char* name) {} ++inline Mutex::Mutex(int type, const char* name) {} ++inline Mutex::~Mutex() {} ++inline status_t Mutex::lock() { return OK; } ++inline void Mutex::unlock() {} ++inline status_t Mutex::tryLock() { return OK; } ++inline void Mutex::_init() {} ++ + #endif // HAVE_PTHREADS + + // --------------------------------------------------------------------------- +@@ -131,7 +142,7 @@ inline status_t Mutex::tryLock() { + typedef Mutex::Autolock AutoMutex; + + // --------------------------------------------------------------------------- +-}; // namespace android ++}; // namespace stagefright + // --------------------------------------------------------------------------- + + #endif // _LIBS_UTILS_MUTEX_H +diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h +index 90beb5f..4c43827 100644 +--- a/include/utils/RWLock.h ++++ b/include/utils/RWLock.h +@@ -28,7 +28,7 @@ + #include <utils/ThreadDefs.h> + + // --------------------------------------------------------------------------- +-namespace android { ++namespace stagefright { + // --------------------------------------------------------------------------- + + #if defined(HAVE_PTHREADS) +@@ -120,7 +120,7 @@ inline void RWLock::unlock() { + #endif // HAVE_PTHREADS + + // --------------------------------------------------------------------------- +-}; // namespace android ++}; // namespace stagefright + // --------------------------------------------------------------------------- + + #endif // _LIBS_UTILS_RWLOCK_H +diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h +index cbfe13a..e1f97c9 100644 +--- a/include/utils/RefBase.h ++++ b/include/utils/RefBase.h +@@ -27,8 +27,12 @@ + #include <utils/StrongPointer.h> + #include <utils/TypeHelpers.h> + ++#ifdef _MSC_VER ++#define __attribute__(X) ++#endif ++ + // --------------------------------------------------------------------------- +-namespace android { ++namespace stagefright { + + class TextOutput; + TextOutput& printWeakPointer(TextOutput& to, const void* val); +@@ -539,7 +543,11 @@ void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { + } + + +-}; // namespace android ++}; // namespace stagefright ++ ++#ifdef _MSC_VER ++#undef __attribute__ ++#endif + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h +index b670953..62d3ca4 100644 +--- a/include/utils/SharedBuffer.h ++++ b/include/utils/SharedBuffer.h +@@ -22,7 +22,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + class SharedBuffer + { +@@ -130,7 +130,7 @@ bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); + } + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h +index 2d3e82a..67bfea8 100644 +--- a/include/utils/SortedVector.h ++++ b/include/utils/SortedVector.h +@@ -29,7 +29,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + template <class TYPE> + class SortedVector : private SortedVectorImpl +@@ -48,7 +48,6 @@ public: + virtual ~SortedVector(); + + /*! copy operator */ +- const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; + SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* +@@ -168,12 +167,6 @@ SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rh + } + + template<class TYPE> inline +-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { +- SortedVectorImpl::operator = (rhs); +- return *this; +-} +- +-template<class TYPE> inline + const TYPE* SortedVector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); + } +@@ -274,7 +267,7 @@ int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) ); + } + +-}; // namespace android ++}; // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/include/utils/String16.h b/include/utils/String16.h +index d131bfc..40632d7 100644 +--- a/include/utils/String16.h ++++ b/include/utils/String16.h +@@ -30,7 +30,7 @@ extern "C" { + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + // --------------------------------------------------------------------------- + +@@ -243,7 +243,7 @@ inline String16::operator const char16_t*() const + return mString; + } + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/String8.h b/include/utils/String8.h +index ef59470..3007f21 100644 +--- a/include/utils/String8.h ++++ b/include/utils/String8.h +@@ -27,7 +27,11 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++#ifdef _MSC_VER ++#define __attribute__(X) ++#endif ++ ++namespace stagefright { + + class String16; + class TextOutput; +@@ -388,7 +392,11 @@ inline String8::operator const char*() const + return mString; + } + +-} // namespace android ++} // namespace stagefright ++ ++#ifdef _MSC_VER ++#undef __attribute__ ++#endif + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h +index aba9577..db22900 100644 +--- a/include/utils/StrongPointer.h ++++ b/include/utils/StrongPointer.h +@@ -24,7 +24,7 @@ + #include <stdlib.h> + + // --------------------------------------------------------------------------- +-namespace android { ++namespace stagefright { + + template<typename T> class wp; + +@@ -204,7 +204,7 @@ void sp<T>::set_pointer(T* ptr) { + m_ptr = ptr; + } + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h +index 13c9081..7a19244 100644 +--- a/include/utils/TypeHelpers.h ++++ b/include/utils/TypeHelpers.h +@@ -24,7 +24,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + /* + * Types traits +@@ -201,7 +201,7 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy) + || traits<TYPE>::has_trivial_move) + { +- memmove(d,s,n*sizeof(TYPE)); ++ memmove((void*)d,(void*)s,n*sizeof(TYPE)); + } else { + while (n--) { + if (!traits<TYPE>::has_trivial_copy) { +@@ -295,7 +295,7 @@ template <typename T> inline hash_t hash_type(T* const & value) { + return hash_type(uintptr_t(value)); + } + +-}; // namespace android ++}; // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h +index c8c87c3..b76a5e2 100644 +--- a/include/utils/Unicode.h ++++ b/include/utils/Unicode.h +@@ -22,9 +22,6 @@ + + extern "C" { + +-typedef uint32_t char32_t; +-typedef uint16_t char16_t; +- + // Standard string functions on char16_t strings. + int strcmp16(const char16_t *, const char16_t *); + int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +diff --git a/include/utils/Vector.h b/include/utils/Vector.h +index ed7b725..2388d06 100644 +--- a/include/utils/Vector.h ++++ b/include/utils/Vector.h +@@ -28,7 +28,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + template <typename TYPE> + class SortedVector; +@@ -55,10 +55,8 @@ public: + virtual ~Vector(); + + /*! copy operator */ +- const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const Vector<TYPE>& rhs); + +- const Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* +@@ -171,8 +169,12 @@ public: + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + +- inline status_t sort(compar_t cmp); +- inline status_t sort(compar_r_t cmp, void* state); ++ inline status_t sort(compar_t cmp) { ++ return VectorImpl::sort((VectorImpl::compar_t)cmp); ++ } ++ inline status_t sort(compar_r_t cmp, void* state) { ++ return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); ++ } + + // for debugging only + inline size_t getItemSize() const { return itemSize(); } +@@ -247,24 +249,12 @@ Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { + } + + template<class TYPE> inline +-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { +- VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); +- return *this; +-} +- +-template<class TYPE> inline + Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; + } + + template<class TYPE> inline +-const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { +- VectorImpl::operator = (rhs); +- return *this; +-} +- +-template<class TYPE> inline + const TYPE* Vector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); + } +@@ -373,16 +363,6 @@ ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); + } + +-template<class TYPE> inline +-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) { +- return VectorImpl::sort((VectorImpl::compar_t)cmp); +-} +- +-template<class TYPE> inline +-status_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) { +- return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +-} +- + // --------------------------------------------------------------------------- + + template<class TYPE> +@@ -415,7 +395,7 @@ void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) co + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); + } + +-}; // namespace android ++}; // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h +index 21ad71c..b83c946 100644 +--- a/include/utils/VectorImpl.h ++++ b/include/utils/VectorImpl.h +@@ -26,7 +26,7 @@ + // No user serviceable parts in here... + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + /*! + * Implementation of the guts of the vector<> class +@@ -175,7 +175,7 @@ private: + ssize_t replaceAt(const void* item, size_t index); + }; + +-}; // namespace android ++}; // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c +index 5283619..a34838c 100644 +--- a/liblog/fake_log_device.c ++++ b/liblog/fake_log_device.c +@@ -31,6 +31,34 @@ + #include <pthread.h> + #endif + ++#ifdef _MSC_VER ++#include <io.h> ++#include <process.h> ++#include <nspr/prprf.h> ++#define snprintf PR_snprintf ++ ++/* We don't want to indent large blocks because it causes unnecessary merge ++ * conflicts */ ++#define UNINDENTED_BLOCK_START { ++#define UNINDENTED_BLOCK_END } ++#else ++#define UNINDENTED_BLOCK_START ++#define UNINDENTED_BLOCK_END ++#endif ++ ++#ifdef _MSC_VER ++#include <io.h> ++#include <process.h> ++ ++/* We don't want to indent large blocks because it causes unnecessary merge ++ * conflicts */ ++#define UNINDENTED_BLOCK_START { ++#define UNINDENTED_BLOCK_END } ++#else ++#define UNINDENTED_BLOCK_START ++#define UNINDENTED_BLOCK_END ++#endif ++ + #define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ + + #define kTagSetSize 16 /* arbitrary */ +@@ -191,6 +219,7 @@ static void configureInitialState(const char* pathName, LogState* logState) + /* + * This is based on the the long-dead utils/Log.cpp code. + */ ++ UNINDENTED_BLOCK_START + const char* tags = getenv("ANDROID_LOG_TAGS"); + TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); + if (tags != NULL) { +@@ -264,11 +293,12 @@ static void configureInitialState(const char* pathName, LogState* logState) + } + } + } +- ++ UNINDENTED_BLOCK_END + + /* + * Taken from the long-dead utils/Log.cpp + */ ++ UNINDENTED_BLOCK_START + const char* fstr = getenv("ANDROID_PRINTF_LOG"); + LogFormat format; + if (fstr == NULL) { +@@ -293,6 +323,7 @@ static void configureInitialState(const char* pathName, LogState* logState) + } + + logState->outputFormat = format; ++ UNINDENTED_BLOCK_END + } + + /* +@@ -321,7 +352,7 @@ static const char* getPriorityString(int priority) + */ + static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { + int result = 0; +- struct iovec* end = iov + iovcnt; ++ const struct iovec* end = iov + iovcnt; + for (; iov < end; iov++) { + int w = write(fd, iov->iov_base, iov->iov_len); + if (w != iov->iov_len) { +@@ -354,7 +385,11 @@ static void showLog(LogState *state, + char prefixBuf[128], suffixBuf[128]; + char priChar; + time_t when; ++#ifdef _MSC_VER ++ int pid, tid; ++#else + pid_t pid, tid; ++#endif + + TRACE("LOG %d: %s %s", logPrio, tag, msg); + +@@ -382,6 +417,7 @@ static void showLog(LogState *state, + /* + * Construct a buffer containing the log header and log message. + */ ++ UNINDENTED_BLOCK_START + size_t prefixLen, suffixLen; + + switch (state->outputFormat) { +@@ -431,6 +467,7 @@ static void showLog(LogState *state, + /* + * Figure out how many lines there will be. + */ ++ UNINDENTED_BLOCK_START + const char* end = msg + strlen(msg); + size_t numLines = 0; + const char* p = msg; +@@ -443,7 +480,8 @@ static void showLog(LogState *state, + * Create an array of iovecs large enough to write all of + * the lines with a prefix and a suffix. + */ +- const size_t INLINE_VECS = 6; ++ UNINDENTED_BLOCK_START ++ #define INLINE_VECS 6 + const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*)); + struct iovec stackVec[INLINE_VECS]; + struct iovec* vec = stackVec; +@@ -467,6 +505,7 @@ static void showLog(LogState *state, + * Fill in the iovec pointers. + */ + p = msg; ++ UNINDENTED_BLOCK_START + struct iovec* v = vec; + int totalLen = 0; + while (numLines > 0 && p < end) { +@@ -476,6 +515,7 @@ static void showLog(LogState *state, + totalLen += prefixLen; + v++; + } ++ UNINDENTED_BLOCK_START + const char* start = p; + while (p < end && *p != '\n') p++; + if ((p-start) > 0) { +@@ -492,6 +532,7 @@ static void showLog(LogState *state, + v++; + } + numLines -= 1; ++ UNINDENTED_BLOCK_END + } + + /* +@@ -529,6 +570,10 @@ static void showLog(LogState *state, + /* if we allocated storage for the iovecs, free it */ + if (vec != stackVec) + free(vec); ++ UNINDENTED_BLOCK_END ++ UNINDENTED_BLOCK_END ++ UNINDENTED_BLOCK_END ++ UNINDENTED_BLOCK_END + } + + +@@ -567,6 +612,7 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count) + } + + /* pull out the three fields */ ++ UNINDENTED_BLOCK_START + int logPrio = *(const char*)vector[0].iov_base; + const char* tag = (const char*) vector[1].iov_base; + const char* msg = (const char*) vector[2].iov_base; +@@ -590,6 +636,7 @@ static ssize_t logWritev(int fd, const struct iovec* vector, int count) + } else { + //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); + } ++ UNINDENTED_BLOCK_END + + bail: + unlock(); +@@ -683,3 +730,6 @@ ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) + /* Assume that open() was called first. */ + return redirectWritev(fd, vector, count); + } ++ ++#undef UNINDENTED_BLOCK_START ++#undef UNINDENTED_BLOCK_END +diff --git a/liblog/logd_write.c b/liblog/logd_write.c +index fff7cc4..a194a9c 100644 +--- a/liblog/logd_write.c ++++ b/liblog/logd_write.c +@@ -33,7 +33,19 @@ + + #define LOG_BUF_SIZE 1024 + ++#ifdef _MSC_VER ++#include <nspr/prprf.h> ++#define snprintf PR_snprintf ++#define __builtin_trap abort ++static int W_OK = 0; ++static int access(char* c, int i) { return -1; } ++#endif ++ + #if FAKE_LOG_DEVICE ++int fakeLogOpen(const char *pathName, int flags); ++ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count); ++int fakeLogClose(int fd); ++ + // This will be defined when building for the host. + #define log_open(pathname, flags) fakeLogOpen(pathname, flags) + #define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +@@ -258,7 +270,11 @@ void __android_log_assert(const char *cond, const char *tag, + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + ++#ifdef _MSC_VER ++ abort(); ++#else + __builtin_trap(); /* trap so we have a chance to debug the situation */ ++#endif + } + + int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +diff --git a/liblog/logprint.c b/liblog/logprint.c +index 508c825..6b229df 100644 +--- a/liblog/logprint.c ++++ b/liblog/logprint.c +@@ -29,6 +29,35 @@ + #include <log/logd.h> + #include <log/logprint.h> + ++#ifdef _MSC_VER ++#include <nspr/prprf.h> ++#define snprintf PR_snprintf ++#define inline ++/* We don't want to indent large blocks because it causes unnecessary merge ++ * conflicts */ ++#define UNINDENTED_BLOCK_START { ++#define UNINDENTED_BLOCK_END } ++ ++static char * ++strsep(char **stringp, const char *delim) ++{ ++ char* res = *stringp; ++ while (**stringp) { ++ const char *c; ++ for (c = delim; *c; c++) { ++ if (**stringp == *c) { ++ **stringp++ = 0; ++ return res; ++ } ++ } ++ } ++ return res; ++} ++#else ++#define UNINDENTED_BLOCK_START ++#define UNINDENTED_BLOCK_END ++#endif ++ + typedef struct FilterInfo_t { + char *mTag; + android_LogPriority mPri; +@@ -268,6 +297,7 @@ int android_log_addFilterRule(AndroidLogFormat *p_format, + pri = ANDROID_LOG_VERBOSE; + } + ++ UNINDENTED_BLOCK_START + char *tagName; + + // Presently HAVE_STRNDUP is never defined, so the second case is always taken +@@ -280,11 +310,14 @@ int android_log_addFilterRule(AndroidLogFormat *p_format, + tagName[tagNameLength] = '\0'; + #endif /*HAVE_STRNDUP*/ + ++ UNINDENTED_BLOCK_START + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; ++ UNINDENTED_BLOCK_END ++ UNINDENTED_BLOCK_END + } + + return 0; +@@ -373,6 +406,7 @@ int android_log_processLogBuffer(struct logger_entry *buf, + return -1; + } + ++ UNINDENTED_BLOCK_START + int msgStart = -1; + int msgEnd = -1; + +@@ -404,6 +438,7 @@ int android_log_processLogBuffer(struct logger_entry *buf, + entry->messageLen = msgEnd - msgStart; + + return 0; ++ UNINDENTED_BLOCK_END + } + + /* +@@ -621,11 +656,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, + eventData += 4; + inCount -= 4; + +- if (map != NULL) { +- entry->tag = android_lookupEventTag(map, tagIndex); +- } else { +- entry->tag = NULL; +- } ++ entry->tag = NULL; + + /* + * If we don't have a map, or didn't find the tag number in the map, +@@ -644,6 +675,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, + /* + * Format the event log data into the buffer. + */ ++ UNINDENTED_BLOCK_START + char* outBuf = messageBuf; + size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ + int result; +@@ -687,6 +719,7 @@ int android_log_processBinaryLogBuffer(struct logger_entry *buf, + entry->message = messageBuf; + + return 0; ++ UNINDENTED_BLOCK_END + } + + /** +@@ -737,6 +770,7 @@ char *android_log_formatLogLine ( + /* + * Construct a buffer containing the log header and log message. + */ ++ UNINDENTED_BLOCK_START + size_t prefixLen, suffixLen; + + switch (p_format->format) { +@@ -807,6 +841,7 @@ char *android_log_formatLogLine ( + + /* the following code is tragically unreadable */ + ++ UNINDENTED_BLOCK_START + size_t numLines; + size_t i; + char *p; +@@ -882,6 +917,8 @@ char *android_log_formatLogLine ( + } + + return ret; ++ UNINDENTED_BLOCK_END ++ UNINDENTED_BLOCK_END + } + + /** +@@ -1014,3 +1051,6 @@ void logprint_run_tests() + fprintf(stderr, "tests complete\n"); + #endif + } ++ ++#undef UNINDENTED_BLOCK_START ++#undef UNINDENTED_BLOCK_END +diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h +index 47ae9d7..98f2190 100644 +--- a/libpixelflinger/codeflinger/tinyutils/Errors.h ++++ b/libpixelflinger/codeflinger/tinyutils/Errors.h +@@ -20,7 +20,7 @@ + #include <sys/types.h> + #include <errno.h> + +-namespace android { ++namespace stagefright { + namespace tinyutils { + + // use this type to return error codes +@@ -41,7 +41,7 @@ enum { + + + } // namespace tinyutils +-} // namespace android ++} // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h +index 9d8668b..62fc760 100644 +--- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h ++++ b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h +@@ -27,7 +27,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + namespace tinyutils { + + template <typename KEY, typename VALUE> +@@ -196,7 +196,7 @@ const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + } + + } // namespace tinyutils +-} // namespace android ++} // namespace stagefright + + // --------------------------------------------------------------------------- + +diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h +index a2b7005..71026c8 100644 +--- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h ++++ b/libpixelflinger/codeflinger/tinyutils/SortedVector.h +@@ -27,7 +27,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + namespace tinyutils { + + template <class TYPE> +@@ -276,7 +276,7 @@ int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { + } + + } // namespace tinyutils +-} // namespace android ++} // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h +index c07a17a..3fe87a2 100644 +--- a/libpixelflinger/codeflinger/tinyutils/Vector.h ++++ b/libpixelflinger/codeflinger/tinyutils/Vector.h +@@ -29,7 +29,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + namespace tinyutils { + + /*! +@@ -345,7 +345,7 @@ void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) co + } + + } // namespace tinyutils +-} // namespace android ++} // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h +index 56089b3..6cc55c4 100644 +--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h ++++ b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h +@@ -25,7 +25,7 @@ + // No user serviceable parts in here... + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + namespace tinyutils { + + /*! +@@ -187,7 +187,7 @@ private: + }; + + } // namespace tinyutils +-} // namespace android ++} // namespace stagefright + + + // --------------------------------------------------------------------------- +diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp +index f398a82..259b0a4 100644 +--- a/libutils/RefBase.cpp ++++ b/libutils/RefBase.cpp +@@ -20,7 +20,14 @@ + #include <utils/RefBase.h> + + #include <utils/Atomic.h> ++#ifdef _MSC_VER ++class CallStack { ++public: ++ CallStack(int x) {} ++}; ++#else + #include <utils/CallStack.h> ++#endif + #include <utils/Log.h> + #include <utils/threads.h> + +@@ -40,7 +47,7 @@ + #define DEBUG_REFS_ENABLED_BY_DEFAULT 0 + + // whether callstack are collected (significantly slows things down) +-#define DEBUG_REFS_CALLSTACK_ENABLED 1 ++#define DEBUG_REFS_CALLSTACK_ENABLED 0 + + // folder where stack traces are saved when DEBUG_REFS is enabled + // this folder needs to exist and be writable +@@ -51,7 +58,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + #define INITIAL_STRONG_VALUE (1<<28) + +@@ -647,4 +654,4 @@ void RefBase::renameRefId(RefBase* ref, + ref->mRefs->renameWeakRefId(old_id, new_id); + } + +-}; // namespace android ++}; // namespace stagefright +diff --git a/libutils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp +index 3555fb7..7aefe80 100644 +--- a/libutils/SharedBuffer.cpp ++++ b/libutils/SharedBuffer.cpp +@@ -22,7 +22,7 @@ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + SharedBuffer* SharedBuffer::alloc(size_t size) + { +@@ -110,4 +110,4 @@ int32_t SharedBuffer::release(uint32_t flags) const + } + + +-}; // namespace android ++}; // namespace stagefright +diff --git a/libutils/Static.cpp b/libutils/Static.cpp +index 3ed07a1..476240f 100644 +--- a/libutils/Static.cpp ++++ b/libutils/Static.cpp +@@ -17,7 +17,7 @@ + // All static variables go here, to control initialization and + // destruction order in the library. + +-namespace android { ++namespace stagefright { + + // For String8.cpp + extern void initialize_string8(); +@@ -46,4 +46,4 @@ public: + static LibUtilsFirstStatics gFirstStatics; + int gDarwinCantLoadAllObjects = 1; + +-} // namespace android ++} // namespace stagefright +diff --git a/libutils/String16.cpp b/libutils/String16.cpp +index b09b728..998cb94 100644 +--- a/libutils/String16.cpp ++++ b/libutils/String16.cpp +@@ -27,7 +27,7 @@ + #include <ctype.h> + + +-namespace android { ++namespace stagefright { + + static SharedBuffer* gEmptyStringBuf = NULL; + static char16_t* gEmptyString = NULL; +@@ -419,4 +419,4 @@ status_t String16::remove(size_t len, size_t begin) + return NO_MEMORY; + } + +-}; // namespace android ++}; // namespace stagefright +diff --git a/libutils/String8.cpp b/libutils/String8.cpp +index e852d77..3d4b285 100644 +--- a/libutils/String8.cpp ++++ b/libutils/String8.cpp +@@ -25,13 +25,13 @@ + #include <ctype.h> + + /* +- * Functions outside android is below the namespace android, since they use ++ * Functions outside android is below the namespace stagefright, since they use + * functions and constants in android namespace. + */ + + // --------------------------------------------------------------------------- + +-namespace android { ++namespace stagefright { + + // Separator used by resource paths. This is not platform dependent contrary + // to OS_PATH_SEPARATOR. +@@ -138,17 +138,8 @@ static char* allocFromUTF32(const char32_t* in, size_t len) + // --------------------------------------------------------------------------- + + String8::String8() +- : mString(getEmptyString()) +-{ +-} +- +-String8::String8(StaticLinkage) + : mString(0) + { +- // this constructor is used when we can't rely on the static-initializers +- // having run. In this case we always allocate an empty string. It's less +- // efficient than using getEmptyString(), but we assume it's uncommon. +- + char* data = static_cast<char*>( + SharedBuffer::alloc(sizeof(char))->data()); + data[0] = 0; +@@ -324,16 +315,27 @@ status_t String8::appendFormat(const char* fmt, ...) + status_t String8::appendFormatV(const char* fmt, va_list args) + { + int result = NO_ERROR; ++#ifndef _MSC_VER ++ va_list o; ++ va_copy(o, args); ++#endif + int n = vsnprintf(NULL, 0, fmt, args); + if (n != 0) { + size_t oldLength = length(); + char* buf = lockBuffer(oldLength + n); + if (buf) { ++#ifdef _MSC_VER + vsnprintf(buf + oldLength, n + 1, fmt, args); ++#else ++ vsnprintf(buf + oldLength, n + 1, fmt, o); ++#endif + } else { + result = NO_MEMORY; + } + } ++#ifndef _MSC_VER ++ va_end(o); ++#endif + return result; + } + +@@ -465,6 +467,8 @@ void String8::getUtf32(char32_t* dst) const + // --------------------------------------------------------------------------- + // Path functions + ++#if 0 ++ + void String8::setPathName(const char* name) + { + setPathName(name, strlen(name)); +@@ -637,4 +641,6 @@ String8& String8::convertToResPath() + return *this; + } + +-}; // namespace android ++#endif ++ ++}; // namespace stagefright +diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp +index a66e3bb..b8aae5e 100644 +--- a/libutils/Unicode.cpp ++++ b/libutils/Unicode.cpp +@@ -576,8 +576,8 @@ void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { + char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) { + const uint8_t* const u8end = src + srcLen; + const uint8_t* u8cur = src; +- const uint16_t* const u16end = dst + dstLen; +- char16_t* u16cur = dst; ++ const uint16_t* const u16end = (const uint16_t* const) dst + dstLen; ++ uint16_t* u16cur = (uint16_t*) dst; + + while (u8cur < u8end && u16cur < u16end) { + size_t u8len = utf8_codepoint_len(*u8cur); +@@ -593,14 +593,14 @@ char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + if (u16cur >= u16end) { + // Ooops... not enough room for this surrogate pair. +- return u16cur-1; ++ return (char16_t*) u16cur-1; + } + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } +- return u16cur; ++ return (char16_t*) u16cur; + } + + } +diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp +index 5a79647..114fa90 100644 +--- a/libutils/VectorImpl.cpp ++++ b/libutils/VectorImpl.cpp +@@ -29,7 +29,7 @@ + /*****************************************************************************/ + + +-namespace android { ++namespace stagefright { + + // ---------------------------------------------------------------------------- + +@@ -621,5 +621,5 @@ ssize_t SortedVectorImpl::remove(const void* item) + + /*****************************************************************************/ + +-}; // namespace android ++}; // namespace stagefright + diff --git a/media/libstagefright/ports/bsd/include/byteswap.h b/media/libstagefright/ports/bsd/include/byteswap.h new file mode 100644 index 000000000..5bd379a8c --- /dev/null +++ b/media/libstagefright/ports/bsd/include/byteswap.h @@ -0,0 +1,15 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef BYTESWAP_H_ +#define BYTESWAP_H_ + +#include <sys/endian.h> + +#ifdef __OpenBSD__ +#define bswap_16(x) swap16(x) +#else +#define bswap_16(x) bswap16(x) +#endif + +#endif diff --git a/media/libstagefright/ports/darwin/include/byteswap.h b/media/libstagefright/ports/darwin/include/byteswap.h new file mode 100644 index 000000000..903a9198c --- /dev/null +++ b/media/libstagefright/ports/darwin/include/byteswap.h @@ -0,0 +1,10 @@ +/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef BYTESWAP_H_
+#define BYTESWAP_H_
+
+#include <libkern/OSByteOrder.h>
+#define bswap_16 OSSwapInt16
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/arpa/inet.h b/media/libstagefright/ports/win32/include/arpa/inet.h new file mode 100644 index 000000000..e693e856e --- /dev/null +++ b/media/libstagefright/ports/win32/include/arpa/inet.h @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef INET_H_
+#define INET_H_
+
+#include <netinet/in.h>
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/byteswap.h b/media/libstagefright/ports/win32/include/byteswap.h new file mode 100644 index 000000000..aabc69726 --- /dev/null +++ b/media/libstagefright/ports/win32/include/byteswap.h @@ -0,0 +1,10 @@ +/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef BYTESWAP_H_
+#define BYTESWAP_H_
+
+#include <stdlib.h>
+#define bswap_16 _byteswap_ushort
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/netinet/in.h b/media/libstagefright/ports/win32/include/netinet/in.h new file mode 100644 index 000000000..90feff10a --- /dev/null +++ b/media/libstagefright/ports/win32/include/netinet/in.h @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef IN_H_ +#define IN_H_ + +#include <stdint.h> + +#if defined(_M_IX86) || defined(_M_AMD64) + +static uint32_t +ntohl(uint32_t x) +{ + return x << 24 | (x << 8 & 0xff0000) | (x >> 8 & 0xff00) | x >> 24; +} + +static uint16_t +ntohs(uint16_t x) +{ + return x << 8 | x >> 8; +} + +static uint32_t +htonl(uint32_t x) +{ + return x << 24 | (x << 8 & 0xff0000) | (x >> 8 & 0xff00) | x >> 24; +} + +static uint16_t +htons(uint16_t x) +{ + return x << 8 | x >> 8; +} + +#else +#error Unsupported architecture +#endif + +#endif diff --git a/media/libstagefright/ports/win32/include/pthread.h b/media/libstagefright/ports/win32/include/pthread.h new file mode 100644 index 000000000..1aa4e7032 --- /dev/null +++ b/media/libstagefright/ports/win32/include/pthread.h @@ -0,0 +1 @@ +// Intentionally left blank diff --git a/media/libstagefright/ports/win32/include/sys/cdefs.h b/media/libstagefright/ports/win32/include/sys/cdefs.h new file mode 100644 index 000000000..1aa4e7032 --- /dev/null +++ b/media/libstagefright/ports/win32/include/sys/cdefs.h @@ -0,0 +1 @@ +// Intentionally left blank diff --git a/media/libstagefright/ports/win32/include/sys/time.h b/media/libstagefright/ports/win32/include/sys/time.h new file mode 100644 index 000000000..b8dd50914 --- /dev/null +++ b/media/libstagefright/ports/win32/include/sys/time.h @@ -0,0 +1,9 @@ +/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#ifndef TIME_H_
+#define TIME_H_
+
+#include <time.h>
+
+#endif
diff --git a/media/libstagefright/ports/win32/include/unistd.h b/media/libstagefright/ports/win32/include/unistd.h new file mode 100644 index 000000000..1aa4e7032 --- /dev/null +++ b/media/libstagefright/ports/win32/include/unistd.h @@ -0,0 +1 @@ +// Intentionally left blank diff --git a/media/libstagefright/stubs/empty/ALooper.h b/media/libstagefright/stubs/empty/ALooper.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/ALooper.h diff --git a/media/libstagefright/stubs/empty/ALooperRoster.h b/media/libstagefright/stubs/empty/ALooperRoster.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/ALooperRoster.h diff --git a/media/libstagefright/stubs/empty/binder/Parcel.h b/media/libstagefright/stubs/empty/binder/Parcel.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/binder/Parcel.h diff --git a/media/libstagefright/stubs/empty/hardware/audio.h b/media/libstagefright/stubs/empty/hardware/audio.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/hardware/audio.h diff --git a/media/libstagefright/stubs/empty/media/AudioParameter.h b/media/libstagefright/stubs/empty/media/AudioParameter.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/media/AudioParameter.h diff --git a/media/libstagefright/stubs/empty/media/AudioSystem.h b/media/libstagefright/stubs/empty/media/AudioSystem.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/media/AudioSystem.h diff --git a/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h b/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/media/MediaPlayerInterface.h diff --git a/media/libstagefright/stubs/empty/sys/system_properties.h b/media/libstagefright/stubs/empty/sys/system_properties.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/sys/system_properties.h diff --git a/media/libstagefright/stubs/empty/system/audio.h b/media/libstagefright/stubs/empty/system/audio.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/media/libstagefright/stubs/empty/system/audio.h diff --git a/media/libstagefright/stubs/include/cutils/atomic.h b/media/libstagefright/stubs/include/cutils/atomic.h new file mode 100644 index 000000000..e22550177 --- /dev/null +++ b/media/libstagefright/stubs/include/cutils/atomic.h @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef ATOMIC_H_ +#define ATOMIC_H_ + +#include <stdint.h> + +// This implements the atomic primatives without any atomicity guarantees. This +// makes the totally unsafe. However we're only using the demuxer in a single +// thread. + +namespace stagefright { +static inline int32_t +android_atomic_dec(volatile int32_t* aValue) +{ + return (*aValue)--; +} + +static inline int32_t +android_atomic_inc(volatile int32_t* aValue) +{ + return (*aValue)++; +} + +static inline int32_t +android_atomic_or(int32_t aModifier, volatile int32_t* aValue) +{ + int32_t ret = *aValue; + *aValue |= aModifier; + return ret; +} + +static inline int32_t +android_atomic_add(int32_t aModifier, volatile int32_t* aValue) +{ + int32_t ret = *aValue; + *aValue += aModifier; + return ret; +} + +static inline int32_t +android_atomic_cmpxchg(int32_t aOld, int32_t aNew, volatile int32_t* aValue) +{ + if (*aValue == aOld) + { + return *aValue = aNew; + } + return aOld; +} +} + +#endif diff --git a/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h b/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h new file mode 100644 index 000000000..f22439dd9 --- /dev/null +++ b/media/libstagefright/stubs/include/media/stagefright/foundation/AMessage.h @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef A_MESSAGE_H_ +#define A_MESSAGE_H_ + +#include <utils/RefBase.h> + +namespace stagefright { + +struct AMessage : public RefBase { +public: + void post() {} +}; + +} + +#endif diff --git a/media/libstagefright/stubs/include/sys/atomics.h b/media/libstagefright/stubs/include/sys/atomics.h new file mode 100644 index 000000000..975f4f7d9 --- /dev/null +++ b/media/libstagefright/stubs/include/sys/atomics.h @@ -0,0 +1,10 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef ATOMICS_H_ +#define ATOMICS_H_ + +#define __atomic_dec android_atomic_dec +#define __atomic_inc android_atomic_inc + +#endif diff --git a/media/libstagefright/stubs/include/ui/GraphicBuffer.h b/media/libstagefright/stubs/include/ui/GraphicBuffer.h new file mode 100644 index 000000000..47fa5dbad --- /dev/null +++ b/media/libstagefright/stubs/include/ui/GraphicBuffer.h @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#ifndef GRAPHIC_BUFFER_H_ +#define GRAPHIC_BUFFER_H_ + +#include <utils/RefBase.h> + +namespace stagefright { + +class GraphicBuffer : public RefBase { +}; + +} + +#endif diff --git a/media/libstagefright/stubs/include/utils/threads.h b/media/libstagefright/stubs/include/utils/threads.h new file mode 100644 index 000000000..09b9bcb2e --- /dev/null +++ b/media/libstagefright/stubs/include/utils/threads.h @@ -0,0 +1,5 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +#include <utils/Condition.h> +#include <utils/Mutex.h> diff --git a/media/libstagefright/system/core/debuggerd/backtrace.h b/media/libstagefright/system/core/debuggerd/backtrace.h new file mode 100644 index 000000000..c5c786a01 --- /dev/null +++ b/media/libstagefright/system/core/debuggerd/backtrace.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef _DEBUGGERD_BACKTRACE_H +#define _DEBUGGERD_BACKTRACE_H + +#include <stddef.h> +#include <stdbool.h> +#include <sys/types.h> + +#include <corkscrew/ptrace.h> + +/* Dumps a backtrace using a format similar to what Dalvik uses so that the result + * can be intermixed in a bug report. */ +void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, + int* total_sleep_time_usec); + +#endif // _DEBUGGERD_BACKTRACE_H diff --git a/media/libstagefright/system/core/include/android/log.h b/media/libstagefright/system/core/include/android/log.h new file mode 100644 index 000000000..0ea4c298b --- /dev/null +++ b/media/libstagefright/system/core/include/android/log.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#ifndef _ANDROID_LOG_H +#define _ANDROID_LOG_H + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit) since + * platform release 1.5 + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +/* + * Support routines to send messages to the Android in-kernel log buffer, + * which can later be accessed through the 'logcat' utility. + * + * Each log message must have + * - a priority + * - a log tag + * - some text + * + * The tag normally corresponds to the component that emits the log message, + * and should be reasonably small. + * + * Log message text may be truncated to less than an implementation-specific + * limit (e.g. 1023 characters max). + * + * Note that a newline character ("\n") will be appended automatically to your + * log message, if not already there. It is not possible to send several messages + * and have them appear on a single line in logcat. + * + * PLEASE USE LOGS WITH MODERATION: + * + * - Sending log messages eats CPU and slow down your application and the + * system. + * + * - The circular log buffer is pretty small (<64KB), sending many messages + * might push off other important log messages from the rest of the system. + * + * - In release builds, only send log messages to account for exceptional + * conditions. + * + * NOTE: These functions MUST be implemented by /system/lib/liblog.so + */ + +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Android log priority values, in ascending priority order. + */ +typedef enum android_LogPriority { + ANDROID_LOG_UNKNOWN = 0, + ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */ + ANDROID_LOG_VERBOSE, + ANDROID_LOG_DEBUG, + ANDROID_LOG_INFO, + ANDROID_LOG_WARN, + ANDROID_LOG_ERROR, + ANDROID_LOG_FATAL, + ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */ +} android_LogPriority; + +/* + * Send a simple string to the log. + */ +int __android_log_write(int prio, const char *tag, const char *text); + +/* + * Send a formatted string to the log, used like printf(fmt,...) + */ +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +#if defined(__GNUC__) + __attribute__ ((format(printf, 3, 4))) +#endif + ; + +/* + * A variant of __android_log_print() that takes a va_list to list + * additional parameters. + */ +int __android_log_vprint(int prio, const char *tag, + const char *fmt, va_list ap); + +/* + * Log an assertion failure and SIGTRAP the process to have a chance + * to inspect it, if a debugger is attached. This uses the FATAL priority. + */ +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +#if defined(__GNUC__) + __attribute__ ((noreturn)) + __attribute__ ((format(printf, 3, 4))) +#endif + ; + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_LOG_H */ diff --git a/media/libstagefright/system/core/include/corkscrew/backtrace.h b/media/libstagefright/system/core/include/corkscrew/backtrace.h new file mode 100644 index 000000000..556ad04c0 --- /dev/null +++ b/media/libstagefright/system/core/include/corkscrew/backtrace.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 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. + */ + +/* A stack unwinder. */ + +#ifndef _CORKSCREW_BACKTRACE_H +#define _CORKSCREW_BACKTRACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <corkscrew/ptrace.h> +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> + +/* + * Describes a single frame of a backtrace. + */ +typedef struct { + uintptr_t absolute_pc; /* absolute PC offset */ + uintptr_t stack_top; /* top of stack for this frame */ + size_t stack_size; /* size of this stack frame */ +} backtrace_frame_t; + +/* + * Describes the symbols associated with a backtrace frame. + */ +typedef struct { + uintptr_t relative_pc; /* relative frame PC offset from the start of the library, + or the absolute PC if the library is unknown */ + uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the + library or 0 if the library is unknown */ + char* map_name; /* executable or library name, or NULL if unknown */ + char* symbol_name; /* symbol name, or NULL if unknown */ + char* demangled_name; /* demangled symbol name, or NULL if unknown */ +} backtrace_symbol_t; + +/* + * Unwinds the call stack for the current thread of execution. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ +ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +/* + * Unwinds the call stack for a thread within this process. + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + * + * The task is briefly suspended while the backtrace is being collected. + */ +ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, + size_t ignore_depth, size_t max_depth); + +/* + * Unwinds the call stack of a task within a remote process using ptrace(). + * Populates the backtrace array with the program counters from the call stack. + * Returns the number of frames collected, or -1 if an error occurred. + */ +ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth); + +/* + * Gets the symbols for each frame of a backtrace. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ +void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + +/* + * Gets the symbols for each frame of a backtrace from a remote process. + * The symbols array must be big enough to hold one symbol record per frame. + * The symbols must later be freed using free_backtrace_symbols. + */ +void get_backtrace_symbols_ptrace(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + +/* + * Frees the storage associated with backtrace symbols. + */ +void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames); + +enum { + // A hint for how big to make the line buffer for format_backtrace_line + MAX_BACKTRACE_LINE_LENGTH = 800, +}; + +/** + * Formats a line from a backtrace as a zero-terminated string into the specified buffer. + */ +void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_BACKTRACE_H diff --git a/media/libstagefright/system/core/include/corkscrew/map_info.h b/media/libstagefright/system/core/include/corkscrew/map_info.h new file mode 100644 index 000000000..14bfad67d --- /dev/null +++ b/media/libstagefright/system/core/include/corkscrew/map_info.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Process memory map. */ + +#ifndef _CORKSCREW_MAP_INFO_H +#define _CORKSCREW_MAP_INFO_H + +#include <sys/types.h> +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct map_info { + struct map_info* next; + uintptr_t start; + uintptr_t end; + bool is_readable; + bool is_writable; + bool is_executable; + void* data; // arbitrary data associated with the map by the user, initially NULL + char name[]; +} map_info_t; + +/* Loads memory map from /proc/<tid>/maps. */ +map_info_t* load_map_info_list(pid_t tid); + +/* Frees memory map. */ +void free_map_info_list(map_info_t* milist); + +/* Finds the memory map that contains the specified address. */ +const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr); + +/* Returns true if the addr is in a readable map. */ +bool is_readable_map(const map_info_t* milist, uintptr_t addr); +/* Returns true if the addr is in a writable map. */ +bool is_writable_map(const map_info_t* milist, uintptr_t addr); +/* Returns true if the addr is in an executable map. */ +bool is_executable_map(const map_info_t* milist, uintptr_t addr); + +/* Acquires a reference to the memory map for this process. + * The result is cached and refreshed automatically. + * Make sure to release the map info when done. */ +map_info_t* acquire_my_map_info_list(); + +/* Releases a reference to the map info for this process that was + * previous acquired using acquire_my_map_info_list(). */ +void release_my_map_info_list(map_info_t* milist); + +/* Flushes the cached memory map so the next call to + * acquire_my_map_info_list() gets fresh data. */ +void flush_my_map_info_list(); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_MAP_INFO_H diff --git a/media/libstagefright/system/core/include/corkscrew/ptrace.h b/media/libstagefright/system/core/include/corkscrew/ptrace.h new file mode 100644 index 000000000..76276d89a --- /dev/null +++ b/media/libstagefright/system/core/include/corkscrew/ptrace.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2011 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. + */ + +/* Useful ptrace() utility functions. */ + +#ifndef _CORKSCREW_PTRACE_H +#define _CORKSCREW_PTRACE_H + +#include <corkscrew/map_info.h> +#include <corkscrew/symbol_table.h> + +#include <sys/types.h> +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Stores information about a process that is used for several different + * ptrace() based operations. */ +typedef struct { + map_info_t* map_info_list; +} ptrace_context_t; + +/* Describes how to access memory from a process. */ +typedef struct { + pid_t tid; + const map_info_t* map_info_list; +} memory_t; + +#if __i386__ +/* ptrace() register context. */ +typedef struct pt_regs_x86 { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t xds; + uint32_t xes; + uint32_t xfs; + uint32_t xgs; + uint32_t orig_eax; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t esp; + uint32_t xss; +} pt_regs_x86_t; +#endif + +#if __mips__ +/* ptrace() GET_REGS context. */ +typedef struct pt_regs_mips { + uint64_t regs[32]; + uint64_t lo; + uint64_t hi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; +} pt_regs_mips_t; +#endif + +/* + * Initializes a memory structure for accessing memory from this process. + */ +void init_memory(memory_t* memory, const map_info_t* map_info_list); + +/* + * Initializes a memory structure for accessing memory from another process + * using ptrace(). + */ +void init_memory_ptrace(memory_t* memory, pid_t tid); + +/* + * Reads a word of memory safely. + * If the memory is local, ensures that the address is readable before dereferencing it. + * Returns false and a value of 0xffffffff if the word could not be read. + */ +bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value); + +/* + * Reads a word of memory safely using ptrace(). + * Returns false and a value of 0xffffffff if the word could not be read. + */ +bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value); + +/* + * Loads information needed for examining a remote process using ptrace(). + * The caller must already have successfully attached to the process + * using ptrace(). + * + * The context can be used for any threads belonging to that process + * assuming ptrace() is attached to them before performing the actual + * unwinding. The context can continue to be used to decode backtraces + * even after ptrace() has been detached from the process. + */ +ptrace_context_t* load_ptrace_context(pid_t pid); + +/* + * Frees a ptrace context. + */ +void free_ptrace_context(ptrace_context_t* context); + +/* + * Finds a symbol using ptrace. + * Returns the containing map and information about the symbol, or + * NULL if one or the other is not available. + */ +void find_symbol_ptrace(const ptrace_context_t* context, + uintptr_t addr, const map_info_t** out_map_info, const symbol_t** out_symbol); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_PTRACE_H diff --git a/media/libstagefright/system/core/include/corkscrew/symbol_table.h b/media/libstagefright/system/core/include/corkscrew/symbol_table.h new file mode 100644 index 000000000..4998750bf --- /dev/null +++ b/media/libstagefright/system/core/include/corkscrew/symbol_table.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef _CORKSCREW_SYMBOL_TABLE_H +#define _CORKSCREW_SYMBOL_TABLE_H + +#include <stdint.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uintptr_t start; + uintptr_t end; + char* name; +} symbol_t; + +typedef struct { + symbol_t* symbols; + size_t num_symbols; +} symbol_table_t; + +/* + * Loads a symbol table from a given file. + * Returns NULL on error. + */ +symbol_table_t* load_symbol_table(const char* filename); + +/* + * Frees a symbol table. + */ +void free_symbol_table(symbol_table_t* table); + +/* + * Finds a symbol associated with an address in the symbol table. + * Returns NULL if not found. + */ +const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr); + +#ifdef __cplusplus +} +#endif + +#endif // _CORKSCREW_SYMBOL_TABLE_H diff --git a/media/libstagefright/system/core/include/cutils/jstring.h b/media/libstagefright/system/core/include/cutils/jstring.h new file mode 100644 index 000000000..ee0018fcc --- /dev/null +++ b/media/libstagefright/system/core/include/cutils/jstring.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef __CUTILS_STRING16_H +#define __CUTILS_STRING16_H + +#include <stdint.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t char16_t; + +extern char * strndup16to8 (const char16_t* s, size_t n); +extern size_t strnlen16to8 (const char16_t* s, size_t n); +extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n); + +extern char16_t * strdup8to16 (const char* s, size_t *out_len); +extern size_t strlen8to16 (const char* utf8Str); +extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len); +extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length, + size_t *out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_STRING16_H */ diff --git a/media/libstagefright/system/core/include/cutils/log.h b/media/libstagefright/system/core/include/cutils/log.h new file mode 100644 index 000000000..0e0248e50 --- /dev/null +++ b/media/libstagefright/system/core/include/cutils/log.h @@ -0,0 +1 @@ +#include <log/log.h> diff --git a/media/libstagefright/system/core/include/cutils/properties.h b/media/libstagefright/system/core/include/cutils/properties.h new file mode 100644 index 000000000..79029f3e6 --- /dev/null +++ b/media/libstagefright/system/core/include/cutils/properties.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef __CUTILS_PROPERTIES_H +#define __CUTILS_PROPERTIES_H + +#include <sys/types.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* System properties are *small* name value pairs managed by the +** property service. If your data doesn't fit in the provided +** space it is not appropriate for a system property. +** +** WARNING: system/bionic/include/sys/system_properties.h also defines +** these, but with different names. (TODO: fix that) +*/ +#define PROPERTY_KEY_MAX PROP_NAME_MAX +#define PROPERTY_VALUE_MAX PROP_VALUE_MAX + +/* property_get: returns the length of the value which will never be +** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated. +** (the length does not include the terminating zero). +** +** If the property read fails or returns an empty value, the default +** value is used (if nonnull). +*/ +int property_get(const char *key, char *value, const char *default_value); + +/* property_set: returns 0 on success, < 0 on failure +*/ +int property_set(const char *key, const char *value); + +int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); + +#if defined(__BIONIC_FORTIFY) + +extern int __property_get_real(const char *, char *, const char *) + __asm__(__USER_LABEL_PREFIX__ "property_get"); +__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer"); + +__BIONIC_FORTIFY_INLINE +int property_get(const char *key, char *value, const char *default_value) { + size_t bos = __bos(value); + if (bos < PROPERTY_VALUE_MAX) { + __property_get_too_small_error(); + } + return __property_get_real(key, value, default_value); +} + +#endif + +#ifdef HAVE_SYSTEM_PROPERTY_SERVER +/* + * We have an external property server instead of built-in libc support. + * Used by the simulator. + */ +#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop" + +enum { + kSystemPropertyUnknown = 0, + kSystemPropertyGet, + kSystemPropertySet, + kSystemPropertyList +}; +#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/media/libstagefright/system/core/include/cutils/sched_policy.h b/media/libstagefright/system/core/include/cutils/sched_policy.h new file mode 100644 index 000000000..ba84ce32a --- /dev/null +++ b/media/libstagefright/system/core/include/cutils/sched_policy.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef __CUTILS_SCHED_POLICY_H +#define __CUTILS_SCHED_POLICY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */ +typedef enum { + SP_DEFAULT = -1, + SP_BACKGROUND = 0, + SP_FOREGROUND = 1, + SP_SYSTEM = 2, // can't be used with set_sched_policy() + SP_AUDIO_APP = 3, + SP_AUDIO_SYS = 4, + SP_CNT, + SP_MAX = SP_CNT - 1, + SP_SYSTEM_DEFAULT = SP_FOREGROUND, +} SchedPolicy; + +/* Assign thread tid to the cgroup associated with the specified policy. + * If the thread is a thread group leader, that is it's gettid() == getpid(), + * then the other threads in the same thread group are _not_ affected. + * On platforms which support gettid(), zero tid means current thread. + * Return value: 0 for success, or -errno for error. + */ +extern int set_sched_policy(int tid, SchedPolicy policy); + +/* Return the policy associated with the cgroup of thread tid via policy pointer. + * On platforms which support gettid(), zero tid means current thread. + * Return value: 0 for success, or -1 for error and set errno. + */ +extern int get_sched_policy(int tid, SchedPolicy *policy); + +/* Return a displayable string corresponding to policy. + * Return value: non-NULL NUL-terminated name of unspecified length; + * the caller is responsible for displaying the useful part of the string. + */ +extern const char *get_sched_policy_name(SchedPolicy policy); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_SCHED_POLICY_H */ diff --git a/media/libstagefright/system/core/include/log/event_tag_map.h b/media/libstagefright/system/core/include/log/event_tag_map.h new file mode 100644 index 000000000..1653c61e9 --- /dev/null +++ b/media/libstagefright/system/core/include/log/event_tag_map.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef _LIBS_CUTILS_EVENTTAGMAP_H +#define _LIBS_CUTILS_EVENTTAGMAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags" + +struct EventTagMap; +typedef struct EventTagMap EventTagMap; + +/* + * Open the specified file as an event log tag map. + * + * Returns NULL on failure. + */ +EventTagMap* android_openEventTagMap(const char* fileName); + +/* + * Close the map. + */ +void android_closeEventTagMap(EventTagMap* map); + +/* + * Look up a tag by index. Returns the tag string, or NULL if not found. + */ +const char* android_lookupEventTag(const EventTagMap* map, int tag); + +#ifdef __cplusplus +} +#endif + +#endif /*_LIBS_CUTILS_EVENTTAGMAP_H*/ diff --git a/media/libstagefright/system/core/include/log/log.h b/media/libstagefright/system/core/include/log/log.h new file mode 100644 index 000000000..6131f0147 --- /dev/null +++ b/media/libstagefright/system/core/include/log/log.h @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG +#define LOG_TAG NULL +#endif + +#ifndef _LIBS_LOG_LOG_H +#define _LIBS_LOG_LOG_H + +#include <stdio.h> +#include <time.h> +#include <sys/types.h> +#include <unistd.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <stdarg.h> + +#include <log/uio.h> +#include <log/logd.h> + +#ifdef _MSC_VER +#define __builtin_expect(X, Y) (X) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------- + +/* + * Normally we strip ALOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#ifndef LOG_NDEBUG +#ifdef NDEBUG +#define LOG_NDEBUG 1 +#else +#define LOG_NDEBUG 0 +#endif +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ +#ifndef ALOGV +#if LOG_NDEBUG +#define ALOGV(...) ((void)0) +#else +#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef ALOGV_IF +#if LOG_NDEBUG +#define ALOGV_IF(cond, ...) ((void)0) +#else +#define ALOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug log message using the current LOG_TAG. + */ +#ifndef ALOGD +#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGD_IF +#define ALOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info log message using the current LOG_TAG. + */ +#ifndef ALOGI +#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGI_IF +#define ALOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning log message using the current LOG_TAG. + */ +#ifndef ALOGW +#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGW_IF +#define ALOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error log message using the current LOG_TAG. + */ +#ifndef ALOGE +#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGE_IF +#define ALOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * verbose priority. + */ +#ifndef IF_ALOGV +#if LOG_NDEBUG +#define IF_ALOGV() if (false) +#else +#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) +#endif +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * debug priority. + */ +#ifndef IF_ALOGD +#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * info priority. + */ +#ifndef IF_ALOGI +#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * warn priority. + */ +#ifndef IF_ALOGW +#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * error priority. + */ +#ifndef IF_ALOGE +#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) +#endif + + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose system log message using the current LOG_TAG. + */ +#ifndef SLOGV +#if LOG_NDEBUG +#define SLOGV(...) ((void)0) +#else +#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef SLOGV_IF +#if LOG_NDEBUG +#define SLOGV_IF(cond, ...) ((void)0) +#else +#define SLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug system log message using the current LOG_TAG. + */ +#ifndef SLOGD +#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGD_IF +#define SLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info system log message using the current LOG_TAG. + */ +#ifndef SLOGI +#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGI_IF +#define SLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning system log message using the current LOG_TAG. + */ +#ifndef SLOGW +#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGW_IF +#define SLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error system log message using the current LOG_TAG. + */ +#ifndef SLOGE +#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGE_IF +#define SLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD +#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF +#define RLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI +#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF +#define RLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW +#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF +#define RLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE +#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF +#define RLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + + +// --------------------------------------------------------------------- + +/* + * Log a fatal error. If the given condition fails, this stops program + * execution like a normal assertion, but also generating the given message. + * It is NOT stripped from release builds. Note that the condition test + * is -inverted- from the normal assert() semantics. + */ +#ifndef LOG_ALWAYS_FATAL_IF +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ + : (void)0 ) +#endif + +#ifndef LOG_ALWAYS_FATAL +#define LOG_ALWAYS_FATAL(...) \ + ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) +#endif + +/* + * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that + * are stripped out of release builds. + */ +#if LOG_NDEBUG + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) ((void)0) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) ((void)0) +#endif + +#else + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) +#endif + +#endif + +/* + * Assertion that generates a log message when the assertion fails. + * Stripped out of release builds. Uses the current LOG_TAG. + */ +#ifndef ALOG_ASSERT +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) +//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif + +// --------------------------------------------------------------------- + +/* + * Basic log message macro. + * + * Example: + * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); + * + * The second argument may be NULL or "" to indicate the "global" tag. + */ +#ifndef ALOG +#define ALOG(priority, tag, ...) \ + LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to specify a number for the priority. + */ +#ifndef LOG_PRI +#define LOG_PRI(priority, tag, ...) \ + android_printLog(priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to pass in a varargs ("args" is a va_list). + */ +#ifndef LOG_PRI_VA +#define LOG_PRI_VA(priority, tag, fmt, args) \ + android_vprintLog(priority, NULL, tag, fmt, args) +#endif + +/* + * Conditional given a desired logging priority and tag. + */ +#ifndef IF_ALOG +#define IF_ALOG(priority, tag) \ + if (android_testLog(ANDROID_##priority, tag)) +#endif + +// --------------------------------------------------------------------- + +/* + * Event logging. + */ + +/* + * Event log entry types. These must match up with the declarations in + * java/android/android/util/EventLog.java. + */ +typedef enum { + EVENT_TYPE_INT = 0, + EVENT_TYPE_LONG = 1, + EVENT_TYPE_STRING = 2, + EVENT_TYPE_LIST = 3, +} AndroidEventLogType; + + +#ifndef LOG_EVENT_INT +#define LOG_EVENT_INT(_tag, _value) { \ + int intBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ + sizeof(intBuf)); \ + } +#endif +#ifndef LOG_EVENT_LONG +#define LOG_EVENT_LONG(_tag, _value) { \ + long long longBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ + sizeof(longBuf)); \ + } +#endif +#ifndef LOG_EVENT_STRING +#define LOG_EVENT_STRING(_tag, _value) \ + ((void) 0) /* not implemented -- must combine len with string */ +#endif +/* TODO: something for LIST */ + +/* + * =========================================================================== + * + * The stuff in the rest of this file should not be used directly. + */ + +#define android_printLog(prio, tag, ...) \ + __android_log_print(prio, tag, __VA_ARGS__) + +#define android_vprintLog(prio, cond, tag, ...) \ + __android_log_vprint(prio, tag, __VA_ARGS__) + +/* XXX Macros to work around syntax errors in places where format string + * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF + * (happens only in debug builds). + */ + +/* Returns 2nd arg. Used to substitute default value if caller's vararg list + * is empty. + */ +#define __android_second(dummy, second, ...) second + +/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise + * returns nothing. + */ +#define __android_rest(first, ...) , ## __VA_ARGS__ + +#define android_printAssert(cond, tag, ...) \ + __android_log_assert(cond, tag, \ + __android_second(0, ## __VA_ARGS__, NULL) __android_rest(__VA_ARGS__)) + +#define android_writeLog(prio, tag, text) \ + __android_log_write(prio, tag, text) + +#define android_bWriteLog(tag, payload, len) \ + __android_log_bwrite(tag, payload, len) +#define android_btWriteLog(tag, type, payload, len) \ + __android_log_btwrite(tag, type, payload, len) + +// TODO: remove these prototypes and their users +#define android_testLog(prio, tag) (1) +#define android_writevLog(vec,num) do{}while(0) +#define android_write1Log(str,len) do{}while (0) +#define android_setMinPriority(tag, prio) do{}while(0) +//#define android_logToCallback(func) do{}while(0) +#define android_logToFile(tag, file) (0) +#define android_logToFd(tag, fd) (0) + +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + + LOG_ID_MAX +} log_id_t; + +/* + * Send a simple string to the log. + */ +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); + + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_CUTILS_LOG_H diff --git a/media/libstagefright/system/core/include/log/logd.h b/media/libstagefright/system/core/include/log/logd.h new file mode 100644 index 000000000..379c37334 --- /dev/null +++ b/media/libstagefright/system/core/include/log/logd.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef _ANDROID_CUTILS_LOGD_H +#define _ANDROID_CUTILS_LOGD_H + +/* the stable/frozen log-related definitions have been + * moved to this header, which is exposed by the NDK + */ +#include <android/log.h> + +/* the rest is only used internally by the system */ +#include <time.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/types.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <log/uio.h> +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len); +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _LOGD_H */ diff --git a/media/libstagefright/system/core/include/log/logger.h b/media/libstagefright/system/core/include/log/logger.h new file mode 100644 index 000000000..04f3fb05b --- /dev/null +++ b/media/libstagefright/system/core/include/log/logger.h @@ -0,0 +1,81 @@ +/* utils/logger.h +** +** Copyright 2007, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGGER_H +#define _UTILS_LOGGER_H + +#include <stdint.h> + +/* + * The userspace structure for version 1 of the logger_entry ABI. + * This structure is returned to userspace by the kernel logger + * driver unless an upgrade to a newer ABI version is requested. + */ +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t __pad; /* no matter what, we get 2 bytes of padding */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +/* + * The userspace structure for version 2 of the logger_entry ABI. + * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) + * is called with version==2 + */ +struct logger_entry_v2 { + uint16_t len; /* length of the payload */ + uint16_t hdr_size; /* sizeof(struct logger_entry_v2) */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + uint32_t euid; /* effective UID of logger */ + char msg[0]; /* the entry's payload */ +}; + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +/* + * The maximum size of the log entry payload that can be + * written to the kernel logger driver. An attempt to write + * more than this amount to /dev/log/* will result in a + * truncated log entry. + */ +#define LOGGER_ENTRY_MAX_PAYLOAD 4076 + +/* + * The maximum size of a log entry which can be read from the + * kernel logger driver. An attempt to read less than this amount + * may result in read() returning EINVAL. + */ +#define LOGGER_ENTRY_MAX_LEN (5*1024) + +#ifdef HAVE_IOCTL + +#include <sys/ioctl.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +#endif // HAVE_IOCTL + +#endif /* _UTILS_LOGGER_H */ diff --git a/media/libstagefright/system/core/include/log/logprint.h b/media/libstagefright/system/core/include/log/logprint.h new file mode 100644 index 000000000..9b57e0e78 --- /dev/null +++ b/media/libstagefright/system/core/include/log/logprint.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2006 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. + */ + +#ifndef _LOGPRINT_H +#define _LOGPRINT_H + +#include <log/log.h> +#include <log/logger.h> +#include <log/event_tag_map.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG, +} AndroidLogPrintFormat; + +typedef struct AndroidLogFormat_t AndroidLogFormat; + +typedef struct AndroidLogEntry_t { + time_t tv_sec; + long tv_nsec; + android_LogPriority priority; + int32_t pid; + int32_t tid; + const char * tag; + size_t messageLen; + const char * message; +} AndroidLogEntry; + +AndroidLogFormat *android_log_format_new(); + +void android_log_format_free(AndroidLogFormat *p_format); + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format); + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char *s); + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterRule(AndroidLogFormat *p_format, + const char *filterExpression); + + +/** + * filterString: a whitespace-separated set of filter expressions + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterString(AndroidLogFormat *p_format, + const char *filterString); + + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int android_log_shouldPrintLine ( + AndroidLogFormat *p_format, const char *tag, android_LogPriority pri); + + +/** + * Splits a wire-format buffer into an AndroidLogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int android_log_processLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry); + +/** + * Like android_log_processLogBuffer, but for binary logs. + * + * If "map" is non-NULL, it will be used to convert the log tag number + * into a string. + */ +int android_log_processBinaryLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, + int messageBufLen); + + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *android_log_formatLogLine ( + AndroidLogFormat *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const AndroidLogEntry *p_line, + size_t *p_outLength); + + +/** + * Either print or do not print log line, based on filter + * + * Assumes single threaded execution + * + */ +int android_log_printLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_LOGPRINT_H*/ diff --git a/media/libstagefright/system/core/include/log/uio.h b/media/libstagefright/system/core/include/log/uio.h new file mode 100644 index 000000000..01a74d26f --- /dev/null +++ b/media/libstagefright/system/core/include/log/uio.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 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. + */ + +// +// implementation of sys/uio.h for platforms that don't have it (Win32) +// +#ifndef _LIBS_CUTILS_UIO_H +#define _LIBS_CUTILS_UIO_H + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#else + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> + +struct iovec { + const void* iov_base; + size_t iov_len; +}; + +extern int readv( int fd, struct iovec* vecs, int count ); +extern int writev( int fd, const struct iovec* vecs, int count ); + +#ifdef __cplusplus +} +#endif + +#endif /* !HAVE_SYS_UIO_H */ + +#endif /* _LIBS_UTILS_UIO_H */ + diff --git a/media/libstagefright/system/core/include/system/graphics.h b/media/libstagefright/system/core/include/system/graphics.h new file mode 100644 index 000000000..be86ae42b --- /dev/null +++ b/media/libstagefright/system/core/include/system/graphics.h @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H +#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * If the HAL needs to create service threads to handle graphics related + * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority + * if they can block the main rendering thread in any way. + * + * the priority of the current thread can be set with: + * + * #include <sys/resource.h> + * setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + * + */ + +#define HAL_PRIORITY_URGENT_DISPLAY (-8) + +/** + * pixel format definitions + */ + +enum { + /* + * "linear" color pixel formats: + * + * The pixel formats below contain sRGB data but are otherwise treated + * as linear formats, i.e.: no special operation is performed when + * reading or writing into a buffer in one of these formats + */ + HAL_PIXEL_FORMAT_RGBA_8888 = 1, + HAL_PIXEL_FORMAT_RGBX_8888 = 2, + HAL_PIXEL_FORMAT_RGB_888 = 3, + HAL_PIXEL_FORMAT_RGB_565 = 4, + HAL_PIXEL_FORMAT_BGRA_8888 = 5, + + /* + * sRGB color pixel formats: + * + * The red, green and blue components are stored in sRGB space, and converted + * to linear space when read, using the standard sRGB to linear equation: + * + * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045 + * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045 + * + * When written the inverse transformation is performed: + * + * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308 + * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308 + * + * + * The alpha component, if present, is always stored in linear space and + * is left unmodified when read or written. + * + */ + HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC, + HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD, + + /* + * 0x100 - 0x1FF + * + * This range is reserved for pixel formats that are specific to the HAL + * implementation. Implementations can use any value in this range to + * communicate video pixel formats between their HAL modules. These formats + * must not have an alpha channel. Additionally, an EGLimage created from a + * gralloc buffer of one of these formats must be supported for use with the + * GL_OES_EGL_image_external OpenGL ES extension. + */ + + /* + * Android YUV format: + * + * This format is exposed outside of the HAL to software decoders and + * applications. EGLImageKHR must support it in conjunction with the + * OES_EGL_image_external extension. + * + * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed + * by (W/2) x (H/2) Cr and Cb planes. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * + * y_size = stride * height + * c_stride = ALIGN(stride/2, 16) + * c_size = c_stride * height/2 + * size = y_size + c_size * 2 + * cr_offset = y_size + * cb_offset = y_size + c_size + * + */ + HAL_PIXEL_FORMAT_YV12 = 0x32315659, // YCrCb 4:2:0 Planar + + + /* + * Android Y8 format: + * + * This format is exposed outside of the HAL to the framework. + * The expected gralloc usage flags are SW_* and HW_CAMERA_*, + * and no other HW_ flags will be used. + * + * Y8 is a YUV planar format comprised of a WxH Y plane, + * with each pixel being represented by 8 bits. + * + * It is equivalent to just the Y plane from YV12. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * + * size = stride * height + * + */ + HAL_PIXEL_FORMAT_Y8 = 0x20203859, + + /* + * Android Y16 format: + * + * This format is exposed outside of the HAL to the framework. + * The expected gralloc usage flags are SW_* and HW_CAMERA_*, + * and no other HW_ flags will be used. + * + * Y16 is a YUV planar format comprised of a WxH Y plane, + * with each pixel being represented by 16 bits. + * + * It is just like Y8, but has double the bits per pixel (little endian). + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels + * - a vertical stride equal to the height + * - strides are specified in pixels, not in bytes + * + * size = stride * height * 2 + * + */ + HAL_PIXEL_FORMAT_Y16 = 0x20363159, + + /* + * Android RAW sensor format: + * + * This format is exposed outside of the HAL to applications. + * + * RAW_SENSOR is a single-channel 16-bit format, typically representing raw + * Bayer-pattern images from an image sensor, with minimal processing. + * + * The exact pixel layout of the data in the buffer is sensor-dependent, and + * needs to be queried from the camera device. + * + * Generally, not all 16 bits are used; more common values are 10 or 12 + * bits. All parameters to interpret the raw data (black and white points, + * color space, etc) must be queried from the camera device. + * + * This format assumes + * - an even width + * - an even height + * - a horizontal stride multiple of 16 pixels (32 bytes). + */ + HAL_PIXEL_FORMAT_RAW_SENSOR = 0x20, + + /* + * Android binary blob graphics buffer format: + * + * This format is used to carry task-specific data which does not have a + * standard image structure. The details of the format are left to the two + * endpoints. + * + * A typical use case is for transporting JPEG-compressed images from the + * Camera HAL to the framework or to applications. + * + * Buffers of this format must have a height of 1, and width equal to their + * size in bytes. + */ + HAL_PIXEL_FORMAT_BLOB = 0x21, + + /* + * Android format indicating that the choice of format is entirely up to the + * device-specific Gralloc implementation. + * + * The Gralloc implementation should examine the usage bits passed in when + * allocating a buffer with this format, and it should derive the pixel + * format from those usage flags. This format will never be used with any + * of the GRALLOC_USAGE_SW_* usage flags. + * + * If a buffer of this format is to be used as an OpenGL ES texture, the + * framework will assume that sampling the texture will always return an + * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values). + * + */ + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22, + + /* + * Android flexible YCbCr formats + * + * This format allows platforms to use an efficient YCbCr/YCrCb buffer + * layout, while still describing the buffer layout in a way accessible to + * the CPU in a device-independent manner. While called YCbCr, it can be + * used to describe formats with either chromatic ordering, as well as + * whole planar or semiplanar layouts. + * + * struct android_ycbcr (below) is the the struct used to describe it. + * + * This format must be accepted by the gralloc module when + * USAGE_HW_CAMERA_WRITE and USAGE_SW_READ_* are set. + * + * This format is locked for use by gralloc's (*lock_ycbcr) method, and + * locking with the (*lock) method will return an error. + */ + HAL_PIXEL_FORMAT_YCbCr_420_888 = 0x23, + + /* Legacy formats (deprecated), used by ImageFormat.java */ + HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x10, // NV16 + HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x11, // NV21 + HAL_PIXEL_FORMAT_YCbCr_422_I = 0x14, // YUY2 +}; + +/* + * Structure for describing YCbCr formats for consumption by applications. + * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888. + * + * Buffer chroma subsampling is defined in the format. + * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0. + * + * Buffers must have a 8 bit depth. + * + * @y, @cb, and @cr point to the first byte of their respective planes. + * + * Stride describes the distance in bytes from the first value of one row of + * the image to the first value of the next row. It includes the width of the + * image plus padding. + * @ystride is the stride of the luma plane. + * @cstride is the stride of the chroma planes. + * + * @chroma_step is the distance in bytes from one chroma pixel value to the + * next. This is 2 bytes for semiplanar (because chroma values are interleaved + * and each chroma value is one byte) and 1 for planar. + */ + +struct android_ycbcr { + void *y; + void *cb; + void *cr; + size_t ystride; + size_t cstride; + size_t chroma_step; + + /** reserved for future use, set to 0 by gralloc's (*lock_ycbcr)() */ + uint32_t reserved[8]; +}; + +/** + * Transformation definitions + * + * IMPORTANT NOTE: + * HAL_TRANSFORM_ROT_90 is applied CLOCKWISE and AFTER HAL_TRANSFORM_FLIP_{H|V}. + * + */ + +enum { + /* flip source image horizontally (around the vertical axis) */ + HAL_TRANSFORM_FLIP_H = 0x01, + /* flip source image vertically (around the horizontal axis)*/ + HAL_TRANSFORM_FLIP_V = 0x02, + /* rotate source image 90 degrees clockwise */ + HAL_TRANSFORM_ROT_90 = 0x04, + /* rotate source image 180 degrees */ + HAL_TRANSFORM_ROT_180 = 0x03, + /* rotate source image 270 degrees clockwise */ + HAL_TRANSFORM_ROT_270 = 0x07, + /* don't use. see system/window.h */ + HAL_TRANSFORM_RESERVED = 0x08, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */ diff --git a/media/libstagefright/system/core/include/sysutils/List.h b/media/libstagefright/system/core/include/sysutils/List.h new file mode 100644 index 000000000..72a9c4460 --- /dev/null +++ b/media/libstagefright/system/core/include/sysutils/List.h @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". +// +#ifndef _SYSUTILS_LIST_H +#define _SYSUTILS_LIST_H + +#include <stddef.h> +#include <stdint.h> + +namespace stagefright { +namespace sysutils { + +/* + * Doubly-linked list. Instantiate with "List<MyClass> myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template<typename T> +class List +{ +protected: + /* + * One element in the list. + */ + class _Node { + public: + explicit _Node(const T& val) : mVal(val) {} + ~_Node() {} + inline T& getRef() { return mVal; } + inline const T& getRef() const { return mVal; } + inline _Node* getPrev() const { return mpPrev; } + inline _Node* getNext() const { return mpNext; } + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } + private: + friend class List; + friend class _ListIterator; + T mVal; + _Node* mpPrev; + _Node* mpNext; + }; + + /* + * Iterator for walking through the list. + */ + + template <typename TYPE> + struct CONST_ITERATOR { + typedef _Node const * NodePtr; + typedef const TYPE Type; + }; + + template <typename TYPE> + struct NON_CONST_ITERATOR { + typedef _Node* NodePtr; + typedef TYPE Type; + }; + + template< + typename U, + template <class> class Constness + > + class _ListIterator { + typedef _ListIterator<U, Constness> _Iter; + typedef typename Constness<U>::NodePtr _NodePtr; + typedef typename Constness<U>::Type _Type; + + explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} + + public: + _ListIterator() {} + _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} + ~_ListIterator() {} + + // this will handle conversions from iterator to const_iterator + // (and also all convertible iterators) + // Here, in this implementation, the iterators can be converted + // if the nodes can be converted + template<typename V> explicit + _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} + + + /* + * Dereference operator. Used to get at the juicy insides. + */ + _Type& operator*() const { return mpNode->getRef(); } + _Type* operator->() const { return &(mpNode->getRef()); } + + /* + * Iterator comparison. + */ + inline bool operator==(const _Iter& right) const { + return mpNode == right.mpNode; } + + inline bool operator!=(const _Iter& right) const { + return mpNode != right.mpNode; } + + /* + * handle comparisons between iterator and const_iterator + */ + template<typename OTHER> + inline bool operator==(const OTHER& right) const { + return mpNode == right.mpNode; } + + template<typename OTHER> + inline bool operator!=(const OTHER& right) const { + return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + inline _Iter& operator++() { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + const _Iter operator++(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getNext(); + return tmp; + } + inline _Iter& operator--() { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + const _Iter operator--(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getPrev(); + return tmp; + } + + inline _NodePtr getNode() const { return mpNode; } + + _NodePtr mpNode; /* should be private, but older gcc fails */ + private: + friend class List; + }; + +public: + List() { + prep(); + } + List(const List<T>& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List() { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator<T, NON_CONST_ITERATOR> iterator; + typedef _ListIterator<T, CONST_ITERATOR> const_iterator; + + List<T>& operator=(const List<T>& right); + + /* returns true if the list is empty */ + inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + size_t size() const { + return size_t(distance(begin(), end())); + } + + /* + * Return the first element or one past the last element. The + * _Node* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + inline iterator begin() { + return iterator(mpMiddle->getNext()); + } + inline const_iterator begin() const { + return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); + } + inline iterator end() { + return iterator(mpMiddle); + } + inline const_iterator end() const { + return const_iterator(const_cast<_Node const*>(mpMiddle)); + } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) + { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return iterator(newNode); + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return iterator(pNext); + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return iterator(last); + } + + /* remove all contents of the list */ + void clear() { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * FIXME: This is actually a generic iterator function. It should be a + * template function at the top-level with specializations for things like + * vector<>, which can just do pointer math). Here we limit it to + * _ListIterator of the same type but different constness. + */ + template< + typename U, + template <class> class CL, + template <class> class CR + > + ptrdiff_t distance( + _ListIterator<U, CL> first, _ListIterator<U, CR> last) const + { + ptrdiff_t count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _Node but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep() { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template<class T> +List<T>& List<T>::operator=(const List<T>& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace sysutils +}; // namespace stagefright + +#endif // _SYSUTILS_LIST_H diff --git a/media/libstagefright/system/core/include/utils/Atomic.h b/media/libstagefright/system/core/include/utils/Atomic.h new file mode 100644 index 000000000..7eb476c94 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Atomic.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_UTILS_ATOMIC_H +#define ANDROID_UTILS_ATOMIC_H + +#include <cutils/atomic.h> + +#endif // ANDROID_UTILS_ATOMIC_H diff --git a/media/libstagefright/system/core/include/utils/CallStack.h b/media/libstagefright/system/core/include/utils/CallStack.h new file mode 100644 index 000000000..eb52c8b36 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/CallStack.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_CALLSTACK_H +#define ANDROID_CALLSTACK_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/String8.h> +#include <corkscrew/backtrace.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +class CallStack +{ +public: + enum { + MAX_DEPTH = 31 + }; + + CallStack(); + CallStack(const char* logtag, int32_t ignoreDepth=1, + int32_t maxDepth=MAX_DEPTH); + CallStack(const CallStack& rhs); + ~CallStack(); + + CallStack& operator = (const CallStack& rhs); + + bool operator == (const CallStack& rhs) const; + bool operator != (const CallStack& rhs) const; + bool operator < (const CallStack& rhs) const; + bool operator >= (const CallStack& rhs) const; + bool operator > (const CallStack& rhs) const; + bool operator <= (const CallStack& rhs) const; + + const void* operator [] (int index) const; + + void clear(); + + void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH); + + // Dump a stack trace to the log using the supplied logtag + void dump(const char* logtag, const char* prefix = 0) const; + + // Return a string (possibly very long) containing the complete stack trace + String8 toString(const char* prefix = 0) const; + + size_t size() const { return mCount; } + +private: + size_t mCount; + backtrace_frame_t mStack[MAX_DEPTH]; +}; + +}; // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_CALLSTACK_H diff --git a/media/libstagefright/system/core/include/utils/Condition.h b/media/libstagefright/system/core/include/utils/Condition.h new file mode 100644 index 000000000..e8e7ae976 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Condition.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef _LIBS_UTILS_CONDITION_H +#define _LIBS_UTILS_CONDITION_H + +#include <stdint.h> +#include <sys/types.h> +#include <time.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +#endif + +#include <utils/Errors.h> +#include <utils/Mutex.h> +#include <utils/Timers.h> + +// --------------------------------------------------------------------------- +namespace stagefright { +// --------------------------------------------------------------------------- + +/* + * Condition variable class. The implementation is system-dependent. + * + * Condition variables are paired up with mutexes. Lock the mutex, + * call wait(), then either re-wait() if things aren't quite what you want, + * or unlock the mutex and continue. All threads calling wait() must + * use the same mutex for a given Condition. + */ +class Condition { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + enum WakeUpType { + WAKE_UP_ONE = 0, + WAKE_UP_ALL = 1 + }; + + Condition(); + Condition(int type); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // same with relative timeout + status_t waitRelative(Mutex& mutex, nsecs_t reltime); + // Signal the condition variable, allowing one thread to continue. + void signal(); + // Signal the condition variable, allowing one or all threads to continue. + void signal(WakeUpType type) { + if (type == WAKE_UP_ONE) { + signal(); + } else { + broadcast(); + } + } + // Signal the condition variable, allowing all threads to continue. + void broadcast(); + +private: +#if defined(HAVE_PTHREADS) + pthread_cond_t mCond; +#else + void* mState; +#endif +}; + +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +inline Condition::Condition() { + pthread_cond_init(&mCond, NULL); +} +inline Condition::Condition(int type) { + if (type == SHARED) { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_cond_init(&mCond, &attr); + pthread_condattr_destroy(&attr); + } else { + pthread_cond_init(&mCond, NULL); + } +} +inline Condition::~Condition() { + pthread_cond_destroy(&mCond); +} +inline status_t Condition::wait(Mutex& mutex) { + return -pthread_cond_wait(&mCond, &mutex.mMutex); +} +inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { +#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) + struct timespec ts; + ts.tv_sec = reltime/1000000000; + ts.tv_nsec = reltime%1000000000; + return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts); +#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + struct timespec ts; +#if defined(HAVE_POSIX_CLOCKS) + clock_gettime(CLOCK_REALTIME, &ts); +#else // HAVE_POSIX_CLOCKS + // we don't support the clocks here. + struct timeval t; + gettimeofday(&t, NULL); + ts.tv_sec = t.tv_sec; + ts.tv_nsec= t.tv_usec*1000; +#endif // HAVE_POSIX_CLOCKS + ts.tv_sec += reltime/1000000000; + ts.tv_nsec+= reltime%1000000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts); +#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE +} +inline void Condition::signal() { + pthread_cond_signal(&mCond); +} +inline void Condition::broadcast() { + pthread_cond_broadcast(&mCond); +} + +#else + +inline Condition::Condition() {} +inline Condition::Condition(int type) {} +inline Condition::~Condition() {} +inline status_t Condition::wait(Mutex& mutex) { return OK; } +inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { + return OK; +} +inline void Condition::signal() {} +inline void Condition::broadcast() {} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- +}; // namespace stagefright +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_CONDITON_H diff --git a/media/libstagefright/system/core/include/utils/Debug.h b/media/libstagefright/system/core/include/utils/Debug.h new file mode 100644 index 000000000..41f09ebb7 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Debug.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_UTILS_DEBUG_H +#define ANDROID_UTILS_DEBUG_H + +#include <stdint.h> +#include <sys/types.h> + +namespace stagefright { +// --------------------------------------------------------------------------- + +#ifdef __cplusplus +template<bool> struct CompileTimeAssert; +template<> struct CompileTimeAssert<true> {}; +#define COMPILE_TIME_ASSERT(_exp) \ + template class CompileTimeAssert< (_exp) >; +#endif +#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \ + CompileTimeAssert<( _exp )>(); + +// --------------------------------------------------------------------------- + +#ifdef __cplusplus +template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse; +template<typename LHS, typename RHS> +struct CompileTimeIfElse<true, LHS, RHS> { typedef LHS TYPE; }; +template<typename LHS, typename RHS> +struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; }; +#endif + +// --------------------------------------------------------------------------- +}; // namespace stagefright + +#endif // ANDROID_UTILS_DEBUG_H diff --git a/media/libstagefright/system/core/include/utils/Errors.h b/media/libstagefright/system/core/include/utils/Errors.h new file mode 100644 index 000000000..0ceda595a --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Errors.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef ANDROID_ERRORS_H +#define ANDROID_ERRORS_H + +#include <sys/types.h> +#include <errno.h> + +namespace stagefright { + +// use this type to return error codes +#ifdef HAVE_MS_C_RUNTIME +typedef int status_t; +#else +typedef int32_t status_t; +#endif + +/* the MS C runtime lacks a few error codes */ + +/* + * Error codes. + * All error codes are negative values. + */ + +// Win32 #defines NO_ERROR as well. It has the same value, so there's no +// real conflict, though it's a bit awkward. +#ifdef _WIN32 +# undef NO_ERROR +#endif + +enum { + OK = 0, // Everything's swell. + NO_ERROR = 0, // No errors. + + UNKNOWN_ERROR = 0x80000000, + + NO_MEMORY = -ENOMEM, + INVALID_OPERATION = -ENOSYS, + BAD_VALUE = -EINVAL, + BAD_TYPE = 0x80000001, + NAME_NOT_FOUND = -ENOENT, + PERMISSION_DENIED = -EPERM, + NO_INIT = -ENODEV, + ALREADY_EXISTS = -EEXIST, + DEAD_OBJECT = -EPIPE, + FAILED_TRANSACTION = 0x80000002, + JPARKS_BROKE_IT = -EPIPE, +#if !defined(HAVE_MS_C_RUNTIME) + BAD_INDEX = -EOVERFLOW, + NOT_ENOUGH_DATA = -ENODATA, + WOULD_BLOCK = -EWOULDBLOCK, + TIMED_OUT = -ETIMEDOUT, + UNKNOWN_TRANSACTION = -EBADMSG, +#else + BAD_INDEX = -E2BIG, + NOT_ENOUGH_DATA = 0x80000003, + WOULD_BLOCK = 0x80000004, + TIMED_OUT = 0x80000005, + UNKNOWN_TRANSACTION = 0x80000006, +#endif + FDS_NOT_ALLOWED = 0x80000007, +}; + +// Restore define; enumeration is in "android" namespace, so the value defined +// there won't work for Win32 code in a different namespace. +#ifdef _WIN32 +# define NO_ERROR 0L +#endif + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_ERRORS_H diff --git a/media/libstagefright/system/core/include/utils/KeyedVector.h b/media/libstagefright/system/core/include/utils/KeyedVector.h new file mode 100644 index 000000000..ca4bfd68a --- /dev/null +++ b/media/libstagefright/system/core/include/utils/KeyedVector.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_KEYED_VECTOR_H +#define ANDROID_KEYED_VECTOR_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include <utils/SortedVector.h> +#include <utils/TypeHelpers.h> +#include <utils/Errors.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +template <typename KEY, typename VALUE> +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns whether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! sets the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + // returns true if the arguments is known to be identical to this vector + inline bool isIdenticalTo(const KeyedVector& rhs) const; + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + const VALUE& operator[] (size_t index) const; + + /*! + * modifying the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t<KEY, VALUE> > mVector; +}; + +// KeyedVector<KEY, VALUE> can be trivially moved using memcpy() because its +// underlying SortedVector can be trivially moved. +template<typename KEY, typename VALUE> struct trait_trivial_move<KeyedVector<KEY, VALUE> > { + enum { value = trait_trivial_move<SortedVector< key_value_pair_t<KEY, VALUE> > >::value }; +}; + + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template <typename KEY, typename VALUE> +class DefaultKeyedVector : public KeyedVector<KEY, VALUE> +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +KeyedVector<KEY,VALUE>::KeyedVector() +{ +} + +template<typename KEY, typename VALUE> inline +bool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const { + return mVector.array() == rhs.mVector.array(); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) ); +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = this->indexOfKey(key); + LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); + return mVector.itemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const { + return valueAt(index); +} + +template<typename KEY, typename VALUE> inline +const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) { + ssize_t i = this->indexOfKey(key); + LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); + return mVector.editItemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) ); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t<KEY,VALUE> pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) { + if (index<size()) { + mVector.editItemAt(index).value = item; + return index; + } + return BAD_INDEX; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t<KEY,VALUE>(key)); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template<typename KEY, typename VALUE> inline +const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = this->indexOfKey(key); + return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; +} + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/media/libstagefright/system/core/include/utils/List.h b/media/libstagefright/system/core/include/utils/List.h new file mode 100644 index 000000000..f5c110e0f --- /dev/null +++ b/media/libstagefright/system/core/include/utils/List.h @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +#include <stddef.h> +#include <stdint.h> + +namespace stagefright { + +/* + * Doubly-linked list. Instantiate with "List<MyClass> myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template<typename T> +class List +{ +protected: + /* + * One element in the list. + */ + class _Node { + public: + explicit _Node(const T& val) : mVal(val) {} + ~_Node() {} + inline T& getRef() { return mVal; } + inline const T& getRef() const { return mVal; } + inline _Node* getPrev() const { return mpPrev; } + inline _Node* getNext() const { return mpNext; } + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } +#ifndef _MSC_VER + private: + friend class List; + friend class _ListIterator; +#endif + T mVal; + _Node* mpPrev; + _Node* mpNext; + }; + + /* + * Iterator for walking through the list. + */ + + template <typename TYPE> + struct CONST_ITERATOR { + typedef _Node const * NodePtr; + typedef const TYPE Type; + }; + + template <typename TYPE> + struct NON_CONST_ITERATOR { + typedef _Node* NodePtr; + typedef TYPE Type; + }; + + template< + typename U, + template <class> class Constness + > + class _ListIterator { + typedef _ListIterator<U, Constness> _Iter; + typedef typename Constness<U>::NodePtr _NodePtr; + typedef typename Constness<U>::Type _Type; + + explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} + + public: + _ListIterator() {} + _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} + ~_ListIterator() {} + + // this will handle conversions from iterator to const_iterator + // (and also all convertible iterators) + // Here, in this implementation, the iterators can be converted + // if the nodes can be converted + template<typename V> explicit + _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} + + + /* + * Dereference operator. Used to get at the juicy insides. + */ + _Type& operator*() const { return mpNode->getRef(); } + _Type* operator->() const { return &(mpNode->getRef()); } + + /* + * Iterator comparison. + */ + inline bool operator==(const _Iter& right) const { + return mpNode == right.mpNode; } + + inline bool operator!=(const _Iter& right) const { + return mpNode != right.mpNode; } + + /* + * handle comparisons between iterator and const_iterator + */ + template<typename OTHER> + inline bool operator==(const OTHER& right) const { + return mpNode == right.mpNode; } + + template<typename OTHER> + inline bool operator!=(const OTHER& right) const { + return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + inline _Iter& operator++() { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + const _Iter operator++(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getNext(); + return tmp; + } + inline _Iter& operator--() { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + const _Iter operator--(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getPrev(); + return tmp; + } + + inline _NodePtr getNode() const { return mpNode; } + + _NodePtr mpNode; /* should be private, but older gcc fails */ + private: + friend class List; + }; + +public: + List() { + prep(); + } + List(const List<T>& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List() { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator<T, NON_CONST_ITERATOR> iterator; + typedef _ListIterator<T, CONST_ITERATOR> const_iterator; + + List<T>& operator=(const List<T>& right); + + /* returns true if the list is empty */ + inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + size_t size() const { + return size_t(distance(begin(), end())); + } + + /* + * Return the first element or one past the last element. The + * _Node* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + inline iterator begin() { + return iterator(mpMiddle->getNext()); + } + inline const_iterator begin() const { + return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); + } + inline iterator end() { + return iterator(mpMiddle); + } + inline const_iterator end() const { + return const_iterator(const_cast<_Node const*>(mpMiddle)); + } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) + { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return iterator(newNode); + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return iterator(pNext); + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return iterator(last); + } + + /* remove all contents of the list */ + void clear() { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * FIXME: This is actually a generic iterator function. It should be a + * template function at the top-level with specializations for things like + * vector<>, which can just do pointer math). Here we limit it to + * _ListIterator of the same type but different constness. + */ + template< + typename U, + template <class> class CL, + template <class> class CR + > + ptrdiff_t distance( + _ListIterator<U, CL> first, _ListIterator<U, CR> last) const + { + ptrdiff_t count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _Node but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep() { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template<class T> +List<T>& List<T>::operator=(const List<T>& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace stagefright + +#endif // _LIBS_UTILS_LIST_H diff --git a/media/libstagefright/system/core/include/utils/Log.h b/media/libstagefright/system/core/include/utils/Log.h new file mode 100644 index 000000000..97cc4f3cf --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Log.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_UTILS_LOG_H +#define _LIBS_UTILS_LOG_H + +#include <cutils/log.h> +#include <sys/types.h> + +#ifdef __cplusplus + +namespace stagefright { + +/* + * A very simple utility that yells in the log when an operation takes too long. + */ +class LogIfSlow { +public: + LogIfSlow(const char* tag, android_LogPriority priority, + int timeoutMillis, const char* message); + ~LogIfSlow(); + +private: + const char* const mTag; + const android_LogPriority mPriority; + const int mTimeoutMillis; + const char* const mMessage; + const int64_t mStart; +}; + +/* + * Writes the specified debug log message if this block takes longer than the + * specified number of milliseconds to run. Includes the time actually taken. + * + * { + * ALOGD_IF_SLOW(50, "Excessive delay doing something."); + * doSomething(); + * } + */ +#define ALOGD_IF_SLOW(timeoutMillis, message) \ + stagefright::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); + +} // namespace stagefright + +#endif // __cplusplus + +#endif // _LIBS_UTILS_LOG_H diff --git a/media/libstagefright/system/core/include/utils/Mutex.h b/media/libstagefright/system/core/include/utils/Mutex.h new file mode 100644 index 000000000..b33efef76 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Mutex.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef _LIBS_UTILS_MUTEX_H +#define _LIBS_UTILS_MUTEX_H + +#include <stdint.h> +#include <sys/types.h> +#include <time.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +#endif + +#include <utils/Errors.h> + +// --------------------------------------------------------------------------- +namespace stagefright { +// --------------------------------------------------------------------------- + +class Condition; + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class Mutex { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + Mutex(); + Mutex(const char* name); + Mutex(int type, const char* name = NULL); + ~Mutex(); + + // lock or unlock the mutex + status_t lock(); + void unlock(); + + // lock if possible; returns 0 on success, error otherwise + status_t tryLock(); + + // Manages the mutex automatically. It'll be locked when Autolock is + // constructed and released when Autolock goes out of scope. + class Autolock { + public: + inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } + inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } + inline ~Autolock() { mLock.unlock(); } + private: + Mutex& mLock; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + +#if defined(HAVE_PTHREADS) + pthread_mutex_t mMutex; +#else + void _init(); + void* mState; +#endif +}; + +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +inline Mutex::Mutex() { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::Mutex(__attribute__((unused)) const char* name) { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) { + if (type == SHARED) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&mMutex, &attr); + pthread_mutexattr_destroy(&attr); + } else { + pthread_mutex_init(&mMutex, NULL); + } +} +inline Mutex::~Mutex() { + pthread_mutex_destroy(&mMutex); +} +inline status_t Mutex::lock() { + return -pthread_mutex_lock(&mMutex); +} +inline void Mutex::unlock() { + pthread_mutex_unlock(&mMutex); +} +inline status_t Mutex::tryLock() { + return -pthread_mutex_trylock(&mMutex); +} + +#else + +inline Mutex::Mutex() {} +inline Mutex::Mutex(const char* name) {} +inline Mutex::Mutex(int type, const char* name) {} +inline Mutex::~Mutex() {} +inline status_t Mutex::lock() { return OK; } +inline void Mutex::unlock() {} +inline status_t Mutex::tryLock() { return OK; } +inline void Mutex::_init() {} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- + +/* + * Automatic mutex. Declare one of these at the top of a function. + * When the function returns, it will go out of scope, and release the + * mutex. + */ + +typedef Mutex::Autolock AutoMutex; + +// --------------------------------------------------------------------------- +}; // namespace stagefright +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_MUTEX_H diff --git a/media/libstagefright/system/core/include/utils/RWLock.h b/media/libstagefright/system/core/include/utils/RWLock.h new file mode 100644 index 000000000..4c438272f --- /dev/null +++ b/media/libstagefright/system/core/include/utils/RWLock.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 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. + */ + +#ifndef _LIBS_UTILS_RWLOCK_H +#define _LIBS_UTILS_RWLOCK_H + +#include <stdint.h> +#include <sys/types.h> + +#if defined(HAVE_PTHREADS) +# include <pthread.h> +#endif + +#include <utils/Errors.h> +#include <utils/ThreadDefs.h> + +// --------------------------------------------------------------------------- +namespace stagefright { +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class RWLock { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + RWLock(); + RWLock(const char* name); + RWLock(int type, const char* name = NULL); + ~RWLock(); + + status_t readLock(); + status_t tryReadLock(); + status_t writeLock(); + status_t tryWriteLock(); + void unlock(); + + class AutoRLock { + public: + inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } + inline ~AutoRLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + + class AutoWLock { + public: + inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } + inline ~AutoWLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + +private: + // A RWLock cannot be copied + RWLock(const RWLock&); + RWLock& operator = (const RWLock&); + + pthread_rwlock_t mRWLock; +}; + +inline RWLock::RWLock() { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(__attribute__((unused)) const char* name) { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) { + if (type == SHARED) { + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(&mRWLock, &attr); + pthread_rwlockattr_destroy(&attr); + } else { + pthread_rwlock_init(&mRWLock, NULL); + } +} +inline RWLock::~RWLock() { + pthread_rwlock_destroy(&mRWLock); +} +inline status_t RWLock::readLock() { + return -pthread_rwlock_rdlock(&mRWLock); +} +inline status_t RWLock::tryReadLock() { + return -pthread_rwlock_tryrdlock(&mRWLock); +} +inline status_t RWLock::writeLock() { + return -pthread_rwlock_wrlock(&mRWLock); +} +inline status_t RWLock::tryWriteLock() { + return -pthread_rwlock_trywrlock(&mRWLock); +} +inline void RWLock::unlock() { + pthread_rwlock_unlock(&mRWLock); +} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- +}; // namespace stagefright +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_RWLOCK_H diff --git a/media/libstagefright/system/core/include/utils/RefBase.h b/media/libstagefright/system/core/include/utils/RefBase.h new file mode 100644 index 000000000..e1f97c9e9 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/RefBase.h @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_REF_BASE_H +#define ANDROID_REF_BASE_H + +#include <cutils/atomic.h> + +#include <stdint.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include <utils/StrongPointer.h> +#include <utils/TypeHelpers.h> + +#ifdef _MSC_VER +#define __attribute__(X) +#endif + +// --------------------------------------------------------------------------- +namespace stagefright { + +class TextOutput; +TextOutput& printWeakPointer(TextOutput& to, const void* val); + +// --------------------------------------------------------------------------- + +#define COMPARE_WEAK(_op_) \ +inline bool operator _op_ (const sp<T>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template<typename U> \ +inline bool operator _op_ (const sp<U>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template<typename U> \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +class ReferenceRenamer { +protected: + // destructor is purposedly not virtual so we avoid code overhead from + // subclasses; we have to make it protected to guarantee that it + // cannot be called from this base class (and to make strict compilers + // happy). + ~ReferenceRenamer() { } +public: + virtual void operator()(size_t i) const = 0; +}; + +// --------------------------------------------------------------------------- + +class RefBase +{ +public: + void incStrong(const void* id) const; + void decStrong(const void* id) const; + + void forceIncStrong(const void* id) const; + + //! DEBUGGING ONLY: Get current strong ref count. + int32_t getStrongCount() const; + + class weakref_type + { + public: + RefBase* refBase() const; + + void incWeak(const void* id); + void decWeak(const void* id); + + // acquires a strong reference if there is already one. + bool attemptIncStrong(const void* id); + + // acquires a weak reference if there is already one. + // This is not always safe. see ProcessState.cpp and BpBinder.cpp + // for proper use. + bool attemptIncWeak(const void* id); + + //! DEBUGGING ONLY: Get current weak ref count. + int32_t getWeakCount() const; + + //! DEBUGGING ONLY: Print references held on object. + void printRefs() const; + + //! DEBUGGING ONLY: Enable tracking for this object. + // enable -- enable/disable tracking + // retain -- when tracking is enable, if true, then we save a stack trace + // for each reference and dereference; when retain == false, we + // match up references and dereferences and keep only the + // outstanding ones. + + void trackMe(bool enable, bool retain); + }; + + weakref_type* createWeak(const void* id) const; + + weakref_type* getWeakRefs() const; + + //! DEBUGGING ONLY: Print references held on object. + inline void printRefs() const { getWeakRefs()->printRefs(); } + + //! DEBUGGING ONLY: Enable tracking of object. + inline void trackMe(bool enable, bool retain) + { + getWeakRefs()->trackMe(enable, retain); + } + + typedef RefBase basetype; + +protected: + RefBase(); + virtual ~RefBase(); + + //! Flags for extendObjectLifetime() + enum { + OBJECT_LIFETIME_STRONG = 0x0000, + OBJECT_LIFETIME_WEAK = 0x0001, + OBJECT_LIFETIME_MASK = 0x0001 + }; + + void extendObjectLifetime(int32_t mode); + + //! Flags for onIncStrongAttempted() + enum { + FIRST_INC_STRONG = 0x0001 + }; + + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + virtual void onLastWeakRef(const void* id); + +private: + friend class weakref_type; + class weakref_impl; + + RefBase(const RefBase& o); + RefBase& operator=(const RefBase& o); + +private: + friend class ReferenceMover; + + static void renameRefs(size_t n, const ReferenceRenamer& renamer); + + static void renameRefId(weakref_type* ref, + const void* old_id, const void* new_id); + + static void renameRefId(RefBase* ref, + const void* old_id, const void* new_id); + + weakref_impl* const mRefs; +}; + +// --------------------------------------------------------------------------- + +template <class T> +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(__attribute__((unused)) const void* id) const { + android_atomic_inc(&mCount); + } + inline void decStrong(__attribute__((unused)) const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete static_cast<const T*>(this); + } + } + //! DEBUGGING ONLY: Get current strong ref count. + inline int32_t getStrongCount() const { + return mCount; + } + + typedef LightRefBase<T> basetype; + +protected: + inline ~LightRefBase() { } + +private: + friend class ReferenceMover; + inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { } + inline static void renameRefId(T* ref, + const void* old_id, const void* new_id) { } + +private: + mutable volatile int32_t mCount; +}; + +// --------------------------------------------------------------------------- + +template <typename T> +class wp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline wp() : m_ptr(0) { } + + wp(T* other); + wp(const wp<T>& other); + wp(const sp<T>& other); + template<typename U> wp(U* other); + template<typename U> wp(const sp<U>& other); + template<typename U> wp(const wp<U>& other); + + ~wp(); + + // Assignment + + wp& operator = (T* other); + wp& operator = (const wp<T>& other); + wp& operator = (const sp<T>& other); + + template<typename U> wp& operator = (U* other); + template<typename U> wp& operator = (const wp<U>& other); + template<typename U> wp& operator = (const sp<U>& other); + + void set_object_and_refs(T* other, weakref_type* refs); + + // promotion to sp + + sp<T> promote() const; + + // Reset + + void clear(); + + // Accessors + + inline weakref_type* get_refs() const { return m_refs; } + + inline T* unsafe_get() const { return m_ptr; } + + // Operators + + COMPARE_WEAK(==) + COMPARE_WEAK(!=) + COMPARE_WEAK(>) + COMPARE_WEAK(<) + COMPARE_WEAK(<=) + COMPARE_WEAK(>=) + + inline bool operator == (const wp<T>& o) const { + return (m_ptr == o.m_ptr) && (m_refs == o.m_refs); + } + template<typename U> + inline bool operator == (const wp<U>& o) const { + return m_ptr == o.m_ptr; + } + + inline bool operator > (const wp<T>& o) const { + return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); + } + template<typename U> + inline bool operator > (const wp<U>& o) const { + return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); + } + + inline bool operator < (const wp<T>& o) const { + return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); + } + template<typename U> + inline bool operator < (const wp<U>& o) const { + return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); + } + inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; } + template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); } + inline bool operator <= (const wp<T>& o) const { return !operator > (o); } + template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); } + inline bool operator >= (const wp<T>& o) const { return !operator < (o); } + template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); } + +private: + template<typename Y> friend class sp; + template<typename Y> friend class wp; + + T* m_ptr; + weakref_type* m_refs; +}; + +template <typename T> +TextOutput& operator<<(TextOutput& to, const wp<T>& val); + +#undef COMPARE_WEAK + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template<typename T> +wp<T>::wp(T* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template<typename T> +wp<T>::wp(const wp<T>& other) + : m_ptr(other.m_ptr), m_refs(other.m_refs) +{ + if (m_ptr) m_refs->incWeak(this); +} + +template<typename T> +wp<T>::wp(const sp<T>& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template<typename T> template<typename U> +wp<T>::wp(U* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template<typename T> template<typename U> +wp<T>::wp(const wp<U>& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = other.m_refs; + m_refs->incWeak(this); + } +} + +template<typename T> template<typename U> +wp<T>::wp(const sp<U>& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template<typename T> +wp<T>::~wp() +{ + if (m_ptr) m_refs->decWeak(this); +} + +template<typename T> +wp<T>& wp<T>::operator = (T* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template<typename T> +wp<T>& wp<T>::operator = (const wp<T>& other) +{ + weakref_type* otherRefs(other.m_refs); + T* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = otherPtr; + m_refs = otherRefs; + return *this; +} + +template<typename T> +wp<T>& wp<T>::operator = (const sp<T>& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + T* otherPtr(other.m_ptr); + if (m_ptr) m_refs->decWeak(this); + m_ptr = otherPtr; + m_refs = newRefs; + return *this; +} + +template<typename T> template<typename U> +wp<T>& wp<T>::operator = (U* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template<typename T> template<typename U> +wp<T>& wp<T>::operator = (const wp<U>& other) +{ + weakref_type* otherRefs(other.m_refs); + U* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = otherPtr; + m_refs = otherRefs; + return *this; +} + +template<typename T> template<typename U> +wp<T>& wp<T>::operator = (const sp<U>& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + U* otherPtr(other.m_ptr); + if (m_ptr) m_refs->decWeak(this); + m_ptr = otherPtr; + m_refs = newRefs; + return *this; +} + +template<typename T> +void wp<T>::set_object_and_refs(T* other, weakref_type* refs) +{ + if (other) refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = refs; +} + +template<typename T> +sp<T> wp<T>::promote() const +{ + sp<T> result; + if (m_ptr && m_refs->attemptIncStrong(&result)) { + result.set_pointer(m_ptr); + } + return result; +} + +template<typename T> +void wp<T>::clear() +{ + if (m_ptr) { + m_refs->decWeak(this); + m_ptr = 0; + } +} + +template <typename T> +inline TextOutput& operator<<(TextOutput& to, const wp<T>& val) +{ + return printWeakPointer(to, val.unsafe_get()); +} + +// --------------------------------------------------------------------------- + +// this class just serves as a namespace so TYPE::moveReferences can stay +// private. +class ReferenceMover { +public: + // it would be nice if we could make sure no extra code is generated + // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase: + // Using a sp<RefBase> override doesn't work; it's a bit like we wanted + // a template<typename TYPE inherits RefBase> template... + + template<typename TYPE> static inline + void move_references(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { + + class Renamer : public ReferenceRenamer { + sp<TYPE>* d; + sp<TYPE> const* s; + virtual void operator()(size_t i) const { + // The id are known to be the sp<>'s this pointer + TYPE::renameRefId(d[i].get(), &s[i], &d[i]); + } + public: + Renamer(sp<TYPE>* d, sp<TYPE> const* s) : s(s), d(d) { } + }; + + memmove(d, s, n*sizeof(sp<TYPE>)); + TYPE::renameRefs(n, Renamer(d, s)); + } + + + template<typename TYPE> static inline + void move_references(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { + + class Renamer : public ReferenceRenamer { + wp<TYPE>* d; + wp<TYPE> const* s; + virtual void operator()(size_t i) const { + // The id are known to be the wp<>'s this pointer + TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]); + } + public: + Renamer(wp<TYPE>* d, wp<TYPE> const* s) : s(s), d(d) { } + }; + + memmove(d, s, n*sizeof(wp<TYPE>)); + TYPE::renameRefs(n, Renamer(d, s)); + } +}; + +// specialization for moving sp<> and wp<> types. +// these are used by the [Sorted|Keyed]Vector<> implementations +// sp<> and wp<> need to be handled specially, because they do not +// have trivial copy operation in the general case (see RefBase.cpp +// when DEBUG ops are enabled), but can be implemented very +// efficiently in most cases. + +template<typename TYPE> inline +void move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template<typename TYPE> inline +void move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template<typename TYPE> inline +void move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template<typename TYPE> inline +void move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + + +}; // namespace stagefright + +#ifdef _MSC_VER +#undef __attribute__ +#endif + +// --------------------------------------------------------------------------- + +#endif // ANDROID_REF_BASE_H diff --git a/media/libstagefright/system/core/include/utils/SharedBuffer.h b/media/libstagefright/system/core/include/utils/SharedBuffer.h new file mode 100644 index 000000000..62d3ca415 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/SharedBuffer.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_SHARED_BUFFER_H +#define ANDROID_SHARED_BUFFER_H + +#include <stdint.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +class SharedBuffer +{ +public: + + /* flags to use with release() */ + enum { + eKeepStorage = 0x00000001 + }; + + /*! allocate a buffer of size 'size' and acquire() it. + * call release() to free it. + */ + static SharedBuffer* alloc(size_t size); + + /*! free the memory associated with the SharedBuffer. + * Fails if there are any users associated with this SharedBuffer. + * In other words, the buffer must have been release by all its + * users. + */ + static ssize_t dealloc(const SharedBuffer* released); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + SharedBuffer(const SharedBuffer&); + SharedBuffer& operator = (const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alignment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) { + return data ? static_cast<SharedBuffer *>(data)-1 : 0; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) { + return data ? static_cast<const SharedBuffer *>(data)-1 : 0; +} + +size_t SharedBuffer::sizeFromData(const void* data) { + return data ? bufferFromData(data)->mSize : 0; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/media/libstagefright/system/core/include/utils/SortedVector.h b/media/libstagefright/system/core/include/utils/SortedVector.h new file mode 100644 index 000000000..67bfea8cd --- /dev/null +++ b/media/libstagefright/system/core/include/utils/SortedVector.h @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_SORTED_VECTOR_H +#define ANDROID_SORTED_VECTOR_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include <utils/Vector.h> +#include <utils/VectorImpl.h> +#include <utils/TypeHelpers.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +template <class TYPE> +class SortedVector : private SortedVectorImpl +{ + friend class Vector<TYPE>; + +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector<TYPE>& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns whether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! sets the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you must keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + + /*! + * modifying the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector<TYPE>& vector); + ssize_t merge(const SortedVector<TYPE>& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + +// SortedVector<T> can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +template<typename T> struct trait_trivial_move<SortedVector<T> > { enum { value = true }; }; + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template<class TYPE> inline +SortedVector<TYPE>::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) + ) +{ +} + +template<class TYPE> inline +SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs) + : SortedVectorImpl(rhs) { +} + +template<class TYPE> inline +SortedVector<TYPE>::~SortedVector() { + finish_vector(); +} + +template<class TYPE> inline +SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const TYPE* SortedVector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); +} + +template<class TYPE> inline +TYPE* SortedVector<TYPE>::editArray() { + return static_cast<TYPE *>(editArrayImpl()); +} + + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::operator[](size_t index) const { + LOG_FATAL_IF(index>=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); + return *(array() + index); +} + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template<class TYPE> inline +size_t SortedVector<TYPE>::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) { + return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) { + return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<class TYPE> +void SortedVector<TYPE>::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) ); +} + +}; // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_SORTED_VECTOR_H diff --git a/media/libstagefright/system/core/include/utils/String16.h b/media/libstagefright/system/core/include/utils/String16.h new file mode 100644 index 000000000..40632d7ef --- /dev/null +++ b/media/libstagefright/system/core/include/utils/String16.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STRING16_H +#define ANDROID_STRING16_H + +#include <utils/Errors.h> +#include <utils/SharedBuffer.h> +#include <utils/Unicode.h> +#include <utils/TypeHelpers.h> + +// --------------------------------------------------------------------------- + +extern "C" { + +} + +// --------------------------------------------------------------------------- + +namespace stagefright { + +// --------------------------------------------------------------------------- + +class String8; +class TextOutput; + +//! This is a string holding UTF-16 characters. +class String16 +{ +public: + /* use String16(StaticLinkage) if you're statically linking against + * libutils and declaring an empty static String16, e.g.: + * + * static String16 sAStaticEmptyString(String16::kEmptyString); + * static String16 sAnotherStaticEmptyString(sAStaticEmptyString); + */ + enum StaticLinkage { kEmptyString }; + + String16(); + explicit String16(StaticLinkage); + String16(const String16& o); + String16(const String16& o, + size_t len, + size_t begin=0); + explicit String16(const char16_t* o); + explicit String16(const char16_t* o, size_t len); + explicit String16(const String8& o); + explicit String16(const char* o); + explicit String16(const char* o, size_t len); + + ~String16(); + + inline const char16_t* string() const; + inline size_t size() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String16& other); + status_t setTo(const char16_t* other); + status_t setTo(const char16_t* other, size_t len); + status_t setTo(const String16& other, + size_t len, + size_t begin=0); + + status_t append(const String16& other); + status_t append(const char16_t* other, size_t len); + + inline String16& operator=(const String16& other); + + inline String16& operator+=(const String16& other); + inline String16 operator+(const String16& other) const; + + status_t insert(size_t pos, const char16_t* chrs); + status_t insert(size_t pos, + const char16_t* chrs, size_t len); + + ssize_t findFirst(char16_t c) const; + ssize_t findLast(char16_t c) const; + + bool startsWith(const String16& prefix) const; + bool startsWith(const char16_t* prefix) const; + + status_t makeLower(); + + status_t replaceAll(char16_t replaceThis, + char16_t withThis); + + status_t remove(size_t len, size_t begin=0); + + inline int compare(const String16& other) const; + + inline bool operator<(const String16& other) const; + inline bool operator<=(const String16& other) const; + inline bool operator==(const String16& other) const; + inline bool operator!=(const String16& other) const; + inline bool operator>=(const String16& other) const; + inline bool operator>(const String16& other) const; + + inline bool operator<(const char16_t* other) const; + inline bool operator<=(const char16_t* other) const; + inline bool operator==(const char16_t* other) const; + inline bool operator!=(const char16_t* other) const; + inline bool operator>=(const char16_t* other) const; + inline bool operator>(const char16_t* other) const; + + inline operator const char16_t*() const; + +private: + const char16_t* mString; +}; + +// String16 can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +ANDROID_TRIVIAL_MOVE_TRAIT(String16) + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String16& lhs, const String16& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String16& lhs, const String16& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char16_t* String16::string() const +{ + return mString; +} + +inline size_t String16::size() const +{ + return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; +} + +inline const SharedBuffer* String16::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String16& String16::operator=(const String16& other) +{ + setTo(other); + return *this; +} + +inline String16& String16::operator+=(const String16& other) +{ + append(other); + return *this; +} + +inline String16 String16::operator+(const String16& other) const +{ + String16 tmp(*this); + tmp += other; + return tmp; +} + +inline int String16::compare(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()); +} + +inline bool String16::operator<(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) < 0; +} + +inline bool String16::operator<=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) <= 0; +} + +inline bool String16::operator==(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) == 0; +} + +inline bool String16::operator!=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) != 0; +} + +inline bool String16::operator>=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) >= 0; +} + +inline bool String16::operator>(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) > 0; +} + +inline bool String16::operator<(const char16_t* other) const +{ + return strcmp16(mString, other) < 0; +} + +inline bool String16::operator<=(const char16_t* other) const +{ + return strcmp16(mString, other) <= 0; +} + +inline bool String16::operator==(const char16_t* other) const +{ + return strcmp16(mString, other) == 0; +} + +inline bool String16::operator!=(const char16_t* other) const +{ + return strcmp16(mString, other) != 0; +} + +inline bool String16::operator>=(const char16_t* other) const +{ + return strcmp16(mString, other) >= 0; +} + +inline bool String16::operator>(const char16_t* other) const +{ + return strcmp16(mString, other) > 0; +} + +inline String16::operator const char16_t*() const +{ + return mString; +} + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING16_H diff --git a/media/libstagefright/system/core/include/utils/String8.h b/media/libstagefright/system/core/include/utils/String8.h new file mode 100644 index 000000000..3007f21af --- /dev/null +++ b/media/libstagefright/system/core/include/utils/String8.h @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STRING8_H +#define ANDROID_STRING8_H + +#include <utils/Errors.h> +#include <utils/SharedBuffer.h> +#include <utils/Unicode.h> +#include <utils/TypeHelpers.h> + +#include <string.h> // for strcmp +#include <stdarg.h> + +// --------------------------------------------------------------------------- + +#ifdef _MSC_VER +#define __attribute__(X) +#endif + +namespace stagefright { + +class String16; +class TextOutput; + +//! This is a string holding UTF-8 characters. Does not allow the value more +// than 0x10FFFF, which is not valid unicode codepoint. +class String8 +{ +public: + /* use String8(StaticLinkage) if you're statically linking against + * libutils and declaring an empty static String8, e.g.: + * + * static String8 sAStaticEmptyString(String8::kEmptyString); + * static String8 sAnotherStaticEmptyString(sAStaticEmptyString); + */ + enum StaticLinkage { kEmptyString }; + + String8(); + explicit String8(StaticLinkage); + String8(const String8& o); + explicit String8(const char* o); + explicit String8(const char* o, size_t numChars); + + explicit String8(const String16& o); + explicit String8(const char16_t* o); + explicit String8(const char16_t* o, size_t numChars); + explicit String8(const char32_t* o); + explicit String8(const char32_t* o, size_t numChars); + ~String8(); + + static inline const String8 empty(); + + static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2))); + static String8 formatV(const char* fmt, va_list args); + + inline const char* string() const; + inline size_t size() const; + inline size_t length() const; + inline size_t bytes() const; + inline bool isEmpty() const; + + inline const SharedBuffer* sharedBuffer() const; + + void clear(); + + void setTo(const String8& other); + status_t setTo(const char* other); + status_t setTo(const char* other, size_t numChars); + status_t setTo(const char16_t* other, size_t numChars); + status_t setTo(const char32_t* other, + size_t length); + + status_t append(const String8& other); + status_t append(const char* other); + status_t append(const char* other, size_t numChars); + + status_t appendFormat(const char* fmt, ...) + __attribute__((format (printf, 2, 3))); + status_t appendFormatV(const char* fmt, va_list args); + + // Note that this function takes O(N) time to calculate the value. + // No cache value is stored. + size_t getUtf32Length() const; + int32_t getUtf32At(size_t index, + size_t *next_index) const; + void getUtf32(char32_t* dst) const; + + inline String8& operator=(const String8& other); + inline String8& operator=(const char* other); + + inline String8& operator+=(const String8& other); + inline String8 operator+(const String8& other) const; + + inline String8& operator+=(const char* other); + inline String8 operator+(const char* other) const; + + inline int compare(const String8& other) const; + + inline bool operator<(const String8& other) const; + inline bool operator<=(const String8& other) const; + inline bool operator==(const String8& other) const; + inline bool operator!=(const String8& other) const; + inline bool operator>=(const String8& other) const; + inline bool operator>(const String8& other) const; + + inline bool operator<(const char* other) const; + inline bool operator<=(const char* other) const; + inline bool operator==(const char* other) const; + inline bool operator!=(const char* other) const; + inline bool operator>=(const char* other) const; + inline bool operator>(const char* other) const; + + inline operator const char*() const; + + char* lockBuffer(size_t size); + void unlockBuffer(); + status_t unlockBuffer(size_t size); + + // return the index of the first byte of other in this at or after + // start, or -1 if not found + ssize_t find(const char* other, size_t start = 0) const; + + void toLower(); + void toLower(size_t start, size_t numChars); + void toUpper(); + void toUpper(size_t start, size_t numChars); + + /* + * These methods operate on the string as if it were a path name. + */ + + /* + * Set the filename field to a specific value. + * + * Normalizes the filename, removing a trailing '/' if present. + */ + void setPathName(const char* name); + void setPathName(const char* name, size_t numChars); + + /* + * Get just the filename component. + * + * "/tmp/foo/bar.c" --> "bar.c" + */ + String8 getPathLeaf(void) const; + + /* + * Remove the last (file name) component, leaving just the directory + * name. + * + * "/tmp/foo/bar.c" --> "/tmp/foo" + * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX + * "bar.c" --> "" + */ + String8 getPathDir(void) const; + + /* + * Retrieve the front (root dir) component. Optionally also return the + * remaining components. + * + * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") + * "/tmp" --> "tmp" (remain = "") + * "bar.c" --> "bar.c" (remain = "") + */ + String8 walkPath(String8* outRemains = NULL) const; + + /* + * Return the filename extension. This is the last '.' and any number + * of characters that follow it. The '.' is included in case we + * decide to expand our definition of what constitutes an extension. + * + * "/tmp/foo/bar.c" --> ".c" + * "/tmp" --> "" + * "/tmp/foo.bar/baz" --> "" + * "foo.jpeg" --> ".jpeg" + * "foo." --> "" + */ + String8 getPathExtension(void) const; + + /* + * Return the path without the extension. Rules for what constitutes + * an extension are described in the comment for getPathExtension(). + * + * "/tmp/foo/bar.c" --> "/tmp/foo/bar" + */ + String8 getBasePath(void) const; + + /* + * Add a component to the pathname. We guarantee that there is + * exactly one path separator between the old path and the new. + * If there is no existing name, we just copy the new name in. + * + * If leaf is a fully qualified path (i.e. starts with '/', it + * replaces whatever was there before. + */ + String8& appendPath(const char* leaf); + String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } + + /* + * Like appendPath(), but does not affect this string. Returns a new one instead. + */ + String8 appendPathCopy(const char* leaf) const + { String8 p(*this); p.appendPath(leaf); return p; } + String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } + + /* + * Converts all separators in this string to /, the default path separator. + * + * If the default OS separator is backslash, this converts all + * backslashes to slashes, in-place. Otherwise it does nothing. + * Returns self. + */ + String8& convertToResPath(); + +private: + status_t real_append(const char* other, size_t numChars); + char* find_extension(void) const; + + const char* mString; +}; + +// String8 can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +ANDROID_TRIVIAL_MOVE_TRAIT(String8) + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String8& lhs, const String8& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String8& lhs, const String8& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const String8 String8::empty() { + return String8(); +} + +inline const char* String8::string() const +{ + return mString; +} + +inline size_t String8::length() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline size_t String8::size() const +{ + return length(); +} + +inline bool String8::isEmpty() const +{ + return length() == 0; +} + +inline size_t String8::bytes() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline const SharedBuffer* String8::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String8& String8::operator=(const String8& other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator=(const char* other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator+=(const String8& other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const String8& other) const +{ + String8 tmp(*this); + tmp += other; + return tmp; +} + +inline String8& String8::operator+=(const char* other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const char* other) const +{ + String8 tmp(*this); + tmp += other; + return tmp; +} + +inline int String8::compare(const String8& other) const +{ + return strcmp(mString, other.mString); +} + +inline bool String8::operator<(const String8& other) const +{ + return strcmp(mString, other.mString) < 0; +} + +inline bool String8::operator<=(const String8& other) const +{ + return strcmp(mString, other.mString) <= 0; +} + +inline bool String8::operator==(const String8& other) const +{ + return strcmp(mString, other.mString) == 0; +} + +inline bool String8::operator!=(const String8& other) const +{ + return strcmp(mString, other.mString) != 0; +} + +inline bool String8::operator>=(const String8& other) const +{ + return strcmp(mString, other.mString) >= 0; +} + +inline bool String8::operator>(const String8& other) const +{ + return strcmp(mString, other.mString) > 0; +} + +inline bool String8::operator<(const char* other) const +{ + return strcmp(mString, other) < 0; +} + +inline bool String8::operator<=(const char* other) const +{ + return strcmp(mString, other) <= 0; +} + +inline bool String8::operator==(const char* other) const +{ + return strcmp(mString, other) == 0; +} + +inline bool String8::operator!=(const char* other) const +{ + return strcmp(mString, other) != 0; +} + +inline bool String8::operator>=(const char* other) const +{ + return strcmp(mString, other) >= 0; +} + +inline bool String8::operator>(const char* other) const +{ + return strcmp(mString, other) > 0; +} + +inline String8::operator const char*() const +{ + return mString; +} + +} // namespace stagefright + +#ifdef _MSC_VER +#undef __attribute__ +#endif + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING8_H diff --git a/media/libstagefright/system/core/include/utils/StrongPointer.h b/media/libstagefright/system/core/include/utils/StrongPointer.h new file mode 100644 index 000000000..db22900f0 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/StrongPointer.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_STRONG_POINTER_H +#define ANDROID_STRONG_POINTER_H + +#include <cutils/atomic.h> + +#include <stdint.h> +#include <sys/types.h> +#include <stdlib.h> + +// --------------------------------------------------------------------------- +namespace stagefright { + +template<typename T> class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp<T>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template<typename U> \ +inline bool operator _op_ (const sp<U>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template<typename U> \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} \ +inline bool operator _op_ (const wp<T>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template<typename U> \ +inline bool operator _op_ (const wp<U>& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} + +// --------------------------------------------------------------------------- + +template<typename T> +class sp { +public: + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp<T>& other); + template<typename U> sp(U* other); + template<typename U> sp(const sp<U>& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp<T>& other); + + template<typename U> sp& operator = (const sp<U>& other); + template<typename U> sp& operator = (U* other); + + //! Special optimization for use by ProcessState (and nobody else). + void force_set(T* other); + + // Reset + + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template<typename Y> friend class sp; + template<typename Y> friend class wp; + void set_pointer(T* ptr); + T* m_ptr; +}; + +#undef COMPARE + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template<typename T> +sp<T>::sp(T* other) + : m_ptr(other) { + if (other) + other->incStrong(this); +} + +template<typename T> +sp<T>::sp(const sp<T>& other) + : m_ptr(other.m_ptr) { + if (m_ptr) + m_ptr->incStrong(this); +} + +template<typename T> template<typename U> +sp<T>::sp(U* other) + : m_ptr(other) { + if (other) + ((T*) other)->incStrong(this); +} + +template<typename T> template<typename U> +sp<T>::sp(const sp<U>& other) + : m_ptr(other.m_ptr) { + if (m_ptr) + m_ptr->incStrong(this); +} + +template<typename T> +sp<T>::~sp() { + if (m_ptr) + m_ptr->decStrong(this); +} + +template<typename T> +sp<T>& sp<T>::operator =(const sp<T>& other) { + T* otherPtr(other.m_ptr); + if (otherPtr) + otherPtr->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); + m_ptr = otherPtr; + return *this; +} + +template<typename T> +sp<T>& sp<T>::operator =(T* other) { + if (other) + other->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template<typename T> template<typename U> +sp<T>& sp<T>::operator =(const sp<U>& other) { + T* otherPtr(other.m_ptr); + if (otherPtr) + otherPtr->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); + m_ptr = otherPtr; + return *this; +} + +template<typename T> template<typename U> +sp<T>& sp<T>::operator =(U* other) { + if (other) + ((T*) other)->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template<typename T> +void sp<T>::force_set(T* other) { + other->forceIncStrong(this); + m_ptr = other; +} + +template<typename T> +void sp<T>::clear() { + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template<typename T> +void sp<T>::set_pointer(T* ptr) { + m_ptr = ptr; +} + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRONG_POINTER_H diff --git a/media/libstagefright/system/core/include/utils/Timers.h b/media/libstagefright/system/core/include/utils/Timers.h new file mode 100644 index 000000000..d01542169 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Timers.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#ifndef _LIBS_UTILS_TIMERS_H +#define _LIBS_UTILS_TIMERS_H + +#include <stdint.h> +#include <sys/types.h> +#include <sys/time.h> + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int64_t nsecs_t; // nano-seconds + +static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000000; +} + +static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000; +} + +static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000; +} + +static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) +{ + return secs/1000000000; +} + +static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) +{ + return secs/1000000; +} + +static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) +{ + return secs/1000; +} + +static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} +static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} +static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} +static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} +static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} +static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} + +static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } +static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } +static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } + +enum { + SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock + SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point + SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock + SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock + SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time +}; + +// return the system-time according to the specified clock +#ifdef __cplusplus +nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); +#else +nsecs_t systemTime(int clock); +#endif // def __cplusplus + +/** + * Returns the number of milliseconds to wait between the reference time and the timeout time. + * If the timeout is in the past relative to the reference time, returns 0. + * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time, + * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay. + * Otherwise, returns the difference between the reference time and timeout time + * rounded up to the next millisecond. + */ +int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _LIBS_UTILS_TIMERS_H diff --git a/media/libstagefright/system/core/include/utils/TypeHelpers.h b/media/libstagefright/system/core/include/utils/TypeHelpers.h new file mode 100644 index 000000000..7a1924416 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/TypeHelpers.h @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_TYPE_HELPERS_H +#define ANDROID_TYPE_HELPERS_H + +#include <new> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +/* + * Types traits + */ + +template <typename T> struct trait_trivial_ctor { enum { value = false }; }; +template <typename T> struct trait_trivial_dtor { enum { value = false }; }; +template <typename T> struct trait_trivial_copy { enum { value = false }; }; +template <typename T> struct trait_trivial_move { enum { value = false }; }; +template <typename T> struct trait_pointer { enum { value = false }; }; +template <typename T> struct trait_pointer<T*> { enum { value = true }; }; + +template <typename TYPE> +struct traits { + enum { + // whether this type is a pointer + is_pointer = trait_pointer<TYPE>::value, + // whether this type's constructor is a no-op + has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value, + // whether this type's destructor is a no-op + has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value, + // whether this type type can be copy-constructed with memcpy + has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value, + // whether this type can be moved with memmove + has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value + }; +}; + +template <typename T, typename U> +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = + traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor, + has_trivial_dtor = + traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor, + has_trivial_copy = + traits<T>::has_trivial_copy && traits<U>::has_trivial_copy, + has_trivial_move = + traits<T>::has_trivial_move && traits<U>::has_trivial_move + }; +}; + +#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \ + template<> struct trait_trivial_move< T > { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ + ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ + ANDROID_TRIVIAL_COPY_TRAIT( T ) \ + ANDROID_TRIVIAL_MOVE_TRAIT( T ) + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ) +ANDROID_BASIC_TYPES_TRAITS( bool ) +ANDROID_BASIC_TYPES_TRAITS( char ) +ANDROID_BASIC_TYPES_TRAITS( unsigned char ) +ANDROID_BASIC_TYPES_TRAITS( short ) +ANDROID_BASIC_TYPES_TRAITS( unsigned short ) +ANDROID_BASIC_TYPES_TRAITS( int ) +ANDROID_BASIC_TYPES_TRAITS( unsigned int ) +ANDROID_BASIC_TYPES_TRAITS( long ) +ANDROID_BASIC_TYPES_TRAITS( unsigned long ) +ANDROID_BASIC_TYPES_TRAITS( long long ) +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ) +ANDROID_BASIC_TYPES_TRAITS( float ) +ANDROID_BASIC_TYPES_TRAITS( double ) + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template<typename TYPE> inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template<typename TYPE> inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and move types... + */ + +template<typename TYPE> inline +void construct_type(TYPE* p, size_t n) { + if (!traits<TYPE>::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template<typename TYPE> inline +void destroy_type(TYPE* p, size_t n) { + if (!traits<TYPE>::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template<typename TYPE> inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits<TYPE>::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template<typename TYPE> inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits<TYPE>::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template<typename TYPE> inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy) + || traits<TYPE>::has_trivial_move) + { + memmove(d,s,n*sizeof(TYPE)); + } else { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits<TYPE>::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits<TYPE>::has_trivial_dtor) { + s->~TYPE(); + } + } + } +} + +template<typename TYPE> inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy) + || traits<TYPE>::has_trivial_move) + { + memmove((void*)d,(void*)s,n*sizeof(TYPE)); + } else { + while (n--) { + if (!traits<TYPE>::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits<TYPE>::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } +} + +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template <typename KEY, typename VALUE> +struct key_value_pair_t { + typedef KEY key_t; + typedef VALUE value_t; + + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } + inline const KEY& getKey() const { + return key; + } + inline const VALUE& getValue() const { + return value; + } +}; + +template <typename K, typename V> +struct trait_trivial_ctor< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; }; +template <typename K, typename V> +struct trait_trivial_dtor< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; }; +template <typename K, typename V> +struct trait_trivial_copy< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; }; +template <typename K, typename V> +struct trait_trivial_move< key_value_pair_t<K, V> > +{ enum { value = aggregate_traits<K,V>::has_trivial_move }; }; + +// --------------------------------------------------------------------------- + +/* + * Hash codes. + */ +typedef uint32_t hash_t; + +template <typename TKey> +hash_t hash_type(const TKey& key); + +/* Built-in hash code specializations. + * Assumes pointers are 32bit. */ +#define ANDROID_INT32_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { return hash_t(value); } +#define ANDROID_INT64_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_t((value >> 32) ^ value); } +#define ANDROID_REINTERPRET_HASH(T, R) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_type(*reinterpret_cast<const R*>(&value)); } + +ANDROID_INT32_HASH(bool) +ANDROID_INT32_HASH(int8_t) +ANDROID_INT32_HASH(uint8_t) +ANDROID_INT32_HASH(int16_t) +ANDROID_INT32_HASH(uint16_t) +ANDROID_INT32_HASH(int32_t) +ANDROID_INT32_HASH(uint32_t) +ANDROID_INT64_HASH(int64_t) +ANDROID_INT64_HASH(uint64_t) +ANDROID_REINTERPRET_HASH(float, uint32_t) +ANDROID_REINTERPRET_HASH(double, uint64_t) + +template <typename T> inline hash_t hash_type(T* const & value) { + return hash_type(uintptr_t(value)); +} + +}; // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/media/libstagefright/system/core/include/utils/Unicode.h b/media/libstagefright/system/core/include/utils/Unicode.h new file mode 100644 index 000000000..b76a5e268 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Unicode.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_UNICODE_H +#define ANDROID_UNICODE_H + +#include <sys/types.h> +#include <stdint.h> + +extern "C" { + +// Standard string functions on char16_t strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +// Standard string functions on char32_t strings. +size_t strlen32(const char32_t *); +size_t strnlen32(const char32_t *, size_t); + +/** + * Measure the length of a UTF-32 string in UTF-8. If the string is invalid + * such as containing a surrogate character, -1 will be returned. + */ +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); + +/** + * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not + * large enough to store the string, the part of the "src" string is stored + * into "dst" as much as possible. See the examples for more detail. + * Returns the size actually used for storing the string. + * dst" is not null-terminated when dst_len is fully used (like strncpy). + * + * Example 1 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" >= 7 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 + * (note that "dst" is null-terminated) + * + * Example 2 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 5 + * -> + * Returned value == 3 + * "dst" becomes \xE3\x81\x82\0 + * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" + * since "dst" does not have enough size to store the character) + * + * Example 3 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 6 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84 + * (note that "dst" is NOT null-terminated, like strncpy) + */ +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); + +/** + * Returns the unicode value at "index". + * Returns -1 when the index is invalid (equals to or more than "src_len"). + * If returned value is positive, it is able to be converted to char32_t, which + * is unsigned. Then, if "next_index" is not NULL, the next index to be used is + * stored in "next_index". "next_index" can be NULL. + */ +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); + + +/** + * Returns the UTF-8 length of UTF-16 string "src". + */ +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); + +/** + * Converts a UTF-16 string to UTF-8. The destination buffer must be large + * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added + * NULL terminator. + */ +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); + +/** + * Returns the length of "src" when "src" is valid UTF-8 string. + * Returns 0 if src is NULL or 0-length string. Returns -1 when the source + * is an invalid string. + * + * This function should be used to determine whether "src" is valid UTF-8 + * characters with valid unicode codepoints. "src" must be null-terminated. + * + * If you are going to use other utf8_to_... functions defined in this header + * with string which may not be valid UTF-8 with valid codepoint (form 0 to + * 0x10FFFF), you should use this function before calling others, since the + * other functions do not check whether the string is valid UTF-8 or not. + * + * If you do not care whether "src" is valid UTF-8 or not, you should use + * strlen() as usual, which should be much faster. + */ +ssize_t utf8_length(const char *src); + +/** + * Measure the length of a UTF-32 string. + */ +size_t utf8_to_utf32_length(const char *src, size_t src_len); + +/** + * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large + * enough to store the entire converted string as measured by + * utf8_to_utf32_length plus space for a NULL terminator. + */ +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); + +/** + * Returns the UTF-16 length of UTF-8 string "src". + */ +ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); + +/** + * Convert UTF-8 to UTF-16 including surrogate pairs. + * Returns a pointer to the end of the string (where a null terminator might go + * if you wanted to add one). + */ +char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst); + +/** + * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer + * must be large enough to hold the result as measured by utf8_to_utf16_length + * plus an added NULL terminator. + */ +void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); + +/** + * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the + * decoded string. The decoded string will fill up to that length; if it is longer + * the returned pointer will be to the character after dstLen. + */ +char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); + +} + +#endif diff --git a/media/libstagefright/system/core/include/utils/Vector.h b/media/libstagefright/system/core/include/utils/Vector.h new file mode 100644 index 000000000..c08577a33 --- /dev/null +++ b/media/libstagefright/system/core/include/utils/Vector.h @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_VECTOR_H +#define ANDROID_VECTOR_H + +#include <new> +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include <utils/VectorImpl.h> +#include <utils/TypeHelpers.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +template <typename TYPE> +class SortedVector; + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template <class TYPE> +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector<TYPE>& rhs); + explicit Vector(const SortedVector<TYPE>& rhs); + virtual ~Vector(); + + /*! copy operator */ + Vector<TYPE>& operator = (const Vector<TYPE>& rhs); + + Vector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns whether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! sets the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * set the size of the vector. items are appended with the default + * constructor, or removed from the end as needed. + */ + inline ssize_t resize(size_t size) { return VectorImpl::resize(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + + /*! + * modifying the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right access to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector<TYPE>& vector); + + + //! insert an array at a given index + ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length); + + //! append an array at the end of this vector + ssize_t appendArray(const TYPE* array, size_t length); + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert one or several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); + } + inline status_t sort(compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); + } + + // for debugging only + inline size_t getItemSize() const { return itemSize(); } + + + /* + * these inlines add some level of compatibility with STL. eventually + * we should probably turn things around. + */ + typedef TYPE* iterator; + typedef TYPE const* const_iterator; + + inline iterator begin() { return editArray(); } + inline iterator end() { return editArray() + size(); } + inline const_iterator begin() const { return array(); } + inline const_iterator end() const { return array() + size(); } + inline void reserve(size_t n) { assert(setCapacity(n) >= 0); } + inline bool empty() const{ return isEmpty(); } + inline void push_back(const TYPE& item) { insertAt(item, size(), 1); } + inline void push_front(const TYPE& item) { insertAt(item, 0, 1); } + inline iterator erase(iterator pos) { + ssize_t index = removeItemsAt(pos-array()); + return begin() + index; + } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + +// Vector<T> can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +template<typename T> struct trait_trivial_move<Vector<T> > { enum { value = true }; }; + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template<class TYPE> inline +Vector<TYPE>::Vector() + : VectorImpl(sizeof(TYPE), + ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) + ) +{ +} + +template<class TYPE> inline +Vector<TYPE>::Vector(const Vector<TYPE>& rhs) + : VectorImpl(rhs) { +} + +template<class TYPE> inline +Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs) + : VectorImpl(static_cast<const VectorImpl&>(rhs)) { +} + +template<class TYPE> inline +Vector<TYPE>::~Vector() { + finish_vector(); +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)); + return *this; +} + +template<class TYPE> inline +const TYPE* Vector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); +} + +template<class TYPE> inline +TYPE* Vector<TYPE>::editArray() { + return static_cast<TYPE *>(editArrayImpl()); +} + + +template<class TYPE> inline +const TYPE& Vector<TYPE>::operator[](size_t index) const { + LOG_FATAL_IF(index>=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); + return *(array() + index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editItemAt(size_t index) { + return *( static_cast<TYPE *>(editItemLocation(index)) ); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editTop() { + return *( static_cast<TYPE *>(editItemLocation(size()-1)) ); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { + return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) { + return VectorImpl::insertArrayAt(array, index, length); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) { + return VectorImpl::appendArray(array, length); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::pop() { + VectorImpl::pop(); +} + +template<class TYPE> inline +void Vector<TYPE>::push() { + VectorImpl::push(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add() { + return VectorImpl::add(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<class TYPE> +void Vector<TYPE>::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +}; // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/media/libstagefright/system/core/include/utils/VectorImpl.h b/media/libstagefright/system/core/include/utils/VectorImpl.h new file mode 100644 index 000000000..b83c9462c --- /dev/null +++ b/media/libstagefright/system/core/include/utils/VectorImpl.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2005 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. + */ + +#ifndef ANDROID_VECTOR_IMPL_H +#define ANDROID_VECTOR_IMPL_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> +#include <utils/Errors.h> + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace stagefright { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + ssize_t resize(size_t size); + + /*! append/insert another vector or array */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + + typedef int (*compar_t)(const void* lhs, const void* rhs); + typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); + status_t sort(compar_t cmp); + status_t sort(compar_r_t cmp, void* state); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/media/libstagefright/system/core/libcutils/strdup16to8.c b/media/libstagefright/system/core/libcutils/strdup16to8.c new file mode 100644 index 000000000..1a8ba8674 --- /dev/null +++ b/media/libstagefright/system/core/libcutils/strdup16to8.c @@ -0,0 +1,168 @@ +/* libs/cutils/strdup16to8.c +** +** Copyright 2006, 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 <limits.h> /* for SIZE_MAX */ + +#include <cutils/jstring.h> +#include <assert.h> +#include <stdlib.h> + + +/** + * Given a UTF-16 string, compute the length of the corresponding UTF-8 + * string in bytes. + */ +extern size_t strnlen16to8(const char16_t* utf16Str, size_t len) +{ + size_t utf8Len = 0; + + /* A small note on integer overflow. The result can + * potentially be as big as 3*len, which will overflow + * for len > SIZE_MAX/3. + * + * Moreover, the result of a strnlen16to8 is typically used + * to allocate a destination buffer to strncpy16to8 which + * requires one more byte to terminate the UTF-8 copy, and + * this is generally done by careless users by incrementing + * the result without checking for integer overflows, e.g.: + * + * dst = malloc(strnlen16to8(utf16,len)+1) + * + * Due to this, the following code will try to detect + * overflows, and never return more than (SIZE_MAX-1) + * when it detects one. A careless user will try to malloc + * SIZE_MAX bytes, which will return NULL which can at least + * be detected appropriately. + * + * As far as I know, this function is only used by strndup16(), + * but better be safe than sorry. + */ + + /* Fast path for the usual case where 3*len is < SIZE_MAX-1. + */ + if (len < (SIZE_MAX-1)/3) { + while (len--) { + unsigned int uic = *utf16Str++; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + } + return utf8Len; + } + + /* The slower but paranoid version */ + while (len--) { + unsigned int uic = *utf16Str++; + size_t utf8Cur = utf8Len; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + + if (utf8Len < utf8Cur) /* overflow detected */ + return SIZE_MAX-1; + } + + /* don't return SIZE_MAX to avoid common user bug */ + if (utf8Len == SIZE_MAX) + utf8Len = SIZE_MAX-1; + + return utf8Len; +} + + +/** + * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string. + * + * This basically means: embedded \0's in the UTF-16 string are encoded + * as "0xc0 0x80" + * + * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1, + * not just "len". + * + * Please note, a terminated \0 is always added, so your result will always + * be "strlen16to8() + 1" bytes long. + */ +extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) +{ + char* utf8cur = utf8Str; + + /* Note on overflows: We assume the user did check the result of + * strnlen16to8() properly or at a minimum checked the result of + * its malloc(SIZE_MAX) in case of overflow. + */ + while (len--) { + unsigned int uic = *utf16Str++; + + if (uic > 0x07ff) { + *utf8cur++ = (uic >> 12) | 0xe0; + *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80; + *utf8cur++ = (uic & 0x3f) | 0x80; + } else if (uic > 0x7f || uic == 0) { + *utf8cur++ = (uic >> 6) | 0xc0; + *utf8cur++ = (uic & 0x3f) | 0x80; + } else { + *utf8cur++ = uic; + + if (uic == 0) { + break; + } + } + } + + *utf8cur = '\0'; + + return utf8Str; +} + +/** + * Convert a UTF-16 string to UTF-8. + * + */ +char * strndup16to8 (const char16_t* s, size_t n) +{ + char* ret; + size_t len; + + if (s == NULL) { + return NULL; + } + + len = strnlen16to8(s, n); + + /* We are paranoid, and we check for SIZE_MAX-1 + * too since it is an overflow value for our + * strnlen16to8 implementation. + */ + if (len >= SIZE_MAX-1) + return NULL; + + ret = malloc(len + 1); + if (ret == NULL) + return NULL; + + strncpy16to8 (ret, s, n); + + return ret; +} diff --git a/media/libstagefright/system/core/liblog/fake_log_device.c b/media/libstagefright/system/core/liblog/fake_log_device.c new file mode 100644 index 000000000..9a4f07f15 --- /dev/null +++ b/media/libstagefright/system/core/liblog/fake_log_device.c @@ -0,0 +1,737 @@ +/* + * Copyright (C) 2008 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. + */ +/* + * Intercepts log messages intended for the Android log device. + * When running in the context of the simulator, the messages are + * passed on to the underlying (fake) log device. When not in the + * simulator, messages are printed to stderr. + */ +#include <log/logd.h> + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif + +#ifdef _MSC_VER +#include <io.h> +#include <process.h> +#if _MSC_VER < 1900 +#include <nspr/prprf.h> +#define snprintf PR_snprintf +#endif + +/* We don't want to indent large blocks because it causes unnecessary merge + * conflicts */ +#define UNINDENTED_BLOCK_START { +#define UNINDENTED_BLOCK_END } +#else +#define UNINDENTED_BLOCK_START +#define UNINDENTED_BLOCK_END +#endif + +#ifdef _MSC_VER +#include <io.h> +#include <process.h> + +/* We don't want to indent large blocks because it causes unnecessary merge + * conflicts */ +#define UNINDENTED_BLOCK_START { +#define UNINDENTED_BLOCK_END } +#else +#define UNINDENTED_BLOCK_START +#define UNINDENTED_BLOCK_END +#endif + +#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ + +#define kTagSetSize 16 /* arbitrary */ + +#if 0 +#define TRACE(...) printf("fake_log_device: " __VA_ARGS__) +#else +#define TRACE(...) ((void)0) +#endif + +/* from the long-dead utils/Log.cpp */ +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG +} LogFormat; + + +/* + * Log driver state. + */ +typedef struct LogState { + /* the fake fd that's seen by the user */ + int fakeFd; + + /* a printable name for this fake device */ + char *debugName; + + /* nonzero if this is a binary log */ + int isBinary; + + /* global minimum priority */ + int globalMinPriority; + + /* output format */ + LogFormat outputFormat; + + /* tags and priorities */ + struct { + char tag[kMaxTagLen]; + int minPriority; + } tagSet[kTagSetSize]; +} LogState; + + +#ifdef HAVE_PTHREADS +/* + * Locking. Since we're emulating a device, we need to be prepared + * to have multiple callers at the same time. This lock is used + * to both protect the fd list and to prevent LogStates from being + * freed out from under a user. + */ +static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; + +static void lock() +{ + pthread_mutex_lock(&fakeLogDeviceLock); +} + +static void unlock() +{ + pthread_mutex_unlock(&fakeLogDeviceLock); +} +#else // !HAVE_PTHREADS +#define lock() ((void)0) +#define unlock() ((void)0) +#endif // !HAVE_PTHREADS + + +/* + * File descriptor management. + */ +#define FAKE_FD_BASE 10000 +#define MAX_OPEN_LOGS 16 +static LogState *openLogTable[MAX_OPEN_LOGS]; + +/* + * Allocate an fd and associate a new LogState with it. + * The fd is available via the fakeFd field of the return value. + */ +static LogState *createLogState() +{ + size_t i; + + for (i = 0; i < sizeof(openLogTable); i++) { + if (openLogTable[i] == NULL) { + openLogTable[i] = calloc(1, sizeof(LogState)); + openLogTable[i]->fakeFd = FAKE_FD_BASE + i; + return openLogTable[i]; + } + } + return NULL; +} + +/* + * Translate an fd to a LogState. + */ +static LogState *fdToLogState(int fd) +{ + if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) { + return openLogTable[fd - FAKE_FD_BASE]; + } + return NULL; +} + +/* + * Unregister the fake fd and free the memory it pointed to. + */ +static void deleteFakeFd(int fd) +{ + LogState *ls; + + lock(); + + ls = fdToLogState(fd); + if (ls != NULL) { + openLogTable[fd - FAKE_FD_BASE] = NULL; + free(ls->debugName); + free(ls); + } + + unlock(); +} + +/* + * Configure logging based on ANDROID_LOG_TAGS environment variable. We + * need to parse a string that looks like + * + * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i + * + * The tag (or '*' for the global level) comes first, followed by a colon + * and a letter indicating the minimum priority level we're expected to log. + * This can be used to reveal or conceal logs with specific tags. + * + * We also want to check ANDROID_PRINTF_LOG to determine how the output + * will look. + */ +static void configureInitialState(const char* pathName, LogState* logState) +{ + static const int kDevLogLen = sizeof("/dev/log/") - 1; + + logState->debugName = strdup(pathName); + + /* identify binary logs */ + if (strcmp(pathName + kDevLogLen, "events") == 0) { + logState->isBinary = 1; + } + + /* global min priority defaults to "info" level */ + logState->globalMinPriority = ANDROID_LOG_INFO; + + /* + * This is based on the the long-dead utils/Log.cpp code. + */ + UNINDENTED_BLOCK_START + const char* tags = getenv("ANDROID_LOG_TAGS"); + TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); + if (tags != NULL) { + int entry = 0; + + while (*tags != '\0') { + char tagName[kMaxTagLen]; + int i, minPrio; + + while (isspace(*tags)) + tags++; + + i = 0; + while (*tags != '\0' && !isspace(*tags) && *tags != ':' && + i < kMaxTagLen) + { + tagName[i++] = *tags++; + } + if (i == kMaxTagLen) { + TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1); + return; + } + tagName[i] = '\0'; + + /* default priority, if there's no ":" part; also zero out '*' */ + minPrio = ANDROID_LOG_VERBOSE; + if (tagName[0] == '*' && tagName[1] == '\0') { + minPrio = ANDROID_LOG_DEBUG; + tagName[0] = '\0'; + } + + if (*tags == ':') { + tags++; + if (*tags >= '0' && *tags <= '9') { + if (*tags >= ('0' + ANDROID_LOG_SILENT)) + minPrio = ANDROID_LOG_VERBOSE; + else + minPrio = *tags - '\0'; + } else { + switch (*tags) { + case 'v': minPrio = ANDROID_LOG_VERBOSE; break; + case 'd': minPrio = ANDROID_LOG_DEBUG; break; + case 'i': minPrio = ANDROID_LOG_INFO; break; + case 'w': minPrio = ANDROID_LOG_WARN; break; + case 'e': minPrio = ANDROID_LOG_ERROR; break; + case 'f': minPrio = ANDROID_LOG_FATAL; break; + case 's': minPrio = ANDROID_LOG_SILENT; break; + default: minPrio = ANDROID_LOG_DEFAULT; break; + } + } + + tags++; + if (*tags != '\0' && !isspace(*tags)) { + TRACE("ERROR: garbage in tag env; expected whitespace\n"); + TRACE(" env='%s'\n", tags); + return; + } + } + + if (tagName[0] == 0) { + logState->globalMinPriority = minPrio; + TRACE("+++ global min prio %d\n", logState->globalMinPriority); + } else { + logState->tagSet[entry].minPriority = minPrio; + strcpy(logState->tagSet[entry].tag, tagName); + TRACE("+++ entry %d: %s:%d\n", + entry, + logState->tagSet[entry].tag, + logState->tagSet[entry].minPriority); + entry++; + } + } + } + UNINDENTED_BLOCK_END + + /* + * Taken from the long-dead utils/Log.cpp + */ + UNINDENTED_BLOCK_START + const char* fstr = getenv("ANDROID_PRINTF_LOG"); + LogFormat format; + if (fstr == NULL) { + format = FORMAT_BRIEF; + } else { + if (strcmp(fstr, "brief") == 0) + format = FORMAT_BRIEF; + else if (strcmp(fstr, "process") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "tag") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "thread") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "raw") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "time") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "long") == 0) + format = FORMAT_PROCESS; + else + format = (LogFormat) atoi(fstr); // really?! + } + + logState->outputFormat = format; + UNINDENTED_BLOCK_END +} + +/* + * Return a human-readable string for the priority level. Always returns + * a valid string. + */ +static const char* getPriorityString(int priority) +{ + /* the first character of each string should be unique */ + static const char* priorityStrings[] = { + "Verbose", "Debug", "Info", "Warn", "Error", "Assert" + }; + int idx; + + idx = (int) priority - (int) ANDROID_LOG_VERBOSE; + if (idx < 0 || + idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0]))) + return "?unknown?"; + return priorityStrings[idx]; +} + +#ifndef HAVE_WRITEV +/* + * Some platforms like WIN32 do not have writev(). + * Make up something to replace it. + */ +static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { + int result = 0; + const struct iovec* end = iov + iovcnt; + for (; iov < end; iov++) { + int w = write(fd, iov->iov_base, iov->iov_len); + if (w != iov->iov_len) { + if (w < 0) + return w; + return result + w; + } + result += w; + } + return result; +} + +#define writev fake_writev +#endif + + +/* + * Write a filtered log message to stderr. + * + * Log format parsing taken from the long-dead utils/Log.cpp. + */ +static void showLog(LogState *state, + int logPrio, const char* tag, const char* msg) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + time_t when; +#ifdef _MSC_VER + int pid, tid; +#else + pid_t pid, tid; +#endif + + TRACE("LOG %d: %s %s", logPrio, tag, msg); + + priChar = getPriorityString(logPrio)[0]; + when = time(NULL); + pid = tid = getpid(); // find gettid()? + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&when, &tmBuf); +#else + ptm = localtime(&when); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + UNINDENTED_BLOCK_START + size_t prefixLen, suffixLen; + + switch (state->outputFormat) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%5d) ", priChar, pid, tid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; prefixLen = 0; + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %-8s\n\t", timeBuf, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s %5d:%5d %c/%-8s ]\n", + timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n\n"); suffixLen = 2; + break; + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, tag, pid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + } + + /* + * Figure out how many lines there will be. + */ + UNINDENTED_BLOCK_START + const char* end = msg + strlen(msg); + size_t numLines = 0; + const char* p = msg; + while (p < end) { + if (*p++ == '\n') numLines++; + } + if (p > msg && *(p-1) != '\n') numLines++; + + /* + * Create an array of iovecs large enough to write all of + * the lines with a prefix and a suffix. + */ + UNINDENTED_BLOCK_START + #define INLINE_VECS 6 + const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*)); + struct iovec stackVec[INLINE_VECS]; + struct iovec* vec = stackVec; + size_t numVecs; + + if (numLines > MAX_LINES) + numLines = MAX_LINES; + + numVecs = numLines*3; // 3 iovecs per line. + if (numVecs > INLINE_VECS) { + vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs); + if (vec == NULL) { + msg = "LOG: write failed, no memory"; + numVecs = 3; + numLines = 1; + vec = stackVec; + } + } + + /* + * Fill in the iovec pointers. + */ + p = msg; + UNINDENTED_BLOCK_START + struct iovec* v = vec; + int totalLen = 0; + while (numLines > 0 && p < end) { + if (prefixLen > 0) { + v->iov_base = prefixBuf; + v->iov_len = prefixLen; + totalLen += prefixLen; + v++; + } + UNINDENTED_BLOCK_START + const char* start = p; + while (p < end && *p != '\n') p++; + if ((p-start) > 0) { + v->iov_base = (void*)start; + v->iov_len = p-start; + totalLen += p-start; + v++; + } + if (*p == '\n') p++; + if (suffixLen > 0) { + v->iov_base = suffixBuf; + v->iov_len = suffixLen; + totalLen += suffixLen; + v++; + } + numLines -= 1; + UNINDENTED_BLOCK_END + } + + /* + * Write the entire message to the log file with a single writev() call. + * We need to use this rather than a collection of printf()s on a FILE* + * because of multi-threading and multi-process issues. + * + * If the file was not opened with O_APPEND, this will produce interleaved + * output when called on the same file from multiple processes. + * + * If the file descriptor is actually a network socket, the writev() + * call may return with a partial write. Putting the writev() call in + * a loop can result in interleaved data. This can be alleviated + * somewhat by wrapping the writev call in the Mutex. + */ + + for(;;) { + int cc = writev(fileno(stderr), vec, v-vec); + + if (cc == totalLen) break; + + if (cc < 0) { + if(errno == EINTR) continue; + + /* can't really log the failure; for now, throw out a stderr */ + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + break; + } else { + /* shouldn't happen when writing to file or tty */ + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen); + break; + } + } + + /* if we allocated storage for the iovecs, free it */ + if (vec != stackVec) + free(vec); + UNINDENTED_BLOCK_END + UNINDENTED_BLOCK_END + UNINDENTED_BLOCK_END + UNINDENTED_BLOCK_END +} + + +/* + * Receive a log message. We happen to know that "vector" has three parts: + * + * priority (1 byte) + * tag (N bytes -- null-terminated ASCII string) + * message (N bytes -- null-terminated ASCII string) + */ +static ssize_t logWritev(int fd, const struct iovec* vector, int count) +{ + LogState* state; + + /* Make sure that no-one frees the LogState while we're using it. + * Also guarantees that only one thread is in showLog() at a given + * time (if it matters). + */ + lock(); + + state = fdToLogState(fd); + if (state == NULL) { + errno = EBADF; + goto error; + } + + if (state->isBinary) { + TRACE("%s: ignoring binary log\n", state->debugName); + goto bail; + } + + if (count != 3) { + TRACE("%s: writevLog with count=%d not expected\n", + state->debugName, count); + goto error; + } + + /* pull out the three fields */ + UNINDENTED_BLOCK_START + int logPrio = *(const char*)vector[0].iov_base; + const char* tag = (const char*) vector[1].iov_base; + const char* msg = (const char*) vector[2].iov_base; + + /* see if this log tag is configured */ + int i; + int minPrio = state->globalMinPriority; + for (i = 0; i < kTagSetSize; i++) { + if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN) + break; /* reached end of configured values */ + + if (strcmp(state->tagSet[i].tag, tag) == 0) { + //TRACE("MATCH tag '%s'\n", tag); + minPrio = state->tagSet[i].minPriority; + break; + } + } + + if (logPrio >= minPrio) { + showLog(state, logPrio, tag, msg); + } else { + //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); + } + UNINDENTED_BLOCK_END + +bail: + unlock(); + return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; +error: + unlock(); + return -1; +} + +/* + * Free up our state and close the fake descriptor. + */ +static int logClose(int fd) +{ + deleteFakeFd(fd); + return 0; +} + +/* + * Open a log output device and return a fake fd. + */ +static int logOpen(const char* pathName, int flags) +{ + LogState *logState; + int fd = -1; + + lock(); + + logState = createLogState(); + if (logState != NULL) { + configureInitialState(pathName, logState); + fd = logState->fakeFd; + } else { + errno = ENFILE; + } + + unlock(); + + return fd; +} + + +/* + * Runtime redirection. If this binary is running in the simulator, + * just pass log messages to the emulated device. If it's running + * outside of the simulator, write the log messages to stderr. + */ + +static int (*redirectOpen)(const char *pathName, int flags) = NULL; +static int (*redirectClose)(int fd) = NULL; +static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count) + = NULL; + +static void setRedirects() +{ + const char *ws; + + /* Wrapsim sets this environment variable on children that it's + * created using its LD_PRELOAD wrapper. + */ + ws = getenv("ANDROID_WRAPSIM"); + if (ws != NULL && strcmp(ws, "1") == 0) { + /* We're running inside wrapsim, so we can just write to the device. */ + redirectOpen = (int (*)(const char *pathName, int flags))open; + redirectClose = close; + redirectWritev = writev; + } else { + /* There's no device to delegate to; handle the logging ourselves. */ + redirectOpen = logOpen; + redirectClose = logClose; + redirectWritev = logWritev; + } +} + +int fakeLogOpen(const char *pathName, int flags) +{ + if (redirectOpen == NULL) { + setRedirects(); + } + return redirectOpen(pathName, flags); +} + +int fakeLogClose(int fd) +{ + /* Assume that open() was called first. */ + return redirectClose(fd); +} + +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) +{ + /* Assume that open() was called first. */ + return redirectWritev(fd, vector, count); +} + +#undef UNINDENTED_BLOCK_START +#undef UNINDENTED_BLOCK_END diff --git a/media/libstagefright/system/core/liblog/logd_write.c b/media/libstagefright/system/core/liblog/logd_write.c new file mode 100644 index 000000000..7bdf61e87 --- /dev/null +++ b/media/libstagefright/system/core/liblog/logd_write.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2007 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 <time.h> +#include <stdio.h> +#ifdef HAVE_PTHREADS +#include <pthread.h> +#endif +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <log/logger.h> +#include <log/logd.h> +#include <log/log.h> + +#define LOG_BUF_SIZE 1024 + +#ifdef _MSC_VER +#if _MSC_VER < 1900 +#include <nspr/prprf.h> +#define snprintf PR_snprintf +#endif +#define __builtin_trap abort +static int W_OK = 0; +static int access(char* c, int i) { return -1; } +#endif + +#if FAKE_LOG_DEVICE +int fakeLogOpen(const char *pathName, int flags); +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count); +int fakeLogClose(int fd); + +// This will be defined when building for the host. +#define log_open(pathname, flags) fakeLogOpen(pathname, flags) +#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +#define log_close(filedes) fakeLogClose(filedes) +#else +#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) +#define log_writev(filedes, vector, count) writev(filedes, vector, count) +#define log_close(filedes) close(filedes) +#endif + +static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); +static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init; +#ifdef HAVE_PTHREADS +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... is available, we're running in + * the simulator rather than a desktop tool and want to use the device. + */ +static enum { + kLogUninitialized, kLogNotAvailable, kLogAvailable +} g_log_status = kLogUninitialized; +int __android_log_dev_available(void) +{ + if (g_log_status == kLogUninitialized) { + if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + int log_fd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + log_fd = log_fds[(int)log_id]; + } else { + return EBADF; + } + + do { + ret = log_writev(log_fd, vec, nr); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) +{ +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + + if (write_to_log == __write_to_log_init) { + log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + + if (log_fds[LOG_ID_SYSTEM] < 0) { + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + } + } + +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + + return write_to_log(log_id, vec, nr); +} + +int __android_log_write(int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + log_id_t log_id = LOG_ID_MAIN; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!strcmp(tag, "HTC_RIL") || + !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK") || + !strcmp(tag, "CDMA") || + !strcmp(tag, "PHONE") || + !strcmp(tag, "SMS")) { + log_id = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(log_id, vec, 3); +} + +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if ((bufID != LOG_ID_RADIO) && + (!strcmp(tag, "HTC_RIL") || + !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ + !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK") || + !strcmp(tag, "CDMA") || + !strcmp(tag, "PHONE") || + !strcmp(tag, "SMS"))) { + bufID = LOG_ID_RADIO; + // Inform third party apps/ril/radio.. to use Rlog or RLOG + snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); + tag = tmp_tag; + } + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(bufID, vec, 3); +} + +int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_buf_write(bufID, prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + char buf[LOG_BUF_SIZE]; + + if (fmt) { + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + } else { + /* Msg not provided, log condition. N.B. Do not use cond directly as + * format string as it could contain spurious '%' syntax (e.g. + * "%d" in "blocks%devs == 0"). + */ + if (cond) + snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond); + else + strcpy(buf, "Unspecified assertion failed"); + } + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + +#ifdef _MSC_VER + abort(); +#else + __builtin_trap(); /* trap so we have a chance to debug the situation */ +#endif +} + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +{ + struct iovec vec[2]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = (void*)payload; + vec[1].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 2); +} + +/* + * Like __android_log_bwrite, but takes the type as well. Doesn't work + * for the general case where we're generating lists of stuff, but very + * handy if we just want to dump an integer into the log. + */ +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len) +{ + struct iovec vec[3]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = (void*)payload; + vec[2].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 3); +} diff --git a/media/libstagefright/system/core/liblog/logprint.c b/media/libstagefright/system/core/liblog/logprint.c new file mode 100644 index 000000000..6bdd8e0b3 --- /dev/null +++ b/media/libstagefright/system/core/liblog/logprint.c @@ -0,0 +1,1057 @@ +/* //device/libs/cutils/logprint.c +** +** Copyright 2006, 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 _GNU_SOURCE /* for asprintf */ + +#include <ctype.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <arpa/inet.h> + +#include <log/logd.h> +#include <log/logprint.h> + +#ifdef _MSC_VER +#if _MSC_VER < 1900 +#include <nspr/prprf.h> +#define snprintf PR_snprintf +#endif +#define inline +/* We don't want to indent large blocks because it causes unnecessary merge + * conflicts */ +#define UNINDENTED_BLOCK_START { +#define UNINDENTED_BLOCK_END } +#else +#define UNINDENTED_BLOCK_START +#define UNINDENTED_BLOCK_END +#endif + +#ifdef WIN32 +static char * +strsep(char **stringp, const char *delim) +{ + char* res = *stringp; + while (**stringp) { + const char *c; + for (c = delim; *c; c++) { + if (**stringp == *c) { + **stringp++ = 0; + return res; + } + } + } + return res; +} +#endif + +typedef struct FilterInfo_t { + char *mTag; + android_LogPriority mPri; + struct FilterInfo_t *p_next; +} FilterInfo; + +struct AndroidLogFormat_t { + android_LogPriority global_pri; + FilterInfo *filters; + AndroidLogPrintFormat format; +}; + +static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) +{ + FilterInfo *p_ret; + + p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); + p_ret->mTag = strdup(tag); + p_ret->mPri = pri; + + return p_ret; +} + +static void filterinfo_free(FilterInfo *p_info) +{ + if (p_info == NULL) { + return; + } + + free(p_info->mTag); + p_info->mTag = NULL; +} + +/* + * Note: also accepts 0-9 priorities + * returns ANDROID_LOG_UNKNOWN if the character is unrecognized + */ +static android_LogPriority filterCharToPri (char c) +{ + android_LogPriority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+ANDROID_LOG_SILENT)) { + pri = ANDROID_LOG_VERBOSE; + } else { + pri = (android_LogPriority)(c - '0'); + } + } else if (c == 'v') { + pri = ANDROID_LOG_VERBOSE; + } else if (c == 'd') { + pri = ANDROID_LOG_DEBUG; + } else if (c == 'i') { + pri = ANDROID_LOG_INFO; + } else if (c == 'w') { + pri = ANDROID_LOG_WARN; + } else if (c == 'e') { + pri = ANDROID_LOG_ERROR; + } else if (c == 'f') { + pri = ANDROID_LOG_FATAL; + } else if (c == 's') { + pri = ANDROID_LOG_SILENT; + } else if (c == '*') { + pri = ANDROID_LOG_DEFAULT; + } else { + pri = ANDROID_LOG_UNKNOWN; + } + + return pri; +} + +static char filterPriToChar (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return 'V'; + case ANDROID_LOG_DEBUG: return 'D'; + case ANDROID_LOG_INFO: return 'I'; + case ANDROID_LOG_WARN: return 'W'; + case ANDROID_LOG_ERROR: return 'E'; + case ANDROID_LOG_FATAL: return 'F'; + case ANDROID_LOG_SILENT: return 'S'; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return '?'; + } +} + +static android_LogPriority filterPriForTag( + AndroidLogFormat *p_format, const char *tag) +{ + FilterInfo *p_curFilter; + + for (p_curFilter = p_format->filters + ; p_curFilter != NULL + ; p_curFilter = p_curFilter->p_next + ) { + if (0 == strcmp(tag, p_curFilter->mTag)) { + if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) { + return p_format->global_pri; + } else { + return p_curFilter->mPri; + } + } + } + + return p_format->global_pri; +} + +/** for debugging */ +static void dumpFilters(AndroidLogFormat *p_format) +{ + FilterInfo *p_fi; + + for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { + char cPri = filterPriToChar(p_fi->mPri); + if (p_fi->mPri == ANDROID_LOG_DEFAULT) { + cPri = filterPriToChar(p_format->global_pri); + } + fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); + } + + fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); + +} + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int android_log_shouldPrintLine ( + AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) +{ + return pri >= filterPriForTag(p_format, tag); +} + +AndroidLogFormat *android_log_format_new() +{ + AndroidLogFormat *p_ret; + + p_ret = calloc(1, sizeof(AndroidLogFormat)); + + p_ret->global_pri = ANDROID_LOG_VERBOSE; + p_ret->format = FORMAT_BRIEF; + + return p_ret; +} + +void android_log_format_free(AndroidLogFormat *p_format) +{ + FilterInfo *p_info, *p_info_old; + + p_info = p_format->filters; + + while (p_info != NULL) { + p_info_old = p_info; + p_info = p_info->p_next; + + free(p_info_old); + } + + free(p_format); +} + + + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format) +{ + p_format->format=format; +} + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char * formatString) +{ + static AndroidLogPrintFormat format; + + if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; + else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; + else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; + else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; + else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; + else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; + else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; + else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; + else format = FORMAT_OFF; + + return format; +} + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + */ + +int android_log_addFilterRule(AndroidLogFormat *p_format, + const char *filterExpression) +{ + size_t tagNameLength; + android_LogPriority pri = ANDROID_LOG_DEFAULT; + + tagNameLength = strcspn(filterExpression, ":"); + + if (tagNameLength == 0) { + goto error; + } + + if(filterExpression[tagNameLength] == ':') { + pri = filterCharToPri(filterExpression[tagNameLength+1]); + + if (pri == ANDROID_LOG_UNKNOWN) { + goto error; + } + } + + if(0 == strncmp("*", filterExpression, tagNameLength)) { + // This filter expression refers to the global filter + // The default level for this is DEBUG if the priority + // is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_DEBUG; + } + + p_format->global_pri = pri; + } else { + // for filter expressions that don't refer to the global + // filter, the default is verbose if the priority is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_VERBOSE; + } + + UNINDENTED_BLOCK_START + char *tagName; + +// Presently HAVE_STRNDUP is never defined, so the second case is always taken +// Darwin doesn't have strnup, everything else does +#ifdef HAVE_STRNDUP + tagName = strndup(filterExpression, tagNameLength); +#else + //a few extra bytes copied... + tagName = strdup(filterExpression); + tagName[tagNameLength] = '\0'; +#endif /*HAVE_STRNDUP*/ + + UNINDENTED_BLOCK_START + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; + UNINDENTED_BLOCK_END + UNINDENTED_BLOCK_END + } + + return 0; +error: + return -1; +} + + +/** + * filterString: a comma/whitespace-separated set of filter expressions + * + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterString(AndroidLogFormat *p_format, + const char *filterString) +{ + char *filterStringCopy = strdup (filterString); + char *p_cur = filterStringCopy; + char *p_ret; + int err; + + // Yes, I'm using strsep + while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { + // ignore whitespace-only entries + if(p_ret[0] != '\0') { + err = android_log_addFilterRule(p_format, p_ret); + + if (err < 0) { + goto error; + } + } + } + + free (filterStringCopy); + return 0; +error: + free (filterStringCopy); + return -1; +} + +static inline char * strip_end(char *str) +{ + char *end = str + strlen(str) - 1; + + while (end >= str && isspace(*end)) + *end-- = '\0'; + return str; +} + +/** + * Splits a wire-format buffer into an AndroidLogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int android_log_processLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry) +{ + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * format: <priority:1><tag:N>\0<message:N>\0 + * + * tag str + * starts at buf->msg+1 + * msg + * starts at buf->msg+1+len(tag)+1 + * + * The message may have been truncated by the kernel log driver. + * When that happens, we must null-terminate the message ourselves. + */ + if (buf->len < 3) { + // An well-formed entry must consist of at least a priority + // and two null characters + fprintf(stderr, "+++ LOG: entry too small\n"); + return -1; + } + + UNINDENTED_BLOCK_START + int msgStart = -1; + int msgEnd = -1; + + int i; + for (i = 1; i < buf->len; i++) { + if (buf->msg[i] == '\0') { + if (msgStart == -1) { + msgStart = i + 1; + } else { + msgEnd = i; + break; + } + } + } + + if (msgStart == -1) { + fprintf(stderr, "+++ LOG: malformed log message\n"); + return -1; + } + if (msgEnd == -1) { + // incoming message not null-terminated; force it + msgEnd = buf->len - 1; + buf->msg[msgEnd] = '\0'; + } + + entry->priority = buf->msg[0]; + entry->tag = buf->msg + 1; + entry->message = buf->msg + msgStart; + entry->messageLen = msgEnd - msgStart; + + return 0; + UNINDENTED_BLOCK_END +} + +/* + * Extract a 4-byte value from a byte stream. + */ +static inline uint32_t get4LE(const uint8_t* src) +{ + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +/* + * Extract an 8-byte value from a byte stream. + */ +static inline uint64_t get8LE(const uint8_t* src) +{ + uint32_t low, high; + + low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); + return ((long long) high << 32) | (long long) low; +} + + +/* + * Recursively convert binary log data to printable form. + * + * This needs to be recursive because you can have lists of lists. + * + * If we run out of room, we stop processing immediately. It's important + * for us to check for space on every output element to avoid producing + * garbled output. + * + * Returns 0 on success, 1 on buffer full, -1 on failure. + */ +static int android_log_printBinaryEvent(const unsigned char** pEventData, + size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) +{ + const unsigned char* eventData = *pEventData; + size_t eventDataLen = *pEventDataLen; + char* outBuf = *pOutBuf; + size_t outBufLen = *pOutBufLen; + unsigned char type; + size_t outCount; + int result = 0; + + if (eventDataLen < 1) + return -1; + type = *eventData++; + eventDataLen--; + + //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); + + switch (type) { + case EVENT_TYPE_INT: + /* 32-bit signed int */ + { + int ival; + + if (eventDataLen < 4) + return -1; + ival = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + outCount = snprintf(outBuf, outBufLen, "%d", ival); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_LONG: + /* 64-bit signed long */ + { + long long lval; + + if (eventDataLen < 8) + return -1; + lval = get8LE(eventData); + eventData += 8; + eventDataLen -= 8; + + outCount = snprintf(outBuf, outBufLen, "%lld", lval); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_STRING: + /* UTF-8 chars, not NULL-terminated */ + { + unsigned int strLen; + + if (eventDataLen < 4) + return -1; + strLen = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + if (eventDataLen < strLen) + return -1; + + if (strLen < outBufLen) { + memcpy(outBuf, eventData, strLen); + outBuf += strLen; + outBufLen -= strLen; + } else if (outBufLen > 0) { + /* copy what we can */ + memcpy(outBuf, eventData, outBufLen); + outBuf += outBufLen; + outBufLen -= outBufLen; + goto no_room; + } + eventData += strLen; + eventDataLen -= strLen; + break; + } + case EVENT_TYPE_LIST: + /* N items, all different types */ + { + unsigned char count; + int i; + + if (eventDataLen < 1) + return -1; + + count = *eventData++; + eventDataLen--; + + if (outBufLen > 0) { + *outBuf++ = '['; + outBufLen--; + } else { + goto no_room; + } + + for (i = 0; i < count; i++) { + result = android_log_printBinaryEvent(&eventData, &eventDataLen, + &outBuf, &outBufLen); + if (result != 0) + goto bail; + + if (i < count-1) { + if (outBufLen > 0) { + *outBuf++ = ','; + outBufLen--; + } else { + goto no_room; + } + } + } + + if (outBufLen > 0) { + *outBuf++ = ']'; + outBufLen--; + } else { + goto no_room; + } + } + break; + default: + fprintf(stderr, "Unknown binary event type %d\n", type); + return -1; + } + +bail: + *pEventData = eventData; + *pEventDataLen = eventDataLen; + *pOutBuf = outBuf; + *pOutBufLen = outBufLen; + return result; + +no_room: + result = 1; + goto bail; +} + +/** + * Convert a binary log entry to ASCII form. + * + * For convenience we mimic the processLogBuffer API. There is no + * pre-defined output length for the binary data, since we're free to format + * it however we choose, which means we can't really use a fixed-size buffer + * here. + */ +int android_log_processBinaryLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, + int messageBufLen) +{ + size_t inCount; + unsigned int tagIndex; + const unsigned char* eventData; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = ANDROID_LOG_INFO; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * Pull the tag out. + */ + eventData = (const unsigned char*) buf->msg; + inCount = buf->len; + if (inCount < 4) + return -1; + tagIndex = get4LE(eventData); + eventData += 4; + inCount -= 4; + + entry->tag = NULL; + + /* + * If we don't have a map, or didn't find the tag number in the map, + * stuff a generated tag value into the start of the output buffer and + * shift the buffer pointers down. + */ + if (entry->tag == NULL) { + int tagLen; + + tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); + entry->tag = messageBuf; + messageBuf += tagLen+1; + messageBufLen -= tagLen+1; + } + + /* + * Format the event log data into the buffer. + */ + UNINDENTED_BLOCK_START + char* outBuf = messageBuf; + size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ + int result; + result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, + &outRemaining); + if (result < 0) { + fprintf(stderr, "Binary log entry conversion failed\n"); + return -1; + } else if (result == 1) { + if (outBuf > messageBuf) { + /* leave an indicator */ + *(outBuf-1) = '!'; + } else { + /* no room to output anything at all */ + *outBuf++ = '!'; + outRemaining--; + } + /* pretend we ate all the data */ + inCount = 0; + } + + /* eat the silly terminating '\n' */ + if (inCount == 1 && *eventData == '\n') { + eventData++; + inCount--; + } + + if (inCount != 0) { + fprintf(stderr, + "Warning: leftover binary log data (%zu bytes)\n", inCount); + } + + /* + * Terminate the buffer. The NUL byte does not count as part of + * entry->messageLen. + */ + *outBuf = '\0'; + entry->messageLen = outBuf - messageBuf; + assert(entry->messageLen == (messageBufLen-1) - outRemaining); + + entry->message = messageBuf; + + return 0; + UNINDENTED_BLOCK_END +} + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *android_log_formatLogLine ( + AndroidLogFormat *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const AndroidLogEntry *entry, + size_t *p_outLength) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + int prefixSuffixIsHeaderFooter = 0; + char * ret = NULL; + + priChar = filterPriToChar(entry->priority); + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&(entry->tv_sec), &tmBuf); +#else + ptm = localtime(&(entry->tv_sec)); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + UNINDENTED_BLOCK_START + size_t prefixLen, suffixLen; + + switch (p_format->format) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, entry->tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, entry->pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", entry->tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; + prefixLen = 0; + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, + priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + entry->pid, entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s.%03ld %5d:%5d %c/%-8s ]\n", + timeBuf, entry->tv_nsec / 1000000, entry->pid, + entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n\n"); + suffixLen = 2; + prefixSuffixIsHeaderFooter = 1; + break; + case FORMAT_BRIEF: + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + } + /* snprintf has a weird return value. It returns what would have been + * written given a large enough buffer. In the case that the prefix is + * longer then our buffer(128), it messes up the calculations below + * possibly causing heap corruption. To avoid this we double check and + * set the length at the maximum (size minus null byte) + */ + if(prefixLen >= sizeof(prefixBuf)) + prefixLen = sizeof(prefixBuf) - 1; + if(suffixLen >= sizeof(suffixBuf)) + suffixLen = sizeof(suffixBuf) - 1; + + /* the following code is tragically unreadable */ + + UNINDENTED_BLOCK_START + size_t numLines; + char *p; + size_t bufferSize; + const char *pm; + + if (prefixSuffixIsHeaderFooter) { + // we're just wrapping message with a header/footer + numLines = 1; + } else { + pm = entry->message; + numLines = 0; + + // The line-end finding here must match the line-end finding + // in for ( ... numLines...) loop below + while (pm < (entry->message + entry->messageLen)) { + if (*pm++ == '\n') numLines++; + } + // plus one line for anything not newline-terminated at the end + if (pm > entry->message && *(pm-1) != '\n') numLines++; + } + + // this is an upper bound--newlines in message may be counted + // extraneously + bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + + if (defaultBufferSize >= bufferSize) { + ret = defaultBuffer; + } else { + ret = (char *)malloc(bufferSize); + + if (ret == NULL) { + return ret; + } + } + + ret[0] = '\0'; /* to start strcat off */ + + p = ret; + pm = entry->message; + + if (prefixSuffixIsHeaderFooter) { + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + strcat(p, suffixBuf); + p += suffixLen; + } else { + while(pm < (entry->message + entry->messageLen)) { + const char *lineStart; + size_t lineLen; + lineStart = pm; + + // Find the next end-of-line in message + while (pm < (entry->message + entry->messageLen) + && *pm != '\n') pm++; + lineLen = pm - lineStart; + + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, lineStart, lineLen); + p += lineLen; + strcat(p, suffixBuf); + p += suffixLen; + + if (*pm == '\n') pm++; + } + } + + if (p_outLength != NULL) { + *p_outLength = p - ret; + } + + return ret; + UNINDENTED_BLOCK_END + UNINDENTED_BLOCK_END +} + +/** + * Either print or do not print log line, based on filter + * + * Returns count bytes written + */ + +int android_log_printLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry) +{ + int ret; + char defaultBuffer[512]; + char *outBuffer = NULL; + size_t totalLen; + + outBuffer = android_log_formatLogLine(p_format, defaultBuffer, + sizeof(defaultBuffer), entry, &totalLen); + + if (!outBuffer) + return -1; + + do { + ret = write(fd, outBuffer, totalLen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + ret = 0; + goto done; + } + + if (((size_t)ret) < totalLen) { + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, + (int)totalLen); + goto done; + } + +done: + if (outBuffer != defaultBuffer) { + free(outBuffer); + } + + return ret; +} + + + +void logprint_run_tests() +{ +#if 0 + + fprintf(stderr, "tests disabled\n"); + +#else + + int err; + const char *tag; + AndroidLogFormat *p_format; + + p_format = android_log_format_new(); + + fprintf(stderr, "running tests\n"); + + tag = "random"; + + android_log_addFilterRule(p_format,"*:i"); + + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "random"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); + assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + err = android_log_addFilterRule(p_format, "random:z"); + assert (err < 0); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + err = android_log_addFilterString(p_format, " "); + assert(err == 0); + assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + + // note trailing space + err = android_log_addFilterString(p_format, "*:s random:d "); + assert(err == 0); + assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + + err = android_log_addFilterString(p_format, "*:s random:z"); + assert(err < 0); + + +#if 0 + char *ret; + char defaultBuffer[512]; + + ret = android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); +#endif + + + fprintf(stderr, "tests complete\n"); +#endif +} + +#undef UNINDENTED_BLOCK_START +#undef UNINDENTED_BLOCK_END diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h new file mode 100644 index 000000000..98f2190ac --- /dev/null +++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Errors.h @@ -0,0 +1,48 @@ +/* + * Copyright 2007 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. + */ + +#ifndef ANDROID_PIXELFLINGER_ERRORS_H +#define ANDROID_PIXELFLINGER_ERRORS_H + +#include <sys/types.h> +#include <errno.h> + +namespace stagefright { +namespace tinyutils { + +// use this type to return error codes +typedef int32_t status_t; + +/* + * Error codes. + * All error codes are negative values. + */ + +enum { + NO_ERROR = 0, // No errors. + NO_MEMORY = -ENOMEM, + BAD_VALUE = -EINVAL, + BAD_INDEX = -EOVERFLOW, + NAME_NOT_FOUND = -ENOENT, +}; + + +} // namespace tinyutils +} // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_ERRORS_H diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h new file mode 100644 index 000000000..62fc7604b --- /dev/null +++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/KeyedVector.h @@ -0,0 +1,203 @@ +/* + * Copyright 2005 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. + */ + +#ifndef ANDROID_PIXELFLINGER_KEYED_VECTOR_H +#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include "Errors.h" +#include "SortedVector.h" +#include "TypeHelpers.h" + +// --------------------------------------------------------------------------- + +namespace stagefright { +namespace tinyutils { + +template <typename KEY, typename VALUE> +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t<KEY, VALUE> > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template <typename KEY, typename VALUE> +class DefaultKeyedVector : public KeyedVector<KEY, VALUE> +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +KeyedVector<KEY,VALUE>::KeyedVector() +{ +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) ); +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template<typename KEY, typename VALUE> inline +VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) ); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t<KEY,VALUE> pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) { + if (index<size()) { + mVector.editValueAt(index).value = item; + return index; + } + return BAD_INDEX; +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t<KEY,VALUE>(key)); +} + +template<typename KEY, typename VALUE> inline +ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<typename KEY, typename VALUE> inline +DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template<typename KEY, typename VALUE> inline +const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault; +} + +} // namespace tinyutils +} // namespace stagefright + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h new file mode 100644 index 000000000..71026c8df --- /dev/null +++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/SortedVector.h @@ -0,0 +1,284 @@ +/* + * Copyright 2005 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. + */ + +#ifndef ANDROID_PIXELFLINGER_SORTED_VECTOR_H +#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +#include "Vector.h" +#include "VectorImpl.h" +#include "TypeHelpers.h" + +// --------------------------------------------------------------------------- + +namespace stagefright { +namespace tinyutils { + +template <class TYPE> +class SortedVector : private SortedVectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector<TYPE>& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + const SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs) const; + SortedVector<TYPE>& operator = (const SortedVector<TYPE>& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you ust keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector<TYPE>& vector); + ssize_t merge(const SortedVector<TYPE>& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template<class TYPE> inline +SortedVector<TYPE>::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template<class TYPE> inline +SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs) + : SortedVectorImpl(rhs) { +} + +template<class TYPE> inline +SortedVector<TYPE>::~SortedVector() { + finish_vector(); +} + +template<class TYPE> inline +SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const TYPE* SortedVector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); +} + +template<class TYPE> inline +TYPE* SortedVector<TYPE>::editArray() { + return static_cast<TYPE *>(editArrayImpl()); +} + + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::operator[](size_t index) const { + assert( index<size() ); + return *(array() + index); +} + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index)<size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template<class TYPE> inline +const TYPE& SortedVector<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template<class TYPE> inline +size_t SortedVector<TYPE>::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) { + return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) { + return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template<class TYPE> inline +ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<class TYPE> +void SortedVector<TYPE>::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) ); +} + +} // namespace tinyutils +} // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h new file mode 100644 index 000000000..3fe87a248 --- /dev/null +++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/Vector.h @@ -0,0 +1,353 @@ +/* + * Copyright 2005 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. + */ + +#ifndef ANDROID_PIXELFLINGER_VECTOR_H +#define ANDROID_PIXELFLINGER_VECTOR_H + +#include <new> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/log.h> + +#include "Errors.h" +#include "VectorImpl.h" +#include "TypeHelpers.h" + +// --------------------------------------------------------------------------- + +namespace stagefright { +namespace tinyutils { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template <class TYPE> +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector<TYPE>& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector<TYPE>& operator = (const Vector<TYPE>& rhs) const; + Vector<TYPE>& operator = (const Vector<TYPE>& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector<TYPE>& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector<TYPE>& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template<class TYPE> inline +Vector<TYPE>::Vector() + : VectorImpl(sizeof(TYPE), + ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template<class TYPE> inline +Vector<TYPE>::Vector(const Vector<TYPE>& rhs) + : VectorImpl(rhs) { +} + +template<class TYPE> inline +Vector<TYPE>::~Vector() { + finish_vector(); +} + +template<class TYPE> inline +Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template<class TYPE> inline +const TYPE* Vector<TYPE>::array() const { + return static_cast<const TYPE *>(arrayImpl()); +} + +template<class TYPE> inline +TYPE* Vector<TYPE>::editArray() { + return static_cast<TYPE *>(editArrayImpl()); +} + + +template<class TYPE> inline +const TYPE& Vector<TYPE>::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template<class TYPE> inline +const TYPE& Vector<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editItemAt(size_t index) { + return *( static_cast<TYPE *>(editItemLocation(index)) ); +} + +template<class TYPE> inline +TYPE& Vector<TYPE>::editTop() { + return *( static_cast<TYPE *>(editItemLocation(size()-1)) ); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) { + return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector)); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template<class TYPE> inline +void Vector<TYPE>::pop() { + VectorImpl::pop(); +} + +template<class TYPE> inline +void Vector<TYPE>::push() { + VectorImpl::push(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::add() { + return VectorImpl::add(); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template<class TYPE> inline +ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template<class TYPE> +void Vector<TYPE>::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast<TYPE*>(storage), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +template<class TYPE> +void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num ); +} + +} // namespace tinyutils +} // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_VECTOR_H diff --git a/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h new file mode 100644 index 000000000..6cc55c49a --- /dev/null +++ b/media/libstagefright/system/core/libpixelflinger/codeflinger/tinyutils/VectorImpl.h @@ -0,0 +1,195 @@ +/* + * Copyright 2005 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. + */ + +#ifndef ANDROID_PIXELFLINGER_VECTOR_IMPL_H +#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H + +#include <assert.h> +#include <stdint.h> +#include <sys/types.h> + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace stagefright { +namespace tinyutils { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +} // namespace tinyutils +} // namespace stagefright + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H diff --git a/media/libstagefright/system/core/libutils/RefBase.cpp b/media/libstagefright/system/core/libutils/RefBase.cpp new file mode 100644 index 000000000..259b0a4e6 --- /dev/null +++ b/media/libstagefright/system/core/libutils/RefBase.cpp @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2005 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 "RefBase" +// #define LOG_NDEBUG 0 + +#include <utils/RefBase.h> + +#include <utils/Atomic.h> +#ifdef _MSC_VER +class CallStack { +public: + CallStack(int x) {} +}; +#else +#include <utils/CallStack.h> +#endif +#include <utils/Log.h> +#include <utils/threads.h> + +#include <stdlib.h> +#include <stdio.h> +#include <typeinfo> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +// compile with refcounting debugging enabled +#define DEBUG_REFS 0 + +// whether ref-tracking is enabled by default, if not, trackMe(true, false) +// needs to be called explicitly +#define DEBUG_REFS_ENABLED_BY_DEFAULT 0 + +// whether callstack are collected (significantly slows things down) +#define DEBUG_REFS_CALLSTACK_ENABLED 0 + +// folder where stack traces are saved when DEBUG_REFS is enabled +// this folder needs to exist and be writable +#define DEBUG_REFS_CALLSTACK_PATH "/data/debug" + +// log all reference counting operations +#define PRINT_REFS 0 + +// --------------------------------------------------------------------------- + +namespace stagefright { + +#define INITIAL_STRONG_VALUE (1<<28) + +// --------------------------------------------------------------------------- + +class RefBase::weakref_impl : public RefBase::weakref_type +{ +public: + volatile int32_t mStrong; + volatile int32_t mWeak; + RefBase* const mBase; + volatile int32_t mFlags; + +#if !DEBUG_REFS + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + { + } + + void addStrongRef(const void* /*id*/) { } + void removeStrongRef(const void* /*id*/) { } + void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } + void addWeakRef(const void* /*id*/) { } + void removeWeakRef(const void* /*id*/) { } + void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } + void printRefs() const { } + void trackMe(bool, bool) { } + +#else + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + , mStrongRefs(NULL) + , mWeakRefs(NULL) + , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) + , mRetain(false) + { + } + + ~weakref_impl() + { + bool dumpStack = false; + if (!mRetain && mStrongRefs != NULL) { + dumpStack = true; + ALOGE("Strong references remain:"); + ref_entry* refs = mStrongRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(LOG_TAG); +#endif + refs = refs->next; + } + } + + if (!mRetain && mWeakRefs != NULL) { + dumpStack = true; + ALOGE("Weak references remain!"); + ref_entry* refs = mWeakRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(LOG_TAG); +#endif + refs = refs->next; + } + } + if (dumpStack) { + ALOGE("above errors at:"); + CallStack stack(LOG_TAG); + } + } + + void addStrongRef(const void* id) { + //ALOGD_IF(mTrackEnabled, + // "addStrongRef: RefBase=%p, id=%p", mBase, id); + addRef(&mStrongRefs, id, mStrong); + } + + void removeStrongRef(const void* id) { + //ALOGD_IF(mTrackEnabled, + // "removeStrongRef: RefBase=%p, id=%p", mBase, id); + if (!mRetain) { + removeRef(&mStrongRefs, id); + } else { + addRef(&mStrongRefs, id, -mStrong); + } + } + + void renameStrongRefId(const void* old_id, const void* new_id) { + //ALOGD_IF(mTrackEnabled, + // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", + // mBase, old_id, new_id); + renameRefsId(mStrongRefs, old_id, new_id); + } + + void addWeakRef(const void* id) { + addRef(&mWeakRefs, id, mWeak); + } + + void removeWeakRef(const void* id) { + if (!mRetain) { + removeRef(&mWeakRefs, id); + } else { + addRef(&mWeakRefs, id, -mWeak); + } + } + + void renameWeakRefId(const void* old_id, const void* new_id) { + renameRefsId(mWeakRefs, old_id, new_id); + } + + void trackMe(bool track, bool retain) + { + mTrackEnabled = track; + mRetain = retain; + } + + void printRefs() const + { + String8 text; + + { + Mutex::Autolock _l(mMutex); + char buf[128]; + sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mStrongRefs); + sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mWeakRefs); + } + + { + char name[100]; + snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644); + if (rc >= 0) { + write(rc, text.string(), text.length()); + close(rc); + ALOGD("STACK TRACE for %p saved in %s", this, name); + } + else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, + name, strerror(errno)); + } + } + +private: + struct ref_entry + { + ref_entry* next; + const void* id; +#if DEBUG_REFS_CALLSTACK_ENABLED + CallStack stack; +#endif + int32_t ref; + }; + + void addRef(ref_entry** refs, const void* id, int32_t mRef) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + + ref_entry* ref = new ref_entry; + // Reference count at the time of the snapshot, but before the + // update. Positive value means we increment, negative--we + // decrement the reference count. + ref->ref = mRef; + ref->id = id; +#if DEBUG_REFS_CALLSTACK_ENABLED + ref->stack.update(2); +#endif + ref->next = *refs; + *refs = ref; + } + } + + void removeRef(ref_entry** refs, const void* id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + + ref_entry* const head = *refs; + ref_entry* ref = head; + while (ref != NULL) { + if (ref->id == id) { + *refs = ref->next; + delete ref; + return; + } + refs = &ref->next; + ref = *refs; + } + + ALOGE("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); + + ref = head; + while (ref) { + char inc = ref->ref >= 0 ? '+' : '-'; + ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ref = ref->next; + } + + CallStack stack(LOG_TAG); + } + } + + void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = r; + while (ref != NULL) { + if (ref->id == old_id) { + ref->id = new_id; + } + ref = ref->next; + } + } + } + + void printRefsLocked(String8* out, const ref_entry* refs) const + { + char buf[128]; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + sprintf(buf, "\t%c ID %p (ref %d):\n", + inc, refs->id, refs->ref); + out->append(buf); +#if DEBUG_REFS_CALLSTACK_ENABLED + out->append(refs->stack.toString("\t\t")); +#else + out->append("\t\t(call stacks disabled)"); +#endif + refs = refs->next; + } + } + + mutable Mutex mMutex; + ref_entry* mStrongRefs; + ref_entry* mWeakRefs; + + bool mTrackEnabled; + // Collect stack traces on addref and removeref, instead of deleting the stack references + // on removeref that match the address ones. + bool mRetain; + +#endif +}; + +// --------------------------------------------------------------------------- + +void RefBase::incStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); +#if PRINT_REFS + ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + if (c != INITIAL_STRONG_VALUE) { + return; + } + + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + refs->mBase->onFirstRef(); +} + +void RefBase::decStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->removeStrongRef(id); + const int32_t c = android_atomic_dec(&refs->mStrong); +#if PRINT_REFS + ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + if (c == 1) { + refs->mBase->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { + delete this; + } + } + refs->decWeak(id); +} + +void RefBase::forceIncStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + refs); +#if PRINT_REFS + ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + + switch (c) { + case INITIAL_STRONG_VALUE: + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + // fall through... + case 0: + refs->mBase->onFirstRef(); + } +} + +int32_t RefBase::getStrongCount() const +{ + return mRefs->mStrong; +} + +RefBase* RefBase::weakref_type::refBase() const +{ + return static_cast<const weakref_impl*>(this)->mBase; +} + +void RefBase::weakref_type::incWeak(const void* id) +{ + weakref_impl* const impl = static_cast<weakref_impl*>(this); + impl->addWeakRef(id); + const int32_t c = android_atomic_inc(&impl->mWeak); + ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); +} + + +void RefBase::weakref_type::decWeak(const void* id) +{ + weakref_impl* const impl = static_cast<weakref_impl*>(this); + impl->removeWeakRef(id); + const int32_t c = android_atomic_dec(&impl->mWeak); + ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + if (c != 1) return; + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { + // This is the regular lifetime case. The object is destroyed + // when the last strong reference goes away. Since weakref_impl + // outlive the object, it is not destroyed in the dtor, and + // we'll have to do it here. + if (impl->mStrong == INITIAL_STRONG_VALUE) { + // Special case: we never had a strong reference, so we need to + // destroy the object now. + delete impl->mBase; + } else { + // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + delete impl; + } + } else { + // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER} + impl->mBase->onLastWeakRef(id); + if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { + // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference + // is gone, we can destroy the object. + delete impl->mBase; + } + } +} + +bool RefBase::weakref_type::attemptIncStrong(const void* id) +{ + incWeak(id); + + weakref_impl* const impl = static_cast<weakref_impl*>(this); + int32_t curCount = impl->mStrong; + + ALOG_ASSERT(curCount >= 0, + "attemptIncStrong called on %p after underflow", this); + + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + // we're in the easy/common case of promoting a weak-reference + // from an existing strong reference. + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { + break; + } + // the strong count has changed on us, we need to re-assert our + // situation. + curCount = impl->mStrong; + } + + if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { + // we're now in the harder case of either: + // - there never was a strong reference on us + // - or, all strong references have been released + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { + // this object has a "normal" life-time, i.e.: it gets destroyed + // when the last strong reference goes away + if (curCount <= 0) { + // the last strong-reference got released, the object cannot + // be revived. + decWeak(id); + return false; + } + + // here, curCount == INITIAL_STRONG_VALUE, which means + // there never was a strong-reference, so we can try to + // promote this object; we need to do that atomically. + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount + 1, + &impl->mStrong) == 0) { + break; + } + // the strong count has changed on us, we need to re-assert our + // situation (e.g.: another thread has inc/decStrong'ed us) + curCount = impl->mStrong; + } + + if (curCount <= 0) { + // promote() failed, some other thread destroyed us in the + // meantime (i.e.: strong count reached zero). + decWeak(id); + return false; + } + } else { + // this object has an "extended" life-time, i.e.: it can be + // revived from a weak-reference only. + // Ask the object's implementation if it agrees to be revived + if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { + // it didn't so give-up. + decWeak(id); + return false; + } + // grab a strong-reference, which is always safe due to the + // extended life-time. + curCount = android_atomic_inc(&impl->mStrong); + } + + // If the strong reference count has already been incremented by + // someone else, the implementor of onIncStrongAttempted() is holding + // an unneeded reference. So call onLastStrongRef() here to remove it. + // (No, this is not pretty.) Note that we MUST NOT do this if we + // are in fact acquiring the first reference. + if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { + impl->mBase->onLastStrongRef(id); + } + } + + impl->addStrongRef(id); + +#if PRINT_REFS + ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); +#endif + + // now we need to fix-up the count if it was INITIAL_STRONG_VALUE + // this must be done safely, i.e.: handle the case where several threads + // were here in attemptIncStrong(). + curCount = impl->mStrong; + while (curCount >= INITIAL_STRONG_VALUE) { + ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE, + "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE", + this); + if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, + &impl->mStrong) == 0) { + break; + } + // the strong-count changed on us, we need to re-assert the situation, + // for e.g.: it's possible the fix-up happened in another thread. + curCount = impl->mStrong; + } + + return true; +} + +bool RefBase::weakref_type::attemptIncWeak(const void* id) +{ + weakref_impl* const impl = static_cast<weakref_impl*>(this); + + int32_t curCount = impl->mWeak; + ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", + this); + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { + break; + } + curCount = impl->mWeak; + } + + if (curCount > 0) { + impl->addWeakRef(id); + } + + return curCount > 0; +} + +int32_t RefBase::weakref_type::getWeakCount() const +{ + return static_cast<const weakref_impl*>(this)->mWeak; +} + +void RefBase::weakref_type::printRefs() const +{ + static_cast<const weakref_impl*>(this)->printRefs(); +} + +void RefBase::weakref_type::trackMe(bool enable, bool retain) +{ + static_cast<weakref_impl*>(this)->trackMe(enable, retain); +} + +RefBase::weakref_type* RefBase::createWeak(const void* id) const +{ + mRefs->incWeak(id); + return mRefs; +} + +RefBase::weakref_type* RefBase::getWeakRefs() const +{ + return mRefs; +} + +RefBase::RefBase() + : mRefs(new weakref_impl(this)) +{ +} + +RefBase::~RefBase() +{ + if (mRefs->mStrong == INITIAL_STRONG_VALUE) { + // we never acquired a strong (and/or weak) reference on this object. + delete mRefs; + } else { + // life-time of this object is extended to WEAK or FOREVER, in + // which case weakref_impl doesn't out-live the object and we + // can free it now. + if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { + // It's possible that the weak count is not 0 if the object + // re-acquired a weak reference in its destructor + if (mRefs->mWeak == 0) { + delete mRefs; + } + } + } + // for debugging purposes, clear this. + const_cast<weakref_impl*&>(mRefs) = NULL; +} + +void RefBase::extendObjectLifetime(int32_t mode) +{ + android_atomic_or(mode, &mRefs->mFlags); +} + +void RefBase::onFirstRef() +{ +} + +void RefBase::onLastStrongRef(const void* /*id*/) +{ +} + +bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return (flags&FIRST_INC_STRONG) ? true : false; +} + +void RefBase::onLastWeakRef(const void* /*id*/) +{ +} + +// --------------------------------------------------------------------------- + +void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { +#if DEBUG_REFS + for (size_t i=0 ; i<n ; i++) { + renamer(i); + } +#endif +} + +void RefBase::renameRefId(weakref_type* ref, + const void* old_id, const void* new_id) { + weakref_impl* const impl = static_cast<weakref_impl*>(ref); + impl->renameStrongRefId(old_id, new_id); + impl->renameWeakRefId(old_id, new_id); +} + +void RefBase::renameRefId(RefBase* ref, + const void* old_id, const void* new_id) { + ref->mRefs->renameStrongRefId(old_id, new_id); + ref->mRefs->renameWeakRefId(old_id, new_id); +} + +}; // namespace stagefright diff --git a/media/libstagefright/system/core/libutils/SharedBuffer.cpp b/media/libstagefright/system/core/libutils/SharedBuffer.cpp new file mode 100644 index 000000000..7aefe80bd --- /dev/null +++ b/media/libstagefright/system/core/libutils/SharedBuffer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005 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 <stdlib.h> +#include <string.h> + +#include <utils/SharedBuffer.h> +#include <utils/Atomic.h> + +// --------------------------------------------------------------------------- + +namespace stagefright { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size)); + if (sb) { + sb->mRefs = 1; + sb->mSize = size; + } + return sb; +} + + +ssize_t SharedBuffer::dealloc(const SharedBuffer* released) +{ + if (released->mRefs != 0) return -1; // XXX: invalid operation + free(const_cast<SharedBuffer*>(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast<SharedBuffer*>(this); + } + SharedBuffer* sb = alloc(mSize); + if (sb) { + memcpy(sb->data(), data(), size()); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::editResize(size_t newSize) const +{ + if (onlyOwner()) { + SharedBuffer* buf = const_cast<SharedBuffer*>(this); + if (buf->mSize == newSize) return buf; + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); + if (buf != NULL) { + buf->mSize = newSize; + return buf; + } + } + SharedBuffer* sb = alloc(newSize); + if (sb) { + const size_t mySize = mSize; + memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::attemptEdit() const +{ + if (onlyOwner()) { + return const_cast<SharedBuffer*>(this); + } + return 0; +} + +SharedBuffer* SharedBuffer::reset(size_t new_size) const +{ + // cheap-o-reset. + SharedBuffer* sb = alloc(new_size); + if (sb) { + release(); + } + return sb; +} + +void SharedBuffer::acquire() const { + android_atomic_inc(&mRefs); +} + +int32_t SharedBuffer::release(uint32_t flags) const +{ + int32_t prev = 1; + if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { + mRefs = 0; + if ((flags & eKeepStorage) == 0) { + free(const_cast<SharedBuffer*>(this)); + } + } + return prev; +} + + +}; // namespace stagefright diff --git a/media/libstagefright/system/core/libutils/Static.cpp b/media/libstagefright/system/core/libutils/Static.cpp new file mode 100644 index 000000000..476240f4f --- /dev/null +++ b/media/libstagefright/system/core/libutils/Static.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 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. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +namespace stagefright { + +// For String8.cpp +extern void initialize_string8(); +extern void terminate_string8(); + +// For String16.cpp +extern void initialize_string16(); +extern void terminate_string16(); + +class LibUtilsFirstStatics +{ +public: + LibUtilsFirstStatics() + { + initialize_string8(); + initialize_string16(); + } + + ~LibUtilsFirstStatics() + { + terminate_string16(); + terminate_string8(); + } +}; + +static LibUtilsFirstStatics gFirstStatics; +int gDarwinCantLoadAllObjects = 1; + +} // namespace stagefright diff --git a/media/libstagefright/system/core/libutils/String16.cpp b/media/libstagefright/system/core/libutils/String16.cpp new file mode 100644 index 000000000..998cb9482 --- /dev/null +++ b/media/libstagefright/system/core/libutils/String16.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2005 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/String16.h> + +#include <utils/Debug.h> +#include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/String8.h> +#include <utils/threads.h> + +#include <memory.h> +#include <stdio.h> +#include <ctype.h> + + +namespace stagefright { + +static SharedBuffer* gEmptyStringBuf = NULL; +static char16_t* gEmptyString = NULL; + +static inline char16_t* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string16() +{ + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); + char16_t* str = (char16_t*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string16() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) +{ + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); + } + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); + if (buf) { + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); + + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); + //printHexData(1, str, buf->size(), 16, 1); + //printf("\n"); + + return u16str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String16::String16() + : mString(getEmptyString()) +{ +} + +String16::String16(StaticLinkage) + : mString(0) +{ + // this constructor is used when we can't rely on the static-initializers + // having run. In this case we always allocate an empty string. It's less + // efficient than using getEmptyString(), but we assume it's uncommon. + + char16_t* data = static_cast<char16_t*>( + SharedBuffer::alloc(sizeof(char16_t))->data()); + data[0] = 0; + mString = data; +} + +String16::String16(const String16& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String16::String16(const String16& o, size_t len, size_t begin) + : mString(getEmptyString()) +{ + setTo(o, len, begin); +} + +String16::String16(const char16_t* o) +{ + size_t len = strlen16(o); + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + strcpy16(str, o); + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const char16_t* o, size_t len) +{ + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, o, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const String8& o) + : mString(allocFromUTF8(o.string(), o.size())) +{ +} + +String16::String16(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ +} + +String16::String16(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ +} + +String16::~String16() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String16::setTo(const String16& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String16::setTo(const String16& other, size_t len, size_t begin) +{ + const size_t N = other.size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + setTo(other); + return NO_ERROR; + } + + if (&other == this) { + LOG_ALWAYS_FATAL("Not implemented"); + } + + return setTo(other.string()+begin, len); +} + +status_t String16::setTo(const char16_t* other) +{ + return setTo(other, strlen16(other)); +} + +status_t String16::setTo(const char16_t* other, size_t len) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memmove(str, other, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const String16& other) +{ + const size_t myLen = size(); + const size_t otherLen = other.size(); + if (myLen == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const char16_t* chrs, size_t otherLen) +{ + const size_t myLen = size(); + if (myLen == 0) { + setTo(chrs, otherLen); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); + str[myLen+otherLen] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::insert(size_t pos, const char16_t* chrs) +{ + return insert(pos, chrs, strlen16(chrs)); +} + +status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) +{ + const size_t myLen = size(); + if (myLen == 0) { + return setTo(chrs, len); + return NO_ERROR; + } else if (len == 0) { + return NO_ERROR; + } + + if (pos > myLen) pos = myLen; + + #if 0 + printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", + String8(*this).string(), pos, + len, myLen, String8(chrs, len).string()); + #endif + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + if (pos < myLen) { + memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); + } + memcpy(str+pos, chrs, len*sizeof(char16_t)); + str[myLen+len] = 0; + mString = str; + #if 0 + printf("Result (%d chrs): %s\n", size(), String8(*this).string()); + #endif + return NO_ERROR; + } + return NO_MEMORY; +} + +ssize_t String16::findFirst(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + if (*p == c) { + return p-str; + } + p++; + } + return -1; +} + +ssize_t String16::findLast(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + e--; + if (*e == c) { + return e-str; + } + } + return -1; +} + +bool String16::startsWith(const String16& prefix) const +{ + const size_t ps = prefix.size(); + if (ps > size()) return false; + return strzcmp16(mString, ps, prefix.string(), ps) == 0; +} + +bool String16::startsWith(const char16_t* prefix) const +{ + const size_t ps = strlen16(prefix); + if (ps > size()) return false; + return strncmp16(mString, prefix, ps) == 0; +} + +status_t String16::makeLower() +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; i<N; i++) { + const char16_t v = str[i]; + if (v >= 'A' && v <= 'Z') { + if (!edit) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = tolower((char)v); + } + } + return NO_ERROR; +} + +status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; i<N; i++) { + if (str[i] == replaceThis) { + if (!edit) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = withThis; + } + } + return NO_ERROR; +} + +status_t String16::remove(size_t len, size_t begin) +{ + const size_t N = size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + return NO_ERROR; + } + + if (begin > 0) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((N+1)*sizeof(char16_t)); + if (!buf) { + return NO_MEMORY; + } + char16_t* str = (char16_t*)buf->data(); + memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); + mString = str; + } + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +}; // namespace stagefright diff --git a/media/libstagefright/system/core/libutils/String8.cpp b/media/libstagefright/system/core/libutils/String8.cpp new file mode 100644 index 000000000..3d4b28569 --- /dev/null +++ b/media/libstagefright/system/core/libutils/String8.cpp @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2005 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/String8.h> + +#include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/SharedBuffer.h> +#include <utils/String16.h> +#include <utils/threads.h> + +#include <ctype.h> + +/* + * Functions outside android is below the namespace stagefright, since they use + * functions and constants in android namespace. + */ + +// --------------------------------------------------------------------------- + +namespace stagefright { + +// Separator used by resource paths. This is not platform dependent contrary +// to OS_PATH_SEPARATOR. +#define RES_PATH_SEPARATOR '/' + +static SharedBuffer* gEmptyStringBuf = NULL; +static char* gEmptyString = NULL; + +extern int gDarwinCantLoadAllObjects; +int gDarwinIsReallyAnnoying; + +void initialize_string8(); + +static inline char* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string8() +{ + // HACK: This dummy dependency forces linking libutils Static.cpp, + // which is needed to initialize String8/String16 classes. + // These variables are named for Darwin, but are needed elsewhere too, + // including static linking on any platform. + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; + + SharedBuffer* buf = SharedBuffer::alloc(1); + char* str = (char*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string8() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +static char* allocFromUTF8(const char* in, size_t len) +{ + if (len > 0) { + SharedBuffer* buf = SharedBuffer::alloc(len+1); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char* str = (char*)buf->data(); + memcpy(str, in, len); + str[len] = 0; + return str; + } + return NULL; + } + + return getEmptyString(); +} + +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (!buf) { + return getEmptyString(); + } + + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; +} + +static char* allocFromUTF32(const char32_t* in, size_t len) +{ + if (len == 0) { + return getEmptyString(); + } + + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (!buf) { + return getEmptyString(); + } + + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); + + return str; +} + +// --------------------------------------------------------------------------- + +String8::String8() + : mString(0) +{ + char* data = static_cast<char*>( + SharedBuffer::alloc(sizeof(char))->data()); + data[0] = 0; + mString = data; +} + +String8::String8(const String8& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String8::String8(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const String16& o) + : mString(allocFromUTF16(o.string(), o.size())) +{ +} + +String8::String8(const char16_t* o) + : mString(allocFromUTF16(o, strlen16(o))) +{ +} + +String8::String8(const char16_t* o, size_t len) + : mString(allocFromUTF16(o, len)) +{ +} + +String8::String8(const char32_t* o) + : mString(allocFromUTF32(o, strlen32(o))) +{ +} + +String8::String8(const char32_t* o, size_t len) + : mString(allocFromUTF32(o, len)) +{ +} + +String8::~String8() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + +void String8::clear() { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); +} + +void String8::setTo(const String8& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String8::setTo(const char* other) +{ + const char *newString = allocFromUTF8(other, strlen(other)); + SharedBuffer::bufferFromData(mString)->release(); + mString = newString; + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char* other, size_t len) +{ + const char *newString = allocFromUTF8(other, len); + SharedBuffer::bufferFromData(mString)->release(); + mString = newString; + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char16_t* other, size_t len) +{ + const char *newString = allocFromUTF16(other, len); + SharedBuffer::bufferFromData(mString)->release(); + mString = newString; + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char32_t* other, size_t len) +{ + const char *newString = allocFromUTF32(other, len); + SharedBuffer::bufferFromData(mString)->release(); + mString = newString; + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::append(const String8& other) +{ + const size_t otherLen = other.bytes(); + if (bytes() == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other.string(), otherLen); +} + +status_t String8::append(const char* other) +{ + return append(other, strlen(other)); +} + +status_t String8::append(const char* other, size_t otherLen) +{ + if (bytes() == 0) { + return setTo(other, otherLen); + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other, otherLen); +} + +status_t String8::appendFormat(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + status_t result = appendFormatV(fmt, args); + + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ + int result = NO_ERROR; +#ifndef _MSC_VER + va_list o; + va_copy(o, args); +#endif + int n = vsnprintf(NULL, 0, fmt, args); + if (n != 0) { + size_t oldLength = length(); + char* buf = lockBuffer(oldLength + n); + if (buf) { +#ifdef _MSC_VER + vsnprintf(buf + oldLength, n + 1, fmt, args); +#else + vsnprintf(buf + oldLength, n + 1, fmt, o); +#endif + } else { + result = NO_MEMORY; + } + } +#ifndef _MSC_VER + va_end(o); +#endif + return result; +} + +status_t String8::real_append(const char* other, size_t otherLen) +{ + const size_t myLen = bytes(); + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(myLen+otherLen+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + str += myLen; + memcpy(str, other, otherLen); + str[otherLen] = '\0'; + return NO_ERROR; + } + return NO_MEMORY; +} + +char* String8::lockBuffer(size_t size) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + return str; + } + return NULL; +} + +void String8::unlockBuffer() +{ + unlockBuffer(strlen(mString)); +} + +status_t String8::unlockBuffer(size_t size) +{ + if (size != this->size()) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (! buf) { + return NO_MEMORY; + } + + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; + } + + return NO_ERROR; +} + +ssize_t String8::find(const char* other, size_t start) const +{ + size_t len = size(); + if (start >= len) { + return -1; + } + const char* s = mString+start; + const char* p = strstr(s, other); + return p ? p-mString : -1; +} + +void String8::toLower() +{ + toLower(0, size()); +} + +void String8::toLower(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = tolower(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +void String8::toUpper() +{ + toUpper(0, size()); +} + +void String8::toUpper(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = toupper(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +size_t String8::getUtf32Length() const +{ + return utf8_to_utf32_length(mString, length()); +} + +int32_t String8::getUtf32At(size_t index, size_t *next_index) const +{ + return utf32_from_utf8_at(mString, length(), index, next_index); +} + +void String8::getUtf32(char32_t* dst) const +{ + utf8_to_utf32(mString, length(), dst); +} + +// --------------------------------------------------------------------------- +// Path functions + +#if 0 + +void String8::setPathName(const char* name) +{ + setPathName(name, strlen(name)); +} + +void String8::setPathName(const char* name, size_t len) +{ + char* buf = lockBuffer(len); + + memcpy(buf, name, len); + + // remove trailing path separator, if present + if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) + len--; + + buf[len] = '\0'; + + unlockBuffer(len); +} + +String8 String8::getPathLeaf(void) const +{ + const char* cp; + const char*const buf = mString; + + cp = strrchr(buf, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(*this); + else + return String8(cp+1); +} + +String8 String8::getPathDir(void) const +{ + const char* cp; + const char*const str = mString; + + cp = strrchr(str, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(""); + else + return String8(str, cp - str); +} + +String8 String8::walkPath(String8* outRemains) const +{ + const char* cp; + const char*const str = mString; + const char* buf = str; + + cp = strchr(buf, OS_PATH_SEPARATOR); + if (cp == buf) { + // don't include a leading '/'. + buf = buf+1; + cp = strchr(buf, OS_PATH_SEPARATOR); + } + + if (cp == NULL) { + String8 res = buf != str ? String8(buf) : *this; + if (outRemains) *outRemains = String8(""); + return res; + } + + String8 res(buf, cp-buf); + if (outRemains) *outRemains = String8(cp+1); + return res; +} + +/* + * Helper function for finding the start of an extension in a pathname. + * + * Returns a pointer inside mString, or NULL if no extension was found. + */ +char* String8::find_extension(void) const +{ + const char* lastSlash; + const char* lastDot; + int extLen; + const char* const str = mString; + + // only look at the filename + lastSlash = strrchr(str, OS_PATH_SEPARATOR); + if (lastSlash == NULL) + lastSlash = str; + else + lastSlash++; + + // find the last dot + lastDot = strrchr(lastSlash, '.'); + if (lastDot == NULL) + return NULL; + + // looks good, ship it + return const_cast<char*>(lastDot); +} + +String8 String8::getPathExtension(void) const +{ + char* ext; + + ext = find_extension(); + if (ext != NULL) + return String8(ext); + else + return String8(""); +} + +String8 String8::getBasePath(void) const +{ + char* ext; + const char* const str = mString; + + ext = find_extension(); + if (ext == NULL) + return String8(*this); + else + return String8(str, ext - str); +} + +String8& String8::appendPath(const char* name) +{ + // TODO: The test below will fail for Win32 paths. Fix later or ignore. + if (name[0] != OS_PATH_SEPARATOR) { + if (*name == '\0') { + // nothing to do + return *this; + } + + size_t len = length(); + if (len == 0) { + // no existing filename, just use the new one + setPathName(name); + return *this; + } + + // make room for oldPath + '/' + newPath + int newlen = strlen(name); + + char* buf = lockBuffer(len+1+newlen); + + // insert a '/' if needed + if (buf[len-1] != OS_PATH_SEPARATOR) + buf[len++] = OS_PATH_SEPARATOR; + + memcpy(buf+len, name, newlen+1); + len += newlen; + + unlockBuffer(len); + + return *this; + } else { + setPathName(name); + return *this; + } +} + +String8& String8::convertToResPath() +{ +#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR + size_t len = length(); + if (len > 0) { + char * buf = lockBuffer(len); + for (char * end = buf + len; buf < end; ++buf) { + if (*buf == OS_PATH_SEPARATOR) + *buf = RES_PATH_SEPARATOR; + } + unlockBuffer(len); + } +#endif + return *this; +} + +#endif + +}; // namespace stagefright diff --git a/media/libstagefright/system/core/libutils/Unicode.cpp b/media/libstagefright/system/core/libutils/Unicode.cpp new file mode 100644 index 000000000..b8aae5e04 --- /dev/null +++ b/media/libstagefright/system/core/libutils/Unicode.cpp @@ -0,0 +1,606 @@ +/* + * Copyright (C) 2005 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/Unicode.h> + +#include <stddef.h> + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include <netinet/in.h> +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to <dstP>. + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast<int32_t>(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + return u16cur; +} + +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { + char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str); + *end = 0; +} + +char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) { + const uint8_t* const u8end = src + srcLen; + const uint8_t* u8cur = src; + const uint16_t* const u16end = (const uint16_t* const) dst + dstLen; + uint16_t* u16cur = (uint16_t*) dst; + + while (u8cur < u8end && u16cur < u16end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + if (u16cur >= u16end) { + // Ooops... not enough room for this surrogate pair. + return (char16_t*) u16cur-1; + } + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + return (char16_t*) u16cur; +} + +} diff --git a/media/libstagefright/system/core/libutils/VectorImpl.cpp b/media/libstagefright/system/core/libutils/VectorImpl.cpp new file mode 100644 index 000000000..b57d211d4 --- /dev/null +++ b/media/libstagefright/system/core/libutils/VectorImpl.cpp @@ -0,0 +1,643 @@ +/* + * Copyright (C) 2005 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 "Vector" + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <cutils/log.h> + +#include <utils/Errors.h> +#include <utils/SharedBuffer.h> +#include <utils/VectorImpl.h> + +static const uint32_t kMAX_ALLOCATION = + ((SIZE_MAX > INT32_MAX ? INT32_MAX : SIZE_MAX) - 1); + +/*****************************************************************************/ + + +namespace stagefright { + +// ---------------------------------------------------------------------------- + +const size_t kMinVectorCapacity = 4; + +static inline size_t max(size_t a, size_t b) { + return a>b ? a : b; +} + +// ---------------------------------------------------------------------------- + +VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) + : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) +{ +} + +VectorImpl::VectorImpl(const VectorImpl& rhs) + : mStorage(rhs.mStorage), mCount(rhs.mCount), + mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) +{ + if (mStorage) { + SharedBuffer::bufferFromData(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + ALOGW_IF(mCount, + "[%p] subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::bufferFromData(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + assert(sb); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + return insertArrayAt(vector.arrayImpl(), index, vector.size()); +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, length); + if (where) { + _do_copy(where, array, length); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendArray(const void* array, size_t length) +{ + return insertArrayAt(array, size(), length); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +static int sortProxy(const void* lhs, const void* rhs, void* func) +{ + return (*(VectorImpl::compar_t)func)(lhs, rhs); +} + +status_t VectorImpl::sort(VectorImpl::compar_t cmp) +{ + return sort(sortProxy, (void*)cmp); +} + +status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) +{ + // the sort must be stable. we're using insertion sort which + // is well suited for small and already sorted arrays + // for big arrays, it could be better to use mergesort + const ssize_t count = size(); + if (count > 1) { + void* array = const_cast<void*>(arrayImpl()); + void* temp = 0; + ssize_t i = 1; + while (i < count) { + void* item = reinterpret_cast<char*>(array) + mItemSize*(i); + void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1); + if (cmp(curr, item, state) > 0) { + + if (!temp) { + // we're going to have to modify the array... + array = editArrayImpl(); + if (!array) return NO_MEMORY; + temp = malloc(mItemSize); + if (!temp) return NO_MEMORY; + item = reinterpret_cast<char*>(array) + mItemSize*(i); + curr = reinterpret_cast<char*>(array) + mItemSize*(i-1); + } else { + _do_destroy(temp, 1); + } + + _do_copy(temp, item, 1); + + ssize_t j = i-1; + void* next = reinterpret_cast<char*>(array) + mItemSize*(i); + do { + _do_destroy(next, 1); + _do_copy(next, curr, 1); + next = curr; + --j; + curr = reinterpret_cast<char*>(array) + mItemSize*(j); + } while (j>=0 && (cmp(curr, temp, state) > 0)); + + _do_destroy(next, 1); + _do_copy(next, temp, 1); + } + i++; + } + + if (temp) { + _do_destroy(temp, 1); + free(temp); + } + } + return NO_ERROR; +} + +void VectorImpl::pop() +{ + if (size()) + removeItemsAt(size()-1, 1); +} + +void VectorImpl::push() +{ + push(0); +} + +void VectorImpl::push(const void* item) +{ + insertAt(item, size()); +} + +ssize_t VectorImpl::add() +{ + return add(0); +} + +ssize_t VectorImpl::add(const void* item) +{ + return insertAt(item, size()); +} + +ssize_t VectorImpl::replaceAt(size_t index) +{ + return replaceAt(0, index); +} + +ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) +{ + ALOG_ASSERT(index<size(), + "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); + + if (index >= size()) { + return BAD_INDEX; + } + + void* item = editItemLocation(index); + if (item != prototype) { + if (item == 0) + return NO_MEMORY; + _do_destroy(item, 1); + if (prototype == 0) { + _do_construct(item, 1); + } else { + _do_copy(item, prototype, 1); + } + } + return ssize_t(index); +} + +ssize_t VectorImpl::removeItemsAt(size_t index, size_t count) +{ + ALOG_ASSERT((index+count)<=size(), + "[%p] remove: index=%d, count=%d, size=%d", + this, (int)index, (int)count, (int)size()); + + if ((index+count) > size()) + return BAD_VALUE; + _shrink(index, count); + return index; +} + +void VectorImpl::finish_vector() +{ + release_storage(); + mStorage = 0; + mCount = 0; +} + +void VectorImpl::clear() +{ + _shrink(0, mCount); +} + +void* VectorImpl::editItemLocation(size_t index) +{ + ALOG_ASSERT(index<capacity(), + "[%p] editItemLocation: index=%d, capacity=%d, count=%d", + this, (int)index, (int)capacity(), (int)mCount); + + if (index < capacity()) { + void* buffer = editArrayImpl(); + if (buffer) { + return reinterpret_cast<char*>(buffer) + index*mItemSize; + } + } + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + ALOG_ASSERT(index<capacity(), + "[%p] itemLocation: index=%d, capacity=%d, count=%d", + this, (int)index, (int)capacity(), (int)mCount); + + if (index < capacity()) { + const void* buffer = arrayImpl(); + if (buffer) { + return reinterpret_cast<const char*>(buffer) + index*mItemSize; + } + } + return 0; +} + +ssize_t VectorImpl::setCapacity(size_t new_capacity) +{ + if (new_capacity <= size()) { + // we can't reduce the capacity + return capacity(); + } + if (new_capacity >= (kMAX_ALLOCATION / mItemSize)) { + return NO_MEMORY; + } + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + _do_copy(array, mStorage, size()); + release_storage(); + mStorage = const_cast<void*>(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +ssize_t VectorImpl::resize(size_t size) { + ssize_t result = NO_ERROR; + if (size > mCount) { + result = insertAt(mCount, size - mCount); + } else if (size < mCount) { + result = removeItemsAt(size, mCount - size); + } + return result < 0 ? result : size; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + ALOG_ASSERT(where <= mCount, + "[%p] _grow: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked + + const size_t new_size = mCount + amount; + assert(amount < kMAX_ALLOCATION - mCount); + if (capacity() < new_size) { + assert(new_size < (SIZE_MAX / 3 - 1)); + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); + assert(new_capacity < (kMAX_ALLOCATION / mItemSize)); +// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); + assert(cur_sb); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + assert(sb); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + assert(sb); + if (sb) { + void* array = sb->data(); + if (where != 0) { + _do_copy(array, mStorage, where); + } + if (where != mCount) { + const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize; + void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast<void*>(array); + } + } + } else { + void* array = editArrayImpl(); + if (where != mCount) { + const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize; + void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_move_forward(to, from, mCount - where); + } + } + mCount = new_size; + void* free_space = const_cast<void*>(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + ALOG_ASSERT(where + amount <= mCount, + "[%p] _shrink: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked + + const size_t new_size = mCount - amount; + assert(new_size < (SIZE_MAX / 2)); + if (new_size*2 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + assert(new_capacity < (kMAX_ALLOCATION / mItemSize)); + if ((where == new_size) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); + assert(cur_sb); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + assert(sb); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + assert(sb); + if (sb) { + void* array = sb->data(); + if (where != 0) { + _do_copy(array, mStorage, where); + } + if (where != new_size) { + const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize; + _do_copy(dest, from, new_size - where); + } + release_storage(); + mStorage = const_cast<void*>(array); + } + } + } else { + void* array = editArrayImpl(); + assert(array); + void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize; + _do_destroy(to, amount); + if (where != new_size) { + const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, new_size - where); + } + } + mCount = new_size; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) ); +} + +ssize_t SortedVectorImpl::indexOf(const void* item) const +{ + return _indexOrderOf(item); +} + +size_t SortedVectorImpl::orderOf(const void* item) const +{ + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size()-1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l)/2; + const void* const curr = reinterpret_cast<const char *>(a) + (mid*s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t SortedVectorImpl::add(const void* item) +{ + size_t order; + ssize_t index = _indexOrderOf(item, &order); + if (index < 0) { + index = VectorImpl::insertAt(item, order, 1); + } else { + index = VectorImpl::replaceAt(item, index); + } + return index; +} + +ssize_t SortedVectorImpl::merge(const VectorImpl& vector) +{ + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i=0 ; i<s ; i++) { + ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is ); + if (err<0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) +{ + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector)); + } else { + // this could be made a little better + err = merge(static_cast<const VectorImpl&>(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +/*****************************************************************************/ + +}; // namespace stagefright + diff --git a/media/libstagefright/update-patches.sh b/media/libstagefright/update-patches.sh new file mode 100755 index 000000000..f9f498b77 --- /dev/null +++ b/media/libstagefright/update-patches.sh @@ -0,0 +1,13 @@ +#!/bin/bash -e +cd `dirname "$0"` +rm -fR patches +for DIR in `find android/ -name .git` +do + DIR=`dirname ${DIR}` + DST=patches/${DIR:8} + echo ${DST} + mkdir -p `dirname ${DST}` + cp -a ${DIR:8} `dirname ${DIR}` + (cd ${DIR} && git diff) > ${DST}.patch + (cd ${DIR} && git rev-parse HEAD) > ${DST}.tag +done |