Bug 1354090: P3. Properly offset the media and init byte ranges. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 07 Apr 2017 12:04:24 +0200
changeset 559504 d89a9222774929ef28b0bc19e966adeb3db3c102
parent 559503 4ef0d10dee436d6a9338aa1d972be5911672890d
child 559505 73ae92315cb6fbfc31f2686f9d45585ffa65b296
push id53114
push userbmo:jyavenard@mozilla.com
push dateMon, 10 Apr 2017 08:02:08 +0000
reviewersgerald
bugs1354090
milestone55.0a1
Bug 1354090: P3. Properly offset the media and init byte ranges. r?gerald The init and media segment byte ranges were not offset by the amount of bytes currently parsed. Whenever a new init segment signature was seen we would recreate a container parser. This would lead to invalid offsets later. MozReview-Commit-ID: 8U7kTa7SK8O
dom/media/mediasource/ContainerParser.cpp
dom/media/mediasource/ContainerParser.h
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -30,16 +30,18 @@ extern mozilla::LogModule* GetMediaSourc
 #define TOSTRING(x) STRINGIFY(x)
 #define MSE_DEBUG(name, arg, ...) MOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Debug, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
 #define MSE_DEBUGV(name, arg, ...) MOZ_LOG(GetMediaSourceSamplesLog(), mozilla::LogLevel::Verbose, (TOSTRING(name) "(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 ContainerParser::ContainerParser(const MediaContainerType& aType)
   : mHasInitData(false)
+  , mTotalParsed(0)
+  , mGlobalOffset(0)
   , mType(aType)
 {
 }
 
 ContainerParser::~ContainerParser() = default;
 
 MediaResult
 ContainerParser::IsInitSegmentPresent(MediaByteBuffer* aData)
@@ -192,33 +194,35 @@ public:
     bool initSegment = NS_SUCCEEDED(IsInitSegmentPresent(aData));
 
     if (mLastMapping &&
         (initSegment || NS_SUCCEEDED(IsMediaSegmentPresent(aData)))) {
       // 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);
+      mCompleteMediaSegmentRange =
+        MediaByteRange(mLastMapping.ref().mSyncOffset, mOffset) + mGlobalOffset;
       mLastMapping.reset();
       MSE_DEBUG(WebMContainerParser,
                 "New cluster found at start, ending previous one");
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     if (initSegment) {
       mOffset = 0;
       mParser = WebMBufferedParser(0);
       mOverlappedMapping.Clear();
       mInitData = new MediaByteBuffer();
       mResource = new SourceBufferResource(
                         MediaContainerType(MEDIAMIMETYPE("video/webm")));
+      mCompleteInitSegmentRange = MediaByteRange();
       mCompleteMediaHeaderRange = MediaByteRange();
       mCompleteMediaSegmentRange = MediaByteRange();
+      mGlobalOffset = mTotalParsed;
     }
 
     // XXX if it only adds new mappings, overlapped but not available
     // (e.g. overlap < 0) frames are "lost" from the reported mappings here.
     nsTArray<WebMTimeDataOffset> mapping;
     mapping.AppendElements(mOverlappedMapping);
     mOverlappedMapping.Clear();
     ReentrantMonitor dummy("dummy");
@@ -232,28 +236,30 @@ public:
     // We should be more precise.
     if (initSegment || !HasCompleteInitData()) {
       if (mParser.mInitEndOffset > 0) {
         MOZ_ASSERT(mParser.mInitEndOffset <= mResource->GetLength());
         if (!mInitData->SetLength(mParser.mInitEndOffset, fallible)) {
           // Super unlikely OOM
           return NS_ERROR_OUT_OF_MEMORY;
         }
-        mCompleteInitSegmentRange = MediaByteRange(0, mParser.mInitEndOffset);
+        mCompleteInitSegmentRange =
+          MediaByteRange(0, mParser.mInitEndOffset) + mGlobalOffset;
         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
         mResource->ReadFromCache(buffer, 0, mParser.mInitEndOffset);
         MSE_DEBUG(WebMContainerParser, "Stashed init of %" PRId64 " bytes.",
                   mParser.mInitEndOffset);
         mResource = nullptr;
       } else {
         MSE_DEBUG(WebMContainerParser, "Incomplete init found.");
       }
       mHasInitData = true;
     }
     mOffset += aData->Length();
+    mTotalParsed += aData->Length();
 
     if (mapping.IsEmpty()) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     // Calculate media range for first media segment.
 
     // Check if we have a cluster finishing in the current data.
@@ -276,34 +282,37 @@ public:
                                       mapping.Length() - completeIdx - 1);
 
     if (completeIdx < 0) {
       mLastMapping.reset();
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     if (mCompleteMediaHeaderRange.IsEmpty()) {
-      mCompleteMediaHeaderRange = MediaByteRange(mapping[0].mSyncOffset,
-                                                 mapping[0].mEndOffset);
+      mCompleteMediaHeaderRange =
+        MediaByteRange(mapping[0].mSyncOffset, mapping[0].mEndOffset) +
+        mGlobalOffset;
     }
 
     if (foundNewCluster && mOffset >= mapping[endIdx].mEndOffset) {
       // We now have all information required to delimit a complete cluster.
       int64_t endOffset = mapping[endIdx+1].mSyncOffset;
       if (mapping[endIdx+1].mInitOffset > mapping[endIdx].mInitOffset) {
         // We have a new init segment before this cluster.
         endOffset = mapping[endIdx+1].mInitOffset;
       }
-      mCompleteMediaSegmentRange = MediaByteRange(mapping[endIdx].mSyncOffset,
-                                                  endOffset);
+      mCompleteMediaSegmentRange =
+        MediaByteRange(mapping[endIdx].mSyncOffset, endOffset) + mGlobalOffset;
     } else if (mapping[endIdx].mClusterEndOffset >= 0 &&
                mOffset >= mapping[endIdx].mClusterEndOffset) {
-      mCompleteMediaSegmentRange = MediaByteRange(
-        mapping[endIdx].mSyncOffset,
-        mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset));
+      mCompleteMediaSegmentRange =
+        MediaByteRange(
+          mapping[endIdx].mSyncOffset,
+          mParser.EndSegmentOffset(mapping[endIdx].mClusterEndOffset))
+        + mGlobalOffset;
     }
 
     Maybe<WebMTimeDataOffset> previousMapping;
     if (completeIdx) {
       previousMapping = Some(mapping[completeIdx - 1]);
     } else {
       previousMapping = mLastMapping;
     }
@@ -491,49 +500,58 @@ public:
                         MediaContainerType(MEDIAMIMETYPE("video/mp4")));
       mStream = new MP4Stream(mResource);
       // We use a timestampOffset of 0 for ContainerParser, and require
       // consumers of ParseStartAndEndTimestamps to add their timestamp offset
       // manually. This allows the ContainerParser to be shared across different
       // timestampOffsets.
       mParser = new mp4_demuxer::MoofParser(mStream, 0, /* aIsAudio = */ false);
       mInitData = new MediaByteBuffer();
+      mCompleteInitSegmentRange = MediaByteRange();
+      mCompleteMediaHeaderRange = MediaByteRange();
+      mCompleteMediaSegmentRange = MediaByteRange();
+      mGlobalOffset = mTotalParsed;
     } else if (!mStream || !mParser) {
+      mTotalParsed += aData->Length();
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     mResource->AppendData(aData);
     MediaByteRangeSet byteRanges;
     byteRanges +=
       MediaByteRange(int64_t(mParser->mOffset), mResource->GetLength());
     mParser->RebuildFragmentedIndex(byteRanges);
 
     if (initSegment || !HasCompleteInitData()) {
       MediaByteRange& range = mParser->mInitRange;
       if (range.Length()) {
-        mCompleteInitSegmentRange = range;
+        mCompleteInitSegmentRange = range + mGlobalOffset;
         if (!mInitData->SetLength(range.Length(), fallible)) {
           // Super unlikely OOM
           return NS_ERROR_OUT_OF_MEMORY;
         }
         char* buffer = reinterpret_cast<char*>(mInitData->Elements());
         mResource->ReadFromCache(buffer, range.mStart, range.Length());
         MSE_DEBUG(MP4ContainerParser ,"Stashed init of %" PRIu64 " bytes.",
                   range.Length());
       } else {
         MSE_DEBUG(MP4ContainerParser, "Incomplete init found.");
       }
       mHasInitData = true;
     }
+    mTotalParsed += aData->Length();
 
     mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
       mParser->GetCompositionRange(byteRanges);
 
-    mCompleteMediaHeaderRange = mParser->FirstCompleteMediaHeader();
-    mCompleteMediaSegmentRange = mParser->FirstCompleteMediaSegment();
+    mCompleteMediaHeaderRange =
+      mParser->FirstCompleteMediaHeader() + mGlobalOffset;
+    mCompleteMediaSegmentRange =
+      mParser->FirstCompleteMediaSegment() + mGlobalOffset;
+
     ErrorResult rv;
     if (HasCompleteInitData()) {
       mResource->EvictData(mParser->mOffset, mParser->mOffset, rv);
     }
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
       return NS_ERROR_OUT_OF_MEMORY;
     }
--- a/dom/media/mediasource/ContainerParser.h
+++ b/dom/media/mediasource/ContainerParser.h
@@ -75,16 +75,18 @@ public:
   MediaByteRange MediaSegmentRange();
 
   static ContainerParser* CreateForMIMEType(const MediaContainerType& aType);
 
 protected:
   RefPtr<MediaByteBuffer> mInitData;
   RefPtr<SourceBufferResource> mResource;
   bool mHasInitData;
+  uint64_t mTotalParsed;
+  uint64_t mGlobalOffset;
   MediaByteRange mCompleteInitSegmentRange;
   MediaByteRange mCompleteMediaHeaderRange;
   MediaByteRange mCompleteMediaSegmentRange;
   const MediaContainerType mType;
 };
 
 } // namespace mozilla