WebRTC VideoEngine超详细教程(二)——集成OPENH264编解码器

来源:互联网 发布:mac mini 2014 换内存 编辑:程序博客网 时间:2024/05/02 06:45

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

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

VideoEncoder

[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  1. int H264EncoderImpl::SetChannelParameters(uint32_t packet_loss, int rtt) {  
  2.     return WEBRTC_VIDEO_CODEC_OK;  
  3. }  

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

解码功能的重新封装

定义H264DecoderImpl类如下,关键在于加入OPENH264解码功能核心类ISVCDecoder* decoder_;
[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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
原创粉丝点击