Bug 1195073: [webm] P6. Calculate cluster's end offset if its size is known in advance. r=kinetik. a=ritu
☠☠ backed out by 5bb661db5c6c ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sun, 23 Aug 2015 20:46:08 +1000
changeset 288983 cbbb8dbd27450cbbfff4b8edb4360ba72aaab2de
parent 288982 e24897436eaa601d8a9ad299c4d47659f7953cdf
child 288984 1fb0a72a7fca8a839412752013851e2c57d48482
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, ritu
bugs1195073
milestone42.0a2
Bug 1195073: [webm] P6. Calculate cluster's end offset if its size is known in advance. r=kinetik. a=ritu This allows to detect the end of a webm media segment without having to wait for the start of a new one. Also record where an init segment (EBML) starts as this will be required by the WebM ContainerParser.
dom/media/webm/WebMBufferedParser.cpp
dom/media/webm/WebMBufferedParser.h
--- a/dom/media/webm/WebMBufferedParser.cpp
+++ b/dom/media/webm/WebMBufferedParser.cpp
@@ -29,16 +29,17 @@ VIntLength(unsigned char aFirstByte, uin
   NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
   return count;
 }
 
 void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
                                 nsTArray<WebMTimeDataOffset>& aMapping,
                                 ReentrantMonitor& aReentrantMonitor)
 {
+  static const uint32_t EBML_ID = 0x1a45dfa3;
   static const uint32_t SEGMENT_ID = 0x18538067;
   static const uint32_t SEGINFO_ID = 0x1549a966;
   static const uint32_t TRACKS_ID = 0x1654AE6B;
   static const uint32_t CLUSTER_ID = 0x1f43b675;
   static const uint32_t TIMECODESCALE_ID = 0x2ad7b1;
   static const unsigned char TIMECODE_ID = 0xe7;
   static const unsigned char BLOCK_ID = 0xa1;
   static const unsigned char SIMPLEBLOCK_ID = 0xa3;
@@ -97,16 +98,22 @@ void WebMBufferedParser::Append(const un
         mVInt = VInt();
         mVIntLeft = mElement.mSize.mValue;
         mState = READ_VINT_REST;
         mNextState = READ_TIMECODESCALE;
         break;
       case CLUSTER_ID:
         mClusterOffset = mCurrentOffset + (p - aBuffer) -
                         (mElement.mID.mLength + mElement.mSize.mLength);
+        // Handle "unknown" length;
+        if (mElement.mSize.mValue + 1 != uint64_t(1) << (mElement.mSize.mLength * 7)) {
+          mClusterEndOffset = mClusterOffset + mElement.mID.mLength + mElement.mSize.mLength + mElement.mSize.mValue;
+        } else {
+          mClusterEndOffset = -1;
+        }
         mState = READ_ELEMENT_ID;
         break;
       case SIMPLEBLOCK_ID:
         /* FALLTHROUGH */
       case BLOCK_ID:
         mBlockSize = mElement.mSize.mValue;
         mBlockTimecode = 0;
         mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH;
@@ -114,16 +121,20 @@ void WebMBufferedParser::Append(const un
                        (mElement.mID.mLength + mElement.mSize.mLength);
         mState = READ_VINT;
         mNextState = READ_BLOCK_TIMECODE;
         break;
       case TRACKS_ID:
         mSkipBytes = mElement.mSize.mValue;
         mState = CHECK_INIT_FOUND;
         break;
+      case EBML_ID:
+        mLastInitStartOffset = mCurrentOffset + (p - aBuffer) -
+                            (mElement.mID.mLength + mElement.mSize.mLength);
+        /* FALLTHROUGH */
       default:
         mSkipBytes = mElement.mSize.mValue;
         mState = SKIP_DATA;
         mNextState = READ_ELEMENT_ID;
         break;
       }
       break;
     case READ_VINT: {
@@ -167,17 +178,18 @@ void WebMBufferedParser::Append(const un
                               mElement.mID.mLength + mElement.mSize.mLength;
           uint32_t idx = aMapping.IndexOfFirstElementGt(endOffset);
           if (idx == 0 || aMapping[idx - 1] != endOffset) {
             // Don't insert invalid negative timecodes.
             if (mBlockTimecode >= 0 || mClusterTimecode >= uint16_t(abs(mBlockTimecode))) {
               MOZ_ASSERT(mGotTimecodeScale);
               uint64_t absTimecode = mClusterTimecode + mBlockTimecode;
               absTimecode *= mTimecodeScale;
-              WebMTimeDataOffset entry(endOffset, absTimecode, mClusterOffset);
+              WebMTimeDataOffset entry(endOffset, absTimecode, mLastInitStartOffset,
+                                       mClusterOffset, mClusterEndOffset);
               aMapping.InsertElementAt(idx, entry);
             }
           }
         }
 
         // Skip rest of block header and the block's payload.
         mBlockSize -= mVInt.mLength;
         mBlockSize -= BLOCK_TIMECODE_LENGTH;
@@ -215,16 +227,26 @@ void WebMBufferedParser::Append(const un
       break;
     }
   }
 
   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
   mCurrentOffset += aLength;
 }
 
+int64_t
+WebMBufferedParser::EndSegmentOffset(int64_t aOffset)
+{
+  if (mLastInitStartOffset > aOffset || mClusterOffset > aOffset) {
+    return std::min(mLastInitStartOffset >= 0 ? mLastInitStartOffset : INT64_MAX,
+                    mClusterOffset >= 0 ? mClusterOffset : INT64_MAX);
+  }
+  return mBlockEndOffset;
+}
+
 // SyncOffsetComparator and TimeComparator are slightly confusing, in that
 // the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and
 // these comparators are used on the other fields of WebMTimeDataOffset.
 // This is only valid because timecodes are required to be monotonically
 // increasing within a file (thus establishing an ordering relationship with
 // mTimecode), and mEndOffset is derived from mSyncOffset.
 struct SyncOffsetComparator {
   bool Equals(const WebMTimeDataOffset& a, const int64_t& b) const {
--- a/dom/media/webm/WebMBufferedParser.h
+++ b/dom/media/webm/WebMBufferedParser.h
@@ -12,34 +12,42 @@
 #include "MediaResource.h"
 
 namespace mozilla {
 
 // Stores a stream byte offset and the scaled timecode of the block at
 // that offset.
 struct WebMTimeDataOffset
 {
-  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode, int64_t aSyncOffset)
-    : mEndOffset(aEndOffset), mSyncOffset(aSyncOffset), mTimecode(aTimecode)
+  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode,
+                     int64_t aInitOffset, int64_t aSyncOffset,
+                     int64_t aClusterEndOffset)
+    : mEndOffset(aEndOffset)
+    , mInitOffset(aInitOffset)
+    , mSyncOffset(aSyncOffset)
+    , mClusterEndOffset(aClusterEndOffset)
+    , mTimecode(aTimecode)
   {}
 
   bool operator==(int64_t aEndOffset) const {
     return mEndOffset == aEndOffset;
   }
 
   bool operator!=(int64_t aEndOffset) const {
     return mEndOffset != aEndOffset;
   }
 
   bool operator<(int64_t aEndOffset) const {
     return mEndOffset < aEndOffset;
   }
 
   int64_t mEndOffset;
+  int64_t mInitOffset;
   int64_t mSyncOffset;
+  int64_t mClusterEndOffset;
   uint64_t mTimecode;
 };
 
 // A simple WebM parser that produces data offset to timecode pairs as it
 // consumes blocks.  A new parser is created for each distinct range of data
 // received and begins parsing from the first WebM cluster within that
 // range.  Old parsers are destroyed when their range merges with a later
 // parser or an already parsed range.  The parser may start at any position
@@ -48,17 +56,19 @@ struct WebMBufferedParser
 {
   explicit WebMBufferedParser(int64_t aOffset)
     : mStartOffset(aOffset)
     , mCurrentOffset(aOffset)
     , mInitEndOffset(-1)
     , mBlockEndOffset(-1)
     , mState(READ_ELEMENT_ID)
     , mVIntRaw(false)
+    , mLastInitStartOffset(-1)
     , mClusterSyncPos(0)
+    , mClusterEndOffset(-1)
     , mTimecodeScale(1000000)
     , mGotTimecodeScale(false)
   {
     if (mStartOffset != 0) {
       mState = FIND_CLUSTER_SYNC;
     }
   }
 
@@ -84,31 +94,36 @@ struct WebMBufferedParser
   bool operator==(int64_t aOffset) const {
     return mCurrentOffset == aOffset;
   }
 
   bool operator<(int64_t aOffset) const {
     return mCurrentOffset < aOffset;
   }
 
+  // Returns the start offset of the init (EBML) or media segment (Cluster)
+  // following the aOffset position. If none were found, returns mBlockEndOffset.
+  // This allows to determine the end of the interval containg aOffset.
+  int64_t EndSegmentOffset(int64_t aOffset);
+
   // The offset at which this parser started parsing.  Used to merge
   // adjacent parsers, in which case the later parser adopts the earlier
   // parser's mStartOffset.
   int64_t mStartOffset;
 
-  // Current offset with the stream.  Updated in chunks as Append() consumes
+  // Current offset within the stream.  Updated in chunks as Append() consumes
   // data.
   int64_t mCurrentOffset;
 
-  // Tracks element's end offset. This indicates the end of the init segment.
-  // Will only be set if a Segment Information has been found.
+  // Tracks element's end offset. This indicates the end of the first init
+  // segment. Will only be set if a Segment Information has been found.
   int64_t mInitEndOffset;
 
-  // End offset of the last block.
-  // Will only be set if a full block has been parsed.
+  // End offset of the last block parsed.
+  // Will only be set if a complete block has been parsed.
   int64_t mBlockEndOffset;
 
 private:
   enum State {
     // Parser start state.  Expects to begin at a valid EBML element.  Move
     // to READ_VINT with mVIntRaw true, then return to READ_ELEMENT_SIZE.
     READ_ELEMENT_ID,
 
@@ -180,16 +195,20 @@ private:
   };
 
   EBMLElement mElement;
 
   VInt mVInt;
 
   bool mVIntRaw;
 
+  // EBML start offset. This indicates the start of the last init segment
+  // parsed. Will only be set if an EBML element has been found.
+  int64_t mLastInitStartOffset;
+
   // Current match position within CLUSTER_SYNC_ID.  Used to find sync
   // within arbitrary data.
   uint32_t mClusterSyncPos;
 
   // Number of bytes of mVInt left to read.  mVInt is complete once this
   // reaches 0.
   uint32_t mVIntLeft;
 
@@ -200,16 +219,19 @@ private:
   // Cluster-level timecode.
   uint64_t mClusterTimecode;
 
   // Start offset of the cluster currently being parsed.  Used as the sync
   // point offset for the offset-to-time mapping as each block timecode is
   // been parsed.
   int64_t mClusterOffset;
 
+  // End offset of the cluster currently being parsed. -1 if unknown.
+  int64_t mClusterEndOffset;
+
   // Start offset of the block currently being parsed.  Used as the byte
   // offset for the offset-to-time mapping once the block timecode has been
   // parsed.
   int64_t mBlockOffset;
 
   // Block-level timecode.  This is summed with mClusterTimecode to produce
   // an absolute timecode for the offset-to-time mapping.
   int16_t mBlockTimecode;