Bug 1037125: Fake GMP plugin that claims to be H.264 r=jesup
authorEKR <ekr@rtfm.com>
Sun, 13 Jul 2014 14:52:44 -0700
changeset 195216 8a186f354cd61de0ad6c13a5a7b01cb3e6f26ac6
parent 195215 2eddd81bb167dae26bacb94777eded1b73ab1a88
child 195217 662c3668db6e5a86948f7de258309e77b581c14e
push id27169
push userryanvm@gmail.com
push dateMon, 21 Jul 2014 01:13:29 +0000
treeherdermozilla-central@4bafe35cfb65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1037125
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 1037125: Fake GMP plugin that claims to be H.264 r=jesup
dom/media/gmp-plugin/fake.info
dom/media/gmp-plugin/gmp-fake.cpp
dom/media/gmp-plugin/moz.build
dom/media/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp-plugin/fake.info
@@ -0,0 +1,4 @@
+Name: fake
+Description: Fake GMP Plugin
+Version: 1.0
+APIs: encode-video[h264], decode-video[h264]
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp-plugin/gmp-fake.cpp
@@ -0,0 +1,419 @@
+/*!
+ * \copy
+ *     Copyright (c)  2009-2014, Cisco Systems
+ *     Copyright (c)  2014, Mozilla
+ *     All rights reserved.
+ *
+ *     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.
+ *
+ *     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.
+ *
+ *
+ *************************************************************************************
+ */
+
+#include <stdint.h>
+#include <time.h>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <string>
+#include <memory>
+#include <assert.h>
+#include <limits.h>
+
+#include "gmp-platform.h"
+#include "gmp-video-host.h"
+#include "gmp-video-encode.h"
+#include "gmp-video-decode.h"
+#include "gmp-video-frame-i420.h"
+#include "gmp-video-frame-encoded.h"
+
+#if defined(_MSC_VER)
+#define PUBLIC_FUNC __declspec(dllexport)
+#else
+#define PUBLIC_FUNC
+#endif
+
+static int g_log_level = 0;
+
+#define GMPLOG(l, x) do { \
+        if (l <= g_log_level) { \
+        const char *log_string = "unknown"; \
+        if ((l >= 0) && (l <= 3)) {               \
+        log_string = kLogStrings[l];            \
+        } \
+        std::cerr << log_string << ": " << x << std::endl; \
+        } \
+    } while(0)
+
+#define GL_CRIT 0
+#define GL_ERROR 1
+#define GL_INFO  2
+#define GL_DEBUG 3
+
+const char* kLogStrings[] = {
+  "Critical",
+  "Error",
+  "Info",
+  "Debug"
+};
+
+
+static GMPPlatformAPI* g_platform_api = NULL;
+
+class FakeVideoEncoder;
+class FakeVideoDecoder;
+
+struct EncodedFrame {
+  uint32_t length_;
+  uint8_t h264_compat_;
+  uint32_t magic_;
+  uint32_t width_;
+  uint32_t height_;
+  uint8_t y_;
+  uint8_t u_;
+  uint8_t v_;
+  uint32_t timestamp_;
+};
+
+#define ENCODED_FRAME_MAGIC 0x4652414d
+
+class FakeEncoderTask : public GMPTask {
+ public:
+  FakeEncoderTask(FakeVideoEncoder* encoder,
+                  GMPVideoi420Frame* frame,
+                  GMPVideoFrameType type)
+      : encoder_(encoder), frame_(frame), type_(type) {}
+
+  virtual void Run();
+  virtual void Destroy() {}
+
+  FakeVideoEncoder* encoder_;
+  GMPVideoi420Frame* frame_;
+  GMPVideoFrameType type_;
+};
+
+class FakeVideoEncoder : public GMPVideoEncoder {
+ public:
+  FakeVideoEncoder (GMPVideoHost* hostAPI) :
+    host_ (hostAPI),
+    callback_ (NULL) {}
+
+  virtual GMPErr InitEncode (const GMPVideoCodec& codecSettings,
+                             const uint8_t* aCodecSpecific,
+                             uint32_t aCodecSpecificSize,
+                             GMPVideoEncoderCallback* callback,
+                             int32_t numberOfCores,
+                             uint32_t maxPayloadSize) {
+    callback_ = callback;
+
+    GMPLOG (GL_INFO, "Initialized encoder");
+
+    return GMPNoErr;
+  }
+
+  virtual GMPErr Encode (GMPVideoi420Frame* inputImage,
+                         const uint8_t* aCodecSpecificInfo,
+                         uint32_t aCodecSpecificInfoLength,
+                         const GMPVideoFrameType* aFrameTypes,
+                         uint32_t aFrameTypesLength) {
+    GMPLOG (GL_DEBUG,
+            __FUNCTION__
+            << " size="
+            << inputImage->Width() << "x" << inputImage->Height());
+
+    assert (aFrameTypesLength != 0);
+
+    g_platform_api->runonmainthread(new FakeEncoderTask(this,
+                                                        inputImage,
+                                                        aFrameTypes[0]));
+
+    return GMPGenericErr;
+  }
+
+  void Encode_m (GMPVideoi420Frame* inputImage,
+                 GMPVideoFrameType frame_type) {
+    if (frame_type  == kGMPKeyFrame) {
+      if (!inputImage)
+        return;
+    }
+    if (!inputImage) {
+      GMPLOG (GL_ERROR, "no input image");
+      return;
+    }
+
+    // Now return the encoded data back to the parent.
+    GMPVideoFrame* ftmp;
+    GMPErr err = host_->CreateFrame (kGMPEncodedVideoFrame, &ftmp);
+    if (err != GMPNoErr) {
+      GMPLOG (GL_ERROR, "Error creating encoded frame");
+      return;
+    }
+
+    GMPVideoEncodedFrame* f = static_cast<GMPVideoEncodedFrame*> (ftmp);
+
+    // Copy the data. This really should convert this to network byte order.
+    EncodedFrame eframe;
+    eframe.length_ = sizeof(eframe) - sizeof(uint32_t);
+    eframe.h264_compat_ = 'g';
+    eframe.magic_ = ENCODED_FRAME_MAGIC;
+    eframe.width_ = inputImage->Width();
+    eframe.height_ = inputImage->Height();
+    eframe.y_ = AveragePlane(inputImage->Buffer(kGMPYPlane),
+                             inputImage->AllocatedSize(kGMPYPlane));
+    eframe.u_ = AveragePlane(inputImage->Buffer(kGMPUPlane),
+                             inputImage->AllocatedSize(kGMPUPlane));
+    eframe.v_ = AveragePlane(inputImage->Buffer(kGMPVPlane),
+                             inputImage->AllocatedSize(kGMPVPlane));
+
+    eframe.timestamp_ = inputImage->Timestamp();
+
+    err = f->CreateEmptyFrame (sizeof(eframe));
+    if (err != GMPNoErr) {
+      GMPLOG (GL_ERROR, "Error allocating frame data");
+      f->Destroy();
+      return;
+    }
+    memcpy(f->Buffer(), &eframe, sizeof(eframe));
+
+    f->SetEncodedWidth (inputImage->Width());
+    f->SetEncodedHeight (inputImage->Height());
+    f->SetTimeStamp (inputImage->Timestamp());
+    f->SetFrameType (frame_type);
+    f->SetCompleteFrame (true);
+    f->SetBufferType(GMP_BufferLength32);
+
+    GMPLOG (GL_DEBUG, "Encoding complete. type= "
+            << f->FrameType()
+            << " length="
+            << f->Size()
+            << " timestamp="
+            << f->TimeStamp());
+
+    // Return the encoded frame.
+    GMPCodecSpecificInfo info;
+    memset (&info, 0, sizeof (info));
+    info.mCodecType = kGMPVideoCodecH264;
+    info.mBufferType = GMP_BufferLength32;
+    info.mCodecSpecific.mH264.mSimulcastIdx = 0;
+    GMPLOG (GL_DEBUG, "Calling callback");
+    callback_->Encoded (f, reinterpret_cast<uint8_t*> (&info), sizeof(info));
+    GMPLOG (GL_DEBUG, "Callback called");
+  }
+
+  virtual GMPErr SetChannelParameters (uint32_t aPacketLoss, uint32_t aRTT) {
+    return GMPNoErr;
+  }
+
+  virtual GMPErr SetRates (uint32_t aNewBitRate, uint32_t aFrameRate) {
+    return GMPNoErr;
+  }
+
+  virtual GMPErr SetPeriodicKeyFrames (bool aEnable) {
+    return GMPNoErr;
+  }
+
+  virtual void EncodingComplete() {
+    delete this;
+  }
+
+ private:
+  uint8_t AveragePlane(uint8_t* ptr, size_t len) {
+    uint64_t val = 0;
+
+    for (size_t i=0; i<len; ++i) {
+      val += ptr[i];
+    }
+
+    return (val / len) % 0xff;
+  }
+
+  GMPVideoHost* host_;
+  GMPVideoEncoderCallback* callback_;
+};
+
+void FakeEncoderTask::Run() {
+  encoder_->Encode_m(frame_, type_);
+  frame_->Destroy();
+}
+
+class FakeDecoderTask : public GMPTask {
+ public:
+  FakeDecoderTask(FakeVideoDecoder* decoder,
+                  GMPVideoEncodedFrame* frame,
+                  int64_t time)
+      : decoder_(decoder), frame_(frame), time_(time) {}
+
+  virtual void Run();
+  virtual void Destroy() {}
+
+  FakeVideoDecoder* decoder_;
+  GMPVideoEncodedFrame* frame_;
+  int64_t time_;
+};
+
+class FakeVideoDecoder : public GMPVideoDecoder {
+ public:
+  FakeVideoDecoder (GMPVideoHost* hostAPI) :
+    host_ (hostAPI),
+    callback_ (NULL) {}
+
+  virtual ~FakeVideoDecoder() {
+  }
+
+  virtual GMPErr InitDecode (const GMPVideoCodec& codecSettings,
+                             const uint8_t* aCodecSpecific,
+                             uint32_t aCodecSpecificSize,
+                             GMPVideoDecoderCallback* callback,
+                             int32_t coreCount) {
+    GMPLOG (GL_INFO, "InitDecode");
+
+    callback_ = callback;
+    return GMPNoErr;
+  }
+
+  virtual GMPErr Decode (GMPVideoEncodedFrame* inputFrame,
+                         bool missingFrames,
+                         const uint8_t* aCodecSpecificInfo,
+                         uint32_t aCodecSpecificInfoLength,
+                         int64_t renderTimeMs = -1) {
+    GMPLOG (GL_DEBUG, __FUNCTION__
+            << "Decoding frame size=" << inputFrame->Size()
+            << " timestamp=" << inputFrame->TimeStamp());
+    g_platform_api->runonmainthread(new FakeDecoderTask(this, inputFrame, renderTimeMs));
+
+    return GMPNoErr;
+  }
+
+  virtual GMPErr Reset() {
+    return GMPNoErr;
+  }
+
+  virtual GMPErr Drain() {
+    return GMPNoErr;
+  }
+
+  virtual void DecodingComplete() {
+    delete this;
+  }
+
+  // Return the decoded data back to the parent.
+  void Decode_m (GMPVideoEncodedFrame* inputFrame,
+                 int64_t renderTimeMs) {
+    EncodedFrame *eframe;
+    if (inputFrame->Size() != (sizeof(*eframe))) {
+      GMPLOG (GL_ERROR, "Couldn't decode frame. Size=" << inputFrame->Size());
+      return;
+    }
+    eframe = reinterpret_cast<EncodedFrame*>(inputFrame->Buffer());
+
+    if (eframe->magic_ != ENCODED_FRAME_MAGIC) {
+      GMPLOG (GL_ERROR, "Couldn't decode frame. Magic=" << eframe->magic_);
+      return;
+    }
+
+    int width = eframe->width_;
+    int height = eframe->height_;
+    int ystride = eframe->width_;
+    int uvstride = eframe->width_/2;
+
+    GMPLOG (GL_DEBUG, "Video frame ready for display "
+            << width
+            << "x"
+            << height
+            << " timestamp="
+            << inputFrame->TimeStamp());
+
+    GMPVideoFrame* ftmp = NULL;
+
+    // Translate the image.
+    GMPErr err = host_->CreateFrame (kGMPI420VideoFrame, &ftmp);
+    if (err != GMPNoErr) {
+      GMPLOG (GL_ERROR, "Couldn't allocate empty I420 frame");
+      return;
+    }
+
+    GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*> (ftmp);
+    err = frame->CreateEmptyFrame (
+        width, height,
+        ystride, uvstride, uvstride);
+    if (err != GMPNoErr) {
+      GMPLOG (GL_ERROR, "Couldn't make decoded frame");
+      return;
+    }
+
+    memset(frame->Buffer(kGMPYPlane),
+           eframe->y_,
+           frame->AllocatedSize(kGMPYPlane));
+    memset(frame->Buffer(kGMPUPlane),
+           eframe->u_,
+           frame->AllocatedSize(kGMPUPlane));
+    memset(frame->Buffer(kGMPVPlane),
+           eframe->v_,
+           frame->AllocatedSize(kGMPVPlane));
+
+    GMPLOG (GL_DEBUG, "Allocated size = "
+            << frame->AllocatedSize (kGMPYPlane));
+    frame->SetTimestamp (inputFrame->TimeStamp());
+    frame->SetDuration (inputFrame->Duration());
+    callback_->Decoded (frame);
+
+  }
+
+  GMPVideoHost* host_;
+  GMPVideoDecoderCallback* callback_;
+};
+
+void FakeDecoderTask::Run() {
+  decoder_->Decode_m(frame_, time_);
+  frame_->Destroy();
+}
+
+extern "C" {
+
+  PUBLIC_FUNC GMPErr
+  GMPInit (GMPPlatformAPI* aPlatformAPI) {
+    g_platform_api = aPlatformAPI;
+    return GMPNoErr;
+  }
+
+  PUBLIC_FUNC GMPErr
+  GMPGetAPI (const char* aApiName, void* aHostAPI, void** aPluginApi) {
+    if (!strcmp (aApiName, "decode-video")) {
+      *aPluginApi = new FakeVideoDecoder (static_cast<GMPVideoHost*> (aHostAPI));
+      return GMPNoErr;
+    } else if (!strcmp (aApiName, "encode-video")) {
+      *aPluginApi = new FakeVideoEncoder (static_cast<GMPVideoHost*> (aHostAPI));
+      return GMPNoErr;
+    }
+    return GMPGenericErr;
+  }
+
+  PUBLIC_FUNC void
+  GMPShutdown (void) {
+    g_platform_api = NULL;
+  }
+
+} // extern "C"
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp-plugin/moz.build
@@ -0,0 +1,18 @@
+# -*- 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/.
+
+NO_DIST_INSTALL = True
+SOURCES += [
+        'gmp-fake.cpp'
+]
+
+LIBRARY_NAME = "gmp-fake"
+
+USE_STATIC_LIBS = True
+FORCE_SHARED_LIB = True
+NO_VISIBILITY_FLAGS = True
+# Don't use STL wrappers; this isn't Gecko code
+DISABLE_STL_WRAPPING = True
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -1,16 +1,16 @@
 # -*- 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/.
 
 if CONFIG['MOZ_WEBRTC']:
-    DIRS += ['bridge']
+    DIRS += ['bridge', 'gmp-plugin']
 
     LOCAL_INCLUDES += [
         '/media/webrtc/signaling/src/common',
         '/media/webrtc/trunk',
     ]
 
     if CONFIG['GNU_CXX']:
         CXXFLAGS += [