diff options
author | Andreas Huber <andih@google.com> | 2011-01-11 16:22:33 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2011-01-11 16:22:33 -0800 |
commit | 4c0104e004f30d7e48381ef8d728b2e93931611f (patch) | |
tree | 99d6c0e2d582ecb8a85a7bd33142535f1cf02595 | |
parent | 960737e761895d5850beabab809d03b3621d0ec3 (diff) | |
parent | 9a0cf4fb7d18e186e06341bde915de234992d969 (diff) | |
download | frameworks_av-4c0104e004f30d7e48381ef8d728b2e93931611f.zip frameworks_av-4c0104e004f30d7e48381ef8d728b2e93931611f.tar.gz frameworks_av-4c0104e004f30d7e48381ef8d728b2e93931611f.tar.bz2 |
am 7e2f9cc8: DO NOT MERGE: Fix Matroska issues
* commit '7e2f9cc81da788006790365ff11f06551fc1bc26':
DO NOT MERGE: Fix Matroska issues
-rw-r--r-- | media/libstagefright/matroska/MatroskaExtractor.cpp | 387 | ||||
-rw-r--r-- | media/libstagefright/matroska/mkvparser.cpp | 3 | ||||
-rw-r--r-- | media/libstagefright/matroska/mkvparser.hpp | 2 |
3 files changed, 317 insertions, 75 deletions
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index d16476d..1661130 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -22,13 +22,15 @@ #include "mkvparser.hpp" +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> #include <utils/String8.h> namespace android { @@ -81,46 +83,6 @@ private: //////////////////////////////////////////////////////////////////////////////// -#include <ctype.h> -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; - } -} - struct BlockIterator { BlockIterator(mkvparser::Segment *segment, unsigned long trackNum); @@ -156,6 +118,9 @@ struct MatroskaSource : public MediaSource { virtual status_t read( MediaBuffer **buffer, const ReadOptions *options); +protected: + virtual ~MatroskaSource(); + private: enum Type { AVC, @@ -167,9 +132,15 @@ private: size_t mTrackIndex; Type mType; BlockIterator mBlockIter; + size_t mNALSizeLen; // for type AVC + + List<MediaBuffer *> mPendingFrames; status_t advance(); + status_t readBlock(); + void clearPendingFrames(); + MatroskaSource(const MatroskaSource &); MatroskaSource &operator=(const MatroskaSource &); }; @@ -180,18 +151,35 @@ MatroskaSource::MatroskaSource( mTrackIndex(index), mType(OTHER), mBlockIter(mExtractor->mSegment, - mExtractor->mTracks.itemAt(index).mTrackNum) { + mExtractor->mTracks.itemAt(index).mTrackNum), + mNALSizeLen(0) { + sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta; + const char *mime; - CHECK(mExtractor->mTracks.itemAt(index).mMeta-> - findCString(kKeyMIMEType, &mime)); + CHECK(meta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { mType = AVC; + + uint32_t dummy; + const uint8_t *avcc; + size_t avccSize; + CHECK(meta->findData( + kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)); + + CHECK_GE(avccSize, 5u); + + mNALSizeLen = 1 + (avcc[4] & 3); + LOGV("mNALSizeLen = %d", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } } +MatroskaSource::~MatroskaSource() { + clearPendingFrames(); +} + status_t MatroskaSource::start(MetaData *params) { mBlockIter.reset(); @@ -199,6 +187,8 @@ status_t MatroskaSource::start(MetaData *params) { } status_t MatroskaSource::stop() { + clearPendingFrames(); + return OK; } @@ -276,66 +266,313 @@ int64_t BlockIterator::blockTimeUs() const { //////////////////////////////////////////////////////////////////////////////// -status_t MatroskaSource::read( - MediaBuffer **out, const ReadOptions *options) { - *out = NULL; +static unsigned U24_AT(const uint8_t *ptr) { + return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; +} - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - mBlockIter.seek(seekTimeUs); +static size_t clz(uint8_t x) { + size_t numLeadingZeroes = 0; + + while (!(x & 0x80)) { + ++numLeadingZeroes; + x = x << 1; } + return numLeadingZeroes; +} + +void MatroskaSource::clearPendingFrames() { + while (!mPendingFrames.empty()) { + MediaBuffer *frame = *mPendingFrames.begin(); + mPendingFrames.erase(mPendingFrames.begin()); + + frame->release(); + frame = NULL; + } +} + +#define BAIL(err) \ + do { \ + if (bigbuf) { \ + bigbuf->release(); \ + bigbuf = NULL; \ + } \ + \ + return err; \ + } while (0) + +status_t MatroskaSource::readBlock() { + CHECK(mPendingFrames.empty()); + if (mBlockIter.eos()) { return ERROR_END_OF_STREAM; } const mkvparser::Block *block = mBlockIter.block(); + size_t size = block->GetSize(); int64_t timeUs = mBlockIter.blockTimeUs(); + int32_t isSync = block->IsKey(); - MediaBuffer *buffer = new MediaBuffer(size + 2); - buffer->meta_data()->setInt64(kKeyTime, timeUs); - buffer->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); + MediaBuffer *bigbuf = new MediaBuffer(size); long res = block->Read( - mExtractor->mReader, (unsigned char *)buffer->data() + 2); + mExtractor->mReader, (unsigned char *)bigbuf->data()); if (res != 0) { + bigbuf->release(); + bigbuf = NULL; + return ERROR_END_OF_STREAM; } - buffer->set_range(2, size); + mBlockIter.advance(); + + bigbuf->meta_data()->setInt64(kKeyTime, timeUs); + bigbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); - if (mType == AVC) { - CHECK(size >= 2); + unsigned lacing = (block->Flags() >> 1) & 3; - uint8_t *data = (uint8_t *)buffer->data(); + if (lacing == 0) { + mPendingFrames.push_back(bigbuf); + return OK; + } - unsigned NALsize = data[2] << 8 | data[3]; - CHECK_EQ(size, NALsize + 2); + LOGV("lacing = %u, size = %d", lacing, size); - memcpy(data, "\x00\x00\x00\x01", 4); - buffer->set_range(0, size + 2); - } else if (mType == AAC) { - // There's strange junk at the beginning... + const uint8_t *data = (const uint8_t *)bigbuf->data(); + // hexdump(data, size); - const uint8_t *data = (const uint8_t *)buffer->data() + 2; - size_t offset = 0; - while (offset < size && data[offset] != 0x21) { - ++offset; - } - buffer->set_range(2 + offset, size - offset); + if (size == 0) { + BAIL(ERROR_MALFORMED); } - *out = buffer; + unsigned numFrames = (unsigned)data[0] + 1; + ++data; + --size; + + Vector<uint64_t> frameSizes; + + switch (lacing) { + case 1: // Xiph + { + for (size_t i = 0; i < numFrames - 1; ++i) { + size_t frameSize = 0; + uint8_t byte; + do { + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + byte = data[0]; + ++data; + --size; + + frameSize += byte; + } while (byte == 0xff); + + frameSizes.push(frameSize); + } + + break; + } + + case 2: // fixed-size + { + if ((size % numFrames) != 0) { + BAIL(ERROR_MALFORMED); + } + + size_t frameSize = size / numFrames; + for (size_t i = 0; i < numFrames - 1; ++i) { + frameSizes.push(frameSize); + } + + break; + } + + case 3: // EBML + { + uint64_t lastFrameSize = 0; + for (size_t i = 0; i < numFrames - 1; ++i) { + uint8_t byte; + + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + byte = data[0]; + ++data; + --size; + + size_t numLeadingZeroes = clz(byte); + + uint64_t frameSize = byte & ~(0x80 >> numLeadingZeroes); + for (size_t j = 0; j < numLeadingZeroes; ++j) { + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + + frameSize = frameSize << 8; + frameSize |= data[0]; + ++data; + --size; + } + + if (i == 0) { + frameSizes.push(frameSize); + } else { + size_t shift = + 7 - numLeadingZeroes + 8 * numLeadingZeroes; + + int64_t delta = + (int64_t)frameSize - (1ll << (shift - 1)) + 1; + + frameSize = lastFrameSize + delta; + + frameSizes.push(frameSize); + } + + lastFrameSize = frameSize; + } + break; + } + + default: + TRESPASS(); + } #if 0 - hexdump((const uint8_t *)buffer->data() + buffer->range_offset(), - buffer->range_length()); + AString out; + for (size_t i = 0; i < frameSizes.size(); ++i) { + if (i > 0) { + out.append(", "); + } + out.append(StringPrintf("%llu", frameSizes.itemAt(i))); + } + LOGV("sizes = [%s]", out.c_str()); #endif - mBlockIter.advance(); + for (size_t i = 0; i < frameSizes.size(); ++i) { + uint64_t frameSize = frameSizes.itemAt(i); + + if (size < frameSize) { + BAIL(ERROR_MALFORMED); + } + + MediaBuffer *mbuf = new MediaBuffer(frameSize); + mbuf->meta_data()->setInt64(kKeyTime, timeUs); + mbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); + memcpy(mbuf->data(), data, frameSize); + mPendingFrames.push_back(mbuf); + + data += frameSize; + size -= frameSize; + } + + size_t offset = bigbuf->range_length() - size; + bigbuf->set_range(offset, size); + + mPendingFrames.push_back(bigbuf); + + return OK; +} + +#undef BAIL + +status_t MatroskaSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + clearPendingFrames(); + mBlockIter.seek(seekTimeUs); + } + +again: + while (mPendingFrames.empty()) { + status_t err = readBlock(); + + if (err != OK) { + clearPendingFrames(); + + return err; + } + } + + MediaBuffer *frame = *mPendingFrames.begin(); + mPendingFrames.erase(mPendingFrames.begin()); + + size_t size = frame->range_length(); + + if (mType != AVC) { + *out = frame; + + return OK; + } + + if (size < mNALSizeLen) { + frame->release(); + frame = NULL; + + return ERROR_MALFORMED; + } + + // In the case of AVC content, each NAL unit is prefixed by + // mNALSizeLen bytes of length. We want to prefix the data with + // a four-byte 0x00000001 startcode instead of the length prefix. + // mNALSizeLen ranges from 1 through 4 bytes, so add an extra + // 3 bytes of padding to the buffer start. + static const size_t kPadding = 3; + + MediaBuffer *buffer = new MediaBuffer(size + kPadding); + + int64_t timeUs; + CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs)); + int32_t isSync; + CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)); + + buffer->meta_data()->setInt64(kKeyTime, timeUs); + buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync); + + memcpy((uint8_t *)buffer->data() + kPadding, + (const uint8_t *)frame->data() + frame->range_offset(), + size); + + buffer->set_range(kPadding, size); + + frame->release(); + frame = NULL; + + uint8_t *data = (uint8_t *)buffer->data(); + + size_t NALsize; + switch (mNALSizeLen) { + case 1: NALsize = data[kPadding]; break; + case 2: NALsize = U16_AT(&data[kPadding]); break; + case 3: NALsize = U24_AT(&data[kPadding]); break; + case 4: NALsize = U32_AT(&data[kPadding]); break; + default: + TRESPASS(); + } + + if (size < NALsize + mNALSizeLen) { + buffer->release(); + buffer = NULL; + + return ERROR_MALFORMED; + } + + if (size > NALsize + mNALSizeLen) { + LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen); + } + + // actual data starts at &data[kPadding + mNALSizeLen] + + memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4); + buffer->set_range(mNALSizeLen - 1, NALsize + 4); + + *out = buffer; return OK; } diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp index 455b1d6..7448d96 100644 --- a/media/libstagefright/matroska/mkvparser.cpp +++ b/media/libstagefright/matroska/mkvparser.cpp @@ -4474,6 +4474,9 @@ bool Block::IsKey() const return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
}
+unsigned char Block::Flags() const {
+ return m_flags;
+}
void Block::SetKey(bool bKey)
{
diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp index c46d349..f7d8948 100644 --- a/media/libstagefright/matroska/mkvparser.hpp +++ b/media/libstagefright/matroska/mkvparser.hpp @@ -80,6 +80,8 @@ public: bool IsKey() const;
void SetKey(bool);
+ unsigned char Flags() const;
+
long long GetOffset() const;
long GetSize() const;
long Read(IMkvReader*, unsigned char*) const;
|