content/media/ogg/OggReader.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #if !defined(OggReader_h_)
     7 #define OggReader_h_
     9 #include <ogg/ogg.h>
    10 #include <theora/theoradec.h>
    11 #ifdef MOZ_TREMOR
    12 #include <tremor/ivorbiscodec.h>
    13 #else
    14 #include <vorbis/codec.h>
    15 #endif
    16 #include "MediaDecoderReader.h"
    17 #include "OggCodecState.h"
    18 #include "VideoUtils.h"
    19 #include "mozilla/Monitor.h"
    21 namespace mozilla {
    22 namespace dom {
    23 class TimeRanges;
    24 }
    25 }
    27 namespace mozilla {
    29 // Thread safe container to store the codec information and the serial for each
    30 // streams.
    31 class OggCodecStore
    32 {
    33   public:
    34     OggCodecStore();
    35     void Add(uint32_t serial, OggCodecState* codecState);
    36     bool Contains(uint32_t serial);
    37     OggCodecState* Get(uint32_t serial);
    38     bool IsKnownStream(uint32_t aSerial);
    40   private:
    41     // Maps Ogg serialnos to OggStreams.
    42     nsClassHashtable<nsUint32HashKey, OggCodecState> mCodecStates;
    44     // Protects the |mCodecStates| and the |mKnownStreams| members.
    45     Monitor mMonitor;
    46 };
    48 class OggReader : public MediaDecoderReader
    49 {
    50 public:
    51   OggReader(AbstractMediaDecoder* aDecoder);
    52   ~OggReader();
    54   virtual nsresult Init(MediaDecoderReader* aCloneDonor);
    55   virtual nsresult ResetDecode();
    56   virtual bool DecodeAudioData();
    58   // If the Theora granulepos has not been captured, it may read several packets
    59   // until one with a granulepos has been captured, to ensure that all packets
    60   // read have valid time info.
    61   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
    62                                   int64_t aTimeThreshold);
    64   virtual bool HasAudio() {
    65     return (mVorbisState != 0 && mVorbisState->mActive)
    66 #ifdef MOZ_OPUS
    67       || (mOpusState != 0 && mOpusState->mActive)
    68 #endif /* MOZ_OPUS */
    69       ;
    70   }
    72   virtual bool HasVideo() {
    73     return mTheoraState != 0 && mTheoraState->mActive;
    74   }
    76   virtual nsresult ReadMetadata(MediaInfo* aInfo,
    77                                 MetadataTags** aTags);
    78   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
    79   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
    81 private:
    82   // This monitor should be taken when reading or writing to mIsChained.
    83   ReentrantMonitor mMonitor;
    85   // Specialized Reset() method to signal if the seek is
    86   // to the start of the stream.
    87   nsresult ResetDecode(bool start);
    89   bool HasSkeleton() {
    90     return mSkeletonState != 0 && mSkeletonState->mActive;
    91   }
    93   // Seeks to the keyframe preceeding the target time using available
    94   // keyframe indexes.
    95   enum IndexedSeekResult {
    96     SEEK_OK,          // Success.
    97     SEEK_INDEX_FAIL,  // Failure due to no index, or invalid index.
    98     SEEK_FATAL_ERROR  // Error returned by a stream operation.
    99   };
   100   IndexedSeekResult SeekToKeyframeUsingIndex(int64_t aTarget);
   102   // Rolls back a seek-using-index attempt, returning a failure error code.
   103   IndexedSeekResult RollbackIndexedSeek(int64_t aOffset);
   105   // Represents a section of contiguous media, with a start and end offset,
   106   // and the timestamps of the start and end of that range, that is cached.
   107   // Used to denote the extremities of a range in which we can seek quickly
   108   // (because it's cached).
   109   class SeekRange {
   110   public:
   111     SeekRange()
   112       : mOffsetStart(0),
   113         mOffsetEnd(0),
   114         mTimeStart(0),
   115         mTimeEnd(0)
   116     {}
   118     SeekRange(int64_t aOffsetStart,
   119               int64_t aOffsetEnd,
   120               int64_t aTimeStart,
   121               int64_t aTimeEnd)
   122       : mOffsetStart(aOffsetStart),
   123         mOffsetEnd(aOffsetEnd),
   124         mTimeStart(aTimeStart),
   125         mTimeEnd(aTimeEnd)
   126     {}
   128     bool IsNull() const {
   129       return mOffsetStart == 0 &&
   130              mOffsetEnd == 0 &&
   131              mTimeStart == 0 &&
   132              mTimeEnd == 0;
   133     }
   135     int64_t mOffsetStart, mOffsetEnd; // in bytes.
   136     int64_t mTimeStart, mTimeEnd; // in usecs.
   137   };
   139   // Seeks to aTarget usecs in the buffered range aRange using bisection search,
   140   // or to the keyframe prior to aTarget if we have video. aAdjustedTarget is
   141   // an adjusted version of the target used to account for Opus pre-roll, if
   142   // necessary. aStartTime must be the presentation time at the start of media,
   143   // and aEndTime the time at end of media. aRanges must be the time/byte ranges
   144   // buffered in the media cache as per GetSeekRanges().
   145   nsresult SeekInBufferedRange(int64_t aTarget,
   146                                int64_t aAdjustedTarget,
   147                                int64_t aStartTime,
   148                                int64_t aEndTime,
   149                                const nsTArray<SeekRange>& aRanges,
   150                                const SeekRange& aRange);
   152   // Seeks to before aTarget usecs in media using bisection search. If the media
   153   // has video, this will seek to before the keyframe required to render the
   154   // media at aTarget. Will use aRanges in order to narrow the bisection
   155   // search space. aStartTime must be the presentation time at the start of
   156   // media, and aEndTime the time at end of media. aRanges must be the time/byte
   157   // ranges buffered in the media cache as per GetSeekRanges().
   158   nsresult SeekInUnbuffered(int64_t aTarget,
   159                             int64_t aStartTime,
   160                             int64_t aEndTime,
   161                             const nsTArray<SeekRange>& aRanges);
   163   // Get the end time of aEndOffset. This is the playback position we'd reach
   164   // after playback finished at aEndOffset.
   165   int64_t RangeEndTime(int64_t aEndOffset);
   167   // Get the end time of aEndOffset, without reading before aStartOffset.
   168   // This is the playback position we'd reach after playback finished at
   169   // aEndOffset. If bool aCachedDataOnly is true, then we'll only read
   170   // from data which is cached in the media cached, otherwise we'll do
   171   // regular blocking reads from the media stream. If bool aCachedDataOnly
   172   // is true, this can safely be called on the main thread, otherwise it
   173   // must be called on the state machine thread.
   174   int64_t RangeEndTime(int64_t aStartOffset,
   175                        int64_t aEndOffset,
   176                        bool aCachedDataOnly);
   178   // Get the start time of the range beginning at aOffset. This is the start
   179   // time of the first frame and or audio sample we'd be able to play if we
   180   // started playback at aOffset.
   181   int64_t RangeStartTime(int64_t aOffset);
   183   // Performs a seek bisection to move the media stream's read cursor to the
   184   // last ogg page boundary which has end time before aTarget usecs on both the
   185   // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
   186   // i.e. it will only read inside of the aRange's start and end offsets.
   187   // aFuzz is the number of usecs of leniency we'll allow; we'll terminate the
   188   // seek when we land in the range (aTime - aFuzz, aTime) usecs.
   189   nsresult SeekBisection(int64_t aTarget,
   190                          const SeekRange& aRange,
   191                          uint32_t aFuzz);
   193   // Returns true if the serial number is for a stream we encountered
   194   // while reading metadata. Call on the main thread only.
   195   bool IsKnownStream(uint32_t aSerial);
   197   // Fills aRanges with SeekRanges denoting the sections of the media which
   198   // have been downloaded and are stored in the media cache. The reader
   199   // monitor must must be held with exactly one lock count. The MediaResource
   200   // must be pinned while calling this.
   201   nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
   203   // Returns the range in which you should perform a seek bisection if
   204   // you wish to seek to aTarget usecs, given the known (buffered) byte ranges
   205   // in aRanges. If aExact is true, we only return an exact copy of a
   206   // range in which aTarget lies, or a null range if aTarget isn't contained
   207   // in any of the (buffered) ranges. Otherwise, when aExact is false,
   208   // we'll construct the smallest possible range we can, based on the times
   209   // and byte offsets known in aRanges. We can then use this to minimize our
   210   // bisection's search space when the target isn't in a known buffered range.
   211   SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
   212                             int64_t aTarget,
   213                             int64_t aStartTime,
   214                             int64_t aEndTime,
   215                             bool aExact);
   216 private:
   218   // Decodes a packet of Vorbis data, and inserts its samples into the
   219   // audio queue.
   220   nsresult DecodeVorbis(ogg_packet* aPacket);
   222   // Decodes a packet of Opus data, and inserts its samples into the
   223   // audio queue.
   224   nsresult DecodeOpus(ogg_packet* aPacket);
   226   // Decodes a packet of Theora data, and inserts its frame into the
   227   // video queue. May return NS_ERROR_OUT_OF_MEMORY. Caller must have obtained
   228   // the reader's monitor. aTimeThreshold is the current playback position
   229   // in media time in microseconds. Frames with an end time before this will
   230   // not be enqueued.
   231   nsresult DecodeTheora(ogg_packet* aPacket, int64_t aTimeThreshold);
   233   // Read a page of data from the Ogg file. Returns true if a page has been
   234   // read, false if the page read failed or end of file reached.
   235   bool ReadOggPage(ogg_page* aPage);
   237   // Reads and decodes header packets for aState, until either header decode
   238   // fails, or is complete. Initializes the codec state before returning.
   239   // Returns true if reading headers and initializtion of the stream
   240   // succeeds.
   241   bool ReadHeaders(OggCodecState* aState);
   243   // Reads the next link in the chain.
   244   bool ReadOggChain();
   246   // Set this media as being a chain and notifies the state machine that the
   247   // media is no longer seekable.
   248   void SetChained(bool aIsChained);
   250   // Returns the next Ogg packet for an bitstream/codec state. Returns a
   251   // pointer to an ogg_packet on success, or nullptr if the read failed.
   252   // The caller is responsible for deleting the packet and its |packet| field.
   253   ogg_packet* NextOggPacket(OggCodecState* aCodecState);
   255   // Fills aTracks with the serial numbers of each active stream, for use by
   256   // various SkeletonState functions.
   257   void BuildSerialList(nsTArray<uint32_t>& aTracks);
   259   OggCodecStore mCodecStore;
   261   // Decode state of the Theora bitstream we're decoding, if we have video.
   262   TheoraState* mTheoraState;
   264   // Decode state of the Vorbis bitstream we're decoding, if we have audio.
   265   VorbisState* mVorbisState;
   267 #ifdef MOZ_OPUS
   268   // Decode state of the Opus bitstream we're decoding, if we have one.
   269   OpusState *mOpusState;
   271   // Represents the user pref media.opus.enabled at the time our
   272   // contructor was called. We can't check it dynamically because
   273   // we're not on the main thread;
   274   bool mOpusEnabled;
   275 #endif /* MOZ_OPUS */
   277   // Decode state of the Skeleton bitstream.
   278   SkeletonState* mSkeletonState;
   280   // Ogg decoding state.
   281   ogg_sync_state mOggState;
   283   // Vorbis/Opus/Theora data used to compute timestamps. This is written on the
   284   // decoder thread and read on the main thread. All reading on the main
   285   // thread must be done after metadataloaded. We can't use the existing
   286   // data in the codec states due to threading issues. You must check the
   287   // associated mTheoraState or mVorbisState pointer is non-null before
   288   // using this codec data.
   289   uint32_t mVorbisSerial;
   290   uint32_t mOpusSerial;
   291   uint32_t mTheoraSerial;
   292   vorbis_info mVorbisInfo;
   293   int mOpusPreSkip;
   294   th_info mTheoraInfo;
   296   // The picture region inside Theora frame to be displayed, if we have
   297   // a Theora video track.
   298   nsIntRect mPicture;
   300   // True if we are decoding a chained ogg. Reading or writing to this member
   301   // should be done with |mMonitor| acquired.
   302   bool mIsChained;
   304   // Number of audio frames decoded so far.
   305   int64_t mDecodedAudioFrames;
   306 };
   308 } // namespace mozilla
   310 #endif

mercurial