LCOV - code coverage report
Current view: top level - dom/media/webm - WebMBufferedParser.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 38 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 15 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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(WebMBufferedParser_h_)
       7             : #define WebMBufferedParser_h_
       8             : 
       9             : #include "nsISupportsImpl.h"
      10             : #include "nsTArray.h"
      11             : #include "mozilla/ReentrantMonitor.h"
      12             : #include "MediaResource.h"
      13             : 
      14             : namespace mozilla {
      15             : 
      16             : // Stores a stream byte offset and the scaled timecode of the block at
      17             : // that offset.
      18             : struct WebMTimeDataOffset
      19             : {
      20           0 :   WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
      21             :                      int64_t aInitOffset, int64_t aSyncOffset,
      22             :                      int64_t aClusterEndOffset)
      23           0 :     : mEndOffset(aEndOffset)
      24             :     , mInitOffset(aInitOffset)
      25             :     , mSyncOffset(aSyncOffset)
      26             :     , mClusterEndOffset(aClusterEndOffset)
      27           0 :     , mTimecode(aTimecode)
      28           0 :   {}
      29             : 
      30           0 :   bool operator==(int64_t aEndOffset) const {
      31           0 :     return mEndOffset == aEndOffset;
      32             :   }
      33             : 
      34           0 :   bool operator!=(int64_t aEndOffset) const {
      35           0 :     return mEndOffset != aEndOffset;
      36             :   }
      37             : 
      38           0 :   bool operator<(int64_t aEndOffset) const {
      39           0 :     return mEndOffset < aEndOffset;
      40             :   }
      41             : 
      42             :   int64_t mEndOffset;
      43             :   int64_t mInitOffset;
      44             :   int64_t mSyncOffset;
      45             :   int64_t mClusterEndOffset;
      46             :   uint64_t mTimecode;
      47             : };
      48             : 
      49             : // A simple WebM parser that produces data offset to timecode pairs as it
      50             : // consumes blocks.  A new parser is created for each distinct range of data
      51             : // received and begins parsing from the first WebM cluster within that
      52             : // range.  Old parsers are destroyed when their range merges with a later
      53             : // parser or an already parsed range.  The parser may start at any position
      54             : // within the stream.
      55             : struct WebMBufferedParser
      56             : {
      57           0 :   explicit WebMBufferedParser(int64_t aOffset)
      58           0 :     : mStartOffset(aOffset)
      59             :     , mCurrentOffset(aOffset)
      60             :     , mInitEndOffset(-1)
      61             :     , mBlockEndOffset(-1)
      62             :     , mState(READ_ELEMENT_ID)
      63             :     , mNextState(READ_ELEMENT_ID)
      64             :     , mVIntRaw(false)
      65             :     , mLastInitStartOffset(-1)
      66             :     , mClusterSyncPos(0)
      67             :     , mVIntLeft(0)
      68             :     , mBlockSize(0)
      69             :     , mClusterTimecode(0)
      70             :     , mClusterOffset(0)
      71             :     , mClusterEndOffset(-1)
      72             :     , mBlockOffset(0)
      73             :     , mBlockTimecode(0)
      74             :     , mBlockTimecodeLength(0)
      75             :     , mSkipBytes(0)
      76             :     , mTimecodeScale(1000000)
      77           0 :     , mGotTimecodeScale(false)
      78             :   {
      79           0 :     if (mStartOffset != 0) {
      80           0 :       mState = FIND_CLUSTER_SYNC;
      81             :     }
      82           0 :   }
      83             : 
      84           0 :   uint32_t GetTimecodeScale() {
      85           0 :     MOZ_ASSERT(mGotTimecodeScale);
      86           0 :     return mTimecodeScale;
      87             :   }
      88             : 
      89             :   // If this parser is not expected to parse a segment info, it must be told
      90             :   // the appropriate timecode scale to use from elsewhere.
      91           0 :   void SetTimecodeScale(uint32_t aTimecodeScale) {
      92           0 :     mTimecodeScale = aTimecodeScale;
      93           0 :     mGotTimecodeScale = true;
      94           0 :   }
      95             : 
      96             :   // Steps the parser through aLength bytes of data.  Always consumes
      97             :   // aLength bytes.  Updates mCurrentOffset before returning.  Acquires
      98             :   // aReentrantMonitor before using aMapping.
      99             :   // Returns false if an error was encountered.
     100             :   bool Append(const unsigned char* aBuffer, uint32_t aLength,
     101             :               nsTArray<WebMTimeDataOffset>& aMapping,
     102             :               ReentrantMonitor& aReentrantMonitor);
     103             : 
     104           0 :   bool operator==(int64_t aOffset) const {
     105           0 :     return mCurrentOffset == aOffset;
     106             :   }
     107             : 
     108           0 :   bool operator<(int64_t aOffset) const {
     109           0 :     return mCurrentOffset < aOffset;
     110             :   }
     111             : 
     112             :   // Returns the start offset of the init (EBML) or media segment (Cluster)
     113             :   // following the aOffset position. If none were found, returns mBlockEndOffset.
     114             :   // This allows to determine the end of the interval containg aOffset.
     115             :   int64_t EndSegmentOffset(int64_t aOffset);
     116             : 
     117             :   // The offset at which this parser started parsing.  Used to merge
     118             :   // adjacent parsers, in which case the later parser adopts the earlier
     119             :   // parser's mStartOffset.
     120             :   int64_t mStartOffset;
     121             : 
     122             :   // Current offset within the stream.  Updated in chunks as Append() consumes
     123             :   // data.
     124             :   int64_t mCurrentOffset;
     125             : 
     126             :   // Tracks element's end offset. This indicates the end of the first init
     127             :   // segment. Will only be set if a Segment Information has been found.
     128             :   int64_t mInitEndOffset;
     129             : 
     130             :   // End offset of the last block parsed.
     131             :   // Will only be set if a complete block has been parsed.
     132             :   int64_t mBlockEndOffset;
     133             : 
     134             : private:
     135             :   enum State {
     136             :     // Parser start state.  Expects to begin at a valid EBML element.  Move
     137             :     // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
     138             :     READ_ELEMENT_ID,
     139             : 
     140             :     // Store element ID read into mVInt into mElement.mID.  Move to
     141             :     // READ_VINT with mVIntRaw false, then return to PARSE_ELEMENT.
     142             :     READ_ELEMENT_SIZE,
     143             : 
     144             :     // Parser start state for parsers started at an arbitrary offset.  Scans
     145             :     // forward for the first cluster, then move to READ_ELEMENT_ID.
     146             :     FIND_CLUSTER_SYNC,
     147             : 
     148             :     // Simplistic core of the parser.  Does not pay attention to nesting of
     149             :     // elements.  Checks mElement for an element ID of interest, then moves
     150             :     // to the next state as determined by the element ID.
     151             :     PARSE_ELEMENT,
     152             : 
     153             :     // Read the first byte of a variable length integer.  The first byte
     154             :     // encodes both the variable integer's length and part of the value.
     155             :     // The value read so far is stored in mVInt.mValue and the length is
     156             :     // stored in mVInt.mLength.  The number of bytes left to read is stored
     157             :     // in mVIntLeft.
     158             :     READ_VINT,
     159             : 
     160             :     // Reads the remaining mVIntLeft bytes into mVInt.mValue.
     161             :     READ_VINT_REST,
     162             : 
     163             :     // mVInt holds the parsed timecode scale, store it in mTimecodeScale,
     164             :     // then return READ_ELEMENT_ID.
     165             :     READ_TIMECODESCALE,
     166             : 
     167             :     // mVInt holds the parsed cluster timecode, store it in
     168             :     // mClusterTimecode, then return to READ_ELEMENT_ID.
     169             :     READ_CLUSTER_TIMECODE,
     170             : 
     171             :     // mBlockTimecodeLength holds the remaining length of the block timecode
     172             :     // left to read.  Read each byte of the timecode into mBlockTimecode.
     173             :     // Once complete, calculate the scaled timecode from the cluster
     174             :     // timecode, block timecode, and timecode scale, and insert a
     175             :     // WebMTimeDataOffset entry into aMapping if one is not already present
     176             :     // for this offset.
     177             :     READ_BLOCK_TIMECODE,
     178             : 
     179             :     // Will skip the current tracks element and set mInitEndOffset if an init
     180             :     // segment has been found.
     181             :     // Currently, only assumes it's the end of the tracks element.
     182             :     CHECK_INIT_FOUND,
     183             : 
     184             :     // Skip mSkipBytes of data before resuming parse at mNextState.
     185             :     SKIP_DATA,
     186             :   };
     187             : 
     188             :   // Current state machine action.
     189             :   State mState;
     190             : 
     191             :   // Next state machine action.  SKIP_DATA and READ_VINT_REST advance to
     192             :   // mNextState when the current action completes.
     193             :   State mNextState;
     194             : 
     195             :   struct VInt {
     196           0 :     VInt() : mValue(0), mLength(0) {}
     197             :     uint64_t mValue;
     198             :     uint64_t mLength;
     199             :   };
     200             : 
     201           0 :   struct EBMLElement {
     202             :     uint64_t Length() { return mID.mLength + mSize.mLength; }
     203             :     VInt mID;
     204             :     VInt mSize;
     205             :   };
     206             : 
     207             :   EBMLElement mElement;
     208             : 
     209             :   VInt mVInt;
     210             : 
     211             :   bool mVIntRaw;
     212             : 
     213             :   // EBML start offset. This indicates the start of the last init segment
     214             :   // parsed. Will only be set if an EBML element has been found.
     215             :   int64_t mLastInitStartOffset;
     216             : 
     217             :   // Current match position within CLUSTER_SYNC_ID.  Used to find sync
     218             :   // within arbitrary data.
     219             :   uint32_t mClusterSyncPos;
     220             : 
     221             :   // Number of bytes of mVInt left to read.  mVInt is complete once this
     222             :   // reaches 0.
     223             :   uint32_t mVIntLeft;
     224             : 
     225             :   // Size of the block currently being parsed.  Any unused data within the
     226             :   // block is skipped once the block timecode has been parsed.
     227             :   uint64_t mBlockSize;
     228             : 
     229             :   // Cluster-level timecode.
     230             :   uint64_t mClusterTimecode;
     231             : 
     232             :   // Start offset of the cluster currently being parsed.  Used as the sync
     233             :   // point offset for the offset-to-time mapping as each block timecode is
     234             :   // been parsed.
     235             :   int64_t mClusterOffset;
     236             : 
     237             :   // End offset of the cluster currently being parsed. -1 if unknown.
     238             :   int64_t mClusterEndOffset;
     239             : 
     240             :   // Start offset of the block currently being parsed.  Used as the byte
     241             :   // offset for the offset-to-time mapping once the block timecode has been
     242             :   // parsed.
     243             :   int64_t mBlockOffset;
     244             : 
     245             :   // Block-level timecode.  This is summed with mClusterTimecode to produce
     246             :   // an absolute timecode for the offset-to-time mapping.
     247             :   int16_t mBlockTimecode;
     248             : 
     249             :   // Number of bytes of mBlockTimecode left to read.
     250             :   uint32_t mBlockTimecodeLength;
     251             : 
     252             :   // Count of bytes left to skip before resuming parse at mNextState.
     253             :   // Mostly used to skip block payload data after reading a block timecode.
     254             :   uint32_t mSkipBytes;
     255             : 
     256             :   // Timecode scale read from the segment info and used to scale absolute
     257             :   // timecodes.
     258             :   uint32_t mTimecodeScale;
     259             : 
     260             :   // True if we read the timecode scale from the segment info or have
     261             :   // confirmed that the default value is to be used.
     262             :   bool mGotTimecodeScale;
     263             : };
     264             : 
     265             : class WebMBufferedState final
     266             : {
     267           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebMBufferedState)
     268             : 
     269             : public:
     270           0 :   WebMBufferedState()
     271           0 :     : mReentrantMonitor("WebMBufferedState")
     272           0 :     , mLastBlockOffset(-1)
     273             :   {
     274           0 :     MOZ_COUNT_CTOR(WebMBufferedState);
     275           0 :   }
     276             : 
     277             :   void NotifyDataArrived(const unsigned char* aBuffer, uint32_t aLength, int64_t aOffset);
     278             :   void Reset();
     279             :   void UpdateIndex(const MediaByteRangeSet& aRanges, MediaResource* aResource);
     280             :   bool CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
     281             :                                  uint64_t* aStartTime, uint64_t* aEndTime);
     282             : 
     283             :   // Returns true if mTimeMapping is not empty and sets aOffset to
     284             :   // the latest offset for which decoding can resume without data
     285             :   // dependencies to arrive at aTime. aTime will be clamped to the start
     286             :   // of mTimeMapping if it is earlier than the first element, and to the end
     287             :   // if later than the last
     288             :   bool GetOffsetForTime(uint64_t aTime, int64_t* aOffset);
     289             : 
     290             :   // Returns end offset of init segment or -1 if none found.
     291             :   int64_t GetInitEndOffset();
     292             :   // Returns the end offset of the last complete block or -1 if none found.
     293             :   int64_t GetLastBlockOffset();
     294             : 
     295             :   // Returns start time
     296             :   bool GetStartTime(uint64_t *aTime);
     297             : 
     298             :   // Returns keyframe for time
     299             :   bool GetNextKeyframeTime(uint64_t aTime, uint64_t* aKeyframeTime);
     300             : 
     301             : private:
     302             :   // Private destructor, to discourage deletion outside of Release():
     303           0 :   ~WebMBufferedState() {
     304           0 :     MOZ_COUNT_DTOR(WebMBufferedState);
     305           0 :   }
     306             : 
     307             :   // Synchronizes access to the mTimeMapping array and mLastBlockOffset.
     308             :   ReentrantMonitor mReentrantMonitor;
     309             : 
     310             :   // Sorted (by offset) map of data offsets to timecodes.  Populated
     311             :   // on the main thread as data is received and parsed by WebMBufferedParsers.
     312             :   nsTArray<WebMTimeDataOffset> mTimeMapping;
     313             :   // The last complete block parsed. -1 if not set.
     314             :   int64_t mLastBlockOffset;
     315             : 
     316             :   // Sorted (by offset) live parser instances.  Main thread only.
     317             :   nsTArray<WebMBufferedParser> mRangeParsers;
     318             : };
     319             : 
     320             : } // namespace mozilla
     321             : 
     322             : #endif

Generated by: LCOV version 1.13