Bug 1123535 - Revert bbc98a8c8142 to reland Bug 1123535. r=me a=sledru
authorChris Pearce <cpearce@mozilla.com>
Tue, 03 Feb 2015 14:19:48 +1300
changeset 243655 41fac15f269a
parent 243654 c860bf9bc4a8
child 243656 f74e583e724f
push id4424
push usercpearce@mozilla.com
push date2015-02-03 01:21 +0000
treeherdermozilla-beta@f74e583e724f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme, sledru
bugs1123535
milestone36.0
Bug 1123535 - Revert bbc98a8c8142 to reland Bug 1123535. r=me a=sledru
dom/media/MediaDecoderStateMachine.cpp
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
modules/libpref/init/all.js
testing/profiles/prefs_general.js
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1565,19 +1565,19 @@ void MediaDecoderStateMachine::StartDeco
   // Ensure that we've got tasks enqueued to decode data if we need to.
   DispatchDecodeTasksIfNeeded();
 
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StartWaitForResources()
 {
-  NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
-               "Should be on state machine or decode thread.");
-  AssertCurrentThreadInMonitor();
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+  NS_ASSERTION(OnDecodeThread(),
+               "Should be on decode thread.");
   SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
   DECODER_LOG("StartWaitForResources");
 }
 
 void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
 {
   AssertCurrentThreadInMonitor();
   DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
@@ -1937,17 +1937,17 @@ MediaDecoderStateMachine::EnsureAudioDec
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%d",
               IsAudioDecoding(), mAudioRequestStatus);
 
-  if (mState >= DECODER_STATE_COMPLETED) {
+  if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mState >= DECODER_STATE_DECODING_FIRSTFRAME);
 
   if (IsAudioDecoding() && mAudioRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
     RefPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeAudio));
@@ -1982,17 +1982,17 @@ MediaDecoderStateMachine::EnsureVideoDec
   AssertCurrentThreadInMonitor();
 
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%d",
              IsVideoDecoding(), mVideoRequestStatus);
 
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
-  if (mState >= DECODER_STATE_COMPLETED) {
+  if (mState >= DECODER_STATE_COMPLETED || mState == DECODER_STATE_DORMANT) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mState >= DECODER_STATE_DECODING_FIRSTFRAME);
 
   if (IsVideoDecoding() && mVideoRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
     RefPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeVideo));
@@ -2149,38 +2149,38 @@ MediaDecoderStateMachine::CallDecodeMeta
 
 nsresult MediaDecoderStateMachine::DecodeMetadata()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   DECODER_LOG("Decoding Media Headers");
 
-  mReader->PreReadMetadata();
-
-  if (mReader->IsWaitingMediaResources()) {
-    StartWaitForResources();
-    return NS_OK;
-  }
-
   nsresult res;
   MediaInfo info;
+  bool isAwaitingResources = false;
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-    res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags));
-  }
-
-  if (NS_SUCCEEDED(res)) {
-    if (mState == DECODER_STATE_DECODING_METADATA &&
-        mReader->IsWaitingMediaResources()) {
-      // change state to DECODER_STATE_WAIT_FOR_RESOURCES
+    mReader->PreReadMetadata();
+
+    if (mReader->IsWaitingMediaResources()) {
       StartWaitForResources();
-      // affect values only if ReadMetadata succeeds
       return NS_OK;
     }
+    res = mReader->ReadMetadata(&info, getter_Transfers(mMetadataTags));
+    isAwaitingResources = mReader->IsWaitingMediaResources();
+  }
+
+  if (NS_SUCCEEDED(res) &&
+      mState == DECODER_STATE_DECODING_METADATA &&
+      isAwaitingResources) {
+    // change state to DECODER_STATE_WAIT_FOR_RESOURCES
+    StartWaitForResources();
+    // affect values only if ReadMetadata succeeds
+    return NS_OK;
   }
 
   if (NS_SUCCEEDED(res)) {
     mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
   }
 
   mInfo = info;
 
@@ -2742,22 +2742,19 @@ nsresult MediaDecoderStateMachine::RunSt
       if (IsPlaying()) {
         StopPlayback();
       }
       StopAudioThread();
       FlushDecoding();
       // Now that those threads are stopped, there's no possibility of
       // mPendingWakeDecoder being needed again. Revoke it.
       mPendingWakeDecoder = nullptr;
-      {
-        ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
-        // Wait for the thread decoding, if any, to exit.
-        DecodeTaskQueue()->AwaitIdle();
-        mReader->ReleaseMediaResources();
-      }
+      DebugOnly<nsresult> rv = DecodeTaskQueue()->Dispatch(
+      NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources));
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
       mAudioRequestStatus = RequestStatus::Idle;
       mVideoRequestStatus = RequestStatus::Idle;
       return NS_OK;
     }
 
     case DECODER_STATE_WAIT_FOR_RESOURCES: {
       return NS_OK;
     }
--- 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
@@ -394,16 +394,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__);
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -250,16 +250,21 @@ pref("media.volume_scale", "1.0");
 
 // Timeout for wakelock release
 pref("media.wakelock_timeout", 2000);
 
 // Whether we should play videos opened in a "video document", i.e. videos
 // opened as top-level documents, as opposed to inside a media element.
 pref("media.play-stand-alone", true);
 
+#if defined(XP_WIN)
+pref("media.decoder.heuristic.dormant.enabled", true);
+pref("media.decoder.heuristic.dormant.timeout", 60000);
+#endif
+
 #ifdef MOZ_WMF
 pref("media.windows-media-foundation.enabled", true);
 pref("media.windows-media-foundation.use-dxva", true);
 #endif
 #ifdef MOZ_DIRECTSHOW
 pref("media.directshow.enabled", true);
 #endif
 #ifdef MOZ_FMP4
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -274,12 +274,16 @@ user_pref("browser.uitour.url", "http://
 user_pref("browser.search.highlightCount", 0);
 // Tell the search service we are running in the US.  This also has the desired
 // side-effect of preventing our geoip lookup.
 user_pref("browser.search.isUS", true);
 user_pref("browser.search.countryCode", "US");
 
 user_pref("media.eme.enabled", true);
 
+#if defined(XP_WIN)
+user_pref("media.decoder.heuristic.dormant.timeout", 0);
+#endif
+
 // Don't prompt about e10s
 user_pref("browser.displayedE10SPrompt.1", 5);
 // Don't use auto-enabled e10s
 user_pref("browser.tabs.remote.autostart.1", false);