北京南站核酸检测点,h5生成app,佛山网站公司建设网站,公司宣传网站建站在8K视频编解码特别是解码部分#xff0c;我做了一些优化工作#xff0c;转码速度提升了50%以上。专家们评价曰#xff1a;“主要围绕算法并行度的优化#xff0c;属于算法性能优化的常规手段#xff0c;在创新性和技术难度方面的体现较为一般”。评价过于犀利#xff0c…在8K视频编解码特别是解码部分我做了一些优化工作转码速度提升了50%以上。专家们评价曰“主要围绕算法并行度的优化属于算法性能优化的常规手段在创新性和技术难度方面的体现较为一般”。评价过于犀利不服不行可能专家们认为编解码提升并行度是多开几个线程的事。
好吧那我换用另一种手段叫“Dont taking off your pants to fart”来做一个提升1‰的优化。读过唐诗的都知道千是虚数优化效果基本上被淹没在测试噪声中…
0、前置信息
H.264常用的bitstream格式有三种annexb、avcc以及RTP协议中的特有格式。简单来说annexb格式是start code加nal_unitavcc是nal_unit的长度加nal_unitRTP H.264负载格式既没有start code也没有长度信息。
不同的容器格式使用不同的bitstream格式比如MP4/MKV/FLV使用avcc格式TS使用annexb格式直接H.264裸流也是使用annexb格式。当然国内有些团队比较随性在FLV里使用annexb格式再让大家兼容它。
除了bitstream格式外还有一个in-band/out-of-band参数集的问题就是在哪里存放SPS/PPS。像MP4/MKV“一般”是要求在文件特定位置存SPS/PPS其他位置只有音视频帧数据。而像TS格式SPS/PPS是随着IDR帧重复出现的。FFmpeg libavformat定义了一个flag叫
#define AVFMT_GLOBALHEADER 0x0040 /** Format wants global header. *
libavcodec定义了一个flag叫
#define AV_CODEC_FLAG_GLOBAL_HEADER (1 22)global header约等于out-of-band SPS/PPS。
关于bitstream格式和in-band/out-of-band参数集问题就介绍到这里。相关资料比较多详细信息可以阅读具体的标准文档。
1、问题描述
我们的优化手段是“Dont taking off your pants to fart”所以对应的问题是一个“taking off your pants to fart”问题
x264编码器能够输出annexb和avcc两种bitstream格式FFmpeg的种种限制导致x264只能输出annexb格式当编码后封装为MP4/MKV格式或者生成FLV走RTMP推流时libavformat再做一次格式转换把annexb转成avcc格式。注意这里是处理所有视频帧处理的时候要拷贝一次packet数据既浪费CPU又浪费内存
当前的处理流程
[x264]---annexb---[avformat/avc]---avcc---[avformat/movenc]---mp4
优化的处理流程
[x264]---avcc---[avformat/movenc]---mp4
看起来很简单但工程落地困难往往藏在细节中把牛顿三定律背的滚瓜烂熟未必造的出二踢脚更别说火箭了。
一个小小的优化我前后改了六次遇到一些有趣的、容易被忽视的小细节下面展开描述。
2、如何让x264输出avcc格式bitstream
x264参数配置有两种方式直接给成员变量赋值以及x264_param_parse接口。
x264设置bitstream格式的成员变量 int b_annexb; /* if set, place start codes (4 bytes) before NAL units,* otherwise place size (4 bytes) before NAL units. */
通过x264_param_parse配置方式
x264_param_parse(param, annexb, 0);
FFmpeg配置x264输出avcc格式
ffmpeg -i foo.mp4 \-c:a copy \-c:v libx264 \-x264-params annexb0 \-y bar.mp4
转码运行过程看着没问题但如果你试着播放输出的视频文件会发现视频无法解码没有画面。问题出在哪里
3、FFmpeg bitstream格式限制
FFmpeg框架层既支持annexb格式也支持avcc格式但是有以下限制
extradata的bitstream格式和AVPacket中的bitstream格式应当是同一种格式不可以混用两种格式。很多地方FFmpeg会根据extradata的格式来判断AVPacket的格式前提假设是两者为同一种格式对于avcc格式FFmpeg要求extradata是完整的AVCDecoderConfigurationRecord 当x264配置输出avcc格式时x264_encoder_headers输出的还是一个个的NALU是sps_nal_lengthsps_nal、pps_nal_lengthpps_nal、sei_nal_lengthsei_nal不是完整的AVCDecoderConfigurationRecord。
int x264_encoder_headers( x264_t *, x264_nal_t **pp_nal, int *pi_nal );
FFmpeg当前构造extradata是直接把x264_encoder_headers输出的数据拼接到一起。annexb格式没问题avcc格式直接拼一起得到的extradata不是完整的AVCDecoderConfigurationRecord不符合FFmpeg的格式要求。输出mp4时FFmpeg误认为extradata是完整的AVCDecoderConfigurationRecord导致生成的mp4文件格式错误视频无法解码。
4、解决方案
我考虑了几种解决方案
1、让FFmpeg extradata支持x264 headers的格式
2、在libavcodec里加一个统一的模块把x264 header格式转成AVCDecoderConfigurationRecord
3、在FFmpeg x264 wrapper里做处理把header转成AVCDecoderConfigurationRecord格式extradata
方案1新增一种extradata格式影响面太广容易制造混乱。
方案2其实我是考虑了其他的编码器比如videotoolbox也面临同样问题。但是否能复用复用到什么程度还不确定。
方案3在x264 wrapper里做处理最简单时机成熟了可以再抽取出来复用到其他编码器。
跟 Andreas Rheinhardt 讨论之后选择了方案3。
5、方案落地
由SPS/PPS生成AVCDecoderConfigurationRecord好像很简单毕竟libavformat/avc.c里已经实现了一遍。简单粗暴的做法那就直接抄了。但FFmpeg里做事的风格不是这样的。
如果你打开libavformat/avc.c看看你会发现处理过程还是挺复杂的原因有两点
由SPS/PPS生成AVCDecoderConfigurationRecord涉及SPS的parse过程libavformat不能依赖libavcodec的内部API因为两者以动态库编译时libavcodec内部的API对libavformat来说是不可见的
因为libavcodec和libavformat的隔离导致libavformat/avc.c实现的啰嗦。在libavcodec/libx264.c里我们可以利用libavcodec的基础设施不需要也不应该像libavformat/avc.c一样啰嗦。
一开始我用ff_h264_decode_seq_parameter_set来解析SPS好像没什么问题。但是Andreas Rheinhardt指出来一个非常隐蔽的漏洞ff_h264_decode_seq_parameter_set处理的是移除了emulation prevention byte的码流x264_encoder_headers输出的NAL可能带着emulation prevention byte所以有可能出现解析错误。
emulation prevention byte的作用是防止出现假的start code。比如如果NAL内的数据出现了0x000001则在里面插入一个emulation prevention byte 0x03变成0x00000301这样就不会误当成start code了。解码的时候需要把0x03剔除掉
James Almer提出了一个疑问可能很多人也存在同样的误区
emulation prevention byte是防止出现假的start codeavcc格式没有start code所以avcc格式没有emulation prevention byte。
这是错误的avcc格式也要使用emulation prevention byte。
考虑剔除emulation prevention byte再加上ff_h264_decode_seq_parameter_set的操作开销很大而AVCDecoderConfigurationRecord里稍微复杂的解析只有chroma format和bit depth的解析所以最后选择用libavcodec提供的golomb utils手动从SPS里解出三个字段。
除了emulation prevention byte还有个小问题被我忽略了。x264_encoder_headers除了输出SPS/PPS还输出一个SEISEI里是x264版权信息和编码参数配置。这个信息是没法保留在AVCDecoderConfigurationRecord里的需要单独拷贝出来跟着后面的IDR帧一起输出。
到此为止我们修复了libx264输出的extradata格式问题以下命令生成正常的mp4文件 ffmpeg -i foo.mp4 \-c:a copy \-c:v libx264 \-x264-params annexb0 \-y bar.mp4
整个流程变成了 [x264]---avcc---[avformat/movenc]---mp4。
CPU占用能节省多少呢和编码相比微乎其微。内存占用的节省倒是能测出来感兴趣的同学可以自己测测看。
6、遗留问题
最后遗留的一个问题是为什么还需要手动设置-x264-params annexb0而不是自动的在需要的时候输出avcc格式呢
从muxer到encoder有一个AV_CODEC_FLAG_GLOBAL_HEADER flag。FFmpeg libx264.c里当AV_CODEC_FLAG_GLOBAL_HEADER 开启时输出out-of-band参数集。虽然常见的global header的mp4、mkv、flv都是要avcc格式但global header和avcc没有强绑定关系不能看到global header就开启avcc。
当前缺少让muxer通知encoder输出什么bitstream格式的机制感兴趣的同学可以考虑实现这个功能。FFmpeg API用户可以根据媒体文件格式手动配置-x264-params annexb0基本也够用了。 粉丝福利 免费领取C音视频学习资料包学习路线大纲、技术视频/代码内容包括音视频开发面试题FFmpeg webRTC rtmp hls rtsp ffplay 编解码推拉流srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓