Bug 1182289 - Clean up dispatches in WebrtcGmpVideoEncoder/Decoder. r=jesup, a=abillings
☠☠ backed out by 8866651b440e ☠ ☠
authorByron Campen [:bwc] <docfaraday@gmail.com>
Tue, 14 Jul 2015 09:20:28 -0500
changeset 275455 49fc56608294d954943af191a17af21b7f0db8db
parent 275454 784d30d6aaa1e731fceb8fe6d3ee52f56900da8d
child 275456 ef641c2968c2d255a36823364b8f8ebf943aa370
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, abillings
bugs1182289
milestone40.0
Bug 1182289 - Clean up dispatches in WebrtcGmpVideoEncoder/Decoder. r=jesup, a=abillings
media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
--- a/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/GmpVideoCodec.cpp
@@ -3,16 +3,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebrtcGmpVideoCodec.h"
 #include "GmpVideoCodec.h"
 
 namespace mozilla {
 
 VideoEncoder* GmpVideoCodec::CreateEncoder() {
-  return static_cast<VideoEncoder*>(new WebrtcGmpVideoEncoder());
+  return static_cast<VideoEncoder*>(new WebrtcVideoEncoderProxy());
 }
 
 VideoDecoder* GmpVideoCodec::CreateDecoder() {
-  return static_cast<VideoDecoder*>(new WebrtcGmpVideoDecoder());
+  return static_cast<VideoDecoder*>(new WebrtcVideoDecoderProxy());
 }
 
 }
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.cpp
@@ -13,16 +13,17 @@
 #include "VideoConduit.h"
 #include "AudioConduit.h"
 #include "runnable_utils.h"
 
 #include "mozIGeckoMediaPluginService.h"
 #include "nsServiceManagerUtils.h"
 #include "GMPVideoDecoderProxy.h"
 #include "GMPVideoEncoderProxy.h"
+#include "MainThreadUtils.h"
 
 #include "gmp-video-host.h"
 #include "gmp-video-frame-i420.h"
 #include "gmp-video-frame-encoded.h"
 
 #include "webrtc/video_engine/include/vie_external_codec.h"
 
 namespace mozilla {
@@ -47,41 +48,71 @@ GetGMPLog()
 #endif
 #define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
 #define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
 #else
 #define LOGD(msg)
 #define LOG(level, msg)
 #endif
 
+WebrtcGmpPCHandleSetter::WebrtcGmpPCHandleSetter(const std::string& aPCHandle)
+{
+  if (!NS_IsMainThread()) {
+    MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
+    return;
+  }
+  MOZ_ASSERT(sCurrentHandle.empty());
+  sCurrentHandle = aPCHandle;
+}
+
+WebrtcGmpPCHandleSetter::~WebrtcGmpPCHandleSetter()
+{
+  if (!NS_IsMainThread()) {
+    MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
+    return;
+  }
+
+  sCurrentHandle.clear();
+}
+
+/* static */ std::string
+WebrtcGmpPCHandleSetter::GetCurrentHandle()
+{
+  if (!NS_IsMainThread()) {
+    MOZ_ASSERT(false, "WebrtcGmpPCHandleSetter can only be used on main");
+    return "";
+  }
+
+  return sCurrentHandle;
+}
+
+std::string WebrtcGmpPCHandleSetter::sCurrentHandle = "";
+
 // Encoder.
 WebrtcGmpVideoEncoder::WebrtcGmpVideoEncoder()
   : mGMP(nullptr)
+  , mInitting(false)
   , mHost(nullptr)
+  , mMaxPayloadSize(0)
+  , mCallbackMutex("WebrtcGmpVideoEncoder encoded callback mutex")
   , mCallback(nullptr)
   , mCachedPluginId(0)
-{}
-
-static void
-Encoder_Close_g(GMPVideoEncoderProxy* aGMP)
 {
-  aGMP->Close();
+#ifdef MOZILLA_INTERNAL_API
+  if (mPCHandle.empty()) {
+    mPCHandle = WebrtcGmpPCHandleSetter::GetCurrentHandle();
+  }
+  MOZ_ASSERT(!mPCHandle.empty());
+#endif
 }
 
 WebrtcGmpVideoEncoder::~WebrtcGmpVideoEncoder()
 {
-  // Note: we only use SyncRunnables to access mGMP
-  // Callbacks may occur at any time until we call Close (or receive
-  // Terminated()), so call Close here synchronously.
-  // Do NOT touch the refcount of 'this'!
-  if (mGMPThread && mGMP) {
-    mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                                            WrapRunnableNM(&Encoder_Close_g, mGMP));
-    mGMP = nullptr;
-  }
+  // We should not have been destroyed if we never closed our GMP
+  MOZ_ASSERT(!mGMP);
 }
 
 static int
 WebrtcFrameTypeToGmpFrameType(webrtc::VideoFrameType aIn,
                               GMPVideoFrameType *aOut)
 {
   MOZ_ASSERT(aOut);
   switch(aIn) {
@@ -146,189 +177,234 @@ WebrtcGmpVideoEncoder::InitEncode(const 
   MOZ_ASSERT(mMPS);
 
   if (!mGMPThread) {
     if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(getter_AddRefs(mGMPThread))))) {
       return WEBRTC_VIDEO_CODEC_ERROR;
     }
   }
 
-  nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
-  MOZ_ASSERT(currentThread != mGMPThread);
+  // Bug XXXXXX: transfer settings from codecSettings to codec.
+  GMPVideoCodec codecParams;
+  memset(&codecParams, 0, sizeof(codecParams));
+
+  codecParams.mGMPApiVersion = 33;
+  codecParams.mStartBitrate = aCodecSettings->startBitrate;
+  codecParams.mMinBitrate = aCodecSettings->minBitrate;
+  codecParams.mMaxBitrate = aCodecSettings->maxBitrate;
+  codecParams.mMaxFramerate = aCodecSettings->maxFramerate;
+  mMaxPayloadSize = aMaxPayloadSize;
+  if (aCodecSettings->codecSpecific.H264.packetizationMode == 1) {
+    mMaxPayloadSize = 0; // No limit.
+  }
 
-  nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
-  mGMPThread->Dispatch(WrapRunnable(this,
-                                    &WebrtcGmpVideoEncoder::InitEncode_g,
-                                    aCodecSettings,
-                                    aNumberOfCores,
-                                    aMaxPayloadSize,
-                                    initDone),
+  if (aCodecSettings->mode == webrtc::kScreensharing) {
+    codecParams.mMode = kGMPScreensharing;
+  } else {
+    codecParams.mMode = kGMPRealtimeVideo;
+  }
+
+  codecParams.mWidth = aCodecSettings->width;
+  codecParams.mHeight = aCodecSettings->height;
+
+  nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
+  mGMPThread->Dispatch(WrapRunnableNM(WebrtcGmpVideoEncoder::InitEncode_g,
+                                      nsRefPtr<WebrtcGmpVideoEncoder>(this),
+                                      codecParams,
+                                      aNumberOfCores,
+                                      aMaxPayloadSize,
+                                      initDone),
                        NS_DISPATCH_NORMAL);
 
-
-  while (!initDone->IsDone()) {
-    NS_ProcessNextEvent(currentThread, true);
-  }
-
-  return initDone->Result();
+  // Since init of the GMP encoder is a multi-step async dispatch (including
+  // dispatches to main), and since this function is invoked on main, there's
+  // no safe way to block until this init is done. If an error occurs, we'll
+  // handle it later.
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
+/* static */
 void
-WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
-                                    int32_t aNumberOfCores,
-                                    uint32_t aMaxPayloadSize,
-                                    InitDoneRunnable* aInitDone)
+WebrtcGmpVideoEncoder::InitEncode_g(
+    const nsRefPtr<WebrtcGmpVideoEncoder>& aThis,
+    const GMPVideoCodec& aCodecParams,
+    int32_t aNumberOfCores,
+    uint32_t aMaxPayloadSize,
+    const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
   UniquePtr<GetGMPVideoEncoderCallback> callback(
-    new InitDoneCallback(this, aInitDone, aCodecSettings, aMaxPayloadSize));
-  nsresult rv = mMPS->GetGMPVideoEncoder(&tags,
-                                         NS_LITERAL_CSTRING(""),
-                                         Move(callback));
+    new InitDoneCallback(aThis, aInitDone, aCodecParams, aMaxPayloadSize));
+  aThis->mInitting = true;
+  nsresult rv = aThis->mMPS->GetGMPVideoEncoder(&tags,
+                                                NS_LITERAL_CSTRING(""),
+                                                Move(callback));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    mMPS = nullptr;
-    mGMP = nullptr;
-    mGMPThread = nullptr;
-    mHost = nullptr;
-    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
-    return;
+    LOGD(("GMP Encode: GetGMPVideoEncoder failed"));
+    aThis->Close_g();
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
+                        "GMP Encode: GetGMPVideoEncoder failed");
   }
 }
 
 int32_t
 WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
                                    GMPVideoHost* aHost,
-                                   const webrtc::VideoCodec* aCodecSettings,
-                                   uint32_t aMaxPayloadSize)
+                                   std::string* aErrorOut)
 {
-  mGMP = aGMP;
-  mHost = aHost;
-  if (!mGMP || !mHost) {
+  if (!mInitting || !aGMP || !aHost) {
+    *aErrorOut = "GMP Encode: Either init was aborted, "
+                 "or init failed to supply either a GMP Encoder or GMP host.";
+    if (aGMP) {
+      // This could destroy us, since aGMP may be the last thing holding a ref
+      // Return immediately.
+      aGMP->Close();
+    }
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
-  // Bug XXXXXX: transfer settings from codecSettings to codec.
-  memset(&mCodecParams, 0, sizeof(mCodecParams));
+  mInitting = false;
 
-  mCodecParams.mGMPApiVersion = 33;
-  mCodecParams.mStartBitrate = aCodecSettings->startBitrate;
-  mCodecParams.mMinBitrate = aCodecSettings->minBitrate;
-  mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate;
-  mCodecParams.mMaxFramerate = aCodecSettings->maxFramerate;
-  mMaxPayloadSize = aMaxPayloadSize;
-  if (aCodecSettings->codecSpecific.H264.packetizationMode == 1) {
-    mMaxPayloadSize = 0; // No limit.
+  if (mGMP && mGMP != aGMP) {
+    Close_g();
   }
 
-  if (aCodecSettings->mode == webrtc::kScreensharing) {
-    mCodecParams.mMode = kGMPScreensharing;
-  } else {
-    mCodecParams.mMode = kGMPRealtimeVideo;
-  }
-
-  return InitEncoderForSize(aCodecSettings->width, aCodecSettings->height);
+  mGMP = aGMP;
+  mHost = aHost;
+  mCachedPluginId = mGMP->GetPluginId();
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
-WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short aHeight)
+WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
+                                   GMPVideoHost* aHost,
+                                   const GMPVideoCodec& aCodecParams,
+                                   uint32_t aMaxPayloadSize,
+                                   std::string* aErrorOut)
+{
+  int32_t r = GmpInitDone(aGMP, aHost, aErrorOut);
+  if (r != WEBRTC_VIDEO_CODEC_OK) {
+    // We might have been destroyed if GmpInitDone failed.
+    // Return immediately.
+    return r;
+  }
+  mCodecParams = aCodecParams;
+  return InitEncoderForSize(aCodecParams.mWidth,
+                            aCodecParams.mHeight,
+                            aErrorOut);
+}
+
+void
+WebrtcGmpVideoEncoder::Close_g()
+{
+  GMPVideoEncoderProxy* gmp(mGMP);
+  mGMP = nullptr;
+  mHost = nullptr;
+  mInitting = false;
+
+  if (gmp) {
+    // Do this last, since this could cause us to be destroyed
+    gmp->Close();
+  }
+}
+
+int32_t
+WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth,
+                                          unsigned short aHeight,
+                                          std::string* aErrorOut)
 {
   mCodecParams.mWidth = aWidth;
   mCodecParams.mHeight = aHeight;
   // Pass dummy codecSpecific data for now...
   nsTArray<uint8_t> codecSpecific;
 
   GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
   if (err != GMPNoErr) {
+    *aErrorOut = "GMP Encode: InitEncode failed";
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 
 int32_t
 WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
                               const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                               const std::vector<webrtc::VideoFrameType>* aFrameTypes)
 {
-  MOZ_ASSERT(mHost);
-  if (!mGMP) {
-    // destroyed via Terminate()
-    return WEBRTC_VIDEO_CODEC_ERROR;
-  }
-
   MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
-  if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
-      static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
-    LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
-          mCodecParams.mWidth, mCodecParams.mHeight, aInputImage.width(), aInputImage.height()));
-
-    nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
-    nsCOMPtr<nsIRunnable> task(
+  // Would be really nice to avoid this sync dispatch, but it would require a
+  // copy of the frame, since it doesn't appear to actually have a refcount.
+  mGMPThread->Dispatch(
       WrapRunnable(this,
-                   &WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange,
+                   &WebrtcGmpVideoEncoder::Encode_g,
                    &aInputImage,
-                   initDone));
-    mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+                   aCodecSpecificInfo,
+                   aFrameTypes),
+      NS_DISPATCH_SYNC);
 
-    nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
-    while (!initDone->IsDone()) {
-      NS_ProcessNextEvent(currentThread, true);
-    }
-
-    if (initDone->Result() != WEBRTC_VIDEO_CODEC_OK) {
-      return initDone->Result();
-    }
-  }
-
-  int32_t ret;
-  mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                WrapRunnableRet(this,
-                                &WebrtcGmpVideoEncoder::Encode_g,
-                                &aInputImage,
-                                aCodecSpecificInfo,
-                                aFrameTypes,
-                                &ret));
-
-  return ret;
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
 void
-WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
-                                                       InitDoneRunnable* aInitDone)
+WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(
+    uint32_t aWidth,
+    uint32_t aHeight,
+    const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
 {
-  mGMP->Close();
+  Close_g();
 
   UniquePtr<GetGMPVideoEncoderCallback> callback(
-    new InitDoneForResolutionChangeCallback(this, aInitDone,
-                                            aInputImage->width(),
-                                            aInputImage->height()));
+    new InitDoneForResolutionChangeCallback(this,
+                                            aInitDone,
+                                            aWidth,
+                                            aHeight));
 
   // OpenH264 codec (at least) can't handle dynamic input resolution changes
   // re-init the plugin when the resolution changes
   // XXX allow codec to indicate it doesn't need re-init!
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
+  mInitting = true;
   if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
                                                     NS_LITERAL_CSTRING(""),
                                                     Move(callback))))) {
-    mGMP = nullptr;
-    mHost = nullptr;
-    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
+                        "GMP Encode: GetGMPVideoEncoder failed");
   }
 }
 
 int32_t
 WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
                                 const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                                 const std::vector<webrtc::VideoFrameType>* aFrameTypes)
 {
+  if (!mGMP) {
+    // destroyed via Terminate(), failed to init, or just not initted yet
+    LOGD(("GMP Encode: not initted yet"));
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
   MOZ_ASSERT(mHost);
-  MOZ_ASSERT(mGMP);
+
+  if (static_cast<uint32_t>(aInputImage->width()) != mCodecParams.mWidth ||
+      static_cast<uint32_t>(aInputImage->height()) != mCodecParams.mHeight) {
+    LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
+          mCodecParams.mWidth, mCodecParams.mHeight, aInputImage->width(), aInputImage->height()));
+
+    nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
+    RegetEncoderForResolutionChange(aInputImage->width(),
+                                    aInputImage->height(),
+                                    initDone);
+    if (!mGMP) {
+      // We needed to go async to re-get the encoder. Bail.
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    }
+  }
 
   GMPVideoFrame* ftmp = nullptr;
   GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
   if (err != GMPNoErr) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   GMPUniquePtr<GMPVideoi420Frame> frame(static_cast<GMPVideoi420Frame*>(ftmp));
 
@@ -375,93 +451,98 @@ WebrtcGmpVideoEncoder::Encode_g(const we
   }
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
 WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback* aCallback)
 {
+  MutexAutoLock lock(mCallbackMutex);
   mCallback = aCallback;
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+/* static */ void
+WebrtcGmpVideoEncoder::ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder)
+{
+  aEncoder->Close_g();
+}
+
 int32_t
-WebrtcGmpVideoEncoder::Release()
+WebrtcGmpVideoEncoder::ReleaseGmp()
 {
   LOGD(("GMP Released:"));
-  // Note: we only use SyncRunnables to access mGMP
-  // Callbacks may occur at any time until we call Close (or receive
-  // Terminated()), so call Close here synchronously.
-  if (mGMPThread && mGMP) {
-    mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                                            WrapRunnableNM(&Encoder_Close_g, mGMP));
+  if (mGMPThread) {
+    mGMPThread->Dispatch(
+        WrapRunnableNM(&WebrtcGmpVideoEncoder::ReleaseGmp_g,
+                       nsRefPtr<WebrtcGmpVideoEncoder>(this)),
+        NS_DISPATCH_NORMAL);
   }
-  // Now safe to forget things
-  mMPS = nullptr;
-  mGMP = nullptr;
-  mHost = nullptr;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
 WebrtcGmpVideoEncoder::SetChannelParameters(uint32_t aPacketLoss, int aRTT)
 {
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
 WebrtcGmpVideoEncoder::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
 {
-  int32_t ret;
   MOZ_ASSERT(mGMPThread);
-  mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                WrapRunnableRet(this,
-                                &WebrtcGmpVideoEncoder::SetRates_g,
-                                aNewBitRate, aFrameRate,
-                                &ret));
+  MOZ_ASSERT(!NS_IsMainThread());
+  mGMPThread->Dispatch(WrapRunnableNM(&WebrtcGmpVideoEncoder::SetRates_g,
+                                      nsRefPtr<WebrtcGmpVideoEncoder>(this),
+                                      aNewBitRate,
+                                      aFrameRate),
+                       NS_DISPATCH_NORMAL);
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
-int32_t
-WebrtcGmpVideoEncoder::SetRates_g(uint32_t aNewBitRate, uint32_t aFrameRate)
+/* static */ int32_t
+WebrtcGmpVideoEncoder::SetRates_g(nsRefPtr<WebrtcGmpVideoEncoder> aThis,
+                                  uint32_t aNewBitRate,
+                                  uint32_t aFrameRate)
 {
-  if (!mGMP) {
+  if (!aThis->mGMP) {
     // destroyed via Terminate()
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
-  GMPErr err = mGMP->SetRates(aNewBitRate, aFrameRate);
+  GMPErr err = aThis->mGMP->SetRates(aNewBitRate, aFrameRate);
   if (err != GMPNoErr) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 // GMPVideoEncoderCallback virtual functions.
 void
 WebrtcGmpVideoEncoder::Terminated()
 {
   LOGD(("GMP Encoder Terminated: %p", (void *)this));
-  mCachedPluginId = PluginID();
 
-  // We need to drop our reference to this
   mGMP->Close();
   mGMP = nullptr;
+  mHost = nullptr;
+  mInitting = false;
   // Could now notify that it's dead
 }
 
 void
 WebrtcGmpVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
                                const nsTArray<uint8_t>& aCodecSpecificInfo)
 {
-  if (mCallback) { // paranoia
+  MutexAutoLock lock(mCallbackMutex);
+  if (mCallback) {
     webrtc::VideoFrameType ft;
     GmpFrameTypeToWebrtcFrameType(aEncodedFrame->FrameType(), &ft);
     uint32_t timestamp = (aEncodedFrame->TimeStamp() * 90ll + 999)/1000;
 
     LOGD(("GMP Encoded: %llu, type %d, len %d",
          aEncodedFrame->TimeStamp(),
          aEncodedFrame->BufferType(),
          aEncodedFrame->Size()));
@@ -561,135 +642,162 @@ WebrtcGmpVideoEncoder::Encoded(GMPVideoE
       }
 
       webrtc::EncodedImage unit(aEncodedFrame->Buffer(), size, size);
       unit._frameType = ft;
       unit._timeStamp = timestamp;
       unit._completeFrame = true;
 
       mCallback->Encoded(unit, nullptr, &fragmentation);
-
     }
   }
 }
 
 // Decoder.
 WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder() :
   mGMP(nullptr),
+  mInitting(false),
   mHost(nullptr),
+  mCallbackMutex("WebrtcGmpVideoDecoder decoded callback mutex"),
   mCallback(nullptr),
   mCachedPluginId(0),
-  mDecoderStatus(GMPNoErr){}
-
-static void
-Decoder_Close_g(GMPVideoDecoderProxy* aGMP)
+  mDecoderStatus(GMPNoErr)
 {
-  aGMP->Close();
+#ifdef MOZILLA_INTERNAL_API
+  if (mPCHandle.empty()) {
+    mPCHandle = WebrtcGmpPCHandleSetter::GetCurrentHandle();
+  }
+  MOZ_ASSERT(!mPCHandle.empty());
+#endif
 }
 
 WebrtcGmpVideoDecoder::~WebrtcGmpVideoDecoder()
 {
-  // Note: we only use SyncRunnables to access mGMP
-  // Callbacks may occur at any time until we call Close (or receive
-  // Terminated()), so call Close here synchronously.
-  // Do NOT touch the refcount of 'this'!
-  if (mGMPThread && mGMP) {
-    mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                                            WrapRunnableNM(&Decoder_Close_g, mGMP));
-    mGMP = nullptr;
-  }
+  // We should not have been destroyed if we never closed our GMP
+  MOZ_ASSERT(!mGMP);
 }
 
 int32_t
 WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
                                   int32_t aNumberOfCores)
 {
-  mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (!mMPS) {
+    mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  }
   MOZ_ASSERT(mMPS);
 
   if (!mGMPThread) {
     if (NS_WARN_IF(NS_FAILED(mMPS->GetThread(getter_AddRefs(mGMPThread))))) {
       return WEBRTC_VIDEO_CODEC_ERROR;
     }
   }
 
-  nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
-  mGMPThread->Dispatch(WrapRunnable(this,
-                                    &WebrtcGmpVideoDecoder::InitDecode_g,
-                                    aCodecSettings,
-                                    aNumberOfCores,
-                                    initDone.get()),
+  nsRefPtr<GmpInitDoneRunnable> initDone(new GmpInitDoneRunnable(mPCHandle));
+  mGMPThread->Dispatch(WrapRunnableNM(&WebrtcGmpVideoDecoder::InitDecode_g,
+                                      nsRefPtr<WebrtcGmpVideoDecoder>(this),
+                                      aCodecSettings,
+                                      aNumberOfCores,
+                                      initDone),
                        NS_DISPATCH_NORMAL);
 
-  nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
-  while (!initDone->IsDone()) {
-    NS_ProcessNextEvent(currentThread, true);
-  }
-
-  return initDone->Result();
+  return WEBRTC_VIDEO_CODEC_OK;
 }
 
-void
-WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
-                                    int32_t aNumberOfCores,
-                                    InitDoneRunnable* aInitDone)
+/* static */ void
+WebrtcGmpVideoDecoder::InitDecode_g(
+    const nsRefPtr<WebrtcGmpVideoDecoder>& aThis,
+    const webrtc::VideoCodec* aCodecSettings,
+    int32_t aNumberOfCores,
+    const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
 {
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_LITERAL_CSTRING("h264"));
   UniquePtr<GetGMPVideoDecoderCallback> callback(
-    new InitDoneCallback(this, aInitDone));
-  if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags,
-                                                    NS_LITERAL_CSTRING(""),
-                                                    Move(callback))))) {
-    mMPS = nullptr;
-    mGMP = nullptr;
-    mGMPThread = nullptr;
-    mHost = nullptr;
-    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
+    new InitDoneCallback(aThis, aInitDone));
+  aThis->mInitting = true;
+  nsresult rv = aThis->mMPS->GetGMPVideoDecoder(&tags,
+                                                NS_LITERAL_CSTRING(""),
+                                                Move(callback));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    LOGD(("GMP Decode: GetGMPVideoDecoder failed"));
+    aThis->Close_g();
+    aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR,
+                        "GMP Decode: GetGMPVideoDecoder failed.");
   }
 }
 
 int32_t
 WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
-                                   GMPVideoHost* aHost)
+                                   GMPVideoHost* aHost,
+                                   std::string* aErrorOut)
 {
-  mGMP = aGMP;
-  mHost = aHost;
-  mMPS = nullptr;
-
-  if (!mGMP || !mHost) {
+  if (!mInitting || !aGMP || !aHost) {
+    *aErrorOut = "GMP Decode: Either init was aborted, "
+                 "or init failed to supply either a GMP decoder or GMP host.";
+    if (aGMP) {
+      // This could destroy us, since aGMP may be the last thing holding a ref
+      // Return immediately.
+      aGMP->Close();
+    }
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
+  mInitting = false;
+
+  if (mGMP && mGMP != aGMP) {
+    Close_g();
+  }
+
+  mGMP = aGMP;
+  mHost = aHost;
+  mCachedPluginId = mGMP->GetPluginId();
   // Bug XXXXXX: transfer settings from codecSettings to codec.
   GMPVideoCodec codec;
   memset(&codec, 0, sizeof(codec));
   codec.mGMPApiVersion = 33;
 
   // XXX this is currently a hack
   //GMPVideoCodecUnion codecSpecific;
   //memset(&codecSpecific, 0, sizeof(codecSpecific));
   nsTArray<uint8_t> codecSpecific;
   nsresult rv = mGMP->InitDecode(codec, codecSpecific, this, 1);
   if (NS_FAILED(rv)) {
+    *aErrorOut = "GMP Decode: InitDecode failed";
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
+void
+WebrtcGmpVideoDecoder::Close_g()
+{
+  GMPVideoDecoderProxy* gmp(mGMP);
+  mGMP = nullptr;
+  mHost = nullptr;
+  mInitting = false;
+
+  if (gmp) {
+    // Do this last, since this could cause us to be destroyed
+    gmp->Close();
+  }
+}
+
 int32_t
 WebrtcGmpVideoDecoder::Decode(const webrtc::EncodedImage& aInputImage,
                               bool aMissingFrames,
                               const webrtc::RTPFragmentationHeader* aFragmentation,
                               const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                               int64_t aRenderTimeMs)
 {
   int32_t ret;
   MOZ_ASSERT(mGMPThread);
+  MOZ_ASSERT(!NS_IsMainThread());
+  // Would be really nice to avoid this sync dispatch, but it would require a
+  // copy of the frame, since it doesn't appear to actually have a refcount.
   mozilla::SyncRunnable::DispatchToThread(mGMPThread,
                 WrapRunnableRet(this,
                                 &WebrtcGmpVideoDecoder::Decode_g,
                                 aInputImage,
                                 aMissingFrames,
                                 aFragmentation,
                                 aCodecSpecificInfo,
                                 aRenderTimeMs,
@@ -700,21 +808,22 @@ WebrtcGmpVideoDecoder::Decode(const webr
 
 int32_t
 WebrtcGmpVideoDecoder::Decode_g(const webrtc::EncodedImage& aInputImage,
                                 bool aMissingFrames,
                                 const webrtc::RTPFragmentationHeader* aFragmentation,
                                 const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                                 int64_t aRenderTimeMs)
 {
-  MOZ_ASSERT(mHost);
   if (!mGMP) {
-    // destroyed via Terminate()
+    // destroyed via Terminate(), failed to init, or just not initted yet
+    LOGD(("GMP Decode: not initted yet"));
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
+  MOZ_ASSERT(mHost);
 
   if (!aInputImage._length) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
   GMPVideoFrame* ftmp = nullptr;
   GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
   if (err != GMPNoErr) {
@@ -767,62 +876,66 @@ WebrtcGmpVideoDecoder::Decode_g(const we
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
 WebrtcGmpVideoDecoder::RegisterDecodeCompleteCallback( webrtc::DecodedImageCallback* aCallback)
 {
+  MutexAutoLock lock(mCallbackMutex);
   mCallback = aCallback;
 
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 
-int32_t
-WebrtcGmpVideoDecoder::Release()
+/* static */ void
+WebrtcGmpVideoDecoder::ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoDecoder>& aDecoder)
 {
-  // Note: we only use SyncRunnables to access mGMP
-  // Callbacks may occur at any time until we call Close (or receive
-  // Terminated()), so call Close here synchronously.
-  if (mGMPThread && mGMP) {
-    mozilla::SyncRunnable::DispatchToThread(mGMPThread,
-                                            WrapRunnableNM(&Decoder_Close_g, mGMP));
+  aDecoder->Close_g();
+}
+
+int32_t
+WebrtcGmpVideoDecoder::ReleaseGmp()
+{
+  LOGD(("GMP Released:"));
+  if (mGMPThread) {
+    mGMPThread->Dispatch(
+        WrapRunnableNM(&WebrtcGmpVideoDecoder::ReleaseGmp_g,
+                       nsRefPtr<WebrtcGmpVideoDecoder>(this)),
+        NS_DISPATCH_NORMAL);
   }
-  // Now safe to forget things
-  mMPS = nullptr;
-  mGMP = nullptr;
-  mGMPThread = nullptr;
-  mHost = nullptr;
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 int32_t
 WebrtcGmpVideoDecoder::Reset()
 {
   // XXX ?
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
 void
 WebrtcGmpVideoDecoder::Terminated()
 {
   LOGD(("GMP Decoder Terminated: %p", (void *)this));
-  mCachedPluginId = PluginID();
 
   mGMP->Close();
   mGMP = nullptr;
+  mHost = nullptr;
+  mInitting = false;
   // Could now notify that it's dead
 }
 
 void
 WebrtcGmpVideoDecoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
 {
-  if (mCallback) { // paranioa
+  MutexAutoLock lock(mCallbackMutex);
+  if (mCallback) {
     webrtc::I420VideoFrame image;
     int ret = image.CreateFrame(aDecodedFrame->AllocatedSize(kGMPYPlane),
                                 aDecodedFrame->Buffer(kGMPYPlane),
                                 aDecodedFrame->AllocatedSize(kGMPUPlane),
                                 aDecodedFrame->Buffer(kGMPUPlane),
                                 aDecodedFrame->AllocatedSize(kGMPVPlane),
                                 aDecodedFrame->Buffer(kGMPVPlane),
                                 aDecodedFrame->Width(),
--- a/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcGmpVideoCodec.h
@@ -14,242 +14,359 @@
  */
 
 
 #ifndef WEBRTCGMPVIDEOCODEC_H_
 #define WEBRTCGMPVIDEOCODEC_H_
 
 #include <iostream>
 #include <queue>
+#include <string>
 
 #include "nsThreadUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 
 #include "mozIGeckoMediaPluginService.h"
 #include "MediaConduitInterface.h"
 #include "AudioConduit.h"
 #include "VideoConduit.h"
 #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h"
 
 #include "gmp-video-host.h"
 #include "GMPVideoDecoderProxy.h"
 #include "GMPVideoEncoderProxy.h"
 
+#include "PeerConnectionImpl.h"
+
 namespace mozilla {
 
-class WebrtcGmpVideoEncoder : public WebrtcVideoEncoder,
-                              public GMPVideoEncoderCallbackProxy
+// Class that allows code on the other side of webrtc.org to tell
+// WebrtcGmpVideoEncoder/Decoder what PC they should send errors to.
+// This is necessary because webrtc.org gives us no way to plumb the handle
+// through, nor does it give us any way to inform it of an error that will
+// make it back to the PC that cares (except for errors encountered
+// synchronously in functions like InitEncode/Decode, which will not happen
+// because GMP init is async).
+// Right now, this is used in MediaPipelineFactory.
+class WebrtcGmpPCHandleSetter
 {
-public:
-  WebrtcGmpVideoEncoder();
-  virtual ~WebrtcGmpVideoEncoder();
-
-  // Implement VideoEncoder interface.
-  virtual const uint64_t PluginID() override
-  {
-    return mGMP ? mGMP->GetPluginId() : mCachedPluginId;
-  }
-
-  virtual void Terminated() override;
-
-  virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
-                             int32_t aNumberOfCores,
-                             uint32_t aMaxPayloadSize) override;
-
-  virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
-                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
-                         const std::vector<webrtc::VideoFrameType>* aFrameTypes) override;
+  public:
+    explicit WebrtcGmpPCHandleSetter(const std::string& aPCHandle);
 
-  virtual int32_t RegisterEncodeCompleteCallback(
-    webrtc::EncodedImageCallback* aCallback) override;
-
-  virtual int32_t Release() override;
+    ~WebrtcGmpPCHandleSetter();
 
-  virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
-                                       int aRTT) override;
-
-  virtual int32_t SetRates(uint32_t aNewBitRate,
-                           uint32_t aFrameRate) override;
+    static std::string GetCurrentHandle();
 
-  // GMPVideoEncoderCallback virtual functions.
-  virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
-                       const nsTArray<uint8_t>& aCodecSpecificInfo) override;
-
-  virtual void Error(GMPErr aError) override {
-  }
+  private:
+    static std::string sCurrentHandle;
+};
 
-private:
-  class InitDoneRunnable : public nsRunnable
-  {
+class GmpInitDoneRunnable : public nsRunnable
+{
   public:
-    InitDoneRunnable()
-      : mInitDone(false),
-        mResult(WEBRTC_VIDEO_CODEC_OK),
-        mThread(do_GetCurrentThread())
+    explicit GmpInitDoneRunnable(const std::string& aPCHandle) :
+      mResult(WEBRTC_VIDEO_CODEC_OK),
+      mPCHandle(aPCHandle)
     {
     }
 
     NS_IMETHOD Run()
     {
-      MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
-      mInitDone = true;
+      if (mResult == WEBRTC_VIDEO_CODEC_OK) {
+        // Might be useful to notify the PeerConnection about successful init
+        // someday.
+        return NS_OK;
+      }
+
+      PeerConnectionWrapper wrapper(mPCHandle);
+      if (wrapper.impl()) {
+        wrapper.impl()->OnMediaError(mError);
+      }
       return NS_OK;
     }
 
-    void Dispatch(int32_t aResult)
+    void Dispatch(int32_t aResult, const std::string& aError = "")
     {
       mResult = aResult;
-      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
-    }
-
-    bool IsDone()
-    {
-      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
-      return mInitDone;
+      mError = aError;
+      nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+      if (mainThread) {
+        // For some reason, the compiler on CI is treating |this| as a const
+        // pointer, despite the fact that we're in a non-const function. And,
+        // interestingly enough, correcting this doesn't require a const_cast.
+        mainThread->Dispatch(this, NS_DISPATCH_NORMAL);
+      }
     }
 
     int32_t Result()
     {
       return mResult;
     }
 
   private:
-    bool mInitDone;
     int32_t mResult;
-    nsCOMPtr<nsIThread> mThread;
-  };
+    std::string mPCHandle;
+    std::string mError;
+};
+
+class WebrtcGmpVideoEncoder : public GMPVideoEncoderCallbackProxy
+{
+public:
+  WebrtcGmpVideoEncoder();
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoEncoder);
+
+  // Implement VideoEncoder interface, sort of.
+  // (We cannot use |Release|, since that's needed for nsRefPtr)
+  virtual const uint64_t PluginID()
+  {
+    return mCachedPluginId;
+  }
+
+  virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                             int32_t aNumberOfCores,
+                             uint32_t aMaxPayloadSize);
+
+  virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                         const std::vector<webrtc::VideoFrameType>* aFrameTypes);
+
+  virtual int32_t RegisterEncodeCompleteCallback(
+    webrtc::EncodedImageCallback* aCallback);
+
+  virtual int32_t ReleaseGmp();
 
-  void InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
-                    int32_t aNumberOfCores,
-                    uint32_t aMaxPayloadSize,
-                    InitDoneRunnable* aInitDone);
+  virtual int32_t SetChannelParameters(uint32_t aPacketLoss,
+                                       int aRTT);
+
+  virtual int32_t SetRates(uint32_t aNewBitRate,
+                           uint32_t aFrameRate);
+
+  // GMPVideoEncoderCallback virtual functions.
+  virtual void Terminated() override;
+
+  virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                       const nsTArray<uint8_t>& aCodecSpecificInfo) override;
+
+  virtual void Error(GMPErr aError) override {
+  }
+
+private:
+  virtual ~WebrtcGmpVideoEncoder();
+
+  static void InitEncode_g(const nsRefPtr<WebrtcGmpVideoEncoder>& aThis,
+                           const GMPVideoCodec& aCodecParams,
+                           int32_t aNumberOfCores,
+                           uint32_t aMaxPayloadSize,
+                           const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
   int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
-                      const webrtc::VideoCodec* aCodecSettings,
-                      uint32_t aMaxPayloadSize);
-  int32_t InitEncoderForSize(unsigned short aWidth, unsigned short aHeight);
+                      const GMPVideoCodec& aCodecParams,
+                      uint32_t aMaxPayloadSize,
+                      std::string* aErrorOut);
+  int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP,
+                      GMPVideoHost* aHost,
+                      std::string* aErrorOut);
+  int32_t InitEncoderForSize(unsigned short aWidth,
+                             unsigned short aHeight,
+                             std::string* aErrorOut);
+  static void ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder);
+  void Close_g();
 
   class InitDoneCallback : public GetGMPVideoEncoderCallback
   {
   public:
-    InitDoneCallback(WebrtcGmpVideoEncoder* aEncoder,
-                     InitDoneRunnable* aInitDone,
-                     const webrtc::VideoCodec* aCodecSettings,
+    InitDoneCallback(const nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder,
+                     const nsRefPtr<GmpInitDoneRunnable>& aInitDone,
+                     const GMPVideoCodec& aCodecParams,
                      uint32_t aMaxPayloadSize)
       : mEncoder(aEncoder),
         mInitDone(aInitDone),
-        mCodecSettings(aCodecSettings),
+        mCodecParams(aCodecParams),
         mMaxPayloadSize(aMaxPayloadSize)
     {
     }
 
     virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
     {
-      mEncoder->mGMP = aGMP;
-      mEncoder->mHost = aHost;
-      int32_t result;
-      if (aGMP || aHost) {
-        result = mEncoder->GmpInitDone(aGMP, aHost, mCodecSettings,
-                                       mMaxPayloadSize);
-      } else {
-        result = WEBRTC_VIDEO_CODEC_ERROR;
-      }
+      std::string errorOut;
+      int32_t result = mEncoder->GmpInitDone(aGMP,
+                                             aHost,
+                                             mCodecParams,
+                                             mMaxPayloadSize,
+                                             &errorOut);
 
-      mInitDone->Dispatch(result);
+      mInitDone->Dispatch(result, errorOut);
     }
 
   private:
-    WebrtcGmpVideoEncoder* mEncoder;
-    nsRefPtr<InitDoneRunnable> mInitDone;
-    const webrtc::VideoCodec* mCodecSettings;
+    nsRefPtr<WebrtcGmpVideoEncoder> mEncoder;
+    nsRefPtr<GmpInitDoneRunnable> mInitDone;
+    GMPVideoCodec mCodecParams;
     uint32_t mMaxPayloadSize;
   };
 
   int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
                    const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                    const std::vector<webrtc::VideoFrameType>* aFrameTypes);
-  void RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
-                                       InitDoneRunnable* aInitDone);
+  void RegetEncoderForResolutionChange(
+      uint32_t aWidth,
+      uint32_t aHeight,
+      const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
 
   class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
   {
   public:
-    InitDoneForResolutionChangeCallback(WebrtcGmpVideoEncoder* aEncoder,
-                                        InitDoneRunnable* aInitDone,
-                                        uint32_t aWidth,
-                                        uint32_t aHeight)
+    InitDoneForResolutionChangeCallback(
+        const nsRefPtr<WebrtcGmpVideoEncoder>& aEncoder,
+        const nsRefPtr<GmpInitDoneRunnable>& aInitDone,
+        uint32_t aWidth,
+        uint32_t aHeight)
       : mEncoder(aEncoder),
         mInitDone(aInitDone),
         mWidth(aWidth),
         mHeight(aHeight)
     {
     }
 
     virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
     {
-      mEncoder->mGMP = aGMP;
-      mEncoder->mHost = aHost;
-      int32_t result;
-      if (aGMP && aHost) {
-        result = mEncoder->InitEncoderForSize(mWidth, mHeight);
-      } else {
-        result = WEBRTC_VIDEO_CODEC_ERROR;
+      std::string errorOut;
+      int32_t result = mEncoder->GmpInitDone(aGMP, aHost, &errorOut);
+      if (result != WEBRTC_VIDEO_CODEC_OK) {
+        mInitDone->Dispatch(result, errorOut);
+        return;
       }
 
-      mInitDone->Dispatch(result);
+      result = mEncoder->InitEncoderForSize(mWidth, mHeight, &errorOut);
+      mInitDone->Dispatch(result, errorOut);
     }
 
   private:
-    WebrtcGmpVideoEncoder* mEncoder;
-    nsRefPtr<InitDoneRunnable> mInitDone;
+    nsRefPtr<WebrtcGmpVideoEncoder> mEncoder;
+    nsRefPtr<GmpInitDoneRunnable> mInitDone;
     uint32_t mWidth;
     uint32_t mHeight;
   };
 
-  virtual int32_t SetRates_g(uint32_t aNewBitRate,
+  static int32_t SetRates_g(nsRefPtr<WebrtcGmpVideoEncoder> aThis,
+                             uint32_t aNewBitRate,
                              uint32_t aFrameRate);
 
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   nsCOMPtr<nsIThread> mGMPThread;
   GMPVideoEncoderProxy* mGMP;
+  // Used to handle a race where Release() is called while init is in progress
+  bool mInitting;
   GMPVideoHost* mHost;
   GMPVideoCodec mCodecParams;
   uint32_t mMaxPayloadSize;
+  // Protects mCallback
+  Mutex mCallbackMutex;
   webrtc::EncodedImageCallback* mCallback;
   uint64_t mCachedPluginId;
+  std::string mPCHandle;
 };
 
 
-class WebrtcGmpVideoDecoder : public WebrtcVideoDecoder,
-                              public GMPVideoDecoderCallbackProxy
+// Basically a strong ref to a WebrtcGmpVideoEncoder, that also translates
+// from Release() to WebrtcGmpVideoEncoder::ReleaseGmp(), since we need
+// WebrtcGmpVideoEncoder::Release() for managing the refcount.
+// The webrtc.org code gets one of these, so it doesn't unilaterally delete
+// the "real" encoder.
+class WebrtcVideoEncoderProxy : public WebrtcVideoEncoder
+{
+  public:
+    WebrtcVideoEncoderProxy() :
+      mEncoderImpl(new WebrtcGmpVideoEncoder)
+    {}
+
+    virtual ~WebrtcVideoEncoderProxy()
+    {
+      RegisterEncodeCompleteCallback(nullptr);
+    }
+
+    const uint64_t PluginID() override
+    {
+      return mEncoderImpl->PluginID();
+    }
+
+    int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                       int32_t aNumberOfCores,
+                       uint32_t aMaxPayloadSize) override
+    {
+      return mEncoderImpl->InitEncode(aCodecSettings,
+                                      aNumberOfCores,
+                                      aMaxPayloadSize);
+    }
+
+    int32_t Encode(
+        const webrtc::I420VideoFrame& aInputImage,
+        const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+        const std::vector<webrtc::VideoFrameType>* aFrameTypes) override
+    {
+      return mEncoderImpl->Encode(aInputImage,
+                                  aCodecSpecificInfo,
+                                  aFrameTypes);
+    }
+
+    int32_t RegisterEncodeCompleteCallback(
+      webrtc::EncodedImageCallback* aCallback) override
+    {
+      return mEncoderImpl->RegisterEncodeCompleteCallback(aCallback);
+    }
+
+    int32_t Release() override
+    {
+      return mEncoderImpl->ReleaseGmp();
+    }
+
+    int32_t SetChannelParameters(uint32_t aPacketLoss,
+                                 int aRTT) override
+    {
+      return mEncoderImpl->SetChannelParameters(aPacketLoss, aRTT);
+    }
+
+    int32_t SetRates(uint32_t aNewBitRate,
+                     uint32_t aFrameRate) override
+    {
+      return mEncoderImpl->SetRates(aNewBitRate, aFrameRate);
+    }
+
+  private:
+    nsRefPtr<WebrtcGmpVideoEncoder> mEncoderImpl;
+};
+
+class WebrtcGmpVideoDecoder : public GMPVideoDecoderCallbackProxy
 {
 public:
   WebrtcGmpVideoDecoder();
-  virtual ~WebrtcGmpVideoDecoder();
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcGmpVideoDecoder);
 
-  // Implement VideoDecoder interface.
-  virtual const uint64_t PluginID() override
+  // Implement VideoEncoder interface, sort of.
+  // (We cannot use |Release|, since that's needed for nsRefPtr)
+  virtual const uint64_t PluginID()
   {
-    return mGMP ? mGMP->GetPluginId() : mCachedPluginId;
+    return mCachedPluginId;
   }
 
-  virtual void Terminated() override;
-
   virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
-                             int32_t aNumberOfCores) override;
+                             int32_t aNumberOfCores);
   virtual int32_t Decode(const webrtc::EncodedImage& aInputImage,
                          bool aMissingFrames,
                          const webrtc::RTPFragmentationHeader* aFragmentation,
-                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo = nullptr,
-                         int64_t aRenderTimeMs = -1) override;
-  virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback) override;
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                         int64_t aRenderTimeMs);
+  virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback);
 
-  virtual int32_t Release() override;
+  virtual int32_t ReleaseGmp();
 
-  virtual int32_t Reset() override;
+  virtual int32_t Reset();
+
+  // GMPVideoDecoderCallbackProxy
+  virtual void Terminated() override;
 
   virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
 
   virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override {
     MOZ_CRASH();
   }
 
   virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {
@@ -265,93 +382,129 @@ public:
   virtual void ResetComplete() override {
   }
 
   virtual void Error(GMPErr aError) override {
      mDecoderStatus = aError;
   }
 
 private:
-  class InitDoneRunnable : public nsRunnable
-  {
-  public:
-    InitDoneRunnable()
-      : mInitDone(false),
-        mResult(WEBRTC_VIDEO_CODEC_OK),
-        mThread(do_GetCurrentThread())
-    {
-    }
-
-    NS_IMETHOD Run()
-    {
-      MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
-      mInitDone = true;
-      return NS_OK;
-    }
+  virtual ~WebrtcGmpVideoDecoder();
 
-    void Dispatch(int32_t aResult)
-    {
-      mResult = aResult;
-      mThread->Dispatch(this, NS_DISPATCH_NORMAL);
-    }
-
-    bool IsDone()
-    {
-      MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
-      return mInitDone;
-    }
-
-    int32_t Result()
-    {
-      return mResult;
-    }
-
-  private:
-    bool mInitDone;
-    int32_t mResult;
-    nsCOMPtr<nsIThread> mThread;
-  };
-
-  void InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
-                    int32_t aNumberOfCores,
-                    InitDoneRunnable* aInitDone);
-  int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
+  static void InitDecode_g(
+      const nsRefPtr<WebrtcGmpVideoDecoder>& aThis,
+      const webrtc::VideoCodec* aCodecSettings,
+      int32_t aNumberOfCores,
+      const nsRefPtr<GmpInitDoneRunnable>& aInitDone);
+  int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP,
+                      GMPVideoHost* aHost,
+                      std::string* aErrorOut);
+  static void ReleaseGmp_g(nsRefPtr<WebrtcGmpVideoDecoder>& aDecoder);
+  void Close_g();
 
   class InitDoneCallback : public GetGMPVideoDecoderCallback
   {
   public:
     explicit InitDoneCallback(WebrtcGmpVideoDecoder* aDecoder,
-                              InitDoneRunnable* aInitDone)
+                              const nsRefPtr<GmpInitDoneRunnable>& aInitDone)
       : mDecoder(aDecoder),
         mInitDone(aInitDone)
     {
     }
 
     virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
     {
-      int32_t result = mDecoder->GmpInitDone(aGMP, aHost);
+      std::string errorOut;
+      int32_t result = mDecoder->GmpInitDone(aGMP, aHost, &errorOut);
 
-      mInitDone->Dispatch(result);
+      mInitDone->Dispatch(result, errorOut);
     }
 
   private:
     WebrtcGmpVideoDecoder* mDecoder;
-    nsRefPtr<InitDoneRunnable> mInitDone;
-};
+    nsRefPtr<GmpInitDoneRunnable> mInitDone;
+  };
 
   virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
                            bool aMissingFrames,
                            const webrtc::RTPFragmentationHeader* aFragmentation,
                            const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                            int64_t aRenderTimeMs);
 
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   nsCOMPtr<nsIThread> mGMPThread;
   GMPVideoDecoderProxy* mGMP; // Addref is held for us
+  // Used to handle a race where Release() is called while init is in progress
+  bool mInitting;
   GMPVideoHost* mHost;
+  // Protects mCallback
+  Mutex mCallbackMutex;
   webrtc::DecodedImageCallback* mCallback;
-  uint64_t mCachedPluginId;
+  Atomic<uint64_t> mCachedPluginId;
   GMPErr mDecoderStatus;
+  std::string mPCHandle;
+};
+
+// Basically a strong ref to a WebrtcGmpVideoDecoder, that also translates
+// from Release() to WebrtcGmpVideoDecoder::ReleaseGmp(), since we need
+// WebrtcGmpVideoDecoder::Release() for managing the refcount.
+// The webrtc.org code gets one of these, so it doesn't unilaterally delete
+// the "real" encoder.
+class WebrtcVideoDecoderProxy : public WebrtcVideoDecoder
+{
+  public:
+    WebrtcVideoDecoderProxy() :
+      mDecoderImpl(new WebrtcGmpVideoDecoder)
+    {}
+
+    virtual ~WebrtcVideoDecoderProxy()
+    {
+      RegisterDecodeCompleteCallback(nullptr);
+    }
+
+    const uint64_t PluginID() override
+    {
+      return mDecoderImpl->PluginID();
+    }
+
+    int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
+                       int32_t aNumberOfCores) override
+    {
+      return mDecoderImpl->InitDecode(aCodecSettings, aNumberOfCores);
+    }
+
+    int32_t Decode(
+        const webrtc::EncodedImage& aInputImage,
+        bool aMissingFrames,
+        const webrtc::RTPFragmentationHeader* aFragmentation,
+        const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+        int64_t aRenderTimeMs) override
+    {
+      return mDecoderImpl->Decode(aInputImage,
+                                  aMissingFrames,
+                                  aFragmentation,
+                                  aCodecSpecificInfo,
+                                  aRenderTimeMs);
+    }
+
+    int32_t RegisterDecodeCompleteCallback(
+      webrtc::DecodedImageCallback* aCallback) override
+    {
+      return mDecoderImpl->RegisterDecodeCompleteCallback(aCallback);
+    }
+
+    int32_t Release() override
+    {
+      return mDecoderImpl->ReleaseGmp();
+    }
+
+    int32_t Reset() override
+    {
+      return mDecoderImpl->Reset();
+    }
+
+  private:
+    nsRefPtr<WebrtcGmpVideoDecoder> mDecoderImpl;
 };
 
 }
 
 #endif
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -35,16 +35,20 @@
 #ifdef MOZ_WEBRTC_MEDIACODEC
 #include "MediaCodecVideoCodec.h"
 #endif
 
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/Preferences.h"
 #endif
 
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+#include "WebrtcGmpVideoCodec.h"
+#endif
+
 #include <stdlib.h>
 
 namespace mozilla {
 
 MOZ_MTLOG_MODULE("MediaPipelineFactory")
 
 static nsresult
 JsepCodecDescToCodecConfig(const JsepCodecDescription& aCodec,
@@ -310,16 +314,24 @@ MediaPipelineFactory::GetTransportParame
   return NS_OK;
 }
 
 nsresult
 MediaPipelineFactory::CreateOrUpdateMediaPipeline(
     const JsepTrackPair& aTrackPair,
     const JsepTrack& aTrack)
 {
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+  // The GMP code is all the way on the other side of webrtc.org, and it is not
+  // feasible to plumb this information all the way through. So, we set it (for
+  // the duration of this call) in a global variable. This allows the GMP code
+  // to report errors to the PC.
+  WebrtcGmpPCHandleSetter setter(mPC->GetHandle());
+#endif
+
   MOZ_ASSERT(aTrackPair.mRtpTransport);
 
   bool receiving =
       aTrack.GetDirection() == JsepTrack::Direction::kJsepTrackReceiving;
 
   size_t level;
   RefPtr<TransportFlow> rtpFlow;
   RefPtr<TransportFlow> rtcpFlow;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1956,16 +1956,23 @@ PeerConnectionImpl::GetStreamId(const DO
   nsString wideStreamId;
   aStream.GetId(wideStreamId);
   return NS_ConvertUTF16toUTF8(wideStreamId).get();
 #else
   return aStream.GetId();
 #endif
 }
 
+void
+PeerConnectionImpl::OnMediaError(const std::string& aError)
+{
+  CSFLogError(logTag, "Encountered media error! %s", aError.c_str());
+  // TODO: Let content know about this somehow.
+}
+
 nsresult
 PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
                              const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)
 {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (!aStreams.Length()) {
     CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -600,16 +600,18 @@ public:
   nsresult GetRemoteTrackId(const std::string streamId,
                             TrackID numericTrackId,
                             std::string* trackId) const;
 #endif
 
   static std::string GetStreamId(const DOMMediaStream& aStream);
   static std::string GetTrackId(const dom::MediaStreamTrack& track);
 
+  void OnMediaError(const std::string& aError);
+
 private:
   virtual ~PeerConnectionImpl();
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
   NS_IMETHODIMP Initialize(PeerConnectionObserver& aObserver,
                            nsGlobalWindow* aWindow,
                            const IceConfiguration* aConfiguration,
                            const RTCConfiguration* aRTCConfiguration,