Webrtc新增FFmpeg视频编解码模块

1 整体描述

目前webrtc内置的视频编解码器包括:VP8、VP9、AV1和H264。一般情况下载pc端基本可以满足大部分的需求,但是有时候为了进行编解码器的扩展包括支持H265或者是支持硬件编解码以提升效率时需要新增编解码模块。
在这里插入图片描述

2 新增外部编码器

编码器实现的要点包括两个部分:
一是需要实现以VideoEncoder为基类的编码器对象,核心API实现如下:
(1)初始化编码器,将编码参数传入进行初始化。

virtual int InitEncode(const VideoCodec* codec_settings,
                         const VideoEncoder::Settings& settings);

(2)回调函数的注册,用于编码后数据的回传。

virtual int32_t RegisterEncodeCompleteCallback(
      EncodedImageCallback* callback) = 0;

(3)编码函数,将视频帧数据传入到解码器中,这个函数是实际编码接口;外部数据输入包括VideoFrame数据帧一般是kI420数据和帧类型(I帧、P帧…)

virtual int32_t Encode(const VideoFrame& frame,
                         const std::vector<VideoFrameType>* frame_types) = 0;

(4) QOS相关控制,设置码率和帧率等操作。

void SetRates(const RateControlParameters& parameters) override;

二是需要实现EncodedImageCallback回调基类来处理,编码后的数据回传到视频引擎。核心函数就是将编码后的数据转成EncodedImage结构回传到视频引擎。
在这里插入图片描述

2.1 编码器初始化

编码器的初始化主要是对编码器的基础参数包括分辨率,码率和荷载等基本数据,我们在完成编码器的初始化基本需要完成编码对象的创建和编码通道的创建等操作。这部分工作主要是在InitEncode完成,需要注意的是初始化函数可能被调用多次,因此这里需要保护防止多次创建造成,内存泄漏和系统资源冲突等问题。
以FFmpeg初始化编码器为例,基本需要包括四部分一是进行编码器的选择,根据codec类型, 寻找并创建编码器的上下文;第二是编码参数的初始化,根据上层传入的用户配置参数及自身对编码器的理解, 对所使用编码器参数进行的配置,可以进行参数的优化提高编码质量和用户体验;第三是编码帧的初始化,最后是开启编码器。
在这里插入图片描述

//编码器
AVCodec *avcodec_find_encoder(enum AVCodecID id);
//上下文获取
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
//初始化视频帧
AVFrame *avcodec_alloc_frame(void);
//开启编码器
int avcodec_open(AVCodecContext *avctx, AVCodec *codec);

2.2 编码实现

编码阶段需要理解两方面数据,一方面是输入数据VideoFrame为原始的图像帧,一般情况为I420,我们需要进行内部转换转成FFmpeg编码的输入参数。
以FFmpeg为例,解码过程分为三步:一是将输入数据转换为I420数据,然后利用类内部接口获取YUV数据并组装成编码器需要的数据;
二是编码数据,一般是调用FFmpeg编码函数编码,内部会进一步根据不同编码类型进行编码;
三是编码结束后,需要根据重新将编码后的数据进行组装得到一个EncodedImage结构体,并且调用回调函数将结果返回给视频引擎处理。
在这里插入图片描述

//将原始视频数据,转成I420格式
input_frame.video_frame_buffer()->ToI420()
//获取YUV数据
frame_buffer->DataY();
//图片填充
int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                   enum AVPixelFormat pix_fmt, int width, int height);
//视频编码
int avcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                         const AVFrame *pict);
//回调,数据返回
Encoded(EncodedImage& encodedImage,
            const CodecSpecificInfo* codecSpecificInfo = NULL,
            const RTPFragmentationHeader* fragmentation = NULL) = 0;

3 新增外部解码器

解码器实现与编码器类似,实现的要点包括两个部分:
一是需要实现以VideoDecoder为基类的解码器对象,核心API实现如下:
(1)初始化解码器,将解码参数传入进行初始化。

virtual bool Configure(const Settings& settings) = 0;

(2)回调函数的注册,用于解码后数据的回传。

virtual int32_t Decode(const EncodedImage& input_image,
                         bool missing_frames,
                         int64_t render_time_ms) = 0;

(3)解码函数,将编码视频帧数据传入到解码器中,这个函数是实际解码接口;外部数据输入包括EncodedImage数据帧,渲染时间和是否丢帧等。

virtual int32_t Decode(const EncodedImage& input_image,
                         bool missing_frames,
                         int64_t render_time_ms) = 0;

(4) 与视频编码不一样的配置函数,包括视频的解码缓冲区大小和渲染分辨率的设置。

//设置解码缓冲区大小
void set_buffer_pool_size(absl::optional<int> value);
//设置渲染分辨率
void set_max_render_resolution(RenderResolution value);

二是需要实现DecodedImageCallback回调基类来处理,解码后的数据回传到视频引擎。核心函数就是将编码后的数据转成原始视频帧VideoFrame结构回传到视频引擎。
在这里插入图片描述

3.1 解码器初始化

解码器的初始化主要是对解码器的基础参数包括分辨率,码率和荷载等基本数据,我们在完成解码器的初始化基本需要完成解码对象的创建和解码通道的创建等操作。这部分工作主要是在Configure完成,需要注意的是初始化函数可能被调用多次,因此这里需要保护防止多次创建造成,内存泄漏和系统资源冲突等问题。
以FFmpeg初始化解码器为例,基本需要包括四部分第一是解码参数的初始化,根据上层传入的用户配置参数及自身对解码器的理解, 对所使用解码器参数进行的配置,可以进行参数的优化提高解码质量和用户体验;二是进行解码器的选择,根据codec类型, 寻找并创建解码器的上下文;第三是开启解码器,传入上下文配置和解码器;第四部分是分配一个解码帧,用于后续解码使用。
在这里插入图片描述

//上下文获取
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
//解码器
AVCodec *avcodec_find_decoder(enum AVCodecID id);
//开启解码器
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
//初始化视频帧
AVFrame *avcodec_alloc_frame(void);

3.2 解码实现

解码阶段需要理解两方面数据,一方面是输入数据EncodedImage为编码后的数据,需要转换会解码器需要的帧结构,然后进行解码操作;第二是解码后数据解释为VideoFrame帧后回调给视频引擎。
以FFmpeg为例,解码过程分为四步:一是将EncodedImage结构视频帧转换为AVPacket结构;第二是将配置上下文和AVPacket传入解码器解码;第三是获取解码后视频帧存入AVFrame中;第四是需要根据重新将解码后的数据进行组装得到一个VideoFrame结构体,并且调用回调函数将结果返回给视频引擎处理。
在这里插入图片描述

//分配一个packet空间
AVPacket *av_packet_alloc(void);
//将编码数据加入到解码队列
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
//获取解码后数据帧
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
//释放packet
void av_packet_free(AVPacket **pkt);

4 总结

webrtc编解码模块设计有较强的对称性,其实在发送和接收,音频和视频,编码和解码等等都在设计上比较接近,因此需要在学习的时候相互借鉴和理解。整个编解码模块的添加相对难度较低,主要是理解输入和输出的数据结构,可以将webrtc的数据结构转换为编解码器内部能够理解的数据,达到将数据编解码的目的。基本的设计理念就是定义好标准的输入和输出,屏蔽内部的处理细节,达到模块之间的相互独立。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
敬告:该系列的课程在抓紧录制更新中,敬请大家关注。敬告: 该系列的课程涉及:FFmpeg,WebRTC,SRS,Nginx,Darwin,Live555,等。包括:音视频、流媒体、直播、Android、视频监控28181、等。 我将带领大家一起来学习WebRTC原理和编程知识,并动手搭建环境完成网页视频会话和文字聊天。具体内容包括: 一、Html与JavaScript小白入门二、WebRTC小白入门与流程原理分析三、网络打洞STUN和TURN四、信令服务器的原理与实战五、手撕WebRTC流程与代码六、亲自敲码踩坑搭建视频会话   音视频与流媒体是一门很复杂的技术,涉及的概念、原理、理论非常多,很多初学者不学 基础理论,而是直接做项目,往往会看到c/c++的代码时一头雾水,不知道代码到底是什么意思,这是为什么呢? 因为没有学习音视频和流媒体的基础理论,就比如学习英语,不学习基本单词,而是天天听英语新闻,总也听不懂。所以呢,一定要认真学习基础理论,然后再学习播放器、转码器、非编、流媒体直播、视频监控、等等。 梅老师从事音视频与流媒体行业18年;曾在永新视博、中科大洋、百度、美国Harris广播事业部等公司就职,经验丰富;曾亲手主导广电直播全套项目,精通h.264/h.265/aac,曾亲自参与百度app上的网页播放器等实战产品。目前全身心自主创业,主要聚焦音视频+流媒体行业,精通音视频加密、流媒体在线转码快编等热门产品。    

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值