Bug 1195073: [MSE/webm] P4. Limit nestegg reads to the last block's boundaries. r=kinetik a=ritu
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 19 Aug 2015 15:27:18 +1000
changeset 289183 2496e584f472e9bebaeaac1d361568f0d8765593
parent 289182 9958c6b9b6528c33875d89e345a308caf5bdf06e
child 289184 f7efe5f448b06bcf4cff666f24d75ac926aac1ce
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] P4. Limit nestegg reads to the last block's boundaries. r=kinetik a=ritu This prevent entering into an unrecoverable error state when parsing incomplete data as often seen with MSE.
dom/media/mediasource/SourceBufferResource.h
dom/media/webm/WebMDemuxer.cpp
dom/media/webm/WebMDemuxer.h
--- a/dom/media/mediasource/SourceBufferResource.h
+++ b/dom/media/mediasource/SourceBufferResource.h
@@ -95,16 +95,21 @@ public:
   }
 
   virtual size_t SizeOfIncludingThis(
                       MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
+  virtual bool IsExpectingMoreData() override
+  {
+    return false;
+  }
+
   // Used by SourceBuffer.
   void AppendData(MediaByteBuffer* aData);
   void Ended();
   bool IsEnded()
   {
     ReentrantMonitorAutoEnter mon(mMonitor);
     return mEnded;
   }
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -30,50 +30,49 @@ namespace mozilla {
 
 using namespace gfx;
 
 extern PRLogModuleInfo* gMediaDecoderLog;
 extern PRLogModuleInfo* gNesteggLog;
 
 // Functions for reading and seeking using WebMDemuxer required for
 // nestegg_io. The 'user data' passed to these functions is the
-// demuxer's MediaResourceIndex
+// demuxer.
 static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  int64_t length = resource->GetLength();
   MOZ_ASSERT(aLength < UINT32_MAX);
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  int64_t length = demuxer->GetEndDataOffset();
   uint32_t count = aLength;
-  if (length >= 0 && count + resource->Tell() > length) {
-    count = uint32_t(length - resource->Tell());
+  int64_t position = demuxer->GetResource()->Tell();
+  if (length >= 0 && count + position > length) {
+    count = length - position;
   }
 
   uint32_t bytes = 0;
-  nsresult rv = resource->Read(static_cast<char*>(aBuffer), count, &bytes);
-  bool eof = !bytes;
+  nsresult rv =
+    demuxer->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
+  bool eof = bytes < aLength;
   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
 }
 
 static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  nsresult rv = resource->Seek(aWhence, aOffset);
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  nsresult rv = demuxer->GetResource()->Seek(aWhence, aOffset);
   return NS_SUCCEEDED(rv) ? 0 : -1;
 }
 
 static int64_t webmdemux_tell(void* aUserData)
 {
   MOZ_ASSERT(aUserData);
-  MediaResourceIndex* resource =
-    reinterpret_cast<MediaResourceIndex*>(aUserData);
-  return resource->Tell();
+  WebMDemuxer* demuxer = reinterpret_cast<WebMDemuxer*>(aUserData);
+  return demuxer->GetResource()->Tell();
 }
 
 static void webmdemux_log(nestegg* aContext,
                           unsigned int aSeverity,
                           char const* aFormat, ...)
 {
   if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
     return;
@@ -124,16 +123,18 @@ WebMDemuxer::WebMDemuxer(MediaResource* 
   , mSeekPreroll(0)
   , mLastAudioFrameTime(0)
   , mLastVideoFrameTime(0)
   , mAudioCodec(-1)
   , mVideoCodec(-1)
   , mHasVideo(false)
   , mHasAudio(false)
   , mNeedReIndex(true)
+  , mLastWebMBlockOffset(-1)
+  , mIsExpectingMoreData(true)
 {
   if (!gNesteggLog) {
     gNesteggLog = PR_NewLogModule("Nestegg");
   }
 }
 
 WebMDemuxer::~WebMDemuxer()
 {
@@ -248,17 +249,17 @@ WebMDemuxer::Cleanup()
 
 nsresult
 WebMDemuxer::ReadMetadata()
 {
   nestegg_io io;
   io.read = webmdemux_read;
   io.seek = webmdemux_seek;
   io.tell = webmdemux_tell;
-  io.userdata = &mResource;
+  io.userdata = this;
   int64_t maxOffset = mBufferedState->GetInitEndOffset();
   if (maxOffset == -1) {
     maxOffset = mResource.GetLength();
   }
   int r = nestegg_init(&mContext, io, &webmdemux_log, maxOffset);
   if (r == -1) {
     return NS_ERROR_FAILURE;
   }
@@ -424,16 +425,18 @@ WebMDemuxer::EnsureUpToDateIndex()
   nsresult rv = resource->GetCachedRanges(byteRanges);
   if (NS_FAILED(rv) || !byteRanges.Length()) {
     return;
   }
   mBufferedState->UpdateIndex(byteRanges, resource);
   if (!mInitData && mBufferedState->GetInitEndOffset() != -1) {
     mInitData = mResource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
   }
+  mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
+  mIsExpectingMoreData = mResource.GetResource()->IsExpectingMoreData();
   mNeedReIndex = false;
 }
 
 void
 WebMDemuxer::NotifyDataArrived(uint32_t aLength, int64_t aOffset)
 {
   WEBM_DEBUG("length: %ld offset: %ld", aLength, aOffset);
   mNeedReIndex = true;
@@ -450,16 +453,18 @@ UniquePtr<EncryptionInfo>
 WebMDemuxer::GetCrypto()
 {
   return nullptr;
 }
 
 bool
 WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples)
 {
+  EnsureUpToDateIndex();
+
   nsRefPtr<NesteggPacketHolder> holder(NextPacket(aType));
 
   if (!holder) {
     return false;
   }
 
   int r = 0;
   unsigned int count = 0;
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -80,16 +80,28 @@ public:
   nsresult Reset();
 
   // Pushes a packet to the front of the audio packet queue.
   virtual void PushAudioPacket(NesteggPacketHolder* aItem);
 
   // Pushes a packet to the front of the video packet queue.
   virtual void PushVideoPacket(NesteggPacketHolder* aItem);
 
+  // Public accessor for nestegg callbacks
+  MediaResourceIndex* GetResource()
+  {
+    return &mResource;
+  }
+
+  int64_t GetEndDataOffset()
+  {
+    return mLastWebMBlockOffset < 0 || mIsExpectingMoreData
+      ? mResource.GetLength() : mLastWebMBlockOffset;
+  }
+
 private:
   friend class WebMTrackDemuxer;
 
   ~WebMDemuxer();
   void Cleanup();
   nsresult InitBufferedState();
   nsresult ReadMetadata();
   void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override;
@@ -147,16 +159,22 @@ private:
   int mAudioCodec;
   // Codec ID of video track
   int mVideoCodec;
 
   // Booleans to indicate if we have audio and/or video data
   bool mHasVideo;
   bool mHasAudio;
   bool mNeedReIndex;
+
+  // The last complete block parsed by the WebMBufferedState. -1 if not set.
+  // We cache those values rather than retrieving them for performance reasons
+  // as nestegg only performs 1-byte read at a time.
+  int64_t mLastWebMBlockOffset;
+  bool mIsExpectingMoreData;
 };
 
 class WebMTrackDemuxer : public MediaTrackDemuxer
 {
 public:
   WebMTrackDemuxer(WebMDemuxer* aParent,
                   TrackInfo::TrackType aType,
                   uint32_t aTrackNumber);