summaryrefslogtreecommitdiffstats
path: root/dom/media/ogg/OggDemuxer.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ogg/OggDemuxer.h')
-rw-r--r--dom/media/ogg/OggDemuxer.h363
1 files changed, 363 insertions, 0 deletions
diff --git a/dom/media/ogg/OggDemuxer.h b/dom/media/ogg/OggDemuxer.h
new file mode 100644
index 0000000000..8a65398cf9
--- /dev/null
+++ b/dom/media/ogg/OggDemuxer.h
@@ -0,0 +1,363 @@
+/* -*- 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/. */
+#if !defined(OggDemuxer_h_)
+# define OggDemuxer_h_
+
+# include "nsTArray.h"
+# include "MediaDataDemuxer.h"
+# include "OggCodecState.h"
+# include "OggCodecStore.h"
+# include "OggRLBoxTypes.h"
+# include "MediaMetadataManager.h"
+
+# include <memory>
+
+namespace mozilla {
+
+class OggTrackDemuxer;
+
+DDLoggedTypeDeclNameAndBase(OggDemuxer, MediaDataDemuxer);
+DDLoggedTypeNameAndBase(OggTrackDemuxer, MediaTrackDemuxer);
+
+class OggDemuxer : public MediaDataDemuxer,
+ public DecoderDoctorLifeLogger<OggDemuxer> {
+ public:
+ explicit OggDemuxer(MediaResource* aResource);
+
+ RefPtr<InitPromise> Init() override;
+
+ uint32_t GetNumberTracks(TrackInfo::TrackType aType) const override;
+
+ already_AddRefed<MediaTrackDemuxer> GetTrackDemuxer(
+ TrackInfo::TrackType aType, uint32_t aTrackNumber) override;
+
+ bool IsSeekable() const override;
+
+ UniquePtr<EncryptionInfo> GetCrypto() override;
+
+ // Set the events to notify when chaining is encountered.
+ void SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
+ MediaEventProducer<void>* aOnSeekableEvent);
+
+ private:
+ // helpers for friend OggTrackDemuxer
+ UniquePtr<TrackInfo> GetTrackInfo(TrackInfo::TrackType aType,
+ size_t aTrackNumber) const;
+
+ struct nsAutoOggSyncState {
+ explicit nsAutoOggSyncState(rlbox_sandbox_ogg* aSandbox);
+ ~nsAutoOggSyncState();
+ rlbox_sandbox_ogg* mSandbox;
+ tainted_opaque_ogg<ogg_sync_state*> mState;
+ };
+ media::TimeIntervals GetBuffered(TrackInfo::TrackType aType);
+ void FindStartTime(int64_t& aOutStartTime);
+ void FindStartTime(TrackInfo::TrackType, int64_t& aOutStartTime);
+
+ nsresult SeekInternal(TrackInfo::TrackType aType,
+ const media::TimeUnit& aTarget);
+
+ // Seeks to the keyframe preceding the target time using available
+ // keyframe indexes.
+ enum IndexedSeekResult {
+ SEEK_OK, // Success.
+ SEEK_INDEX_FAIL, // Failure due to no index, or invalid index.
+ SEEK_FATAL_ERROR // Error returned by a stream operation.
+ };
+ IndexedSeekResult SeekToKeyframeUsingIndex(TrackInfo::TrackType aType,
+ int64_t aTarget);
+
+ // Rolls back a seek-using-index attempt, returning a failure error code.
+ IndexedSeekResult RollbackIndexedSeek(TrackInfo::TrackType aType,
+ int64_t aOffset);
+
+ // Represents a section of contiguous media, with a start and end offset,
+ // and the timestamps of the start and end of that range, that is cached.
+ // Used to denote the extremities of a range in which we can seek quickly
+ // (because it's cached).
+ class SeekRange {
+ public:
+ SeekRange() : mOffsetStart(0), mOffsetEnd(0), mTimeStart(0), mTimeEnd(0) {}
+
+ SeekRange(int64_t aOffsetStart, int64_t aOffsetEnd, int64_t aTimeStart,
+ int64_t aTimeEnd)
+ : mOffsetStart(aOffsetStart),
+ mOffsetEnd(aOffsetEnd),
+ mTimeStart(aTimeStart),
+ mTimeEnd(aTimeEnd) {}
+
+ bool IsNull() const {
+ return mOffsetStart == 0 && mOffsetEnd == 0 && mTimeStart == 0 &&
+ mTimeEnd == 0;
+ }
+
+ int64_t mOffsetStart, mOffsetEnd; // in bytes.
+ int64_t mTimeStart, mTimeEnd; // in usecs.
+ };
+
+ nsresult GetSeekRanges(TrackInfo::TrackType aType,
+ nsTArray<SeekRange>& aRanges);
+ SeekRange SelectSeekRange(TrackInfo::TrackType aType,
+ const nsTArray<SeekRange>& ranges, int64_t aTarget,
+ int64_t aStartTime, int64_t aEndTime, bool aExact);
+
+ // Seeks to aTarget usecs in the buffered range aRange using bisection search,
+ // or to the keyframe prior to aTarget if we have video. aAdjustedTarget is
+ // an adjusted version of the target used to account for Opus pre-roll, if
+ // necessary. aStartTime must be the presentation time at the start of media,
+ // and aEndTime the time at end of media. aRanges must be the time/byte ranges
+ // buffered in the media cache as per GetSeekRanges().
+ nsresult SeekInBufferedRange(TrackInfo::TrackType aType, int64_t aTarget,
+ int64_t aAdjustedTarget, int64_t aStartTime,
+ int64_t aEndTime,
+ const nsTArray<SeekRange>& aRanges,
+ const SeekRange& aRange);
+
+ // Seeks to before aTarget usecs in media using bisection search. If the media
+ // has video, this will seek to before the keyframe required to render the
+ // media at aTarget. Will use aRanges in order to narrow the bisection
+ // search space. aStartTime must be the presentation time at the start of
+ // media, and aEndTime the time at end of media. aRanges must be the time/byte
+ // ranges buffered in the media cache as per GetSeekRanges().
+ nsresult SeekInUnbuffered(TrackInfo::TrackType aType, int64_t aTarget,
+ int64_t aStartTime, int64_t aEndTime,
+ const nsTArray<SeekRange>& aRanges);
+
+ // Performs a seek bisection to move the media stream's read cursor to the
+ // last ogg page boundary which has end time before aTarget usecs on both the
+ // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
+ // i.e. it will only read inside of the aRange's start and end offsets.
+ // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the
+ // seek when we land in the range (aTime - aFuzz, aTime) usecs.
+ nsresult SeekBisection(TrackInfo::TrackType aType, int64_t aTarget,
+ const SeekRange& aRange, uint32_t aFuzz);
+
+ // Chunk size to read when reading Ogg files. Average Ogg page length
+ // is about 4300 bytes, so we read the file in chunks larger than that.
+ static const int PAGE_STEP = 8192;
+
+ enum PageSyncResult {
+ PAGE_SYNC_ERROR = 1,
+ PAGE_SYNC_END_OF_RANGE = 2,
+ PAGE_SYNC_OK = 3
+ };
+ static PageSyncResult PageSync(rlbox_sandbox_ogg* aSandbox,
+ MediaResourceIndex* aResource,
+ tainted_opaque_ogg<ogg_sync_state*> aState,
+ bool aCachedDataOnly, int64_t aOffset,
+ int64_t aEndOffset,
+ tainted_ogg<ogg_page*> aPage,
+ int& aSkippedBytes);
+
+ // Demux next Ogg packet
+ ogg_packet* GetNextPacket(TrackInfo::TrackType aType);
+
+ nsresult Reset(TrackInfo::TrackType aType);
+
+ static const nsString GetKind(const nsCString& aRole);
+ static void InitTrack(MessageField* aMsgInfo, TrackInfo* aInfo, bool aEnable);
+
+ // Really private!
+ ~OggDemuxer();
+
+ // Read enough of the file to identify track information and header
+ // packets necessary for decoding to begin.
+ nsresult ReadMetadata();
+
+ // Read a page of data from the Ogg file. Returns true if a page has been
+ // read, false if the page read failed or end of file reached.
+ bool ReadOggPage(TrackInfo::TrackType aType,
+ tainted_opaque_ogg<ogg_page*> aPage);
+
+ // Send a page off to the individual streams it belongs to.
+ // Reconstructed packets, if any are ready, will be available
+ // on the individual OggCodecStates.
+ nsresult DemuxOggPage(TrackInfo::TrackType aType,
+ tainted_opaque_ogg<ogg_page*> aPage);
+
+ // Read data and demux until a packet is available on the given stream state
+ void DemuxUntilPacketAvailable(TrackInfo::TrackType aType,
+ OggCodecState* aState);
+
+ // Reads and decodes header packets for aState, until either header decode
+ // fails, or is complete. Initializes the codec state before returning.
+ // Returns true if reading headers and initializtion of the stream
+ // succeeds.
+ bool ReadHeaders(TrackInfo::TrackType aType, OggCodecState* aState);
+
+ // Reads the next link in the chain.
+ bool ReadOggChain(const media::TimeUnit& aLastEndTime);
+
+ // Set this media as being a chain and notifies the state machine that the
+ // media is no longer seekable.
+ void SetChained();
+
+ // 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 SetupTarget(OggCodecState** aSavedState, OggCodecState* aNewState);
+ void SetupTargetSkeleton();
+ void SetupMediaTracksInfo(const nsTArray<uint32_t>& aSerials);
+ void FillTags(TrackInfo* aInfo, UniquePtr<MetadataTags>&& aTags);
+
+ // Compute an ogg page's checksum
+ tainted_opaque_ogg<ogg_uint32_t> GetPageChecksum(
+ tainted_opaque_ogg<ogg_page*> aPage);
+
+ // Get the end time of aEndOffset. This is the playback position we'd reach
+ // after playback finished at aEndOffset.
+ int64_t RangeEndTime(TrackInfo::TrackType aType, int64_t aEndOffset);
+
+ // Get the end time of aEndOffset, without reading before aStartOffset.
+ // This is the playback position we'd reach after playback finished at
+ // aEndOffset. If bool aCachedDataOnly is true, then we'll only read
+ // from data which is cached in the media cached, otherwise we'll do
+ // regular blocking reads from the media stream. If bool aCachedDataOnly
+ // is true, this can safely be called on the main thread, otherwise it
+ // must be called on the state machine thread.
+ int64_t RangeEndTime(TrackInfo::TrackType aType, int64_t aStartOffset,
+ int64_t aEndOffset, bool aCachedDataOnly);
+
+ // Get the start time of the range beginning at aOffset. This is the start
+ // time of the first aType sample we'd be able to play if we
+ // started playback at aOffset.
+ int64_t RangeStartTime(TrackInfo::TrackType aType, int64_t aOffset);
+
+ // All invocations of libogg functionality from the demuxer is sandboxed using
+ // wasm library sandboxes on supported platforms. These functions that create
+ // and destroy the sandbox instance.
+ static rlbox_sandbox_ogg* CreateSandbox();
+ struct SandboxDestroy {
+ void operator()(rlbox_sandbox_ogg* sandbox);
+ };
+
+ // The sandbox instance used to sandbox libogg functionality in the demuxer.
+ // This must be declared before other members so that constructors/destructors
+ // run in the right order.
+ std::unique_ptr<rlbox_sandbox_ogg, SandboxDestroy> mSandbox;
+
+ MediaInfo mInfo;
+ nsTArray<RefPtr<OggTrackDemuxer>> mDemuxers;
+
+ // Map of codec-specific bitstream states.
+ OggCodecStore mCodecStore;
+
+ // Decode state of the Theora bitstream we're decoding, if we have video.
+ OggCodecState* mTheoraState;
+
+ // Decode state of the Vorbis bitstream we're decoding, if we have audio.
+ OggCodecState* mVorbisState;
+
+ // Decode state of the Opus bitstream we're decoding, if we have one.
+ OggCodecState* mOpusState;
+
+ // Get the bitstream decode state for the given track type
+ // Decode state of the Flac bitstream we're decoding, if we have one.
+ OggCodecState* 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;
+
+ // Decode state of the Skeleton bitstream.
+ SkeletonState* mSkeletonState;
+
+ // Ogg decoding state.
+ struct OggStateContext {
+ explicit OggStateContext(MediaResource* aResource,
+ rlbox_sandbox_ogg* aSandbox)
+ : mOggState(aSandbox), mResource(aResource), mNeedKeyframe(true) {}
+ nsAutoOggSyncState mOggState;
+ MediaResourceIndex mResource;
+ Maybe<media::TimeUnit> mStartTime;
+ bool mNeedKeyframe;
+ };
+
+ OggStateContext& OggState(TrackInfo::TrackType aType);
+ tainted_opaque_ogg<ogg_sync_state*> OggSyncState(TrackInfo::TrackType aType);
+ MediaResourceIndex* Resource(TrackInfo::TrackType aType);
+ MediaResourceIndex* CommonResource();
+ OggStateContext mAudioOggState;
+ OggStateContext mVideoOggState;
+
+ Maybe<int64_t> mStartTime;
+
+ // Booleans to indicate if we have audio and/or video data
+ bool HasVideo() const;
+ bool HasAudio() const;
+ bool HasSkeleton() const {
+ return mSkeletonState != 0 && mSkeletonState->mActive;
+ }
+ bool HaveStartTime() const;
+ bool HaveStartTime(TrackInfo::TrackType aType);
+ int64_t StartTime() const;
+ int64_t StartTime(TrackInfo::TrackType aType);
+
+ // The picture region inside Theora frame to be displayed, if we have
+ // a Theora video track.
+ gfx::IntRect mPicture;
+
+ // True if we are decoding a chained ogg.
+ bool mIsChained;
+
+ // Total audio duration played so far.
+ media::TimeUnit mDecodedAudioDuration;
+
+ // Events manager
+ TimedMetadataEventProducer* mTimedMetadataEvent;
+ MediaEventProducer<void>* mOnSeekableEvent;
+
+ // This will be populated only if a content change occurs, otherwise it
+ // will be left as null so the original metadata is used.
+ // It is updated once a chained ogg is encountered.
+ // As Ogg chaining is only supported for audio, we only need an audio track
+ // info.
+ RefPtr<TrackInfoSharedPtr> mSharedAudioTrackInfo;
+
+ friend class OggTrackDemuxer;
+};
+
+class OggTrackDemuxer : public MediaTrackDemuxer,
+ public DecoderDoctorLifeLogger<OggTrackDemuxer> {
+ public:
+ OggTrackDemuxer(OggDemuxer* aParent, TrackInfo::TrackType aType,
+ uint32_t aTrackNumber);
+
+ UniquePtr<TrackInfo> GetInfo() const override;
+
+ RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override;
+
+ RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples = 1) override;
+
+ void Reset() override;
+
+ RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint(
+ const media::TimeUnit& aTimeThreshold) override;
+
+ media::TimeIntervals GetBuffered() override;
+
+ void BreakCycles() override;
+
+ private:
+ ~OggTrackDemuxer();
+ void SetNextKeyFrameTime();
+ RefPtr<MediaRawData> NextSample();
+ RefPtr<OggDemuxer> mParent;
+ TrackInfo::TrackType mType;
+ UniquePtr<TrackInfo> mInfo;
+
+ // Queued sample extracted by the demuxer, but not yet returned.
+ RefPtr<MediaRawData> mQueuedSample;
+};
+} // namespace mozilla
+
+#endif