Bug 1062661 - Part 1: Discard decoders that don't have any data in them. r=karlt
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 04 Nov 2014 11:16:34 +1300
changeset 214247 bb67cbef0a07
parent 214246 974bf86f7002
child 214248 82171e97db6e
push id51451
push usermwoodrow@mozilla.com
push dateThu, 06 Nov 2014 04:52:07 +0000
treeherdermozilla-inbound@82171e97db6e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs1062661
milestone36.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 1062661 - Part 1: Discard decoders that don't have any data in them. r=karlt
dom/media/MediaDecoderReader.h
dom/media/mediasource/TrackBuffer.cpp
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -175,16 +175,24 @@ public:
   VideoData* DecodeToFirstVideoData();
 
   MediaInfo GetMediaInfo() { return mInfo; }
 
   // Indicates if the media is seekable.
   // ReadMetada should be called before calling this method.
   virtual bool IsMediaSeekable() = 0;
 
+  MediaTaskQueue* GetTaskQueue() {
+    return mTaskQueue;
+  }
+
+  void ClearDecoder() {
+    mDecoder = nullptr;
+  }
+
 protected:
   virtual ~MediaDecoderReader();
 
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
@@ -201,20 +209,16 @@ protected:
     return false;
   }
 
   RequestSampleCallback* GetCallback() {
     MOZ_ASSERT(mSampleDecodedCallback);
     return mSampleDecodedCallback;
   }
 
-  virtual MediaTaskQueue* GetTaskQueue() {
-    return mTaskQueue;
-  }
-
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<AudioData> mAudioQueue;
 
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
   MediaQueue<VideoData> mVideoQueue;
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -54,16 +54,17 @@ TrackBuffer::~TrackBuffer()
 class ReleaseDecoderTask : public nsRunnable {
 public:
   explicit ReleaseDecoderTask(SourceBufferDecoder* aDecoder)
     : mDecoder(aDecoder)
   {
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+    mDecoder->GetReader()->BreakCycles();
     mDecoder = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<SourceBufferDecoder> mDecoder;
 };
 
@@ -150,44 +151,54 @@ TrackBuffer::AppendDataToCurrentResource
 
   return true;
 }
 
 bool
 TrackBuffer::EvictData(uint32_t aThreshold)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
 
   int64_t totalSize = 0;
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
     totalSize += mDecoders[i]->GetResource()->GetSize();
   }
 
   int64_t toEvict = totalSize - aThreshold;
   if (toEvict <= 0) {
     return false;
   }
 
-  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
+  for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
     MSE_DEBUG("TrackBuffer(%p)::EvictData decoder=%u threshold=%u toEvict=%lld",
               this, i, aThreshold, toEvict);
-    toEvict -= mDecoders[i]->GetResource()->EvictData(toEvict);
+    toEvict -= mInitializedDecoders[i]->GetResource()->EvictData(toEvict);
+    if (!mInitializedDecoders[i]->GetResource()->GetSize() &&
+        mInitializedDecoders[i] != mCurrentDecoder) {
+      RemoveDecoder(mInitializedDecoders[i]);
+    }
   }
   return toEvict < (totalSize - aThreshold);
 }
 
 void
 TrackBuffer::EvictBefore(double aTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    int64_t endOffset = mDecoders[i]->ConvertToByteOffset(aTime);
+  ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
+  for (uint32_t i = 0; i < mInitializedDecoders.Length(); ++i) {
+    int64_t endOffset = mInitializedDecoders[i]->ConvertToByteOffset(aTime);
     if (endOffset > 0) {
       MSE_DEBUG("TrackBuffer(%p)::EvictBefore decoder=%u offset=%lld", this, i, endOffset);
-      mDecoders[i]->GetResource()->EvictBefore(endOffset);
+      mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset);
+      if (!mInitializedDecoders[i]->GetResource()->GetSize() &&
+          mInitializedDecoders[i] != mCurrentDecoder) {
+        RemoveDecoder(mInitializedDecoders[i]);
+      }
     }
   }
 }
 
 double
 TrackBuffer::Buffered(dom::TimeRanges* aRanges)
 {
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
@@ -453,25 +464,63 @@ TrackBuffer::Dump(const char* aPath)
     PR_snprintf(buf, sizeof(buf), "%s/reader-%p", path, mDecoders[i]->GetReader());
     PR_MkDir(buf, 0700);
 
     mDecoders[i]->GetResource()->Dump(buf);
   }
 }
 #endif
 
+class DelayedDispatchToMainThread : public nsRunnable {
+public:
+  explicit DelayedDispatchToMainThread(SourceBufferDecoder* aDecoder)
+    : mDecoder(aDecoder)
+  {
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL {
+    // Shutdown the reader, and remove its reference to the decoder
+    // so that it can't accidentally read it after the decoder
+    // is destroyed.
+    mDecoder->GetReader()->Shutdown();
+    mDecoder->GetReader()->ClearDecoder();
+    RefPtr<nsIRunnable> task = new ReleaseDecoderTask(mDecoder);
+    mDecoder = nullptr;
+    // task now holds the only ref to the decoder.
+    NS_DispatchToMainThread(task);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<SourceBufferDecoder> mDecoder;
+};
+
 void
 TrackBuffer::RemoveDecoder(SourceBufferDecoder* aDecoder)
 {
-  RefPtr<nsIRunnable> task = new ReleaseDecoderTask(aDecoder);
+  RefPtr<nsIRunnable> task;
+  nsRefPtr<MediaTaskQueue> taskQueue;
   {
     ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
-    MOZ_ASSERT(!mInitializedDecoders.Contains(aDecoder));
+    if (mInitializedDecoders.RemoveElement(aDecoder)) {
+      taskQueue = aDecoder->GetReader()->GetTaskQueue();
+      task = new DelayedDispatchToMainThread(aDecoder);
+    } else {
+      task = new ReleaseDecoderTask(aDecoder);
+    }
     mDecoders.RemoveElement(aDecoder);
+
     if (mCurrentDecoder == aDecoder) {
       DiscardDecoder();
     }
   }
   // At this point, task should be holding the only reference to aDecoder.
-  NS_DispatchToMainThread(task);
+  if (taskQueue) {
+    // If we were initialized, post the task via the reader's
+    // task queue to ensure that the reader isn't in the middle
+    // of an existing task.
+    taskQueue->Dispatch(task);
+  } else {
+    NS_DispatchToMainThread(task);
+  }
 }
 
 } // namespace mozilla