WebRTC VideoEngine综合应用示例(二)——集成OPENH264编解码器

转自 zhanghui_cuc :http://blog.csdn.net/nonmarking/article/details/47910043

总述

WebRTC原生支持VP8和VP9,但也可以自行集成H264编解码器,比较常见的是OPENH264和X264(X264自身只有编码功能,如果要加入解码功能,可以再结合ffmpeg),总体来说,集成H264编解码器的流程和直接使用它们的库的流程类似,但是要先将相应功能依照WebRTC中对编解码器的封装形式重新封装,然后再通过注册外部编解码器的方法在主流程中使用它们。

下面先看一下WebRTC对编解码器的封装形式是怎么样的,定义在webrtc\modules\video_coding\codecs\interface\video_codec_interface.h中,如下

VideoEncoder

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class EncodedImageCallback  
  2. {  
  3. public:  
  4.     virtual ~EncodedImageCallback() {};  
  5.   
  6.     // Callback function which is called when an image has been encoded.  
  7.     //  
  8.     // Input:  
  9.     //          - encodedImage         : The encoded image  
  10.     //  
  11.     // Return value                    : > 0,   signals to the caller that one or more future frames  
  12.     //                                          should be dropped to keep bit rate or frame rate.  
  13.     //                                   = 0,   if OK.  
  14.     //                                   < 0,   on error.  
  15.     virtual int32_t  
  16.     Encoded(EncodedImage& encodedImage,  
  17.             const CodecSpecificInfo* codecSpecificInfo = NULL,  
  18.             const RTPFragmentationHeader* fragmentation = NULL) = 0;  
  19. };  
  20.   
  21. class VideoEncoder  
  22. {  
  23. public:  
  24.     virtual ~VideoEncoder() {};  
  25.   
  26.     // Initialize the encoder with the information from the VideoCodec.  
  27.     //  
  28.     // Input:  
  29.     //          - codecSettings     : Codec settings  
  30.     //          - numberOfCores     : Number of cores available for the encoder  
  31.     //          - maxPayloadSize    : The maximum size each payload is allowed  
  32.     //                                to have. Usually MTU - overhead.  
  33.     //  
  34.     // Return value                 : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  35.     virtual int32_t InitEncode(const VideoCodec* codecSettings, int32_t numberOfCores, uint32_t maxPayloadSize) = 0;  
  36.   
  37.     // Encode an I420 image (as a part of a video stream). The encoded image  
  38.     // will be returned to the user through the encode complete callback.  
  39.     //  
  40.     // Input:  
  41.     //          - inputImage        : Image to be encoded  
  42.     //          - codecSpecificInfo : Pointer to codec specific data  
  43.     //          - frame_types        : The frame type to encode  
  44.     //  
  45.     // Return value                 : WEBRTC_VIDEO_CODEC_OK if OK, < 0  
  46.     //                                otherwise.  
  47.     virtual int32_t Encode(  
  48.         const I420VideoFrame& inputImage,  
  49.         const CodecSpecificInfo* codecSpecificInfo,  
  50.         const std::vector<VideoFrameType>* frame_types) = 0;  
  51.   
  52.     // Register an encode complete callback object.  
  53.     //  
  54.     // Input:  
  55.     //          - callback         : Callback object which handles encoded images.  
  56.     //  
  57.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  58.     virtual int32_t RegisterEncodeCompleteCallback(EncodedImageCallback* callback) = 0;  
  59.   
  60.     // Free encoder memory.  
  61.     //  
  62.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  63.     virtual int32_t Release() = 0;  
  64.   
  65.     // Inform the encoder about the packet loss and round trip time on the  
  66.     // network used to decide the best pattern and signaling.  
  67.     //  
  68.     //          - packetLoss       : Fraction lost (loss rate in percent =  
  69.     //                               100 * packetLoss / 255)  
  70.     //          - rtt              : Round-trip time in milliseconds  
  71.     //  
  72.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  73.     virtual int32_t SetChannelParameters(uint32_t packetLoss, int rtt) = 0;  
  74.   
  75.     // Inform the encoder about the new target bit rate.  
  76.     //  
  77.     //          - newBitRate       : New target bit rate  
  78.     //          - frameRate        : The target frame rate  
  79.     //  
  80.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  81.     virtual int32_t SetRates(uint32_t newBitRate, uint32_t frameRate) = 0;  
  82.   
  83.     // Use this function to enable or disable periodic key frames. Can be useful for codecs  
  84.     // which have other ways of stopping error propagation.  
  85.     //  
  86.     //          - enable           : Enable or disable periodic key frames  
  87.     //  
  88.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  89.     virtual int32_t SetPeriodicKeyFrames(bool enable) { return WEBRTC_VIDEO_CODEC_ERROR; }  
  90.   
  91.     // Codec configuration data to send out-of-band, i.e. in SIP call setup  
  92.     //  
  93.     //          - buffer           : Buffer pointer to where the configuration data  
  94.     //                               should be stored  
  95.     //          - size             : The size of the buffer in bytes  
  96.     //  
  97.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  98.     virtual int32_t CodecConfigParameters(uint8_t* /*buffer*/, int32_t /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; }  
  99. };  
VideoDecoder

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class DecodedImageCallback  
  2. {  
  3. public:  
  4.     virtual ~DecodedImageCallback() {};  
  5.   
  6.     // Callback function which is called when an image has been decoded.  
  7.     //  
  8.     // Input:  
  9.     //          - decodedImage         : The decoded image.  
  10.     //  
  11.     // Return value                    : 0 if OK, < 0 otherwise.  
  12.     virtual int32_t Decoded(I420VideoFrame& decodedImage) = 0;  
  13.   
  14.     virtual int32_t ReceivedDecodedReferenceFrame(const uint64_t pictureId) {return -1;}  
  15.   
  16.     virtual int32_t ReceivedDecodedFrame(const uint64_t pictureId) {return -1;}  
  17. };  
  18.   
  19. class VideoDecoder  
  20. {  
  21. public:  
  22.     virtual ~VideoDecoder() {};  
  23.   
  24.     // Initialize the decoder with the information from the VideoCodec.  
  25.     //  
  26.     // Input:  
  27.     //          - inst              : Codec settings  
  28.     //          - numberOfCores     : Number of cores available for the decoder  
  29.     //  
  30.     // Return value                 : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  31.     virtual int32_t InitDecode(const VideoCodec* codecSettings, int32_t numberOfCores) = 0;  
  32.   
  33.     // Decode encoded image (as a part of a video stream). The decoded image  
  34.     // will be returned to the user through the decode complete callback.  
  35.     //  
  36.     // Input:  
  37.     //          - inputImage        : Encoded image to be decoded  
  38.     //          - missingFrames     : True if one or more frames have been lost  
  39.     //                                since the previous decode call.  
  40.     //          - fragmentation     : Specifies where the encoded frame can be  
  41.     //                                split into separate fragments. The meaning  
  42.     //                                of fragment is codec specific, but often  
  43.     //                                means that each fragment is decodable by  
  44.     //                                itself.  
  45.     //          - codecSpecificInfo : Pointer to codec specific data  
  46.     //          - renderTimeMs      : System time to render in milliseconds. Only  
  47.     //                                used by decoders with internal rendering.  
  48.     //  
  49.     // Return value                 : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  50.     virtual int32_t  
  51.     Decode(const EncodedImage& inputImage,  
  52.            bool missingFrames,  
  53.            const RTPFragmentationHeader* fragmentation,  
  54.            const CodecSpecificInfo* codecSpecificInfo = NULL,  
  55.            int64_t renderTimeMs = -1) = 0;  
  56.   
  57.     // Register an decode complete callback object.  
  58.     //  
  59.     // Input:  
  60.     //          - callback         : Callback object which handles decoded images.  
  61.     //  
  62.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  63.     virtual int32_t RegisterDecodeCompleteCallback(DecodedImageCallback* callback) = 0;  
  64.   
  65.     // Free decoder memory.  
  66.     //  
  67.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  68.     virtual int32_t Release() = 0;  
  69.   
  70.     // Reset decoder state and prepare for a new call.  
  71.     //  
  72.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  73.     virtual int32_t Reset() = 0;  
  74.   
  75.     // Codec configuration data sent out-of-band, i.e. in SIP call setup  
  76.     //  
  77.     // Input/Output:  
  78.     //          - buffer           : Buffer pointer to the configuration data  
  79.     //          - size             : The size of the configuration data in  
  80.     //                               bytes  
  81.     //  
  82.     // Return value                : WEBRTC_VIDEO_CODEC_OK if OK, < 0 otherwise.  
  83.     virtual int32_t SetCodecConfigParameters(const uint8_t* /*buffer*/, int32_t /*size*/) { return WEBRTC_VIDEO_CODEC_ERROR; }  
  84.   
  85.     // Create a copy of the codec and its internal state.  
  86.     //  
  87.     // Return value                : A copy of the instance if OK, NULL otherwise.  
  88.     virtual VideoDecoder* Copy() { return NULL; }  
  89. };  


具体到WebRTC原生支持的VP8编解码器,可以参见webrtc\modules\video_coding\codecs\vp8\vp8_impl.h和vp8_impl.cc两个文件,其中定义的VP8EncoderImpl和VP8DecoderImpl这两个类分别继承自VideoEncoder和VideoDecoder类,并且加入了一些私有的成员变量和函数。这也就意味着,我们在试图集成H264编解码器时,也应该有H264EncoderImpl和H264DecoderImpl这样的两个类。

本文先以OPENH264为例做详细说明
首先当然要通过OPENH264项目编译出welsenc.lib和welsdec.lib两个库,再把codec_api.h、codec_app_def.h、codec_def.h、codec_ver.h四个头文件以及welsdec.dll welsenc.dll welsvp.dll三个动态库加入到工程目录下,并且在项目属性中进行相应设置。

编码功能的重新封装

先说编码部分,定义H264EncoderImpl类如下,关键在于加入OPENH264编码功能核心类ISVCEncoder* encoder_;

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class H264EncoderImpl : public VideoEncoder{  
  2.  public:  
  3.   H264EncoderImpl();  
  4.   
  5.   ~H264EncoderImpl();  
  6.   
  7.   int Release();  
  8.   
  9.   int InitEncode(const VideoCodec* codec_settings,  
  10.                          int number_of_cores,  
  11.                          size_t max_payload_size);  
  12.   
  13.   int Encode(const I420VideoFrame& input_image,  
  14.                      const CodecSpecificInfo* codec_specific_info,  
  15.                      const std::vector<VideoFrameType>* frame_types);  
  16.   
  17.   int RegisterEncodeCompleteCallback(EncodedImageCallback* callback);  
  18.   
  19.   int SetChannelParameters(uint32_t packet_loss, int rtt);  
  20.   
  21.   int SetRates(uint32_t new_bitrate_kbit, uint32_t frame_rate);  
  22.   
  23.  private:  
  24.   // Update frame size for codec.  
  25.   int UpdateCodecFrameSize(const I420VideoFrame& input_image);  
  26.   
  27.   EncodedImage encoded_image_;  
  28.   EncodedImageCallback* encoded_complete_callback_;  
  29.   VideoCodec codec_;  
  30.   bool inited_;  
  31.   
  32. //openh264编码功能类  
  33.   ISVCEncoder* encoder_;  
  34.   
  35. };  // end of H264Encoder class  

然后再来看一下每个方法的具体实现,基本可以参照如下的OPENH264基本编码流程

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //setup encoder  
  2. int rv = WelsCreateSVCEncoder (&encoder_);  
  3. //initilize with basic parameter  
  4. SEncParamBase param;  
  5. memset (&param, 0, sizeof (SEncParamBase));  
  6. param.iUsageType = usageType;  
  7. param.fMaxFrameRate = frameRate;  
  8. param.iPicWidth = width;  
  9. param.iPicHeight = height;  
  10. param.iTargetBitrate = 5000000;  
  11. encoder_->Initialize (&param);  
  12. //set option, set option during encoding process  
  13. encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &g_LevelSetting);  
  14. int videoFormat = videoFormatI420;  
  15. encoder_->SetOption (ENCODER_OPTION_DATAFORMAT, &videoFormat);  
  16. //encode and store ouput bistream  
  17. int frameSize = width * height * 3 / 2;  
  18. BufferedData buf;  
  19. buf.SetLength (frameSize);  
  20. ASSERT_TRUE (buf.Length() == (size_t)frameSize);  
  21. SFrameBSInfo info;  
  22. memset (&info, 0, sizeof (SFrameBSInfo));  
  23. SSourcePicture pic;  
  24. memset (&pic, 0, sizeof (SsourcePicture));  
  25. pic.iPicWidth = width;  
  26. pic.iPicHeight = height;  
  27. pic.iColorFormat = videoFormatI420;  
  28. pic.iStride[0] = pic.iPicWidth;  
  29. pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;  
  30. pic.pData[0] = buf.data();  
  31. pic.pData[1] = pic.pData[0] + width * height;  
  32. pic.pData[2] = pic.pData[1] + (width * height >> 2);  
  33. for(int num = 0;num<total_num;num++) {  
  34.    //prepare input data  
  35.    rv = encoder_->EncodeFrame (&pic, &info);  
  36.    ASSERT_TRUE (rv == cmResultSuccess);  
  37.    if (info.eFrameType != videoFrameTypeSkip && cbk != NULL) {  
  38.     //output bitstream  
  39.    }  
  40. }  
  41. //teardown encoder  
  42. if (encoder_) {  
  43.     encoder_->Uninitialize();  
  44.     WelsDestroySVCEncoder (encoder_);  
  45. }  

 

H264EncoderImpl()方法实现如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. H264EncoderImpl::H264EncoderImpl()  
  2.     : encoded_image_(),  
  3.       encoded_complete_callback_(NULL),  
  4.       inited_(false),  
  5.       encoder_(NULL)  
  6. {  
  7.   memset(&codec_, 0, sizeof(codec_));  
  8. }  

Release()方法实现如下,这里调用了OPENH264的WelsDestroySVCEncoder方法

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::Release() {  
  2.   if (encoded_image_._buffer != NULL) {  
  3.     delete [] encoded_image_._buffer;  
  4.     encoded_image_._buffer = NULL;  
  5.   }  
  6.   if (encoder_ != NULL) {  
  7.     encoder_->Uninitialize();  
  8.     WelsDestroySVCEncoder(encoder_);  
  9.     encoder_ = NULL;  
  10.   }  
  11.   inited_ = false;  
  12.   return WEBRTC_VIDEO_CODEC_OK;  
  13. }  

InitEncode()方法实现如下,基本上就是OPENH264编码器的创建WelsCreateSVCEncoder与初始化encoder_->Initialize以及参数设置SEncParamBase的流程

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::InitEncode(const VideoCodec* inst,  
  2.                                int number_of_cores,  
  3.                                size_t max_payload_size) {  
  4.   if (inst == NULL) {  
  5.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  6.   }  
  7.   if (inst->maxFramerate < 1) {  
  8.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  9.   }  
  10.   // allow zero to represent an unspecified maxBitRate  
  11.   if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {  
  12.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  13.   }  
  14.   if (inst->width < 1 || inst->height < 1) {  
  15.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  16.   }  
  17.   if (number_of_cores < 1) {  
  18.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  19.   }  
  20.   
  21.   int ret_val= Release();  
  22.   if (ret_val < 0) {  
  23.     return ret_val;  
  24.   }  
  25.   
  26.   if (encoder_ == NULL) {  
  27.     ret_val = WelsCreateSVCEncoder(&encoder_);  
  28.   
  29.     if (ret_val != 0) {  
  30.      WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  31.                   "H264EncoderImpl::InitEncode() fails to create encoder ret_val %d",  
  32.                    ret_val);  
  33.       return WEBRTC_VIDEO_CODEC_ERROR;  
  34.     }  
  35.   }  
  36.   SEncParamBase param;  
  37.   memset (param, 0, sizeof(SEncParamBase));  
  38.   param.iUsageType = CAMERA_VIDEO_REAL_TIME;  
  39.   param.iRCMode = RC_QUALITY_MODE;  
  40.   param.fMaxFrameRate = inst->maxFramerate;  
  41.   param.iPicWidth = inst->width;  
  42.   param.iPicHeight = inst->height;  
  43.   param.iTargetBitrate = inst->maxBitrate;  
  44.   
  45.   ret_val =  encoder_->Initialize(param);  
  46.   int videoFormat = videoFormatI420;  
  47.   encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, &videoFormat);  
  48.   
  49.   if (ret_val != 0) {  
  50.       WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  51.           "H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",  
  52.           ret_val);  
  53.       WelsDestroySVCEncoder(encoder_);  
  54.       encoder_ = NULL;  
  55.       return WEBRTC_VIDEO_CODEC_ERROR;  
  56.   }  
  57.   
  58.   if (&codec_ != inst) {  
  59.     codec_ = *inst;  
  60.   }  
  61.   
  62.   if (encoded_image_._buffer != NULL) {  
  63.     delete [] encoded_image_._buffer;  
  64.   }  
  65.   encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height);  
  66.   encoded_image_._buffer = new uint8_t[encoded_image_._size];  
  67.   encoded_image_._completeFrame = true;  
  68.     
  69.   inited_ = true;  
  70.   WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  71.                "H264EncoderImpl::InitEncode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",  
  72.                inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);  
  73.   
  74.   return WEBRTC_VIDEO_CODEC_OK;  
  75. }  

Encode()方法中包含了两个功能,一方面是视频帧的编码,这一步骤同样基本可以参照OPENH264的编码流程

另一方面是将编码后数据封装为RTP包发送出去,具体的内容由WebRTC为我们提供的VCMEncodedFrameCallback类的Encoded方法实现,详见webrtc\modules\video_coding\main\source\generic_encoder.cc文件,对这一方法的调用则是在我们的H264EncoderImpl类的RegisterEncodeCompleteCallback方法中实现的,如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::RegisterEncodeCompleteCallback(  
  2.     EncodedImageCallback* callback) {  
  3.   encoded_complete_callback_ = callback;  
  4.   return WEBRTC_VIDEO_CODEC_OK;  
  5. }  
而我们需要做的只是获取与每个RTP fragment对应的NAL大小、类型等信息,并且写入RTPFragmentationHeader类中

Encode方法内容如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::Encode(const I420VideoFrame& input_image,  
  2.                            const CodecSpecificInfo* codec_specific_info,  
  3.                            const std::vector<VideoFrameType>* frame_types) {  
  4.   if (!inited_) {  
  5.     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  6.   }  
  7.   if (input_image.IsZeroSize()) {  
  8.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  9.   }  
  10.   if (encoded_complete_callback_ == NULL) {  
  11.     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  12.   }  
  13.   
  14.   VideoFrameType frame_type = kDeltaFrame;  
  15.   // We only support one stream at the moment.  
  16.   if (frame_types && frame_types->size() > 0) {  
  17.     frame_type = (*frame_types)[0];  
  18.   }  
  19.   
  20.   bool send_keyframe = (frame_type == kKeyFrame);  
  21.   if (send_keyframe) {  
  22.     encoder_->ForceIntraFrame(true);  
  23.     WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  24.                  "H264EncoderImpl::EncodeKeyFrame(width:%d, height:%d)",  
  25.                  input_image.width(), input_image.height());  
  26.   }  
  27.   
  28.   // Check for change in frame size.  
  29.   if (input_image.width() != codec_.width ||  
  30.       input_image.height() != codec_.height) {  
  31.     int ret = UpdateCodecFrameSize(input_image);  
  32.     if (ret < 0) {  
  33.       return ret;  
  34.     }  
  35.   }  
  36.  //编码过程,整个Encode方法是会被反复调用的  
  37.   SFrameBSInfo info;  
  38.   memset(&info, 0, sizeof(SFrameBSInfo));  
  39.   
  40.   SSourcePicture pic;  
  41.   memset(&pic,0,sizeof(SSourcePicture));  
  42.   pic.iPicWidth = input_image.width();  
  43.   pic.iPicHeight = input_image.height();  
  44.   pic.iColorFormat = videoFormatI420;  
  45.   
  46.   pic.iStride[0] = input_image.stride(kYPlane);  
  47.   pic.iStride[1] = input_image.stride(kUPlane);  
  48.   pic.iStride[2] = input_image.stride(kVPlane);  
  49.   
  50.   pic.pData[0]   = const_cast<uint8_t*>(input_image.buffer(kYPlane));  
  51.   pic.pData[1]   = const_cast<uint8_t*>(input_image.buffer(kUPlane));  
  52.   pic.pData[2]   = const_cast<uint8_t*>(input_image.buffer(kVPlane));  
  53.   
  54.   int retVal = encoder_->EncodeFrame(&pic, &info);  
  55.   if (retVal == videoFrameTypeSkip) {  
  56.     return WEBRTC_VIDEO_CODEC_OK;  
  57.   }  
  58. //获取与每个RTP fragment对应的NAL大小、类型等信息  
  59.   int layer = 0;  
  60.   
  61.   uint32_t totalNaluCount = 0;  
  62.   while (layer < info.iLayerNum) {  
  63.       const SLayerBSInfo* layer_bs_info = &info.sLayerInfo[layer];  
  64.       if (layer_bs_info != NULL) {  
  65.           totalNaluCount += layer_bs_info->iNalCount;  
  66.       }  
  67.       layer++;  
  68.   }  
  69.   if (totalNaluCount == 0) {  
  70.       return WEBRTC_VIDEO_CODEC_OK;  
  71.   }  
  72. //这里我们认为每个分片恰好包含一个NAL单元,具体的RTP分片方法则由WebRTC的VCMPacketizationCallback实现,我们不用管  
  73.   RTPFragmentationHeader frag_info;  
  74.   frag_info.VerifyAndAllocateFragmentationHeader(totalNaluCount);  
  75.     
  76.   encoded_image_._length = 0;  
  77.   layer = 0;  
  78.   uint32_t totalNaluIndex = 0;  
  79.   
  80.   while (layer < info.iLayerNum) {  
  81.       const SLayerBSInfo* layer_bs_info = &info.sLayerInfo[layer];  
  82.       if (layer_bs_info != NULL) {  
  83.           int layer_size = 0;  
  84.           int nal_begin = 4;  
  85.           uint8_t* nal_buffer = NULL;  
  86.           char nal_type = 0;  
  87.           for (int nal_index = 0; nal_index < layer_bs_info->iNalCount; nal_index++) {  
  88.               nal_buffer = layer_bs_info->pBsBuf + nal_begin;  
  89.               nal_type = (nal_buffer[0] & 0x1F);  
  90.               layer_size += layer_bs_info->pNalLengthInByte[nal_index];  
  91.               nal_begin += layer_size;  
  92.               if (nal_type == 14) {  
  93.                   continue;  
  94.               }  
  95.               uint32_t currentNaluSize = layer_bs_info->pNalLengthInByte[nal_index] - 4;  
  96.               memcpy(encoded_image_._buffer + encoded_image_._length, nal_buffer, currentNaluSize);  
  97.               encoded_image_._length += currentNaluSize;  
  98.   
  99.               WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  100.                             "H264EncoderImpl::Encode() nal_type %d, length:%d",  
  101.                              nal_type, encoded_image_._length);  
  102.                     
  103.               // Offset of pointer to data for each fragm.  
  104.               frag_info.fragmentationOffset[totalNaluIndex] = encoded_image_._length - currentNaluSize;  
  105.               // Data size for each fragmentation  
  106.               frag_info.fragmentationLength[totalNaluIndex] = currentNaluSize;  
  107.               // Payload type of each fragmentation  
  108.               frag_info.fragmentationPlType[totalNaluIndex] = nal_type;  
  109.               // Timestamp difference relative "now" for  
  110.               // each fragmentation  
  111.               frag_info.fragmentationTimeDiff[totalNaluIndex] = 0;  
  112.               totalNaluIndex++;  
  113.           } // for  
  114.       }  
  115.       layer++;  
  116.   }  
  117.   
  118.   
  119.           if (encoded_image_._length > 0) {  
  120.               encoded_image_._timeStamp = input_image.timestamp();  
  121.               encoded_image_.capture_time_ms_ = input_image.render_time_ms();  
  122.               encoded_image_._encodedHeight = codec_.height;  
  123.               encoded_image_._encodedWidth = codec_.width;  
  124.                           encoded_image_._frameType = frame_type;  
  125. // call back   
  126. encoded_complete_callback_->Encoded(encoded_image_, NULL, &frag_info);   
  127. }   
  128. return WEBRTC_VIDEO_CODEC_OK;  
  129. }  

 
最后是SetRate和SetChannelParameter两个方法,用于根据网络情况自适应改变码率和帧率,这里我们暂时不考虑,使用固定码率和帧率,二者的实现如下 

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::SetRates(uint32_t new_bitrate_kbit,  
  2.                              uint32_t new_framerate) {  
  3.   WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  4.                "H264EncoderImpl::SetRates(%d, %d)", new_bitrate_kbit, new_framerate);  
  5.   if (!inited_) {  
  6.     return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  7.   }  
  8.   if (new_framerate < 1) {  
  9.     return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  10.   }  
  11.   // update bit rate  
  12.   if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {  
  13.     new_bitrate_kbit = codec_.maxBitrate;  
  14.   }  
  15.   
  16.   return WEBRTC_VIDEO_CODEC_OK;  
  17. }  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264EncoderImpl::SetChannelParameters(uint32_t packet_loss, int rtt) {  
  2.     return WEBRTC_VIDEO_CODEC_OK;  
  3. }  

由此编码功能的重新封装全部完成

解码功能的重新封装

定义H264DecoderImpl类如下,关键在于加入OPENH264解码功能核心类ISVCDecoder* decoder_;
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class H264DecoderImpl : public VideoDecoder{  
  2.  public:  
  3.   enum {  
  4.       MAX_ENCODED_IMAGE_SIZE = 32768  
  5.   };  
  6.   
  7.   H264DecoderImpl();  
  8.   
  9.   ~H264DecoderImpl();  
  10.   
  11.   int InitDecode(const VideoCodec* inst, int number_of_cores);  
  12.   
  13.   int Decode(const EncodedImage& input_image,  
  14.                      bool missing_frames,  
  15.                      const RTPFragmentationHeader* fragmentation,  
  16.                      const CodecSpecificInfo* codec_specific_info,  
  17.                      int64_t /*render_time_ms*/);  
  18.   
  19.   int RegisterDecodeCompleteCallback(DecodedImageCallback* callback);  
  20.   
  21.   int Release();  
  22.   
  23.   int Reset();  
  24.   
  25.   VideoDecoder* Copy();  
  26.   
  27.  private:  
  28.   I420VideoFrame decoded_image_;  
  29.   DecodedImageCallback* decode_complete_callback_;  
  30.   bool inited_;  
  31.   VideoCodec codec_;  
  32.   bool key_frame_required_;  
  33.   
  34.   ISVCDecoder* decoder_;  
  35.   unsigned char* buffer_with_start_code_;  
  36.   
  37. };    

还是一样,先来看一下OPENH264自己的基本解码流程

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //decoder declaration  
  2. ISVCDecoder *pSvcDecoder;  
  3. //input: encoded bitstream start position; should include start code prefix  
  4. unsigned char *pBuf =...;  
  5. //input: encoded bit stream length; should include the size of start code prefix  
  6. int iSize =...;  
  7. //output: [0~2] for Y,U,V buffer for Decoding only  
  8. unsigned char *pData[3] =...;  
  9. //in-out: for Decoding only: declare and initialize the output buffer info  
  10. memset(&sDstBufInfo, 0, sizeof(SBufferInfo));  
  11.   
  12. //decoder creation  
  13. CreateDecoder(pSvcDecoder);//declare required parameter  
  14. SDecodingParam sDecParam = {0};  
  15. sDecParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_AVC;  
  16.   
  17. //initialize the parameter and decoder context, allocate memory  
  18. Initialize(&sDecParam);  
  19. //do actual decoding process in slice level; this can be done in a loop until data ends  
  20. //for Decoding only  
  21.  iRet = DecodeFrameNoDelay(pBuf, iSize, pData, &sDstBufInfo);  
  22.  //or  
  23.  iRet = DecodeFrame2(pBuf, iSize, pData, &sDstBufInfo);  
  24.    
  25.  //for Decoding only, pData can be used for render.  
  26.  if (sDstBufInfo.iBufferStatus==1){  
  27.      output pData[0], pData[1], pData[2];  
  28.  }  
  29. //uninitialize the decoder and memory free  
  30. Uninitialize();  
  31. //destroy the decoder  
  32. DestroyDecoder();  


 
 
照葫芦画瓢,即可得出H264DecoderImpl类的各方法的具体实现 

H264DecoderImpl()方法的实现如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. H264DecoderImpl::H264DecoderImpl()  
  2.         : decode_complete_callback_(NULL),  
  3.         inited_(false),  
  4.         key_frame_required_(true)  
  5.         ,buffer_with_start_code_(NULL)  
  6.         ,decoder_(NULL)  
  7.     {  
  8.         memset(&codec_, 0, sizeof(codec_));  
  9.         buffer_with_start_code_ = new unsigned char[MAX_ENCODED_IMAGE_SIZE];  
  10.   
  11.     }  
Release()方法的实现如下,调用了OPENH264的Uninitialize和WelsDestroyDecoder方法

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264DecoderImpl::Release() {  
  2.         if (decoder_ != NULL) {  
  3.             decoder_->Uninitialize();  
  4.             WelsDestroyDecoder(decoder_);  
  5.             decoder_ = NULL;  
  6.         }  
  7.         inited_ = false;  
  8.         return WEBRTC_VIDEO_CODEC_OK;  
  9.     }  

InitDecode方法的实现如下,进行了OPENH264解码参数的设置和初始化

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264DecoderImpl::InitDecode(const VideoCodec* inst, int number_of_cores) {  
  2.         if (inst == NULL) {  
  3.             return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  4.         }  
  5.         int ret_val = Release();  
  6.         if (ret_val < 0) {  
  7.             return ret_val;  
  8.         }  
  9.   
  10.         if (&codec_ != inst) {  
  11.             // Save VideoCodec instance for later; mainly for duplicating the decoder.  
  12.             codec_ = *inst;  
  13.         }  
  14.         if (decoder_ == NULL) {  
  15.             ret_val = WelsCreateDecoder(&decoder_);  
  16.             if (ret_val != 0) {  
  17.                 decoder_ = NULL;  
  18.                 return WEBRTC_VIDEO_CODEC_ERROR;  
  19.             }  
  20.         }  
  21.         SDecodingParam dec_param;  
  22.         memset(&dec_param, 0, sizeof(SDecodingParam));  
  23.         dec_param.eOutputColorFormat = videoFormatI420;  
  24.         dec_param.uiTargetDqLayer = UCHAR_MAX;  
  25.         dec_param.eEcActiveIdc = ERROR_CON_FRAME_COPY_CROSS_IDR;  
  26.         dec_param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;  
  27.         ret_val = decoder_->Initialize(&dec_param);  
  28.         if (ret_val != 0) {  
  29.             decoder_->Uninitialize();  
  30.             WelsDestroyDecoder(decoder_);  
  31.             decoder_ = NULL;  
  32.             return WEBRTC_VIDEO_CODEC_ERROR;  
  33.         }  
  34.         inited_ = true;  
  35.   
  36.         // Always start with a complete key frame.  
  37.         key_frame_required_ = true;  
  38.         WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  39.             "H264DecoderImpl::InitDecode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",  
  40.             inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);  
  41.         return WEBRTC_VIDEO_CODEC_OK;  
  42.     }  
Decode方法的实现如下,包含两个功能,一是解码接收到的图像帧,这方面可以参照OPENh264的解码流程

另一个是将解码数据转换为YUV420P像素格式的数据供渲染显示,具体的渲染显示由WebRTC为我们提供的VCMDecodedFrameCallback类的Decoded方法实现,详见webrtc\modules\video_coding\main\source\generic_decoder.cc文件,对这一方法的调用则是在我们的H264DecoderImpl类的RegisterDecodeCompleteCallback方法中实现的,如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264DecoderImpl::RegisterDecodeCompleteCallback(  
  2.         DecodedImageCallback* callback) {  
  3.         decode_complete_callback_ = callback;  
  4.         return WEBRTC_VIDEO_CODEC_OK;  
  5.     }  

我们需要做的只是将解码数据转换为YUV数据,使用WebRTC的CreateFrame方法,整个Decode方法的实现如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264DecoderImpl::Decode(const EncodedImage& input_image,  
  2.         bool missing_frames,  
  3.         const RTPFragmentationHeader* fragmentation,  
  4.         const CodecSpecificInfo* codec_specific_info,  
  5.         int64_t /*render_time_ms*/) {  
  6.         if (!inited_) {  
  7.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  8.                 "H264DecoderImpl::Decode, decoder is not initialized");  
  9.             return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  10.         }  
  11.   
  12.         if (decode_complete_callback_ == NULL) {  
  13.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  14.                 "H264DecoderImpl::Decode, decode complete call back is not set");  
  15.             return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  16.         }  
  17.   
  18.         if (input_image._buffer == NULL) {  
  19.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  20.                 "H264DecoderImpl::Decode, null buffer");  
  21.             return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;  
  22.         }  
  23.         if (!codec_specific_info) {  
  24.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  25.                 "H264EncoderImpl::Decode, no codec info");  
  26.             return WEBRTC_VIDEO_CODEC_ERROR;  
  27.         }  
  28.         if (codec_specific_info->codecType != kVideoCodecH264) {  
  29.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  30.                 "H264EncoderImpl::Decode, non h264 codec %d", codec_specific_info->codecType);  
  31.             return WEBRTC_VIDEO_CODEC_ERROR;  
  32.         }  
  33.   
  34.         WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  35.             "H264DecoderImpl::Decode(frame_type:%d, length:%d",  
  36.             input_image._frameType, input_image._length);  
  37.   
  38.         void* data[3];  
  39.         SBufferInfo buffer_info;  
  40.         memset(data, 0, sizeof(data));  
  41.         memset(&buffer_info, 0, sizeof(SBufferInfo));  
  42.   
  43.         memset(buffer_with_start_code_, 0, MAX_ENCODED_IMAGE_SIZE);  
  44.         int encoded_image_size = 0;  
  45.             memcpy(buffer_with_start_code_ , input_image._buffer, input_image._length);  
  46.             encoded_image_size =  input_image._length;  
  47.   
  48.         DECODING_STATE rv = decoder_->DecodeFrame2(buffer_with_start_code_, encoded_image_size, (unsigned char**)data, &buffer_info);  
  49.   
  50.         if (rv != dsErrorFree) {  
  51.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  52.                 "H264DecoderImpl::Decode, openH264 decoding fails with error %d", rv);  
  53.             return WEBRTC_VIDEO_CODEC_ERROR;  
  54.         }  
  55.   
  56.         if (buffer_info.iBufferStatus == 1) {  
  57.             int size_y = buffer_info.UsrData.sSystemBuffer.iStride[0] * buffer_info.UsrData.sSystemBuffer.iHeight;  
  58.             int size_u = buffer_info.UsrData.sSystemBuffer.iStride[1] * (buffer_info.UsrData.sSystemBuffer.iHeight / 2);  
  59.             int size_v = buffer_info.UsrData.sSystemBuffer.iStride[1] * (buffer_info.UsrData.sSystemBuffer.iHeight / 2);  
  60.   
  61.             decoded_image_.CreateFrame(size_y, static_cast<uint8_t*>(data[0]),  
  62.                 size_u, static_cast<uint8_t*>(data[1]),  
  63.                 size_v, static_cast<uint8_t*>(data[2]),  
  64.                 buffer_info.UsrData.sSystemBuffer.iWidth,  
  65.                 buffer_info.UsrData.sSystemBuffer.iHeight,  
  66.                 buffer_info.UsrData.sSystemBuffer.iStride[0],  
  67.                 buffer_info.UsrData.sSystemBuffer.iStride[1],  
  68.                 buffer_info.UsrData.sSystemBuffer.iStride[1]);  
  69.   
  70.             decoded_image_.set_timestamp(input_image._timeStamp);  
  71.             decode_complete_callback_->Decoded(decoded_image_);  
  72.             return WEBRTC_VIDEO_CODEC_OK;  
  73.         }else {  
  74.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  75.                 "H264DecoderImpl::Decode, buffer status:%d", buffer_info.iBufferStatus);  
  76.             return WEBRTC_VIDEO_CODEC_OK;  
  77.         }  
  78.     }  
最后是Reset和Copy方法,分别用于重置和复制,这里暂时用不到,二者的实现如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. VideoDecoder* H264DecoderImpl::Copy() {  
  2.         // Sanity checks.  
  3.         if (!inited_) {  
  4.             // Not initialized.  
  5.             assert(false);  
  6.             return NULL;  
  7.         }  
  8.         if (decoded_image_.IsZeroSize()) {  
  9.             // Nothing has been decoded before; cannot clone.  
  10.             return NULL;  
  11.         }  
  12.         // Create a new VideoDecoder object  
  13.         H264DecoderImpl *copy = new H264DecoderImpl;  
  14.   
  15.         // Initialize the new decoder  
  16.         if (copy->InitDecode(&codec_, 1) != WEBRTC_VIDEO_CODEC_OK) {  
  17.             delete copy;  
  18.             return NULL;  
  19.         }  
  20.   
  21.         return static_cast<VideoDecoder*>(copy);  
  22.     }  

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int H264DecoderImpl::Reset() {  
  2.         if (!inited_) {  
  3.             return WEBRTC_VIDEO_CODEC_UNINITIALIZED;  
  4.         }  
  5.         InitDecode(&codec_, 1);  
  6.         return WEBRTC_VIDEO_CODEC_OK;  
  7.     }  

由此,对OPENH264解码功能的重新封装也全部完成

注册与调用

最后,我们要在主流程中(见上一篇文章)中注册OPENH264为外部编码器,使用RegisterExternalSendCodec和RegisterExternalReceiveCodec方法,如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.        webrtc::H264Encoder *h264encoder = webrtc::H264Encoder::Create();  
  2. webrtc::H264Decoder *h264decoder = webrtc::H264Decoder::Create();  
  3.   
  4. webrtc::ViEExternalCodec* external_codec = webrtc::ViEExternalCodec  
  5.     ::GetInterface(ptrViE);  
  6. external_codec->RegisterExternalSendCodec(videoChannel, 88,  
  7.     h264encoder, false);  
  8. external_codec->RegisterExternalReceiveCodec(videoChannel,  
  9.     88, h264decoder, false);  
这里的88也可以改为其他数值,但是要与后面设置的videoCodec.plType值相符。

至此,就成功地将OPENH264集成到了WebRTC的VideoEngine中。
本工程的源代码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenH264 是思科公司发布的一个开源的 H.264 编码和解码器。编码器特性Constrained Baseline Profile up to Level 5.2 (4096x2304)Arbitrary resolution, not constrained to multiples of 16x16Rate control with adaptive quantization, or constant quantizationSlice options: 1 slice per frame, N slices per frame, N macroblocks per slice, or N bytes per sliceMultiple threads automatically used for multiple slicesTemporal scalability up to 4 layers in a dyadic hierarchySpatial simulcast up to 4 resolutions from a single inputLong Term Reference (LTR) framesMemory Management Control Operation (MMCO)Reference picture list modificationSingle reference frame for inter predictionMultiple reference frames when using LTR and/or 3-4 temporal layersPeriodic and on-demand Instantaneous Decoder Refresh (IDR) frame insertionDynamic changes to bit rate, frame rate, and resolutionAnnex B byte stream outputYUV 4:2:0 planar input解码器特性Constrained Baseline Profile up to Level 5.2 (4096x2304)Arbitrary resolution, not constrained to multiples of 16x16Single thread for all slicesLong Term Reference (LTR) framesMemory Management Control Operation (MMCO)Reference picture list modificationMultiple reference frames when specified in Sequence Parameter Set (SPS)Annex B byte stream inputYUV 4:2:0 planar output支持的操作系统Windows 64-bit and 32-bit (initial release is only 32-bit, 64-bit will follow soon)Mac OS X 64-bit (initial release does not include this target, will follow soon)Linux 64-bit and 32-bit (initial release is only 32-bit, 64-bit will follow soon)Android 32-bit (initial release does not include this target, will follow soon)iOS 64-bit and 32-bit (not supported yet, may be added in the future)支持的处理器Intel x86 optionally with MMX/SSE (no AVX yet, help is welcome)ARMv7 optionally with NEON (initial release does not include this target, will follow later)Any architecture using C/C fallback functions 标签:OpenH264

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值