Bug 1123535 - Make MP4Reader and WMFMediaDataDecoder support going dormant. r=kentuckyfriedtakahe a=sledru
💩💩 backed out by bbc98a8c8142 💩 💩
authorChris Pearce <cpearce@mozilla.com>
Thu, 29 Jan 2015 21:50:48 +1300
changeset 243610 e884a5b5ff18
parent 243609 0280782f6bac
child 243611 f94e2c0e2971
push id4415
push userrgiles@mozilla.com
push date2015-02-01 19:08 +0000
treeherdermozilla-beta@1192f29eb59a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe, sledru
bugs1123535
milestone36.0
Bug 1123535 - Make MP4Reader and WMFMediaDataDecoder support going dormant. r=kentuckyfriedtakahe a=sledru
dom/media/fmp4/MP4Reader.cpp
dom/media/fmp4/MP4Reader.h
dom/media/fmp4/SharedDecoderManager.cpp
dom/media/fmp4/SharedDecoderManager.h
dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
dom/media/fmp4/wmf/WMFMediaDataDecoder.h
dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
dom/media/mediasource/MediaSourceReader.cpp
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -112,16 +112,19 @@ MP4Reader::MP4Reader(AbstractMediaDecode
   , mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
   , mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
   , mDemuxerInitialized(false)
   , mIsEncrypted(false)
   , mIndexReady(false)
   , mDemuxerMonitor("MP4 Demuxer")
+#if defined(XP_WIN)
+  , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
+#endif
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
 {
   MOZ_COUNT_DTOR(MP4Reader);
@@ -247,25 +250,25 @@ public:
 private:
   nsRefPtr<AbstractMediaDecoder> mDecoder;
   nsTArray<uint8_t> mInitData;
   nsString mInitDataType;
 };
 #endif
 
 void MP4Reader::RequestCodecResource() {
-#ifdef MOZ_GONK_MEDIACODEC
-  if(mVideo.mDecoder) {
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
+  if (mVideo.mDecoder) {
     mVideo.mDecoder->AllocateMediaResources();
   }
 #endif
 }
 
 bool MP4Reader::IsWaitingOnCodecResource() {
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
   return mVideo.mDecoder && mVideo.mDecoder->IsWaitingMediaResources();
 #endif
   return false;
 }
 
 bool MP4Reader::IsWaitingOnCDMResource() {
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> proxy;
@@ -441,17 +444,18 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
     if (mInfo.mVideo.mHasVideo && !IsSupportedVideoMimeType(video.mime_type)) {
       return NS_ERROR_FAILURE;
     }
     mInfo.mVideo.mDisplay =
       nsIntSize(video.display_width, video.display_height);
     mVideo.mCallback = new DecoderCallback(this, kVideo);
     if (mSharedDecoderManager) {
       mVideo.mDecoder =
-        mSharedDecoderManager->CreateVideoDecoder(video,
+        mSharedDecoderManager->CreateVideoDecoder(mPlatform,
+                                                  video,
                                                   mLayersBackendType,
                                                   mDecoder->GetImageContainer(),
                                                   mVideo.mTaskQueue,
                                                   mVideo.mCallback);
     } else {
       mVideo.mDecoder = mPlatform->CreateVideoDecoder(video,
                                                       mLayersBackendType,
                                                       mDecoder->GetImageContainer(),
@@ -996,40 +1000,45 @@ MP4Reader::GetBuffered(dom::TimeRanges* 
     }
   }
 
   return NS_OK;
 }
 
 bool MP4Reader::IsDormantNeeded()
 {
-#ifdef MOZ_GONK_MEDIACODEC
-  return mVideo.mDecoder && mVideo.mDecoder->IsDormantNeeded();
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
+  return
+#if defined(XP_WIN)
+        mDormantEnabled &&
+#endif
+        mVideo.mDecoder &&
+        mVideo.mDecoder->IsDormantNeeded();
 #endif
   return false;
 }
 
 void MP4Reader::ReleaseMediaResources()
 {
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->ClearCurrentFrame();
   }
   if (mVideo.mDecoder) {
     mVideo.mDecoder->ReleaseMediaResources();
   }
 #endif
 }
 
 void MP4Reader::NotifyResourcesStatusChanged()
 {
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
   if (mDecoder) {
     mDecoder->NotifyWaitingForResourcesStatusChanged();
   }
 #endif
 }
 
 void
 MP4Reader::SetIdle()
@@ -1038,14 +1047,14 @@ MP4Reader::SetIdle()
     mSharedDecoderManager->SetIdle(mVideo.mDecoder);
     NotifyResourcesStatusChanged();
   }
 }
 
 void
 MP4Reader::SetSharedDecoderManager(SharedDecoderManager* aManager)
 {
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
   mSharedDecoderManager = aManager;
 #endif
 }
 
 } // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -262,13 +262,17 @@ private:
   bool mDemuxerInitialized;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mIndexReady;
   Monitor mDemuxerMonitor;
   nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
+
+#if defined(XP_WIN)
+  const bool mDormantEnabled;
+#endif
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -50,36 +50,43 @@ public:
       mManager->mActiveCallback->ReleaseMediaResources();
     }
   }
 
   SharedDecoderManager* mManager;
 };
 
 SharedDecoderManager::SharedDecoderManager()
-  : mActiveProxy(nullptr)
+  : mTaskQueue(new MediaTaskQueue(GetMediaDecodeThreadPool()))
+  , mActiveProxy(nullptr)
   , mActiveCallback(nullptr)
   , mWaitForInternalDrain(false)
   , mMonitor("SharedDecoderProxy")
+  , mDecoderReleasedResources(false)
 {
+  MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread.
   mCallback = new SharedDecoderCallback(this);
 }
 
 SharedDecoderManager::~SharedDecoderManager() {}
 
 already_AddRefed<MediaDataDecoder>
 SharedDecoderManager::CreateVideoDecoder(
+  PlatformDecoderModule* aPDM,
   const mp4_demuxer::VideoDecoderConfig& aConfig,
   layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer,
   MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
 {
   if (!mDecoder) {
-    nsRefPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
-    mDecoder = platform->CreateVideoDecoder(
-      aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback);
+    // We use the manager's task queue for the decoder, rather than the one
+    // passed in, so that none of the objects sharing the decoder can shutdown
+    // the task queue while we're potentially still using it for a *different*
+    // object also sharing the decoder.
+    mDecoder = aPDM->CreateVideoDecoder(
+      aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
     if (!mDecoder) {
       return nullptr;
     }
     nsresult rv = mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
 
   nsRefPtr<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
@@ -91,16 +98,21 @@ SharedDecoderManager::Select(SharedDecod
 {
   if (mActiveProxy == aProxy) {
     return;
   }
   SetIdle(mActiveProxy);
 
   mActiveProxy = aProxy;
   mActiveCallback = aProxy->mCallback;
+
+  if (mDecoderReleasedResources) {
+    mDecoder->AllocateMediaResources();
+    mDecoderReleasedResources = false;
+  }
 }
 
 void
 SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
 {
   if (aProxy && mActiveProxy == aProxy) {
     mWaitForInternalDrain = true;
     mActiveProxy->Drain();
@@ -120,16 +132,38 @@ SharedDecoderManager::DrainComplete()
     MonitorAutoLock mon(mMonitor);
     mWaitForInternalDrain = false;
     mon.NotifyAll();
   } else {
     mActiveCallback->DrainComplete();
   }
 }
 
+void
+SharedDecoderManager::ReleaseMediaResources()
+{
+  mDecoderReleasedResources = true;
+  mDecoder->ReleaseMediaResources();
+  mActiveProxy = nullptr;
+}
+
+void
+SharedDecoderManager::Shutdown()
+{
+  if (mDecoder) {
+    mDecoder->Shutdown();
+    mDecoder = nullptr;
+  }
+  if (mTaskQueue) {
+    mTaskQueue->BeginShutdown();
+    mTaskQueue->AwaitShutdownAndIdle();
+    mTaskQueue = nullptr;
+  }
+}
+
 SharedDecoderProxy::SharedDecoderProxy(
   SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback)
   : mManager(aManager), mCallback(aCallback)
 {
 }
 
 SharedDecoderProxy::~SharedDecoderProxy() { Shutdown(); }
 
@@ -141,17 +175,16 @@ SharedDecoderProxy::Init()
 
 nsresult
 SharedDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
 {
   if (mManager->mActiveProxy != this) {
     mManager->Select(this);
   }
   return mManager->mDecoder->Input(aSample);
-  return NS_OK;
 }
 
 nsresult
 SharedDecoderProxy::Flush()
 {
   if (mManager->mActiveProxy == this) {
     return mManager->mDecoder->Flush();
   }
@@ -188,17 +221,17 @@ SharedDecoderProxy::IsDormantNeeded()
 {
   return mManager->mDecoder->IsDormantNeeded();
 }
 
 void
 SharedDecoderProxy::ReleaseMediaResources()
 {
   if (mManager->mActiveProxy == this) {
-    mManager->mDecoder->ReleaseMediaResources();
+    mManager->ReleaseMediaResources();
   }
 }
 
 void
 SharedDecoderProxy::ReleaseDecoder()
 {
   if (mManager->mActiveProxy == this) {
     mManager->mDecoder->ReleaseMediaResources();
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -20,38 +20,43 @@ class SharedDecoderCallback;
 class SharedDecoderManager
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedDecoderManager)
 
   SharedDecoderManager();
 
   already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
+    PlatformDecoderModule* aPDM,
     const mp4_demuxer::VideoDecoderConfig& aConfig,
     layers::LayersBackend aLayersBackend,
     layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue,
     MediaDataDecoderCallback* aCallback);
 
   void SetReader(MediaDecoderReader* aReader);
   void Select(SharedDecoderProxy* aProxy);
   void SetIdle(MediaDataDecoder* aProxy);
+  void ReleaseMediaResources();
+  void Shutdown();
 
   friend class SharedDecoderProxy;
   friend class SharedDecoderCallback;
 
 private:
   virtual ~SharedDecoderManager();
   void DrainComplete();
 
   nsRefPtr<MediaDataDecoder> mDecoder;
+  nsRefPtr<MediaTaskQueue> mTaskQueue;
   SharedDecoderProxy* mActiveProxy;
   MediaDataDecoderCallback* mActiveCallback;
   nsAutoPtr<MediaDataDecoderCallback> mCallback;
   bool mWaitForInternalDrain;
   Monitor mMonitor;
+  bool mDecoderReleasedResources;
 };
 
 class SharedDecoderProxy : public MediaDataDecoder
 {
 public:
   SharedDecoderProxy(SharedDecoderManager* aManager,
                      MediaDataDecoderCallback* aCallback);
   virtual ~SharedDecoderProxy();
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -43,28 +43,41 @@ WMFMediaDataDecoder::Init()
   NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 WMFMediaDataDecoder::Shutdown()
 {
-  mTaskQueue->FlushAndDispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
+  DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
+    NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
+#ifdef DEBUG
+  if (NS_FAILED(rv)) {
+    NS_WARNING("WMFMediaDataDecoder::Shutdown() dispatch of task failed!");
+  }
+#endif
   return NS_OK;
 }
 
 void
 WMFMediaDataDecoder::ProcessShutdown()
 {
   mMFTManager->Shutdown();
   mMFTManager = nullptr;
   mDecoder = nullptr;
 }
 
+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,
@@ -137,9 +150,33 @@ WMFMediaDataDecoder::ProcessDrain()
 
 nsresult
 WMFMediaDataDecoder::Drain()
 {
   mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain));
   return NS_OK;
 }
 
+void
+WMFMediaDataDecoder::AllocateMediaResources()
+{
+  mDecoder = mMFTManager->Init();
+}
+
+void
+WMFMediaDataDecoder::ReleaseMediaResources()
+{
+  DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
+    NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessReleaseDecoder));
+#ifdef DEBUG
+  if (NS_FAILED(rv)) {
+    NS_WARNING("WMFMediaDataDecoder::ReleaseMediaResources() dispatch of task failed!");
+  }
+#endif
+}
+
+void
+WMFMediaDataDecoder::ReleaseDecoder()
+{
+  ReleaseMediaResources();
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -65,31 +65,38 @@ public:
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
 
   virtual nsresult Flush() MOZ_OVERRIDE;
 
   virtual nsresult Drain() MOZ_OVERRIDE;
 
   virtual nsresult Shutdown() MOZ_OVERRIDE;
 
+  virtual bool IsWaitingMediaResources() { return false; };
+  virtual bool IsDormantNeeded() { return true; };
+  virtual void AllocateMediaResources() MOZ_OVERRIDE;
+  virtual void ReleaseMediaResources() MOZ_OVERRIDE;
+  virtual void ReleaseDecoder() MOZ_OVERRIDE;
+
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
   void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
 
   // 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();
 
   void ProcessShutdown();
+  void ProcessReleaseDecoder();
 
   RefPtr<MediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   RefPtr<MFTDecoder> mDecoder;
   nsAutoPtr<MFTManager> mMFTManager;
 
   // The last offset into the media resource that was passed into Input().
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -94,17 +94,19 @@ WMFVideoMFTManager::WMFVideoMFTManager(
     mStreamType = Unknown;
   }
 }
 
 WMFVideoMFTManager::~WMFVideoMFTManager()
 {
   MOZ_COUNT_DTOR(WMFVideoMFTManager);
   // Ensure DXVA/D3D9 related objects are released on the main thread.
-  DeleteOnMainThread(mDXVA2Manager);
+  if (mDXVA2Manager) {
+    DeleteOnMainThread(mDXVA2Manager);
+  }
 }
 
 const GUID&
 WMFVideoMFTManager::GetMFTGUID()
 {
   MOZ_ASSERT(mStreamType != Unknown);
   switch (mStreamType) {
     case H264: return CLSID_CMSH264DecoderMFT;
@@ -134,16 +136,18 @@ public:
     return NS_OK;
   }
   nsAutoPtr<DXVA2Manager> mDXVA2Manager;
 };
 
 bool
 WMFVideoMFTManager::InitializeDXVA()
 {
+  MOZ_ASSERT(!mDXVA2Manager);
+
   // If we use DXVA but aren't running with a D3D layer manager then the
   // readback of decoded video frames from GPU to CPU memory grinds painting
   // to a halt, and makes playback performance *worse*.
   if (!mDXVAEnabled ||
       (mLayersBackend != LayersBackend::LAYERS_D3D9 &&
        mLayersBackend != LayersBackend::LAYERS_D3D10 &&
        mLayersBackend != LayersBackend::LAYERS_D3D11)) {
     return false;
@@ -481,11 +485,12 @@ WMFVideoMFTManager::Output(int64_t aStre
 
   return S_OK;
 }
 
 void
 WMFVideoMFTManager::Shutdown()
 {
   mDecoder = nullptr;
+  DeleteOnMainThread(mDXVA2Manager);
 }
 
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -406,16 +406,21 @@ MediaSourceReader::ContinueShutdown()
     return;
   }
 
   mAudioTrack = nullptr;
   mAudioReader = nullptr;
   mVideoTrack = nullptr;
   mVideoReader = nullptr;
 
+  if (mSharedDecoderManager) {
+    mSharedDecoderManager->Shutdown();
+    mSharedDecoderManager = nullptr;
+  }
+
   MOZ_ASSERT(mAudioPromise.IsEmpty());
   MOZ_ASSERT(mVideoPromise.IsEmpty());
 
   mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
   mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
 
   MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
 }