Bug 1195723: [ogg/flac] P3. Add flac support in ogg. r?kamidphish
This feature is intended to debug the flac parser only and is behind a hidden pref.
There's lots of redundant code in OggCodecState, there's need for a serious cleanup there.
MozReview-Commit-ID: 9H4efd2cfuE
--- a/dom/media/ogg/OggCodecState.cpp
+++ b/dom/media/ogg/OggCodecState.cpp
@@ -44,20 +44,22 @@ OggCodecState::Create(ogg_page* aPage)
if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
codecState = new TheoraState(aPage);
} else if (aPage->body_len > 6 && memcmp(aPage->body+1, "vorbis", 6) == 0) {
codecState = new VorbisState(aPage);
} else if (aPage->body_len > 8 && memcmp(aPage->body, "OpusHead", 8) == 0) {
codecState = new OpusState(aPage);
} else if (aPage->body_len > 8 && memcmp(aPage->body, "fishead\0", 8) == 0) {
codecState = new SkeletonState(aPage);
+ } else if (aPage->body_len > 5 && memcmp(aPage->body, "\177FLAC", 5) == 0) {
+ codecState = new FlacState(aPage);
} else {
codecState = new OggCodecState(aPage, false);
}
- return codecState->OggCodecState::Init() ? codecState.forget() : nullptr;
+ return codecState->OggCodecState::InternalInit() ? codecState.forget() : nullptr;
}
OggCodecState::OggCodecState(ogg_page* aBosPage, bool aActive)
: mPacketCount(0)
, mSerial(ogg_page_serialno(aBosPage))
, mActive(aActive)
, mDoneReadingHeaders(!aActive)
{
@@ -92,17 +94,17 @@ OggCodecState::ClearUnstamped()
{
for (uint32_t i = 0; i < mUnstamped.Length(); ++i) {
OggCodecState::ReleasePacket(mUnstamped[i]);
}
mUnstamped.Clear();
}
bool
-OggCodecState::Init()
+OggCodecState::InternalInit()
{
int ret = ogg_stream_init(&mState, mSerial);
return ret == 0;
}
bool
OggCodecState::IsValidVorbisTagName(nsCString& aName)
{
@@ -825,17 +827,17 @@ VorbisState::ReconstructVorbisGranulepos
// (for example if the stream was truncated).
//
// We validate our prediction of the number of samples decoded when
// VALIDATE_VORBIS_SAMPLE_CALCULATION is defined by recording the predicted
// number of samples, and verifing we extract that many when decoding
// each packet.
NS_ASSERTION(mUnstamped.Length() > 0, "Length must be > 0");
- ogg_packet* last = mUnstamped[mUnstamped.Length()-1];
+ ogg_packet* last = mUnstamped.LastElement();
NS_ASSERTION(last->e_o_s || last->granulepos >= 0,
"Must know last granulepos!");
if (mUnstamped.Length() == 1) {
ogg_packet* packet = mUnstamped[0];
long blockSize = vorbis_packet_blocksize(&mInfo, packet);
if (blockSize < 0) {
// On failure vorbis_packet_blocksize returns < 0. If we've got
// a bad packet, we just assume that decode will have to skip this
@@ -1139,17 +1141,17 @@ OpusState::PacketDuration(ogg_packet* aP
CheckedInt64 t = SaferMultDiv(GetOpusDeltaGP(aPacket), USECS_PER_S, 48000);
return t.isValid() ? t.value() : -1;
}
bool
OpusState::ReconstructOpusGranulepos(void)
{
NS_ASSERTION(mUnstamped.Length() > 0, "Must have unstamped packets");
- ogg_packet* last = mUnstamped[mUnstamped.Length()-1];
+ ogg_packet* last = mUnstamped.LastElement();
NS_ASSERTION(last->e_o_s || last->granulepos > 0,
"Must know last granulepos!");
int64_t gp;
// If this is the last page, and we've seen at least one previous page (or
// this is the first page)...
if (last->e_o_s) {
if (mPrevPageGranulepos != -1) {
// If this file only has one page and the final granule position is
@@ -1225,16 +1227,144 @@ OpusState::ReconstructOpusGranulepos(voi
// We MUST reject such streams.
if (!mDoneReadingHeaders && GetOpusDeltaGP(mUnstamped[0]) > gp) {
return false;
}
mPrevPageGranulepos = last->granulepos;
return true;
}
+FlacState::FlacState(ogg_page* aBosPage)
+ : OggCodecState(aBosPage, true)
+{
+}
+
+bool
+FlacState::DecodeHeader(ogg_packet* aPacket)
+{
+ nsAutoRef<ogg_packet> autoRelease(aPacket);
+
+ if (!mParser.DecodeHeaderBlock(aPacket->packet, aPacket->bytes)) {
+ return false;
+ }
+ if (mParser.HasFullMetadata()) {
+ mDoneReadingHeaders = true;
+ }
+ return true;
+}
+
+int64_t
+FlacState::Time(int64_t granulepos)
+{
+ if (!mParser.mInfo.IsValid()) {
+ return -1;
+ }
+ CheckedInt64 t =
+ SaferMultDiv(granulepos, USECS_PER_S, mParser.mInfo.mRate);
+ if (!t.isValid()) {
+ return -1;
+ }
+ return t.value();
+}
+
+int64_t
+FlacState::PacketDuration(ogg_packet* aPacket)
+{
+ return mParser.BlockDuration(aPacket->packet, aPacket->bytes);
+}
+
+bool
+FlacState::IsHeader(ogg_packet* aPacket)
+{
+ return mParser.IsHeaderBlock(aPacket->packet, aPacket->bytes);
+}
+
+nsresult
+FlacState::PageIn(ogg_page* aPage)
+{
+ if (!mActive) {
+ return NS_OK;
+ }
+ NS_ASSERTION(static_cast<uint32_t>(ogg_page_serialno(aPage)) == mSerial,
+ "Page must be for this stream!");
+ if (ogg_stream_pagein(&mState, aPage) == -1)
+ return NS_ERROR_FAILURE;
+ bool foundGp;
+ nsresult res = PacketOutUntilGranulepos(foundGp);
+ if (NS_FAILED(res)) {
+ return res;
+ }
+ if (foundGp && mDoneReadingHeaders) {
+ // We've found a packet with a granulepos, and we've loaded our metadata
+ // and initialized our decoder. Determine granulepos of buffered packets.
+ ReconstructFlacGranulepos();
+ for (uint32_t i = 0; i < mUnstamped.Length(); ++i) {
+ ogg_packet* packet = mUnstamped[i];
+ NS_ASSERTION(!IsHeader(packet), "Don't try to recover header packet gp");
+ NS_ASSERTION(packet->granulepos != -1, "Packet must have gp by now");
+ mPackets.Append(packet);
+ }
+ mUnstamped.Clear();
+ }
+ return NS_OK;
+}
+
+// Return a hash table with tag metadata.
+MetadataTags*
+FlacState::GetTags()
+{
+ return mParser.GetTags();
+}
+
+const AudioInfo&
+FlacState::Info()
+{
+ return mParser.mInfo;
+}
+
+bool
+FlacState::ReconstructFlacGranulepos(void)
+{
+ NS_ASSERTION(mUnstamped.Length() > 0, "Must have unstamped packets");
+ ogg_packet* last = mUnstamped.LastElement();
+ NS_ASSERTION(last->e_o_s || last->granulepos > 0,
+ "Must know last granulepos!");
+ int64_t gp;
+
+ gp = last->granulepos;
+ // Loop through the packets backwards, subtracting the next
+ // packet's duration from its granulepos to get the value
+ // for the current packet.
+ for (uint32_t i = mUnstamped.Length() - 1; i > 0; i--) {
+ int offset =
+ mParser.BlockDuration(mUnstamped[i]->packet, mUnstamped[i]->bytes);
+ // Check for error (negative offset) and overflow.
+ if (offset >= 0) {
+ if (offset <= gp) {
+ gp -= offset;
+ } else {
+ // If the granule position of the first data page is smaller than the
+ // number of decodable audio samples on that page, then we MUST reject
+ // the stream.
+ if (!mDoneReadingHeaders) {
+ return false;
+ }
+ // It's too late to reject the stream.
+ // If we get here, this almost certainly means the file has screwed-up
+ // timestamps somewhere after the first page.
+ NS_WARNING("Clamping negative granulepos to zero.");
+ gp = 0;
+ }
+ }
+ mUnstamped[i - 1]->granulepos = gp;
+ }
+
+ return true;
+}
+
SkeletonState::SkeletonState(ogg_page* aBosPage)
: OggCodecState(aBosPage, true)
, mVersion(0)
, mPresentationTime(0)
, mLength(0)
{
MOZ_COUNT_CTOR(SkeletonState);
}
@@ -1661,11 +1791,10 @@ SkeletonState::DecodeHeader(ogg_packet*
return DecodeFisbone(aPacket);
} else if (aPacket->e_o_s) {
mDoneReadingHeaders = true;
return true;
}
return true;
}
-
} // namespace mozilla
--- a/dom/media/ogg/OggCodecState.h
+++ b/dom/media/ogg/OggCodecState.h
@@ -20,16 +20,17 @@
#include "MediaDecoderStateMachine.h"
#include "MediaDecoderReader.h"
#include <nsAutoPtr.h>
#include <nsAutoRef.h>
#include <nsDeque.h>
#include <nsTArray.h>
#include <nsClassHashtable.h>
#include "VideoUtils.h"
+#include "FlacFrameParser.h"
#include <stdint.h>
// Uncomment the following to validate that we're predicting the number
// of Vorbis samples in each packet correctly.
#define VALIDATE_VORBIS_SAMPLE_CALCULATION
#ifdef VALIDATE_VORBIS_SAMPLE_CALCULATION
#include <map>
@@ -80,20 +81,21 @@ public:
class OggCodecState
{
public:
typedef mozilla::MetadataTags MetadataTags;
// Ogg types we know about
enum CodecType
{
TYPE_VORBIS=0,
- TYPE_THEORA=1,
- TYPE_OPUS=2,
- TYPE_SKELETON=3,
- TYPE_UNKNOWN=4
+ TYPE_THEORA,
+ TYPE_OPUS,
+ TYPE_SKELETON,
+ TYPE_FLAC,
+ TYPE_UNKNOWN
};
virtual ~OggCodecState();
// Factory for creating nsCodecStates. Use instead of constructor.
// aPage should be a beginning-of-stream page.
static OggCodecState* Create(ogg_page* aPage);
@@ -136,17 +138,17 @@ public:
// Audio preskip may eat a whole packet or more.
return 0;
} else {
return endTime - duration;
}
}
// Initializes the codec state.
- virtual bool Init();
+ virtual bool Init() { return true; }
// Returns true when this bitstream has finished reading all its
// header packets.
bool DoneReadingHeaders() { return mDoneReadingHeaders; }
// Deactivates the bitstream. Only the primary video and audio bitstreams
// should be active.
void Deactivate()
@@ -221,16 +223,26 @@ public:
OggPacketQueue mPackets;
// Is the bitstream active; whether we're decoding and playing this bitstream.
bool mActive;
// True when all headers packets have been read.
bool mDoneReadingHeaders;
+ // Validation utility for vorbis-style tag names.
+ static bool IsValidVorbisTagName(nsCString& aName);
+
+ // Utility method to parse and add a vorbis-style comment
+ // to a metadata hash table. Most Ogg-encapsulated codecs
+ // use the vorbis comment format for metadata.
+ static bool AddVorbisComment(MetadataTags* aTags,
+ const char* aComment,
+ uint32_t aLength);
+
protected:
// Constructs a new OggCodecState. aActive denotes whether the stream is
// active. For streams of unsupported or unknown types, aActive should be
// false.
OggCodecState(ogg_page* aBosPage, bool aActive);
// Deallocates all packets stored in mUnstamped, and clears the array.
void ClearUnstamped();
@@ -243,25 +255,18 @@ protected:
// can be pushed over to mPackets. Used by PageIn() implementations in
// subclasses.
nsresult PacketOutUntilGranulepos(bool& aFoundGranulepos);
// Temporary buffer in which to store packets while we're reading packets
// in order to capture granulepos.
nsTArray<ogg_packet*> mUnstamped;
- // Validation utility for vorbis-style tag names.
- static bool IsValidVorbisTagName(nsCString& aName);
-
- // Utility method to parse and add a vorbis-style comment
- // to a metadata hash table. Most Ogg-encapsulated codecs
- // use the vorbis comment format for metadata.
- static bool AddVorbisComment(MetadataTags* aTags,
- const char* aComment,
- uint32_t aLength);
+private:
+ bool InternalInit();
};
class VorbisState : public OggCodecState
{
public:
explicit VorbisState(ogg_page* aBosPage);
virtual ~VorbisState();
@@ -465,17 +470,16 @@ public:
explicit SkeletonState(ogg_page* aBosPage);
~SkeletonState();
nsClassHashtable<nsUint32HashKey, MessageField> mMsgFieldStore;
CodecType GetType() override { return TYPE_SKELETON; }
bool DecodeHeader(ogg_packet* aPacket) override;
int64_t Time(int64_t granulepos) override { return -1; }
- bool Init() override { return true; }
bool IsHeader(ogg_packet* aPacket) override { return true; }
// Return true if the given time (in milliseconds) is within
// the presentation time defined in the skeleton track.
bool IsPresentable(int64_t aTime) { return aTime >= mPresentationTime; }
// Stores the offset of the page on which a keyframe starts,
// and its presentation time.
@@ -598,16 +602,39 @@ private:
private:
nsTArray<nsKeyPoint> mKeyPoints;
};
// Maps Ogg serialnos to the index-keypoint list.
nsClassHashtable<nsUint32HashKey, nsKeyFrameIndex> mIndex;
};
+class FlacState : public OggCodecState
+{
+public:
+ explicit FlacState(ogg_page* aBosPage);
+
+ CodecType GetType() override { return TYPE_FLAC; }
+ bool DecodeHeader(ogg_packet* aPacket) override;
+ int64_t Time(int64_t granulepos) override;
+ int64_t PacketDuration(ogg_packet* aPacket) override;
+ bool IsHeader(ogg_packet* aPacket) override;
+ nsresult PageIn(ogg_page* aPage) override;
+
+ // Return a hash table with tag metadata.
+ MetadataTags* GetTags() override;
+
+ const AudioInfo& Info();
+
+private:
+ bool ReconstructFlacGranulepos(void);
+
+ FlacFrameParser mParser;
+};
+
} // namespace mozilla
// This allows the use of nsAutoRefs for an ogg_packet that properly free the
// contents of the packet.
template <>
class nsAutoRefTraits<ogg_packet> : public nsPointerRefTraits<ogg_packet>
{
public:
--- a/dom/media/ogg/OggDecoder.cpp
+++ b/dom/media/ogg/OggDecoder.cpp
@@ -62,25 +62,24 @@ OggDecoder::CanHandleMediaType(const nsA
// Verify that all the codecs specified are ones that we expect that
// we can play.
nsTArray<nsString> codecs;
if (!ParseCodecsString(aCodecs, codecs)) {
return false;
}
for (const nsString& codec : codecs) {
if ((IsOpusEnabled() && codec.EqualsLiteral("opus")) ||
- codec.EqualsLiteral("vorbis")) {
+ codec.EqualsLiteral("vorbis") ||
+ (MediaPrefs::FlacInOgg() && codec.EqualsLiteral("flac"))) {
continue;
}
// Note: Only accept Theora in a video content type, not in an audio
// content type.
if (isOggVideo && codec.EqualsLiteral("theora")) {
continue;
}
// Some unsupported codec.
return false;
}
return true;
}
-
-
} // namespace mozilla
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -5,23 +5,23 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsError.h"
#include "MediaDecoderStateMachine.h"
#include "AbstractMediaDecoder.h"
#include "OggDemuxer.h"
#include "OggCodecState.h"
#include "mozilla/PodOperations.h"
-#include "mozilla/Preferences.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "MediaDataDemuxer.h"
#include "nsAutoRef.h"
#include "XiphExtradata.h"
+#include "MediaPrefs.h"
#include <algorithm>
extern mozilla::LazyLogModule gMediaDemuxerLog;
#define OGG_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("OggDemuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
// Un-comment to enable logging of seek bisections.
//#define SEEK_LOGGING
@@ -122,23 +122,25 @@ OggDemuxer::InitTrack(MessageField* aMsg
sLanguage? NS_ConvertUTF8toUTF16(*sLanguage):EmptyString(),
aEnable);
}
OggDemuxer::OggDemuxer(MediaResource* aResource)
: mTheoraState(nullptr)
, mVorbisState(nullptr)
, mOpusState(nullptr)
+ , mFlacState(nullptr)
, mOpusEnabled(MediaDecoder::IsOpusEnabled())
, mSkeletonState(nullptr)
, mAudioOggState(aResource)
, mVideoOggState(aResource)
, mVorbisSerial(0)
, mOpusSerial(0)
, mTheoraSerial(0)
+ , mFlacSerial(0)
, mOpusPreSkip(0)
, mIsChained(false)
, mTimedMetadataEvent(nullptr)
, mOnSeekableEvent(nullptr)
{
MOZ_COUNT_CTOR(OggDemuxer);
PodZero(&mTheoraInfo);
}
@@ -168,17 +170,17 @@ OggDemuxer::SetChainingEvents(TimedMetad
mOnSeekableEvent = aOnSeekableEvent;
}
bool
OggDemuxer::HasAudio()
const
{
- return mVorbisState || mOpusState;
+ return mVorbisState || mOpusState || mFlacState;
}
bool
OggDemuxer::HasVideo()
const
{
return mTheoraState;
}
@@ -244,34 +246,37 @@ OggDemuxer::HasTrackType(TrackInfo::Trac
OggCodecState*
OggDemuxer::GetTrackCodecState(TrackInfo::TrackType aType) const
{
switch(aType) {
case TrackInfo::kAudioTrack:
if (mVorbisState) {
return mVorbisState;
+ } else if (mOpusState) {
+ return mOpusState;
} else {
- return mOpusState;
+ return mFlacState;
}
case TrackInfo::kVideoTrack:
return mTheoraState;
default:
return 0;
}
}
TrackInfo::TrackType
OggDemuxer::GetCodecStateType(OggCodecState* aState) const
{
switch (aState->GetType()) {
case OggCodecState::TYPE_THEORA:
return TrackInfo::kVideoTrack;
case OggCodecState::TYPE_OPUS:
case OggCodecState::TYPE_VORBIS:
+ case OggCodecState::TYPE_FLAC:
return TrackInfo::kAudioTrack;
default:
return TrackInfo::kUndefinedTrack;
}
}
uint32_t
OggDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
@@ -457,16 +462,28 @@ OggDemuxer::SetupTargetOpus(OpusState* a
aHeaders.mHeaderLens[0]);
mOpusState = aOpusState;
mOpusSerial = aOpusState->mSerial;
mOpusPreSkip = aOpusState->mPreSkip;
}
void
+OggDemuxer::SetupTargetFlac(FlacState* aFlacState, OggHeaders& aHeaders)
+{
+ if (mFlacState) {
+ mFlacState->Reset();
+ }
+
+ mInfo.mAudio = aFlacState->Info();
+ mFlacState = aFlacState;
+ mFlacSerial = aFlacState->mSerial;
+}
+
+void
OggDemuxer::SetupTargetSkeleton()
{
// Setup skeleton related information after mVorbisState & mTheroState
// being set (if they exist).
if (mSkeletonState) {
OggHeaders headers;
if (!HasAudio() && !HasVideo()) {
// We have a skeleton track, but no audio or video, may as well disable
@@ -552,16 +569,28 @@ OggDemuxer::SetupMediaTracksInfo(const n
if (msgInfo) {
InitTrack(msgInfo, &mInfo.mAudio, mOpusState == opusState);
}
mInfo.mAudio.mRate = opusState->mRate;
mInfo.mAudio.mChannels = opusState->mChannels;
FillTags(&mInfo.mAudio, opusState->GetTags());
+ } else if (codecState->GetType() == OggCodecState::TYPE_FLAC) {
+ FlacState* flacState = static_cast<FlacState*>(codecState);
+ if (!(mFlacState && mFlacState->mSerial == flacState->mSerial)) {
+ continue;
+ }
+
+ if (msgInfo) {
+ InitTrack(msgInfo, &mInfo.mAudio, mFlacState == flacState);
+ }
+
+ mInfo.mAudio = flacState->Info();
+ FillTags(&mInfo.mAudio, flacState->GetTags());
}
}
}
void
OggDemuxer::FillTags(TrackInfo* aInfo, MetadataTags* aTags)
{
if (!aTags) {
@@ -657,16 +686,25 @@ OggDemuxer::ReadMetadata()
SetupTargetOpus(opusState, headers);
} else {
s->Deactivate();
}
} else {
NS_WARNING("Opus decoding disabled."
" See media.opus.enabled in about:config");
}
+ } else if (MediaPrefs::FlacInOgg() &&
+ s->GetType() == OggCodecState::TYPE_FLAC &&
+ ReadHeaders(TrackInfo::kAudioTrack, s, headers)) {
+ if (!mFlacState) {
+ FlacState* flacState = static_cast<FlacState*>(s);
+ SetupTargetFlac(flacState, headers);
+ } else {
+ s->Deactivate();
+ }
} else if (s->GetType() == OggCodecState::TYPE_SKELETON && !mSkeletonState) {
mSkeletonState = static_cast<SkeletonState*>(s);
} else {
// Deactivate any non-primary bitstreams.
s->Deactivate();
}
}
}
@@ -730,16 +768,17 @@ OggDemuxer::SetChained() {
}
bool
OggDemuxer::ReadOggChain(const media::TimeUnit& aLastEndTime)
{
bool chained = false;
OpusState* newOpusState = nullptr;
VorbisState* newVorbisState = nullptr;
+ FlacState* newFlacState = nullptr;
nsAutoPtr<MetadataTags> tags;
if (HasVideo() || HasSkeleton() || !HasAudio()) {
return false;
}
ogg_page page;
if (!ReadOggPage(TrackInfo::kAudioTrack, &page) || !ogg_page_bos(&page)) {
@@ -757,16 +796,18 @@ OggDemuxer::ReadOggChain(const media::Ti
if (!codecState) {
return false;
}
if (mVorbisState && (codecState->GetType() == OggCodecState::TYPE_VORBIS)) {
newVorbisState = static_cast<VorbisState*>(codecState.get());
} else if (mOpusState && (codecState->GetType() == OggCodecState::TYPE_OPUS)) {
newOpusState = static_cast<OpusState*>(codecState.get());
+ } else if (mFlacState && (codecState->GetType() == OggCodecState::TYPE_FLAC)) {
+ newFlacState = static_cast<FlacState*>(codecState.get());
} else {
return false;
}
OggCodecState* state;
mCodecStore.Add(serial, codecState.forget());
state = mCodecStore.Get(serial);
@@ -816,16 +857,34 @@ OggDemuxer::ReadOggChain(const media::Ti
mInfo.mAudio.mMimeType = NS_LITERAL_CSTRING("audio/ogg; codec=opus");
mInfo.mAudio.mRate = newOpusState->mRate;
mInfo.mAudio.mChannels = newOpusState->mChannels;
chained = true;
tags = newOpusState->GetTags();
}
+ OggHeaders flacHeaders;
+ if ((newFlacState &&
+ ReadHeaders(TrackInfo::kAudioTrack, newFlacState, flacHeaders)) &&
+ (mFlacState->Info().mRate == newFlacState->Info().mRate) &&
+ (mFlacState->Info().mChannels == newFlacState->Info().mChannels)) {
+
+ SetupTargetFlac(newFlacState, flacHeaders);
+ LOG(LogLevel::Debug, ("New flac ogg link, serial=%d\n", mFlacSerial));
+
+ if (msgInfo) {
+ InitTrack(msgInfo, &mInfo.mAudio, true);
+ }
+
+ mInfo.mAudio = newFlacState->Info();
+ chained = true;
+ tags = newFlacState->GetTags();
+ }
+
if (chained) {
SetChained();
mInfo.mMediaSeekable = false;
mDecodedAudioDuration += aLastEndTime;
if (mTimedMetadataEvent) {
mTimedMetadataEvent->Notify(
TimedMetadata(mDecodedAudioDuration,
Move(tags),
@@ -1052,16 +1111,20 @@ OggDemuxer::GetBuffered(TrackInfo::Track
if (aType == TrackInfo::kAudioTrack && mVorbisState &&
serial == mVorbisSerial) {
startTime = VorbisState::Time(&mVorbisInfo, granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (aType == TrackInfo::kAudioTrack && mOpusState &&
serial == mOpusSerial) {
startTime = OpusState::Time(mOpusPreSkip, granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
+ } else if (aType == TrackInfo::kAudioTrack && mFlacState &&
+ serial == mFlacSerial) {
+ startTime = mFlacState->Time(granulepos);
+ NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (aType == TrackInfo::kVideoTrack && mTheoraState &&
serial == mTheoraSerial) {
startTime = TheoraState::Time(&mTheoraInfo, granulepos);
NS_ASSERTION(startTime > 0, "Must have positive start time");
} else if (mCodecStore.Contains(serial)) {
// Stream is not the theora or vorbis stream we're playing,
// but is one that we have header data for.
startOffset += page.header_len + page.body_len;
@@ -2007,16 +2070,18 @@ OggDemuxer::SeekBisection(TrackInfo::Tra
ogg_int64_t granulepos = ogg_page_granulepos(&page);
if (aType == TrackInfo::kAudioTrack &&
granulepos > 0 && audioTime == -1) {
if (mVorbisState && serial == mVorbisState->mSerial) {
audioTime = mVorbisState->Time(granulepos);
} else if (mOpusState && serial == mOpusState->mSerial) {
audioTime = mOpusState->Time(granulepos);
+ } else if (mFlacState && serial == mFlacState->mSerial) {
+ audioTime = mFlacState->Time(granulepos);
}
}
if (aType == TrackInfo::kVideoTrack &&
granulepos > 0 && serial == mTheoraState->mSerial &&
videoTime == -1) {
videoTime = mTheoraState->Time(granulepos);
}
--- a/dom/media/ogg/OggDemuxer.h
+++ b/dom/media/ogg/OggDemuxer.h
@@ -215,16 +215,17 @@ private:
// Fills aTracks with the serial numbers of each active stream, for use by
// various SkeletonState functions.
void BuildSerialList(nsTArray<uint32_t>& aTracks);
// Setup target bitstreams for decoding.
void SetupTargetTheora(TheoraState* aTheoraState, OggHeaders& aHeaders);
void SetupTargetVorbis(VorbisState* aVorbisState, OggHeaders& aHeaders);
void SetupTargetOpus(OpusState* aOpusState, OggHeaders& aHeaders);
+ void SetupTargetFlac(FlacState* aFlacState, OggHeaders& aHeaders);
void SetupTargetSkeleton();
void SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials);
void FillTags(TrackInfo* aInfo, MetadataTags* aTags);
// Compute an ogg page's checksum
ogg_uint32_t GetPageChecksum(ogg_page* aPage);
// Get the end time of aEndOffset. This is the playback position we'd reach
@@ -259,16 +260,19 @@ private:
// Decode state of the Vorbis bitstream we're decoding, if we have audio.
VorbisState* mVorbisState;
// Decode state of the Opus bitstream we're decoding, if we have one.
OpusState* mOpusState;
// Get the bitstream decode state for the given track type
+ // Decode state of the Flac bitstream we're decoding, if we have one.
+ FlacState* mFlacState;
+
OggCodecState* GetTrackCodecState(TrackInfo::TrackType aType) const;
TrackInfo::TrackType GetCodecStateType(OggCodecState* aState) const;
// Represents the user pref media.opus.enabled at the time our
// contructor was called. We can't check it dynamically because
// we're not on the main thread;
bool mOpusEnabled;
@@ -297,16 +301,18 @@ private:
// decoder thread and read on the main thread. All reading on the main
// thread must be done after metadataloaded. We can't use the existing
// data in the codec states due to threading issues. You must check the
// associated mTheoraState or mVorbisState pointer is non-null before
// using this codec data.
uint32_t mVorbisSerial;
uint32_t mOpusSerial;
uint32_t mTheoraSerial;
+ uint32_t mFlacSerial;
+
vorbis_info mVorbisInfo;
int mOpusPreSkip;
th_info mTheoraInfo;
Maybe<int64_t> mStartTime;
// Booleans to indicate if we have audio and/or video data
bool HasVideo() const;