зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1045909 - Fix buffer range calculation for fMP4; r=edwin
This commit is contained in:
Родитель
06a6e9f955
Коммит
d66cd54dfd
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
mozilla::Monitor mMonitor;
|
||||
nsTArray<stagefright::MediaSource::Indice> mIndex;
|
||||
nsAutoPtr<MoofParser> mMoofParser;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
|
|
Загрузка…
Ссылка в новой задаче