Merge pull request #981 from ethanhugg/gmp_rollup

Rollup of Gecko Media Plugin patches
This commit is contained in:
ruil2 2014-06-18 11:17:25 +08:00
commit 4cc4f364d6
9 changed files with 2873 additions and 2 deletions

View File

@ -5,5 +5,19 @@ compiler:
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq nasm g++-4.6-multilib gcc-multilib libc6-dev-i386
install: make gtest-bootstrap
script: make -B ENABLE64BIT=Yes && make test && make -B ENABLE64BIT=Yes BUILDTYPE=Debug && make test && make -B ENABLE64BIT=No && make test && make -B ENABLE64BIT=No BUILDTYPE=Debug && make test
install:
- make gmp-bootstrap
- make gtest-bootstrap
script:
- make -B ENABLE64BIT=Yes
- make -B ENABLE64BIT=Yes plugin
- make -B ENABLE64BIT=Yes test
- make -B ENABLE64BIT=Yes BUILDTYPE=Debug
- make -B ENABLE64BIT=Yes BUILDTYPE=Debug plugin
- make -B ENABLE64BIT=Yes test
- make -B ENABLE64BIT=No
- make -B ENABLE64BIT=No plugin
- make -B ENABLE64BIT=No test
- make -B ENABLE64BIT=No BUILDTYPE=Debug
- make -B ENABLE64BIT=No BUILDTYPE=Debug plugin
- make -B ENABLE64BIT=No test

View File

@ -15,8 +15,15 @@ PREFIX=/usr/local
SHARED=-shared
OBJ=o
PROJECT_NAME=openh264
MODULE_NAME=gmpopenh264
CCASFLAGS=$(CFLAGS)
ifeq (,$(wildcard ./gmp-api))
HAVE_GMP_API=No
else
HAVE_GMP_API=Yes
endif
ifeq (,$(wildcard ./gtest))
HAVE_GTEST=No
else
@ -98,6 +105,8 @@ ENCODER_UNITTEST_INCLUDES = $(CODEC_UNITTEST_INCLUDES) $(ENCODER_INCLUDES) -Ites
PROCESSING_UNITTEST_INCLUDES = $(CODEC_UNITTEST_INCLUDES) $(PROCESSING_INCLUDES) -Itest -Itest/processing
API_TEST_INCLUDES = $(CODEC_UNITTEST_INCLUDES) -Itest -Itest/api
COMMON_UNITTEST_INCLUDES = $(CODEC_UNITTEST_INCLUDES) $(DECODER_INCLUDES) -Itest -Itest/common
MODULE_INCLUDES = -Igmp-api
.PHONY: test gtest-bootstrap clean
all: libraries binaries
@ -108,6 +117,9 @@ clean: clean_Android
endif
$(QUIET)rm -f $(OBJS) $(OBJS:.$(OBJ)=.d) $(LIBRARIES) $(BINARIES)
gmp-bootstrap:
git clone https://github.com/mozilla/gmp-api gmp-api
gtest-bootstrap:
svn co https://googletest.googlecode.com/svn/trunk/ gtest
@ -131,6 +143,10 @@ include codec/decoder/targets.mk
include codec/encoder/targets.mk
include codec/processing/targets.mk
ifeq ($(HAVE_GMP_API),Yes)
include module/targets.mk
endif
ifneq (android, $(OS))
ifneq (ios, $(OS))
include codec/console/dec/targets.mk
@ -154,6 +170,19 @@ $(LIBPREFIX)$(PROJECT_NAME).$(SHAREDLIBSUFFIX): $(ENCODER_OBJS) $(DECODER_OBJS)
$(QUIET)rm -f $@
$(QUIET_CXX)$(CXX) $(SHARED) $(LDFLAGS) $(CXX_LINK_O) $+ $(SHLDFLAGS)
ifeq ($(HAVE_GMP_API),Yes)
plugin: $(LIBPREFIX)$(MODULE_NAME).$(SHAREDLIBSUFFIX)
PLUGINS += $(LIBPREFIX)$(MODULE_NAME).$(SHAREDLIBSUFFIX)
else
plugin:
@echo "./gmp-api : No such file or directory."
@echo "You do not have gmp-api. Run make gmp-bootstrap to get the gmp-api headers."
endif
$(LIBPREFIX)$(MODULE_NAME).$(SHAREDLIBSUFFIX): $(MODULE_OBJS) $(ENCODER_OBJS) $(DECODER_OBJS) $(PROCESSING_OBJS) $(COMMON_OBJS)
$(QUIET)rm -f $@
$(QUIET_CXX)$(CXX) $(SHARED) $(LDFLAGS) $(CXX_LINK_O) $+ $(SHLDFLAGS)
install-headers:
mkdir -p $(PREFIX)/include/wels
install -m 644 codec/api/svc/codec*.h $(PREFIX)/include/wels

View File

@ -11,4 +11,5 @@ python build/mktargets.py --directory test/encoder --prefix encoder_unittest
python build/mktargets.py --directory test/decoder --prefix decoder_unittest
python build/mktargets.py --directory test/processing --prefix processing_unittest
python build/mktargets.py --directory test/api --prefix api_test
python build/mktargets.py --directory module --library module
python build/mktargets.py --directory gtest --library gtest --out build/gtest-targets.mk --cpp-suffix .cc --include gtest-all.cc

4
gmpopenh264.info Normal file
View File

@ -0,0 +1,4 @@
Name: gmpopenh264
Description: GMP Plugin for OpenH264.
Version: 1.0
APIs: encode-video[h264:vp8], decode-video[h264:vp8]

703
module/gmp-openh264.cpp Normal file
View File

@ -0,0 +1,703 @@
/*!
* \copy
* Copyright (c) 2009-2014, Cisco Systems
* 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 <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"
#include "codec_def.h"
#include "codec_app_def.h"
#include "codec_api.h"
#include "task_utils.h"
#if defined(_MSC_VER)
#define PUBLIC_FUNC __declspec(dllexport)
#else
#define PUBLIC_FUNC
#endif
// This is for supporting older versions which do not have support for nullptr.
#if defined(__clang__)
# ifndef __has_extension
# define __has_extension __has_feature
# endif
# if __has_extension(cxx_nullptr)
# define GMP_HAVE_NULLPTR
# endif
#elif defined(__GNUC__)
# if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
# if (__GNU_C__ >=4)
# if (__GNU_C_MINOR__ >= 6)
# define GMP_HAVE_NULLPTR
# endif
# endif
# endif
#elif defined(_MSC_VER)
# define GMP_HAVE_NULLPTR
#endif
#if !defined (GMP_HAVE_NULLPTR)
# define nullptr __null
#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 = nullptr;
class OpenH264VideoEncoder;
template <typename T> class SelfDestruct {
public:
SelfDestruct(T* t) : t_(t) {}
~SelfDestruct() {
if (t_) {
t_->Destroy();
}
}
T* forget() {
T* t = t_;
t_ = nullptr;
return t;
}
private:
T* t_;
};
class FrameStats {
public:
FrameStats(const char *type) :
frames_in_(0),
frames_out_(0),
start_time_(time(0)),
last_time_(start_time_),
type_(type) {}
void FrameIn() {
++frames_in_;
time_t now = time(0);
if (now == last_time_) {
return;
}
if (!(frames_in_ % 10)) {
GMPLOG(GL_INFO, type_ << ": " << now << " Frame count "
<< frames_in_
<< "(" << (frames_in_ / (now - start_time_)) << "/"
<< (30 / (now - last_time_)) << ")"
<< " -- " << frames_out_);
last_time_ = now;
}
}
void FrameOut() {
++frames_out_;
}
private:
uint64_t frames_in_;
uint64_t frames_out_;
time_t start_time_;
time_t last_time_;
const std::string type_;
};
class OpenH264VideoEncoder : public GMPVideoEncoder
{
public:
OpenH264VideoEncoder(GMPVideoHost *hostAPI) :
host_(hostAPI),
worker_thread_(nullptr),
encoder_(nullptr),
max_payload_size_(0),
callback_(nullptr),
stats_("Encoder") {}
virtual ~OpenH264VideoEncoder() {
worker_thread_->Join();
}
virtual GMPVideoErr InitEncode(const GMPVideoCodec& codecSettings,
GMPEncoderCallback* callback,
int32_t numberOfCores,
uint32_t maxPayloadSize) {
GMPErr err = g_platform_api->createthread(&worker_thread_);
if (err != GMPNoErr) {
GMPLOG(GL_ERROR, "Couldn't create new thread");
return GMPVideoGenericErr;
}
int rv = WelsCreateSVCEncoder(&encoder_);
if (rv) {
return GMPVideoGenericErr;
}
SEncParamBase param;
memset(&param, 0, sizeof(param));
GMPLOG(GL_INFO, "Initializing encoder at "
<< codecSettings.mWidth
<< "x"
<< codecSettings.mHeight
<< "@"
<< static_cast<int>(codecSettings.mMaxFramerate)
<< "max payload size="
<< maxPayloadSize);
// Translate parameters.
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
param.iPicWidth = codecSettings.mWidth;
param.iPicHeight = codecSettings.mHeight;
param.iTargetBitrate = codecSettings.mStartBitrate * 1000;
GMPLOG(GL_INFO, "Initializing Bit Rate at: Start: "
<< codecSettings.mStartBitrate
<< "; Min: "
<< codecSettings.mMinBitrate
<< "; Max: "
<< codecSettings.mMaxBitrate);
param.iRCMode = RC_BITRATE_MODE;
// TODO(ekr@rtfm.com). Scary conversion from unsigned char to float below.
param.fMaxFrameRate = static_cast<float>(codecSettings.mMaxFramerate);
param.iInputCsp = videoFormatI420;
rv = encoder_->Initialize(&param);
if (rv) {
GMPLOG(GL_ERROR, "Couldn't initialize encoder");
return GMPVideoGenericErr;
}
max_payload_size_ = maxPayloadSize;
callback_ = callback;
GMPLOG(GL_INFO, "Initialized encoder");
return GMPVideoNoErr;
}
virtual GMPVideoErr Encode(GMPVideoi420Frame* inputImage,
const GMPCodecSpecificInfo& codecSpecificInfo,
const std::vector<GMPVideoFrameType>& frameTypes)
{
GMPLOG(GL_DEBUG,
__FUNCTION__
<< " size="
<< inputImage->Width() << "x" << inputImage->Height());
stats_.FrameIn();
assert(!frameTypes.empty());
if (frameTypes.empty()) {
GMPLOG(GL_ERROR, "No frame types provided");
return GMPVideoGenericErr;
}
worker_thread_->Post(WrapTask(
this, &OpenH264VideoEncoder::Encode_w,
inputImage,
(frameTypes)[0]));
return GMPVideoGenericErr;
}
void Encode_w(GMPVideoi420Frame* inputImage,
GMPVideoFrameType frame_type) {
SFrameBSInfo encoded;
SSourcePicture src;
src.iColorFormat = videoFormatI420;
src.iStride[0] = inputImage->Stride(kGMPYPlane);
src.pData[0] = reinterpret_cast<unsigned char*>(
const_cast<uint8_t *>(inputImage->Buffer(kGMPYPlane)));
src.iStride[1] = inputImage->Stride(kGMPUPlane);
src.pData[1] = reinterpret_cast<unsigned char*>(
const_cast<uint8_t *>(inputImage->Buffer(kGMPUPlane)));
src.iStride[2] = inputImage->Stride(kGMPVPlane);
src.pData[2] = reinterpret_cast<unsigned char*>(
const_cast<uint8_t *>(inputImage->Buffer(kGMPVPlane)));
src.iStride[3] = 0;
src.pData[3] = nullptr;
src.iPicWidth = inputImage->Width();
src.iPicHeight = inputImage->Height();
const SSourcePicture* pics = &src;
int result = encoder_->EncodeFrame(pics, &encoded);
if (result != cmResultSuccess) {
GMPLOG(GL_ERROR, "Couldn't encode frame. Error = " << result);
}
// Translate int to enum
GMPVideoFrameType encoded_type;
bool has_frame = false;
switch (encoded.eOutputFrameType) {
case videoFrameTypeIDR:
encoded_type = kGMPKeyFrame;
has_frame = true;
break;
case videoFrameTypeI:
encoded_type = kGMPKeyFrame;
has_frame = true;
break;
case videoFrameTypeP:
encoded_type = kGMPDeltaFrame;
has_frame = true;
break;
case videoFrameTypeSkip:
// Can skip the call back since no actual bitstream will be generated
break;
case videoFrameTypeIPMixed://this type is currently not suppported
case videoFrameTypeInvalid:
GMPLOG(GL_ERROR, "Couldn't encode frame. Type = "
<< encoded.eOutputFrameType);
break;
default:
// The API is defined as returning a type.
assert(false);
break;
}
if (!has_frame) {
return;
}
// Synchronously send this back to the main thread for delivery.
g_platform_api->syncrunonmainthread(WrapTask(
this,
&OpenH264VideoEncoder::Encode_m,
inputImage,
&encoded,
encoded_type));
}
void Encode_m(GMPVideoi420Frame* frame, SFrameBSInfo* encoded,
GMPVideoFrameType frame_type) {
// Now return the encoded data back to the parent.
GMPVideoFrame* ftmp;
GMPVideoErr err = host_->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (err != GMPVideoNoErr) {
GMPLOG(GL_ERROR, "Error creating encoded frame");
return;
}
GMPVideoEncodedFrame* f = static_cast<GMPVideoEncodedFrame*>(ftmp);
// Buffer up the data.
uint32_t length = 0;
std::vector<uint32_t> lengths;
for (int i=0; i<encoded->iLayerNum; ++i) {
lengths.push_back(0);
for (int j=0; j<encoded->sLayerInfo[i].iNalCount; ++j) {
lengths[i] += encoded->sLayerInfo[i].pNalLengthInByte[j];
length += encoded->sLayerInfo[i].pNalLengthInByte[j];
}
}
// TODO start-code to length conversion here when gmp
// stops doing it for us before this call.
err = f->CreateEmptyFrame(length);
if (err != GMPVideoNoErr) {
GMPLOG(GL_ERROR, "Error allocating frame data");
f->Destroy();
return;
}
// Copy the data.
// Here we concatenate into one big buffer
uint8_t* tmp = f->Buffer();
for (int i=0; i<encoded->iLayerNum; ++i) {
memcpy(tmp, encoded->sLayerInfo[i].pBsBuf, lengths[i]);
tmp += lengths[i];
}
f->SetEncodedWidth(frame->Width());
f->SetEncodedHeight(frame->Height());
f->SetTimeStamp(frame->Timestamp());
f->SetFrameType(frame_type);
f->SetCompleteFrame(true);
GMPLOG(GL_DEBUG, "Encoding complete. type= "
<< f->FrameType()
<< " length="
<< f->Size()
<< " timestamp="
<< f->TimeStamp());
// Destroy the frame.
frame->Destroy();
// Return the encoded frame.
GMPCodecSpecificInfo info;
memset(&info, 0, sizeof(info));
// TODO need to set what goes in this info structure.
callback_->Encoded(f, info);
stats_.FrameOut();
}
virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) {
return GMPVideoNoErr;
}
virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) {
GMPLOG(GL_INFO, "[SetRates] Begin with: "
<< aNewBitRate << " , "<< aFrameRate);
//update bitrate if needed
const int32_t newBitRate = aNewBitRate*1000; //kbps->bps
SBitrateInfo existEncoderBitRate;
existEncoderBitRate.iLayer = SPATIAL_LAYER_ALL;
int rv = encoder_->GetOption(ENCODER_OPTION_BITRATE, &existEncoderBitRate);
if (rv!=cmResultSuccess) {
GMPLOG(GL_ERROR, "[SetRates] Error in Getting Bit Rate at Layer:"
<< rv
<< " ; Layer = "
<< existEncoderBitRate.iLayer
<< " ; BR = "
<< existEncoderBitRate.iBitrate);
return GMPVideoGenericErr;
}
if ( rv==cmResultSuccess && existEncoderBitRate.iBitrate!=newBitRate ) {
SBitrateInfo newEncoderBitRate;
newEncoderBitRate.iLayer = SPATIAL_LAYER_ALL;
newEncoderBitRate.iBitrate = newBitRate;
rv = encoder_->SetOption(ENCODER_OPTION_BITRATE, &newEncoderBitRate);
if (rv==cmResultSuccess) {
GMPLOG(GL_INFO, "[SetRates] Update Encoder Bandwidth (AllLayers): ReturnValue: "
<< rv
<< "BitRate(kbps): "
<< aNewBitRate);
} else {
GMPLOG(GL_ERROR, "[SetRates] Error in Setting Bit Rate at Layer:"
<< rv
<< " ; Layer = "
<< newEncoderBitRate.iLayer
<< " ; BR = "
<< newEncoderBitRate.iBitrate);
return GMPVideoGenericErr;
}
}
//update framerate if needed
float existFrameRate = 0;
rv = encoder_->GetOption(ENCODER_OPTION_FRAME_RATE, &existFrameRate);
if (rv!=cmResultSuccess) {
GMPLOG(GL_ERROR, "[SetRates] Error in Getting Frame Rate:"
<< rv << " FrameRate: " << existFrameRate);
return GMPVideoGenericErr;
}
if ( rv==cmResultSuccess &&
( aFrameRate-existFrameRate > 0.001f ||
existFrameRate-aFrameRate > 0.001f ) ) {
float newFrameRate = static_cast<float>(aFrameRate);
rv = encoder_->SetOption(ENCODER_OPTION_FRAME_RATE, &newFrameRate);
if (rv==cmResultSuccess) {
GMPLOG(GL_INFO, "[SetRates] Update Encoder Frame Rate: ReturnValue: "
<< rv << " FrameRate: " << aFrameRate);
} else {
GMPLOG(GL_ERROR, "[SetRates] Error in Setting Frame Rate: ReturnValue: "
<< rv << " FrameRate: " << aFrameRate);
return GMPVideoGenericErr;
}
}
return GMPVideoNoErr;
}
virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) {
return GMPVideoNoErr;
}
virtual void EncodingComplete() {
delete this;
}
private:
GMPVideoHost* host_;
GMPThread* worker_thread_;
ISVCEncoder* encoder_;
uint32_t max_payload_size_;
GMPEncoderCallback* callback_;
FrameStats stats_;
};
class OpenH264VideoDecoder : public GMPVideoDecoder {
public:
OpenH264VideoDecoder(GMPVideoHost *hostAPI) :
host_(hostAPI),
worker_thread_(nullptr),
callback_(nullptr),
decoder_(nullptr),
stats_("Decoder") {}
virtual ~OpenH264VideoDecoder() {
}
virtual GMPVideoErr InitDecode(const GMPVideoCodec& codecSettings,
GMPDecoderCallback* callback,
int32_t coreCount) {
GMPLOG(GL_INFO, "InitDecode");
GMPErr err = g_platform_api->createthread(&worker_thread_);
if (err != GMPNoErr) {
GMPLOG(GL_ERROR, "Couldn't create new thread");
return GMPVideoGenericErr;
}
if (WelsCreateDecoder(&decoder_)) {
GMPLOG(GL_ERROR, "Couldn't create decoder");
return GMPVideoGenericErr;
}
if (!decoder_) {
GMPLOG(GL_ERROR, "Couldn't create decoder");
return GMPVideoGenericErr;
}
SDecodingParam param;
memset(&param, 0, sizeof(param));
param.iOutputColorFormat = videoFormatI420;
param.uiTargetDqLayer = UCHAR_MAX; // Default value
param.uiEcActiveFlag = 1; // Error concealment on.
param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
if (decoder_->Initialize(&param)) {
GMPLOG(GL_ERROR, "Couldn't initialize decoder");
return GMPVideoGenericErr;
}
callback_ = callback;
return GMPVideoNoErr;
}
virtual GMPVideoErr Decode(GMPVideoEncodedFrame* inputFrame,
bool missingFrames,
const GMPCodecSpecificInfo& codecSpecificInfo,
int64_t renderTimeMs = -1) {
GMPLOG(GL_DEBUG, __FUNCTION__
<< "Decoding frame size=" << inputFrame->Size()
<< " timestamp=" << inputFrame->TimeStamp());
stats_.FrameIn();
worker_thread_->Post(WrapTask(
this, &OpenH264VideoDecoder::Decode_w,
inputFrame,
missingFrames,
renderTimeMs));
return GMPVideoNoErr;
}
virtual GMPVideoErr Reset() {
return GMPVideoNoErr;
}
virtual GMPVideoErr Drain() {
return GMPVideoNoErr;
}
virtual void DecodingComplete() {
delete this;
}
private:
void Decode_w(GMPVideoEncodedFrame* inputFrame,
bool missingFrames,
int64_t renderTimeMs = -1) {
GMPLOG(GL_DEBUG, "Frame decode on worker thread length = "
<< inputFrame->Size());
SBufferInfo decoded;
bool valid = false;
memset(&decoded, 0, sizeof(decoded));
unsigned char *data[3] = {nullptr, nullptr, nullptr};
int rv = decoder_->DecodeFrame2(inputFrame->Buffer(),
inputFrame->Size(),
data,
&decoded);
if (rv) {
GMPLOG(GL_ERROR, "Decoding error rv=" << rv);
} else {
valid = true;
}
g_platform_api->syncrunonmainthread(WrapTask(
this,
&OpenH264VideoDecoder::Decode_m,
inputFrame,
&decoded,
data,
renderTimeMs,
valid));
}
// Return the decoded data back to the parent.
void Decode_m(GMPVideoEncodedFrame* inputFrame,
SBufferInfo* decoded,
unsigned char* data[3],
int64_t renderTimeMs,
bool valid) {
// Attach a self-destructor so that this dies on return.
SelfDestruct<GMPVideoEncodedFrame> ifd(inputFrame);
// If we don't actually have data, just abort.
if (!valid) {
return;
}
if (decoded->iBufferStatus != 1) {
return;
}
int width = decoded->UsrData.sSystemBuffer.iWidth;
int height = decoded->UsrData.sSystemBuffer.iHeight;
int ystride = decoded->UsrData.sSystemBuffer.iStride[0];
int uvstride = decoded->UsrData.sSystemBuffer.iStride[1];
GMPLOG(GL_DEBUG, "Video frame ready for display "
<< width
<< "x"
<< height
<< " timestamp="
<< inputFrame->TimeStamp());
GMPVideoFrame* ftmp = nullptr;
// Translate the image.
GMPVideoErr err = host_->CreateFrame(kGMPI420VideoFrame, &ftmp);
if (err != GMPVideoNoErr) {
GMPLOG(GL_ERROR, "Couldn't allocate empty I420 frame");
return;
}
GMPVideoi420Frame* frame = static_cast<GMPVideoi420Frame*>(ftmp);
err = frame->CreateFrame(
ystride * height, static_cast<uint8_t *>(data[0]),
uvstride * height/2, static_cast<uint8_t *>(data[1]),
uvstride * height/2, static_cast<uint8_t *>(data[2]),
width, height,
ystride, uvstride, uvstride);
if (err != GMPVideoNoErr) {
GMPLOG(GL_ERROR, "Couldn't make decoded frame");
return;
}
GMPLOG(GL_DEBUG, "Allocated size = "
<< frame->AllocatedSize(kGMPYPlane));
frame->SetTimestamp(inputFrame->TimeStamp());
frame->SetRenderTime_ms(renderTimeMs);
callback_->Decoded(frame);
stats_.FrameOut();
}
GMPVideoHost* host_;
GMPThread* worker_thread_;
GMPDecoderCallback* callback_;
ISVCDecoder* decoder_;
FrameStats stats_;
};
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 OpenH264VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
return GMPNoErr;
} else if (!strcmp(aApiName, "encode-video")) {
*aPluginApi = new OpenH264VideoEncoder(static_cast<GMPVideoHost*>(aHostAPI));
return GMPNoErr;
}
return GMPGenericErr;
}
PUBLIC_FUNC void
GMPShutdown(void) {
g_platform_api = nullptr;
}
} // extern "C"

16
module/targets.mk Normal file
View File

@ -0,0 +1,16 @@
MODULE_SRCDIR=module
MODULE_CPP_SRCS=\
$(MODULE_SRCDIR)/gmp-openh264.cpp\
MODULE_OBJS += $(MODULE_CPP_SRCS:.cpp=.$(OBJ))
OBJS += $(MODULE_OBJS)
$(MODULE_SRCDIR)/%.$(OBJ): $(MODULE_SRCDIR)/%.cpp
$(QUIET_CXX)$(CXX) $(CFLAGS) $(CXXFLAGS) $(INCLUDES) $(MODULE_CFLAGS) $(MODULE_INCLUDES) -c $(CXX_O) $<
$(LIBPREFIX)module.$(LIBSUFFIX): $(MODULE_OBJS)
$(QUIET)rm -f $@
$(QUIET_AR)$(AR) $(AR_OPTS) $+
plugin: $(LIBPREFIX)module.$(LIBSUFFIX)
LIBRARIES += $(LIBPREFIX)module.$(LIBSUFFIX)

35
module/task_utils.h Normal file
View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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/. */
// Original author: ekr@rtfm.com
#ifndef task_utils_h__
#define task_utils_h__
class gmp_args_base : public GMPTask {
public:
void Run() = 0;
};
// The generated file contains four major function templates
// (in variants for arbitrary numbers of arguments up to 10,
// which is why it is machine generated). The four templates
// are:
//
// WrapTask(o, m, ...) -- wraps a member function m of an object ptr o
// WrapTaskRet(o, m, ..., r) -- wraps a member function m of an object ptr o
// the function returns something that can
// be assigned to *r
// WrapTaskNM(f, ...) -- wraps a function f
// WrapTaskNMRet(f, ..., r) -- wraps a function f that returns something
// that can be assigned to *r
//
// All of these template functions return a Task* which can be passed
// to Post().
#include "task_utils_generated.h"
#endif

171
module/task_utils.py Normal file
View File

@ -0,0 +1,171 @@
# 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/.
MAX_ARGS = 15
boilerplate = "/* This Source Code Form is subject to the terms of the Mozilla Public\n\
* License, v. 2.0. If a copy of the MPL was not distributed with this\n\
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n"
def gen_args_type(args, member):
if member:
ret = ["C o"]
else:
ret = []
ret.append("M m")
for arg in range(0, args):
ret.append("A%d a%d"%(arg, arg))
return ", ".join(ret)
def gen_args(args, member):
if member:
ret = ["o"]
else:
ret = []
ret.append("m")
for arg in range(0, args):
ret.append("a%d"%(arg))
return ", ".join(ret)
def gen_args_(args):
ret = []
for arg in range(0, args):
ret.append("a%d_"%(arg))
return ", ".join(ret)
def gen_init(args, r = False, member = False):
if member:
ret = ["o_(o)"]
else:
ret = []
ret.append("m_(m)")
if r:
ret.append("r_(r)")
for arg in range(0, args):
ret.append("a%d_(a%d)"%(arg, arg))
return ", ".join(ret)
def gen_typenames(args, member):
if member:
ret = ["typename C"]
else:
ret = []
ret.append("typename M")
for arg in range(0, args):
ret.append("typename A%d"%(arg))
return ", ".join(ret)
def gen_types(args, member):
if member:
ret = ["C"]
else:
ret = []
ret.append("M")
for arg in range(0, args):
ret.append("A%d"%(arg))
return ", ".join(ret)
def generate_class_template(args, ret = False, member = True):
print "// %d arguments --"%args
if member:
nm = "m"
else:
nm = "nm"
if not ret:
print "template<"+ gen_typenames(args, member) + "> class gmp_args_%s_%d : public gmp_args_base {"%(nm, args)
else:
print "template<"+ gen_typenames(args, member) + ", typename R> class gmp_args_%s_%d_ret : public gmp_args_base {"%(nm, args)
print " public:"
if not ret:
print " gmp_args_%s_%d("%(nm, args) + gen_args_type(args, member) + ") :"
print " " + gen_init(args, False, member) + " {}"
else:
print " gmp_args_%s_%d_ret("%(nm, args) + gen_args_type(args, member) + ", R *r) :"
print " " + gen_init(args, True, member) + " {}"
print " virtual bool returns_value() const { return true; }"
print
print " void Run() {"
if ret:
print " *r_ =",
else:
print " ",
if member:
print "((*o_).*m_)(" + gen_args_(args) + ");"
else:
print "m_(" + gen_args_(args) + ");"
print " }"
print
print " private:"
if member:
print " C o_;"
print " M m_;"
if ret:
print " R* r_;"
for arg in range(0, args):
print " A%d a%d_;"%(arg, arg)
print "};"
print
print
print
def generate_function_template(args, member):
if member:
nm = "m"
NM = "";
else:
nm = "nm"
NM = "NM";
print "// %d arguments --"%args
print "template<" + gen_typenames(args, member) + ">"
print "gmp_args_%s_%d<"%(nm, args) + gen_types(args, member) + ">* WrapTask%s("%NM + gen_args_type(args, member) + ") {"
print " return new gmp_args_%s_%d<"%(nm, args) + gen_types(args, member) + ">"
print " (" + gen_args(args, member) + ");"
print "}"
print
def generate_function_template_ret(args, member):
if member:
nm = "m"
NM = "";
else:
nm = "nm"
NM = "NM";
print "// %d arguments --"%args
print "template<" + gen_typenames(args, member) + ", typename R>"
print "gmp_args_%s_%d_ret<"%(nm, args) + gen_types(args, member) + ", R>* WrapTask%sRet("%NM + gen_args_type(args, member) + ", R* r) {"
print " return new gmp_args_%s_%d_ret<"%(nm, args) + gen_types(args, member) + ", R>"
print " (" + gen_args(args, member) + ", r);"
print "}"
print
print boilerplate
print
for num_args in range (0, MAX_ARGS):
generate_class_template(num_args, False, False)
generate_class_template(num_args, True, False)
generate_class_template(num_args, False, True)
generate_class_template(num_args, True, True)
print
print
print
for num_args in range(0, MAX_ARGS):
generate_function_template(num_args, False)
generate_function_template_ret(num_args, False)
generate_function_template(num_args, True)
generate_function_template_ret(num_args, True)

File diff suppressed because it is too large Load Diff