Bug 1044498 - Improve accuracy of WebM buffered parser. r=cajbir
authorMatthew Gregan <kinetik@flim.org>
Mon, 25 Aug 2014 16:09:44 +1200
changeset 201266 f277da7fa9fac4046a97f20e4c21669d0eff684f
parent 201265 c40f86ae9b17c95cb2f81ba48e677cf3fb183307
child 201267 2c94c5407b1c330bb1480ed160d736ba8154f5dd
push id48139
push usermgregan@mozilla.com
push dateMon, 25 Aug 2014 04:10:24 +0000
treeherdermozilla-inbound@2c94c5407b1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscajbir
bugs1044498
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1044498 - Improve accuracy of WebM buffered parser. r=cajbir Store the end offset of the block, rather than the start offset, since a frame is only usable if we have all of it. Also compute the start of the buffered range based on the offset of the block's resync point, since we can't decode frames unless we have their cluster to resync at and compute the absolute timecode from.
content/media/webm/WebMBufferedParser.cpp
content/media/webm/WebMBufferedParser.h
--- a/content/media/webm/WebMBufferedParser.cpp
+++ b/content/media/webm/WebMBufferedParser.cpp
@@ -139,24 +139,26 @@ void WebMBufferedParser::Append(const un
         mBlockTimecode <<= 8;
         mBlockTimecode |= *p++;
         mBlockTimecodeLength -= 1;
       } else {
         // It's possible we've parsed this data before, so avoid inserting
         // duplicate WebMTimeDataOffset entries.
         {
           ReentrantMonitorAutoEnter mon(aReentrantMonitor);
-          uint32_t idx = aMapping.IndexOfFirstElementGt(mBlockOffset);
-          if (idx == 0 || !(aMapping[idx - 1] == mBlockOffset)) {
+          int64_t endOffset = mBlockOffset + mBlockSize +
+                              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(mBlockOffset, absTimecode, mClusterOffset);
+              WebMTimeDataOffset entry(endOffset, absTimecode, mClusterOffset);
               aMapping.InsertElementAt(idx, entry);
             }
           }
         }
 
         // Skip rest of block header and the block's payload.
         mBlockSize -= mVInt.mLength;
         mBlockSize -= BLOCK_TIMECODE_LENGTH;
@@ -177,57 +179,63 @@ void WebMBufferedParser::Append(const un
       break;
     }
   }
 
   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
   mCurrentOffset += aLength;
 }
 
+struct SyncOffsetComparator {
+  bool Equals(const WebMTimeDataOffset& a, const int64_t& b) const {
+    return a.mSyncOffset == b;
+  }
+
+  bool LessThan(const WebMTimeDataOffset& a, const int64_t& b) const {
+    return a.mSyncOffset < b;
+  }
+};
+
 bool WebMBufferedState::CalculateBufferedForRange(int64_t aStartOffset, int64_t aEndOffset,
                                                   uint64_t* aStartTime, uint64_t* aEndTime)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   // Find the first WebMTimeDataOffset at or after aStartOffset.
-  uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset - 1);
+  uint32_t start = mTimeMapping.IndexOfFirstElementGt(aStartOffset - 1, SyncOffsetComparator());
   if (start == mTimeMapping.Length()) {
     return false;
   }
 
   // Find the first WebMTimeDataOffset at or before aEndOffset.
-  uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset - 1);
+  uint32_t end = mTimeMapping.IndexOfFirstElementGt(aEndOffset);
   if (end > 0) {
     end -= 1;
   }
 
   // Range is empty.
   if (end <= start) {
     return false;
   }
 
-  NS_ASSERTION(mTimeMapping[start].mOffset >= aStartOffset &&
-               mTimeMapping[end].mOffset <= aEndOffset,
+  NS_ASSERTION(mTimeMapping[start].mSyncOffset >= aStartOffset &&
+               mTimeMapping[end].mEndOffset <= aEndOffset,
                "Computed time range must lie within data range.");
   if (start > 0) {
-    NS_ASSERTION(mTimeMapping[start - 1].mOffset <= aStartOffset,
+    NS_ASSERTION(mTimeMapping[start - 1].mSyncOffset < aStartOffset,
                  "Must have found least WebMTimeDataOffset for start");
   }
   if (end < mTimeMapping.Length() - 1) {
-    NS_ASSERTION(mTimeMapping[end + 1].mOffset >= aEndOffset,
+    NS_ASSERTION(mTimeMapping[end + 1].mEndOffset > aEndOffset,
                  "Must have found greatest WebMTimeDataOffset for end");
   }
 
-  // The timestamp of the first media sample, in ns. We must subtract this
-  // from the ranges' start and end timestamps, so that those timestamps are
-  // normalized in the range [0,duration].
-
+  uint64_t frameDuration = mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode;
   *aStartTime = mTimeMapping[start].mTimecode;
-  *aEndTime = mTimeMapping[end].mTimecode;
-  *aEndTime += mTimeMapping[end].mTimecode - mTimeMapping[end - 1].mTimecode;
+  *aEndTime = mTimeMapping[end].mTimecode + frameDuration;
   return true;
 }
 
 bool WebMBufferedState::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   WebMTimeDataOffset result(0, 0, 0);
 
--- a/content/media/webm/WebMBufferedParser.h
+++ b/content/media/webm/WebMBufferedParser.h
@@ -15,29 +15,33 @@ namespace mozilla {
 namespace dom {
 class TimeRanges;
 }
 
 // Stores a stream byte offset and the scaled timecode of the block at
 // that offset.
 struct WebMTimeDataOffset
 {
-  WebMTimeDataOffset(int64_t aOffset, uint64_t aTimecode, int64_t aSyncOffset)
-    : mOffset(aOffset), mSyncOffset(aSyncOffset), mTimecode(aTimecode)
+  WebMTimeDataOffset(int64_t aEndOffset, uint64_t aTimecode, int64_t aSyncOffset)
+    : mEndOffset(aEndOffset), mSyncOffset(aSyncOffset), mTimecode(aTimecode)
   {}
 
-  bool operator==(int64_t aOffset) const {
-    return mOffset == aOffset;
+  bool operator==(int64_t aEndOffset) const {
+    return mEndOffset == aEndOffset;
   }
 
-  bool operator<(int64_t aOffset) const {
-    return mOffset < aOffset;
+  bool operator!=(int64_t aEndOffset) const {
+    return mEndOffset != aEndOffset;
   }
 
-  int64_t mOffset;
+  bool operator<(int64_t aEndOffset) const {
+    return mEndOffset < aEndOffset;
+  }
+
+  int64_t mEndOffset;
   int64_t mSyncOffset;
   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