Bug 1020090: Proxy all GMP Shmem create/delete to parent and reduce allocation traffic. r=nical
authorRandell Jesup <rjesup@jesup.org>
Tue, 01 Jul 2014 22:26:35 -0400
changeset 215441 c928106052fe2d0bb92ac678e8122a52f6a508a5
parent 215440 7c60cb7a9403d4320b58b14dd200882bb1de80b8
child 215442 b1ba940ea14bde7c2a198c85c785eda39c0b4d85
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1020090
milestone33.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 1020090: Proxy all GMP Shmem create/delete to parent and reduce allocation traffic. r=nical
content/media/gmp/GMPSharedMemManager.cpp
content/media/gmp/GMPSharedMemManager.h
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/GMPVideoEncoderChild.cpp
content/media/gmp/GMPVideoEncoderChild.h
content/media/gmp/GMPVideoEncoderParent.cpp
content/media/gmp/GMPVideoEncoderParent.h
content/media/gmp/GMPVideoPlaneImpl.cpp
content/media/gmp/PGMP.ipdl
content/media/gmp/PGMPVideoDecoder.ipdl
content/media/gmp/PGMPVideoEncoder.ipdl
content/media/gmp/moz.build
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPSharedMemManager.cpp
@@ -0,0 +1,85 @@
+/* -*- 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 "GMPSharedMemManager.h"
+#include "GMPMessageUtils.h"
+#include "mozilla/ipc/SharedMemory.h"
+
+namespace mozilla {
+namespace gmp {
+
+// Really one set of pools on each side of the plugin API.
+
+// YUV buffers go from Encoder parent to child; pool there, and then return
+// with Decoded() frames to the Decoder parent and goes into the parent pool.
+// Compressed (encoded) data goes from the Decoder parent to the child;
+// pool there, and then return with Encoded() frames and goes into the parent
+// pool.
+static nsTArray<ipc::Shmem> sGmpFreelist[GMPSharedMemManager::kGMPNumTypes];
+static uint32_t sGmpAllocated[GMPSharedMemManager::kGMPNumTypes]; // 0's
+
+bool
+GMPSharedMemManager::MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize,
+                                   ipc::Shmem::SharedMemory::SharedMemoryType aType,
+                                   ipc::Shmem* aMem)
+{
+  CheckThread();
+
+  // first look to see if we have a free buffer large enough
+  for (uint32_t i = 0; i < sGmpFreelist[aClass].Length(); i++) {
+    MOZ_ASSERT(sGmpFreelist[aClass][i].IsWritable());
+    if (aSize <= sGmpFreelist[aClass][i].Size<uint8_t>()) {
+      *aMem = sGmpFreelist[aClass][i];
+      sGmpFreelist[aClass].RemoveElementAt(i);
+      return true;
+    }
+  }
+
+  // Didn't find a buffer free with enough space; allocate one
+  size_t pagesize = ipc::SharedMemory::SystemPageSize();
+  aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size
+  bool retval = Alloc(aSize, aType, aMem);
+  if (retval) {
+    sGmpAllocated[aClass]++;
+  }
+  return retval;
+}
+
+bool
+GMPSharedMemManager::MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem)
+{
+  CheckThread();
+
+  size_t size = aMem.Size<uint8_t>();
+  size_t total = 0;
+  // XXX This works; there are better pool algorithms.  We need to avoid
+  // "falling off a cliff" with too low a number
+  if (sGmpFreelist[aClass].Length() > 10) {
+    Dealloc(sGmpFreelist[aClass][0]);
+    sGmpFreelist[aClass].RemoveElementAt(0);
+    // The allocation numbers will be fubar on the Child!
+    sGmpAllocated[aClass]--;
+  }
+  for (uint32_t i = 0; i < sGmpFreelist[aClass].Length(); i++) {
+    MOZ_ASSERT(sGmpFreelist[aClass][i].IsWritable());
+    total += sGmpFreelist[aClass][i].Size<uint8_t>();
+    if (size < sGmpFreelist[aClass][i].Size<uint8_t>()) {
+      sGmpFreelist[aClass].InsertElementAt(i, aMem);
+      return true;
+    }
+  }
+  sGmpFreelist[aClass].AppendElement(aMem);
+
+  return true;
+}
+
+uint32_t
+GMPSharedMemManager::NumInUse(GMPMemoryClasses aClass)
+{
+  return sGmpAllocated[aClass] - sGmpFreelist[aClass].Length();
+}
+
+}
+}
--- a/content/media/gmp/GMPSharedMemManager.h
+++ b/content/media/gmp/GMPSharedMemManager.h
@@ -2,25 +2,56 @@
 /* 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"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPSharedMemManager
 {
 public:
-  virtual bool MgrAllocShmem(size_t aSize,
+  typedef enum {
+    kGMPFrameData = 0,
+    kGMPEncodedData,
+    kGMPNumTypes
+  } GMPMemoryClasses;
+
+  // This is a heuristic - max of 10 free in the Child pool, plus those
+  // in-use for the encoder and decoder at the given moment and not yet
+  // returned to the parent pool (which is not included).  If more than
+  // this are needed, we presume the client has either crashed or hung
+  // (perhaps temporarily).
+  static const uint32_t kGMPBufLimit = 20;
+
+  GMPSharedMemManager() {}
+
+  virtual ~GMPSharedMemManager() {
+    // XXX free everything in the freelist
+  }
+
+  virtual bool MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize,
                              ipc::Shmem::SharedMemory::SharedMemoryType aType,
-                             ipc::Shmem* aMem) = 0;
-  virtual bool MgrDeallocShmem(ipc::Shmem& aMem) = 0;
+                             ipc::Shmem* aMem);
+  virtual bool MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem);
+
+  // So we can know if data is "piling up" for the plugin - I.e. it's hung or crashed
+  virtual uint32_t NumInUse(GMPMemoryClasses aClass);
+
+  // Parent and child impls will differ here
+  virtual void CheckThread() = 0;
+
+  // These have to be implemented using the AllocShmem/etc provided by the IPDL-generated interfaces,
+  // so have the Parent/Child implement them.
+  virtual bool Alloc(size_t aSize, ipc::Shmem::SharedMemory::SharedMemoryType aType, ipc::Shmem* aMem) = 0;
+  virtual void Dealloc(ipc::Shmem& aMem) = 0;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPSharedMemManager_h_
--- a/content/media/gmp/GMPVideoDecoderChild.cpp
+++ b/content/media/gmp/GMPVideoDecoderChild.cpp
@@ -75,32 +75,20 @@ GMPVideoDecoderChild::ReceivedDecodedFra
 void
 GMPVideoDecoderChild::InputDataExhausted()
 {
   MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
 
   SendInputDataExhausted();
 }
 
-bool
-GMPVideoDecoderChild::MgrAllocShmem(size_t aSize,
-                                    ipc::Shmem::SharedMemory::SharedMemoryType aType,
-                                    ipc::Shmem* aMem)
+void
+GMPVideoDecoderChild::CheckThread()
 {
   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;
@@ -122,16 +110,28 @@ GMPVideoDecoderChild::RecvDecode(const G
     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 SHM to sender to recycle
+  //SendDecodeReturn(aInputFrame, aCodecSpecificInfo);
+  return true;
+}
+
+bool
+GMPVideoDecoderChild::RecvChildShmemForPool(Shmem& aFrameBuffer)
+{
+  if (aFrameBuffer.IsWritable()) {
+    mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData,
+                                               aFrameBuffer);
+  }
   return true;
 }
 
 bool
 GMPVideoDecoderChild::RecvReset()
 {
   if (!mVideoDecoder) {
     return false;
--- a/content/media/gmp/GMPVideoDecoderChild.h
+++ b/content/media/gmp/GMPVideoDecoderChild.h
@@ -30,29 +30,47 @@ public:
 
   // 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;
+  virtual void CheckThread();
+  virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
+  {
+#ifndef SHMEM_ALLOC_IN_CHILD
+    return CallNeedShmem(aSize, aMem);
+#else
+#ifdef GMP_SAFE_SHMEM
+    return AllocShmem(aSize, aType, aMem);
+#else
+    return AllocUnsafeShmem(aSize, aType, aMem);
+#endif
+#endif
+  }
+  virtual void Dealloc(Shmem& aMem)
+  {
+#ifndef SHMEM_ALLOC_IN_CHILD
+    SendParentShmemForPool(aMem);
+#else
+    DeallocShmem(aMem);
+#endif
+  }
 
 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 RecvChildShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE;
   virtual bool RecvReset() MOZ_OVERRIDE;
   virtual bool RecvDrain() MOZ_OVERRIDE;
   virtual bool RecvDecodingComplete() MOZ_OVERRIDE;
 
   GMPChild* mPlugin;
   GMPVideoDecoder* mVideoDecoder;
   GMPVideoHostImpl mVideoHost;
 };
--- a/content/media/gmp/GMPVideoDecoderParent.cpp
+++ b/content/media/gmp/GMPVideoDecoderParent.cpp
@@ -37,34 +37,16 @@ GMPVideoDecoderParent::~GMPVideoDecoderP
 }
 
 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;
@@ -100,16 +82,24 @@ GMPVideoDecoderParent::Decode(GMPVideoEn
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   auto inputFrameImpl = static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame);
 
   GMPVideoEncodedFrameData frameData;
   inputFrameImpl->RelinquishFrameData(frameData);
 
+  // Very rough kill-switch if the plugin stops processing.  If it's merely
+  // hung and continues, we'll come back to life eventually.
+  // 3* is because we're using 3 buffers per frame for i420 data for now.
+  if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
+      NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
+    return GMPVideoGenericErr;
+  }
+
   if (!SendDecode(frameData,
                   aMissingFrames,
                   aCodecSpecificInfo,
                   aRenderTimeMs)) {
     return GMPVideoGenericErr;
   }
 
   // Async IPC, we don't have access to a return value.
@@ -181,16 +171,22 @@ GMPVideoDecoderParent::ActorDestroy(Acto
     mPlugin->VideoDecoderDestroyed(this);
     mPlugin = nullptr;
   }
   mCanSendMessages = false;
   mCallback = nullptr;
   mVideoHost.ActorDestroyed();
 }
 
+void
+GMPVideoDecoderParent::CheckThread()
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+}
+
 bool
 GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
 {
   if (!mCallback) {
     return false;
   }
 
   auto f = new GMPVideoi420FrameImpl(aDecodedFrame, &mVideoHost);
@@ -236,16 +232,43 @@ GMPVideoDecoderParent::RecvInputDataExha
 
   // Ignore any return code. It is OK for this to fail without killing the process.
   mCallback->InputDataExhausted();
 
   return true;
 }
 
 bool
+GMPVideoDecoderParent::RecvParentShmemForPool(Shmem& aEncodedBuffer)
+{
+  if (aEncodedBuffer.IsWritable()) {
+    mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData,
+                                               aEncodedBuffer);
+  }
+  return true;
+}
+
+bool
+GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize,
+                                       Shmem* aMem)
+{
+  ipc::Shmem mem;
+
+  if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData,
+                                                aFrameBufferSize,
+                                                ipc::SharedMemory::TYPE_BASIC, &mem))
+  {
+    return false;
+  }
+  *aMem = mem;
+  mem = ipc::Shmem();
+  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;
   }
 
--- a/content/media/gmp/GMPVideoDecoderParent.h
+++ b/content/media/gmp/GMPVideoDecoderParent.h
@@ -24,43 +24,55 @@ class GMPVideoDecoderParent MOZ_FINAL : 
 {
 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;
 
+  // GMPSharedMemManager
+  virtual void CheckThread();
+  virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
+  {
+#ifdef GMP_SAFE_SHMEM
+    return AllocShmem(aSize, aType, aMem);
+#else
+    return AllocUnsafeShmem(aSize, aType, aMem);
+#endif
+  }
+  virtual void Dealloc(Shmem& aMem)
+  {
+    DeallocShmem(aMem);
+  }
+
 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 RecvParentShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE;
+  virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize,
+                               Shmem* aMem) MOZ_OVERRIDE;
   virtual bool Recv__delete__() MOZ_OVERRIDE;
 
   bool mCanSendMessages;
   GMPParent* mPlugin;
   GMPDecoderCallback* mCallback;
   GMPVideoHostImpl mVideoHost;
 };
 
--- a/content/media/gmp/GMPVideoEncodedFrameImpl.cpp
+++ b/content/media/gmp/GMPVideoEncodedFrameImpl.cpp
@@ -94,33 +94,34 @@ GMPVideoEncodedFrameImpl::RelinquishFram
 
   return true;
 }
 
 void
 GMPVideoEncodedFrameImpl::DestroyBuffer()
 {
   if (mHost && mBuffer.IsWritable()) {
-    mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer);
+    mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData, mBuffer);
   }
   mBuffer = ipc::Shmem();
 }
 
 GMPVideoErr
 GMPVideoEncodedFrameImpl::CreateEmptyFrame(uint32_t aSize)
 {
-  DestroyBuffer();
-
-  if (aSize != 0) {
-    if (!mHost->SharedMemMgr()->MgrAllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &mBuffer) ||
+  if (aSize == 0) {
+    DestroyBuffer();
+  } else if (aSize > AllocatedSize()) {
+    DestroyBuffer();
+    if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aSize,
+                                              ipc::SharedMemory::TYPE_BASIC, &mBuffer) ||
         !Buffer()) {
       return GMPVideoAllocErr;
     }
   }
-
   mSize = aSize;
 
   return GMPVideoNoErr;
 }
 
 GMPVideoErr
 GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame)
 {
@@ -133,17 +134,17 @@ GMPVideoEncodedFrameImpl::CopyFrame(cons
     }
     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;
+  mSize = f.mSize; // already set...
   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)
@@ -212,17 +213,18 @@ GMPVideoEncodedFrameImpl::SetAllocatedSi
     return;
   }
 
   if (!mHost) {
     return;
   }
 
   ipc::Shmem new_mem;
-  if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
+  if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aNewSize,
+                                            ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
       !new_mem.get<uint8_t>()) {
     return;
   }
 
   if (mBuffer.IsReadable()) {
     memcpy(new_mem.get<uint8_t>(), Buffer(), mSize);
   }
 
--- a/content/media/gmp/GMPVideoEncoderChild.cpp
+++ b/content/media/gmp/GMPVideoEncoderChild.cpp
@@ -5,16 +5,18 @@
 
 #include "GMPVideoEncoderChild.h"
 #include "GMPChild.h"
 #include <stdio.h>
 #include "mozilla/unused.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "GMPVideoi420FrameImpl.h"
 
+using mozilla::ipc::ProcessChild;
+
 namespace mozilla {
 namespace gmp {
 
 GMPVideoEncoderChild::GMPVideoEncoderChild(GMPChild* aPlugin)
 : mPlugin(aPlugin),
   mVideoEncoder(nullptr),
   mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
 {
@@ -49,32 +51,20 @@ GMPVideoEncoderChild::Encoded(GMPVideoEn
   GMPVideoEncodedFrameData frameData;
   ef->RelinquishFrameData(frameData);
 
   SendEncoded(frameData, aCodecSpecificInfo);
 
   aEncodedFrame->Destroy();
 }
 
-bool
-GMPVideoEncoderChild::MgrAllocShmem(size_t aSize,
-                                    ipc::Shmem::SharedMemory::SharedMemoryType aType,
-                                    ipc::Shmem* aMem)
+void
+GMPVideoEncoderChild::CheckThread()
 {
   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) {
@@ -97,24 +87,33 @@ GMPVideoEncoderChild::RecvEncode(const G
   }
 
   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::RecvChildShmemForPool(Shmem& aEncodedBuffer)
+{
+  if (aEncodedBuffer.IsWritable()) {
+    mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData,
+                                               aEncodedBuffer);
+  }
+  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.
--- a/content/media/gmp/GMPVideoEncoderChild.h
+++ b/content/media/gmp/GMPVideoEncoderChild.h
@@ -28,29 +28,47 @@ public:
   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;
+  virtual void CheckThread();
+  virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
+  {
+#ifndef SHMEM_ALLOC_IN_CHILD
+    return CallNeedShmem(aSize, aMem);
+#else
+#ifdef GMP_SAFE_SHMEM
+    return AllocShmem(aSize, aType, aMem);
+#else
+    return AllocUnsafeShmem(aSize, aType, aMem);
+#endif
+#endif
+  }
+  virtual void Dealloc(Shmem& aMem)
+  {
+#ifndef SHMEM_ALLOC_IN_CHILD
+    SendParentShmemForPool(aMem);
+#else
+    DeallocShmem(aMem);
+#endif
+  }
 
 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 RecvChildShmemForPool(Shmem& aEncodedBuffer) 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;
--- a/content/media/gmp/GMPVideoEncoderParent.cpp
+++ b/content/media/gmp/GMPVideoEncoderParent.cpp
@@ -38,34 +38,16 @@ GMPVideoEncoderParent::~GMPVideoEncoderP
 }
 
 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!");
@@ -101,16 +83,24 @@ GMPVideoEncoderParent::Encode(GMPVideoi4
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   auto inputFrameImpl = static_cast<GMPVideoi420FrameImpl*>(aInputFrame);
 
   GMPVideoi420FrameData frameData;
   inputFrameImpl->InitFrameData(frameData);
 
+  // Very rough kill-switch if the plugin stops processing.  If it's merely
+  // hung and continues, we'll come back to life eventually.
+  // 3* is because we're using 3 buffers per frame for i420 data for now.
+  if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
+      NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
+    return GMPVideoGenericErr;
+  }
+
   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,
@@ -205,29 +195,64 @@ GMPVideoEncoderParent::ActorDestroy(Acto
     mPlugin->VideoEncoderDestroyed(this);
     mPlugin = nullptr;
   }
   mCanSendMessages = false;
   mCallback = nullptr;
   mVideoHost.ActorDestroyed();
 }
 
+void
+GMPVideoEncoderParent::CheckThread()
+{
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+}
+
 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 SHM to sender to recycle
+  //SendEncodedReturn(aEncodedFrame, aCodecSpecificInfo);
+  return true;
+}
+
+bool
+GMPVideoEncoderParent::RecvParentShmemForPool(Shmem& aFrameBuffer)
+{
+  if (aFrameBuffer.IsWritable()) {
+    mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData,
+                                               aFrameBuffer);
+  }
+  return true;
+}
+
+bool
+GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
+                                       Shmem* aMem)
+{
+  ipc::Shmem mem;
+
+  if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData,
+                                                aEncodedBufferSize,
+                                                ipc::SharedMemory::TYPE_BASIC, &mem))
+  {
+    return false;
+  }
+  *aMem = mem;
+  mem = ipc::Shmem();
   return true;
 }
 
 bool
 GMPVideoEncoderParent::Recv__delete__()
 {
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
--- a/content/media/gmp/GMPVideoEncoderParent.h
+++ b/content/media/gmp/GMPVideoEncoderParent.h
@@ -24,42 +24,54 @@ class GMPVideoEncoderParent : public GMP
 {
 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;
 
+  // GMPSharedMemManager
+  virtual void CheckThread();
+  virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
+  {
+#ifdef GMP_SAFE_SHMEM
+    return AllocShmem(aSize, aType, aMem);
+#else
+    return AllocUnsafeShmem(aSize, aType, aMem);
+#endif
+  }
+  virtual void Dealloc(Shmem& aMem)
+  {
+    DeallocShmem(aMem);
+  }
+
 private:
   virtual ~GMPVideoEncoderParent();
 
   // PGMPVideoEncoderParent
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
   virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
                            const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE;
+  virtual bool RecvParentShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE;
+  virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
+                               Shmem* aMem) MOZ_OVERRIDE;
   virtual bool Recv__delete__() MOZ_OVERRIDE;
 
   bool mCanSendMessages;
   GMPParent* mPlugin;
   GMPEncoderCallback* mCallback;
   GMPVideoHostImpl mVideoHost;
 };
 
--- a/content/media/gmp/GMPVideoPlaneImpl.cpp
+++ b/content/media/gmp/GMPVideoPlaneImpl.cpp
@@ -79,17 +79,18 @@ GMPPlaneImpl::MaybeResize(int32_t aNewSi
     return GMPVideoNoErr;
   }
 
   if (!mHost) {
     return GMPVideoGenericErr;
   }
 
   ipc::Shmem new_mem;
-  if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
+  if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData, 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);
   }
 
@@ -99,17 +100,17 @@ GMPPlaneImpl::MaybeResize(int32_t aNewSi
 
   return GMPVideoNoErr;
 }
 
 void
 GMPPlaneImpl::DestroyBuffer()
 {
   if (mHost && mBuffer.IsWritable()) {
-    mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer);
+    mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData, mBuffer);
   }
   mBuffer = ipc::Shmem();
 }
 
 GMPVideoErr
 GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize)
 {
   if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) {
--- a/content/media/gmp/PGMP.ipdl
+++ b/content/media/gmp/PGMP.ipdl
@@ -4,19 +4,19 @@
  * 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
+intr protocol PGMP
 {
   manages PGMPVideoDecoder;
   manages PGMPVideoEncoder;
 child:
-  PGMPVideoDecoder();
-  PGMPVideoEncoder();
+  async PGMPVideoDecoder();
+  async PGMPVideoEncoder();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/media/gmp/PGMPVideoDecoder.ipdl
+++ b/content/media/gmp/PGMPVideoDecoder.ipdl
@@ -9,31 +9,37 @@ 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
+intr protocol PGMPVideoDecoder
 {
   manager PGMP;
 child:
-  InitDecode(GMPVideoCodec aCodecSettings,
-             int32_t aCoreCount);
-  Decode(GMPVideoEncodedFrameData aInputFrame,
-         bool aMissingFrames,
-         GMPCodecSpecificInfo aCodecSpecificInfo,
-         int64_t aRenderTimeMs);
-  Reset();
-  Drain();
-  DecodingComplete();
+  async InitDecode(GMPVideoCodec aCodecSettings,
+                   int32_t aCoreCount);
+  async Decode(GMPVideoEncodedFrameData aInputFrame,
+               bool aMissingFrames,
+               GMPCodecSpecificInfo aCodecSpecificInfo,
+               int64_t aRenderTimeMs);
+  async Reset();
+  async Drain();
+  async DecodingComplete();
+  async ChildShmemForPool(Shmem aFrameBuffer);
+
 parent:
-  __delete__();
-  Decoded(GMPVideoi420FrameData aDecodedFrame);
-  ReceivedDecodedReferenceFrame(uint64_t aPictureId);
-  ReceivedDecodedFrame(uint64_t aPictureId);
-  InputDataExhausted();
+  async __delete__();
+  async Decoded(GMPVideoi420FrameData aDecodedFrame);
+  async ReceivedDecodedReferenceFrame(uint64_t aPictureId);
+  async ReceivedDecodedFrame(uint64_t aPictureId);
+  async InputDataExhausted();
+  async ParentShmemForPool(Shmem aEncodedBuffer);
+  // MUST be intr - if sync and we create a new Shmem, when the returned
+  // Shmem is received in the Child it will fail to Deserialize
+  intr NeedShmem(uint32_t aFrameBufferSize) returns (Shmem aMem);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/media/gmp/PGMPVideoEncoder.ipdl
+++ b/content/media/gmp/PGMPVideoEncoder.ipdl
@@ -9,31 +9,36 @@ 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
+intr 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();
+  async InitEncode(GMPVideoCodec aCodecSettings,
+                   int32_t aNumberOfCores,
+                   uint32_t aMaxPayloadSize);
+  async Encode(GMPVideoi420FrameData aInputFrame,
+               GMPCodecSpecificInfo aCodecSpecificInfo,
+               int[] aFrameTypes);
+  async SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT);
+  async SetRates(uint32_t aNewBitRate, uint32_t aFrameRate);
+  async SetPeriodicKeyFrames(bool aEnable);
+  async EncodingComplete();
+  async ChildShmemForPool(Shmem aEncodedBuffer);
 
 parent:
-  __delete__();
-  Encoded(GMPVideoEncodedFrameData aEncodedFrame,
-          GMPCodecSpecificInfo aCodecSpecificInfo);
+  async __delete__();
+  async Encoded(GMPVideoEncodedFrameData aEncodedFrame,
+                GMPCodecSpecificInfo aCodecSpecificInfo);
+  async ParentShmemForPool(Shmem aFrameBuffer);
+  // MUST be intr - if sync and we create a new Shmem, when the returned
+  // Shmem is received in the Child it will fail to Deserialize
+  intr NeedShmem(uint32_t aEncodedBufferSize) returns (Shmem aMem);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/media/gmp/moz.build
+++ b/content/media/gmp/moz.build
@@ -43,16 +43,17 @@ EXPORTS += [
 
 UNIFIED_SOURCES += [
     'GMPChild.cpp',
     'GMPParent.cpp',
     'GMPPlatform.cpp',
     'GMPProcessChild.cpp',
     'GMPProcessParent.cpp',
     'GMPService.cpp',
+    'GMPSharedMemManager.cpp',
     'GMPVideoDecoderChild.cpp',
     'GMPVideoDecoderParent.cpp',
     'GMPVideoEncodedFrameImpl.cpp',
     'GMPVideoEncoderChild.cpp',
     'GMPVideoEncoderParent.cpp',
     'GMPVideoHost.cpp',
     'GMPVideoi420FrameImpl.cpp',
     'GMPVideoPlaneImpl.cpp',
@@ -65,16 +66,19 @@ IPDL_SOURCES += [
   'PGMPVideoEncoder.ipdl',
 ]
 
 LIBRARY_NAME = 'mozgmp'
 
 if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
     NO_VISIBILITY_FLAGS = True
 
+# comment this out to use Unsafe Shmem for more performance
+DEFINES['GMP_SAFE_SHMEM'] = True
+
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../base',
     '/xpcom/base',