Bug 1155432 - Don't flush WMF PDM task queues. r=jya, a=sledru
authorChris Pearce <cpearce@mozilla.com>
Mon, 20 Apr 2015 20:03:41 +1200
changeset 260229 0920ace0d8b0
parent 260228 a9be9167d92b
child 260230 92fb098ace7a
push id723
push userryanvm@gmail.com
push date2015-04-22 14:15 +0000
treeherdermozilla-release@22f8fa3a9273 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, sledru
bugs1155432
milestone38.0
Bug 1155432 - Don't flush WMF PDM task queues. r=jya, a=sledru
dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
dom/media/fmp4/wmf/WMFMediaDataDecoder.h
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -22,87 +22,130 @@ PRLogModuleInfo* GetDemuxerLog();
 namespace mozilla {
 
 WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager,
                                          FlushableMediaTaskQueue* aTaskQueue,
                                          MediaDataDecoderCallback* aCallback)
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mMFTManager(aMFTManager)
+  , mMonitor("WMFMediaDataDecoder")
+  , mIsDecodeTaskDispatched(false)
+  , mIsFlushing(false)
 {
-  MOZ_COUNT_CTOR(WMFMediaDataDecoder);
 }
 
 WMFMediaDataDecoder::~WMFMediaDataDecoder()
 {
-  MOZ_COUNT_DTOR(WMFMediaDataDecoder);
 }
 
 nsresult
 WMFMediaDataDecoder::Init()
 {
   mDecoder = mMFTManager->Init();
   NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 WMFMediaDataDecoder::Shutdown()
 {
-  DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
+  DebugOnly<nsresult> rv = mTaskQueue->Dispatch(
     NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
 #ifdef DEBUG
   if (NS_FAILED(rv)) {
     NS_WARNING("WMFMediaDataDecoder::Shutdown() dispatch of task failed!");
   }
+  {
+    MonitorAutoLock mon(mMonitor);
+    // The MP4Reader should have flushed before calling Shutdown().
+    MOZ_ASSERT(!mIsDecodeTaskDispatched);
+  }
 #endif
   return NS_OK;
 }
 
 void
 WMFMediaDataDecoder::ProcessShutdown()
 {
-  mMFTManager->Shutdown();
-  mMFTManager = nullptr;
+  if (mMFTManager) {
+    mMFTManager->Shutdown();
+    mMFTManager = nullptr;
+  }
   mDecoder = nullptr;
 }
 
 void
+WMFMediaDataDecoder::EnsureDecodeTaskDispatched()
+{
+  mMonitor.AssertCurrentThreadOwns();
+  if (!mIsDecodeTaskDispatched) {
+    mTaskQueue->Dispatch(
+      NS_NewRunnableMethod(this,
+      &WMFMediaDataDecoder::Decode));
+    mIsDecodeTaskDispatched = true;
+  }
+}
+
+void
 WMFMediaDataDecoder::ProcessReleaseDecoder()
 {
   mMFTManager->Shutdown();
   mDecoder = nullptr;
 }
 
 // Inserts data into the decoder's pipeline.
 nsresult
 WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
 {
-  mTaskQueue->Dispatch(
-    NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
-      this,
-      &WMFMediaDataDecoder::ProcessDecode,
-      nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
+  MonitorAutoLock mon(mMonitor);
+  mInput.push(nsAutoPtr<mp4_demuxer::MP4Sample>(aSample));
+  EnsureDecodeTaskDispatched();
   return NS_OK;
 }
 
 void
-WMFMediaDataDecoder::ProcessDecode(mp4_demuxer::MP4Sample* aSample)
+WMFMediaDataDecoder::Decode()
 {
-  HRESULT hr = mMFTManager->Input(aSample);
-  if (FAILED(hr)) {
-    NS_WARNING("MFTManager rejected sample");
-    mCallback->Error();
-    return;
+  while (true) {
+    nsAutoPtr<mp4_demuxer::MP4Sample> input;
+    {
+      MonitorAutoLock mon(mMonitor);
+      MOZ_ASSERT(mIsDecodeTaskDispatched);
+      if (mInput.empty()) {
+        if (mIsFlushing) {
+          if (mDecoder) {
+            mDecoder->Flush();
+          }
+          mIsFlushing = false;
+        }
+        mIsDecodeTaskDispatched = false;
+        mon.NotifyAll();
+        return;
+      }
+      input = mInput.front();
+      mInput.pop();
+    }
+
+    HRESULT hr = mMFTManager->Input(input);
+    if (FAILED(hr)) {
+      NS_WARNING("MFTManager rejected sample");
+      {
+        MonitorAutoLock mon(mMonitor);
+        PurgeInputQueue();
+      }
+      mCallback->Error();
+      return;
+    }
+
+    mLastStreamOffset = input->byte_offset;
+
+    ProcessOutput();
   }
-
-  mLastStreamOffset = aSample->byte_offset;
-
-  ProcessOutput();
 }
 
 void
 WMFMediaDataDecoder::ProcessOutput()
 {
   nsRefPtr<MediaData> output;
   HRESULT hr = S_OK;
   while (SUCCEEDED(hr = mMFTManager->Output(mLastStreamOffset, output)) &&
@@ -110,34 +153,43 @@ WMFMediaDataDecoder::ProcessOutput()
     mCallback->Output(output);
   }
   if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
     if (mTaskQueue->IsEmpty()) {
       mCallback->InputExhausted();
     }
   } else if (FAILED(hr)) {
     NS_WARNING("WMFMediaDataDecoder failed to output data");
+    {
+      MonitorAutoLock mon(mMonitor);
+      PurgeInputQueue();
+    }
     mCallback->Error();
   }
 }
 
+void
+WMFMediaDataDecoder::PurgeInputQueue()
+{
+  mMonitor.AssertCurrentThreadOwns();
+  while (!mInput.empty()) {
+    mInput.pop();
+  }
+}
+
 nsresult
 WMFMediaDataDecoder::Flush()
 {
-  // Flush the input task queue. This cancels all pending Decode() calls.
-  // Note this blocks until the task queue finishes its current job, if
-  // it's executing at all. Note the MP4Reader ignores all output while
-  // flushing.
-  mTaskQueue->Flush();
-
-  // Order the MFT to flush; drop all internal data.
-  NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
-  HRESULT hr = mDecoder->Flush();
-  NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
-
+  MonitorAutoLock mon(mMonitor);
+  PurgeInputQueue();
+  mIsFlushing = true;
+  EnsureDecodeTaskDispatched();
+  while (mIsDecodeTaskDispatched || mIsFlushing) {
+    mon.Wait();
+  }
   return NS_OK;
 }
 
 void
 WMFMediaDataDecoder::ProcessDrain()
 {
   if (mDecoder) {
     // Order the decoder to drain...
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -78,17 +78,19 @@ public:
   virtual void AllocateMediaResources() override;
   virtual void ReleaseMediaResources() override;
   virtual bool IsHardwareAccelerated() const override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
-  void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
+  void Decode();
+  void EnsureDecodeTaskDispatched();
+  void PurgeInputQueue();
 
   // Called on the task queue. Extracts output if available, and delivers
   // it to the reader. Called after ProcessDecode() and ProcessDrain().
   void ProcessOutput();
 
   // Called on the task queue. Orders the MFT to drain, and then extracts
   // all available output.
   void ProcessDrain();
@@ -100,13 +102,18 @@ private:
   MediaDataDecoderCallback* mCallback;
 
   RefPtr<MFTDecoder> mDecoder;
   nsAutoPtr<MFTManager> mMFTManager;
 
   // The last offset into the media resource that was passed into Input().
   // This is used to approximate the decoder's position in the media resource.
   int64_t mLastStreamOffset;
+
+  Monitor mMonitor;
+  std::queue<nsAutoPtr<mp4_demuxer::MP4Sample>> mInput;
+  bool mIsDecodeTaskDispatched;
+  bool mIsFlushing;
 };
 
 } // namespace mozilla
 
 #endif // WMFMediaDataDecoder_h_