/*! * \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 <cmath>#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" #include "codec_def.h"#include "codec_app_def.h"#include "codec_api.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(nullptr)# define GMP_HAVE_NULLPTR #elif 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 #include "task_utils.h" 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"}; #define OPENH264_MAX_MB 36864 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 RefCounted { public: OpenH264VideoEncoder (GMPVideoHost* hostAPI) : host_ (hostAPI), worker_thread_ (nullptr), encoder_ (nullptr), max_payload_size_ (0), callback_ (nullptr), stats_ ("Encoder"), shutting_down(false) { AddRef(); } virtual void InitEncode (const GMPVideoCodec& codecSettings, const uint8_t* aCodecSpecific, uint32_t aCodecSpecificSize, GMPVideoEncoderCallback* callback, int32_t numberOfCores, uint32_t maxPayloadSize) { callback_ = callback; GMPErr err = g_platform_api->createthread (&worker_thread_); if (err != GMPNoErr) { GMPLOG (GL_ERROR, "Couldn't create new thread"); Error (GMPGenericErr); return; } int rv = WelsCreateSVCEncoder (&encoder_); if (rv) { Error (GMPGenericErr); return; } SEncParamExt param; memset (¶m, 0, sizeof (param)); encoder_->GetDefaultParams (¶m); GMPLOG (GL_INFO, "Initializing encoder at " << codecSettings.mWidth << "x" << codecSettings.mHeight << "@" << static_cast<int> (codecSettings.mMaxFramerate)); // Translate parameters. param.iUsageType = CAMERA_VIDEO_REAL_TIME; if(codecSettings.mMode == kGMPScreensharing) param.iUsageType = SCREEN_CONTENT_REAL_TIME; param.iPicWidth = codecSettings.mWidth; param.iPicHeight = codecSettings.mHeight; param.iRCMode = RC_BITRATE_MODE; param.iTargetBitrate = codecSettings.mStartBitrate * 1000; param.iMaxBitrate = codecSettings.mMaxBitrate * 1000; GMPLOG (GL_INFO, "Initializing Bit Rate at: Start: " << codecSettings.mStartBitrate << "; Min: " << codecSettings.mMinBitrate << "; Max: " << codecSettings.mMaxBitrate << "; Max payload size:" << maxPayloadSize); param.uiMaxNalSize = maxPayloadSize; // TODO(ekr@rtfm.com). Scary conversion from unsigned char to float below. param.fMaxFrameRate = static_cast<float> (codecSettings.mMaxFramerate); // Set up layers. Currently we have one layer. SSpatialLayerConfig* layer = ¶m.sSpatialLayers[0]; // Make sure the output resolution doesn't exceed the Openh264 capability double width_mb = std::ceil(codecSettings.mWidth/16.0); double height_mb = std::ceil(codecSettings.mHeight/16.0); double input_mb = width_mb * height_mb; if (static_cast<uint32_t>(input_mb) > OPENH264_MAX_MB) { double scale = std::sqrt(OPENH264_MAX_MB / input_mb); layer->iVideoWidth = static_cast<uint32_t>(width_mb * 16 * scale); layer->iVideoHeight = static_cast<uint32_t>(height_mb * 16 * scale); GMPLOG (GL_INFO, "InitEncode: the output resolution overflows, w x h = " << codecSettings.mWidth << " x " << codecSettings.mHeight << ", turned to be " << layer->iVideoWidth << " x " << layer->iVideoHeight); } else { layer->iVideoWidth = codecSettings.mWidth; layer->iVideoHeight = codecSettings.mHeight; } if (layer->iVideoWidth < 16) { layer->iVideoWidth = 16; } if (layer->iVideoHeight < 16) { layer->iVideoHeight = 16; } layer->fFrameRate = param.fMaxFrameRate; layer->iSpatialBitrate = param.iTargetBitrate; layer->iMaxSpatialBitrate = param.iMaxBitrate; //for controlling the NAL size (normally for packetization-mode=0) if (maxPayloadSize != 0) { layer->sSliceArgument.uiSliceMode = SM_SIZELIMITED_SLICE; layer->sSliceArgument.uiSliceSizeConstraint = maxPayloadSize; } rv = encoder_->InitializeExt (¶m); if (rv) { GMPLOG (GL_ERROR, "Couldn't initialize encoder"); Error (GMPGenericErr); return; } max_payload_size_ = maxPayloadSize; GMPLOG (GL_INFO, "Initialized encoder"); } virtual void 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()); stats_.FrameIn(); assert (aFrameTypesLength != 0); worker_thread_->Post (WrapTaskRefCounted ( this, &OpenH264VideoEncoder::Encode_w, inputImage, (aFrameTypes)[0])); } virtual void SetChannelParameters (uint32_t aPacketLoss, uint32_t aRTT) { } virtual void 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); Error (GMPGenericErr); return; } 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); Error (GMPGenericErr); return; } } //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); Error (GMPGenericErr); return; } 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); Error (GMPGenericErr); return; } } } virtual void SetPeriodicKeyFrames (bool aEnable) { } virtual void EncodingComplete() { shutting_down = true; // Release the reference to the external objects, because it is no longer safe to call them host_ = nullptr; callback_ = nullptr; TearDownEncoder(); Release(); } private: virtual ~OpenH264VideoEncoder() { // Tear down the internal encoder in case of EncodingComplete() not being called TearDownEncoder(); } void TearDownEncoder() { // Stop the worker thread first if (worker_thread_) { worker_thread_->Join(); worker_thread_ = nullptr; } // Destroy OpenH264 encoder if (encoder_) { WelsDestroySVCEncoder(encoder_); encoder_ = nullptr; } } void TrySyncRunOnMainThread(GMPTask* aTask) { if (!shutting_down && g_platform_api) { g_platform_api->syncrunonmainthread (aTask); } } void Error (GMPErr error) { if (callback_) { callback_->Error (error); } } void Encode_w (GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type) { SFrameBSInfo encoded; if (frame_type == kGMPKeyFrame) { encoder_->ForceIntraFrame (true); if (!inputImage) return; } if (!inputImage) { GMPLOG (GL_ERROR, "no input image"); return; } 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(); src.uiTimeStamp = inputImage->Timestamp() / 1000; //encoder needs millisecond 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.eFrameType) { 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.eFrameType); break; default: // The API is defined as returning a type. assert (false); break; } if (!has_frame) { // This frame must be destroyed on the main thread. TrySyncRunOnMainThread (WrapTask ( this,Lines 1–500 of 955. Load more
/*! * \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 <cmath> #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" #include "codec_def.h" #include "codec_app_def.h" #include "codec_api.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(nullptr) # define GMP_HAVE_NULLPTR #elif 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 #include "task_utils.h" 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" }; #define OPENH264_MAX_MB 36864 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 RefCounted { public: OpenH264VideoEncoder (GMPVideoHost* hostAPI) : host_ (hostAPI), worker_thread_ (nullptr), encoder_ (nullptr), max_payload_size_ (0), callback_ (nullptr), stats_ ("Encoder"), shutting_down(false) { AddRef(); } virtual void InitEncode (const GMPVideoCodec& codecSettings, const uint8_t* aCodecSpecific, uint32_t aCodecSpecificSize, GMPVideoEncoderCallback* callback, int32_t numberOfCores, uint32_t maxPayloadSize) { callback_ = callback; GMPErr err = g_platform_api->createthread (&worker_thread_); if (err != GMPNoErr) { GMPLOG (GL_ERROR, "Couldn't create new thread"); Error (GMPGenericErr); return; } int rv = WelsCreateSVCEncoder (&encoder_); if (rv) { Error (GMPGenericErr); return; } SEncParamExt param; memset (¶m, 0, sizeof (param)); encoder_->GetDefaultParams (¶m); GMPLOG (GL_INFO, "Initializing encoder at " << codecSettings.mWidth << "x" << codecSettings.mHeight << "@" << static_cast<int> (codecSettings.mMaxFramerate)); // Translate parameters. param.iUsageType = CAMERA_VIDEO_REAL_TIME; if(codecSettings.mMode == kGMPScreensharing) param.iUsageType = SCREEN_CONTENT_REAL_TIME; param.iPicWidth = codecSettings.mWidth; param.iPicHeight = codecSettings.mHeight; param.iRCMode = RC_BITRATE_MODE; param.iTargetBitrate = codecSettings.mStartBitrate * 1000; param.iMaxBitrate = codecSettings.mMaxBitrate * 1000; GMPLOG (GL_INFO, "Initializing Bit Rate at: Start: " << codecSettings.mStartBitrate << "; Min: " << codecSettings.mMinBitrate << "; Max: " << codecSettings.mMaxBitrate << "; Max payload size:" << maxPayloadSize); param.uiMaxNalSize = maxPayloadSize; // TODO(ekr@rtfm.com). Scary conversion from unsigned char to float below. param.fMaxFrameRate = static_cast<float> (codecSettings.mMaxFramerate); // Set up layers. Currently we have one layer. SSpatialLayerConfig* layer = ¶m.sSpatialLayers[0]; // Make sure the output resolution doesn't exceed the Openh264 capability double width_mb = std::ceil(codecSettings.mWidth/16.0); double height_mb = std::ceil(codecSettings.mHeight/16.0); double input_mb = width_mb * height_mb; if (static_cast<uint32_t>(input_mb) > OPENH264_MAX_MB) { double scale = std::sqrt(OPENH264_MAX_MB / input_mb); layer->iVideoWidth = static_cast<uint32_t>(width_mb * 16 * scale); layer->iVideoHeight = static_cast<uint32_t>(height_mb * 16 * scale); GMPLOG (GL_INFO, "InitEncode: the output resolution overflows, w x h = " << codecSettings.mWidth << " x " << codecSettings.mHeight << ", turned to be " << layer->iVideoWidth << " x " << layer->iVideoHeight); } else { layer->iVideoWidth = codecSettings.mWidth; layer->iVideoHeight = codecSettings.mHeight; } if (layer->iVideoWidth < 16) { layer->iVideoWidth = 16; } if (layer->iVideoHeight < 16) { layer->iVideoHeight = 16; } layer->fFrameRate = param.fMaxFrameRate; layer->iSpatialBitrate = param.iTargetBitrate; layer->iMaxSpatialBitrate = param.iMaxBitrate; //for controlling the NAL size (normally for packetization-mode=0) if (maxPayloadSize != 0) { layer->sSliceArgument.uiSliceMode = SM_SIZELIMITED_SLICE; layer->sSliceArgument.uiSliceSizeConstraint = maxPayloadSize; } rv = encoder_->InitializeExt (¶m); if (rv) { GMPLOG (GL_ERROR, "Couldn't initialize encoder"); Error (GMPGenericErr); return; } max_payload_size_ = maxPayloadSize; GMPLOG (GL_INFO, "Initialized encoder"); } virtual void 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()); stats_.FrameIn(); assert (aFrameTypesLength != 0); worker_thread_->Post (WrapTaskRefCounted ( this, &OpenH264VideoEncoder::Encode_w, inputImage, (aFrameTypes)[0])); } virtual void SetChannelParameters (uint32_t aPacketLoss, uint32_t aRTT) { } virtual void 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); Error (GMPGenericErr); return; } 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); Error (GMPGenericErr); return; } } //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); Error (GMPGenericErr); return; } 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); Error (GMPGenericErr); return; } } } virtual void SetPeriodicKeyFrames (bool aEnable) { } virtual void EncodingComplete() { shutting_down = true; // Release the reference to the external objects, because it is no longer safe to call them host_ = nullptr; callback_ = nullptr; TearDownEncoder(); Release(); } private: virtual ~OpenH264VideoEncoder() { // Tear down the internal encoder in case of EncodingComplete() not being called TearDownEncoder(); } void TearDownEncoder() { // Stop the worker thread first if (worker_thread_) { worker_thread_->Join(); worker_thread_ = nullptr; } // Destroy OpenH264 encoder if (encoder_) { WelsDestroySVCEncoder(encoder_); encoder_ = nullptr; } } void TrySyncRunOnMainThread(GMPTask* aTask) { if (!shutting_down && g_platform_api) { g_platform_api->syncrunonmainthread (aTask); } } void Error (GMPErr error) { if (callback_) { callback_->Error (error); } } void Encode_w (GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type) { SFrameBSInfo encoded; if (frame_type == kGMPKeyFrame) { encoder_->ForceIntraFrame (true); if (!inputImage) return; } if (!inputImage) { GMPLOG (GL_ERROR, "no input image"); return; } 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(); src.uiTimeStamp = inputImage->Timestamp() / 1000; //encoder needs millisecond 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.eFrameType) { 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.eFrameType); break; default: // The API is defined as returning a type. assert (false); break; } if (!has_frame) { // This frame must be destroyed on the main thread. TrySyncRunOnMainThread (WrapTask ( this,