From d66cd54dfd76e11e6be7fd610a02be30345af212 Mon Sep 17 00:00:00 2001 From: Anthony Jones Date: Tue, 5 Aug 2014 18:12:48 +1200 Subject: [PATCH] Bug 1045909 - Fix buffer range calculation for fMP4; r=edwin --- content/media/fmp4/MP4Reader.cpp | 23 +- content/media/fmp4/MP4Reader.h | 5 + media/libstagefright/binding/Box.cpp | 97 +++++++ media/libstagefright/binding/DecoderData.cpp | 18 +- media/libstagefright/binding/Index.cpp | 32 ++- media/libstagefright/binding/MoofParser.cpp | 270 ++++++++++++++++++ .../binding/include/mp4_demuxer/Box.h | 78 +++++ .../binding/include/mp4_demuxer/ByteReader.h | 33 ++- .../binding/include/mp4_demuxer/DecoderData.h | 40 +-- .../binding/include/mp4_demuxer/Index.h | 10 +- .../binding/include/mp4_demuxer/MoofParser.h | 119 ++++++++ .../binding/include/mp4_demuxer/mp4_demuxer.h | 6 +- media/libstagefright/binding/mp4_demuxer.cpp | 24 +- .../media/libstagefright/MPEG4Extractor.cpp | 4 + media/libstagefright/moz.build | 2 + 15 files changed, 703 insertions(+), 58 deletions(-) create mode 100644 media/libstagefright/binding/Box.cpp create mode 100644 media/libstagefright/binding/MoofParser.cpp create mode 100644 media/libstagefright/binding/include/mp4_demuxer/Box.h create mode 100644 media/libstagefright/binding/include/mp4_demuxer/MoofParser.h diff --git a/content/media/fmp4/MP4Reader.cpp b/content/media/fmp4/MP4Reader.cpp index 0d6c3370570f..69a91ec19498 100644 --- a/content/media/fmp4/MP4Reader.cpp +++ b/content/media/fmp4/MP4Reader.cpp @@ -105,6 +105,7 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder) , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2)) , mLastReportedNumDecodedFrames(0) , mLayersBackendType(layers::LayersBackend::LAYERS_NONE) + , mTimeRangesMonitor("MP4Reader::TimeRanges") , mDemuxerInitialized(false) , mIsEncrypted(false) { @@ -705,20 +706,30 @@ MP4Reader::Seek(int64_t aTime, return NS_OK; } -nsresult -MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) +void +MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset) { nsTArray ranges; if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) { - return NS_ERROR_FAILURE; + return; } nsTArray> timeRanges; mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges); - for (size_t i = 0; i < timeRanges.Length(); i++) { - aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0, - (timeRanges[i].end - aStartTime) / 1000000.0); + MonitorAutoLock mon(mTimeRangesMonitor); + mTimeRanges = timeRanges; +} + + +nsresult +MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) +{ + MonitorAutoLock mon(mTimeRangesMonitor); + for (size_t i = 0; i < mTimeRanges.Length(); i++) { + aBuffered->Add((mTimeRanges[i].start - aStartTime) / 1000000.0, + (mTimeRanges[i].end - aStartTime) / 1000000.0); } return NS_OK; diff --git a/content/media/fmp4/MP4Reader.h b/content/media/fmp4/MP4Reader.h index 73be4e2e1734..7045c68272ab 100644 --- a/content/media/fmp4/MP4Reader.h +++ b/content/media/fmp4/MP4Reader.h @@ -53,6 +53,9 @@ public: virtual bool IsMediaSeekable() MOZ_OVERRIDE; + virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset) MOZ_OVERRIDE; + virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE; @@ -165,6 +168,8 @@ private: layers::LayersBackend mLayersBackendType; nsTArray> mInitDataEncountered; + Monitor mTimeRangesMonitor; + nsTArray> mTimeRanges; // True if we've read the streams' metadata. bool mDemuxerInitialized; diff --git a/media/libstagefright/binding/Box.cpp b/media/libstagefright/binding/Box.cpp new file mode 100644 index 000000000000..d33cfcc634ac --- /dev/null +++ b/media/libstagefright/binding/Box.cpp @@ -0,0 +1,97 @@ +/* -*- 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/mp4_demuxer.h" +#include "mozilla/Endian.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent) + : mContext(aContext), mType(0), mParent(aParent) +{ + uint8_t header[8]; + 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 = &mContext->mByteRanges[i]; + if (byteRange->Contains(headerRange)) { + break; + } + } + + size_t bytes; + if (!mContext->mSource->ReadAt(aOffset, header, sizeof(header), &bytes) || + bytes != sizeof(header)) { + return; + } + + uint64_t size = BigEndian::readUint32(header); + if (size == 1) { + uint8_t bigLength[8]; + MediaByteRange bigLengthRange(headerRange.mEnd, + headerRange.mEnd + sizeof(bigLength)); + if ((mParent && !mParent->mRange.Contains(bigLengthRange)) || + !byteRange->Contains(bigLengthRange) || + !mContext->mSource->ReadAt(aOffset, bigLength, + sizeof(bigLengthRange), &bytes) || + bytes != sizeof(bigLengthRange)) { + return; + } + size = BigEndian::readUint64(bigLength); + mChildOffset = bigLengthRange.mEnd; + } else { + mChildOffset = headerRange.mEnd; + } + + MediaByteRange boxRange(aOffset, aOffset + size); + if (boxRange.mEnd > mChildOffset || + (mParent && !mParent->mRange.Contains(boxRange)) || + !byteRange->Contains(boxRange)) { + return; + } + mRange = MediaByteRange(aOffset, aOffset + size); + mType = BigEndian::readUint32(&header[4]); +} + +Box +Box::Next() const +{ + MOZ_ASSERT(IsAvailable()); + return Box(mContext, mRange.mEnd, mParent); +} + +Box +Box::FirstChild() const +{ + MOZ_ASSERT(IsAvailable()); + return Box(mContext, mChildOffset, this); +} + +void +Box::Read(nsTArray* aDest) +{ + aDest->SetLength(mRange.mEnd - mChildOffset); + size_t bytes; + if (!mContext->mSource->ReadAt(mChildOffset, &(*aDest)[0], aDest->Length(), + &bytes) || + bytes != aDest->Length()) { + // Byte ranges are being reported incorrectly + MOZ_ASSERT(false); + aDest->Clear(); + } +} +} diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp index 7267dbc399a5..32c1d5e1ceff 100644 --- a/media/libstagefright/binding/DecoderData.cpp +++ b/media/libstagefright/binding/DecoderData.cpp @@ -124,11 +124,19 @@ CryptoSample::Update(sp& aMetaData) } void -AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) +TrackConfig::Update(sp& aMetaData, const char* aMimeType) { // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it mime_type = aMimeType; duration = FindInt64(aMetaData, kKeyDuration); + mTrackId = FindInt32(aMetaData, kKeyTrackID); + crypto.Update(aMetaData); +} + +void +AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) +{ + TrackConfig::Update(aMetaData, aMimeType); channel_count = FindInt32(aMetaData, kKeyChannelCount); bits_per_sample = FindInt32(aMetaData, kKeySampleSize); samples_per_second = FindInt32(aMetaData, kKeySampleRate); @@ -145,8 +153,6 @@ AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) size); } } - - crypto.Update(aMetaData); } bool @@ -159,9 +165,7 @@ AudioDecoderConfig::IsValid() void VideoDecoderConfig::Update(sp& aMetaData, const char* aMimeType) { - // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it - mime_type = aMimeType; - duration = FindInt64(aMetaData, kKeyDuration); + TrackConfig::Update(aMetaData, aMimeType); display_width = FindInt32(aMetaData, kKeyDisplayWidth); display_height = FindInt32(aMetaData, kKeyDisplayHeight); @@ -171,8 +175,6 @@ VideoDecoderConfig::Update(sp& aMetaData, const char* aMimeType) extra_data[4] |= 3; annex_b = AnnexB::ConvertExtraDataToAnnexB(extra_data); } - - crypto.Update(aMetaData); } bool diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index f0a01e4e2475..75e2374dc095 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -4,6 +4,7 @@ #include "mp4_demuxer/Index.h" #include "mp4_demuxer/Interval.h" +#include "mp4_demuxer/MoofParser.h" #include "media/stagefright/MediaSource.h" #include "MediaResource.h" @@ -71,11 +72,13 @@ RangeFinder::Contains(MediaByteRange aByteRange) return false; } -void -Index::Init(const stagefright::Vector& aIndex) +Index::Index(const stagefright::Vector& aIndex, + Stream* aSource, uint32_t aTrackId) + : mMonitor("mp4_demuxer::Index") { - MOZ_ASSERT(mIndex.IsEmpty()); - if (!aIndex.isEmpty()) { + if (aIndex.isEmpty()) { + mMoofParser = new MoofParser(aSource, aTrackId); + } else { mIndex.AppendElements(&aIndex[0], aIndex.size()); } } @@ -85,12 +88,29 @@ Index::ConvertByteRangesToTimeRanges( const nsTArray& aByteRanges, nsTArray>* aTimeRanges) { + nsTArray moofIndex; + nsTArray* index; + if (mMoofParser) { + { + MonitorAutoLock mon(mMonitor); + mMoofParser->RebuildFragmentedIndex(aByteRanges); + + // 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. + moofIndex = mMoofParser->mIndex; + } + index = &moofIndex; + } else { + index = &mIndex; + } + nsTArray> timeRanges; RangeFinder rangeFinder(aByteRanges); bool hasSync = false; - for (size_t i = 0; i < mIndex.Length(); i++) { - const MediaSource::Indice& indice = mIndex[i]; + for (size_t i = 0; i < index->Length(); i++) { + const MediaSource::Indice& indice = (*index)[i]; if (!rangeFinder.Contains(MediaByteRange(indice.start_offset, indice.end_offset))) { // We process the index in decode order so we clear hasSync when we hit diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp new file mode 100644 index 000000000000..45625bd4fb5c --- /dev/null +++ b/media/libstagefright/binding/MoofParser.cpp @@ -0,0 +1,270 @@ +/* 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 "MediaResource.h" + +using namespace stagefright; +using namespace mozilla; + +namespace mp4_demuxer +{ + +class Moof +{ +public: + Moof(Box& aBox, MoofParser* aMoofParser); + void ParseTraf(Box& aBox); + void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt); + +private: + MoofParser* mMoofParser; +}; + +void +MoofParser::RebuildFragmentedIndex(const nsTArray& aByteRanges) +{ + BoxContext context(mSource, aByteRanges); + + mIndex.Clear(); + size_t moofCount = 0; + for (size_t i = 0; i + 1 < mMoofOffsets.Length(); i++) { + Box box(&context, mMoofOffsets[i]); + if (box.IsAvailable()) { + MOZ_ASSERT(box.IsType("moof")); + Moof(box, this); + } + } + for (Box box = mMoofOffsets.IsEmpty() + ? Box(&context, 0) + : Box(&context, mMoofOffsets.LastElement()); + box.IsAvailable(); box = box.Next()) { + if (box.IsType("moov")) { + ParseMoov(box); + } else if (box.IsType("moof")) { + if (mMoofOffsets.IsEmpty() || + mMoofOffsets.LastElement() != box.Offset()) { + mMoofOffsets.AppendElement(box.Offset()); + } + Moof(box, this); + } + } +} + +void +MoofParser::ParseMoov(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + 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")) { + ParseMdia(box, tkhd); + } + } +} + +void +MoofParser::ParseMdia(Box& aBox, Tkhd& aTkhd) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("mdhd")) { + if (mTrackId == aTkhd.mTrackId) { + mMdhd = Mdhd(box); + } + } + } +} + +void +MoofParser::ParseMvex(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("trex")) { + mTrex = Trex(box); + } + } +} + +Moof::Moof(Box& aBox, MoofParser* aMoofParser) : mMoofParser(aMoofParser) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("traf")) { + ParseTraf(box); + } + } +} + +void +Moof::ParseTraf(Box& aBox) +{ + Tfhd tfhd(mMoofParser->mTrex); + Tfdt tfdt; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tfhd")) { + tfhd = Tfhd(box, mMoofParser->mTrex); + } else if (box.IsType("tfdt")) { + tfdt = Tfdt(box); + } else if (box.IsType("trun")) { + if (mMoofParser->mTrackId == tfhd.mTrackId) { + ParseTrun(box, tfhd, tfdt); + } + } + } +} + +void +Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt) +{ + if (!mMoofParser->mMdhd.mTimescale) { + return; + } + + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + if ((flags & 0x404) == 0x404) { + // Can't use these flags together + reader->DiscardRemaining(); + return; + } + + uint32_t sampleCount = reader->ReadU32(); + uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0); + bool hasFirstSampleFlags = flags & 4; + uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0; + uint64_t decodeTime = aTfdt.mBaseMediaDecodeTime; + 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() : hasFirstSampleFlags && i == 0 + ? firstSampleFlags + : aTfhd.mDefaultSampleFlags; + uint32_t ctsOffset = flags & 0x800 ? reader->ReadU32() : 0; + + MediaSource::Indice indice; + indice.start_offset = offset; + offset += sampleSize; + indice.end_offset = offset; + + indice.start_composition = + ((decodeTime + ctsOffset) * 1000000ll) / mMoofParser->mMdhd.mTimescale; + decodeTime += sampleDuration; + indice.end_composition = + ((decodeTime + ctsOffset) * 1000000ll) / mMoofParser->mMdhd.mTimescale; + + indice.sync = !(sampleFlags & 0x1010000); + + mMoofParser->mIndex.AppendElement(indice); + } +} + +Tkhd::Tkhd(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + 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(); + } + // More stuff that we don't care about + reader->DiscardRemaining(); +} + +Mdhd::Mdhd(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + 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(); + } + // language and pre_defined=0 + reader->ReadU32(); +} + +Trex::Trex(Box& aBox) +{ + BoxReader reader(aBox); + mFlags = reader->ReadU32(); + mTrackId = reader->ReadU32(); + mDefaultSampleDescriptionIndex = reader->ReadU32(); + mDefaultSampleDuration = reader->ReadU32(); + mDefaultSampleSize = reader->ReadU32(); + mDefaultSampleFlags = reader->ReadU32(); +} + +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); + mFlags = reader->ReadU32(); + mBaseDataOffset = + mFlags & 1 ? reader->ReadU32() : aBox.Parent()->Parent()->Offset(); + mTrackId = reader->ReadU32(); + if (mFlags & 2) { + mDefaultSampleDescriptionIndex = reader->ReadU32(); + } + if (mFlags & 8) { + mDefaultSampleDuration = reader->ReadU32(); + } + if (mFlags & 0x10) { + mDefaultSampleSize = reader->ReadU32(); + } + if (mFlags & 0x20) { + mDefaultSampleFlags = reader->ReadU32(); + } +} + +Tfdt::Tfdt(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + if (version == 0) { + mBaseMediaDecodeTime = reader->ReadU32(); + } else if (version == 1) { + mBaseMediaDecodeTime = reader->ReadU64(); + } + reader->DiscardRemaining(); +} +} 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 000000000000..c5dbf600bd0d --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Box.h @@ -0,0 +1,78 @@ +/* -*- 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 +#include "nsTArray.h" +#include "MediaResource.h" +#include "mozilla/Endian.h" +#include "mp4_demuxer/ByteReader.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +class Stream; + +class BoxContext +{ +public: + BoxContext(Stream* aSource, const nsTArray& aByteRanges) + : mSource(aSource), mByteRanges(aByteRanges) + { + } + + Stream* mSource; + const nsTArray& mByteRanges; +}; + +class Box +{ +public: + Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent = nullptr); + + bool IsAvailable() const { return !mRange.IsNull(); } + uint64_t Offset() const { return mRange.mStart; } + uint64_t Length() const { return mRange.mEnd - mRange.mStart; } + const Box* Parent() const { return mParent; } + + bool IsType(const char* aType) const + { + return mType == BigEndian::readUint32(aType); + } + + Box Next() const; + Box FirstChild() const; + void Read(nsTArray* aDest); + +private: + bool Contains(MediaByteRange aRange) const; + BoxContext* mContext; + mozilla::MediaByteRange mRange; + uint64_t mChildOffset; + uint32_t mType; + const Box* mParent; +}; + +class BoxReader +{ +public: + BoxReader(Box& aBox) + { + aBox.Read(&mBuffer); + mReader.SetData(mBuffer); + } + ByteReader* operator->() { return &mReader; } + ByteReader mReader; + +private: + nsTArray mBuffer; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h index 6f5dd6b6ad2a..8897d976fbff 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h +++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h @@ -5,15 +5,16 @@ #ifndef BYTE_READER_H_ #define BYTE_READER_H_ +#include "mozilla/Endian.h" #include "mozilla/Vector.h" #include "nsTArray.h" -namespace mp4_demuxer -{ +namespace mp4_demuxer { class ByteReader { public: + ByteReader() : mPtr(nullptr), mRemaining(0) {} ByteReader(const mozilla::Vector& aData) : mPtr(&aData[0]), mRemaining(aData.length()) { @@ -22,6 +23,12 @@ public: : mPtr(aData), mRemaining(aSize) { } + void SetData(const nsTArray& aData) + { + MOZ_ASSERT(!mPtr && !mRemaining); + mPtr = &aData[0]; + mRemaining = aData.Length(); + } ~ByteReader() { @@ -56,7 +63,27 @@ public: MOZ_ASSERT(false); return 0; } - return ptr[0] << 8 | ptr[1]; + return mozilla::BigEndian::readUint16(ptr); + } + + uint32_t ReadU32() + { + auto ptr = Read(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint32(ptr); + } + + uint64_t ReadU64() + { + auto ptr = Read(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint64(ptr); } const uint8_t* Read(size_t aCount) diff --git a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h index 288cb9376b4f..c59fded4f002 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h +++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h @@ -67,20 +67,31 @@ public: nsTArray iv; }; -class AudioDecoderConfig +class TrackConfig +{ +public: + TrackConfig() : mime_type(nullptr), mTrackId(0), duration(0) {} + const char* mime_type; + uint32_t mTrackId; + int64_t duration; + CryptoTrack crypto; + + void Update(stagefright::sp& aMetaData, + const char* aMimeType); +}; + +class AudioDecoderConfig : public TrackConfig { public: AudioDecoderConfig() - : mime_type(nullptr) - , duration(0) - , channel_count(0) + : channel_count(0) , bits_per_sample(0) , samples_per_second(0) + , frequency_index(0) , aac_profile(0) { } - const char* mime_type; int64_t duration; uint32_t channel_count; uint32_t bits_per_sample; @@ -90,7 +101,8 @@ public: mozilla::Vector audio_specific_config; CryptoTrack crypto; - void Update(stagefright::sp& aMetaData, const char* aMimeType); + void Update(stagefright::sp& aMetaData, + const char* aMimeType); bool IsValid(); private: @@ -98,27 +110,19 @@ private: int8_t aac_profile; }; -class VideoDecoderConfig +class VideoDecoderConfig : public TrackConfig { public: - VideoDecoderConfig() - : mime_type(nullptr) - , duration(0) - , display_width(0) - , display_height(0) - { - } + VideoDecoderConfig() : display_width(0), display_height(0) {} - const char* mime_type; - int64_t duration; int32_t display_width; int32_t display_height; mozilla::Vector extra_data; // Unparsed AVCDecoderConfig payload. mozilla::Vector annex_b; // Parsed version for sample prepend. - CryptoTrack crypto; - void Update(stagefright::sp& aMetaData, const char* aMimeType); + void Update(stagefright::sp& aMetaData, + const char* aMimeType); bool IsValid(); }; diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h index 01fe1ce8271d..81bb23824ade 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/Index.h +++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h @@ -6,26 +6,28 @@ #define INDEX_H_ #include "media/stagefright/MediaSource.h" +#include "mozilla/Monitor.h" #include "mp4_demuxer/mp4_demuxer.h" namespace mp4_demuxer { template class Interval; +class MoofParser; class Index { public: - Index() {} - - void Init( - const stagefright::Vector& aIndex); + Index(const stagefright::Vector& aIndex, + Stream* aSource, uint32_t aTrackId); void ConvertByteRangesToTimeRanges( const nsTArray& aByteRanges, nsTArray>* aTimeRanges); private: + mozilla::Monitor mMonitor; nsTArray mIndex; + nsAutoPtr mMoofParser; }; } 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 000000000000..25d5074cf7c5 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -0,0 +1,119 @@ +/* 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 "media/stagefright/MediaSource.h" +#include "mp4_demuxer/mp4_demuxer.h" + +namespace mozilla { class MediaByteRange; } + +namespace mp4_demuxer { + +class Stream; +class Box; + +class Tkhd +{ +public: + Tkhd() + : mCreationTime(0) + , mModificationTime(0) + , mTrackId(0) + , mDuration(0) + { + } + Tkhd(Box& aBox); + + uint64_t mCreationTime; + uint64_t mModificationTime; + uint32_t mTrackId; + uint64_t mDuration; +}; + +class Mdhd +{ +public: + Mdhd() + : mCreationTime(0) + , mModificationTime(0) + , mTimescale(0) + , mDuration(0) + { + } + Mdhd(Box& aBox); + + uint64_t mCreationTime; + uint64_t mModificationTime; + uint32_t mTimescale; + uint64_t mDuration; +}; + +class Trex +{ +public: + Trex() + : mFlags(0) + , mTrackId(0) + , mDefaultSampleDescriptionIndex(0) + , mDefaultSampleDuration(0) + , mDefaultSampleSize(0) + , mDefaultSampleFlags(0) + { + } + + 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: + Tfhd(Trex& aTrex) : Trex(aTrex), mBaseDataOffset(0) {} + Tfhd(Box& aBox, Trex& aTrex); + + uint64_t mBaseDataOffset; +}; + +class Tfdt +{ +public: + Tfdt() : mBaseMediaDecodeTime(0) {} + Tfdt(Box& aBox); + + uint64_t mBaseMediaDecodeTime; +}; + +class MoofParser +{ +public: + MoofParser(Stream* aSource, uint32_t aTrackId) + : mSource(aSource), mTrackId(aTrackId) + { + } + void RebuildFragmentedIndex( + const nsTArray& aByteRanges); + void ParseMoov(Box& aBox); + void ParseTrak(Box& aBox); + void ParseMdia(Box& aBox, Tkhd& aTkhd); + void ParseMvex(Box& aBox); + + nsRefPtr mSource; + uint32_t mTrackId; + nsTArray mMoofOffsets; + Mdhd mMdhd; + Trex mTrex; + Tfdt mTfdt; + nsTArray mIndex; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h index f322691aa239..72fa4c6690c8 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h +++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h @@ -9,6 +9,7 @@ #include "nsTArray.h" #include "mp4_demuxer/DecoderData.h" #include "mp4_demuxer/Interval.h" +#include "nsISupportsImpl.h" namespace mozilla { class MediaByteRange; } @@ -20,12 +21,14 @@ typedef int64_t Microseconds; 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 Length(int64_t* size) = 0; +protected: virtual ~Stream() {} }; @@ -66,6 +69,7 @@ private: CryptoFile mCrypto; nsAutoPtr mPrivate; + nsRefPtr mSource; }; } // namespace mozilla diff --git a/media/libstagefright/binding/mp4_demuxer.cpp b/media/libstagefright/binding/mp4_demuxer.cpp index a3078ef27bfb..f4d9242bb129 100644 --- a/media/libstagefright/binding/mp4_demuxer.cpp +++ b/media/libstagefright/binding/mp4_demuxer.cpp @@ -24,11 +24,11 @@ struct StageFrightPrivate sp mAudio; MediaSource::ReadOptions mAudioOptions; - Index mAudioIndex; + nsAutoPtr mAudioIndex; sp mVideo; MediaSource::ReadOptions mVideoOptions; - Index mVideoIndex; + nsAutoPtr mVideoIndex; }; class DataSourceAdapter : public DataSource @@ -66,11 +66,11 @@ public: virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; } private: - nsAutoPtr mSource; + nsRefPtr mSource; }; MP4Demuxer::MP4Demuxer(Stream* source) - : mPrivate(new StageFrightPrivate()) + : mPrivate(new StageFrightPrivate()), mSource(source) { mPrivate->mExtractor = new MPEG4Extractor(new DataSourceAdapter(source)); } @@ -102,13 +102,13 @@ MP4Demuxer::Init() mPrivate->mAudio->start(); mAudioConfig.Update(metaData, mimeType); auto index = mPrivate->mAudio->exportIndex(); - mPrivate->mAudioIndex.Init(index); + mPrivate->mAudioIndex = new Index(index, mSource, mAudioConfig.mTrackId); } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) { mPrivate->mVideo = e->getTrack(i); mPrivate->mVideo->start(); mVideoConfig.Update(metaData, mimeType); auto index = mPrivate->mVideo->exportIndex(); - mPrivate->mVideoIndex.Init(index); + mPrivate->mVideoIndex = new Index(index, mSource, mVideoConfig.mTrackId); } } sp metaData = e->getMetaData(); @@ -207,22 +207,22 @@ MP4Demuxer::ConvertByteRangesToTime( if (HasValidVideo()) { nsTArray> ranges; if (!HasValidAudio()) { - mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges, - aIntervals); + mPrivate->mVideoIndex->ConvertByteRangesToTimeRanges(aByteRanges, + aIntervals); return; } - mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges, &video); + mPrivate->mVideoIndex->ConvertByteRangesToTimeRanges(aByteRanges, &video); } nsTArray> audio; if (HasValidAudio()) { nsTArray> ranges; if (!HasValidVideo()) { - mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges, - aIntervals); + mPrivate->mAudioIndex->ConvertByteRangesToTimeRanges(aByteRanges, + aIntervals); return; } - mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges, &audio); + mPrivate->mAudioIndex->ConvertByteRangesToTimeRanges(aByteRanges, &audio); } Interval::Intersection(audio, video, aIntervals); diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index b70a4f10712a..4549779ad231 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -3653,6 +3653,10 @@ status_t MPEG4Source::fragmentedRead( Vector MPEG4Source::exportIndex() { Vector index; + if (!mTimescale) { + return index; + } + for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples(); sampleIndex++) { off64_t offset; diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build index 977404e16998..e3d199268eb6 100644 --- a/media/libstagefright/moz.build +++ b/media/libstagefright/moz.build @@ -64,8 +64,10 @@ SOURCES += [ UNIFIED_SOURCES += [ 'binding/Adts.cpp', 'binding/AnnexB.cpp', + 'binding/Box.cpp', 'binding/DecoderData.cpp', 'binding/Index.cpp', + 'binding/MoofParser.cpp', 'binding/mp4_demuxer.cpp', 'frameworks/av/media/libstagefright/DataSource.cpp', 'frameworks/av/media/libstagefright/ESDS.cpp',