Bug 1195073: [MSE/webm] P4. Limit nestegg reads to the last block's boundaries. r=kinetik
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 19 Aug 2015 15:27:18 +1000
changeset 259036 1e7405c70314d01fb6a807fb36ff2af613b18d54
parent 259035 487ede19c0725efdc361c875c5fb727b3cbfc3a8
child 259037 90fac53dc387c3508c8501474b5924ae1b626ef7
push id29268
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:37:23 +0000
treeherdermozilla-central@08015770c9d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1195073
milestone43.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 1195073: [MSE/webm] P4. Limit nestegg reads to the last block's boundaries. r=kinetik 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
@@ -31,50 +31,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;
@@ -125,16 +124,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()
 {
@@ -249,17 +250,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;
   }
@@ -438,16 +439,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;
@@ -464,16 +467,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);