开启辅助访问
 找回密码
 立即注册

如何合理设置H264编码视频码率?

金大圣 回答数5 浏览数221
如何设置码率可以使视频既保持原画清晰,又不会因码率过大而增大文件体积?
虽说视频码率越高视频越清晰,但是它肯定有个极限值(超过这个极限不会增加清晰度,却会增大文件体积),如何根据视频分辨率和视频编码以及要处理的视频文件算出最优码率呢
使用道具 举报
| 来自北京
六弦琴 | 来自北京
CAVLC-CAVLC概念

CAVLC的全称是Context-Adaptive Varialbe-Length Coding,即基于上下文的自适应变长编码。CAVLC的本质是变长编码,它的特性主要体现在自适应能力上,CAVLC可以根据已编码句法元素的情况动态的选择编码中使用的码表,并且随时更新拖尾系数后缀的长度,从而获得极高的压缩比。H.264标准中使用CAVLC对4×4模块的亮度和色度残差数据进行编码。
CAVLC-CAVLC原理

在H.264标准编码体系中,视频图像在经过了预测、变换及量化编码后表现出如下的特性:4×4块残差数据块比较稀疏,其中非零系数主要集中在低频部分,而高频系数大部分是零;量化后的数据经过zig-zag扫描,DC系数附近的非零系数值较大,而高频位置上的非零系数值大部分是+1和-1;相邻的4×4块的非零系数的数目是相关的。CAVLC就是利用编码后残差数据的这些特性,通过自适应对不同码表的选择,利用较少的编码数据对残差数据进行无损的熵编码,进一步减少了编码数据的冗余和相关性,提高了H.264的压缩效率。
CAVLC-CAVLC编码流程

视频图像在经过预测、变换和量化编码后,需要经过Zig-zag扫描和重新的排序过程,为后序的CAVLC编码进行准备。一个残差数据块的CAVLC熵编码的流程如图所示:


CAVLC熵编码处理流程

1、TotalCoeffs和TrailingOnes的编码

从码流的起始位置开始计算整个编码块中非零系数的数目(TotalCoeffs),非零系数的数目为从0-16,非零系数的数目被赋值给变量TotalCoeffs。 拖尾系数是指码流中正或者负1的个数(+/-1)。拖尾系数的数目(TrailingOnes)被限定在3个以内,如果+/-1的数目超过3个,则只有最后3个被视为拖尾系数,其余的被视为普通的非零系数,拖尾系数的数目被赋值为变量TrailingOnes。
2、判断计算nC值

nC(Number Current 当前块值)值的计算集中体现了CAVLC的基于上下文的思想,通过nC值选择不同H.264标准附录CAVLC码表。
3、查表获得coeff_token编码

根据之前编码和计算过程所得的变量TotalCoeffs、TrailingOnes和nC值可以查H.264标准附录CAVLC码表,即可得出coeff_token编码序列。
4、编码每个拖尾系数的符号:前面的coeff_token编码中已经包含了拖尾系数的总数,还需进一步对拖尾系数的符号进行编码。由于拖尾系数符合为正(+)或负(-),因此,在H.264标准中规定用0表示正1(+1)、1表示负1(-1)。当拖尾系数的数目超过3个只有最后3个被认定为拖尾系数,因此对符号的编码顺序应按照反向扫描的顺序进行。

5、编码除拖尾系数之外的非零系数的幅值(Levels)

非零系数的幅值(Levels)由两个部分组成:前缀(level_prefix)和后缀(level_suffix)。levelCode、levelSuffixsSize和suffixLength是编码过程中需要使用的三个变量,其中levelCode是中间过程中用到的无符号数,levelSuffixsSize表示后缀长度位数,suffixLength代表Level的码表序号。
6、编码最后一个非零系数前零的数目(TotalZeros)

TotalZeros指的是在最后一个非零系数前零的数目,此非零系数指的是按照正向扫描的最后一个非零系数。因为非零系数数目(TotalCoeffs)是已知,这就决定了TotalZeros可能的最大值。根据TotalCoeffs值,H.264标准共提供了25个变长表格供查找,其中编码亮度数据时有15个表格供查找,编码色度DC 2×2块(4:2:0格式)有3个表格、编码色度DC 2×4块(4:2:2格式)有7个表格。
7、编码每个非零系数前零的个数(RunBefore)

在CAVLC中,变量 ZerosLeft表示当前非零系数左边的所有零的个数,ZerosLeft的初始值等于TotalZeros。每个非零系数前零的个数(RunBefore)是按照反序来进行编码的,从最高频的非零系数开始。H.264标准中根据不同ZerosLeft和RunBefore,构建了RunBefore编码表格供编码查找使用。根据表格每编码完一个RunBefore,对ZerosLeft的值进行更新,继续编码下一个RunBefore,直至全部完成所有非零系数前零的个数的编码。当ZerosLeft=0即没有剩余0需要编码时或者只有一个非零系数时,均不需要再进行RunBefore编码。
CAVLC-CAVLC解码流程

CAVLC熵解码是上述CAVLC熵编码的逆过程,CAVLC熵解码的输入数据是来自片层数据的比特流,解码的基本单位是一个4×4的像素块,输出为包含4×4块每个像素点所有幅值的序列。CAVLC解码步骤如下:

  • 初始化所有的系数幅值
  • 解码非零系数个数(TotalCoeff)和拖尾系数个数(TrailingOnes)。
  • 解码拖尾系数符号(trailing_ones_sign_flag)
  • 解码非零系数幅值
  • 解码total_zeros和run_before
  • 组合非零系数幅值和游程信息,得到整个残差数据块
上下文自适应的变长编码(CAVLC)

CAVLC的全称叫做“上下文自适应的变长编码Context-based Adaptive Variable Length Coding”。所谓“上下文自适应”,说明了CAVLC算法不是像指数哥伦布编码那样采用固定的码流-码字映射的编码,而是一种动态编码的算法,因而压缩比远远超过固定变长编码UVLC等算法。
在H.264标准中,CAVLC主要用于预测残差的编码。在本系列第二篇博文中我们给出了H.264的编码流图,其中可知,熵编码的输入为帧内/帧间预测残差经过变换-量化后的系数矩阵。以4×4大小的系数矩阵为例,经过变换-量化后,矩阵通常呈现以下特性:

  • 经过变换量化后的矩阵通常具有稀疏的特性,即矩阵中大多数的数据已0为主。CAVLC可以通过游程编码高效压缩连续的0系数串;
  • 经过zig-zag扫描的系数矩阵的最高频非0系数通常是值为±1的数据串。CAVLC可以通过传递连续的+1或-1的长度来高效编码高频分量;
  • 非零系数的幅值通常在靠近DC(即直流分量)部分较大,而在高频部分较小;
  • 矩阵内非0系数的个数同相邻块相关;
熵编码回顾

熵编码:


  • 无损编码:解码后可无失真还原信源信息;
  • 利用信源符号的概率特性,使编码后的信息尽可能接近信源的熵;
常见熵编码方法:


  • 变长编码:哈夫曼编码、香农-费诺编码、指数哥伦布编码;
  • 算数编码;
CAVLC

上下文自适应的变长编码:

   用于亮度和色度预测残差的编码,以量化后的变换系数的形式;    变换系数矩阵的特征:

  • 稀疏:矩阵元素以0为主;
  • 非零系数集中于低频;
  • 高频部分的非零系数大部分为±1;
  • 非零系数个数同相邻块有关;
CAVLC的上下文模型:


  • 编码非零系数的表格索引;
  • 更新编码非零系数时的后缀长度
CALVC的编码过程

编码需要的重要元素:
一般都是以4X4的宏块做编码,所以下面是0~16

  • 非零系数的个数(TotalCoeffs):取值范围为[0, 16],即当前系数矩阵中包括多少个非0值的元素;
  • 拖尾系数的个数(TrailingOnes):取值范围为[0, 3],表示最高频的几个值为±1的系数的个数,如果±1 的个数大于 3 个,只有最后 3 个被视为拖尾系数,其余的被视为普通的非零系数。
  • 拖尾系数的符号:以1 bit表示,0表示+,1表示-;
  • 当前块值(numberCurrent):用于选择编码码表,由上方和左侧的相邻块的非零系数个数计算得到。普通非0系数的幅值(level):幅值的编码分为prefix和suffix两个部分进行编码。编码过程按照反序编码,即从最高频率非零系数开始。
  • 最后一个非0系数之前的0的个数(TotalZeros);
  • 每个非0系数之前0的个数(RunBefore):按照反序编码,即从最高频非零系数开始;对于最后一个非零系数(即最低频的非零系数)前的0的个数,以及没有剩余的0系数需要编码时,不需要再继续进行编码。
系数矩阵Z形扫描

CAVLC对一个固定大小的系数矩阵进行编码,例如:

{
      3, 2, -1, 0,
      1, 0,  1, 0,
     -1, 0,  0, 0,
      0, 0,  0, 0,
}扫描:


扫描重排之后得到一维数组:[3, 2, 1, -1, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
编码过程


  • 确定当前块值nC,选择coeff_token的码表;
  • 根据非零系数个数TotalCoeffs和拖尾系数个数TrailingOnes,编码coeff_token;
  • 编码拖尾系数的符号;
  • 编码拖尾系数之外的普通非零系数;
  • 编码最末非零系数之前0的总个数;
  • 编码每个非零系数之前的0的个数;
CAVLC解析残差过程

在编码残差时对宏块的分割:

  • cbp:表示一个8×8像素块是否进行编码;
  • block:CAVLC编码的基本单元为4×4,在一个cbp表示的块中进一步分割


使用CAVLC解析宏块残差语法元素的顺序:

  • numCoeff, trailingOnes;
  • levels;
  • totalZeros;
  • runBefore;
编码上下文的确定与相邻块的有效性

在解析coeffToken时,码表的选择依赖于上下文信息; 决定上下文的信息由相邻块获取,主要是块中非0系数的个数; 块的有效和无效:

  • Slice上边沿宏块的0、1、2、3号块,其上邻块无效;
  • Slice左边沿宏块的0、4、8、12号块,其左邻块无效;
  • Slice左上宏块的0号块,其左、上邻块均无效;


色度块、Intra16x16模式的解析

色度块、Intra16x16模式块的解析思想类似4x4亮度块:

  • 依次解析numCoeff、trailingOnes、trailingSigns、levels、totalZeros、runBefore;
不同之处:

  • 每个单元内系数数量最大值;
  • AC/DC是否分别解析;
  • nC值的计算方法;
不同分割模式的比较



全文主要讲音视频中的H264;基于上下文自适应可变长编码CAVLC原理;音视频的技术是非常广泛的,本文只讲了其中的一个小小技术点,想进阶更多音视频技术,可参考《音视频入门到精通手册》点击查看类目,获取你想要的进阶文档哦!
《全套音视频入门到精通手册》

文末

CAVLC属于熵编码。熵编码是一种无损压缩编码方法,它生成的码流可以经解码无失真地恢复出原数据。熵编码是建立在随机过程的统计特性基础上的,因此它主要为了降低数据的统计冗余。
在 H.264 的 CAVLC(基于上下文自适应的可变长编码)相比于huffman编码,它可以通过根据已编码句法元素的情况自适应调整当前编码中使用的码表,从而取得了极高的压缩比。
回复
使用道具 举报
anns | 未知
视频是利⽤⼈眼视觉暂留的原理,通过播放⼀系列的图⽚,使⼈眼产⽣运动的感觉。单纯传输视频画⾯,视频量⾮常⼤,对现有的⽹络和存储来说是不可接受的。为了能够使视频便于传输和存储,⼈们发现视频有⼤量重复的信息,如果将重复信息在发送端去掉,在接收端恢复出来,这样就⼤⼤减少了视频数据的⽂件,因此有了H.264视频压缩标准。
定义

在H.264压缩标准中I帧、P帧、B帧⽤于表⽰传输的视频画⾯。在视频压缩中,每帧都代表着一幅静止的图像。在实际的视频压缩编码时,会采取各种算法减少数据的容量,其中IPB帧就是最常见的一种算法。
I‑frame (Intra-coded picture): 即完整的一张图片
P‑frame (Predicted picture): 与前面一张图片的区别的区域
B‑frame (Bidirectional predicted picture):与前面以及后面的图片的区别区域
【戳我获取】《音视频入门精通手册》

I帧

I帧⼜称帧内编码帧,又称全帧压缩编码帧,是⼀种⾃带全部信息的独⽴帧,⽆需参考其他图像便可独⽴进⾏解码,可以简单理解为⼀张静态画⾯。视频序列中的第⼀个帧始终都是I帧,因为它是关键帧。I帧通常是每个GOP(MPEG所使用的一种视频压缩技术)的第一个帧,经过适度地压缩,作为随机访问的参考点,可以当成静态图像。
现在有一段影片如下:


该影片总共是20张图片组成的,每一张完整的图片我们都可以叫做I帧。假如每张JPEG的图片大小为100KB,那么传输两张图片即为100 * 20 = 2000KB
要知道这个还不到几秒的影片就2M了,要是几个小时的那还得了,所以就有了影片压缩的算法
I帧特点:

1.它是一个全帧压缩编码帧。它将全帧图像信息进行JPEG压缩编码及传输; 2.解码时仅用I帧的数据就可重构完整图像; 3.I帧描述了图像背景和运动主体的详情; 4.I帧不需要参考其他画面而生成; 5.I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量); 6.I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧; 7.I帧不需要考虑运动矢量; 8.I帧所占数据的信息量比较大。
P帧

P帧⼜称帧间预测编码帧,又称前向预测编码帧,需要参考前⾯的I帧才能进⾏编码。表⽰的是当前帧画⾯与前⼀帧(前⼀帧可能是I帧也可能是P帧)的差别。解码时需要⽤之前缓存的画⾯叠加上本帧定义的差别,⽣成最终画⾯。
与I帧相⽐,P帧通常占⽤更少的数据位,但不⾜是,由于P帧对前⾯的P和I参考帧有着复杂的依耐性,因此对传输错误⾮常敏感。通常将图像序列中前面已经编码帧的时间冗余信息充分去除来压缩传输数据量的编码图像,也称为预测帧。通过观察,我们可以看出,实际上每一帧之间其实只有一部分细微的差别而已,如下图提取了6帧


当传输完第一帧以后,第二帧其实我们只需要传输一部分,然后由另外一端进行图片算法来进行组合


用这种方式,在传输第二帧的时候,还不到原来的1/10,只需要传输第一帧的100KB,后续的都是按照这种方式传输部分,这种只传输部分的图片,就是P帧了。最后整个影片的大小即为100KB + 10KB * 19 = 290 KB,比原来缩小了很多。
P帧特点:

1.P帧是I帧后面相隔1~2帧的编码帧;
2.P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差);
3.解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像;
4.P帧属于前向预测的帧间编码。它只参考前面最靠近它的I帧或P帧;
5.P帧可以是其后面P帧的参考帧,也可以是其前后的B帧的参考帧;
6.由于P帧是参考帧,它可能造成解码错误的扩散;
7.由于是差值传送,P帧的压缩比较高。
B帧

B帧⼜称双向预测编码帧,又称双向预测内插编码帧,也就是B帧记录的是本帧与前后帧的差别。也就是说要解码B帧,不仅要取得之前的缓存画⾯,还要解码之后的画⾯,通过前后画⾯的与本帧数据的叠加取得最终的画⾯。B帧压缩率⾼,但是对解码性能要求较⾼。
既考虑源图像序列前面的已编码帧,又顾及源图像序列后面的已编码帧之间的时间冗余信息,来压缩传输数据量的编码图像,也称为双向预测帧。B帧其实就是与前后两张图片的区别。如果理解了P帧和I帧,这个就很好理解了。
B帧比P帧更小,更节省空间
假设现在有三张图片,如下图:


在经过编码后,会变成如下:


B帧特点

1.B帧是由前面的I或P帧和后面的P帧来进行预测的;
2.B帧传送的是它与前面的I或P帧和后面的P帧之间的预测误差及运动矢量;
3.B帧是双向预测编码帧;
4.B帧压缩比最高,因为它只反映丙参考帧间运动主体的变化情况,预测比较准确;
5.B帧不是参考帧,不会造成解码错误的扩散。
I帧、B帧、P帧的编码基本流程:

I 帧编码的基本流程为:

(1) 进行帧内预测,决定所采用的帧内预测模式。
(2) 像素值减去预测值,得到残差。
(3) 对残差进行变换和量化。
(4) 变长编码和算术编码。
(5) 重构图像并滤波,得到的图像作为其它帧的参考帧。
P 帧和 B 帧编码的基本流程为:

(1) 进行运动估计,计算采用帧间编码模式的率失真函数(节)值。P帧只参考前面的帧,B 帧可参考后面的帧。
(2) 进行帧内预测,选取率失真函数值最小的帧内模式与帧间模式比较,确定采用哪种编码模式。
(3) 计算实际值和预测值的差值。
(4) 对残差进行变换和量化。
(5) 熵编码,如果是帧间编码模式,编码运动矢量
【一键获取】Android音视频工程师必备《全套音视频入门到精通手册》



【戳我免费】Android音视频工程师必备《全套音视频入门到精通手册》



Android音视频工程师必备《全套音视频入门到精通手册》小结

I帧只需考虑本帧;P帧记录的是与前⼀帧的差别;B帧记录的是前⼀帧及后⼀帧的差别,能节约更多的空间,视频⽂件⼩了,但相对来说解码的时候就⽐较⿇烦。因为在解码时,不仅要⽤之前缓存的画⾯,⽽且要知道下⼀个I或者P的画⾯,对于不⽀持B帧解码的播放器容易卡顿。
回复
使用道具 举报
luorose | 来自黑龙江
H264编码视频码率这个本身就是一个很大的话题,要针对不同的场景、不同的目标进行设置。
最近刚调优完MediaCodec相关内容,不同的编码模式、对应的参数不同,使用场景也不同;
CBROMX_Video_ControlRateConstant:该值表示恒定码率,编码器会尽量把输出码率控制为配置时设定码率值;码率相对稳定可控,能够很好的检测网络状态,方便进行检测、丢包控制等后续操作,适合网络流媒体传输。
CQOMX_Video_ControlRateConstantQuality:该值表示恒定质量码率,码率会非常大,编码的目标就是尽最大可能保证图像质量,而忽略码率大小该模式适合本体文件存储,以及网络条件比较好,对于质量要求严格,带宽不受限的情况。同时要注意解码器能够正确解码,毕竟这种模式码率在不同运动环境下码率变化也会比较大。
VBR OMX_Video_ControlRateVariable:该值表示编码器会根据视频采集内容的复杂度(实际上是帧间变化量、运动情况)动态调整输出码率,图像复杂则码率高,图像简单则码率低,码率会波动比较大该模式在输出码率时有一定的波动。对于静止画面,压缩后的码率会非常低;而对于小幅度运动场景,可能会有方块效应,这时画面质量逐渐变差,码率也会上升;但是对于剧烈晃动,比如连续挥手测试,则出现严重马赛克。
具体可以参考如下文章:
Fenngtun:音视频编解码--MediaCodec实战优化

针对软解之前也跟过CRF模式,包括FFMPEG和X264中的代码,具体的流程可以关注这篇文章:
Fenngtun:音视频编解码--编码参数CRF
回复
使用道具 举报
lanzichun | 来自北京
如果你只是关心清晰度,可以使用crf模式(恒定画质/压缩率模式)。使用crf时,编码运算会自动根据画面复杂度调整码率达到crf所需的画质。crf 23是默认值,可以满足大多数清晰度要求。要求高的话,可以使用更低的数值,看你自己能接受的清晰度效果和编码速度。crf 18可以达到肉眼识别极限,但比crf 23时码率(文件大小)翻倍,且速度慢很多很多很多。
使用ffmpeg的例子:
ffmpeg -i input.mkv -c:v libx264 -crf 22 -c:a copy output.mkv
上述的"-crf 22"不写就是默认值23. -c:a copy就是音频直接复制,不重复编码
敲黑板:
1. 不同的视频,因为画面复杂度差异,使用同样的crf设定时,输出画质是一致的,但码率不一样
2. 使用x265时,可以比h264节省约一半的码率,所以可以使用libx265编码,crf设置为28,达到h264/x264 crf23的画质
回复
使用道具 举报
武汉高校联盟 | 来自云南
一个取决于编码器里面的编码流控算法和模式CBR QP VBR这些,另外范围需要根据你的编码档次规格以及视频的原始分辨率,H.264编码规范有建议范围值,可以查表看。
回复
使用道具 举报
快速回复
您需要登录后才可以回帖 登录 | 立即注册

当贝投影