Bug 1123535 - Make MP4Reader and WMFMediaDataDecoder support going dormant. r=kentuckyfriedtakahe
☠☠ backed out by b2b594928bb3 ☠ ☠
authorChris Pearce <cpearce@mozilla.com>
Tue, 27 Jan 2015 19:30:11 +1300
changeset 225927 cdcfd5fda7b4ce42796028220b02b0d282c7745f
parent 225926 b6bec74def09427aa02c470f8e5fe14a33e3d171
child 225928 7348cb10d7b0282075aef7ed8313c901bac34c68
push id54702
push usercpearce@mozilla.com
push dateTue, 27 Jan 2015 06:30:29 +0000
treeherdermozilla-inbound@f6da914b2a93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe
bugs1123535
milestone38.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 1123535 - Make MP4Reader and WMFMediaDataDecoder support going dormant. r=kentuckyfriedtakahe
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
@@ -95,17 +95,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;
@@ -135,16 +137,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;
@@ -482,11 +486,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__);
 }