WebRTC VideoEngine综合应用示例(三)——集成X264编码和ffmpeg解码

本系列目前共三篇文章,后续还会更新

WebRTC VideoEngine综合应用示例(一)——视频通话的基本流程

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

WebRTC VideoEngine综合应用示例(三)——集成X264编码和ffmpeg解码

总述

在前一篇文章中,讲解了如何将OPENH264编解码器集成到WebRTC中,但是OPENH264只能编码baseline的H264视频,而且就编码质量而言,还是X264最好,本文就来讲解一下如何将X264编码器集成到WebRTC中,为了实现解码,同时要用到ffmpeg。总体流程和之前一样,分为重新封装编解码器和注册调用两大步骤,注册调用这一步没有任何不同,主要是重新封装这一步骤有较大区别。

重新封装X264编码功能

首先当然还是要下载X264源码编译出相应的库以供调用。在windows下使用mingw进行编译,再使用poxports工具导出库,最后得到libx264.dll和libx264.lib,同时把x264.h和x264_config.h总共四个文件放到工程目录下,并在项目属性中进行相应配置。

使用x264进行视频编码的基本流程如下
  1. #include <stdint.h>  
  2. #include <stdio.h>  
  3. #include <x264.h>  
  4.   
  5. int main( int argc, char **argv )  
  6. {  
  7.     int width, height;  
  8.     x264_param_t param;  
  9.     x264_picture_t pic;  
  10.     x264_picture_t pic_out;  
  11.     x264_t *h;  
  12.     int i_frame = 0;  
  13.     int i_frame_size;  
  14.     x264_nal_t *nal;  
  15.     int i_nal;  
  16.   
  17.     /* Get default params for preset/tuning */  
  18.     if( x264_param_default_preset( &param, "medium", NULL ) < 0 )  
  19.         goto fail;  
  20.   
  21.     /* Configure non-default params */  
  22.     param.i_csp = X264_CSP_I420;  
  23.     param.i_width  = width;  
  24.     param.i_height = height;  
  25.     param.b_vfr_input = 0;  
  26.     param.b_repeat_headers = 1;  
  27.     param.b_annexb = 1;  
  28.   
  29.     /* Apply profile restrictions. */  
  30.     if( x264_param_apply_profile( &param, "high" ) < 0 )  
  31.         goto fail;  
  32.   
  33.     if( x264_picture_alloc( &pic, param.i_csp, param.i_width, param.i_height ) < 0 )  
  34.         goto fail;  
  35.   
  36.     h = x264_encoder_open( &param);  
  37.     if( !h )  
  38.         goto fail;  
  39.   
  40.     int luma_size = width * height;  
  41.     int chroma_size = luma_size / 4;  
  42.     /* Encode frames */  
  43.     for( ;; i_frame++ )  
  44.     {  
  45.         /* Read input frame */  
  46.         if( fread( pic.img.plane[0], 1, luma_size, stdin ) != luma_size )  
  47.             break;  
  48.         if( fread( pic.img.plane[1], 1, chroma_size, stdin ) != chroma_size )  
  49.             break;  
  50.         if( fread( pic.img.plane[2], 1, chroma_size, stdin ) != chroma_size )  
  51.             break;  
  52.   
  53.         pic.i_pts = i_frame;  
  54.         i_frame_size = x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );  
  55.         if( i_frame_size < 0 )  
  56.             goto fail;  
  57.         else if( i_frame_size )  
  58.         {  
  59.             if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )  
  60.                 goto fail;  
  61.         }  
  62.     }  
  63.     /* Flush delayed frames */  
  64.     while( x264_encoder_delayed_frames( h ) )  
  65.     {  
  66.         i_frame_size = x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out );  
  67.         if( i_frame_size < 0 )  
  68.             goto fail;  
  69.         else if( i_frame_size )  
  70.         {  
  71.             if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )  
  72.                 goto fail;  
  73.         }  
  74.     }  
  75.   
  76.     x264_encoder_close( h );  
  77.     x264_picture_clean( &pic );  
  78.     return 0;  
  79. }  
#include <stdint.h>
#include <stdio.h>
#include <x264.h>

int main( int argc, char **argv )
{
    int width, height;
    x264_param_t param;
    x264_picture_t pic;
    x264_picture_t pic_out;
    x264_t *h;
    int i_frame = 0;
    int i_frame_size;
    x264_nal_t *nal;
    int i_nal;

    /* Get default params for preset/tuning */
    if( x264_param_default_preset( &param, "medium", NULL ) < 0 )
        goto fail;

    /* Configure non-default params */
    param.i_csp = X264_CSP_I420;
    param.i_width  = width;
    param.i_height = height;
    param.b_vfr_input = 0;
    param.b_repeat_headers = 1;
    param.b_annexb = 1;

    /* Apply profile restrictions. */
    if( x264_param_apply_profile( &param, "high" ) < 0 )
        goto fail;

    if( x264_picture_alloc( &pic, param.i_csp, param.i_width, param.i_height ) < 0 )
        goto fail;

    h = x264_encoder_open( &param);
    if( !h )
        goto fail;

    int luma_size = width * height;
    int chroma_size = luma_size / 4;
    /* Encode frames */
    for( ;; i_frame++ )
    {
        /* Read input frame */
        if( fread( pic.img.plane[0], 1, luma_size, stdin ) != luma_size )
            break;
        if( fread( pic.img.plane[1], 1, chroma_size, stdin ) != chroma_size )
            break;
        if( fread( pic.img.plane[2], 1, chroma_size, stdin ) != chroma_size )
            break;

        pic.i_pts = i_frame;
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, &pic, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )
                goto fail;
        }
    }
    /* Flush delayed frames */
    while( x264_encoder_delayed_frames( h ) )
    {
        i_frame_size = x264_encoder_encode( h, &nal, &i_nal, NULL, &pic_out );
        if( i_frame_size < 0 )
            goto fail;
        else if( i_frame_size )
        {
            if( !fwrite( nal->p_payload, i_frame_size, 1, stdout ) )
                goto fail;
        }
    }

    x264_encoder_close( h );
    x264_picture_clean( &pic );
    return 0;
}
还是一样,照葫芦画瓢,改写上一篇文章中提到的H264EncoderImpl类
首先是类的定义,去掉了原来的私有成员变量ISVCEncoder* encoder_,加入了以下几项,其他内容不变
  1. x264_picture_t pic;  
  2. x264_picture_t pic_out;  
  3. x264_t *encoder_;  
  4. int i_frame = 0;//frame index  
  5. x264_nal_t *nal;  
  x264_picture_t pic;
  x264_picture_t pic_out;
  x264_t *encoder_;
  int i_frame = 0;//frame index
  x264_nal_t *nal;

相应的,构造函数和析构函数也要改变,这里就不赘述了,重点看InitEncode方法和Encode方法。
InitEncode方法的实现改写如下
  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.         /* Get default params for preset/tuning */  
  26.         x264_param_t param;  
  27.         ret_val = x264_param_default_preset(&param, "medium", NULL);  
  28.         if (ret_val != 0) {  
  29.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  30.                 "H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",  
  31.                 ret_val);  
  32.             x264_encoder_close(encoder_);  
  33.             encoder_ = NULL;  
  34.             return WEBRTC_VIDEO_CODEC_ERROR;  
  35.         }  
  36.         /* Configure non-default params */  
  37.         param.i_csp = X264_CSP_I420;  
  38.         param.i_width = inst->width;  
  39.         param.i_height = inst->height;  
  40.         param.b_vfr_input = 0;  
  41.         param.b_repeat_headers = 1;  
  42.         param.b_annexb = 0;//这里设置为0,是为了使编码后的NAL统一有4字节的起始码,便于处理,否则会同时有3字节和4字节的起始码,很麻烦  
  43.         param.i_fps_num = 1;  
  44.         param.i_fps_num = codec_.maxFramerate;  
  45.         param.rc.i_bitrate = codec_.maxBitrate;  
  46.         /* Apply profile restrictions. */  
  47.         ret_val = x264_param_apply_profile(&param, "high");  
  48.         if (ret_val != 0) {  
  49.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  50.                 "H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",  
  51.                 ret_val);  
  52.             x264_encoder_close(encoder_);  
  53.             encoder_ = NULL;  
  54.             return WEBRTC_VIDEO_CODEC_ERROR;  
  55.         }  
  56.   
  57.         ret_val = x264_picture_alloc(&pic, param.i_csp, param.i_width, param.i_height);  
  58.         if (ret_val != 0) {  
  59.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  60.                 "H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",  
  61.                 ret_val);  
  62.             x264_encoder_close(encoder_);  
  63.             encoder_ = NULL;  
  64.             return WEBRTC_VIDEO_CODEC_ERROR;  
  65.         }  
  66.   
  67.         encoder_ = x264_encoder_open(&param);  
  68.         if (!encoder_){  
  69.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  70.                 "H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",  
  71.                 ret_val);  
  72.             x264_encoder_close(encoder_);  
  73.             x264_picture_clean(&pic);  
  74.             encoder_ = NULL;  
  75.             return WEBRTC_VIDEO_CODEC_ERROR;  
  76.         }  
  77.   
  78.         if (&codec_ != inst) {  
  79.             codec_ = *inst;  
  80.         }  
  81.   
  82.         if (encoded_image_._buffer != NULL) {  
  83.             delete[] encoded_image_._buffer;  
  84.         }  
  85.         encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height);  
  86.         encoded_image_._buffer = new uint8_t[encoded_image_._size];  
  87.         encoded_image_._completeFrame = true;  
  88.   
  89.         inited_ = true;  
  90.         WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  91.             "H264EncoderImpl::InitEncode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",  
  92.             inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);  
  93.   
  94.         return WEBRTC_VIDEO_CODEC_OK;  
  95.     }  
int H264EncoderImpl::InitEncode(const VideoCodec* inst,
		int number_of_cores,
		size_t max_payload_size) {
		if (inst == NULL) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		if (inst->maxFramerate < 1) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		// allow zero to represent an unspecified maxBitRate
		if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		if (inst->width < 1 || inst->height < 1) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		if (number_of_cores < 1) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}

		int ret_val = Release();
		if (ret_val < 0) {
			return ret_val;
		}
		/* Get default params for preset/tuning */
		x264_param_t param;
		ret_val = x264_param_default_preset(&param, "medium", NULL);
		if (ret_val != 0) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",
				ret_val);
			x264_encoder_close(encoder_);
			encoder_ = NULL;
			return WEBRTC_VIDEO_CODEC_ERROR;
		}
		/* Configure non-default params */
		param.i_csp = X264_CSP_I420;
		param.i_width = inst->width;
		param.i_height = inst->height;
		param.b_vfr_input = 0;
		param.b_repeat_headers = 1;
		param.b_annexb = 0;//这里设置为0,是为了使编码后的NAL统一有4字节的起始码,便于处理,否则会同时有3字节和4字节的起始码,很麻烦
		param.i_fps_num = 1;
		param.i_fps_num = codec_.maxFramerate;
		param.rc.i_bitrate = codec_.maxBitrate;
		/* Apply profile restrictions. */
		ret_val = x264_param_apply_profile(&param, "high");
		if (ret_val != 0) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",
				ret_val);
			x264_encoder_close(encoder_);
			encoder_ = NULL;
			return WEBRTC_VIDEO_CODEC_ERROR;
		}

		ret_val = x264_picture_alloc(&pic, param.i_csp, param.i_width, param.i_height);
		if (ret_val != 0) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",
				ret_val);
			x264_encoder_close(encoder_);
			encoder_ = NULL;
			return WEBRTC_VIDEO_CODEC_ERROR;
		}

		encoder_ = x264_encoder_open(&param);
		if (!encoder_){
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::InitEncode() fails to initialize encoder ret_val %d",
				ret_val);
			x264_encoder_close(encoder_);
			x264_picture_clean(&pic);
			encoder_ = NULL;
			return WEBRTC_VIDEO_CODEC_ERROR;
		}

		if (&codec_ != inst) {
			codec_ = *inst;
		}

		if (encoded_image_._buffer != NULL) {
			delete[] encoded_image_._buffer;
		}
		encoded_image_._size = CalcBufferSize(kI420, codec_.width, codec_.height);
		encoded_image_._buffer = new uint8_t[encoded_image_._size];
		encoded_image_._completeFrame = true;

		inited_ = true;
		WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,
			"H264EncoderImpl::InitEncode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",
			inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);

		return WEBRTC_VIDEO_CODEC_OK;
	}

Encode方法的实现改写如下
  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.             pic.b_keyframe = 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.   
  37.         /* Read input frame */  
  38.         pic.img.plane[0] = const_cast<uint8_t*>(input_image.buffer(kYPlane));  
  39.         pic.img.plane[1] = const_cast<uint8_t*>(input_image.buffer(kUPlane));  
  40.         pic.img.plane[2] = const_cast<uint8_t*>(input_image.buffer(kVPlane));  
  41.         pic.i_pts = i_frame;  
  42.   
  43.         int i_nal = 0;  
  44.         int i_frame_size = x264_encoder_encode(encoder_, &nal, &i_nal, &pic, &pic_out);  
  45.         if (i_frame_size < 0)  
  46.         {  
  47.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  48.                 "H264EncoderImpl::Encode() fails to encode %d",  
  49.                 i_frame_size);  
  50.             x264_encoder_close(encoder_);  
  51.             x264_picture_clean(&pic);  
  52.             encoder_ = NULL;  
  53.             return WEBRTC_VIDEO_CODEC_ERROR;  
  54.         }  
  55.   
  56.         RTPFragmentationHeader frag_info;  
  57.           
  58.         if (i_frame_size)  
  59.         {  
  60.             if (i_nal == 0) {  
  61.                 return WEBRTC_VIDEO_CODEC_OK;  
  62.             }  
  63.             frag_info.VerifyAndAllocateFragmentationHeader(i_nal);  
  64.   
  65.             encoded_image_._length = 0;  
  66.   
  67.             uint32_t totalNaluIndex = 0;  
  68.             for (int nal_index = 0; nal_index < i_nal; nal_index++)  
  69.             {  
  70.                 uint32_t currentNaluSize = 0;  
  71.                 currentNaluSize = nal[nal_index].i_payload - 4; //x264_encoder_encode编码得到的nal单元是已经带有起始码的,此外,这里直接使用nal[index]即可,不必再使用x264_nal_encode函数  
  72.                 memcpy(encoded_image_._buffer + encoded_image_._length, nal[nal_index].p_payload + 4, currentNaluSize);//encoded_image_中存有的是去掉起始码的数据  
  73.                 encoded_image_._length += currentNaluSize;  
  74.   
  75.                 WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  76.                     "H264EncoderImpl::Encode() nal_type %d, length:%d",  
  77.                     nal[nal_index].i_type, encoded_image_._length);  
  78.   
  79.                 frag_info.fragmentationOffset[totalNaluIndex] = encoded_image_._length - currentNaluSize;  
  80.                 frag_info.fragmentationLength[totalNaluIndex] = currentNaluSize;  
  81.                 frag_info.fragmentationPlType[totalNaluIndex] = nal[nal_index].i_type;  
  82.                 frag_info.fragmentationTimeDiff[totalNaluIndex] = 0;  
  83.                 totalNaluIndex++;  
  84.             }  
  85.         }  
  86.         i_frame++;  
  87.         if (encoded_image_._length > 0) {  
  88.             encoded_image_._timeStamp = input_image.timestamp();  
  89.             encoded_image_.capture_time_ms_ = input_image.render_time_ms();  
  90.             encoded_image_._encodedHeight = codec_.height;  
  91.             encoded_image_._encodedWidth = codec_.width;  
  92.             encoded_image_._frameType = frame_type;  
  93.             // call back  
  94.             encoded_complete_callback_->Encoded(encoded_image_, NULL, &frag_info);  
  95.         }  
  96.         return WEBRTC_VIDEO_CODEC_OK;  
  97.     }  
int H264EncoderImpl::Encode(const I420VideoFrame& input_image,
		const CodecSpecificInfo* codec_specific_info,
		const std::vector<VideoFrameType>* frame_types) {
		if (!inited_) {
			return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
		}
		if (input_image.IsZeroSize()) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		if (encoded_complete_callback_ == NULL) {
			return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
		}

		VideoFrameType frame_type = kDeltaFrame;
		// We only support one stream at the moment.
		if (frame_types && frame_types->size() > 0) {
			frame_type = (*frame_types)[0];
		}

		bool send_keyframe = (frame_type == kKeyFrame);
		if (send_keyframe) {
			pic.b_keyframe = TRUE;
			WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::EncodeKeyFrame(width:%d, height:%d)",
				input_image.width(), input_image.height());
		}

		// Check for change in frame size.
		if (input_image.width() != codec_.width ||
			input_image.height() != codec_.height) {
			int ret = UpdateCodecFrameSize(input_image);
			if (ret < 0) {
				return ret;
			}
		}

		/* Read input frame */
		pic.img.plane[0] = const_cast<uint8_t*>(input_image.buffer(kYPlane));
		pic.img.plane[1] = const_cast<uint8_t*>(input_image.buffer(kUPlane));
		pic.img.plane[2] = const_cast<uint8_t*>(input_image.buffer(kVPlane));
		pic.i_pts = i_frame;

		int i_nal = 0;
		int i_frame_size = x264_encoder_encode(encoder_, &nal, &i_nal, &pic, &pic_out);
		if (i_frame_size < 0)
		{
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::Encode() fails to encode %d",
				i_frame_size);
			x264_encoder_close(encoder_);
			x264_picture_clean(&pic);
			encoder_ = NULL;
			return WEBRTC_VIDEO_CODEC_ERROR;
		}

		RTPFragmentationHeader frag_info;
		
		if (i_frame_size)
		{
			if (i_nal == 0) {
				return WEBRTC_VIDEO_CODEC_OK;
			}
			frag_info.VerifyAndAllocateFragmentationHeader(i_nal);

			encoded_image_._length = 0;

			uint32_t totalNaluIndex = 0;
			for (int nal_index = 0; nal_index < i_nal; nal_index++)
			{
				uint32_t currentNaluSize = 0;
				currentNaluSize = nal[nal_index].i_payload - 4; //x264_encoder_encode编码得到的nal单元是已经带有起始码的,此外,这里直接使用nal[index]即可,不必再使用x264_nal_encode函数
				memcpy(encoded_image_._buffer + encoded_image_._length, nal[nal_index].p_payload + 4, currentNaluSize);//encoded_image_中存有的是去掉起始码的数据
				encoded_image_._length += currentNaluSize;

				WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,
					"H264EncoderImpl::Encode() nal_type %d, length:%d",
					nal[nal_index].i_type, encoded_image_._length);

				frag_info.fragmentationOffset[totalNaluIndex] = encoded_image_._length - currentNaluSize;
				frag_info.fragmentationLength[totalNaluIndex] = currentNaluSize;
				frag_info.fragmentationPlType[totalNaluIndex] = nal[nal_index].i_type;
				frag_info.fragmentationTimeDiff[totalNaluIndex] = 0;
				totalNaluIndex++;
			}
		}
		i_frame++;
		if (encoded_image_._length > 0) {
			encoded_image_._timeStamp = input_image.timestamp();
			encoded_image_.capture_time_ms_ = input_image.render_time_ms();
			encoded_image_._encodedHeight = codec_.height;
			encoded_image_._encodedWidth = codec_.width;
			encoded_image_._frameType = frame_type;
			// call back
			encoded_complete_callback_->Encoded(encoded_image_, NULL, &frag_info);
		}
		return WEBRTC_VIDEO_CODEC_OK;
	}

其他方法的实现均没有改变。
至此,X264编码器重新封装完毕,还是比较好理解的。

重新封装ffmpeg解码功能

首先还是一样,获得ffmpeg的头文件和库文件,加入工程中并进行相应设置,这里只需使用avcodec avformat avutil swscale四个库,头文件也可以做相应的删减。
ffmpeg解码的基本流程如下,实际集成之后是从WebRTC的EncodedImage& input_image中获得待解码数据的,所以不能使用常见的基于文件的解码流程
  1. AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);  
  2. AVCodecContext *codecCtx = avcodec_alloc_context3(codec);  
  3. avcodec_open2(codecCtx, codec, nil);  
  4. char *videoData;  
  5. int len;  
  6. AVFrame *frame = av_frame_alloc();  
  7. AVPacket packet;  
  8. av_new_packet(&packet, len);  
  9. memcpy(packet.data, videoData, len);  
  10. int ret, got_picture;  
  11. ret = avcodec_decode_video2(codecCtx, frame, &got_picture, &packet);  
  12. if (ret > 0){  
  13.     if(got_picture){  
  14.     //进行下一步的处理  
  15.     }  
  16. }  
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
avcodec_open2(codecCtx, codec, nil);
char *videoData;
int len;
AVFrame *frame = av_frame_alloc();
AVPacket packet;
av_new_packet(&packet, len);
memcpy(packet.data, videoData, len);
int ret, got_picture;
ret = avcodec_decode_video2(codecCtx, frame, &got_picture, &packet);
if (ret > 0){
    if(got_picture){
    //进行下一步的处理
    }
}
相应的,对H264DecoderImpl类的定义和各方法的实现要进行改写。
首先是类的定义,去掉了ISVCDecoder* decoder_,加入了以下私有成员变量
  1. AVCodecContext  *pCodecCtx;  
  2.   AVCodec           *pCodec;  
  3.   AVFrame   *pFrame, *pFrameYUV;  
  4.   AVPacket *packet;  
  5.   struct SwsContext *img_convert_ctx;  
  6.   uint8_t *decode_buffer;//存储最开始收到的SPS、PPS和IDR帧以便进行最开始的解码  
  7.   uint8_t *out_buffer;  
  8.   int framecnt = 0;  
  9.   int encoded_length = 0;  
AVCodecContext	*pCodecCtx;
  AVCodec			*pCodec;
  AVFrame	*pFrame, *pFrameYUV;
  AVPacket *packet;
  struct SwsContext *img_convert_ctx;
  uint8_t *decode_buffer;//存储最开始收到的SPS、PPS和IDR帧以便进行最开始的解码
  uint8_t *out_buffer;
  int framecnt = 0;
  int encoded_length = 0;
构造函数和析构函数的改写省略不表,重点看一下InitDecode方法和Decode方法
InitDecode方法改写如下
  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.         pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);  
  15.         pCodecCtx = avcodec_alloc_context3(pCodec);  
  16.         pCodecCtx->pix_fmt = PIX_FMT_YUV420P;  
  17.         pCodecCtx->width = codec_.width;  
  18.         pCodecCtx->height = codec_.height;  
  19.         //pCodecCtx->bit_rate = codec_.targetBitrate*1000;  
  20.         pCodecCtx->time_base.num = 1;  
  21.         pCodecCtx->time_base.den = codec_.maxFramerate;  
  22.   
  23.         if (pCodec == NULL){  
  24.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  25.                 "H264DecoderImpl::InitDecode, Codec not found.");  
  26.             return WEBRTC_VIDEO_CODEC_ERROR;  
  27.         }  
  28.         if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){  
  29.             WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  30.                 "H264DecoderImpl::InitDecode, Could not open codec.");  
  31.             return WEBRTC_VIDEO_CODEC_ERROR;  
  32.         }  
  33.         inited_ = true;  
  34.   
  35.         // Always start with a complete key frame.  
  36.         key_frame_required_ = true;  
  37.         WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,  
  38.             "H264DecoderImpl::InitDecode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",  
  39.             inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);  
  40.         return WEBRTC_VIDEO_CODEC_OK;  
  41.     }  
int H264DecoderImpl::InitDecode(const VideoCodec* inst, int number_of_cores) {
		if (inst == NULL) {
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		int ret_val = Release();
		if (ret_val < 0) {
			return ret_val;
		}

		if (&codec_ != inst) {
			// Save VideoCodec instance for later; mainly for duplicating the decoder.
			codec_ = *inst;
		}
		pCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
		pCodecCtx = avcodec_alloc_context3(pCodec);
		pCodecCtx->pix_fmt = PIX_FMT_YUV420P;
		pCodecCtx->width = codec_.width;
		pCodecCtx->height = codec_.height;
		//pCodecCtx->bit_rate = codec_.targetBitrate*1000;
		pCodecCtx->time_base.num = 1;
		pCodecCtx->time_base.den = codec_.maxFramerate;

		if (pCodec == NULL){
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264DecoderImpl::InitDecode, Codec not found.");
			return WEBRTC_VIDEO_CODEC_ERROR;
		}
		if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264DecoderImpl::InitDecode, Could not open codec.");
			return WEBRTC_VIDEO_CODEC_ERROR;
		}
		inited_ = true;

		// Always start with a complete key frame.
		key_frame_required_ = true;
		WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,
			"H264DecoderImpl::InitDecode(width:%d, height:%d, framerate:%d, start_bitrate:%d, max_bitrate:%d)",
			inst->width, inst->height, inst->maxFramerate, inst->startBitrate, inst->maxBitrate);
		return WEBRTC_VIDEO_CODEC_OK;
	}
Decode方法的实现改写如下
  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.         if (framecnt < 2)  
  39.         {//存储最开始的SPS PPS 和 IDR帧以便进行初始的解码  
  40.             memcpy(decode_buffer + encoded_length, input_image._buffer, input_image._length);  
  41.             encoded_length += input_image._length;  
  42.             framecnt++;  
  43.         }  
  44.         else  
  45.         {  
  46.             pFrame = av_frame_alloc();  
  47.             pFrameYUV = av_frame_alloc();  
  48.             out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));  
  49.             avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);  
  50.             img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,  
  51.                 pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);  
  52.               
  53.             if (framecnt == 2)  
  54.             {  
  55.                 packet = (AVPacket *)av_malloc(sizeof(AVPacket));  
  56.                 av_new_packet(packet, encoded_length);  
  57.                 memcpy(packet->data, decode_buffer, encoded_length);  
  58.                 av_free(decode_buffer);  
  59.                 framecnt++;  
  60.                 printf("\n\nLoading");  
  61.             }  
  62.             else  
  63.             {  
  64.                 packet = (AVPacket *)av_malloc(sizeof(AVPacket));  
  65.                 av_new_packet(packet, input_image._length);  
  66.                 memcpy(packet->data, input_image._buffer, input_image._length);  
  67.             }  
  68.               
  69.             int got_picture = 0;  
  70.             int ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);  
  71.             if (ret < 0){  
  72.                 WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,  
  73.                     "H264DecoderImpl::Decode, Decode Error.");  
  74.                 return WEBRTC_VIDEO_CODEC_ERROR;  
  75.             }  
  76.             if (got_picture){  
  77.                 sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,  
  78.                     pFrameYUV->data, pFrameYUV->linesize);  
  79.   
  80.                 int size_y = pFrameYUV->linesize[0] * pCodecCtx->height;  
  81.                 int size_u = pFrameYUV->linesize[1] * pCodecCtx->height / 2;  
  82.                 int size_v = pFrameYUV->linesize[2] * pCodecCtx->height / 2;  
  83.   
  84.                 decoded_image_.CreateFrame(size_y, static_cast<uint8_t*>(pFrameYUV->data[0]),  
  85.                     size_u, static_cast<uint8_t*>(pFrameYUV->data[1]),  
  86.                     size_v, static_cast<uint8_t*>(pFrameYUV->data[2]),  
  87.                     pCodecCtx->width,  
  88.                     pCodecCtx->height,  
  89.                     pFrameYUV->linesize[0],  
  90.                     pFrameYUV->linesize[1],  
  91.                     pFrameYUV->linesize[2]);  
  92.   
  93.                 decoded_image_.set_timestamp(input_image._timeStamp);  
  94.                 decode_complete_callback_->Decoded(decoded_image_);  
  95.                 return WEBRTC_VIDEO_CODEC_OK;  
  96.             }  
  97.             else  
  98.                 printf(".");  
  99.             av_free_packet(packet);  
  100.         }  
  101.         return WEBRTC_VIDEO_CODEC_OK;  
  102.     }  
int H264DecoderImpl::Decode(const EncodedImage& input_image,
		bool missing_frames,
		const RTPFragmentationHeader* fragmentation,
		const CodecSpecificInfo* codec_specific_info,
		int64_t /*render_time_ms*/) {
		if (!inited_) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264DecoderImpl::Decode, decoder is not initialized");
			return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
		}

		if (decode_complete_callback_ == NULL) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264DecoderImpl::Decode, decode complete call back is not set");
			return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
		}

		if (input_image._buffer == NULL) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264DecoderImpl::Decode, null buffer");
			return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
		}
		if (!codec_specific_info) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::Decode, no codec info");
			return WEBRTC_VIDEO_CODEC_ERROR;
		}
		if (codec_specific_info->codecType != kVideoCodecH264) {
			WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
				"H264EncoderImpl::Decode, non h264 codec %d", codec_specific_info->codecType);
			return WEBRTC_VIDEO_CODEC_ERROR;
		}

		WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideoCoding, -1,
			"H264DecoderImpl::Decode(frame_type:%d, length:%d",
			input_image._frameType, input_image._length);
	
		if (framecnt < 2)
		{//存储最开始的SPS PPS 和 IDR帧以便进行初始的解码
			memcpy(decode_buffer + encoded_length, input_image._buffer, input_image._length);
			encoded_length += input_image._length;
			framecnt++;
		}
		else
		{
			pFrame = av_frame_alloc();
			pFrameYUV = av_frame_alloc();
			out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
			avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
			img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
				pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
			
			if (framecnt == 2)
			{
				packet = (AVPacket *)av_malloc(sizeof(AVPacket));
				av_new_packet(packet, encoded_length);
				memcpy(packet->data, decode_buffer, encoded_length);
				av_free(decode_buffer);
				framecnt++;
				printf("\n\nLoading");
			}
			else
			{
				packet = (AVPacket *)av_malloc(sizeof(AVPacket));
				av_new_packet(packet, input_image._length);
				memcpy(packet->data, input_image._buffer, input_image._length);
			}
			
			int got_picture = 0;
			int ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
			if (ret < 0){
				WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCoding, -1,
					"H264DecoderImpl::Decode, Decode Error.");
				return WEBRTC_VIDEO_CODEC_ERROR;
			}
			if (got_picture){
				sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
					pFrameYUV->data, pFrameYUV->linesize);

				int size_y = pFrameYUV->linesize[0] * pCodecCtx->height;
				int size_u = pFrameYUV->linesize[1] * pCodecCtx->height / 2;
				int size_v = pFrameYUV->linesize[2] * pCodecCtx->height / 2;

				decoded_image_.CreateFrame(size_y, static_cast<uint8_t*>(pFrameYUV->data[0]),
					size_u, static_cast<uint8_t*>(pFrameYUV->data[1]),
					size_v, static_cast<uint8_t*>(pFrameYUV->data[2]),
					pCodecCtx->width,
					pCodecCtx->height,
					pFrameYUV->linesize[0],
					pFrameYUV->linesize[1],
					pFrameYUV->linesize[2]);

				decoded_image_.set_timestamp(input_image._timeStamp);
				decode_complete_callback_->Decoded(decoded_image_);
				return WEBRTC_VIDEO_CODEC_OK;
			}
			else
				printf(".");
			av_free_packet(packet);
		}
		return WEBRTC_VIDEO_CODEC_OK;
	}

其他方法的实现保持不变,至此ffmpeg解码功能的重新封装也完成了。

从最后实现的效果来看,X264的视频质量的确是最好的,但是播放端的解码延时比较高,暂时还不清楚原因,希望了解的朋友指教。
本项目 源代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值