Bug 1315510 - Automatically recreate VideoDecoderManager if the GPU process crashes. r=dvander
authorMatt Woodrow <mwoodrow@mozilla.com>
Tue, 08 Nov 2016 15:21:35 +1300
changeset 321732 95ab9f05b980662a420d6d664c1996c0dfb8e4c8
parent 321731 c6d5c06685a248fd8eaa01d695e4509392db9209
child 321733 d76e0d2be865f40a4bf86fddd1d5430f74b7b5bf
push id83661
push usermwoodrow@mozilla.com
push dateWed, 09 Nov 2016 02:49:51 +0000
treeherdermozilla-inbound@d76e0d2be865 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs1315510
milestone52.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 1315510 - Automatically recreate VideoDecoderManager if the GPU process crashes. r=dvander
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/media/ipc/RemoteVideoDecoder.cpp
dom/media/ipc/VideoDecoderChild.cpp
dom/media/ipc/VideoDecoderManagerChild.cpp
dom/media/ipc/VideoDecoderManagerChild.h
gfx/ipc/GPUProcessManager.cpp
gfx/ipc/GPUProcessManager.h
layout/build/nsLayoutStatics.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/Unused.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FlyWebPublishedServerIPC.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/PCrashReporterChild.h"
 #include "mozilla/dom/ProcessGlobal.h"
@@ -1176,34 +1177,37 @@ ContentChild::RecvGMPsChanged(nsTArray<G
 {
   GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
   return true;
 }
 
 bool
 ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
                                 Endpoint<PImageBridgeChild>&& aImageBridge,
-                                Endpoint<PVRManagerChild>&& aVRBridge)
+                                Endpoint<PVRManagerChild>&& aVRBridge,
+                                Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
 {
   if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) {
     return false;
   }
   if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) {
     return false;
   }
   if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) {
     return false;
   }
+  VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return true;
 }
 
 bool
 ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
                                   Endpoint<PImageBridgeChild>&& aImageBridge,
-                                  Endpoint<PVRManagerChild>&& aVRBridge)
+                                  Endpoint<PVRManagerChild>&& aVRBridge,
+                                  Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
 {
   nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
 
   // Zap all the old layer managers we have lying around.
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->InvalidateLayers();
     }
@@ -1221,16 +1225,18 @@ ContentChild::RecvReinitRendering(Endpoi
   }
 
   // Establish new PLayerTransactions.
   for (const auto& tabChild : tabs) {
     if (tabChild->LayersId()) {
       tabChild->ReinitRendering();
     }
   }
+
+  VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
   return true;
 }
 
 PBackgroundChild*
 ContentChild::AllocPBackgroundChild(Transport* aTransport,
                                     ProcessId aOtherProcess)
 {
   return BackgroundChild::Alloc(aTransport, aOtherProcess);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -164,23 +164,25 @@ public:
 
   bool
   RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override;
 
   bool
   RecvInitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
-    Endpoint<PVRManagerChild>&& aVRBridge) override;
+    Endpoint<PVRManagerChild>&& aVRBridge,
+    Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
 
   bool
   RecvReinitRendering(
     Endpoint<PCompositorBridgeChild>&& aCompositor,
     Endpoint<PImageBridgeChild>&& aImageBridge,
-    Endpoint<PVRManagerChild>&& aVRBridge) override;
+    Endpoint<PVRManagerChild>&& aVRBridge,
+    Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
 
   PProcessHangMonitorChild*
   AllocPProcessHangMonitorChild(Transport* aTransport,
                                 ProcessId aOtherProcess) override;
 
   virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
 
   PBackgroundChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1022,23 +1022,16 @@ ContentParent::RecvFindPlugins(const uin
                                nsresult* aRv,
                                nsTArray<PluginTag>* aPlugins,
                                uint32_t* aNewPluginEpoch)
 {
   *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
   return true;
 }
 
-bool
-ContentParent::RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* aEndpoint)
-{
-  GPUProcessManager::Get()->CreateContentVideoDecoderManager(OtherPid(), aEndpoint);
-  return true;
-}
-
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent,
                                   bool aFreshProcess)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
@@ -2222,28 +2215,31 @@ ContentParent::InitInternal(ProcessPrior
     // on demand.)
     bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
     if (useOffMainThreadCompositing) {
       GPUProcessManager* gpm = GPUProcessManager::Get();
 
       Endpoint<PCompositorBridgeChild> compositor;
       Endpoint<PImageBridgeChild> imageBridge;
       Endpoint<PVRManagerChild> vrBridge;
+      Endpoint<PVideoDecoderManagerChild> videoManager;
 
       DebugOnly<bool> opened = gpm->CreateContentBridges(
         OtherPid(),
         &compositor,
         &imageBridge,
-        &vrBridge);
+        &vrBridge,
+        &videoManager);
       MOZ_ASSERT(opened);
 
       Unused << SendInitRendering(
         Move(compositor),
         Move(imageBridge),
-        Move(vrBridge));
+        Move(vrBridge),
+        Move(videoManager));
 
       gpm->AddListener(this);
     }
   }
 
   if (gAppData) {
     // Sending all information to content process.
     Unused << SendAppInit();
@@ -2378,28 +2374,31 @@ ContentParent::RecvGetGfxVars(Infallible
 void
 ContentParent::OnCompositorUnexpectedShutdown()
 {
   GPUProcessManager* gpm = GPUProcessManager::Get();
 
   Endpoint<PCompositorBridgeChild> compositor;
   Endpoint<PImageBridgeChild> imageBridge;
   Endpoint<PVRManagerChild> vrBridge;
+  Endpoint<PVideoDecoderManagerChild> videoManager;
 
   DebugOnly<bool> opened = gpm->CreateContentBridges(
     OtherPid(),
     &compositor,
     &imageBridge,
-    &vrBridge);
+    &vrBridge,
+    &videoManager);
   MOZ_ASSERT(opened);
 
   Unused << SendReinitRendering(
     Move(compositor),
     Move(imageBridge),
-    Move(vrBridge));
+    Move(vrBridge),
+    Move(videoManager));
 }
 
 void
 ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
 {
   if (!mIPCOpen) {
     return;
   }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -257,18 +257,16 @@ public:
   virtual bool RecvGetBlocklistState(const uint32_t& aPluginId,
                                      uint32_t* aIsBlocklisted) override;
 
   virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
                                nsresult* aRv,
                                nsTArray<PluginTag>* aPlugins,
                                uint32_t* aNewPluginEpoch) override;
 
-  virtual bool RecvInitVideoDecoderManager(Endpoint<PVideoDecoderManagerChild>* endpoint) override;
-
   virtual bool RecvUngrabPointer(const uint32_t& aTime) override;
 
   virtual bool RecvRemovePermission(const IPC::Principal& aPrincipal,
                                     const nsCString& aPermissionType,
                                     nsresult* aRv) override;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -424,25 +424,27 @@ both:
     async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
                                      uint64_t aOuterWindowID);
 
 child:
     // Give the content process its endpoints to the compositor.
     async InitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> imageBridge,
-      Endpoint<PVRManagerChild> vr);
+      Endpoint<PVRManagerChild> vr,
+      Endpoint<PVideoDecoderManagerChild> video);
 
     // Re-create the rendering stack using the given endpoints. This is sent
     // after the compositor process has crashed. The new endpoints may be to a
     // newly launched GPU process, or the compositor thread of the UI process.
     async ReinitRendering(
       Endpoint<PCompositorBridgeChild> compositor,
       Endpoint<PImageBridgeChild> bridge,
-      Endpoint<PVRManagerChild> vr);
+      Endpoint<PVRManagerChild> vr,
+      Endpoint<PVideoDecoderManagerChild> video);
 
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox(MaybeFileDesc aBroker);
 
@@ -737,18 +739,16 @@ parent:
 
     async PJavaScript();
 
     async PRemoteSpellcheckEngine();
     async PDeviceStorageRequest(DeviceStorageParams params);
 
     sync PCrashReporter(NativeThreadId tid, uint32_t processType);
 
-    sync InitVideoDecoderManager() returns (Endpoint<PVideoDecoderManagerChild> endpoint);
-
     /**
      * Is this token compatible with the provided version?
      *
      * |version| The offered version to test
      * Returns |True| if the offered version is compatible
      */
     sync NSSU2FTokenIsCompatibleVersion(nsString version)
         returns (bool result);
--- a/dom/media/ipc/RemoteVideoDecoder.cpp
+++ b/dom/media/ipc/RemoteVideoDecoder.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "RemoteVideoDecoder.h"
 #include "VideoDecoderChild.h"
 #include "VideoDecoderManagerChild.h"
 #include "mozilla/layers/TextureClient.h"
 #include "base/thread.h"
 #include "MediaInfo.h"
+#include "MediaPrefs.h"
 #include "ImageContainer.h"
 
 namespace mozilla {
 namespace dom {
 
 using base::Thread;
 using namespace ipc;
 using namespace layers;
@@ -142,17 +143,18 @@ PlatformDecoderModule::ConversionRequire
 RemoteDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   return mWrapped->DecoderNeedsConversion(aConfig);
 }
 
 already_AddRefed<MediaDataDecoder>
 RemoteDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
-  if (!aParams.mKnowsCompositor ||
+  if (!MediaPrefs::PDMUseGPUDecoder() ||
+      !aParams.mKnowsCompositor ||
       aParams.mKnowsCompositor->GetTextureFactoryIdentifier().mParentProcessType != GeckoProcessType_GPU) {
     return nullptr;
   }
 
   MediaDataDecoderCallback* callback = aParams.mCallback;
   MOZ_ASSERT(callback->OnReaderTaskQueue());
   RefPtr<RemoteVideoDecoder> object = new RemoteVideoDecoder(callback);
 
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -98,34 +98,45 @@ VideoDecoderChild::RecvInitFailed(const 
   mInitPromise.Reject(aReason, __func__);
   return true;
 }
 
 void
 VideoDecoderChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   if (aWhy == AbnormalShutdown) {
-    if (mInitialized) {
-      mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
-    } else {
-      mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
-    }
+    // Defer reporting an error until we've recreated the manager so that
+    // it'll be safe for MediaFormatReader to recreate decoders
+    RefPtr<VideoDecoderChild> ref = this;
+    GetManager()->RunWhenRecreated(NS_NewRunnableFunction([=]() {
+      if (ref->mInitialized) {
+        ref->mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
+      } else {
+        ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
+      }
+    }));
   }
   mCanSend = false;
 }
 
 void
 VideoDecoderChild::InitIPDL(MediaDataDecoderCallback* aCallback,
                             const VideoInfo& aVideoInfo,
                             layers::KnowsCompositor* aKnowsCompositor)
 {
   RefPtr<VideoDecoderManagerChild> manager = VideoDecoderManagerChild::GetSingleton();
-  if (!manager) {
+  // If the manager isn't available, then don't initialize mIPDLSelfRef and leave
+  // us in an error state. We'll then immediately reject the promise when Init()
+  // is called and the caller can try again. Hopefully by then the new manager is
+  // ready, or we've notified the caller of it being no longer available.
+  // If not, then the cycle repeats until we're ready.
+  if (!manager || !manager->CanSend()) {
     return;
   }
+
   mIPDLSelfRef = this;
   mCallback = aCallback;
   mVideoInfo = aVideoInfo;
   mKnowsCompositor = aKnowsCompositor;
   if (manager->SendPVideoDecoderConstructor(this)) {
     mCanSend = true;
   }
 }
@@ -145,96 +156,99 @@ VideoDecoderChild::IPDLActorDestroyed()
 }
 
 // MediaDataDecoder methods
 
 RefPtr<MediaDataDecoder::InitPromise>
 VideoDecoderChild::Init()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier())) {
+
+  if (!mIPDLSelfRef) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
-      NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+      NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__);
+  }
+  // If we failed to send this, then we'll still resolve the Init promise
+  // as ActorDestroy handles it.
+  if (mCanSend) {
+    SendInit(mVideoInfo, mKnowsCompositor->GetTextureFactoryIdentifier());
   }
   return mInitPromise.Ensure(__func__);
 }
 
 void
 VideoDecoderChild::Input(MediaRawData* aSample)
 {
   AssertOnManagerThread();
   if (!mCanSend) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
     return;
   }
 
   // TODO: It would be nice to add an allocator method to
   // MediaDataDecoder so that the demuxer could write directly
   // into shmem rather than requiring a copy here.
   Shmem buffer;
   if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &buffer)) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+    mCallback->Error(NS_ERROR_DOM_MEDIA_DECODE_ERR);
     return;
   }
 
   memcpy(buffer.get<uint8_t>(), aSample->Data(), aSample->Size());
 
   MediaRawDataIPDL sample(MediaDataIPDL(aSample->mOffset,
                                         aSample->mTime,
                                         aSample->mTimecode,
                                         aSample->mDuration,
                                         aSample->mFrames,
                                         aSample->mKeyframe),
                           buffer);
-  if (!SendInput(sample)) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
-  }
+  SendInput(sample);
 }
 
 void
 VideoDecoderChild::Flush()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendFlush()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendFlush();
   }
 }
 
 void
 VideoDecoderChild::Drain()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendDrain()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendDrain();
   }
 }
 
 void
 VideoDecoderChild::Shutdown()
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendShutdown()) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendShutdown();
   }
   mInitialized = false;
 }
 
 bool
 VideoDecoderChild::IsHardwareAccelerated(nsACString& aFailureReason) const
 {
   aFailureReason = mHardwareAcceleratedReason;
   return mIsHardwareAccelerated;
 }
 
 void
 VideoDecoderChild::SetSeekThreshold(const media::TimeUnit& aTime)
 {
   AssertOnManagerThread();
-  if (!mCanSend || !SendSetSeekThreshold(aTime.ToMicroseconds())) {
-    mCallback->Error(NS_ERROR_DOM_MEDIA_FATAL_ERR);
+  if (mCanSend) {
+    SendSetSeekThreshold(aTime.ToMicroseconds());
   }
 }
 
 void
 VideoDecoderChild::AssertOnManagerThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mThread);
 }
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -8,109 +8,98 @@
 #include "mozilla/dom/ContentChild.h"
 #include "MediaPrefs.h"
 #include "nsThreadUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
+#include "base/task.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
 // Only modified on the main-thread
 StaticRefPtr<nsIThread> sVideoDecoderChildThread;
 StaticRefPtr<AbstractThread> sVideoDecoderChildAbstractThread;
 
 // Only accessed from sVideoDecoderChildThread
 static StaticRefPtr<VideoDecoderManagerChild> sDecoderManager;
+static UniquePtr<nsTArray<RefPtr<Runnable>>> sRecreateTasks;
 
 /* static */ void
-VideoDecoderManagerChild::Initialize()
+VideoDecoderManagerChild::InitializeThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  MediaPrefs::GetSingleton();
-
-#ifdef XP_WIN
-  if (!MediaPrefs::PDMUseGPUDecoder()) {
-    return;
-  }
-
-  // Can't run remote video decoding in the parent process.
-  if (!ContentChild::GetSingleton()) {
-    return;
-  }
-
   if (!sVideoDecoderChildThread) {
     RefPtr<nsIThread> childThread;
     nsresult rv = NS_NewNamedThread("VideoChild", getter_AddRefs(childThread));
     NS_ENSURE_SUCCESS_VOID(rv);
     sVideoDecoderChildThread = childThread;
 
     sVideoDecoderChildAbstractThread =
       AbstractThread::CreateXPCOMThreadWrapper(childThread, false);
+
+    sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
   }
-#else
-  return;
-#endif
+}
 
+/* static */ void
+VideoDecoderManagerChild::InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
+{
+  InitializeThread();
+  sVideoDecoderChildThread->Dispatch(NewRunnableFunction(&Open, Move(aVideoManager)), NS_DISPATCH_NORMAL);
 }
 
 /* static */ void
 VideoDecoderManagerChild::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sVideoDecoderChildThread) {
     sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([]() {
-      if (sDecoderManager) {
+      if (sDecoderManager && sDecoderManager->CanSend()) {
         sDecoderManager->Close();
         sDecoderManager = nullptr;
       }
     }), NS_DISPATCH_NORMAL);
 
     sVideoDecoderChildAbstractThread = nullptr;
     sVideoDecoderChildThread->Shutdown();
     sVideoDecoderChildThread = nullptr;
+
+    sRecreateTasks = nullptr;
   }
 }
 
+void
+VideoDecoderManagerChild::RunWhenRecreated(already_AddRefed<Runnable> aTask)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
+
+  // If we've already been recreated, then run the task immediately.
+  if (sDecoderManager && sDecoderManager != this && sDecoderManager->CanSend()) {
+    RefPtr<Runnable> task = aTask;
+    task->Run();
+  } else {
+    sRecreateTasks->AppendElement(aTask);
+  }
+}
+
+
 /* static */ VideoDecoderManagerChild*
 VideoDecoderManagerChild::GetSingleton()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
-
-  if (!sDecoderManager || !sDecoderManager->mCanSend) {
-    RefPtr<VideoDecoderManagerChild> manager;
-      
-    NS_DispatchToMainThread(NS_NewRunnableFunction([&]() {
-      Endpoint<PVideoDecoderManagerChild> endpoint;
-      if (!ContentChild::GetSingleton()->SendInitVideoDecoderManager(&endpoint)) {
-        return;
-      }
-
-      if (!endpoint.IsValid()) {
-        return;
-      }
-
-      manager = new VideoDecoderManagerChild();
-
-      RefPtr<Runnable> task = NewRunnableMethod<Endpoint<PVideoDecoderManagerChild>&&>(
-        manager, &VideoDecoderManagerChild::Open, Move(endpoint));
-      sVideoDecoderChildThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
-    }), NS_DISPATCH_SYNC);
-
-    sDecoderManager = manager;
-  }
   return sDecoderManager;
 }
 
 /* static */ nsIThread*
 VideoDecoderManagerChild::GetManagerThread()
 {
   return sVideoDecoderChildThread;
 }
@@ -133,43 +122,66 @@ VideoDecoderManagerChild::DeallocPVideoD
   VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
   child->IPDLActorDestroyed();
   return true;
 }
 
 void
 VideoDecoderManagerChild::Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint)
 {
-  if (!aEndpoint.Bind(this)) {
-    return;
+  // Make sure we always dispatch everything in sRecreateTasks, even if we
+  // fail since this is as close to being recreated as we will ever be.
+  sDecoderManager = nullptr;
+  if (aEndpoint.IsValid()) {
+    RefPtr<VideoDecoderManagerChild> manager = new VideoDecoderManagerChild();
+    if (aEndpoint.Bind(manager)) {
+      sDecoderManager = manager;
+      manager->InitIPDL();
+    }
   }
-  AddRef();
+  for (Runnable* task : *sRecreateTasks) {
+    task->Run();
+  }
+  sRecreateTasks->Clear();
+}
+
+void
+VideoDecoderManagerChild::InitIPDL()
+{
   mCanSend = true;
+  mIPDLSelfRef = this;
 }
 
 void
 VideoDecoderManagerChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mCanSend = false;
 }
 
 void
 VideoDecoderManagerChild::DeallocPVideoDecoderManagerChild()
 {
-  Release();
+  mIPDLSelfRef = nullptr;
+}
+
+bool
+VideoDecoderManagerChild::CanSend()
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == GetManagerThread());
+  return mCanSend;
 }
 
 bool
 VideoDecoderManagerChild::DeallocShmem(mozilla::ipc::Shmem& aShmem)
 {
   if (NS_GetCurrentThread() != sVideoDecoderChildThread) {
     RefPtr<VideoDecoderManagerChild> self = this;
     mozilla::ipc::Shmem shmem = aShmem;
     sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([self, shmem]() {
-      if (self->mCanSend) {
+      if (self->CanSend()) {
         mozilla::ipc::Shmem shmemCopy = shmem;
         self->DeallocShmem(shmemCopy);
       }
     }), NS_DISPATCH_NORMAL);
     return true;
   }
   return PVideoDecoderManagerChild::DeallocShmem(aShmem);
 }
@@ -202,17 +214,17 @@ VideoDecoderManagerChild::Readback(const
   // loop while it waits. This function can be called from JS and we
   // don't want that to happen.
   SynchronousTask task("Readback sync");
 
   RefPtr<VideoDecoderManagerChild> ref = this;
   SurfaceDescriptor sd;
   sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([&]() {
     AutoCompleteTask complete(&task);
-    if (ref->mCanSend) {
+    if (ref->CanSend()) {
       ref->SendReadback(aSD, &sd);
     }
   }), NS_DISPATCH_NORMAL);
 
   task.Wait();
 
   if (!IsSurfaceDescriptorValid(sd)) {
     return nullptr;
@@ -234,17 +246,17 @@ VideoDecoderManagerChild::Readback(const
 }
 
 void
 VideoDecoderManagerChild::DeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD)
 {
   RefPtr<VideoDecoderManagerChild> ref = this;
   SurfaceDescriptorGPUVideo sd = Move(aSD);
   sVideoDecoderChildThread->Dispatch(NS_NewRunnableFunction([ref, sd]() {
-    if (ref->mCanSend) {
+    if (ref->CanSend()) {
       ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
     }
   }), NS_DISPATCH_NORMAL);
 }
 
 void
 VideoDecoderManagerChild::HandleFatalError(const char* aName, const char* aMsg) const
 {
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -46,35 +46,50 @@ public:
     return PVideoDecoderManagerChild::AllocUnsafeShmem(aSize, aShmType, aShmem);
   }
 
   // Can be called from any thread, dispatches the request to the IPDL thread internally
   // and will be ignored if the IPDL actor has been destroyed.
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   // Main thread only
-  static void Initialize();
+  static void InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager);
   static void Shutdown();
 
+  // Run aTask (on the manager thread) when we next attempt to create a new manager
+  // (even if creation fails). Intended to be called from ActorDestroy when we get
+  // notified that the old manager is being destroyed.
+  // Can only be called from the manager thread.
+  void RunWhenRecreated(already_AddRefed<Runnable> aTask);
+
+  bool CanSend();
+
 protected:
+  void InitIPDL();
+
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVideoDecoderManagerChild() override;
 
   void HandleFatalError(const char* aName, const char* aMsg) const override;
 
   PVideoDecoderChild* AllocPVideoDecoderChild() override;
   bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
 
 private:
+  // Main thread only
+  static void InitializeThread();
+
   VideoDecoderManagerChild()
     : mCanSend(false)
   {}
   ~VideoDecoderManagerChild() {}
 
-  void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
+  static void Open(Endpoint<PVideoDecoderManagerChild>&& aEndpoint);
+
+  RefPtr<VideoDecoderManagerChild> mIPDLSelfRef;
 
   // Should only ever be accessed on the manager thread.
   bool mCanSend;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -24,16 +24,17 @@
 #include "nsContentUtils.h"
 #include "VRManagerChild.h"
 #include "VRManagerParent.h"
 #include "VsyncBridgeChild.h"
 #include "VsyncIOThreadHolder.h"
 #include "VsyncSource.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/VideoDecoderManagerParent.h"
+#include "MediaPrefs.h"
 
 namespace mozilla {
 namespace gfx {
 
 using namespace mozilla::layers;
 
 static StaticAutoPtr<GPUProcessManager> sSingleton;
 
@@ -539,24 +540,28 @@ GPUProcessManager::CreateRemoteSession(n
   return nullptr;
 #endif
 }
 
 bool
 GPUProcessManager::CreateContentBridges(base::ProcessId aOtherProcess,
                                         ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
                                         ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
-                                        ipc::Endpoint<PVRManagerChild>* aOutVRBridge)
+                                        ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
+                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager)
 {
   if (!CreateContentCompositorBridge(aOtherProcess, aOutCompositor) ||
       !CreateContentImageBridge(aOtherProcess, aOutImageBridge) ||
       !CreateContentVRManager(aOtherProcess, aOutVRBridge))
   {
     return false;
   }
+  // VideoDeocderManager is only supported in the GPU process, so we allow this to be
+  // fallible.
+  CreateContentVideoDecoderManager(aOtherProcess, aOutVideoManager);
   return true;
 }
 
 bool
 GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                                  ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint)
 {
   EnsureGPUReady();
@@ -662,41 +667,41 @@ GPUProcessManager::CreateContentVRManage
       return false;
     }
   }
 
   *aOutEndpoint = Move(childPipe);
   return true;
 }
 
-bool
+void
 GPUProcessManager::CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
                                                     ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndpoint)
 {
-  if (!mGPUChild) {
-    return false;
+  if (!mGPUChild || !MediaPrefs::PDMUseGPUDecoder()) {
+    return;
   }
 
   ipc::Endpoint<dom::PVideoDecoderManagerParent> parentPipe;
   ipc::Endpoint<dom::PVideoDecoderManagerChild> childPipe;
 
   nsresult rv = dom::PVideoDecoderManager::CreateEndpoints(
     mGPUChild->OtherPid(),
     aOtherProcess,
     &parentPipe,
     &childPipe);
   if (NS_FAILED(rv)) {
     gfxCriticalNote << "Could not create content video decoder: " << hexa(int(rv));
-    return false;
+    return;
   }
 
   mGPUChild->SendNewContentVideoDecoderManager(Move(parentPipe));
 
   *aOutEndpoint = Move(childPipe);
-  return true;
+  return;
 }
 
 already_AddRefed<IAPZCTreeManager>
 GPUProcessManager::GetAPZCTreeManagerForLayers(uint64_t aLayersId)
 {
   return CompositorBridgeParent::GetAPZCTreeManager(aLayersId);
 }
 
--- a/gfx/ipc/GPUProcessManager.h
+++ b/gfx/ipc/GPUProcessManager.h
@@ -87,19 +87,18 @@ public:
     bool aUseAPZ,
     bool aUseExternalSurfaceSize,
     const gfx::IntSize& aSurfaceSize);
 
   bool CreateContentBridges(
     base::ProcessId aOtherProcess,
     ipc::Endpoint<PCompositorBridgeChild>* aOutCompositor,
     ipc::Endpoint<PImageBridgeChild>* aOutImageBridge,
-    ipc::Endpoint<PVRManagerChild>* aOutVRBridge);
-  bool CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
-                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
+    ipc::Endpoint<PVRManagerChild>* aOutVRBridge,
+    ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutVideoManager);
 
   // This returns a reference to the APZCTreeManager to which
   // pan/zoom-related events can be sent.
   already_AddRefed<IAPZCTreeManager> GetAPZCTreeManagerForLayers(uint64_t aLayersId);
 
   // Maps the layer tree and process together so that aOwningPID is allowed
   // to access aLayersId across process.
   void MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId);
@@ -155,16 +154,18 @@ private:
   void OnXPCOMShutdown();
 
   bool CreateContentCompositorBridge(base::ProcessId aOtherProcess,
                                      ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint);
   bool CreateContentImageBridge(base::ProcessId aOtherProcess,
                                 ipc::Endpoint<PImageBridgeChild>* aOutEndpoint);
   bool CreateContentVRManager(base::ProcessId aOtherProcess,
                               ipc::Endpoint<PVRManagerChild>* aOutEndpoint);
+  void CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
+                                        ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndPoint);
 
   // Called from RemoteCompositorSession. We track remote sessions so we can
   // notify their owning widgets that the session must be restarted.
   void RegisterSession(RemoteCompositorSession* aSession);
   void UnregisterSession(RemoteCompositorSession* aSession);
 
 private:
   GPUProcessManager();
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -62,17 +62,16 @@
 #include "DOMStorageObserver.h"
 #include "CacheObserver.h"
 #include "DisplayItemClip.h"
 #include "ActiveLayerTracker.h"
 #include "CounterStyleManager.h"
 #include "FrameLayerBuilder.h"
 #include "AnimationCommon.h"
 #include "LayerAnimationInfo.h"
-#include "mozilla/dom/VideoDecoderManagerChild.h"
 
 #include "AudioChannelService.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/WebCryptoThreadPool.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
@@ -297,18 +296,16 @@ nsLayoutStatics::Initialize()
 
 #ifdef DEBUG
   nsStyleContext::Initialize();
   mozilla::LayerAnimationInfo::Initialize();
 #endif
 
   MediaDecoder::InitStatics();
 
-  VideoDecoderManagerChild::Initialize();
-
   PromiseDebugging::Init();
 
   mozilla::dom::devicestorage::DeviceStorageStatics::Initialize();
 
   mozilla::dom::WebCryptoThreadPool::Initialize();
 
   // NB: We initialize servo in nsAppRunner.cpp, because we need to do it after
   // creating the hidden DOM window to support some current stylo hacks. We