Bug 1144523 - Convert input buffer to expected format for OpenH264 GMP plugin. r=cpearce
authorMatthew Gregan <kinetik@flim.org>
Fri, 20 Mar 2015 17:52:10 +1300
changeset 263548 43f2f38859d029ce9bb238ed3dd64ea3275d61fd
parent 263547 e0d9c9cbee73a42a83dbd8d1db9b4f037debcee5
child 263549 4867d8464f88b6b9e15d42056452c75d171b979c
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1144523
milestone39.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 1144523 - Convert input buffer to expected format for OpenH264 GMP plugin. r=cpearce
dom/media/fmp4/gmp/GMPVideoDecoder.cpp
dom/media/fmp4/gmp/GMPVideoDecoder.h
dom/media/gmp/GMPParent.cpp
dom/media/gmp/GMPParent.h
dom/media/gmp/GMPVideoDecoderParent.cpp
dom/media/gmp/GMPVideoDecoderParent.h
dom/media/gmp/GMPVideoDecoderProxy.h
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "GMPVideoDecoder.h"
 #include "GMPVideoHost.h"
+#include "mozilla/Endian.h"
 #include "prsystem.h"
 
 namespace mozilla {
 
 #if defined(DEBUG)
 extern bool IsOnGMPThread();
 #endif
 
@@ -127,23 +128,36 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer
   err = frame->CreateEmptyFrame(aSample->size);
   if (GMP_FAILED(err)) {
     mCallback->Error();
     return nullptr;
   }
 
   memcpy(frame->Buffer(), aSample->data, frame->Size());
 
+  // Convert 4-byte NAL unit lengths to host-endian 4-byte buffer lengths to
+  // suit the GMP API.
+  if (mConvertNALUnitLengths) {
+    const int kNALLengthSize = 4;
+    uint8_t* buf = frame->Buffer();
+    while (buf < frame->Buffer() + frame->Size() - kNALLengthSize) {
+      uint32_t length = BigEndian::readUint32(buf) + kNALLengthSize;
+      *reinterpret_cast<uint32_t *>(buf) = length;
+      buf += length;
+    }
+  }
+
+  frame->SetBufferType(GMP_BufferLength32);
+
   frame->SetEncodedWidth(mConfig.display_width);
   frame->SetEncodedHeight(mConfig.display_height);
   frame->SetTimeStamp(aSample->composition_timestamp);
   frame->SetCompleteFrame(true);
   frame->SetDuration(aSample->duration);
   frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
-  frame->SetBufferType(GMP_BufferLength32);
 
   return frame;
 }
 
 nsresult
 GMPVideoDecoder::Init()
 {
   MOZ_ASSERT(IsOnGMPThread());
@@ -152,16 +166,26 @@ GMPVideoDecoder::Init()
   MOZ_ASSERT(mMPS);
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   nsresult rv = mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), &mHost, &mGMP);
   NS_ENSURE_SUCCESS(rv, rv);
   MOZ_ASSERT(mHost && mGMP);
 
+  // GMP implementations have interpreted the meaning of GMP_BufferLength32
+  // differently.  The OpenH264 GMP expects GMP_BufferLength32 to behave as
+  // specified in the GMP API, where each buffer is prefixed by a 32-bit
+  // host-endian buffer length that includes the size of the buffer length
+  // field.  Other existing GMPs currently expect GMP_BufferLength32 (when
+  // combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
+  // 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
+  // and do not include the length of the buffer length field.
+  mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
+
   GMPVideoCodec codec;
   memset(&codec, 0, sizeof(codec));
 
   codec.mGMPApiVersion = kGMPVersion33;
 
   codec.mCodecType = kGMPVideoCodecH264;
   codec.mWidth = mConfig.display_width;
   codec.mHeight = mConfig.display_height;
--- a/dom/media/fmp4/gmp/GMPVideoDecoder.h
+++ b/dom/media/fmp4/gmp/GMPVideoDecoder.h
@@ -57,16 +57,17 @@ protected:
                   MediaTaskQueue* aTaskQueue,
                   MediaDataDecoderCallbackProxy* aCallback,
                   VideoCallbackAdapter* aAdapter)
    : mConfig(aConfig)
    , mCallback(aCallback)
    , mGMP(nullptr)
    , mHost(nullptr)
    , mAdapter(aAdapter)
+   , mConvertNALUnitLengths(false)
   {
   }
 
 public:
   GMPVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
                   layers::LayersBackend aLayersBackend,
                   layers::ImageContainer* aImageContainer,
                   MediaTaskQueue* aTaskQueue,
@@ -74,16 +75,17 @@ public:
    : mConfig(aConfig)
    , mCallback(aCallback)
    , mGMP(nullptr)
    , mHost(nullptr)
    , mAdapter(new VideoCallbackAdapter(aCallback,
                                        VideoInfo(aConfig.display_width,
                                                  aConfig.display_height),
                                        aImageContainer))
+   , mConvertNALUnitLengths(false)
   {
   }
 
   virtual nsresult Init() MOZ_OVERRIDE;
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
   virtual nsresult Flush() MOZ_OVERRIDE;
   virtual nsresult Drain() MOZ_OVERRIDE;
   virtual nsresult Shutdown() MOZ_OVERRIDE;
@@ -95,14 +97,14 @@ protected:
 
 private:
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
   nsAutoPtr<VideoCallbackAdapter> mAdapter;
+  bool mConvertNALUnitLengths;
 };
 
-
 } // namespace mozilla
 
 #endif // GMPVideoDecoder_h_
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -998,16 +998,22 @@ void
 GMPParent::SetNodeId(const nsACString& aNodeId)
 {
   MOZ_ASSERT(!aNodeId.IsEmpty());
   MOZ_ASSERT(CanBeUsedFrom(aNodeId));
   mNodeId = aNodeId;
 }
 
 const nsCString&
+GMPParent::GetDisplayName() const
+{
+  return mDisplayName;
+}
+
+const nsCString&
 GMPParent::GetVersion() const
 {
   return mVersion;
 }
 
 bool
 GMPParent::RecvAsyncShutdownRequired()
 {
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -110,16 +110,17 @@ public:
   //
   // If a plugin has no NodeId specified and it is loaded, it is assumed to
   // be shared across NodeIds.
 
   // Specifies that a GMP can only work with the specified NodeIds.
   void SetNodeId(const nsACString& aNodeId);
   const nsACString& GetNodeId() const { return mNodeId; }
 
+  const nsCString& GetDisplayName() const;
   const nsCString& GetVersion() const;
 
   // Returns true if a plugin can be or is being used across multiple NodeIds.
   bool CanBeSharedCrossNodeIds() const;
 
   // A GMP can be used from a NodeId if it's already been set to work with
   // that NodeId, or if it's not been set to work with any NodeId and has
   // not yet been loaded (i.e. it's not shared across NodeIds).
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -177,16 +177,28 @@ GMPVideoDecoderParent::Drain()
   if (!SendDrain()) {
     return NS_ERROR_FAILURE;
   }
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
+const nsCString&
+GMPVideoDecoderParent::GetDisplayName() const
+{
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video decoder");
+  }
+
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+
+  return mPlugin->GetDisplayName();
+}
+
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 nsresult
 GMPVideoDecoderParent::Shutdown()
 {
   LOGD(("%s: %p", __FUNCTION__, this));
   MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (mShuttingDown) {
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -40,16 +40,17 @@ public:
                               int32_t aCoreCount) MOZ_OVERRIDE;
   virtual nsresult Decode(GMPUnique<GMPVideoEncodedFrame>::Ptr aInputFrame,
                           bool aMissingFrames,
                           const nsTArray<uint8_t>& aCodecSpecificInfo,
                           int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
   virtual nsresult Reset() MOZ_OVERRIDE;
   virtual nsresult Drain() MOZ_OVERRIDE;
   virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); }
+  virtual const nsCString& GetDisplayName() const MOZ_OVERRIDE;
 
   // GMPSharedMemManager
   virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) MOZ_OVERRIDE
   {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
 #else
     return AllocUnsafeShmem(aSize, aType, aMem);
--- a/dom/media/gmp/GMPVideoDecoderProxy.h
+++ b/dom/media/gmp/GMPVideoDecoderProxy.h
@@ -44,11 +44,13 @@ public:
                           int64_t aRenderTimeMs = -1) = 0;
   virtual nsresult Reset() = 0;
   virtual nsresult Drain() = 0;
   virtual const uint64_t ParentID() = 0;
 
   // Call to tell GMP/plugin the consumer will no longer use this
   // interface/codec.
   virtual void Close() = 0;
+
+  virtual const nsCString& GetDisplayName() const = 0;
 };
 
 #endif