Bug 1045909 - Fix buffer range calculation for fMP4; r=edwin

This commit is contained in:
Anthony Jones 2014-08-05 13:35:04 +12:00
parent 901eef7d13
commit 1d729bea82
15 changed files with 703 additions and 58 deletions

View File

@ -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<MediaByteRange> ranges;
if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) {
return NS_ERROR_FAILURE;
return;
}
nsTArray<Interval<Microseconds>> 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;

View File

@ -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<nsTArray<uint8_t>> mInitDataEncountered;
Monitor mTimeRangesMonitor;
nsTArray<mp4_demuxer::Interval<Microseconds>> mTimeRanges;
// True if we've read the streams' metadata.
bool mDemuxerInitialized;

View File

@ -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<uint8_t>* 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();
}
}
}

View File

@ -124,11 +124,19 @@ CryptoSample::Update(sp<MetaData>& aMetaData)
}
void
AudioDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
TrackConfig::Update(sp<MetaData>& 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<MetaData>& 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<MetaData>& aMetaData, const char* aMimeType)
size);
}
}
crypto.Update(aMetaData);
}
bool
@ -159,9 +165,7 @@ AudioDecoderConfig::IsValid()
void
VideoDecoderConfig::Update(sp<MetaData>& 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<MetaData>& aMetaData, const char* aMimeType)
extra_data[4] |= 3;
annex_b = AnnexB::ConvertExtraDataToAnnexB(extra_data);
}
crypto.Update(aMetaData);
}
bool

View File

@ -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<MediaSource::Indice>& aIndex)
Index::Index(const stagefright::Vector<MediaSource::Indice>& 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<MediaByteRange>& aByteRanges,
nsTArray<Interval<Microseconds>>* aTimeRanges)
{
nsTArray<stagefright::MediaSource::Indice> moofIndex;
nsTArray<stagefright::MediaSource::Indice>* 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<Interval<Microseconds>> 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

View File

@ -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<MediaByteRange>& 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();
}
}

View File

@ -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 <stdint.h>
#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<MediaByteRange>& aByteRanges)
: mSource(aSource), mByteRanges(aByteRanges)
{
}
Stream* mSource;
const nsTArray<MediaByteRange>& 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<uint8_t>* 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<uint8_t> mBuffer;
};
}
#endif

View File

@ -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<uint8_t>& aData)
: mPtr(&aData[0]), mRemaining(aData.length())
{
@ -22,6 +23,12 @@ public:
: mPtr(aData), mRemaining(aSize)
{
}
void SetData(const nsTArray<uint8_t>& 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)

View File

@ -67,20 +67,31 @@ public:
nsTArray<uint8_t> 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<stagefright::MetaData>& 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<uint8_t> audio_specific_config;
CryptoTrack crypto;
void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
void Update(stagefright::sp<stagefright::MetaData>& 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<uint8_t> extra_data; // Unparsed AVCDecoderConfig payload.
mozilla::Vector<uint8_t> annex_b; // Parsed version for sample prepend.
CryptoTrack crypto;
void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
void Update(stagefright::sp<stagefright::MetaData>& aMetaData,
const char* aMimeType);
bool IsValid();
};

View File

@ -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 <typename T> class Interval;
class MoofParser;
class Index
{
public:
Index() {}
void Init(
const stagefright::Vector<stagefright::MediaSource::Indice>& aIndex);
Index(const stagefright::Vector<stagefright::MediaSource::Indice>& aIndex,
Stream* aSource, uint32_t aTrackId);
void ConvertByteRangesToTimeRanges(
const nsTArray<mozilla::MediaByteRange>& aByteRanges,
nsTArray<Interval<Microseconds>>* aTimeRanges);
private:
Monitor mMonitor;
nsTArray<stagefright::MediaSource::Indice> mIndex;
nsAutoPtr<MoofParser> mMoofParser;
};
}

View File

@ -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<mozilla::MediaByteRange>& aByteRanges);
void ParseMoov(Box& aBox);
void ParseTrak(Box& aBox);
void ParseMdia(Box& aBox, Tkhd& aTkhd);
void ParseMvex(Box& aBox);
nsRefPtr<Stream> mSource;
uint32_t mTrackId;
nsTArray<uint64_t> mMoofOffsets;
Mdhd mMdhd;
Trex mTrex;
Tfdt mTfdt;
nsTArray<stagefright::MediaSource::Indice> mIndex;
};
}
#endif

View File

@ -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<StageFrightPrivate> mPrivate;
nsRefPtr<Stream> mSource;
};
} // namespace mozilla

View File

@ -24,11 +24,11 @@ struct StageFrightPrivate
sp<MediaSource> mAudio;
MediaSource::ReadOptions mAudioOptions;
Index mAudioIndex;
nsAutoPtr<Index> mAudioIndex;
sp<MediaSource> mVideo;
MediaSource::ReadOptions mVideoOptions;
Index mVideoIndex;
nsAutoPtr<Index> mVideoIndex;
};
class DataSourceAdapter : public DataSource
@ -66,11 +66,11 @@ public:
virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; }
private:
nsAutoPtr<Stream> mSource;
nsRefPtr<Stream> 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> metaData = e->getMetaData();
@ -207,22 +207,22 @@ MP4Demuxer::ConvertByteRangesToTime(
if (HasValidVideo()) {
nsTArray<Interval<Microseconds>> 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<Interval<Microseconds>> audio;
if (HasValidAudio()) {
nsTArray<Interval<Microseconds>> 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<Microseconds>::Intersection(audio, video, aIntervals);

View File

@ -3653,6 +3653,10 @@ status_t MPEG4Source::fragmentedRead(
Vector<MediaSource::Indice> MPEG4Source::exportIndex()
{
Vector<Indice> index;
if (!mTimescale) {
return index;
}
for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples();
sampleIndex++) {
off64_t offset;

View File

@ -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',