Bug 957928: Gecko Media Plugins implementation. No consumers in Gecko yet. r=bent
☠☠ backed out by 661eaeca6dea ☠ ☠
authorJosh Aas <joshmoz@gmail.com>
Sat, 17 May 2014 18:53:03 -0500
changeset 203992 a4b51aff4b3cc743741c36856fcb81a0f193f774
parent 203991 f06a7deabcb96a22b26be7bf8d6474d61d1efaee
child 203993 661eaeca6deab724a754e0363e3dac150cd01788
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs957928
milestone32.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 957928: Gecko Media Plugins implementation. No consumers in Gecko yet. r=bent
content/base/public/nsContentCID.h
content/media/gmp/GMPChild.cpp
content/media/gmp/GMPChild.h
content/media/gmp/GMPMessageUtils.h
content/media/gmp/GMPParent.cpp
content/media/gmp/GMPParent.h
content/media/gmp/GMPPlatform.cpp
content/media/gmp/GMPPlatform.h
content/media/gmp/GMPProcessChild.cpp
content/media/gmp/GMPProcessChild.h
content/media/gmp/GMPProcessParent.cpp
content/media/gmp/GMPProcessParent.h
content/media/gmp/GMPService.cpp
content/media/gmp/GMPService.h
content/media/gmp/GMPSharedMemManager.h
content/media/gmp/GMPTypes.ipdlh
content/media/gmp/GMPVideoDecoderChild.cpp
content/media/gmp/GMPVideoDecoderChild.h
content/media/gmp/GMPVideoDecoderParent.cpp
content/media/gmp/GMPVideoDecoderParent.h
content/media/gmp/GMPVideoEncodedFrameImpl.cpp
content/media/gmp/GMPVideoEncodedFrameImpl.h
content/media/gmp/GMPVideoEncoderChild.cpp
content/media/gmp/GMPVideoEncoderChild.h
content/media/gmp/GMPVideoEncoderParent.cpp
content/media/gmp/GMPVideoEncoderParent.h
content/media/gmp/GMPVideoHost.cpp
content/media/gmp/GMPVideoHost.h
content/media/gmp/GMPVideoPlaneImpl.cpp
content/media/gmp/GMPVideoPlaneImpl.h
content/media/gmp/GMPVideoi420FrameImpl.cpp
content/media/gmp/GMPVideoi420FrameImpl.h
content/media/gmp/PGMP.ipdl
content/media/gmp/PGMPVideoDecoder.ipdl
content/media/gmp/PGMPVideoEncoder.ipdl
content/media/gmp/README.txt
content/media/gmp/gmp-api/gmp-entrypoints.h
content/media/gmp/gmp-api/gmp-errors.h
content/media/gmp/gmp-api/gmp-platform.h
content/media/gmp/gmp-api/gmp-video-codec.h
content/media/gmp/gmp-api/gmp-video-decode.h
content/media/gmp/gmp-api/gmp-video-encode.h
content/media/gmp/gmp-api/gmp-video-errors.h
content/media/gmp/gmp-api/gmp-video-frame-encoded.h
content/media/gmp/gmp-api/gmp-video-frame-i420.h
content/media/gmp/gmp-api/gmp-video-frame.h
content/media/gmp/gmp-api/gmp-video-host.h
content/media/gmp/gmp-api/gmp-video-plane.h
content/media/gmp/moz.build
content/media/gmp/mozIGeckoMediaPluginService.idl
content/media/moz.build
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
layout/build/nsLayoutModule.cpp
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsEmbedFunctions.cpp
xpcom/build/nsXPComInit.cpp
xpcom/build/nsXULAppAPI.h
xpcom/system/nsIXULRuntime.idl
--- a/content/base/public/nsContentCID.h
+++ b/content/base/public/nsContentCID.h
@@ -170,9 +170,13 @@
 // {f96f5ec9-755b-447e-b1f3-717d1a84bb41}
 #define NS_PLUGINDOCUMENT_CID \
 { 0xf96f5ec9, 0x755b, 0x447e, { 0xb1, 0xf3, 0x71, 0x7d, 0x1a, 0x84, 0xbb, 0x41 } }
 
 // {08c6cc8b-cfb0-421d-b1f7-683ff2989681}
 #define THIRDPARTYUTIL_CID \
  {0x08c6cc8b, 0xcfb0, 0x421d, {0xb1, 0xf7, 0x68, 0x3f, 0xf2, 0x98, 0x96, 0x81}}
 
+// {7B121F7E-EBE4-43AB-9410-DC9087A1DBA6}
+#define GECKO_MEDIA_PLUGIN_SERVICE_CID \
+ {0x7B121F7E, 0xEBE4, 0x43AB, {0x94, 0x10, 0xDC, 0x90, 0x87, 0xA1, 0xDB, 0xA6}}
+
 #endif /* nsContentCID_h__ */
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPChild.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPChild.h"
+#include "GMPVideoDecoderChild.h"
+#include "GMPVideoEncoderChild.h"
+#include "GMPVideoHost.h"
+#include "nsIFile.h"
+#include "nsXULAppAPI.h"
+#include <stdlib.h>
+#include "gmp-video-decode.h"
+#include "gmp-video-encode.h"
+#include "GMPPlatform.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPChild::GMPChild()
+  : mLib(nullptr)
+  , mGetAPIFunc(nullptr)
+  , mGMPMessageLoop(MessageLoop::current())
+{
+}
+
+GMPChild::~GMPChild()
+{
+}
+
+bool
+GMPChild::Init(const std::string& aPluginPath,
+               base::ProcessHandle aParentProcessHandle,
+               MessageLoop* aIOLoop,
+               IPC::Channel* aChannel)
+{
+  return LoadPluginLibrary(aPluginPath) &&
+         Open(aChannel, aParentProcessHandle, aIOLoop);
+}
+
+bool
+GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
+{
+  nsDependentCString pluginPath(aPluginPath.c_str());
+
+  nsCOMPtr<nsIFile> libFile;
+  nsresult rv = NS_NewNativeLocalFile(pluginPath, true, getter_AddRefs(libFile));
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  nsAutoString leafName;
+  if (NS_FAILED(libFile->GetLeafName(leafName))) {
+    return false;
+  }
+  nsAutoString baseName(Substring(leafName, 4, leafName.Length() - 1));
+
+#if defined(XP_MACOSX)
+  nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
+#elif defined(OS_POSIX)
+  nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".so");
+#elif defined(XP_WIN)
+  nsAutoString binaryName =                            baseName + NS_LITERAL_STRING(".dll");
+#else
+#error not defined
+#endif
+  libFile->AppendRelativePath(binaryName);
+
+  nsAutoCString nativePath;
+  libFile->GetNativePath(nativePath);
+  mLib = PR_LoadLibrary(nativePath.get());
+  if (!mLib) {
+    return false;
+  }
+
+  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
+  if (!initFunc) {
+    return false;
+  }
+
+  auto platformAPI = new GMPPlatformAPI();
+  InitPlatformAPI(*platformAPI);
+
+  if (initFunc(platformAPI) != GMPNoErr) {
+    return false;
+  }
+
+  mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
+  if (!mGetAPIFunc) {
+    return false;
+  }
+
+  return true;
+}
+
+MessageLoop*
+GMPChild::GMPMessageLoop()
+{
+  return mGMPMessageLoop;
+}
+
+void
+GMPChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mLib) {
+    GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
+    if (shutdownFunc) {
+      shutdownFunc();
+    }
+  }
+
+  if (AbnormalShutdown == aWhy) {
+    NS_WARNING("Abnormal shutdown of GMP process!");
+    _exit(0);
+  }
+
+  XRE_ShutdownChildProcess();
+}
+
+void
+GMPChild::ProcessingError(Result aWhat)
+{
+  switch (aWhat) {
+    case MsgDropped:
+      _exit(0); // Don't trigger a crash report.
+    case MsgNotKnown:
+      MOZ_CRASH("aborting because of MsgNotKnown");
+    case MsgNotAllowed:
+      MOZ_CRASH("aborting because of MsgNotAllowed");
+    case MsgPayloadError:
+      MOZ_CRASH("aborting because of MsgPayloadError");
+    case MsgProcessingError:
+      MOZ_CRASH("aborting because of MsgProcessingError");
+    case MsgRouteError:
+      MOZ_CRASH("aborting because of MsgRouteError");
+    case MsgValueError:
+      MOZ_CRASH("aborting because of MsgValueError");
+    default:
+      MOZ_CRASH("not reached");
+  }
+}
+
+PGMPVideoDecoderChild*
+GMPChild::AllocPGMPVideoDecoderChild()
+{
+  return new GMPVideoDecoderChild(this);
+}
+
+bool
+GMPChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PGMPVideoEncoderChild*
+GMPChild::AllocPGMPVideoEncoderChild()
+{
+  return new GMPVideoEncoderChild(this);
+}
+
+bool
+GMPChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+bool
+GMPChild::RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor)
+{
+  auto vdc = static_cast<GMPVideoDecoderChild*>(aActor);
+
+  void* vd = nullptr;
+  GMPErr err = mGetAPIFunc("decode-video", &vdc->Host(), &vd);
+  if (err != GMPNoErr || !vd) {
+    return false;
+  }
+
+  vdc->Init(static_cast<GMPVideoDecoder*>(vd));
+
+  return true;
+}
+
+bool
+GMPChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor)
+{
+  auto vec = static_cast<GMPVideoEncoderChild*>(aActor);
+
+  void* ve = nullptr;
+  GMPErr err = mGetAPIFunc("encode-video", &vec->Host(), &ve);
+  if (err != GMPNoErr || !ve) {
+    return false;
+  }
+
+  vec->Init(static_cast<GMPVideoEncoder*>(ve));
+
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPChild.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPChild_h_
+#define GMPChild_h_
+
+#include "mozilla/gmp/PGMPChild.h"
+#include "gmp-entrypoints.h"
+#include "prlink.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPChild : public PGMPChild
+{
+public:
+  GMPChild();
+  virtual ~GMPChild();
+
+  bool Init(const std::string& aPluginPath,
+            base::ProcessHandle aParentProcessHandle,
+            MessageLoop* aIOLoop,
+            IPC::Channel* aChannel);
+  bool LoadPluginLibrary(const std::string& aPluginPath);
+  MessageLoop* GMPMessageLoop();
+
+private:
+  virtual PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() MOZ_OVERRIDE;
+  virtual bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) MOZ_OVERRIDE;
+  virtual PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() MOZ_OVERRIDE;
+  virtual bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) MOZ_OVERRIDE;
+  virtual bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) MOZ_OVERRIDE;
+  virtual bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) MOZ_OVERRIDE;
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE;
+
+  PRLibrary* mLib;
+  GMPGetAPIFunc mGetAPIFunc;
+  MessageLoop* mGMPMessageLoop;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPChild_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPMessageUtils.h
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPMessageUtils_h_
+#define GMPMessageUtils_h_
+
+#include "gmp-video-codec.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<GMPVideoCodecComplexity>
+: public ContiguousEnumSerializer<GMPVideoCodecComplexity,
+                                  kGMPComplexityNormal,
+                                  kGMPComplexityInvalid>
+{};
+
+template <>
+struct ParamTraits<GMPVP8ResilienceMode>
+: public ContiguousEnumSerializer<GMPVP8ResilienceMode,
+                                  kResilienceOff,
+                                  kResilienceInvalid>
+{};
+
+template <>
+struct ParamTraits<GMPVideoCodecType>
+: public ContiguousEnumSerializer<GMPVideoCodecType,
+                                  kGMPVideoCodecVP8,
+                                  kGMPVideoCodecInvalid>
+{};
+
+template <>
+struct ParamTraits<GMPVideoCodecMode>
+: public ContiguousEnumSerializer<GMPVideoCodecMode,
+                                  kGMPRealtimeVideo,
+                                  kGMPCodecModeInvalid>
+{};
+
+template <>
+struct ParamTraits<GMPVideoCodecVP8>
+{
+  typedef GMPVideoCodecVP8 paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mPictureLossIndicationOn);
+    WriteParam(aMsg, aParam.mFeedbackModeOn);
+    WriteParam(aMsg, aParam.mComplexity);
+    WriteParam(aMsg, aParam.mResilience);
+    WriteParam(aMsg, aParam.mNumberOfTemporalLayers);
+    WriteParam(aMsg, aParam.mDenoisingOn);
+    WriteParam(aMsg, aParam.mErrorConcealmentOn);
+    WriteParam(aMsg, aParam.mAutomaticResizeOn);
+    WriteParam(aMsg, aParam.mFrameDroppingOn);
+    WriteParam(aMsg, aParam.mKeyFrameInterval);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (ReadParam(aMsg, aIter, &(aResult->mPictureLossIndicationOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mFeedbackModeOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mComplexity)) &&
+        ReadParam(aMsg, aIter, &(aResult->mResilience)) &&
+        ReadParam(aMsg, aIter, &(aResult->mNumberOfTemporalLayers)) &&
+        ReadParam(aMsg, aIter, &(aResult->mDenoisingOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mErrorConcealmentOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mAutomaticResizeOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mFrameDroppingOn)) &&
+        ReadParam(aMsg, aIter, &(aResult->mKeyFrameInterval))) {
+      return true;
+    }
+
+    return false;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    aLog->append(StringPrintf(L"[%d, %d, %d, %d, %u, %d, %d, %d, %d, %d]",
+                              aParam.mPictureLossIndicationOn,
+                              aParam.mFeedbackModeOn,
+                              aParam.mComplexity,
+                              aParam.mResilience,
+                              aParam.mNumberOfTemporalLayers,
+                              aParam.mDenoisingOn,
+                              aParam.mErrorConcealmentOn,
+                              aParam.mAutomaticResizeOn,
+                              aParam.mFrameDroppingOn,
+                              aParam.mKeyFrameInterval));
+  }
+};
+
+template <>
+struct ParamTraits<GMPSimulcastStream>
+{
+  typedef GMPSimulcastStream paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mWidth);
+    WriteParam(aMsg, aParam.mHeight);
+    WriteParam(aMsg, aParam.mNumberOfTemporalLayers);
+    WriteParam(aMsg, aParam.mMaxBitrate);
+    WriteParam(aMsg, aParam.mTargetBitrate);
+    WriteParam(aMsg, aParam.mMinBitrate);
+    WriteParam(aMsg, aParam.mQPMax);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (ReadParam(aMsg, aIter, &(aResult->mWidth)) &&
+        ReadParam(aMsg, aIter, &(aResult->mHeight)) &&
+        ReadParam(aMsg, aIter, &(aResult->mNumberOfTemporalLayers)) &&
+        ReadParam(aMsg, aIter, &(aResult->mMaxBitrate)) &&
+        ReadParam(aMsg, aIter, &(aResult->mTargetBitrate)) &&
+        ReadParam(aMsg, aIter, &(aResult->mMinBitrate)) &&
+        ReadParam(aMsg, aIter, &(aResult->mQPMax))) {
+      return true;
+    }
+    return false;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    aLog->append(StringPrintf(L"[%u, %u, %u, %u, %u, %u, %u]", aParam.mWidth, aParam.mHeight,
+                              aParam.mNumberOfTemporalLayers, aParam.mMaxBitrate,
+                              aParam.mTargetBitrate, aParam.mMinBitrate, aParam.mQPMax));
+  }
+};
+
+template <>
+struct ParamTraits<GMPVideoCodec>
+{
+  typedef GMPVideoCodec paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mCodecType);
+    WriteParam(aMsg, nsAutoCString(aParam.mPLName));
+    WriteParam(aMsg, aParam.mPLType);
+    WriteParam(aMsg, aParam.mWidth);
+    WriteParam(aMsg, aParam.mHeight);
+    WriteParam(aMsg, aParam.mStartBitrate);
+    WriteParam(aMsg, aParam.mMaxBitrate);
+    WriteParam(aMsg, aParam.mMinBitrate);
+    WriteParam(aMsg, aParam.mMaxFramerate);
+    if (aParam.mCodecType == kGMPVideoCodecVP8) {
+      WriteParam(aMsg, aParam.mCodecSpecific.mVP8);
+    } else {
+      MOZ_ASSERT(false, "Serializing unknown codec type!");
+    }
+    WriteParam(aMsg, aParam.mQPMax);
+    WriteParam(aMsg, aParam.mNumberOfSimulcastStreams);
+    for (uint32_t i = 0; i < aParam.mNumberOfSimulcastStreams; i++) {
+      WriteParam(aMsg, aParam.mSimulcastStream[i]);
+    }
+    WriteParam(aMsg, aParam.mMode);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) {
+      return false;
+    }
+
+    nsAutoCString plName;
+    if (!ReadParam(aMsg, aIter, &plName) ||
+        plName.Length() > kGMPPayloadNameSize - 1) {
+      return false;
+    }
+    memcpy(aResult->mPLName, plName.get(), plName.Length());
+    memset(aResult->mPLName + plName.Length(), 0, kGMPPayloadNameSize - plName.Length());
+
+    if (!ReadParam(aMsg, aIter, &(aResult->mPLType)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mWidth)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mHeight)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mStartBitrate)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mMaxBitrate)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mMinBitrate)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mMaxFramerate))) {
+      return false;
+    }
+
+    if (aResult->mCodecType == kGMPVideoCodecVP8) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) {
+        return false;
+      }
+    } else {
+      MOZ_ASSERT(false, "De-serializing unknown codec type!");
+      return false;
+    }
+
+    if (!ReadParam(aMsg, aIter, &(aResult->mQPMax)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mNumberOfSimulcastStreams))) {
+      return false;
+    }
+
+    if (aResult->mNumberOfSimulcastStreams > kGMPMaxSimulcastStreams) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < aResult->mNumberOfSimulcastStreams; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mSimulcastStream[i]))) {
+        return false;
+      }
+    }
+
+    if (!ReadParam(aMsg, aIter, &(aResult->mMode))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    const char* codecName = nullptr;
+    if (aParam.mCodecType == kGMPVideoCodecVP8) {
+      codecName = "VP8";
+    }
+    aLog->append(StringPrintf(L"[%s, %u, %u]",
+                              codecName,
+                              aParam.mWidth,
+                              aParam.mHeight));
+  }
+};
+
+template <>
+struct ParamTraits<GMPCodecSpecificInfoVP8>
+{
+  typedef GMPCodecSpecificInfoVP8 paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mHasReceivedSLI);
+    WriteParam(aMsg, aParam.mPictureIdSLI);
+    WriteParam(aMsg, aParam.mHasReceivedRPSI);
+    WriteParam(aMsg, aParam.mPictureIdRPSI);
+    WriteParam(aMsg, aParam.mPictureId);
+    WriteParam(aMsg, aParam.mNonReference);
+    WriteParam(aMsg, aParam.mSimulcastIdx);
+    WriteParam(aMsg, aParam.mTemporalIdx);
+    WriteParam(aMsg, aParam.mLayerSync);
+    WriteParam(aMsg, aParam.mTL0PicIdx);
+    WriteParam(aMsg, aParam.mKeyIdx);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (ReadParam(aMsg, aIter, &(aResult->mHasReceivedSLI)) &&
+        ReadParam(aMsg, aIter, &(aResult->mPictureIdSLI)) &&
+        ReadParam(aMsg, aIter, &(aResult->mHasReceivedRPSI)) &&
+        ReadParam(aMsg, aIter, &(aResult->mPictureIdRPSI)) &&
+        ReadParam(aMsg, aIter, &(aResult->mPictureId)) &&
+        ReadParam(aMsg, aIter, &(aResult->mNonReference)) &&
+        ReadParam(aMsg, aIter, &(aResult->mSimulcastIdx)) &&
+        ReadParam(aMsg, aIter, &(aResult->mTemporalIdx)) &&
+        ReadParam(aMsg, aIter, &(aResult->mLayerSync)) &&
+        ReadParam(aMsg, aIter, &(aResult->mTL0PicIdx)) &&
+        ReadParam(aMsg, aIter, &(aResult->mKeyIdx))) {
+      return true;
+    }
+    return false;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    aLog->append(StringPrintf(L"[%d, %u, %d, %u, %d, %d, %u, %u, %d, %d, %d]",
+                              aParam.mHasReceivedSLI,
+                              aParam.mPictureIdSLI,
+                              aParam.mHasReceivedRPSI,
+                              aParam.mPictureIdRPSI,
+                              aParam.mPictureId,
+                              aParam.mNonReference,
+                              aParam.mSimulcastIdx,
+                              aParam.mTemporalIdx,
+                              aParam.mLayerSync,
+                              aParam.mTL0PicIdx,
+                              aParam.mKeyIdx));
+  }
+};
+
+template <>
+struct ParamTraits<GMPCodecSpecificInfo>
+{
+  typedef GMPCodecSpecificInfo paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mCodecType);
+    if (aParam.mCodecType == kGMPVideoCodecVP8) {
+      WriteParam(aMsg, aParam.mCodecSpecific.mVP8);
+    } else {
+      MOZ_ASSERT(false, "Serializing unknown codec type!");
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) {
+      return false;
+    }
+
+    if (aResult->mCodecType == kGMPVideoCodecVP8) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) {
+        return false;
+      }
+    } else {
+      MOZ_ASSERT(false, "De-serializing unknown codec type!");
+      return false;
+    }
+
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+    const char* codecName = nullptr;
+    if (aParam.mCodecType == kGMPVideoCodecVP8) {
+      codecName = "VP8";
+    }
+    aLog->append(StringPrintf(L"[%s]", codecName));
+  }
+};
+
+} // namespace IPC
+
+#endif // GMPMessageUtils_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPParent.cpp
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsNetUtil.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsThreadUtils.h"
+#include "nsIRunnable.h"
+#include "mozIGeckoMediaPluginService.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPParent::GMPParent()
+  : mState(GMPStateNotLoaded)
+  , mProcess(nullptr)
+{
+}
+
+GMPParent::~GMPParent()
+{
+}
+
+nsresult
+GMPParent::Init(nsIFile* aPluginDir)
+{
+  MOZ_ASSERT(aPluginDir);
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  mDirectory = aPluginDir;
+
+  nsAutoString leafname;
+  nsresult rv = aPluginDir->GetLeafName(leafname);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  MOZ_ASSERT(leafname.Length() > 4);
+  mName = Substring(leafname, 4);
+
+  return ReadGMPMetaData();
+}
+
+nsresult
+GMPParent::LoadProcess()
+{
+  MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (mState == GMPStateLoaded) {
+    return NS_OK;
+  }
+
+  nsAutoCString path;
+  if (NS_FAILED(mDirectory->GetNativePath(path))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mProcess = new GMPProcessParent(path.get());
+  if (!mProcess->Launch(30 * 1000)) {
+    mProcess->Delete();
+    mProcess = nullptr;
+    return NS_ERROR_FAILURE;
+  }
+
+  bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle());
+  if (!opened) {
+    mProcess->Delete();
+    mProcess = nullptr;
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = GMPStateLoaded;
+
+  return NS_OK;
+}
+
+void
+GMPParent::MaybeUnloadProcess()
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (mVideoDecoders.Length() == 0 &&
+      mVideoEncoders.Length() == 0) {
+    UnloadProcess();
+  }
+}
+
+void
+GMPParent::UnloadProcess()
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (mState == GMPStateNotLoaded) {
+    MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty());
+    return;
+  }
+
+  mState = GMPStateNotLoaded;
+
+  // Invalidate and remove any remaining API objects.
+  for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) {
+    mVideoDecoders[i - 1]->DecodingComplete();
+  }
+
+  // Invalidate and remove any remaining API objects.
+  for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) {
+    mVideoEncoders[i - 1]->EncodingComplete();
+  }
+
+  Close();
+  if (mProcess) {
+    mProcess->Delete();
+    mProcess = nullptr;
+  }
+}
+
+void
+GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  MOZ_ALWAYS_TRUE(mVideoDecoders.RemoveElement(aDecoder));
+
+  // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
+  // until after this has completed.
+  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
+  NS_DispatchToCurrentThread(event);
+}
+
+void
+GMPParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  MOZ_ALWAYS_TRUE(mVideoEncoders.RemoveElement(aEncoder));
+
+  // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
+  // until after this has completed.
+  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
+  NS_DispatchToCurrentThread(event);
+}
+
+GMPState
+GMPParent::State() const
+{
+  return mState;
+}
+
+#ifdef DEBUG
+nsIThread*
+GMPParent::GMPThread()
+{
+  if (!mGMPThread) {
+    nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+    MOZ_ASSERT(mps);
+    if (!mps) {
+      return nullptr;
+    }
+    mps->GetThread(getter_AddRefs(mGMPThread));
+    MOZ_ASSERT(mGMPThread);
+  }
+
+  return mGMPThread;
+}
+#endif
+
+bool
+GMPParent::SupportsAPI(const nsCString& aAPI, const nsCString& aTag)
+{
+  for (uint32_t i = 0; i < mCapabilities.Length(); i++) {
+    if (!mCapabilities[i]->mAPIName.Equals(aAPI)) {
+      continue;
+    }
+    nsTArray<nsCString>& tags = mCapabilities[i]->mAPITags;
+    for (uint32_t j = 0; j < tags.Length(); j++) {
+      if (tags[j].Equals(aTag)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool
+GMPParent::EnsureProcessLoaded()
+{
+  if (mState == GMPStateLoaded) {
+    return true;
+  }
+
+  nsresult rv = LoadProcess();
+
+  return NS_SUCCEEDED(rv);
+}
+
+nsresult
+GMPParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (!EnsureProcessLoaded()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
+  if (!pvdp) {
+    return NS_ERROR_FAILURE;
+  }
+  nsRefPtr<GMPVideoDecoderParent> vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
+  mVideoDecoders.AppendElement(vdp);
+  vdp.forget(aGMPVD);
+
+  return NS_OK;
+}
+
+nsresult
+GMPParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE)
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (!EnsureProcessLoaded()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PGMPVideoEncoderParent* pvep = SendPGMPVideoEncoderConstructor();
+  if (!pvep) {
+    return NS_ERROR_FAILURE;
+  }
+  nsRefPtr<GMPVideoEncoderParent> vep = static_cast<GMPVideoEncoderParent*>(pvep);
+  mVideoEncoders.AppendElement(vep);
+  vep.forget(aGMPVE);
+
+  return NS_OK;
+}
+
+void
+GMPParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  UnloadProcess();
+}
+
+PGMPVideoDecoderParent*
+GMPParent::AllocPGMPVideoDecoderParent()
+{
+  GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
+  NS_ADDREF(vdp);
+  return vdp;
+}
+
+bool
+GMPParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
+{
+  GMPVideoDecoderParent* vdp = static_cast<GMPVideoDecoderParent*>(aActor);
+  NS_RELEASE(vdp);
+  return true;
+}
+
+PGMPVideoEncoderParent*
+GMPParent::AllocPGMPVideoEncoderParent()
+{
+  GMPVideoEncoderParent* vep = new GMPVideoEncoderParent(this);
+  NS_ADDREF(vep);
+  return vep;
+}
+
+bool
+GMPParent::DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor)
+{
+  GMPVideoEncoderParent* vep = static_cast<GMPVideoEncoderParent*>(aActor);
+  NS_RELEASE(vep);
+  return true;
+}
+
+nsresult
+ParseNextRecord(nsILineInputStream* aLineInputStream,
+                const nsCString& aPrefix,
+                nsCString& aResult,
+                bool& aMoreLines)
+{
+  nsAutoCString record;
+  nsresult rv = aLineInputStream->ReadLine(record, &aMoreLines);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (record.Length() <= aPrefix.Length() ||
+      !Substring(record, 0, aPrefix.Length()).Equals(aPrefix)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aResult = Substring(record, aPrefix.Length());
+  aResult.Trim("\b\t\r\n ");
+
+  return NS_OK;
+}
+
+nsresult
+GMPParent::ReadGMPMetaData()
+{
+  MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
+  MOZ_ASSERT(!mName.IsEmpty(), "Plugin mName cannot be empty!");
+
+  nsCOMPtr<nsIFile> infoFile;
+  nsresult rv = mDirectory->Clone(getter_AddRefs(infoFile));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  infoFile->AppendRelativePath(mName + NS_LITERAL_STRING(".info"));
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), infoFile);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(inputStream, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCString value;
+  bool moreLines = false;
+
+  // 'Name:' record
+  nsCString prefix = NS_LITERAL_CSTRING("Name:");
+  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (value.IsEmpty()) {
+    // Not OK for name to be empty. Must have one non-whitespace character.
+    return NS_ERROR_FAILURE;
+  }
+  mDisplayName = value;
+
+  // 'Description:' record
+  if (!moreLines) {
+    return NS_ERROR_FAILURE;
+  }
+  prefix = NS_LITERAL_CSTRING("Description:");
+  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  mDescription = value;
+
+  // 'Version:' record
+  if (!moreLines) {
+    return NS_ERROR_FAILURE;
+  }
+  prefix = NS_LITERAL_CSTRING("Version:");
+  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  mVersion = value;
+
+  // 'Capability:' record
+  if (!moreLines) {
+    return NS_ERROR_FAILURE;
+  }
+  prefix = NS_LITERAL_CSTRING("APIs:");
+  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsCCharSeparatedTokenizer apiTokens(value, ',');
+  while (apiTokens.hasMoreTokens()) {
+    nsAutoCString api(apiTokens.nextToken());
+    api.StripWhitespace();
+    if (api.IsEmpty()) {
+      continue;
+    }
+
+    int32_t tagsStart = api.FindChar('[');
+    if (tagsStart == 0) {
+      // Not allowed to be the first character.
+      // API name must be at least one character.
+      continue;
+    }
+
+    auto cap = new GMPCapability();
+    if (tagsStart == -1) {
+      // No tags.
+      cap->mAPIName.Assign(api);
+    } else {
+      auto tagsEnd = api.FindChar(']');
+      if (tagsEnd == -1 || tagsEnd < tagsStart) {
+        // Invalid syntax, skip whole capability.
+        delete cap;
+        continue;
+      }
+
+      cap->mAPIName.Assign(Substring(api, 0, tagsStart));
+
+      if ((tagsEnd - tagsStart) > 1) {
+        const nsDependentCSubstring ts(Substring(api, tagsStart + 1, tagsEnd - tagsStart - 1));
+        nsCCharSeparatedTokenizer tagTokens(ts, ':');
+        while (tagTokens.hasMoreTokens()) {
+          const nsDependentCSubstring tag(tagTokens.nextToken());
+          cap->mAPITags.AppendElement(tag);
+        }
+      }
+    }
+
+    mCapabilities.AppendElement(cap);
+  }
+
+  if (mCapabilities.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPParent.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPParent_h_
+#define GMPParent_h_
+
+#include "GMPProcessParent.h"
+#include "GMPVideoDecoderParent.h"
+#include "GMPVideoEncoderParent.h"
+#include "mozilla/gmp/PGMPParent.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+class nsILineInputStream;
+class nsIThread;
+class nsIFile;
+
+namespace mozilla {
+namespace gmp {
+
+class GMPCapability
+{
+public:
+  nsCString mAPIName;
+  nsTArray<nsCString> mAPITags;
+};
+
+enum GMPState {
+  GMPStateNotLoaded,
+  GMPStateLoaded
+};
+
+class GMPParent MOZ_FINAL : public PGMPParent
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(GMPParent)
+
+  GMPParent();
+
+  nsresult Init(nsIFile* aPluginDir);
+  nsresult LoadProcess();
+  void MaybeUnloadProcess();
+  void UnloadProcess();
+  bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag);
+  nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
+  void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
+  nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
+  void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
+  GMPState State() const;
+#ifdef DEBUG
+  nsIThread* GMPThread();
+#endif
+
+private:
+  ~GMPParent();
+  bool EnsureProcessLoaded();
+  nsresult ReadGMPMetaData();
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() MOZ_OVERRIDE;
+  virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) MOZ_OVERRIDE;
+  virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() MOZ_OVERRIDE;
+  virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) MOZ_OVERRIDE;
+
+  GMPState mState;
+  nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
+  nsString mName; // base name of plugin on disk, UTF-16 because used for paths
+  nsCString mDisplayName; // name of plugin displayed to users
+  nsCString mDescription; // description of plugin for display to users
+  nsCString mVersion;
+  nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
+  GMPProcessParent* mProcess;
+
+  nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
+  nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
+#ifdef DEBUG
+  nsCOMPtr<nsIThread> mGMPThread;
+#endif
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPParent_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPPlatform.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPPlatform.h"
+#include "mozilla/Monitor.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace gmp {
+
+static MessageLoop* sMainLoop = nullptr;
+
+// We just need a refcounted wrapper for GMPTask objects.
+class Runnable MOZ_FINAL
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Runnable)
+
+  Runnable(GMPTask* aTask)
+  : mTask(aTask)
+  {
+    MOZ_ASSERT(mTask);
+  }
+
+  void Run()
+  {
+    mTask->Run();
+    mTask = nullptr;
+  }
+
+private:
+  ~Runnable()
+  {
+  }
+
+  nsAutoPtr<GMPTask> mTask;
+};
+
+class SyncRunnable MOZ_FINAL
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SyncRunnable)
+
+  SyncRunnable(GMPTask* aTask, MessageLoop* aMessageLoop)
+  : mDone(false)
+  , mTask(aTask)
+  , mMessageLoop(aMessageLoop)
+  , mMonitor("GMPSyncRunnable")
+  {
+    MOZ_ASSERT(mTask);
+    MOZ_ASSERT(mMessageLoop);
+  }
+
+  void Post()
+  {
+    // We assert here for two reasons.
+    // 1) Nobody should be blocking the main thread.
+    // 2) This prevents deadlocks when doing sync calls to main which if the
+    //    main thread tries to do a sync call back to the calling thread.
+    MOZ_ASSERT(MessageLoop::current() != sMainLoop);
+
+    mMessageLoop->PostTask(FROM_HERE, NewRunnableMethod(this, &SyncRunnable::Run));
+    MonitorAutoLock lock(mMonitor);
+    while (!mDone) {
+      lock.Wait();
+    }
+  }
+
+  void Run()
+  {
+    mTask->Run();
+    mTask = nullptr;
+    MonitorAutoLock lock(mMonitor);
+    mDone = true;
+    lock.Notify();
+  }
+
+private:
+  ~SyncRunnable()
+  {
+  }
+
+  bool mDone;
+  nsAutoPtr<GMPTask> mTask;
+  MessageLoop* mMessageLoop;
+  Monitor mMonitor;
+};
+
+GMPErr
+CreateThread(GMPThread** aThread)
+{
+  if (!aThread) {
+    return GMPGenericErr;
+  }
+
+  *aThread = new GMPThreadImpl();
+
+  return GMPNoErr;
+}
+
+GMPErr
+RunOnMainThread(GMPTask* aTask)
+{
+  if (!aTask || !sMainLoop) {
+    return GMPGenericErr;
+  }
+
+  nsRefPtr<Runnable> r = new Runnable(aTask);
+
+  sMainLoop->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run));
+
+  return GMPNoErr;
+}
+
+GMPErr
+SyncRunOnMainThread(GMPTask* aTask)
+{
+  if (!aTask || !sMainLoop || sMainLoop == MessageLoop::current()) {
+    return GMPGenericErr;
+  }
+
+  nsRefPtr<SyncRunnable> r = new SyncRunnable(aTask, sMainLoop);
+
+  r->Post();
+
+  return GMPNoErr;
+}
+
+GMPErr
+CreateMutex(GMPMutex** aMutex)
+{
+  if (!aMutex) {
+    return GMPGenericErr;
+  }
+
+  *aMutex = new GMPMutexImpl();
+
+  return GMPNoErr;
+}
+
+void
+InitPlatformAPI(GMPPlatformAPI& aPlatformAPI)
+{
+  if (!sMainLoop) {
+    sMainLoop = MessageLoop::current();
+  }
+
+  aPlatformAPI.version = 0;
+  aPlatformAPI.createthread = &CreateThread;
+  aPlatformAPI.runonmainthread = &RunOnMainThread;
+  aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread;
+  aPlatformAPI.createmutex = &CreateMutex;
+}
+
+GMPThreadImpl::GMPThreadImpl()
+: mMutex("GMPThreadImpl"),
+  mThread("GMPThread")
+{
+}
+
+GMPThreadImpl::~GMPThreadImpl()
+{
+}
+
+void
+GMPThreadImpl::Post(GMPTask* aTask)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (!mThread.IsRunning()) {
+    bool started = mThread.Start();
+    if (!started) {
+      NS_WARNING("Unable to start GMPThread!");
+      return;
+    }
+  }
+
+  nsRefPtr<Runnable> r = new Runnable(aTask);
+
+  mThread.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run));
+}
+
+void
+GMPThreadImpl::Join()
+{
+  MutexAutoLock lock(mMutex);
+  if (mThread.IsRunning()) {
+    mThread.Stop();
+  }
+}
+
+GMPMutexImpl::GMPMutexImpl()
+: mMutex("gmp-mutex")
+{
+}
+
+GMPMutexImpl::~GMPMutexImpl()
+{
+}
+
+void
+GMPMutexImpl::Acquire()
+{
+  mMutex.Lock();
+}
+
+void
+GMPMutexImpl::Release()
+{
+  mMutex.Unlock();
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPPlatform.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPPlatform_h_
+#define GMPPlatform_h_
+
+#include "mozilla/Mutex.h"
+#include "gmp-platform.h"
+#include "base/thread.h"
+
+namespace mozilla {
+namespace gmp {
+
+void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI);
+
+class GMPThreadImpl : public GMPThread
+{
+public:
+  GMPThreadImpl();
+  virtual ~GMPThreadImpl();
+
+  // GMPThread
+  virtual void Post(GMPTask* aTask) MOZ_OVERRIDE;
+  virtual void Join() MOZ_OVERRIDE;
+
+private:
+  Mutex mMutex;
+  base::Thread mThread;
+};
+
+class GMPMutexImpl : public GMPMutex
+{
+public:
+  GMPMutexImpl();
+  virtual ~GMPMutexImpl();
+
+  // GMPMutex
+  virtual void Acquire() MOZ_OVERRIDE;
+  virtual void Release() MOZ_OVERRIDE;
+
+private:
+  Mutex mMutex;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPPlatform_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPProcessChild.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPProcessChild.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace gmp {
+
+GMPProcessChild::GMPProcessChild(ProcessHandle parentHandle)
+: ProcessChild(parentHandle)
+{
+}
+
+GMPProcessChild::~GMPProcessChild()
+{
+}
+
+bool
+GMPProcessChild::Init()
+{
+  std::string pluginFilename;
+
+#if defined(OS_POSIX)
+  // NB: need to be very careful in ensuring that the first arg
+  // (after the binary name) here is indeed the plugin module path.
+  // Keep in sync with dom/plugins/PluginModuleParent.
+  std::vector<std::string> values = CommandLine::ForCurrentProcess()->argv();
+  NS_ABORT_IF_FALSE(values.size() >= 2, "not enough args");
+  pluginFilename = values[1];
+#elif defined(OS_WIN)
+  std::vector<std::wstring> values = CommandLine::ForCurrentProcess()->GetLooseValues();
+  NS_ABORT_IF_FALSE(values.size() >= 1, "not enough loose args");
+  pluginFilename = WideToUTF8(values[0]);
+#else
+#error Not implemented
+#endif
+
+  BackgroundHangMonitor::Startup();
+
+  return mPlugin.Init(pluginFilename,
+                      ParentHandle(),
+                      IOThreadChild::message_loop(),
+                      IOThreadChild::channel());
+}
+
+void
+GMPProcessChild::CleanUp()
+{
+  BackgroundHangMonitor::Shutdown();
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPProcessChild.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPProcessChild_h_
+#define GMPProcessChild_h_
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "GMPChild.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPProcessChild MOZ_FINAL : public mozilla::ipc::ProcessChild {
+protected:
+  typedef mozilla::ipc::ProcessChild ProcessChild;
+
+public:
+  GMPProcessChild(ProcessHandle parentHandle);
+  ~GMPProcessChild();
+
+  virtual bool Init() MOZ_OVERRIDE;
+  virtual void CleanUp() MOZ_OVERRIDE;
+
+private:
+  GMPChild mPlugin;
+
+  DISALLOW_COPY_AND_ASSIGN(GMPProcessChild);
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPProcessChild_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPProcessParent.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPProcessParent.h"
+
+#include "base/string_util.h"
+#include "base/process_util.h"
+
+using std::vector;
+using std::string;
+
+using mozilla::gmp::GMPProcessParent;
+using mozilla::ipc::GeckoChildProcessHost;
+using base::ProcessArchitecture;
+
+template<>
+struct RunnableMethodTraits<GMPProcessParent>
+{
+  static void RetainCallee(GMPProcessParent* obj) { }
+  static void ReleaseCallee(GMPProcessParent* obj) { }
+};
+
+namespace mozilla {
+namespace gmp {
+
+GMPProcessParent::GMPProcessParent(const std::string& aGMPPath)
+: GeckoChildProcessHost(GeckoProcessType_GMPlugin),
+  mGMPPath(aGMPPath)
+{
+}
+
+GMPProcessParent::~GMPProcessParent()
+{
+}
+
+bool
+GMPProcessParent::Launch(int32_t aTimeoutMs)
+{
+  vector<string> args;
+  args.push_back(mGMPPath);
+  return SyncLaunch(args, aTimeoutMs, base::GetCurrentProcessArchitecture());
+}
+
+void
+GMPProcessParent::Delete()
+{
+  MessageLoop* currentLoop = MessageLoop::current();
+  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+
+  if (currentLoop == ioLoop) {
+    delete this;
+    return;
+  }
+
+  ioLoop->PostTask(FROM_HERE, NewRunnableMethod(this, &GMPProcessParent::Delete));
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPProcessParent.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=4 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPProcessParent_h
+#define GMPProcessParent_h 1
+
+#include "mozilla/Attributes.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/scoped_ptr.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "chrome/common/child_process_host.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPProcessParent MOZ_FINAL : public mozilla::ipc::GeckoChildProcessHost
+{
+public:
+  GMPProcessParent(const std::string& aGMPPath);
+  ~GMPProcessParent();
+
+  // Synchronously launch the plugin process. If the process fails to launch
+  // after timeoutMs, this method will return false.
+  bool Launch(int32_t aTimeoutMs);
+
+  void Delete();
+
+  virtual bool CanShutdown() MOZ_OVERRIDE { return true; }
+  const std::string& GetPluginFilePath() { return mGMPPath; }
+
+  using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent;
+  using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+  using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle;
+
+private:
+  std::string mGMPPath;
+
+  DISALLOW_COPY_AND_ASSIGN(GMPProcessParent);
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // ifndef GMPProcessParent_h
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPService.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPService.h"
+#include "GMPParent.h"
+#include "GMPVideoDecoderParent.h"
+#include "nsIObserverService.h"
+#include "GeckoChildProcessHost.h"
+#if defined(XP_WIN)
+#include "nsIWindowsRegKey.h"
+#endif
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsXPCOMPrivate.h"
+#include "nsISimpleEnumerator.h"
+#include "mozilla/Services.h"
+
+namespace mozilla {
+namespace gmp {
+
+static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
+
+class GMPServiceCreateHelper MOZ_FINAL : public nsRunnable
+{
+  nsRefPtr<GeckoMediaPluginService> mService;
+
+public:
+  static already_AddRefed<GeckoMediaPluginService>
+  GetOrCreate()
+  {
+    nsRefPtr<GeckoMediaPluginService> service;
+
+    if (NS_IsMainThread()) {
+      service = GetOrCreateOnMainThread();
+    } else {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      MOZ_ASSERT(mainThread);
+
+      nsRefPtr<GMPServiceCreateHelper> createHelper = new GMPServiceCreateHelper();
+
+      mozilla::SyncRunnable::DispatchToThread(mainThread, createHelper, true);
+
+      service = createHelper->mService.forget();
+    }
+
+    return service.forget();
+  }
+
+private:
+  GMPServiceCreateHelper()
+  {
+  }
+
+  ~GMPServiceCreateHelper()
+  {
+    MOZ_ASSERT(!mService);
+  }
+
+  static already_AddRefed<GeckoMediaPluginService>
+  GetOrCreateOnMainThread()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
+    if (!service) {
+      service = new GeckoMediaPluginService();
+      service->Init();
+
+      sSingletonService = service;
+      ClearOnShutdown(&sSingletonService);
+    }
+
+    return service.forget();
+  }
+
+  NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mService = GetOrCreateOnMainThread();
+    return NS_OK;
+  }
+};
+
+already_AddRefed<GeckoMediaPluginService>
+GeckoMediaPluginService::GetGeckoMediaPluginService()
+{
+  return GMPServiceCreateHelper::GetOrCreate();
+}
+
+NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
+
+GeckoMediaPluginService::GeckoMediaPluginService()
+  : mMutex("GeckoMediaPluginService::mMutex")
+  , mShuttingDown(false)
+  , mShuttingDownOnGMPThread(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+GeckoMediaPluginService::~GeckoMediaPluginService()
+{
+  MOZ_ASSERT(mPlugins.IsEmpty());
+}
+
+void
+GeckoMediaPluginService::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Cache user directory while we're on the main thread. We do this because
+  // if we try to use "~" in a path during plugin lookup on a non-main thread,
+  // the nsIFile code will try to resolve it using NS_GetSpecialDirectory, which
+  // doesn't work on non-main threads.
+  nsCOMPtr<nsIFile> homeDir;
+  NS_GetSpecialDirectory(NS_OS_HOME_DIR, getter_AddRefs(homeDir));
+  if (homeDir) {
+    homeDir->GetPath(mHomePath);
+  }
+
+  nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
+  MOZ_ASSERT(obsService);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginService::Observe(nsISupports* aSubject,
+                                 const char* aTopic,
+                                 const char16_t* aSomeData)
+{
+  if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
+    nsCOMPtr<nsIThread> gmpThread;
+    {
+      MutexAutoLock lock(mMutex);
+      MOZ_ASSERT(!mShuttingDown);
+      mShuttingDown = true;
+      gmpThread = mGMPThread;
+    }
+
+    if (gmpThread) {
+      gmpThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
+                           NS_DISPATCH_SYNC);
+    } else {
+      MOZ_ASSERT(mPlugins.IsEmpty());
+    }
+  } else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
+    nsCOMPtr<nsIThread> gmpThread;
+    {
+      MutexAutoLock lock(mMutex);
+      MOZ_ASSERT(mShuttingDown);
+      mGMPThread.swap(gmpThread);
+    }
+
+    if (gmpThread) {
+      gmpThread->Shutdown();
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginService::GetThread(nsIThread** aThread)
+{
+  MOZ_ASSERT(aThread);
+
+  // This can be called from any thread.
+  MutexAutoLock lock(mMutex);
+
+  if (!mGMPThread) {
+    // Don't allow the thread to be created after shutdown has started.
+    if (mShuttingDown) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  NS_ADDREF(mGMPThread);
+  *aThread = mGMPThread;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginService::GetGMPVideoDecoderVP8(GMPVideoHost** aOutVideoHost, GMPVideoDecoder** aGMPVD)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  if (mShuttingDownOnGMPThread) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(NS_LITERAL_CSTRING("decode-video"),
+                                               NS_LITERAL_CSTRING("vp8"));
+  if (!gmp) {
+    return NS_ERROR_FAILURE;
+  }
+
+  GMPVideoDecoderParent* gmpVDP;
+  nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  *aGMPVD = gmpVDP;
+  *aOutVideoHost = &gmpVDP->Host();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginService::GetGMPVideoEncoderVP8(GMPVideoHost** aOutVideoHost, GMPVideoEncoder** aGMPVE)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  if (mShuttingDownOnGMPThread) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<GMPParent> gmp = SelectPluginForAPI(NS_LITERAL_CSTRING("encode-video"),
+                                               NS_LITERAL_CSTRING("vp8"));
+  if (!gmp) {
+    return NS_ERROR_FAILURE;
+  }
+
+  GMPVideoEncoderParent* gmpVEP;
+  nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  *aGMPVE = gmpVEP;
+  *aOutVideoHost = &gmpVEP->Host();
+
+  return NS_OK;
+}
+
+void
+GeckoMediaPluginService::UnloadPlugins()
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  MOZ_ASSERT(!mShuttingDownOnGMPThread);
+  mShuttingDownOnGMPThread = true;
+
+  for (uint32_t i = 0; i < mPlugins.Length(); i++) {
+    mPlugins[i]->UnloadProcess();
+  }
+  mPlugins.Clear();
+}
+
+GMPParent*
+GeckoMediaPluginService::SelectPluginForAPI(const nsCString& aAPI,
+                                            const nsCString& aTag)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  GMPParent* gmp = SelectPluginFromListForAPI(aAPI, aTag);
+  if (gmp) {
+    return gmp;
+  }
+
+  RefreshPluginList();
+
+  return SelectPluginFromListForAPI(aAPI, aTag);
+}
+
+GMPParent*
+GeckoMediaPluginService::SelectPluginFromListForAPI(const nsCString& aAPI,
+                                                    const nsCString& aTag)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  for (uint32_t i = 0; i < mPlugins.Length(); i++) {
+    GMPParent* gmp = mPlugins[i];
+    if (gmp->SupportsAPI(aAPI, aTag)) {
+      return gmp;
+    }
+  }
+  return nullptr;
+}
+
+nsresult
+GeckoMediaPluginService::GetDirectoriesToSearch(nsTArray<nsCOMPtr<nsIFile>> &aDirs)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+#if defined(XP_MACOSX)
+  nsCOMPtr<nsIFile> searchDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  MOZ_ASSERT(!mHomePath.IsEmpty());
+  nsresult rv = searchDir->InitWithPath(mHomePath + NS_LITERAL_STRING("/Library/Internet Plug-Ins/"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  aDirs.AppendElement(searchDir);
+  searchDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  rv = searchDir->InitWithPath(NS_LITERAL_STRING("/Library/Internet Plug-Ins/"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  aDirs.AppendElement(searchDir);
+#elif defined(OS_POSIX)
+  nsCOMPtr<nsIFile> searchDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+  nsresult rv = searchDir->InitWithPath(NS_LITERAL_STRING("/usr/lib/mozilla/plugins/"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  aDirs.AppendElement(searchDir);
+#endif
+  return NS_OK;
+}
+
+#if defined(XP_WIN)
+static nsresult
+GetPossiblePluginsForRegRoot(uint32_t aKey, nsTArray<nsCOMPtr<nsIFile>>& aDirs)
+{
+  nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!regKey) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = regKey->Open(aKey,
+                             NS_LITERAL_STRING("Software\\MozillaPlugins"),
+                             nsIWindowsRegKey::ACCESS_READ);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  uint32_t childCount = 0;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(regKey->GetChildCount(&childCount)));
+  for (uint32_t index = 0; index < childCount; index++) {
+    nsAutoString childName;
+    rv = regKey->GetChildName(index, childName);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIWindowsRegKey> childKey;
+    rv = regKey->OpenChild(childName, nsIWindowsRegKey::ACCESS_QUERY_VALUE,
+                           getter_AddRefs(childKey));
+    if (NS_FAILED(rv) || !childKey) {
+      continue;
+    }
+
+    nsAutoString path;
+    rv = childKey->ReadStringValue(NS_LITERAL_STRING("Path"), path);
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIFile> localFile;
+    if (NS_SUCCEEDED(NS_NewLocalFile(path, true, getter_AddRefs(localFile))) &&
+        localFile) {
+      bool isFileThere = false;
+      if (NS_SUCCEEDED(localFile->Exists(&isFileThere)) && isFileThere) {
+        aDirs.AppendElement(localFile);
+      }
+    }
+  }
+
+  regKey->Close();
+
+  return NS_OK;
+}
+#endif
+
+nsresult
+GeckoMediaPluginService::GetPossiblePlugins(nsTArray<nsCOMPtr<nsIFile>>& aDirs)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+#if defined(XP_WIN)
+  // The ROOT_KEY_CURRENT_USER entry typically fails to open, causing this call to
+  // fail. Don't check any return values because if we find nothing we don't care.
+  GetPossiblePluginsForRegRoot(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, aDirs);
+  GetPossiblePluginsForRegRoot(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, aDirs);
+#endif
+  return NS_OK;
+}
+
+nsresult
+GeckoMediaPluginService::SearchDirectory(nsIFile* aSearchDir)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+  MOZ_ASSERT(aSearchDir);
+
+  nsCOMPtr<nsISimpleEnumerator> iter;
+  nsresult rv = aSearchDir->GetDirectoryEntries(getter_AddRefs(iter));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsISupports> supports;
+    rv = iter->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+    ProcessPossiblePlugin(dirEntry);
+  }
+
+  return NS_OK;
+}
+
+void
+GeckoMediaPluginService::ProcessPossiblePlugin(nsIFile* aDir)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+  MOZ_ASSERT(aDir);
+
+  bool isDirectory = false;
+  nsresult rv = aDir->IsDirectory(&isDirectory);
+  if (NS_FAILED(rv) || !isDirectory) {
+    return;
+  }
+
+  nsAutoString leafName;
+  rv = aDir->GetLeafName(leafName);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  NS_NAMED_LITERAL_STRING(prefix, "gmp-");
+  if (leafName.Length() <= prefix.Length() ||
+      !Substring(leafName, 0, prefix.Length()).Equals(prefix)) {
+    return;
+  }
+
+  nsRefPtr<GMPParent> gmp = new GMPParent();
+  rv = gmp->Init(aDir);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  mPlugins.AppendElement(gmp);
+}
+
+void
+GeckoMediaPluginService::RefreshPluginList()
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  for (uint32_t iPlusOne = mPlugins.Length(); iPlusOne > 0; iPlusOne--) {
+    if (mPlugins[iPlusOne - 1]->State() == GMPStateNotLoaded) {
+      mPlugins.RemoveElementAt(iPlusOne - 1);
+    }
+  }
+
+  nsTArray<nsCOMPtr<nsIFile>> searchDirs;
+  nsresult rv = GetDirectoriesToSearch(searchDirs);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < searchDirs.Length(); i++) {
+    SearchDirectory(searchDirs[i]);
+  }
+
+  nsTArray<nsCOMPtr<nsIFile>> possiblePlugins;
+  rv = GetPossiblePlugins(possiblePlugins);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < possiblePlugins.Length(); i++) {
+    ProcessPossiblePlugin(possiblePlugins[i]);
+  }
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPService.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPService_h_
+#define GMPService_h_
+
+#include "mozIGeckoMediaPluginService.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "mozilla/Mutex.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+
+class nsIFile;
+template <class> class already_AddRefed;
+
+namespace mozilla {
+namespace gmp {
+
+class GMPParent;
+
+class GeckoMediaPluginService MOZ_FINAL : public mozIGeckoMediaPluginService
+                                        , public nsIObserver
+{
+public:
+  static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
+
+  GeckoMediaPluginService();
+  void Init();
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE
+  NS_DECL_NSIOBSERVER
+
+private:
+  ~GeckoMediaPluginService();
+
+  GMPParent* SelectPluginFromListForAPI(const nsCString& aAPI, const nsCString& aTag);
+  GMPParent* SelectPluginForAPI(const nsCString& aAPI, const nsCString& aTag);
+  void UnloadPlugins();
+
+  void RefreshPluginList();
+  void ProcessPossiblePlugin(nsIFile* aDir);
+  nsresult SearchDirectory(nsIFile* aSearchDir);
+  nsresult GetPossiblePlugins(nsTArray<nsCOMPtr<nsIFile>>& aDirs);
+  nsresult GetDirectoriesToSearch(nsTArray<nsCOMPtr<nsIFile>>& aDirs);
+
+  nsTArray<nsRefPtr<GMPParent>> mPlugins;
+  Mutex mMutex; // Protects mGMPThread and mShuttingDown
+  nsCOMPtr<nsIThread> mGMPThread;
+  bool mShuttingDown;
+  bool mShuttingDownOnGMPThread;
+  nsString mHomePath;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPService_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPSharedMemManager.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPSharedMemManager_h_
+#define GMPSharedMemManager_h_
+
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPSharedMemManager
+{
+public:
+  virtual bool MgrAllocShmem(size_t aSize,
+                             ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                             ipc::Shmem* aMem) = 0;
+  virtual bool MgrDeallocShmem(ipc::Shmem& aMem) = 0;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPSharedMemManager_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPTypes.ipdlh
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+namespace mozilla {
+namespace gmp {
+
+struct GMPVideoEncodedFrameData
+{
+  int64_t mCaptureTime_ms;
+  uint32_t mEncodedWidth;
+  uint32_t mEncodedHeight;
+  uint32_t mTimeStamp;
+  uint32_t mFrameType;
+  uint32_t mSize;
+  Shmem mBuffer;
+  bool mCompleteFrame;
+};
+
+struct GMPPlaneData
+{
+  int32_t mSize;
+  int32_t mStride;
+  Shmem mBuffer;
+};
+
+struct GMPVideoi420FrameData
+{
+  GMPPlaneData mYPlane;
+  GMPPlaneData mUPlane;
+  GMPPlaneData mVPlane;
+  int32_t mWidth;
+  int32_t mHeight;
+  uint32_t mTimestamp;
+  int64_t mRenderTime_ms;
+};
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoDecoderChild.cpp
@@ -0,0 +1,177 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoDecoderChild.h"
+#include "GMPVideoi420FrameImpl.h"
+#include <stdio.h>
+#include "mozilla/unused.h"
+#include "GMPVideoEncodedFrameImpl.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoDecoderChild::GMPVideoDecoderChild(GMPChild* aPlugin)
+: mPlugin(aPlugin),
+  mVideoDecoder(nullptr),
+  mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+  MOZ_ASSERT(mPlugin);
+}
+
+GMPVideoDecoderChild::~GMPVideoDecoderChild()
+{
+}
+
+void
+GMPVideoDecoderChild::Init(GMPVideoDecoder* aDecoder)
+{
+  MOZ_ASSERT(aDecoder, "Cannot initialize video decoder child without a video decoder!");
+  mVideoDecoder = aDecoder;
+}
+
+GMPVideoHostImpl&
+GMPVideoDecoderChild::Host()
+{
+  return mVideoHost;
+}
+
+void
+GMPVideoDecoderChild::Decoded(GMPVideoi420Frame* aDecodedFrame)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  if (!aDecodedFrame) {
+    MOZ_CRASH("Not given a decoded frame!");
+  }
+
+  auto df = static_cast<GMPVideoi420FrameImpl*>(aDecodedFrame);
+
+  GMPVideoi420FrameData frameData;
+  df->InitFrameData(frameData);
+  SendDecoded(frameData);
+
+  aDecodedFrame->Destroy();
+}
+
+void
+GMPVideoDecoderChild::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  SendReceivedDecodedReferenceFrame(aPictureId);
+}
+
+void
+GMPVideoDecoderChild::ReceivedDecodedFrame(const uint64_t aPictureId)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  SendReceivedDecodedFrame(aPictureId);
+}
+
+void
+GMPVideoDecoderChild::InputDataExhausted()
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  SendInputDataExhausted();
+}
+
+bool
+GMPVideoDecoderChild::MgrAllocShmem(size_t aSize,
+                                    ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                                    ipc::Shmem* aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  return AllocShmem(aSize, aType, aMem);
+}
+
+bool
+GMPVideoDecoderChild::MgrDeallocShmem(Shmem& aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  return DeallocShmem(aMem);
+}
+
+bool
+GMPVideoDecoderChild::RecvInitDecode(const GMPVideoCodec& aCodecSettings,
+                                     const int32_t& aCoreCount)
+{
+  if (!mVideoDecoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoDecoder->InitDecode(aCodecSettings, this, aCoreCount);
+
+  return true;
+}
+
+bool
+GMPVideoDecoderChild::RecvDecode(const GMPVideoEncodedFrameData& aInputFrame,
+                                 const bool& aMissingFrames,
+                                 const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                                 const int64_t& aRenderTimeMs)
+{
+  if (!mVideoDecoder) {
+    return false;
+  }
+
+  auto f = new GMPVideoEncodedFrameImpl(aInputFrame, &mVideoHost);
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoDecoder->Decode(f, aMissingFrames, aCodecSpecificInfo, aRenderTimeMs);
+
+  return true;
+}
+
+bool
+GMPVideoDecoderChild::RecvReset()
+{
+  if (!mVideoDecoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoDecoder->Reset();
+
+  return true;
+}
+
+bool
+GMPVideoDecoderChild::RecvDrain()
+{
+  if (!mVideoDecoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoDecoder->Drain();
+
+  return true;
+}
+
+bool
+GMPVideoDecoderChild::RecvDecodingComplete()
+{
+  if (mVideoDecoder) {
+    // Ignore any return code. It is OK for this to fail without killing the process.
+    mVideoDecoder->DecodingComplete();
+    mVideoDecoder = nullptr;
+  }
+
+  mVideoHost.DoneWithAPI();
+
+  mPlugin = nullptr;
+
+  unused << Send__delete__(this);
+
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoDecoderChild.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoDecoderChild_h_
+#define GMPVideoDecoderChild_h_
+
+#include "nsString.h"
+#include "mozilla/gmp/PGMPVideoDecoderChild.h"
+#include "gmp-video-decode.h"
+#include "GMPSharedMemManager.h"
+#include "GMPVideoHost.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPChild;
+
+class GMPVideoDecoderChild : public PGMPVideoDecoderChild,
+                             public GMPDecoderCallback,
+                             public GMPSharedMemManager
+{
+public:
+  GMPVideoDecoderChild(GMPChild* aPlugin);
+  virtual ~GMPVideoDecoderChild();
+
+  void Init(GMPVideoDecoder* aDecoder);
+  GMPVideoHostImpl& Host();
+
+  // GMPDecoderCallback
+  virtual void Decoded(GMPVideoi420Frame* decodedFrame) MOZ_OVERRIDE;
+  virtual void ReceivedDecodedReferenceFrame(const uint64_t pictureId) MOZ_OVERRIDE;
+  virtual void ReceivedDecodedFrame(const uint64_t pictureId) MOZ_OVERRIDE;
+  virtual void InputDataExhausted() MOZ_OVERRIDE;
+
+  // GMPSharedMemManager
+  virtual bool MgrAllocShmem(size_t aSize,
+                             ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                             ipc::Shmem* aMem) MOZ_OVERRIDE;
+  virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE;
+
+private:
+  // PGMPVideoDecoderChild
+  virtual bool RecvInitDecode(const GMPVideoCodec& codecSettings,
+                              const int32_t& coreCount) MOZ_OVERRIDE;
+  virtual bool RecvDecode(const GMPVideoEncodedFrameData& inputFrame,
+                          const bool& missingFrames,
+                          const GMPCodecSpecificInfo& codecSpecificInfo,
+                          const int64_t& renderTimeMs) MOZ_OVERRIDE;
+  virtual bool RecvReset() MOZ_OVERRIDE;
+  virtual bool RecvDrain() MOZ_OVERRIDE;
+  virtual bool RecvDecodingComplete() MOZ_OVERRIDE;
+
+  GMPChild* mPlugin;
+  GMPVideoDecoder* mVideoDecoder;
+  GMPVideoHostImpl mVideoHost;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoDecoderChild_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoDecoderParent.cpp
@@ -0,0 +1,255 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoDecoderParent.h"
+#include "GMPVideoEncodedFrameImpl.h"
+#include "GMPVideoi420FrameImpl.h"
+#include "GMPParent.h"
+#include <stdio.h>
+#include "mozilla/unused.h"
+#include "GMPMessageUtils.h"
+#include "nsAutoRef.h"
+
+template <>
+class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame>
+{
+public:
+  static void Release(GMPVideoEncodedFrame* aFrame) { aFrame->Destroy(); }
+};
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoDecoderParent::GMPVideoDecoderParent(GMPParent* aPlugin)
+  : mCanSendMessages(true)
+  , mPlugin(aPlugin)
+  , mCallback(nullptr)
+  , mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+  MOZ_ASSERT(mPlugin);
+}
+
+GMPVideoDecoderParent::~GMPVideoDecoderParent()
+{
+}
+
+GMPVideoHostImpl&
+GMPVideoDecoderParent::Host()
+{
+  return mVideoHost;
+}
+
+bool
+GMPVideoDecoderParent::MgrAllocShmem(size_t aSize,
+                                     ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                                     ipc::Shmem* aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  return AllocShmem(aSize, aType, aMem);
+}
+
+bool
+GMPVideoDecoderParent::MgrDeallocShmem(Shmem& aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  return DeallocShmem(aMem);
+}
+
+GMPVideoErr
+GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
+                                  GMPDecoderCallback* aCallback,
+                                  int32_t aCoreCount)
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video decoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!aCallback) {
+    return GMPVideoGenericErr;
+  }
+  mCallback = aCallback;
+
+  if (!SendInitDecode(aCodecSettings, aCoreCount)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame,
+                              bool aMissingFrames,
+                              const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                              int64_t aRenderTimeMs)
+{
+  nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame);
+
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video decoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  auto inputFrameImpl = static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame);
+
+  GMPVideoEncodedFrameData frameData;
+  inputFrameImpl->RelinquishFrameData(frameData);
+
+  if (!SendDecode(frameData,
+                  aMissingFrames,
+                  aCodecSpecificInfo,
+                  aRenderTimeMs)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoDecoderParent::Reset()
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video decoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!SendReset()) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoDecoderParent::Drain()
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video decoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!SendDrain()) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+// Note: Consider keeping ActorDestroy sync'd up when making changes here.
+void
+GMPVideoDecoderParent::DecodingComplete()
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video decoder!");
+    return;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  mCanSendMessages = false;
+
+  mCallback = nullptr;
+
+  mVideoHost.DoneWithAPI();
+
+  unused << SendDecodingComplete();
+}
+
+// Note: Keep this sync'd up with DecodingComplete
+void
+GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mPlugin) {
+    // Ignore any return code. It is OK for this to fail without killing the process.
+    mPlugin->VideoDecoderDestroyed(this);
+    mPlugin = nullptr;
+  }
+  mCanSendMessages = false;
+  mCallback = nullptr;
+  mVideoHost.ActorDestroyed();
+}
+
+bool
+GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
+{
+  if (!mCallback) {
+    return false;
+  }
+
+  auto f = new GMPVideoi420FrameImpl(aDecodedFrame, &mVideoHost);
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mCallback->Decoded(f);
+
+  return true;
+}
+
+bool
+GMPVideoDecoderParent::RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId)
+{
+  if (!mCallback) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mCallback->ReceivedDecodedReferenceFrame(aPictureId);
+
+  return true;
+}
+
+bool
+GMPVideoDecoderParent::RecvReceivedDecodedFrame(const uint64_t& aPictureId)
+{
+  if (!mCallback) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mCallback->ReceivedDecodedFrame(aPictureId);
+
+  return true;
+}
+
+bool
+GMPVideoDecoderParent::RecvInputDataExhausted()
+{
+  if (!mCallback) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mCallback->InputDataExhausted();
+
+  return true;
+}
+
+bool
+GMPVideoDecoderParent::Recv__delete__()
+{
+  if (mPlugin) {
+    // Ignore any return code. It is OK for this to fail without killing the process.
+    mPlugin->VideoDecoderDestroyed(this);
+    mPlugin = nullptr;
+  }
+
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoDecoderParent.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoDecoderParent_h_
+#define GMPVideoDecoderParent_h_
+
+#include "mozilla/RefPtr.h"
+#include "gmp-video-decode.h"
+#include "mozilla/gmp/PGMPVideoDecoderParent.h"
+#include "GMPMessageUtils.h"
+#include "GMPSharedMemManager.h"
+#include "GMPVideoHost.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPParent;
+
+class GMPVideoDecoderParent MOZ_FINAL : public GMPVideoDecoder
+                                      , public PGMPVideoDecoderParent
+                                      , public GMPSharedMemManager
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
+
+  GMPVideoDecoderParent(GMPParent *aPlugin);
+
+  GMPVideoHostImpl& Host();
+
+  // GMPSharedMemManager
+  virtual bool MgrAllocShmem(size_t aSize,
+                             ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                             ipc::Shmem* aMem) MOZ_OVERRIDE;
+  virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE;
+
+  // GMPVideoDecoder
+  virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings,
+                                 GMPDecoderCallback* aCallback,
+                                 int32_t aCoreCount) MOZ_OVERRIDE;
+  virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame,
+                             bool aMissingFrames,
+                             const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                             int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
+  virtual GMPVideoErr Reset() MOZ_OVERRIDE;
+  virtual GMPVideoErr Drain() MOZ_OVERRIDE;
+  virtual void DecodingComplete() MOZ_OVERRIDE;
+
+private:
+  ~GMPVideoDecoderParent();
+
+  // PGMPVideoDecoderParent
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  virtual bool RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) MOZ_OVERRIDE;
+  virtual bool RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId) MOZ_OVERRIDE;
+  virtual bool RecvReceivedDecodedFrame(const uint64_t& aPictureId) MOZ_OVERRIDE;
+  virtual bool RecvInputDataExhausted() MOZ_OVERRIDE;
+  virtual bool Recv__delete__() MOZ_OVERRIDE;
+
+  bool mCanSendMessages;
+  GMPParent* mPlugin;
+  GMPDecoderCallback* mCallback;
+  GMPVideoHostImpl mVideoHost;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoDecoderParent_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncodedFrameImpl.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoEncodedFrameImpl.h"
+#include "GMPVideoHost.h"
+#include "mozilla/gmp/GMPTypes.h"
+#include "GMPSharedMemManager.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(GMPVideoHostImpl* aHost)
+: mEncodedWidth(0),
+  mEncodedHeight(0),
+  mTimeStamp(0),
+  mCaptureTime_ms(0),
+  mFrameType(kGMPDeltaFrame),
+  mSize(0),
+  mCompleteFrame(false),
+  mHost(aHost)
+{
+  MOZ_ASSERT(aHost);
+  aHost->EncodedFrameCreated(this);
+}
+
+GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameData& aFrameData,
+                                                   GMPVideoHostImpl* aHost)
+: mEncodedWidth(aFrameData.mEncodedWidth()),
+  mEncodedHeight(aFrameData.mEncodedHeight()),
+  mTimeStamp(aFrameData.mTimeStamp()),
+  mCaptureTime_ms(aFrameData.mCaptureTime_ms()),
+  mFrameType(static_cast<GMPVideoFrameType>(aFrameData.mFrameType())),
+  mSize(aFrameData.mSize()),
+  mCompleteFrame(aFrameData.mCompleteFrame()),
+  mHost(aHost),
+  mBuffer(aFrameData.mBuffer())
+{
+  MOZ_ASSERT(aHost);
+  aHost->EncodedFrameCreated(this);
+}
+
+GMPVideoEncodedFrameImpl::~GMPVideoEncodedFrameImpl()
+{
+  DestroyBuffer();
+  if (mHost) {
+    mHost->EncodedFrameDestroyed(this);
+  }
+}
+
+GMPVideoFrameFormat
+GMPVideoEncodedFrameImpl::GetFrameFormat()
+{
+  return kGMPEncodedVideoFrame;
+}
+
+void
+GMPVideoEncodedFrameImpl::DoneWithAPI()
+{
+  DestroyBuffer();
+
+  // Do this after destroying the buffer because destruction
+  // involves deallocation, which requires a host.
+  mHost = nullptr;
+}
+
+void
+GMPVideoEncodedFrameImpl::ActorDestroyed()
+{
+  // Simply clear out Shmem reference, do not attempt to
+  // properly free it. It has already been freed.
+  mBuffer = ipc::Shmem();
+  // No more host.
+  mHost = nullptr;
+}
+
+bool
+GMPVideoEncodedFrameImpl::RelinquishFrameData(GMPVideoEncodedFrameData& aFrameData)
+{
+  aFrameData.mEncodedWidth() = mEncodedWidth;
+  aFrameData.mEncodedHeight() = mEncodedHeight;
+  aFrameData.mTimeStamp() = mTimeStamp;
+  aFrameData.mCaptureTime_ms() = mCaptureTime_ms;
+  aFrameData.mFrameType() = mFrameType;
+  aFrameData.mSize() = mSize;
+  aFrameData.mCompleteFrame() = mCompleteFrame;
+  aFrameData.mBuffer() = mBuffer;
+
+  // This method is called right before Shmem is sent to another process.
+  // We need to effectively zero out our member copy so that we don't
+  // try to delete Shmem we don't own later.
+  mBuffer = ipc::Shmem();
+
+  return true;
+}
+
+void
+GMPVideoEncodedFrameImpl::DestroyBuffer()
+{
+  if (mHost && mBuffer.IsWritable()) {
+    mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer);
+  }
+  mBuffer = ipc::Shmem();
+}
+
+GMPVideoErr
+GMPVideoEncodedFrameImpl::CreateEmptyFrame(uint32_t aSize)
+{
+  DestroyBuffer();
+
+  if (aSize != 0) {
+    if (!mHost->SharedMemMgr()->MgrAllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &mBuffer) ||
+        !Buffer()) {
+      return GMPVideoAllocErr;
+    }
+  }
+
+  mSize = aSize;
+
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame)
+{
+  auto& f = static_cast<const GMPVideoEncodedFrameImpl&>(aFrame);
+
+  if (f.mSize != 0) {
+    GMPVideoErr err = CreateEmptyFrame(f.mSize);
+    if (err != GMPVideoNoErr) {
+      return err;
+    }
+    memcpy(Buffer(), f.Buffer(), f.mSize);
+  }
+  mEncodedWidth = f.mEncodedWidth;
+  mEncodedHeight = f.mEncodedHeight;
+  mTimeStamp = f.mTimeStamp;
+  mCaptureTime_ms = f.mCaptureTime_ms;
+  mFrameType = f.mFrameType;
+  mSize = f.mSize;
+  mCompleteFrame = f.mCompleteFrame;
+  // Don't copy host, that should have been set properly on object creation via host.
+
+  return GMPVideoNoErr;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetEncodedWidth(uint32_t aEncodedWidth)
+{
+  mEncodedWidth = aEncodedWidth;
+}
+
+uint32_t
+GMPVideoEncodedFrameImpl::EncodedWidth()
+{
+  return mEncodedWidth;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetEncodedHeight(uint32_t aEncodedHeight)
+{
+  mEncodedHeight = aEncodedHeight;
+}
+
+uint32_t
+GMPVideoEncodedFrameImpl::EncodedHeight()
+{
+  return mEncodedHeight;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetTimeStamp(uint32_t aTimeStamp)
+{
+  mTimeStamp = aTimeStamp;
+}
+
+uint32_t
+GMPVideoEncodedFrameImpl::TimeStamp()
+{
+  return mTimeStamp;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetCaptureTime(int64_t aCaptureTime)
+{
+  mCaptureTime_ms = aCaptureTime;
+}
+
+int64_t
+GMPVideoEncodedFrameImpl::CaptureTime()
+{
+  return mCaptureTime_ms;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetFrameType(GMPVideoFrameType aFrameType)
+{
+  mFrameType = aFrameType;
+}
+
+GMPVideoFrameType
+GMPVideoEncodedFrameImpl::FrameType()
+{
+  return mFrameType;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetAllocatedSize(uint32_t aNewSize)
+{
+  if (aNewSize <= AllocatedSize()) {
+    return;
+  }
+
+  if (!mHost) {
+    return;
+  }
+
+  ipc::Shmem new_mem;
+  if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
+      !new_mem.get<uint8_t>()) {
+    return;
+  }
+
+  if (mBuffer.IsReadable()) {
+    memcpy(new_mem.get<uint8_t>(), Buffer(), mSize);
+  }
+
+  DestroyBuffer();
+
+  mBuffer = new_mem;
+}
+
+uint32_t
+GMPVideoEncodedFrameImpl::AllocatedSize()
+{
+  if (mBuffer.IsWritable()) {
+    return mBuffer.Size<uint8_t>();
+  }
+  return 0;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetSize(uint32_t aSize)
+{
+  mSize = aSize;
+}
+
+uint32_t
+GMPVideoEncodedFrameImpl::Size()
+{
+  return mSize;
+}
+
+void
+GMPVideoEncodedFrameImpl::SetCompleteFrame(bool aCompleteFrame)
+{
+  mCompleteFrame = aCompleteFrame;
+}
+
+bool
+GMPVideoEncodedFrameImpl::CompleteFrame()
+{
+  return mCompleteFrame;
+}
+
+const uint8_t*
+GMPVideoEncodedFrameImpl::Buffer() const
+{
+  return mBuffer.get<uint8_t>();
+}
+
+uint8_t*
+GMPVideoEncodedFrameImpl::Buffer()
+{
+  return mBuffer.get<uint8_t>();
+}
+
+void
+GMPVideoEncodedFrameImpl::Destroy()
+{
+  delete this;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncodedFrameImpl.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2014, Mozilla Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMPVideoEncodedFrameImpl_h_
+#define GMPVideoEncodedFrameImpl_h_
+
+#include "gmp-video-errors.h"
+#include "gmp-video-frame.h"
+#include "gmp-video-frame-encoded.h"
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPVideoHostImpl;
+class GMPVideoEncodedFrameData;
+
+class GMPVideoEncodedFrameImpl: public GMPVideoEncodedFrame
+{
+  friend struct IPC::ParamTraits<mozilla::gmp::GMPVideoEncodedFrameImpl>;
+public:
+  GMPVideoEncodedFrameImpl(GMPVideoHostImpl* aHost);
+  GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameData& aFrameData, GMPVideoHostImpl* aHost);
+  virtual ~GMPVideoEncodedFrameImpl();
+
+  // This is called during a normal destroy sequence, which is
+  // when a consumer is finished or during XPCOM shutdown.
+  void DoneWithAPI();
+  // Does not attempt to release Shmem, as the Shmem has already been released.
+  void ActorDestroyed();
+
+  bool RelinquishFrameData(GMPVideoEncodedFrameData& aFrameData);
+
+  // GMPVideoFrame
+  virtual GMPVideoFrameFormat GetFrameFormat() MOZ_OVERRIDE;
+  virtual void Destroy() MOZ_OVERRIDE;
+
+  // GMPVideoEncodedFrame
+  virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) MOZ_OVERRIDE;
+  virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aFrame) MOZ_OVERRIDE;
+  virtual void     SetEncodedWidth(uint32_t aEncodedWidth) MOZ_OVERRIDE;
+  virtual uint32_t EncodedWidth() MOZ_OVERRIDE;
+  virtual void     SetEncodedHeight(uint32_t aEncodedHeight) MOZ_OVERRIDE;
+  virtual uint32_t EncodedHeight() MOZ_OVERRIDE;
+  virtual void     SetTimeStamp(uint32_t aTimeStamp) MOZ_OVERRIDE;
+  virtual uint32_t TimeStamp() MOZ_OVERRIDE;
+  virtual void     SetCaptureTime(int64_t aCaptureTime) MOZ_OVERRIDE;
+  virtual int64_t  CaptureTime() MOZ_OVERRIDE;
+  virtual void     SetFrameType(GMPVideoFrameType aFrameType) MOZ_OVERRIDE;
+  virtual GMPVideoFrameType FrameType() MOZ_OVERRIDE;
+  virtual void     SetAllocatedSize(uint32_t aNewSize) MOZ_OVERRIDE;
+  virtual uint32_t AllocatedSize() MOZ_OVERRIDE;
+  virtual void     SetSize(uint32_t aSize) MOZ_OVERRIDE;
+  virtual uint32_t Size() MOZ_OVERRIDE;
+  virtual void     SetCompleteFrame(bool aCompleteFrame) MOZ_OVERRIDE;
+  virtual bool     CompleteFrame() MOZ_OVERRIDE;
+  virtual const uint8_t* Buffer() const MOZ_OVERRIDE;
+  virtual uint8_t* Buffer() MOZ_OVERRIDE;
+
+private:
+  void DestroyBuffer();
+
+  uint32_t mEncodedWidth;
+  uint32_t mEncodedHeight;
+  uint32_t mTimeStamp;
+  int64_t  mCaptureTime_ms;
+  GMPVideoFrameType mFrameType;
+  uint32_t mSize;
+  bool     mCompleteFrame;
+  GMPVideoHostImpl* mHost;
+  ipc::Shmem mBuffer;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoEncodedFrameImpl_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncoderChild.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoEncoderChild.h"
+#include "GMPChild.h"
+#include <stdio.h>
+#include "mozilla/unused.h"
+#include "GMPVideoEncodedFrameImpl.h"
+#include "GMPVideoi420FrameImpl.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoEncoderChild::GMPVideoEncoderChild(GMPChild* aPlugin)
+: mPlugin(aPlugin),
+  mVideoEncoder(nullptr),
+  mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+  MOZ_ASSERT(mPlugin);
+}
+
+GMPVideoEncoderChild::~GMPVideoEncoderChild()
+{
+}
+
+void
+GMPVideoEncoderChild::Init(GMPVideoEncoder* aEncoder)
+{
+  MOZ_ASSERT(aEncoder, "Cannot initialize video encoder child without a video encoder!");
+  mVideoEncoder = aEncoder;
+}
+
+GMPVideoHostImpl&
+GMPVideoEncoderChild::Host()
+{
+  return mVideoHost;
+}
+
+void
+GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                              const GMPCodecSpecificInfo& aCodecSpecificInfo)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  auto ef = static_cast<GMPVideoEncodedFrameImpl*>(aEncodedFrame);
+
+  GMPVideoEncodedFrameData frameData;
+  ef->RelinquishFrameData(frameData);
+
+  SendEncoded(frameData, aCodecSpecificInfo);
+
+  aEncodedFrame->Destroy();
+}
+
+bool
+GMPVideoEncoderChild::MgrAllocShmem(size_t aSize,
+                                    ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                                    ipc::Shmem* aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  return AllocShmem(aSize, aType, aMem);
+}
+
+bool
+GMPVideoEncoderChild::MgrDeallocShmem(Shmem& aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
+
+  return DeallocShmem(aMem);
+}
+
+bool
+GMPVideoEncoderChild::RecvInitEncode(const GMPVideoCodec& aCodecSettings,
+                                     const int32_t& aNumberOfCores,
+                                     const uint32_t& aMaxPayloadSize)
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->InitEncode(aCodecSettings, this, aNumberOfCores, aMaxPayloadSize);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderChild::RecvEncode(const GMPVideoi420FrameData& aInputFrame,
+                                 const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                                 const InfallibleTArray<int>& aFrameTypes)
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  auto f = new GMPVideoi420FrameImpl(aInputFrame, &mVideoHost);
+
+  std::vector<GMPVideoFrameType> frameTypes(aFrameTypes.Length());
+  for (uint32_t i = 0; i < aFrameTypes.Length(); i++) {
+    frameTypes[i] = static_cast<GMPVideoFrameType>(aFrameTypes[i]);
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->Encode(f, aCodecSpecificInfo, frameTypes);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderChild::RecvSetChannelParameters(const uint32_t& aPacketLoss,
+                                               const uint32_t& aRTT)
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->SetChannelParameters(aPacketLoss, aRTT);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderChild::RecvSetRates(const uint32_t& aNewBitRate,
+                                   const uint32_t& aFrameRate)
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->SetRates(aNewBitRate, aFrameRate);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderChild::RecvSetPeriodicKeyFrames(const bool& aEnable)
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->SetPeriodicKeyFrames(aEnable);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderChild::RecvEncodingComplete()
+{
+  if (!mVideoEncoder) {
+    return false;
+  }
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mVideoEncoder->EncodingComplete();
+
+  mVideoHost.DoneWithAPI();
+
+  mPlugin = nullptr;
+
+  unused << Send__delete__(this);
+
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncoderChild.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoEncoderChild_h_
+#define GMPVideoEncoderChild_h_
+
+#include "nsString.h"
+#include "mozilla/gmp/PGMPVideoEncoderChild.h"
+#include "gmp-video-encode.h"
+#include "GMPSharedMemManager.h"
+#include "GMPVideoHost.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPChild;
+
+class GMPVideoEncoderChild : public PGMPVideoEncoderChild,
+                             public GMPEncoderCallback,
+                             public GMPSharedMemManager
+{
+public:
+  GMPVideoEncoderChild(GMPChild* aPlugin);
+  virtual ~GMPVideoEncoderChild();
+
+  void Init(GMPVideoEncoder* aEncoder);
+  GMPVideoHostImpl& Host();
+
+  // GMPEncoderCallback
+  virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                       const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE;
+
+  // GMPSharedMemManager
+  virtual bool MgrAllocShmem(size_t aSize,
+                             ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                             ipc::Shmem* aMem) MOZ_OVERRIDE;
+  virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE;
+
+private:
+  // PGMPVideoEncoderChild
+  virtual bool RecvInitEncode(const GMPVideoCodec& aCodecSettings,
+                              const int32_t& aNumberOfCores,
+                              const uint32_t& aMaxPayloadSize) MOZ_OVERRIDE;
+  virtual bool RecvEncode(const GMPVideoi420FrameData& aInputFrame,
+                          const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                          const InfallibleTArray<int>& aFrameTypes) MOZ_OVERRIDE;
+  virtual bool RecvSetChannelParameters(const uint32_t& aPacketLoss,
+                                        const uint32_t& aRTT) MOZ_OVERRIDE;
+  virtual bool RecvSetRates(const uint32_t& aNewBitRate,
+                            const uint32_t& aFrameRate) MOZ_OVERRIDE;
+  virtual bool RecvSetPeriodicKeyFrames(const bool& aEnable) MOZ_OVERRIDE;
+  virtual bool RecvEncodingComplete() MOZ_OVERRIDE;
+
+  GMPChild* mPlugin;
+  GMPVideoEncoder* mVideoEncoder;
+  GMPVideoHostImpl mVideoHost;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoEncoderChild_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncoderParent.cpp
@@ -0,0 +1,241 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoEncoderParent.h"
+#include "GMPVideoi420FrameImpl.h"
+#include "GMPVideoEncodedFrameImpl.h"
+#include <stdio.h>
+#include "mozilla/unused.h"
+#include "GMPMessageUtils.h"
+#include "nsAutoRef.h"
+#include "GMPParent.h"
+#include "mozilla/gmp/GMPTypes.h"
+
+template <>
+class nsAutoRefTraits<GMPVideoi420Frame> : public nsPointerRefTraits<GMPVideoi420Frame>
+{
+public:
+  static void Release(GMPVideoi420Frame* aFrame) { aFrame->Destroy(); }
+};
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoEncoderParent::GMPVideoEncoderParent(GMPParent *aPlugin)
+: mCanSendMessages(true),
+  mPlugin(aPlugin),
+  mCallback(nullptr),
+  mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+  MOZ_ASSERT(mPlugin);
+}
+
+GMPVideoEncoderParent::~GMPVideoEncoderParent()
+{
+}
+
+GMPVideoHostImpl&
+GMPVideoEncoderParent::Host()
+{
+  return mVideoHost;
+}
+
+bool
+GMPVideoEncoderParent::MgrAllocShmem(size_t aSize,
+                                     ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                                     ipc::Shmem* aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  return AllocShmem(aSize, aType, aMem);
+}
+
+bool
+GMPVideoEncoderParent::MgrDeallocShmem(Shmem& aMem)
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  return DeallocShmem(aMem);
+}
+
+GMPVideoErr
+GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
+                                  GMPEncoderCallback* aCallback,
+                                  int32_t aNumberOfCores,
+                                  uint32_t aMaxPayloadSize)
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!aCallback) {
+    return GMPVideoGenericErr;
+  }
+  mCallback = aCallback;
+
+  if (!SendInitEncode(aCodecSettings, aNumberOfCores, aMaxPayloadSize)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame,
+                              const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                              const std::vector<GMPVideoFrameType>& aFrameTypes)
+{
+  nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame);
+
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  auto inputFrameImpl = static_cast<GMPVideoi420FrameImpl*>(aInputFrame);
+
+  GMPVideoi420FrameData frameData;
+  inputFrameImpl->InitFrameData(frameData);
+
+  InfallibleTArray<int> frameTypes;
+  frameTypes.SetCapacity(aFrameTypes.size());
+  for (std::vector<int>::size_type i = 0; i != aFrameTypes.size(); i++) {
+    frameTypes.AppendElement(static_cast<int>(aFrameTypes[i]));
+  }
+
+  if (!SendEncode(frameData,
+                  aCodecSpecificInfo,
+                  frameTypes)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT)
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!SendSetChannelParameters(aPacketLoss, aRTT)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!SendSetRates(aNewBitRate, aFrameRate)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable)
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return GMPVideoGenericErr;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  if (!SendSetPeriodicKeyFrames(aEnable)) {
+    return GMPVideoGenericErr;
+  }
+
+  // Async IPC, we don't have access to a return value.
+  return GMPVideoNoErr;
+}
+
+// Note: Consider keeping ActorDestroy sync'd up when making changes here.
+void
+GMPVideoEncoderParent::EncodingComplete()
+{
+  if (!mCanSendMessages) {
+    NS_WARNING("Trying to use an invalid GMP video encoder!");
+    return;
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  mCanSendMessages = false;
+
+  mCallback = nullptr;
+
+  mVideoHost.DoneWithAPI();
+
+  unused << SendEncodingComplete();
+}
+
+// Note: Keep this sync'd up with DecodingComplete
+void
+GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  if (mPlugin) {
+    // Ignore any return code. It is OK for this to fail without killing the process.
+    mPlugin->VideoEncoderDestroyed(this);
+    mPlugin = nullptr;
+  }
+  mCanSendMessages = false;
+  mCallback = nullptr;
+  mVideoHost.ActorDestroyed();
+}
+
+bool
+GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
+                                   const GMPCodecSpecificInfo& aCodecSpecificInfo)
+{
+  if (!mCallback) {
+    return false;
+  }
+
+  auto f = new GMPVideoEncodedFrameImpl(aEncodedFrame, &mVideoHost);
+
+  // Ignore any return code. It is OK for this to fail without killing the process.
+  mCallback->Encoded(f, aCodecSpecificInfo);
+
+  return true;
+}
+
+bool
+GMPVideoEncoderParent::Recv__delete__()
+{
+  if (mPlugin) {
+    // Ignore any return code. It is OK for this to fail without killing the process.
+    mPlugin->VideoEncoderDestroyed(this);
+    mPlugin = nullptr;
+  }
+
+  return true;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoEncoderParent.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoEncoderParent_h_
+#define GMPVideoEncoderParent_h_
+
+#include "mozilla/RefPtr.h"
+#include "gmp-video-encode.h"
+#include "mozilla/gmp/PGMPVideoEncoderParent.h"
+#include "GMPMessageUtils.h"
+#include "GMPSharedMemManager.h"
+#include "GMPVideoHost.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPParent;
+
+class GMPVideoEncoderParent : public GMPVideoEncoder,
+                              public PGMPVideoEncoderParent,
+                              public GMPSharedMemManager
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent)
+
+  GMPVideoEncoderParent(GMPParent *aPlugin);
+
+  GMPVideoHostImpl& Host();
+
+  // GMPSharedMemManager
+  virtual bool MgrAllocShmem(size_t aSize,
+                             ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                             ipc::Shmem* aMem) MOZ_OVERRIDE;
+  virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE;
+
+  // GMPVideoEncoder
+  virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings,
+                                 GMPEncoderCallback* aCallback,
+                                 int32_t aNumberOfCores,
+                                 uint32_t aMaxPayloadSize) MOZ_OVERRIDE;
+  virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame,
+                             const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                             const std::vector<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE;
+  virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE;
+  virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE;
+  virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE;
+  virtual void EncodingComplete() MOZ_OVERRIDE;
+
+private:
+  virtual ~GMPVideoEncoderParent();
+
+  // PGMPVideoEncoderParent
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+  virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
+                           const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE;
+  virtual bool Recv__delete__() MOZ_OVERRIDE;
+
+  bool mCanSendMessages;
+  GMPParent* mPlugin;
+  GMPEncoderCallback* mCallback;
+  GMPVideoHostImpl mVideoHost;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoEncoderParent_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoHost.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoHost.h"
+#include "mozilla/Assertions.h"
+#include "GMPVideoi420FrameImpl.h"
+#include "GMPVideoEncodedFrameImpl.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoHostImpl::GMPVideoHostImpl(GMPSharedMemManager* aSharedMemMgr)
+: mSharedMemMgr(aSharedMemMgr)
+{
+}
+
+GMPVideoHostImpl::~GMPVideoHostImpl()
+{
+}
+
+GMPVideoErr
+GMPVideoHostImpl::CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame)
+{
+  if (!mSharedMemMgr) {
+    return GMPVideoGenericErr;
+  }
+
+  if (!aFrame) {
+    return GMPVideoGenericErr;
+  }
+  *aFrame = nullptr;
+
+  switch (aFormat) {
+    case kGMPI420VideoFrame:
+      *aFrame = new GMPVideoi420FrameImpl(this);
+      return GMPVideoNoErr;
+    case kGMPEncodedVideoFrame:
+      *aFrame = new GMPVideoEncodedFrameImpl(this);
+      return GMPVideoNoErr;
+    default:
+      NS_NOTREACHED("Unknown frame format!");
+  }
+
+  return GMPVideoGenericErr;
+}
+
+GMPVideoErr
+GMPVideoHostImpl::CreatePlane(GMPPlane** aPlane)
+{
+  if (!mSharedMemMgr) {
+    return GMPVideoGenericErr;
+  }
+
+  if (!aPlane) {
+    return GMPVideoGenericErr;
+  }
+  *aPlane = nullptr;
+
+  auto p = new GMPPlaneImpl(this);
+
+  *aPlane = p;
+
+  return GMPVideoNoErr;
+}
+
+GMPSharedMemManager*
+GMPVideoHostImpl::SharedMemMgr()
+{
+  return mSharedMemMgr;
+}
+
+void
+GMPVideoHostImpl::DoneWithAPI()
+{
+  for (uint32_t i = mPlanes.Length(); i > 0; i--) {
+    mPlanes[i - 1]->DoneWithAPI();
+    mPlanes.RemoveElementAt(i - 1);
+  }
+  for (uint32_t i = mEncodedFrames.Length(); i > 0; i--) {
+    mEncodedFrames[i - 1]->DoneWithAPI();
+    mEncodedFrames.RemoveElementAt(i - 1);
+  }
+  mSharedMemMgr = nullptr;
+}
+
+void
+GMPVideoHostImpl::ActorDestroyed()
+{
+  for (uint32_t i = mPlanes.Length(); i > 0; i--) {
+    mPlanes[i - 1]->ActorDestroyed();
+    mPlanes.RemoveElementAt(i - 1);
+  }
+  for (uint32_t i = mEncodedFrames.Length(); i > 0; i--) {
+    mEncodedFrames[i - 1]->ActorDestroyed();
+    mEncodedFrames.RemoveElementAt(i - 1);
+  }
+  mSharedMemMgr = nullptr;  
+}
+
+void
+GMPVideoHostImpl::PlaneCreated(GMPPlaneImpl* aPlane)
+{
+  mPlanes.AppendElement(aPlane);
+}
+
+void
+GMPVideoHostImpl::PlaneDestroyed(GMPPlaneImpl* aPlane)
+{
+  MOZ_ALWAYS_TRUE(mPlanes.RemoveElement(aPlane));
+}
+
+void
+GMPVideoHostImpl::EncodedFrameCreated(GMPVideoEncodedFrameImpl* aEncodedFrame)
+{
+  mEncodedFrames.AppendElement(aEncodedFrame);
+}
+
+void
+GMPVideoHostImpl::EncodedFrameDestroyed(GMPVideoEncodedFrameImpl* aFrame)
+{
+  MOZ_ALWAYS_TRUE(mEncodedFrames.RemoveElement(aFrame));
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoHost.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoHost_h_
+#define GMPVideoHost_h_
+
+#include "gmp-video-host.h"
+#include "gmp-video-plane.h"
+#include "gmp-video-frame.h"
+#include "gmp-video-host.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPSharedMemManager;
+class GMPPlaneImpl;
+class GMPVideoEncodedFrameImpl;
+
+class GMPVideoHostImpl : public GMPVideoHost
+{
+public:
+  GMPVideoHostImpl(GMPSharedMemManager* aSharedMemMgr);
+  virtual ~GMPVideoHostImpl();
+
+  // Used for shared memory allocation and deallocation.
+  GMPSharedMemManager* SharedMemMgr();
+  void DoneWithAPI();
+  void ActorDestroyed();
+  void PlaneCreated(GMPPlaneImpl* aPlane);
+  void PlaneDestroyed(GMPPlaneImpl* aPlane);
+  void EncodedFrameCreated(GMPVideoEncodedFrameImpl* aEncodedFrame);
+  void EncodedFrameDestroyed(GMPVideoEncodedFrameImpl* aFrame);
+
+  // GMPVideoHost
+  virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) MOZ_OVERRIDE;
+  virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) MOZ_OVERRIDE;
+
+private:
+  // All shared memory allocations have to be made by an IPDL actor.
+  // This is a reference to the owning actor. If this reference is
+  // null then the actor has died and all allocations must fail.
+  GMPSharedMemManager* mSharedMemMgr;
+
+  // We track all of these things because they need to handle further
+  // allocations through us and we need to notify them when they
+  // can't use us any more.
+  nsTArray<GMPPlaneImpl*> mPlanes;
+  nsTArray<GMPVideoEncodedFrameImpl*> mEncodedFrames;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoHost_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoPlaneImpl.cpp
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoPlaneImpl.h"
+#include "mozilla/gmp/GMPTypes.h"
+#include "GMPVideoHost.h"
+#include "GMPSharedMemManager.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPPlaneImpl::GMPPlaneImpl(GMPVideoHostImpl* aHost)
+: mSize(0),
+  mStride(0),
+  mHost(aHost)
+{
+  MOZ_ASSERT(mHost);
+  mHost->PlaneCreated(this);
+}
+
+GMPPlaneImpl::GMPPlaneImpl(const GMPPlaneData& aPlaneData, GMPVideoHostImpl* aHost)
+: mBuffer(aPlaneData.mBuffer()),
+  mSize(aPlaneData.mSize()),
+  mStride(aPlaneData.mStride()),
+  mHost(aHost)
+{
+  MOZ_ASSERT(mHost);
+  mHost->PlaneCreated(this);
+}
+
+GMPPlaneImpl::~GMPPlaneImpl()
+{
+  DestroyBuffer();
+  if (mHost) {
+    mHost->PlaneDestroyed(this);
+  }
+}
+
+void
+GMPPlaneImpl::DoneWithAPI()
+{
+  DestroyBuffer();
+
+  // Do this after destroying the buffer because destruction
+  // involves deallocation, which requires a host.
+  mHost = nullptr;
+}
+
+void
+GMPPlaneImpl::ActorDestroyed()
+{
+  // Simply clear out Shmem reference, do not attempt to
+  // properly free it. It has already been freed.
+  mBuffer = ipc::Shmem();
+  // No more host.
+  mHost = nullptr;
+}
+
+bool
+GMPPlaneImpl::InitPlaneData(GMPPlaneData& aPlaneData)
+{
+  aPlaneData.mBuffer() = mBuffer;
+  aPlaneData.mSize() = mSize;
+  aPlaneData.mStride() = mStride;
+
+  // This method is called right before Shmem is sent to another process.
+  // We need to effectively zero out our member copy so that we don't
+  // try to delete memory we don't own later.
+  mBuffer = ipc::Shmem();
+
+  return true;
+}
+
+GMPVideoErr
+GMPPlaneImpl::MaybeResize(int32_t aNewSize) {
+  if (aNewSize <= AllocatedSize()) {
+    return GMPVideoNoErr;
+  }
+
+  if (!mHost) {
+    return GMPVideoGenericErr;
+  }
+
+  ipc::Shmem new_mem;
+  if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
+      !new_mem.get<uint8_t>()) {
+    return GMPVideoAllocErr;
+  }
+
+  if (mBuffer.IsReadable()) {
+    memcpy(new_mem.get<uint8_t>(), Buffer(), mSize);
+  }
+
+  DestroyBuffer();
+
+  mBuffer = new_mem;
+
+  return GMPVideoNoErr;
+}
+
+void
+GMPPlaneImpl::DestroyBuffer()
+{
+  if (mHost && mBuffer.IsWritable()) {
+    mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer);
+  }
+  mBuffer = ipc::Shmem();
+}
+
+GMPVideoErr
+GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize)
+{
+  if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) {
+    return GMPVideoGenericErr;
+  }
+
+  GMPVideoErr err = MaybeResize(aAllocatedSize);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  mSize = aPlaneSize;
+  mStride = aStride;
+
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPPlaneImpl::Copy(const GMPPlane& aPlane)
+{
+  auto& planeimpl = static_cast<const GMPPlaneImpl&>(aPlane);
+
+  GMPVideoErr err = MaybeResize(planeimpl.mSize);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  if (planeimpl.Buffer() && planeimpl.mSize > 0) {
+    memcpy(Buffer(), planeimpl.Buffer(), mSize);
+  }
+
+  mSize = planeimpl.mSize;
+  mStride = planeimpl.mStride;
+
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer)
+{
+  GMPVideoErr err = MaybeResize(aSize);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  if (aBuffer && aSize > 0) {
+    memcpy(Buffer(), aBuffer, aSize);
+  }
+
+  mSize = aSize;
+  mStride = aStride;
+
+  return GMPVideoNoErr;
+}
+
+void
+GMPPlaneImpl::Swap(GMPPlane& aPlane)
+{
+  auto& planeimpl = static_cast<GMPPlaneImpl&>(aPlane);
+
+  std::swap(mStride, planeimpl.mStride);
+  std::swap(mSize, planeimpl.mSize);
+  std::swap(mBuffer, planeimpl.mBuffer);
+}
+
+int32_t
+GMPPlaneImpl::AllocatedSize() const
+{
+  if (mBuffer.IsWritable()) {
+    return mBuffer.Size<uint8_t>();
+  }
+  return 0;
+}
+
+void
+GMPPlaneImpl::ResetSize()
+{
+  mSize = 0;
+}
+
+bool
+GMPPlaneImpl::IsZeroSize() const
+{
+  return (mSize == 0);
+}
+
+int32_t
+GMPPlaneImpl::Stride() const
+{
+  return mStride;
+}
+
+const uint8_t*
+GMPPlaneImpl::Buffer() const
+{
+  return mBuffer.get<uint8_t>();
+}
+
+uint8_t*
+GMPPlaneImpl::Buffer()
+{
+  return mBuffer.get<uint8_t>();
+}
+
+void
+GMPPlaneImpl::Destroy()
+{
+  delete this;
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoPlaneImpl.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoPlaneImpl_h_
+#define GMPVideoPlaneImpl_h_
+
+#include "gmp-video-plane.h"
+#include "mozilla/ipc/Shmem.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPVideoHostImpl;
+class GMPPlaneData;
+
+class GMPPlaneImpl : public GMPPlane
+{
+  friend struct IPC::ParamTraits<mozilla::gmp::GMPPlaneImpl>;
+public:
+  GMPPlaneImpl(GMPVideoHostImpl* aHost);
+  GMPPlaneImpl(const GMPPlaneData& aPlaneData, GMPVideoHostImpl* aHost);
+  virtual ~GMPPlaneImpl();
+
+  // This is called during a normal destroy sequence, which is
+  // when a consumer is finished or during XPCOM shutdown.
+  void DoneWithAPI();
+  // This is called when something has gone wrong - specicifically,
+  // a child process has crashed. Does not attempt to release Shmem,
+  // as the Shmem has already been released.
+  void ActorDestroyed();
+
+  bool InitPlaneData(GMPPlaneData& aPlaneData);
+
+  // GMPPlane
+  virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize,
+                                       int32_t aStride,
+                                       int32_t aPlaneSize) MOZ_OVERRIDE;
+  virtual GMPVideoErr Copy(const GMPPlane& aPlane) MOZ_OVERRIDE;
+  virtual GMPVideoErr Copy(int32_t aSize,
+                           int32_t aStride,
+                           const uint8_t* aBuffer) MOZ_OVERRIDE;
+  virtual void Swap(GMPPlane& aPlane) MOZ_OVERRIDE;
+  virtual int32_t AllocatedSize() const MOZ_OVERRIDE;
+  virtual void ResetSize() MOZ_OVERRIDE;
+  virtual bool IsZeroSize() const MOZ_OVERRIDE;
+  virtual int32_t Stride() const MOZ_OVERRIDE;
+  virtual const uint8_t* Buffer() const MOZ_OVERRIDE;
+  virtual uint8_t* Buffer() MOZ_OVERRIDE;
+  virtual void Destroy() MOZ_OVERRIDE;
+
+private:
+  GMPVideoErr MaybeResize(int32_t aNewSize);
+  void DestroyBuffer();
+
+  ipc::Shmem mBuffer;
+  int32_t mSize;
+  int32_t mStride;
+  GMPVideoHostImpl* mHost;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoPlaneImpl_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoi420FrameImpl.cpp
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "GMPVideoi420FrameImpl.h"
+#include "mozilla/gmp/GMPTypes.h"
+
+namespace mozilla {
+namespace gmp {
+
+GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost)
+: mYPlane(aHost),
+  mUPlane(aHost),
+  mVPlane(aHost),
+  mWidth(0),
+  mHeight(0),
+  mTimestamp(0),
+  mRenderTime_ms(0)
+{
+  MOZ_ASSERT(aHost);
+}
+
+GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData,
+                                             GMPVideoHostImpl* aHost)
+: mYPlane(aFrameData.mYPlane(), aHost),
+  mUPlane(aFrameData.mUPlane(), aHost),
+  mVPlane(aFrameData.mVPlane(), aHost),
+  mWidth(aFrameData.mWidth()),
+  mHeight(aFrameData.mHeight()),
+  mTimestamp(aFrameData.mTimestamp()),
+  mRenderTime_ms(aFrameData.mRenderTime_ms())
+{
+  MOZ_ASSERT(aHost);
+}
+
+GMPVideoi420FrameImpl::~GMPVideoi420FrameImpl()
+{
+}
+
+bool
+GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData)
+{
+  mYPlane.InitPlaneData(aFrameData.mYPlane());
+  mUPlane.InitPlaneData(aFrameData.mUPlane());
+  mVPlane.InitPlaneData(aFrameData.mVPlane());
+  aFrameData.mWidth() = mWidth;
+  aFrameData.mHeight() = mHeight;
+  aFrameData.mTimestamp() = mTimestamp;
+  aFrameData.mRenderTime_ms() = mRenderTime_ms;
+  return true;
+}
+
+GMPVideoFrameFormat
+GMPVideoi420FrameImpl::GetFrameFormat()
+{
+  return kGMPI420VideoFrame;
+}
+
+void
+GMPVideoi420FrameImpl::Destroy()
+{
+  delete this;
+}
+
+bool
+GMPVideoi420FrameImpl::CheckDimensions(int32_t aWidth, int32_t aHeight,
+                                       int32_t aStride_y, int32_t aStride_u, int32_t aStride_v)
+{
+  int32_t half_width = (aWidth + 1) / 2;
+  if (aWidth < 1 || aHeight < 1 || aStride_y < aWidth ||
+                                   aStride_u < half_width ||
+                                   aStride_v < half_width) {
+    return false;
+  }
+  return true;
+}
+
+const GMPPlaneImpl*
+GMPVideoi420FrameImpl::GetPlane(GMPPlaneType aType) const {
+  switch (aType) {
+    case kGMPYPlane:
+      return &mYPlane;
+    case kGMPUPlane:
+      return &mUPlane;
+    case kGMPVPlane:
+      return &mVPlane;
+    default:
+      MOZ_CRASH("Unknown plane type!");
+  }
+  return nullptr;
+}
+
+GMPPlaneImpl*
+GMPVideoi420FrameImpl::GetPlane(GMPPlaneType aType) {
+  switch (aType) {
+    case kGMPYPlane :
+      return &mYPlane;
+    case kGMPUPlane :
+      return &mUPlane;
+    case kGMPVPlane :
+      return &mVPlane;
+    default:
+      MOZ_CRASH("Unknown plane type!");
+  }
+  return nullptr;
+}
+
+GMPVideoErr
+GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight,
+                                        int32_t aStride_y, int32_t aStride_u, int32_t aStride_v)
+{
+  if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) {
+    return GMPVideoGenericErr;
+  }
+
+  int32_t size_y = aStride_y * aHeight;
+  int32_t half_height = (aHeight + 1) / 2;
+  int32_t size_u = aStride_u * half_height;
+  int32_t size_v = aStride_v * half_height;
+
+  GMPVideoErr err = mYPlane.CreateEmptyPlane(size_y, aStride_y, size_y);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+  err = mUPlane.CreateEmptyPlane(size_u, aStride_u, size_u);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+  err = mVPlane.CreateEmptyPlane(size_v, aStride_v, size_v);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  mWidth = aWidth;
+  mHeight = aHeight;
+  mTimestamp = 0;
+  mRenderTime_ms = 0;
+
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoi420FrameImpl::CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
+                                   int32_t aSize_u, const uint8_t* aBuffer_u,
+                                   int32_t aSize_v, const uint8_t* aBuffer_v,
+                                   int32_t aWidth, int32_t aHeight,
+                                   int32_t aStride_y, int32_t aStride_u, int32_t aStride_v)
+{
+  MOZ_ASSERT(aBuffer_y);
+  MOZ_ASSERT(aBuffer_u);
+  MOZ_ASSERT(aBuffer_v);
+
+  if (aSize_y < 1 || aSize_u < 1 || aSize_v < 1) {
+    return GMPVideoGenericErr;
+  }
+
+  if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) {
+    return GMPVideoGenericErr;
+  }
+
+  GMPVideoErr err = mYPlane.Copy(aSize_y, aStride_y, aBuffer_y);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+  err = mUPlane.Copy(aSize_u, aStride_u, aBuffer_u);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+  err = mVPlane.Copy(aSize_v, aStride_v, aBuffer_v);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  mWidth = aWidth;
+  mHeight = aHeight;
+
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoi420FrameImpl::CopyFrame(const GMPVideoi420Frame& aFrame)
+{
+  auto& f = static_cast<const GMPVideoi420FrameImpl&>(aFrame);
+
+  GMPVideoErr err = mYPlane.Copy(f.mYPlane);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  err = mUPlane.Copy(f.mUPlane);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  err = mVPlane.Copy(f.mVPlane);
+  if (err != GMPVideoNoErr) {
+    return err;
+  }
+
+  mWidth = f.mWidth;
+  mHeight = f.mHeight;
+  mTimestamp = f.mTimestamp;
+  mRenderTime_ms = f.mRenderTime_ms;
+
+  return GMPVideoNoErr;
+}
+
+void
+GMPVideoi420FrameImpl::SwapFrame(GMPVideoi420Frame* aFrame)
+{
+  auto f = static_cast<GMPVideoi420FrameImpl*>(aFrame);
+  mYPlane.Swap(f->mYPlane);
+  mUPlane.Swap(f->mUPlane);
+  mVPlane.Swap(f->mVPlane);
+  std::swap(mWidth, f->mWidth);
+  std::swap(mHeight, f->mHeight);
+  std::swap(mTimestamp, f->mTimestamp);
+  std::swap(mRenderTime_ms, f->mRenderTime_ms);
+}
+
+uint8_t*
+GMPVideoi420FrameImpl::Buffer(GMPPlaneType aType)
+{
+  GMPPlane* p = GetPlane(aType);
+  if (p) {
+    return p->Buffer();
+  }
+  return nullptr;
+}
+
+const uint8_t*
+GMPVideoi420FrameImpl::Buffer(GMPPlaneType aType) const
+{
+ const GMPPlane* p = GetPlane(aType);
+  if (p) {
+    return p->Buffer();
+  }
+  return nullptr;
+}
+
+int32_t
+GMPVideoi420FrameImpl::AllocatedSize(GMPPlaneType aType) const
+{
+  const GMPPlane* p = GetPlane(aType);
+  if (p) {
+    return p->AllocatedSize();
+  }
+  return -1;
+}
+
+int32_t
+GMPVideoi420FrameImpl::Stride(GMPPlaneType aType) const
+{
+  const GMPPlane* p = GetPlane(aType);
+  if (p) {
+    return p->Stride();
+  }
+  return -1;
+}
+
+GMPVideoErr
+GMPVideoi420FrameImpl::SetWidth(int32_t aWidth)
+{
+  if (!CheckDimensions(aWidth, mHeight,
+                       mYPlane.Stride(), mUPlane.Stride(),
+                       mVPlane.Stride())) {
+    return GMPVideoGenericErr;
+  }
+  mWidth = aWidth;
+  return GMPVideoNoErr;
+}
+
+GMPVideoErr
+GMPVideoi420FrameImpl::SetHeight(int32_t aHeight)
+{
+  if (!CheckDimensions(mWidth, aHeight,
+                       mYPlane.Stride(), mUPlane.Stride(),
+                       mVPlane.Stride())) {
+    return GMPVideoGenericErr;
+  }
+  mHeight = aHeight;
+  return GMPVideoNoErr;
+}
+
+int32_t
+GMPVideoi420FrameImpl::Width() const
+{
+  return mWidth;
+}
+
+int32_t
+GMPVideoi420FrameImpl::Height() const
+{
+  return mHeight;
+}
+
+void
+GMPVideoi420FrameImpl::SetTimestamp(uint32_t aTimestamp)
+{
+  mTimestamp = aTimestamp;
+}
+
+uint32_t
+GMPVideoi420FrameImpl::Timestamp() const
+{
+  return mTimestamp;
+}
+
+void
+GMPVideoi420FrameImpl::SetRenderTime_ms(int64_t aRenderTime_ms)
+{
+  mRenderTime_ms = aRenderTime_ms;
+}
+
+int64_t
+GMPVideoi420FrameImpl::RenderTime_ms() const
+{
+  return mRenderTime_ms;
+}
+
+bool
+GMPVideoi420FrameImpl::IsZeroSize() const
+{
+  return (mYPlane.IsZeroSize() && mUPlane.IsZeroSize() && mVPlane.IsZeroSize());
+}
+
+void
+GMPVideoi420FrameImpl::ResetSize()
+{
+  mYPlane.ResetSize();
+  mUPlane.ResetSize();
+  mVPlane.ResetSize();
+}
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPVideoi420FrameImpl.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#ifndef GMPVideoi420FrameImpl_h_
+#define GMPVideoi420FrameImpl_h_
+
+#include "gmp-video-frame-i420.h"
+#include "mozilla/ipc/Shmem.h"
+#include "GMPVideoPlaneImpl.h"
+
+namespace mozilla {
+namespace gmp {
+
+class GMPVideoi420FrameData;
+
+class GMPVideoi420FrameImpl : public GMPVideoi420Frame
+{
+  friend struct IPC::ParamTraits<mozilla::gmp::GMPVideoi420FrameImpl>;
+public:
+  GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost);
+  GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData, GMPVideoHostImpl* aHost);
+  virtual ~GMPVideoi420FrameImpl();
+
+  bool InitFrameData(GMPVideoi420FrameData& aFrameData);
+  const GMPPlaneImpl* GetPlane(GMPPlaneType aType) const;
+  GMPPlaneImpl* GetPlane(GMPPlaneType aType);
+
+  // GMPVideoFrame
+  virtual GMPVideoFrameFormat GetFrameFormat() MOZ_OVERRIDE;
+  virtual void Destroy() MOZ_OVERRIDE;
+
+  // GMPVideoi420Frame
+  virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth,
+                                       int32_t aHeight,
+                                       int32_t aStride_y,
+                                       int32_t aStride_u,
+                                       int32_t aStride_v) MOZ_OVERRIDE;
+  virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
+                                  int32_t aSize_u, const uint8_t* aBuffer_u,
+                                  int32_t aSize_v, const uint8_t* aBuffer_v,
+                                  int32_t aWidth,
+                                  int32_t aHeight,
+                                  int32_t aStride_y,
+                                  int32_t aStride_u,
+                                  int32_t aStride_v) MOZ_OVERRIDE;
+  virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aFrame) MOZ_OVERRIDE;
+  virtual void SwapFrame(GMPVideoi420Frame* aFrame) MOZ_OVERRIDE;
+  virtual uint8_t* Buffer(GMPPlaneType aType) MOZ_OVERRIDE;
+  virtual const uint8_t* Buffer(GMPPlaneType aType) const MOZ_OVERRIDE;
+  virtual int32_t AllocatedSize(GMPPlaneType aType) const MOZ_OVERRIDE;
+  virtual int32_t Stride(GMPPlaneType aType) const MOZ_OVERRIDE;
+  virtual GMPVideoErr SetWidth(int32_t aWidth) MOZ_OVERRIDE;
+  virtual GMPVideoErr SetHeight(int32_t aHeight) MOZ_OVERRIDE;
+  virtual int32_t Width() const MOZ_OVERRIDE;
+  virtual int32_t Height() const MOZ_OVERRIDE;
+  virtual void SetTimestamp(uint32_t aTimestamp) MOZ_OVERRIDE;
+  virtual uint32_t Timestamp() const MOZ_OVERRIDE;
+  virtual void SetRenderTime_ms(int64_t aRenderTime_ms) MOZ_OVERRIDE;
+  virtual int64_t RenderTime_ms() const MOZ_OVERRIDE;
+  virtual bool IsZeroSize() const MOZ_OVERRIDE;
+  virtual void ResetSize() MOZ_OVERRIDE;
+
+private:
+  bool CheckDimensions(int32_t aWidth, int32_t aHeight,
+                       int32_t aStride_y, int32_t aStride_u, int32_t aStride_v);
+
+  GMPPlaneImpl mYPlane;
+  GMPPlaneImpl mUPlane;
+  GMPPlaneImpl mVPlane;
+  int32_t mWidth;
+  int32_t mHeight;
+  uint32_t mTimestamp;
+  int64_t mRenderTime_ms;
+};
+
+} // namespace gmp
+} // namespace mozilla
+
+#endif // GMPVideoi420FrameImpl_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/PGMP.ipdl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 protocol PGMPVideoDecoder;
+include protocol PGMPVideoEncoder;
+
+namespace mozilla {
+namespace gmp {
+
+async protocol PGMP
+{
+  manages PGMPVideoDecoder;
+  manages PGMPVideoEncoder;
+child:
+  PGMPVideoDecoder();
+  PGMPVideoEncoder();
+};
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/PGMPVideoDecoder.ipdl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 protocol PGMP;
+include GMPTypes;
+
+using GMPVideoCodec from "gmp-video-codec.h";
+using GMPCodecSpecificInfo from "gmp-video-codec.h";
+
+include "GMPMessageUtils.h";
+
+namespace mozilla {
+namespace gmp {
+
+async protocol PGMPVideoDecoder
+{
+  manager PGMP;
+child:
+  InitDecode(GMPVideoCodec aCodecSettings,
+             int32_t aCoreCount);
+  Decode(GMPVideoEncodedFrameData aInputFrame,
+         bool aMissingFrames,
+         GMPCodecSpecificInfo aCodecSpecificInfo,
+         int64_t aRenderTimeMs);
+  Reset();
+  Drain();
+  DecodingComplete();
+parent:
+  __delete__();
+  Decoded(GMPVideoi420FrameData aDecodedFrame);
+  ReceivedDecodedReferenceFrame(uint64_t aPictureId);
+  ReceivedDecodedFrame(uint64_t aPictureId);
+  InputDataExhausted();
+};
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/PGMPVideoEncoder.ipdl
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 protocol PGMP;
+include GMPTypes;
+
+using GMPVideoCodec from "gmp-video-codec.h";
+using GMPCodecSpecificInfo from "gmp-video-codec.h";
+
+include "GMPMessageUtils.h";
+
+namespace mozilla {
+namespace gmp {
+
+async protocol PGMPVideoEncoder
+{
+  manager PGMP;
+child:
+  InitEncode(GMPVideoCodec aCodecSettings,
+             int32_t aNumberOfCores,
+             uint32_t aMaxPayloadSize);
+  Encode(GMPVideoi420FrameData aInputFrame,
+         GMPCodecSpecificInfo aCodecSpecificInfo,
+         int[] aFrameTypes);
+  SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT);
+  SetRates(uint32_t aNewBitRate, uint32_t aFrameRate);
+  SetPeriodicKeyFrames(bool aEnable);
+  EncodingComplete();
+
+parent:
+  __delete__();
+  Encoded(GMPVideoEncodedFrameData aEncodedFrame,
+          GMPCodecSpecificInfo aCodecSpecificInfo);
+};
+
+} // namespace gmp
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/README.txt
@@ -0,0 +1,1 @@
+This directory contains code supporting Gecko Media Plugins (GMPs). The GMP API is not the same thing as the Media Plugin API (MPAPI).
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-entrypoints.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_ENTRYPOINTS_h_
+#define GMP_ENTRYPOINTS_h_
+
+#include "gmp-errors.h"
+#include "gmp-platform.h"
+
+/* C functions exposed by Gecko Media Plugin shared library. */
+
+// GMPInit
+// - Called once after plugin library is loaded, before GMPGetAPI or GMPShutdown are called.
+// - Called on main thread.
+// - 'aPlatformAPI' is a structure containing platform-provided APIs. It is valid until
+//   'GMPShutdown' is called. Owned and must be deleted by plugin.
+typedef GMPErr (*GMPInitFunc)(const GMPPlatformAPI* aPlatformAPI);
+
+// GMPGetAPI
+// - Called when host wants to use an API.
+// - Called on main thread.
+// - 'aAPIName' is a string indicating the API being requested.
+// - 'aHostAPI' is the host API which is specific to the API being requested
+//   from the plugin. It is valid so long as the API object requested from the
+//   plugin is valid. It is owned by the host, plugin should not attempt to delete.
+//   May be null.
+// - 'aPluginAPI' is for returning the requested API. Destruction of the requsted
+//   API object is defined by the API.
+typedef GMPErr (*GMPGetAPIFunc)(const char* aAPIName, void* aHostAPI, void** aPluginAPI);
+
+// GMPShutdown
+// - Called once before exiting process (unloading library).
+// - Called on main thread.
+typedef void   (*GMPShutdownFunc)(void);
+
+#endif // GMP_ENTRYPOINTS_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-errors.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_ERRORS_h_
+#define GMP_ERRORS_h_
+
+typedef enum {
+  GMPNoErr = 0,
+  GMPGenericErr = 1
+} GMPErr;
+
+#endif // GMP_ERRORS_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-platform.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_PLATFORM_h_
+#define GMP_PLATFORM_h_
+
+#include "gmp-errors.h"
+#include <stdint.h>
+
+/* Platform helper API. */
+
+class GMPTask {
+public:
+  virtual ~GMPTask() {}
+  virtual void Run() = 0;
+};
+
+class GMPThread {
+public:
+  virtual ~GMPThread() {}
+  virtual void Post(GMPTask* aTask) = 0;
+  virtual void Join() = 0;
+};
+
+class GMPMutex {
+public:
+  virtual ~GMPMutex() {}
+  virtual void Acquire() = 0;
+  virtual void Release() = 0;
+};
+
+typedef GMPErr (*GMPCreateThreadPtr)(GMPThread** aThread);
+typedef GMPErr (*GMPRunOnMainThreadPtr)(GMPTask* aTask);
+typedef GMPErr (*GMPSyncRunOnMainThreadPtr)(GMPTask* aTask);
+typedef GMPErr (*GMPCreateMutexPtr)(GMPMutex** aMutex);
+
+struct GMPPlatformAPI {
+  // Increment the version when things change. Can only add to the struct,
+  // do not change what already exists. Pointers to functions may be NULL
+  // when passed to plugins, but beware backwards compat implications of
+  // doing that.
+  uint16_t version; // Currently version 0
+
+  GMPCreateThreadPtr createthread;
+  GMPRunOnMainThreadPtr runonmainthread;
+  GMPSyncRunOnMainThreadPtr syncrunonmainthread;
+  GMPCreateMutexPtr createmutex;
+};
+
+#endif // GMP_PLATFORM_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-codec.h
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_CODEC_h_
+#define GMP_VIDEO_CODEC_h_
+
+#include <stdint.h>
+
+enum { kGMPPayloadNameSize = 32};
+enum { kGMPMaxSimulcastStreams = 4};
+
+enum GMPVideoCodecComplexity
+{
+  kGMPComplexityNormal = 0,
+  kGMPComplexityHigh = 1,
+  kGMPComplexityHigher = 2,
+  kGMPComplexityMax = 3,
+  kGMPComplexityInvalid // Should always be last
+};
+
+enum GMPVP8ResilienceMode {
+  kResilienceOff,    // The stream produced by the encoder requires a
+                     // recovery frame (typically a key frame) to be
+                     // decodable after a packet loss.
+  kResilientStream,  // A stream produced by the encoder is resilient to
+                     // packet losses, but packets within a frame subsequent
+                     // to a loss can't be decoded.
+  kResilientFrames,  // Same as kResilientStream but with added resilience
+                     // within a frame.
+  kResilienceInvalid // Should always be last.
+};
+
+// VP8 specific
+struct GMPVideoCodecVP8
+{
+  bool mPictureLossIndicationOn;
+  bool mFeedbackModeOn;
+  GMPVideoCodecComplexity mComplexity;
+  GMPVP8ResilienceMode mResilience;
+  uint32_t mNumberOfTemporalLayers;
+  bool mDenoisingOn;
+  bool mErrorConcealmentOn;
+  bool mAutomaticResizeOn;
+  bool mFrameDroppingOn;
+  int32_t mKeyFrameInterval;
+};
+
+enum GMPVideoCodecType
+{
+  kGMPVideoCodecVP8,
+  kGMPVideoCodecInvalid // Should always be last.
+};
+
+union GMPVideoCodecUnion
+{
+  GMPVideoCodecVP8 mVP8;
+};
+
+// Simulcast is when the same stream is encoded multiple times with different
+// settings such as resolution.
+struct GMPSimulcastStream
+{
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mNumberOfTemporalLayers;
+  uint32_t mMaxBitrate; // kilobits/sec.
+  uint32_t mTargetBitrate; // kilobits/sec.
+  uint32_t mMinBitrate; // kilobits/sec.
+  uint32_t mQPMax; // minimum quality
+};
+
+enum GMPVideoCodecMode {
+  kGMPRealtimeVideo,
+  kGMPScreensharing,
+  kGMPCodecModeInvalid // Should always be last.
+};
+
+struct GMPVideoCodec
+{
+  GMPVideoCodecType mCodecType;
+  char mPLName[kGMPPayloadNameSize]; // Must be NULL-terminated!
+  uint32_t mPLType;
+
+  uint32_t mWidth;
+  uint32_t mHeight;
+
+  uint32_t mStartBitrate; // kilobits/sec.
+  uint32_t mMaxBitrate; // kilobits/sec.
+  uint32_t mMinBitrate; // kilobits/sec.
+  uint32_t mMaxFramerate;
+
+  GMPVideoCodecUnion mCodecSpecific;
+
+  uint32_t mQPMax;
+  uint32_t mNumberOfSimulcastStreams;
+  GMPSimulcastStream mSimulcastStream[kGMPMaxSimulcastStreams];
+
+  GMPVideoCodecMode mMode;
+};
+
+struct GMPCodecSpecificInfoGeneric {
+  uint8_t mSimulcastIdx;
+};
+
+// Note: if any pointers are added to this struct, it must be fitted
+// with a copy-constructor. See below.
+struct GMPCodecSpecificInfoVP8
+{
+  bool mHasReceivedSLI;
+  uint8_t mPictureIdSLI;
+  bool mHasReceivedRPSI;
+  uint64_t mPictureIdRPSI;
+  int16_t mPictureId; // negative value to skip pictureId
+  bool mNonReference;
+  uint8_t mSimulcastIdx;
+  uint8_t mTemporalIdx;
+  bool mLayerSync;
+  int32_t mTL0PicIdx; // negative value to skip tl0PicIdx
+  int8_t mKeyIdx; // negative value to skip keyIdx
+};
+
+union GMPCodecSpecificInfoUnion
+{
+  GMPCodecSpecificInfoGeneric mGeneric;
+  GMPCodecSpecificInfoVP8 mVP8;
+};
+
+// Note: if any pointers are added to this struct or its sub-structs, it
+// must be fitted with a copy-constructor. This is because it is copied
+// in the copy-constructor of VCMEncodedFrame.
+struct GMPCodecSpecificInfo
+{
+  GMPVideoCodecType mCodecType;
+  GMPCodecSpecificInfoUnion mCodecSpecific;
+};
+
+#endif // GMP_VIDEO_CODEC_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-decode.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_DECODE_h_
+#define GMP_VIDEO_DECODE_h_
+
+#include "gmp-video-errors.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+#include "gmp-video-codec.h"
+#include <stdint.h>
+
+// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
+class GMPDecoderCallback
+{
+public:
+  virtual ~GMPDecoderCallback() {}
+
+  virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) = 0;
+
+  virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) = 0;
+
+  virtual void ReceivedDecodedFrame(const uint64_t aPictureId) = 0;
+
+  virtual void InputDataExhausted() = 0;
+};
+
+// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
+class GMPVideoDecoder
+{
+public:
+  virtual ~GMPVideoDecoder() {}
+
+  // aCallback: Subclass should retain reference to it until DecodingComplete
+  //            is called. Do not attempt to delete it, host retains ownership.
+  virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings,
+                                 GMPDecoderCallback* aCallback,
+                                 int32_t aCoreCount) = 0;
+
+  // Decode encoded frame (as a part of a video stream). The decoded frame
+  // will be returned to the user through the decode complete callback.
+  //
+  // inputFrame:        Frame to decode.
+  //
+  // missingFrames:     True if one or more frames have been lost since the previous decode call.
+  //
+  // fragmentation:     Specifies where the encoded frame can be split into separate fragments.
+  //                    The meaning of fragment is codec specific, but often means that each
+  //                    fragment is decodable by itself.
+  //
+  // codecSpecificInfo: Codec-specific data
+  //
+  // renderTimeMs :     System time to render in milliseconds. Only used by decoders with internal
+  //                    rendering.
+  virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame,
+                             bool aMissingFrames,
+                             const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                             int64_t aRenderTimeMs = -1) = 0;
+
+  // Reset decoder state and prepare for a new call to Decode(...). Flushes the decoder pipeline.
+  virtual GMPVideoErr Reset() = 0;
+
+  // Output decoded frames for any data in the pipeline, regardless of ordering.
+  virtual GMPVideoErr Drain() = 0;
+
+  // May free decoder memory.
+  virtual void DecodingComplete() = 0;
+};
+
+#endif // GMP_VIDEO_DECODE_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-encode.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_ENCODE_h_
+#define GMP_VIDEO_ENCODE_h_
+
+#include <vector>
+#include <stdint.h>
+
+#include "gmp-video-errors.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+#include "gmp-video-codec.h"
+
+// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
+class GMPEncoderCallback
+{
+public:
+  virtual ~GMPEncoderCallback() {}
+
+  virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
+                       const GMPCodecSpecificInfo& aCodecSpecificInfo) = 0;
+};
+
+// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
+class GMPVideoEncoder
+{
+public:
+  virtual ~GMPVideoEncoder() {}
+
+  // Initialize the encoder with the information from the VideoCodec.
+  //
+  // Input:
+  // - codecSettings : Codec settings
+  // - aCallback: Subclass should retain reference to it until EncodingComplete
+  //              is called. Do not attempt to delete it, host retains ownership.
+  // - numberOfCores : Number of cores available for the encoder
+  // - maxPayloadSize : The maximum size each payload is allowed
+  //                    to have. Usually MTU - overhead.
+  virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings,
+                                 GMPEncoderCallback* aCallback,
+                                 int32_t aNumberOfCores,
+                                 uint32_t aMaxPayloadSize) = 0;
+
+  // Encode an I420 frame (as a part of a video stream). The encoded frame
+  // will be returned to the user through the encode complete callback.
+  //
+  // Input:
+  // - inputFrame : Frame to be encoded
+  // - codecSpecificInfo : Pointer to codec specific data
+  // - frame_types : The frame type to encode
+  virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame,
+                             const GMPCodecSpecificInfo& aCodecSpecificInfo,
+                             const std::vector<GMPVideoFrameType>& aFrameTypes) = 0;
+
+  // Inform the encoder about the packet loss and round trip time on the
+  // network used to decide the best pattern and signaling.
+  //
+  // - packetLoss : Fraction lost (loss rate in percent =
+  // 100 * packetLoss / 255)
+  // - rtt : Round-trip time in milliseconds
+  virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0;
+
+  // Inform the encoder about the new target bit rate.
+  //
+  // - newBitRate : New target bit rate
+  // - frameRate : The target frame rate
+  virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0;
+
+  // Use this function to enable or disable periodic key frames. Can be useful for codecs
+  // which have other ways of stopping error propagation.
+  //
+  // - enable : Enable or disable periodic key frames
+  virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) = 0;
+
+  // May free Encoder memory.
+  virtual void EncodingComplete() = 0;
+};
+
+#endif // GMP_VIDEO_ENCODE_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-errors.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_ERRORS_h_
+#define GMP_VIDEO_ERRORS_h_
+
+enum GMPVideoErr {
+  GMPVideoNoErr = 0,
+  GMPVideoGenericErr = 1,
+  GMPVideoAllocErr = 2
+};
+
+#endif // GMP_VIDEO_ERRORS_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-frame-encoded.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_FRAME_ENCODED_h_
+#define GMP_VIDEO_FRAME_ENCODED_h_
+
+#include <stdint.h>
+
+enum GMPVideoFrameType
+{
+  kGMPKeyFrame = 0,
+  kGMPDeltaFrame = 1,
+  kGMPGoldenFrame = 2,
+  kGMPAltRefFrame = 3,
+  kGMPSkipFrame = 4
+};
+
+// The implementation backing this interface uses shared memory for the
+// buffer(s). This means it can only be used by the "owning" process.
+// At first the process which created the object owns it. When the object
+// is passed to an interface the creator loses ownership and must Destroy()
+// the object. Further attempts to use it may fail due to not being able to
+// access the underlying buffer(s).
+//
+// Methods that create or destroy shared memory must be called on the main
+// thread. They are marked below.
+class GMPVideoEncodedFrame : public GMPVideoFrame
+{
+public:
+  // MAIN THREAD ONLY
+  virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) = 0;
+  // MAIN THREAD ONLY
+  virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aVideoFrame) = 0;
+  virtual void     SetEncodedWidth(uint32_t aEncodedWidth) = 0;
+  virtual uint32_t EncodedWidth() = 0;
+  virtual void     SetEncodedHeight(uint32_t aEncodedHeight) = 0;
+  virtual uint32_t EncodedHeight() = 0;
+  virtual void     SetTimeStamp(uint32_t aTimeStamp) = 0;
+  virtual uint32_t TimeStamp() = 0;
+  virtual void     SetCaptureTime(int64_t aCaptureTime) = 0;
+  virtual int64_t  CaptureTime() = 0;
+  virtual void     SetFrameType(GMPVideoFrameType aFrameType) = 0;
+  virtual GMPVideoFrameType FrameType() = 0;
+  virtual void     SetAllocatedSize(uint32_t aNewSize) = 0;
+  virtual uint32_t AllocatedSize() = 0;
+  virtual void     SetSize(uint32_t aSize) = 0;
+  virtual uint32_t Size() = 0;
+  virtual void     SetCompleteFrame(bool aCompleteFrame) = 0;
+  virtual bool     CompleteFrame() = 0;
+  virtual const uint8_t* Buffer() const = 0;
+  virtual uint8_t*       Buffer() = 0;
+};
+
+#endif // GMP_VIDEO_FRAME_ENCODED_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-frame-i420.h
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_FRAME_I420_h_
+#define GMP_VIDEO_FRAME_I420_h_
+
+#include "gmp-video-errors.h"
+#include "gmp-video-frame.h"
+#include "gmp-video-plane.h"
+
+#include <stdint.h>
+
+enum GMPPlaneType {
+  kGMPYPlane = 0,
+  kGMPUPlane = 1,
+  kGMPVPlane = 2,
+  kGMPNumOfPlanes = 3
+};
+
+// The implementation backing this interface uses shared memory for the
+// buffer(s). This means it can only be used by the "owning" process.
+// At first the process which created the object owns it. When the object
+// is passed to an interface the creator loses ownership and must Destroy()
+// the object. Further attempts to use it may fail due to not being able to
+// access the underlying buffer(s).
+//
+// Methods that create or destroy shared memory must be called on the main
+// thread. They are marked below.
+class GMPVideoi420Frame : public GMPVideoFrame {
+public:
+  // MAIN THREAD ONLY
+  // CreateEmptyFrame: Sets frame dimensions and allocates buffers based
+  // on set dimensions - height and plane stride.
+  // If required size is bigger than the allocated one, new buffers of adequate
+  // size will be allocated.
+  virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight,
+                                       int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0;
+
+  // MAIN THREAD ONLY
+  // CreateFrame: Sets the frame's members and buffers. If required size is
+  // bigger than allocated one, new buffers of adequate size will be allocated.
+  virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
+                                  int32_t aSize_u, const uint8_t* aBuffer_u,
+                                  int32_t aSize_v, const uint8_t* aBuffer_v,
+                                  int32_t aWidth, int32_t aHeight,
+                                  int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0;
+
+  // MAIN THREAD ONLY
+  // Copy frame: If required size is bigger than allocated one, new buffers of
+  // adequate size will be allocated.
+  virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aVideoFrame) = 0;
+
+  // Swap Frame.
+  virtual void SwapFrame(GMPVideoi420Frame* aVideoFrame) = 0;
+
+  // Get pointer to buffer per plane.
+  virtual uint8_t* Buffer(GMPPlaneType aType) = 0;
+
+  // Overloading with const.
+  virtual const uint8_t* Buffer(GMPPlaneType aType) const = 0;
+
+  // Get allocated size per plane.
+  virtual int32_t AllocatedSize(GMPPlaneType aType) const = 0;
+
+  // Get allocated stride per plane.
+  virtual int32_t Stride(GMPPlaneType aType) const = 0;
+
+  // Set frame width.
+  virtual GMPVideoErr SetWidth(int32_t aWidth) = 0;
+
+  // Set frame height.
+  virtual GMPVideoErr SetHeight(int32_t aHeight) = 0;
+
+  // Get frame width.
+  virtual int32_t Width() const = 0;
+
+  // Get frame height.
+  virtual int32_t Height() const = 0;
+
+  // Set frame timestamp (90kHz).
+  virtual void SetTimestamp(uint32_t aTimestamp) = 0;
+
+  // Get frame timestamp (90kHz).
+  virtual uint32_t Timestamp() const = 0;
+
+  // Set render time in miliseconds.
+  virtual void SetRenderTime_ms(int64_t aRenderTime_ms) = 0;
+
+  // Get render time in miliseconds.
+  virtual int64_t RenderTime_ms() const = 0;
+
+  // Return true if underlying plane buffers are of zero size, false if not.
+  virtual bool IsZeroSize() const = 0;
+
+  // Reset underlying plane buffers sizes to 0. This function doesn't clear memory.
+  virtual void ResetSize() = 0;
+};
+
+#endif // GMP_VIDEO_FRAME_I420_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-frame.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_FRAME_h_
+#define GMP_VIDEO_FRAME_h_
+
+#include "gmp-video-errors.h"
+#include "gmp-video-plane.h"
+
+enum GMPVideoFrameFormat {
+  kGMPEncodedVideoFrame = 0,
+  kGMPI420VideoFrame = 1
+};
+
+class GMPVideoFrame {
+public:
+  virtual GMPVideoFrameFormat GetFrameFormat() = 0;
+  // MAIN THREAD ONLY IF OWNING PROCESS
+  virtual void Destroy() = 0;
+};
+
+#endif // GMP_VIDEO_FRAME_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-host.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_HOST_h_
+#define GMP_VIDEO_HOST_h_
+
+#include "gmp-video-errors.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+#include "gmp-video-codec.h"
+
+class GMPVideoHost
+{
+public:
+  // Construct various video API objects. Host does not retain reference,
+  // caller is owner and responsible for deleting.
+  virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) = 0;
+  virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) = 0;
+};
+
+#endif // GMP_VIDEO_HOST_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/gmp-api/gmp-video-plane.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
+ * Copyright (c) 2014, Mozilla
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ ** Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ ** Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in
+ *  the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ ** Neither the name of Google nor the names of its contributors may
+ *  be used to endorse or promote products derived from this software
+ *  without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GMP_VIDEO_PLANE_h_
+#define GMP_VIDEO_PLANE_h_
+
+#include "gmp-video-errors.h"
+#include <stdint.h>
+
+// The implementation backing this interface uses shared memory for the
+// buffer(s). This means it can only be used by the "owning" process.
+// At first the process which created the object owns it. When the object
+// is passed to an interface the creator loses ownership and must Destroy()
+// the object. Further attempts to use it may fail due to not being able to
+// access the underlying buffer(s).
+//
+// Methods that create or destroy shared memory must be called on the main
+// thread. They are marked below.
+class GMPPlane {
+public:
+  // MAIN THREAD ONLY
+  // CreateEmptyPlane - set allocated size, actual plane size and stride:
+  // If current size is smaller than current size, then a buffer of sufficient
+  // size will be allocated.
+  virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize,
+                                       int32_t aStride,
+                                       int32_t aPlaneSize) = 0;
+
+  // MAIN THREAD ONLY
+  // Copy the entire plane data.
+  virtual GMPVideoErr Copy(const GMPPlane& aPlane) = 0;
+
+  // MAIN THREAD ONLY
+  // Copy buffer: If current size is smaller
+  // than current size, then a buffer of sufficient size will be allocated.
+  virtual GMPVideoErr Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) = 0;
+
+  // Swap plane data.
+  virtual void Swap(GMPPlane& aPlane) = 0;
+
+  // Get allocated size.
+  virtual int32_t AllocatedSize() const = 0;
+
+  // Set actual size.
+  virtual void ResetSize() = 0;
+
+  // Return true is plane size is zero, false if not.
+  virtual bool IsZeroSize() const = 0;
+
+  // Get stride value.
+  virtual int32_t Stride() const = 0;
+
+  // Return data pointer.
+  virtual const uint8_t* Buffer() const = 0;
+
+  // Overloading with non-const.
+  virtual uint8_t* Buffer() = 0;
+
+  // MAIN THREAD ONLY IF OWNING PROCESS
+  // Call this when done with the object. This may delete it.
+  virtual void Destroy() = 0;
+};
+
+#endif // GMP_VIDEO_PLANE_h_
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/moz.build
@@ -0,0 +1,83 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# 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/.
+
+XPIDL_MODULE = 'content_geckomediaplugins'
+
+XPIDL_SOURCES += [
+    'mozIGeckoMediaPluginService.idl',
+]
+
+EXPORTS += [
+    'gmp-api/gmp-entrypoints.h',
+    'gmp-api/gmp-errors.h',
+    'gmp-api/gmp-platform.h',
+    'gmp-api/gmp-video-codec.h',
+    'gmp-api/gmp-video-decode.h',
+    'gmp-api/gmp-video-encode.h',
+    'gmp-api/gmp-video-errors.h',
+    'gmp-api/gmp-video-frame-encoded.h',
+    'gmp-api/gmp-video-frame-i420.h',
+    'gmp-api/gmp-video-frame.h',
+    'gmp-api/gmp-video-host.h',
+    'gmp-api/gmp-video-plane.h',
+    'GMPChild.h',
+    'GMPMessageUtils.h',
+    'GMPParent.h',
+    'GMPPlatform.h',
+    'GMPProcessChild.h',
+    'GMPProcessParent.h',
+    'GMPService.h',
+    'GMPSharedMemManager.h',
+    'GMPVideoDecoderChild.h',
+    'GMPVideoDecoderParent.h',
+    'GMPVideoEncodedFrameImpl.h',
+    'GMPVideoEncoderChild.h',
+    'GMPVideoEncoderParent.h',
+    'GMPVideoHost.h',
+    'GMPVideoi420FrameImpl.h',
+    'GMPVideoPlaneImpl.h',
+]
+
+UNIFIED_SOURCES += [
+    'GMPChild.cpp',
+    'GMPParent.cpp',
+    'GMPPlatform.cpp',
+    'GMPProcessChild.cpp',
+    'GMPProcessParent.cpp',
+    'GMPService.cpp',
+    'GMPVideoDecoderChild.cpp',
+    'GMPVideoDecoderParent.cpp',
+    'GMPVideoEncodedFrameImpl.cpp',
+    'GMPVideoEncoderChild.cpp',
+    'GMPVideoEncoderParent.cpp',
+    'GMPVideoHost.cpp',
+    'GMPVideoi420FrameImpl.cpp',
+    'GMPVideoPlaneImpl.cpp',
+]
+
+IPDL_SOURCES += [
+  'GMPTypes.ipdlh',
+  'PGMP.ipdl',
+  'PGMPVideoDecoder.ipdl',
+  'PGMPVideoEncoder.ipdl',
+]
+
+LIBRARY_NAME = 'mozgmp'
+
+if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
+    NO_VISIBILITY_FLAGS = True
+
+FAIL_ON_WARNINGS = True
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+    '../base',
+    '/xpcom/base',
+    '/xpcom/build',
+    '/xpcom/threads',
+]
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/mozIGeckoMediaPluginService.idl
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 "nsISupports.idl"
+#include "nsIThread.idl"
+
+%{C++
+class GMPVideoDecoder;
+class GMPVideoEncoder;
+class GMPVideoHost;
+%}
+
+[ptr] native GMPVideoDecoder(GMPVideoDecoder);
+[ptr] native GMPVideoEncoder(GMPVideoEncoder);
+[ptr] native GMPVideoHost(GMPVideoHost);
+[ptr] native MessageLoop(MessageLoop);
+
+[uuid(BF5A9086-70F5-4D38-832D-1609BBF963CD)]
+interface mozIGeckoMediaPluginService : nsISupports
+{
+  // Returns the GMP thread.
+  // Callable from any thread.
+  readonly attribute nsIThread thread;
+
+  // Returns a video decoder API object that should support VP8.
+  // Callable only on GMP thread.
+  GMPVideoDecoder getGMPVideoDecoderVP8(out GMPVideoHost outVideoHost);
+
+  // Returns a video encoder API object that should support VP8.
+  // Callable only on GMP thread.
+  GMPVideoEncoder getGMPVideoEncoderVP8(out GMPVideoHost outVideoHost);
+};
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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/.
 
 PARALLEL_DIRS += [
   'encoder',
+  'gmp',
   'mediasource',
   'ogg',
   'webaudio',
   'webvtt'
 ]
 
 TEST_TOOL_DIRS += ['compiledtest']
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -60,17 +60,16 @@ static const bool kLowRightsSubprocesses
 #ifdef MOZ_WIDGET_GONK
   true
 #else
   false
 #endif
   ;
 
 mozilla::StaticRefPtr<nsIFile> GeckoChildProcessHost::sGreDir;
-mozilla::DebugOnly<bool> GeckoChildProcessHost::sGreDirCached;
 
 static bool
 ShouldHaveDirectoryService()
 {
   return GeckoProcessType_Default == XRE_GetProcessType();
 }
 
 template<>
@@ -125,17 +124,17 @@ GeckoChildProcessHost::~GeckoChildProces
 #endif
 }
 
 //static
 void
 GeckoChildProcessHost::GetPathToBinary(FilePath& exePath)
 {
   if (ShouldHaveDirectoryService()) {
-    MOZ_ASSERT(sGreDirCached);
+    MOZ_ASSERT(sGreDir);
     if (sGreDir) {
 #ifdef OS_WIN
       nsString path;
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetPath(path)));
 #else
       nsCString path;
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path)));
 #endif
@@ -262,45 +261,36 @@ GeckoChildProcessHost::PrepareLaunch()
 #endif
   CacheGreDir();
 }
 
 //static
 void
 GeckoChildProcessHost::CacheGreDir()
 {
-  // PerformAysncLaunchInternal/GetPathToBinary may be called on the IO thread,
-  // and they want to use the directory service, which needs to happen on the
-  // main thread (in the event that its implemented in JS). So we grab
-  // NS_GRE_DIR here and stash it.
+  if (sGreDir) {
+    return;
+  }
 
-#ifdef MOZ_WIDGET_GONK
-  // Apparently, this ASSERT should be present on all platforms. Currently,
-  // this assert causes mochitest failures on the Mac platform if its left in.
-
-  // B2G overrides the directory service in JS, so this needs to be called
-  // on the main thread.
   MOZ_ASSERT(NS_IsMainThread());
-#endif
 
   if (ShouldHaveDirectoryService()) {
     nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     MOZ_ASSERT(directoryService, "Expected XPCOM to be available");
     if (directoryService) {
       // getter_AddRefs doesn't work with StaticRefPtr, so we need to store the
       // result in an nsCOMPtr and copy it over.
       nsCOMPtr<nsIFile> greDir;
       nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
       if (NS_SUCCEEDED(rv)) {
         sGreDir = greDir;
         mozilla::ClearOnShutdown(&sGreDir);
       }
     }
   }
-  sGreDirCached = true;
 }
 
 #ifdef XP_WIN
 void GeckoChildProcessHost::InitWindowsGroupID()
 {
   // On Win7+, pass the application user model to the child, so it can
   // register with it. This insures windows created by the container
   // properly group with the parent app on the Win7 taskbar.
@@ -554,17 +544,17 @@ GeckoChildProcessHost::PerformAsyncLaunc
   if (privs == base::PRIVILEGES_DEFAULT) {
     privs = DefaultChildPrivileges();
   }
   // XPCOM may not be initialized in some subprocesses.  We don't want
   // to initialize XPCOM just for the directory service, especially
   // since LD_LIBRARY_PATH is already set correctly in subprocesses
   // (meaning that we don't need to set that up in the environment).
   if (ShouldHaveDirectoryService()) {
-    MOZ_ASSERT(sGreDirCached);
+    MOZ_ASSERT(sGreDir);
     if (sGreDir) {
       nsCString path;
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(sGreDir->GetNativePath(path)));
 # if defined(OS_LINUX) || defined(OS_BSD)
 #  if defined(MOZ_WIDGET_ANDROID)
       path += "/lib";
 #  endif  // MOZ_WIDGET_ANDROID
       const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -130,16 +130,18 @@ public:
 
   // For bug 943174: Skip the EnsureProcessTerminated call in the destructor.
   void SetAlreadyDead();
 
   void SetSandboxEnabled(bool aSandboxEnabled) {
     mSandboxEnabled = aSandboxEnabled;
   }
 
+  static void CacheGreDir();
+
 protected:
   GeckoProcessType mProcessType;
   bool mSandboxEnabled;
   ChildPrivileges mPrivileges;
   Monitor mMonitor;
   FilePath mProcessPath;
 
   // This value must be accessed while holding mMonitor.
@@ -187,17 +189,16 @@ private:
   // Does the actual work for AsyncLaunch, on the IO thread.
   bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts,
                                   base::ProcessArchitecture arch);
 
   bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
 			     base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
 
   static void GetPathToBinary(FilePath& exePath);
-  static void CacheGreDir();
 
   // In between launching the subprocess and handing off its IPC
   // channel, there's a small window of time in which *we* might still
   // be the channel listener, and receive messages.  That's bad
   // because we have no idea what to do with those messages.  So queue
   // them here until we hand off the eventual listener.
   //
   // FIXME/cjones: this strongly indicates bad design.  Shame on us.
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -237,30 +237,33 @@ static void Shutdown();
 #include "mozilla/dom/telephony/TelephonyFactory.h"
 #include "nsITelephonyProvider.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "GonkGPSGeolocationProvider.h"
 #endif
 #include "MediaManager.h"
 
+#include "GMPService.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::TCPSocketParent;
 using mozilla::dom::TCPServerSocketChild;
 using mozilla::dom::UDPSocketChild;
 using mozilla::dom::time::TimeService;
 using mozilla::net::StreamingProtocolControllerService;
+using mozilla::gmp::GeckoMediaPluginService;
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
 #define TRANSFORMIIX_NODESET_CID \
 { 0x5d5d92cd, 0x6bf8, 0x11d9, { 0xbf, 0x4a, 0x0, 0x0a, 0x95, 0xdc, 0x23, 0x4c } }
 
 #define TRANSFORMIIX_NODESET_CONTRACTID \
 "@mozilla.org/transformiix-nodeset;1"
@@ -601,16 +604,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNu
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStructuredCloneContainer)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(OSFileConstantsService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TCPSocketChild)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TCPSocketParent)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TCPServerSocketChild)
 NS_GENERIC_FACTORY_CONSTRUCTOR(UDPSocketChild)
 
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(GeckoMediaPluginService, GeckoMediaPluginService::GetGeckoMediaPluginService)
+
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 
   MAKE_CTOR(CreateA11yService, nsIAccessibilityService, NS_GetAccessibilityService)
 #endif
 
 static nsresult
 Construct_nsIScriptSecurityManager(nsISupports *aOuter, REFNSIID aIID,
@@ -780,16 +785,18 @@ NS_DEFINE_NAMED_CID(NS_FAKE_SPEECH_RECOG
 NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID);
 #endif
 
 #ifdef ACCESSIBILITY
 NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(TELEPHONY_PROVIDER_CID);
 
+NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID);
+
 static nsresult
 CreateWindowCommandTableConstructor(nsISupports *aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIControllerCommandTable> commandTable =
       do_CreateInstance(NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) return rv;
@@ -1050,16 +1057,17 @@ static const mozilla::Module::CIDEntry k
   { &kMOBILE_MESSAGE_DATABASE_SERVICE_CID, false, nullptr, nsIMobileMessageDatabaseServiceConstructor },
   { &kNS_POWERMANAGERSERVICE_CID, false, nullptr, nsIPowerManagerServiceConstructor },
   { &kOSFILECONSTANTSSERVICE_CID, true, nullptr, OSFileConstantsServiceConstructor },
   { &kNS_ALARMHALSERVICE_CID, false, nullptr, nsIAlarmHalServiceConstructor },
   { &kTCPSOCKETCHILD_CID, false, nullptr, TCPSocketChildConstructor },
   { &kTCPSOCKETPARENT_CID, false, nullptr, TCPSocketParentConstructor },
   { &kTCPSERVERSOCKETCHILD_CID, false, nullptr, TCPServerSocketChildConstructor },
   { &kUDPSOCKETCHILD_CID, false, nullptr, UDPSocketChildConstructor },
+  { &kGECKO_MEDIA_PLUGIN_SERVICE_CID, true, nullptr, GeckoMediaPluginServiceConstructor },
   { &kNS_TIMESERVICE_CID, false, nullptr, nsITimeServiceConstructor },
   { &kNS_MEDIASTREAMCONTROLLERSERVICE_CID, false, nullptr, nsIStreamingProtocolControllerServiceConstructor },
 #ifdef MOZ_WIDGET_GONK
   { &kGONK_GPS_GEOLOCATION_PROVIDER_CID, false, nullptr, nsIGeolocationProviderConstructor },
 #endif
   { &kNS_MEDIAMANAGERSERVICE_CID, false, nullptr, nsIMediaManagerServiceConstructor },
 #ifdef MOZ_GAMEPAD
   { &kNS_GAMEPAD_TEST_CID, false, nullptr, GamepadServiceTestConstructor },
@@ -1219,16 +1227,17 @@ static const mozilla::Module::ContractID
   { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
 #endif
   { MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
 #ifdef ACCESSIBILITY
   { "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID },
   { "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID },
 #endif
   { TELEPHONY_PROVIDER_CONTRACTID, &kTELEPHONY_PROVIDER_CID },
+  { "@mozilla.org/gecko-media-plugin-service;1",  &kGECKO_MEDIA_PLUGIN_SERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
   XPCONNECT_CATEGORIES
   { "content-policy", NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID, NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID },
   { "content-policy", NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID, NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID },
   { "content-policy", "CSPService", CSPSERVICE_CONTRACTID },
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -774,17 +774,17 @@ nsXULAppInfo::GetWidgetToolkit(nsACStrin
                 "GeckoProcessType in nsXULAppAPI.h not synchronized with nsIXULRuntime.idl");
 
 SYNC_ENUMS(DEFAULT, Default)
 SYNC_ENUMS(PLUGIN, Plugin)
 SYNC_ENUMS(CONTENT, Content)
 SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest)
 
 // .. and ensure that that is all of them:
-static_assert(GeckoProcessType_IPDLUnitTest + 1 == GeckoProcessType_End,
+static_assert(GeckoProcessType_GMPlugin + 1 == GeckoProcessType_End,
               "Did not find the final GeckoProcessType");
 
 NS_IMETHODIMP
 nsXULAppInfo::GetProcessType(uint32_t* aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = XRE_GetProcessType();
   return NS_OK;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -63,16 +63,18 @@
 #include "mozilla/plugins/PluginProcessChild.h"
 #include "mozilla/dom/ContentProcess.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/XPCShellEnvironment.h"
 
+#include "GMPProcessChild.h"
+
 #include "GeckoProfiler.h"
 
 #ifdef MOZ_IPDL_TESTS
 #include "mozilla/_ipdltest/IPDLUnitTests.h"
 #include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h"
 
 using mozilla::_ipdltest::IPDLUnitTestProcessChild;
 #endif  // ifdef MOZ_IPDL_TESTS
@@ -497,16 +499,20 @@ XRE_InitChildProcess(int aArgc,
       case GeckoProcessType_IPDLUnitTest:
 #ifdef MOZ_IPDL_TESTS
         process = new IPDLUnitTestProcessChild(parentHandle);
 #else 
         NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests");
 #endif
         break;
 
+      case GeckoProcessType_GMPlugin:
+        process = new gmp::GMPProcessChild(parentHandle);
+        break;
+
       default:
         NS_RUNTIMEABORT("Unknown main thread class");
       }
 
       if (!process->Init()) {
         profiler_shutdown();
         NS_LogTerm();
         return NS_ERROR_FAILURE;
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -124,16 +124,18 @@ extern nsresult nsStringInputStreamConst
 #include "base/command_line.h"
 #include "base/message_loop.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/AvailableMemoryTracker.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SystemMemoryReporter.h"
 
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
 #ifdef MOZ_VISUAL_EVENT_TRACER
 #include "mozilla/VisualEventTracer.h"
 #endif
 
 #include "ogg/ogg.h"
 #if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
 #include "vpx_mem/vpx_mem.h"
 #endif
@@ -696,16 +698,20 @@ NS_InitXPCOM2(nsIServiceManager* *result
         loop->thread_name().c_str(),
         loop->transient_hang_timeout(),
         loop->permanent_hang_timeout());
 
 #ifdef MOZ_VISUAL_EVENT_TRACER
     mozilla::eventtracer::Init();
 #endif
 
+    // TODO: Cache the GRE dir here instead of telling GeckoChildProcessHost to do it.
+    //       Then have GeckoChildProcessHost get the dir from XPCOM::GetGREPath().
+    mozilla::ipc::GeckoChildProcessHost::CacheGreDir();
+
     return NS_OK;
 }
 
 
 //
 // NS_ShutdownXPCOM()
 //
 // The shutdown sequence for xpcom would be
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -351,25 +351,28 @@ XRE_API(void,
 enum GeckoProcessType {
   GeckoProcessType_Default = 0,
 
   GeckoProcessType_Plugin,
   GeckoProcessType_Content,
 
   GeckoProcessType_IPDLUnitTest,
 
+  GeckoProcessType_GMPlugin, // Gecko Media Plugin
+
   GeckoProcessType_End,
   GeckoProcessType_Invalid = GeckoProcessType_End
 };
 
 static const char* const kGeckoProcessTypeString[] = {
   "default",
   "plugin",
   "tab",
-  "ipdlunittest"
+  "ipdlunittest",
+  "geckomediaplugin"
 };
 
 static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) ==
               GeckoProcessType_End,
               "Array length mismatch");
 
 XRE_API(const char*,
         XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -65,16 +65,17 @@ interface nsIXULRuntime : nsISupports
 
   /**
    * The legal values of processType.
    */
   const unsigned long PROCESS_TYPE_DEFAULT = 0;
   const unsigned long PROCESS_TYPE_PLUGIN = 1;
   const unsigned long PROCESS_TYPE_CONTENT = 2;
   const unsigned long PROCESS_TYPE_IPDLUNITTEST = 3;
+  const unsigned long PROCESS_TYPE_GMPLUGIN = 4;
 
   /**
    * The type of the caller's process.  Returns one of the values above.
    */
   readonly attribute unsigned long processType;
 
   /**
    * The system process ID of the caller's process.