793 lines · 23.6 KB · C++
RawDownload
/*! * \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 <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" #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 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 (&param, 0, sizeof (param));    encoder_->GetDefaultParams (&param);     GMPLOG (GL_INFO, "Initializing encoder at "            << codecSettings.mWidth            << "x"            << codecSettings.mHeight            << "@"            << static_cast<int> (codecSettings.mMaxFramerate));     // Translate parameters.    param.iUsageType = CAMERA_VIDEO_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 = &param.sSpatialLayers[0];     layer->iVideoWidth = codecSettings.mWidth;    layer->iVideoHeight = codecSettings.mHeight;    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->sSliceCfg.uiSliceMode = SM_DYN_SLICE;      layer->sSliceCfg.sSliceArgument.uiSliceSizeConstraint = maxPayloadSize;    }    rv = encoder_->InitializeExt (&param);    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 (WrapTask (                            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() {    delete this;  }  private:  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.      g_platform_api->syncrunonmainthread (WrapTask (                                             this,                                             &OpenH264VideoEncoder::DestroyInputFrame_m,                                             inputImage));      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;    GMPErr err = host_->CreateFrame (kGMPEncodedVideoFrame, &ftmp);    if (err != GMPNoErr) {      GMPLOG (GL_ERROR, "Error creating encoded frame");      frame->Destroy();      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);      uint8_t* tmp = encoded->sLayerInfo[i].pBsBuf;      for (int j = 0; j < encoded->sLayerInfo[i].iNalCount; ++j) {        lengths[i] += encoded->sLayerInfo[i].pNalLengthInByte[j];        // Convert from 4-byte start codes to GMP_BufferLength32 (NAL lengths)        assert (* (reinterpret_cast<uint32_t*> (tmp)) == 0x01000000);        // BufferType32 doesn't include the length of the length itself!        * (reinterpret_cast<uint32_t*> (tmp)) = encoded->sLayerInfo[i].pNalLengthInByte[j] - sizeof (uint32_t);        length += encoded->sLayerInfo[i].pNalLengthInByte[j];        tmp += encoded->sLayerInfo[i].pNalLengthInByte[j];      }    }     err = f->CreateEmptyFrame (length);    if (err != GMPNoErr) {      GMPLOG (GL_ERROR, "Error allocating frame data");      f->Destroy();      frame->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]);

Lines 1–500 of 793.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t