Bug 1098126 - Add CENC support to MoofParser; r=edwin

This commit is contained in:
Anthony Jones 2014-12-15 17:43:59 +13:00
parent 2140036c4f
commit 8a5dd08528
11 changed files with 258 additions and 38 deletions

View File

@ -165,6 +165,8 @@ public:
std::max(mEnd, aByteRange.mEnd));
}
int64_t Length() { return mEnd - mStart; }
int64_t mStart, mEnd;
};

View File

@ -13,7 +13,7 @@ using namespace mozilla;
namespace mp4_demuxer {
Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
: mContext(aContext), mType(0), mParent(aParent)
: mContext(aContext), mParent(aParent)
{
uint8_t header[8];
MediaByteRange headerRange(aOffset, aOffset + sizeof(header));
@ -69,7 +69,7 @@ Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent)
}
Box::Box()
: mContext(nullptr), mType(0)
: mContext(nullptr)
{}
Box

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mp4_demuxer/ByteReader.h"
#include "mp4_demuxer/Index.h"
#include "mp4_demuxer/Interval.h"
#include "mp4_demuxer/MoofParser.h"
@ -84,27 +85,54 @@ SampleIterator::SampleIterator(Index* aIndex)
MP4Sample* SampleIterator::GetNext()
{
nsAutoPtr<MP4Sample> sample(Get());
if (!sample) {
Sample* s(Get());
if (!s) {
return nullptr;
}
Next();
nsAutoPtr<MP4Sample> sample(new MP4Sample());
sample->decode_timestamp = s->mDecodeTime;
sample->composition_timestamp = s->mCompositionRange.start;
sample->duration = s->mCompositionRange.Length();
sample->byte_offset = s->mByteRange.mStart;
sample->is_sync_point = s->mSync;
sample->size = s->mByteRange.Length();
// Do the blocking read
sample->data = sample->extra_buffer = new uint8_t[sample->size];
size_t bytesRead;
mIndex->mSource->ReadAt(sample->byte_offset, sample->data, sample->size,
&bytesRead);
if (!mIndex->mSource->ReadAt(sample->byte_offset, sample->data, sample->size,
&bytesRead) || bytesRead != sample->size) {
return nullptr;
}
// Lets just return what we've got so that we propagate the error
sample->size = bytesRead;
Next();
if (!s->mCencRange.IsNull()) {
// The size comes from an 8 bit field
nsAutoTArray<uint8_t, 256> cenc;
cenc.SetLength(s->mCencRange.Length());
if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, &cenc[0], cenc.Length(),
&bytesRead) || bytesRead != cenc.Length()) {
return nullptr;
}
ByteReader reader(cenc);
sample->crypto.valid = true;
reader.ReadArray(sample->crypto.iv, 16);
if (reader.Remaining()) {
uint16_t count = reader.ReadU16();
for (size_t i = 0; i < count; i++) {
sample->crypto.plain_sizes.AppendElement(reader.ReadU16());
sample->crypto.encrypted_sizes.AppendElement(reader.ReadU32());
}
}
}
return sample.forget();
}
MP4Sample* SampleIterator::Get()
Sample* SampleIterator::Get()
{
if (!mIndex->mMoofParser) {
return nullptr;
@ -114,7 +142,7 @@ MP4Sample* SampleIterator::Get()
while (true) {
if (mCurrentMoof == moofs.Length()) {
if (!mIndex->mMoofParser->BlockingReadNextMoof()) {
return nsAutoPtr<MP4Sample>();
return nullptr;
}
MOZ_ASSERT(mCurrentMoof < moofs.Length());
}
@ -124,17 +152,7 @@ MP4Sample* SampleIterator::Get()
mCurrentSample = 0;
++mCurrentMoof;
}
Sample& s = moofs[mCurrentMoof].mIndex[mCurrentSample];
nsAutoPtr<MP4Sample> sample(new MP4Sample());
sample->decode_timestamp = s.mDecodeTime;
sample->composition_timestamp = s.mCompositionRange.start;
sample->duration = s.mCompositionRange.end - s.mCompositionRange.start;
sample->byte_offset = s.mByteRange.mStart;
sample->is_sync_point = s.mSync;
sample->size = s.mByteRange.mEnd - s.mByteRange.mStart;
return sample.forget();
return &moofs[mCurrentMoof].mIndex[mCurrentSample];
}
void SampleIterator::Next()
@ -148,16 +166,16 @@ void SampleIterator::Seek(Microseconds aTime)
size_t syncSample = 0;
mCurrentMoof = 0;
mCurrentSample = 0;
while (true) {
nsAutoPtr<MP4Sample> sample(Get());
if (sample->composition_timestamp > aTime) {
Sample* sample;
while (!!(sample = Get())) {
if (sample->mCompositionRange.start > aTime) {
break;
}
if (sample->is_sync_point) {
if (sample->mSync) {
syncMoof = mCurrentMoof;
syncSample = mCurrentSample;
}
if (sample->composition_timestamp == aTime) {
if (sample->mCompositionRange.start == aTime) {
break;
}
Next();

View File

@ -173,6 +173,71 @@ Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts) :
ParseTraf(box, aTrex, aMdhd, aEdts);
}
}
ProcessCenc();
}
bool
Moof::GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges)
{
aByteRanges->Clear();
Saiz* saiz = nullptr;
for (int i = 0; ; i++) {
if (i == mSaizs.Length()) {
return false;
}
if (mSaizs[i].mAuxInfoType == aType) {
saiz = &mSaizs[i];
break;
}
}
Saio* saio = nullptr;
for (int i = 0; ; i++) {
if (i == mSaios.Length()) {
return false;
}
if (mSaios[i].mAuxInfoType == aType) {
saio = &mSaios[i];
break;
}
}
if (saio->mOffsets.Length() == 1) {
aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
uint64_t offset = mRange.mStart + saio->mOffsets[0];
for (size_t i = 0; i < saiz->mSampleInfoSize.Length(); i++) {
aByteRanges->AppendElement(
MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
offset += saiz->mSampleInfoSize[i];
}
return true;
}
if (saio->mOffsets.Length() == saiz->mSampleInfoSize.Length()) {
aByteRanges->SetCapacity(saiz->mSampleInfoSize.Length());
for (size_t i = 0; i < saio->mOffsets.Length(); i++) {
uint64_t offset = mRange.mStart + saio->mOffsets[i];
aByteRanges->AppendElement(
MediaByteRange(offset, offset + saiz->mSampleInfoSize[i]));
}
return true;
}
return false;
}
bool
Moof::ProcessCenc()
{
nsTArray<MediaByteRange> cencRanges;
if (!GetAuxInfo(AtomType("cenc"), &cencRanges) ||
cencRanges.Length() != mIndex.Length()) {
return false;
}
for (int i = 0; i < cencRanges.Length(); i++) {
mIndex[i].mCencRange = cencRanges[i];
}
return true;
}
void
@ -191,6 +256,10 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts)
if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) {
ParseTrun(box, tfhd, tfdt, aMdhd, aEdts);
}
} else if (box.IsType("saiz")) {
mSaizs.AppendElement(Saiz(box));
} else if (box.IsType("saio")) {
mSaios.AppendElement(Saio(box));
}
}
}
@ -285,7 +354,7 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd, Edts& aEdts)
}
ctsOrder.Sort(CtsComparator());
for (int i = 0; i < ctsOrder.Length(); i++) {
for (size_t i = 0; i < ctsOrder.Length(); i++) {
if (i + 1 < ctsOrder.Length()) {
ctsOrder[i]->mCompositionRange.end = ctsOrder[i + 1]->mCompositionRange.start;
}
@ -417,4 +486,48 @@ Edts::Edts(Box& aBox)
NS_ASSERTION(segment_duration == 0, "Can't handle edits with fixed durations");
reader->DiscardRemaining();
}
Saiz::Saiz(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0)
{
BoxReader reader(aBox);
uint32_t flags = reader->ReadU32();
uint8_t version = flags >> 24;
if (flags & 1) {
mAuxInfoType = reader->ReadU32();
mAuxInfoTypeParameter = reader->ReadU32();
}
uint8_t defaultSampleInfoSize = reader->ReadU8();
uint32_t count = reader->ReadU32();
if (defaultSampleInfoSize) {
for (int i = 0; i < count; i++) {
mSampleInfoSize.AppendElement(defaultSampleInfoSize);
}
} else {
reader->ReadArray(mSampleInfoSize, count);
}
}
Saio::Saio(Box& aBox) : mAuxInfoType("sinf"), mAuxInfoTypeParameter(0)
{
BoxReader reader(aBox);
uint32_t flags = reader->ReadU32();
uint8_t version = flags >> 24;
if (flags & 1) {
mAuxInfoType = reader->ReadU32();
mAuxInfoTypeParameter = reader->ReadU32();
}
size_t count = reader->ReadU32();
mOffsets.SetCapacity(count);
if (version == 0) {
for (size_t i = 0; i < count; i++) {
mOffsets.AppendElement(reader->ReadU32());
}
} else {
for (size_t i = 0; i < count; i++) {
mOffsets.AppendElement(reader->ReadU64());
}
}
}
}

View File

@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ATOM_TYPE_H_
#define ATOM_TYPE_H_
#include <stdint.h>
#include "mozilla/Endian.h"
using namespace mozilla;
namespace mp4_demuxer {
class AtomType
{
public:
AtomType() : mType(0) { }
AtomType(uint32_t aType) : mType(aType) { }
AtomType(const char* aType) : mType(BigEndian::readUint32(aType)) { }
bool operator==(const AtomType& aType) const { return mType == aType.mType; }
private:
uint32_t mType;
};
}
#endif

View File

@ -11,6 +11,7 @@
#include "nsTArray.h"
#include "MediaResource.h"
#include "mozilla/Endian.h"
#include "mp4_demuxer/AtomType.h"
#include "mp4_demuxer/ByteReader.h"
using namespace mozilla;
@ -42,13 +43,8 @@ public:
uint64_t Length() const { return mRange.mEnd - mRange.mStart; }
uint64_t NextOffset() const { return mRange.mEnd; }
const MediaByteRange& Range() const { return mRange; }
const Box* Parent() const { return mParent; }
bool IsType(const char* aType) const
{
return mType == BigEndian::readUint32(aType);
}
bool IsType(const char* aType) const { return mType == AtomType(aType); }
Box Next() const;
Box FirstChild() const;
@ -59,7 +55,7 @@ private:
BoxContext* mContext;
mozilla::MediaByteRange mRange;
uint64_t mChildOffset;
uint32_t mType;
AtomType mType;
const Box* mParent;
};

View File

@ -23,6 +23,12 @@ public:
: mPtr(aData), mRemaining(aSize)
{
}
template<size_t S>
ByteReader(const nsAutoTArray<uint8_t, S>& aData)
: mPtr(&aData[0]), mRemaining(aData.Length())
{
}
void SetData(const nsTArray<uint8_t>& aData)
{
MOZ_ASSERT(!mPtr && !mRemaining);

View File

@ -24,7 +24,7 @@ public:
void Seek(Microseconds aTime);
private:
MP4Sample* Get();
Sample* Get();
void Next();
nsRefPtr<Index> mIndex;
size_t mCurrentMoof;

View File

@ -5,6 +5,7 @@
#ifndef MOOF_PARSER_H_
#define MOOF_PARSER_H_
#include "mp4_demuxer/AtomType.h"
#include "mp4_demuxer/mp4_demuxer.h"
#include "MediaResource.h"
@ -109,15 +110,49 @@ public:
struct Sample
{
mozilla::MediaByteRange mByteRange;
mozilla::MediaByteRange mCencRange;
Microseconds mDecodeTime;
Interval<Microseconds> mCompositionRange;
bool mSync;
};
class Saiz
{
public:
Saiz(Box& aBox);
AtomType mAuxInfoType;
uint32_t mAuxInfoTypeParameter;
nsTArray<uint8_t> mSampleInfoSize;
};
class Saio
{
public:
Saio(Box& aBox);
AtomType mAuxInfoType;
uint32_t mAuxInfoTypeParameter;
nsTArray<uint64_t> mOffsets;
};
class AuxInfo {
public:
AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio);
bool GetByteRanges(nsTArray<MediaByteRange>* aByteRanges);
private:
int64_t mMoofOffset;
Saiz& mSaiz;
Saio& mSaio;
};
class Moof
{
public:
Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts);
bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges);
void FixRounding(const Moof& aMoof);
mozilla::MediaByteRange mRange;
@ -125,9 +160,15 @@ public:
Interval<Microseconds> mTimeRange;
nsTArray<Sample> mIndex;
nsTArray<Saiz> mSaizs;
nsTArray<Saio> mSaios;
private:
void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd, Edts& aEdts);
void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd, Edts& aEdts);
void ParseSaiz(Box& aBox);
void ParseSaio(Box& aBox);
bool ProcessCenc();
uint64_t mMaxRoundingError;
};

View File

@ -185,7 +185,15 @@ MP4Sample*
MP4Demuxer::DemuxAudioSample()
{
if (mPrivate->mAudioIterator) {
return mPrivate->mAudioIterator->GetNext();
nsAutoPtr<MP4Sample> sample(mPrivate->mAudioIterator->GetNext());
if (sample) {
if (sample->crypto.valid) {
sample->crypto.mode = mAudioConfig.crypto.mode;
sample->crypto.iv_size = mAudioConfig.crypto.iv_size;
sample->crypto.key.AppendElements(mAudioConfig.crypto.key);
}
}
return sample.forget();
}
nsAutoPtr<MP4Sample> sample(new MP4Sample());
@ -209,6 +217,11 @@ MP4Demuxer::DemuxVideoSample()
nsAutoPtr<MP4Sample> sample(mPrivate->mVideoIterator->GetNext());
if (sample) {
sample->prefix_data = mVideoConfig.annex_b;
if (sample->crypto.valid) {
sample->crypto.mode = mVideoConfig.crypto.mode;
sample->crypto.iv_size = mVideoConfig.crypto.iv_size;
sample->crypto.key.AppendElements(mVideoConfig.crypto.key);
}
}
return sample.forget();
}

View File

@ -50,6 +50,7 @@ if CONFIG['OS_TARGET'] != 'Android':
EXPORTS.mp4_demuxer += [
'binding/include/mp4_demuxer/Adts.h',
'binding/include/mp4_demuxer/AnnexB.h',
'binding/include/mp4_demuxer/AtomType.h',
'binding/include/mp4_demuxer/BufferStream.h',
'binding/include/mp4_demuxer/ByteReader.h',
'binding/include/mp4_demuxer/DecoderData.h',