Bug 1195073: [MSE/webm] P1. Detect individual webm clusters. r=kinetik a=ritu
☠☠ backed out by 5bb661db5c6c ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 19 Aug 2015 15:20:09 +1000
changeset 288978 73daa8e4568dff87d1e8c488d9ab1278c3d9e578
parent 288977 89ec7fc554b9a7bd6d0543a5c7e8e15da2a0bfa2
child 288979 d2ed0c5abd35f821508afe5097394c9824ce7eb3
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: [MSE/webm] P1. Detect individual webm clusters. r=kinetik a=ritu WebMContainerParser was incorrectly reporting webm blocks rather than clusters, causing the webm demuxer to later fail to parse the remaining data.
dom/media/mediasource/ContainerParser.cpp
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -6,16 +6,17 @@
 
 #include "ContainerParser.h"
 
 #include "WebMBufferedParser.h"
 #include "mozilla/Endian.h"
 #include "mozilla/ErrorResult.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "mozilla/Logging.h"
+#include "mozilla/Maybe.h"
 #include "MediaData.h"
 #ifdef MOZ_FMP4
 #include "MP4Stream.h"
 #include "mp4_demuxer/AtomType.h"
 #include "mp4_demuxer/ByteReader.h"
 #endif
 #include "SourceBufferResource.h"
 
@@ -172,34 +173,35 @@ public:
       return true;
     }
     // 0x1c53bb6b // Cues
     if (aData->Length() >= 4 &&
         (*aData)[0] == 0x1c && (*aData)[1] == 0x53 && (*aData)[2] == 0xbb &&
         (*aData)[3] == 0x6b) {
       return true;
     }
-    // 0xa3 // SimpleBlock
-    if (aData->Length() >= 1 &&
-        (*aData)[0] == 0xa3) {
-      return true;
-    }
-    // 0xa1 // Block
-    if (aData->Length() >= 1 &&
-        (*aData)[0] == 0xa1) {
-      return true;
-    }
     return false;
   }
 
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
+      if (mLastMapping) {
+        // The last data contained a complete cluster but we can only detect it
+        // now that a new one is starting.
+        // We use mOffset as end position to ensure that any blocks not reported
+        // by WebMBufferParser are properly skipped.
+        mCompleteMediaSegmentRange = MediaByteRange(mLastMapping.ref().mSyncOffset,
+                                                    mOffset);
+        mLastMapping.reset();
+        MSE_DEBUG(WebMContainerParser, "New cluster found at start, ending previous one");
+        return false;
+      }
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
       mInitData = new MediaByteBuffer();
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/webm"));
       mCompleteMediaHeaderRange = MediaByteRange();
       mCompleteMediaSegmentRange = MediaByteRange();
     }
@@ -237,62 +239,96 @@ public:
       mHasInitData = true;
     }
     mOffset += aData->Length();
 
     if (mapping.IsEmpty()) {
       return false;
     }
 
-    uint32_t endIdx = mapping.Length() - 1;
-
-    // Calculate media range for first media segment
-    uint32_t segmentEndIdx = endIdx;
-    while (mapping[0].mSyncOffset != mapping[segmentEndIdx].mSyncOffset) {
-      segmentEndIdx -= 1;
-    }
-    if (segmentEndIdx > 0 && mOffset >= mapping[segmentEndIdx].mEndOffset) {
-      mCompleteMediaHeaderRange = MediaByteRange(mParser.mInitEndOffset,
-                                                 mapping[0].mEndOffset);
-      mCompleteMediaSegmentRange = MediaByteRange(mParser.mInitEndOffset,
-                                                  mapping[segmentEndIdx].mEndOffset);
-    }
-
-    // Exclude frames that we don't have enough data to cover the end of.
-    while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
-      endIdx -= 1;
-    }
-
-    if (endIdx == 0) {
+    if (mLastMapping &&
+        mLastMapping.ref().mSyncOffset != mapping[0].mSyncOffset) {
+      // The last data contained a complete cluster but we can only detect it
+      // now that a new one is starting.
+      // We use the start of the next cluster as end position to ensure that any
+      // blocks not reported by WebMBufferParser is properly skipped.
+      mCompleteMediaSegmentRange = MediaByteRange(mLastMapping.ref().mSyncOffset,
+                                                  mapping[0].mSyncOffset);
+      mOverlappedMapping.AppendElements(mapping);
+      mLastMapping.reset();
+      MSE_DEBUG(WebMContainerParser, "New cluster found at start, ending previous one");
       return false;
     }
 
-    uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
-    aStart = mapping[0].mTimecode / NS_PER_USEC;
-    aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
+    // Calculate media range for first media segment.
+
+    // Check if we have a cluster finishing in the current data.
+    uint32_t endIdx = mapping.Length() - 1;
+    bool foundNewCluster = false;
+    while (mapping[0].mSyncOffset != mapping[endIdx].mSyncOffset) {
+      endIdx -= 1;
+      foundNewCluster = true;
+    }
+
+    int32_t completeIdx = endIdx;
+    while (completeIdx >= 0 && mOffset < mapping[completeIdx].mEndOffset) {
+      MSE_DEBUG(WebMContainerParser, "block is incomplete, missing: %lld",
+                mapping[completeIdx].mEndOffset - mOffset);
+      completeIdx -= 1;
+    }
+
+    // Save parsed blocks for which we do not have all data yet.
+    mOverlappedMapping.AppendElements(mapping.Elements() + completeIdx + 1,
+                                      mapping.Length() - completeIdx - 1);
+
+    if (completeIdx < 0) {
+      mLastMapping.reset();
+      return false;
+    }
 
-    MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
-              aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
+    if (mCompleteMediaHeaderRange.IsNull()) {
+      mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset,
+                                                 mapping[0].mEndOffset);
+    }
+    mLastMapping = Some(mapping[completeIdx]);
+
+    if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
+      // We now have all information required to delimit a complete cluster.
+      mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
+                                                  mapping[endIdx].mEndOffset);
+    }
 
-    mapping.RemoveElementsAt(0, endIdx + 1);
-    mOverlappedMapping.AppendElements(mapping);
+    if (!completeIdx) {
+      return false;
+    }
+
+    uint64_t frameDuration =
+      mapping[completeIdx].mTimecode - mapping[completeIdx - 1].mTimecode;
+    aStart = mapping[0].mTimecode / NS_PER_USEC;
+    aEnd = (mapping[completeIdx].mTimecode + frameDuration) / NS_PER_USEC;
+
+    MSE_DEBUG(WebMContainerParser, "[%lld, %lld] [fso=%lld, leo=%lld, l=%u processedIdx=%u fs=%lld]",
+              aStart, aEnd, mapping[0].mSyncOffset,
+              mapping[completeIdx].mEndOffset, mapping.Length(), completeIdx,
+              mCompleteMediaSegmentRange.mEnd);
 
     return true;
   }
 
   int64_t GetRoundingError() override
   {
     int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
     return error * 2;
   }
 
 private:
   WebMBufferedParser mParser;
   nsTArray<WebMTimeDataOffset> mOverlappedMapping;
   int64_t mOffset;
+  Maybe<WebMTimeDataOffset> mLastMapping;
 };
 
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
     , mMonitor("MP4ContainerParser Index Monitor")