H.264码率控制算法研究及JM相应代码分析(二)
在前一篇文章的基础上,现在先看一下MPEG4 编码标准中应用的码率控制算法,总结起来,各大算法都是在解决两个问题:RD 率失真的优化以及避免缓冲区的上溢下溢。
MPEG-4 VM8 码率控制算法
在这里要先介绍一个非常著名的二项式率失真模型,其实说来也简单,在前面我们提到拉普拉斯分布信源的概率密度函数为:
代码分析
下面结合JM代码来看一下H.264的码率控制算法。
首先需要知道的是,在JM中默认使用的是基于JVT-G012r1的码率控制方法。
该提案中提出了自适应基本单元层码率控制方案,提出基本单元和线性模型的概念。其中,基本单元可为一帧、一片或一个宏块。而线性模型是用前一帧相同位置处的基本单元的MAD值来预测当前帧当前基本单元的MAD值,这样求MAD值就可以解决蛋鸡悖论。
解决过程如下:采用漏桶模型和线性跟踪理论,根据已经确定的帧率、当前的缓冲占有率、目标缓冲级别和可获取的信道带宽来算出当前帧的目标码率。剩余比特数则平均分配给当前帧中没有编码的基本单元,因为这些基本单元的MAD值还不知道。通过线性模型,可用前一帧相同位置处的基本单元的实际MAD值来预测出当前基本单元的MAD值。之后,用二次率失真模型来计算相应的QP值,从而用来对当前基本单元的每一宏块进行率失真优化。
这里首先设计到三个新的概念,基本单元、MAD线性预测模型以及用于计算缓冲区充盈度的流体传输模型。
Main-init_encoder-init_global_buffers-rc_allocate_memory (rc_quadratic.c)-rc_alloc_generic(ratectl.c)-rc_alloc_quadratic (rc_quadratic.c)
在初始化过程的代码中同时完成了基本单元数目的计算
int rcBufSize = p_Vid->FrameSizeInMbs / p_Inp->basicunit;
此外
Main-init_encoder-rc_init_sequence-rc_init_seq
其中同样包含有计算基本单元总数的代码
p_quad->TotalNumberofBasicUnit = p_Vid->FrameSizeInMbs/p_Inp->basicunit;
Main-init_encoder-rc_init_sequence-rc_init_seq
在该函数的代码中即可看到MAD预测模型参数的初始化
/* linear prediction model for P picture*/p_quad->PMADPictureC1 = 1.0;p_quad->PMADPictureC2 = 0.0;
main-encode_sequence-encode_one_frame-perform_encode_frame-rc_init_frame-updateQPRC0-predictCurrPicMAD-update current picture MAD
p_quad->CurrentFrameMAD=p_quad->MADPictureC1*p_quad->BUPFMAD[p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit]+p_quad->MADPictureC2;p_quad->TotalBUMAD=0;for(i=p_quad->TotalNumberofBasicUnit-1; i>=(p_quad->TotalNumberofBasicUnit-p_quad->NumberofBasicUnit);i--){p_quad->CurrentBUMAD=p_quad->MADPictureC1*p_quad->BUPFMAD[i]+p_quad->MADPictureC2;p_quad->TotalBUMAD +=p_quad->CurrentBUMAD*p_quad->CurrentBUMAD;}
由此,该方案按如下步骤进行:
- 用流体传输模型和线性跟踪理论来计算当前帧的目标比特数。
- 平均分配剩余的比特数给当前帧中其他没有编码的基本单元。
- 通过MAD线性预测模型用前一帧相同位置处的基本单元的实际MAD值来预测当前帧的当前基本单元的MAD值。
- 用二次R-D模型计算相应的QP值。
- 用从步骤4得来的QP值来对当前基本单元中的每一个宏块进行RDO。
该算法包括三个层面的码率控制,对不同的层面我们有不同的关注点。
首先是GOP层面,这里我们要关注缓冲区的更新过程。
然后是帧层面,这里我们要关注图像复杂度的计算过程。
最后是基本单元层面,这里我们要关注率失真模型的系数更新过程。
GOP层的码率控制
首先
main-encode_sequence-prepare_frame_params-rc_init_gop_params-rc_init_GOP
/*compute the total number of bits for the current GOP*/AllocatedBits = (int64) floor((1 + np + nb) * p_quad->bit_rate / p_quad->frame_rate + 0.5);p_gen->RemainingBits += AllocatedBits;p_quad->Np = np;p_quad->Nb = nb;
main-encode_sequence-encode_one_frame-perform_encode_frame-rc_init_frame-rc_init_pict
/* Since the available bandwidth may vary at any time, the total number of bits is updated picture by picture*/
if(prc->PrevBitRate!=prc->bit_rate)generic_RC->RemainingBits +=(int) floor((prc->bit_rate-prc->PrevBitRate)*(prc->Np + prc->Nb)/prc->frame_rate+0.5);
其次,第一个GOP的初始QP为一个预先确定的值QP0。此时,这个GOP的I帧和第一个P帧以QP0编码。QP0是基于可获得的信道带宽和GOP长度而预先确定的。一般情况下,信道带宽高,选小QP0;信道带宽低,选大QP0。带宽一定时,GOP长度增加15,QP0减小1。
Main-init_encoder-rc_init_sequence-rc_init_seq
实际代码中对第一个GOP的初始QP计算如下
if (p_Inp->SeinitialQP==0){
/*compute the initial QP*/
//bpp即每像素比特数bpp = 1.0*p_quad->bit_rate /(p_quad->frame_rate*p_Vid->size);if (p_Vid->width == 176){L1 = 0.1;L2 = 0.3;L3 = 0.6;}else if (p_Vid->width == 352){L1 = 0.2;L2 = 0.6;L3 = 1.2;}else{L1 = 0.6;L2 = 1.4;L3 = 2.4;}if (bpp<= L1)qp = 35;else if(bpp<=L2)qp = 25;else if(bpp<=L3)qp = 20;elseqp = 10;p_Inp->SeinitialQP = qp;}
最后,
main-encode_sequence-prepare_frame_params-rc_init_gop_params-rc_init_GOP
/*compute the average QP of P frames in the previous GOP*/p_quad->PAverageQp=(int)(1.0 * p_quad->TotalQpforPPicture / p_quad->NumberofPPicture+0.5);GOPDquant=(int)((1.0*(np+nb+1)/15.0) + 0.5);if(GOPDquant>2)GOPDquant=2;p_quad->PAverageQp -= GOPDquant;if (p_quad->PAverageQp > (p_quad->QPLastPFrame - 2))p_quad->PAverageQp--;// QP is constrained by QP of previous GOPp_quad->PAverageQp = iClip3(p_quad->QPLastGOP - 2, p_quad->QPLastGOP + 2, p_quad->PAverageQp);// Also clipped within range.p_quad->PAverageQp = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->PAverageQp);p_quad->MyInitialQp = p_quad->PAverageQp;p_quad->Pm_Qp = p_quad->PAverageQp;p_quad->PAveFrameQP = p_quad->PAverageQp;p_quad->QPLastGOP = p_quad->MyInitialQp;p_quad->PrevLastQP = p_quad->CurrLastQP;p_quad->CurrLastQP = p_quad->MyInitialQp - 1;
帧层码率控制
帧层码率控制包括两个阶段:编码前和编码后。
编码前阶段的目的是计算所有帧的QP值。
首先来看看如何计算B帧的QP值。
由于B帧不能预测其他任意帧,所以B帧的QP值会比它邻近的P帧或I帧的QP值大,这样I帧和P帧能节省一部分比特开销。另一方面,为了保持视频质量的连贯性,两个相邻帧的QP值之差不能大于2。基于观察,通过如下的线性插值方法,可以获得B帧的QP值: 假设L为两个P帧之间连续B帧的个数,且这两个P帧的QP值分别为QP1和QP2,则第i个B帧的QP值可以根据如下两种情况算出:
if(p_Inp->NumberBFrames==1){p_quad->m_Qc = imin(p_quad->PrevLastQP, p_quad->CurrLastQP) + 2;p_quad->m_Qc = imax(p_quad->m_Qc, imax(p_quad->PrevLastQP, p_quad->CurrLastQP));p_quad->m_Qc = imax(p_quad->m_Qc, p_quad->CurrLastQP + 1);p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping}else{BFrameNumber = (p_quad->NumberofBFrames + 1) % p_Inp->NumberBFrames;if(BFrameNumber==0)BFrameNumber = p_Inp->NumberBFrames;if((p_quad->CurrLastQP-p_quad->PrevLastQP)<=(-2*p_Inp->NumberBFrames-3))StepSize=-3;else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-2))StepSize=-2;else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames-1))StepSize=-1;else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames))StepSize=0;else if((p_quad->CurrLastQP-p_quad->PrevLastQP)==(-2*p_Inp->NumberBFrames+1))StepSize=1;elseStepSize=2;p_quad->m_Qc = p_quad->PrevLastQP + StepSize;p_quad->m_Qc += iClip3( -2 * (BFrameNumber - 1), 2*(BFrameNumber-1),(BFrameNumber-1)*(p_quad->CurrLastQP-p_quad->PrevLastQP)/(p_Inp->NumberBFrames-1));p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping}return p_quad->m_Qc;
然后再看一下P帧的QP值的计算
P帧的QP值通过下面两步求出:
main-encode_sequence-encode_one_frame-perform_encode_frame-rc_init_frame-rc_init_pict
/* predefine the target buffer level for each picture.
basic unit layer rate control */
if(p_gen->NumberofGOP==1){if(p_quad->NumberofPPicture==1){p_quad->TargetBufferLevel = (double) p_gen->CurrentBufferFullness;p_quad->DeltaP = (p_gen->CurrentBufferFullness - p_quad->GOPTargetBufferLevel)/(p_quad->TotalPFrame - 1);p_quad->TargetBufferLevel -= p_quad->DeltaP;}else if(p_quad->NumberofPPicture>1)p_quad->TargetBufferLevel -= p_quad->DeltaP;}else if(p_gen->NumberofGOP>1){if(p_quad->NumberofPPicture==0){p_quad->TargetBufferLevel = (double) p_gen->CurrentBufferFullness;p_quad->DeltaP = (p_gen->CurrentBufferFullness - p_quad->GOPTargetBufferLevel) / p_quad->TotalPFrame;p_quad->TargetBufferLevel -= p_quad->DeltaP;}else if(p_quad->NumberofPPicture>0)p_quad->TargetBufferLevel -= p_quad->DeltaP;}……
if(p_quad->NumberofCodedPFrame==1)p_quad->AveWp = p_quad->Wp;if((p_quad->NumberofCodedPFrame<8)&&(p_quad->NumberofCodedPFrame>1))p_quad->AveWp = (p_quad->AveWp + p_quad->Wp * (p_quad->NumberofCodedPFrame-1))/p_quad->NumberofCodedPFrame;else if(p_quad->NumberofCodedPFrame>1)p_quad->AveWp = (p_quad->Wp + 7 * p_quad->AveWp) / 8;……
// compute the average complexity of B framesif(p_Inp->NumberBFrames>0){// compute the target buffer levelp_quad->TargetBufferLevel += (p_quad->AveWp * (p_Inp->NumberBFrames + 1)*p_quad->bit_rate\/(p_quad->frame_rate*(p_quad->AveWp+p_quad->AveWb*p_Inp->NumberBFrames))-p_quad->bit_rate/p_quad->frame_rate);}
……
//compute the average weightif(p_gen->NumberofCodedBFrame<8)p_quad->AveWb = (p_quad->AveWb + p_quad->Wb*(p_gen->NumberofCodedBFrame-1)) / p_gen->NumberofCodedBFrame;elsep_quad->AveWb = (p_quad->Wb + 7 * p_quad->AveWb) / 8;
Main-init_encoder-rc_init_sequence-rc_init_seq
实际代码中对γ和β的设置如下
/*control parameter*/if(p_Inp->NumberBFrames>0){p_quad->GAMMAP=0.25;p_quad->BETAP=0.9;}else{p_quad->GAMMAP=0.5;p_quad->BETAP=0.5;}
mainencode_sequenceencode_one_frameperform_encode_framerc_init_framerc_init_pict
p_quad->Target = (int) (floor( p_quad->Wp * p_gen->RemainingBits / (p_quad->Np * p_quad->Wp + p_quad->Nb * p_quad->Wb) + 0.5));tmp_T = imax(0, (int) (floor(p_quad->bit_rate / p_quad->frame_rate - p_quad->GAMMAP * (p_gen->CurrentBufferFullness-p_quad->TargetBufferLevel) + 0.5)));p_quad->Target = (int) (floor(p_quad->BETAP * (p_quad->Target - tmp_T) + tmp_T + 0.5));
main-encode_sequence-encode_one_frame-perform_encode_frame-rc_init_frame-rc_init_pict-rc_updateQP
/* predict the MAD of current picture*/p_quad->CurrentFrameMAD = p_quad->MADPictureC1*p_quad->PreviousPictureMAD + p_quad->MADPictureC2;
/*compute the number of bits for the texture*/if(p_quad->Target < 0){p_quad->m_Qc=m_Qp+MaxQpChange;p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // Clipping}else{m_Bits = p_quad->Target-m_Hp;m_Bits = imax(m_Bits, (int)(p_quad->bit_rate/(MINVALUE*p_quad->frame_rate)));updateModelQPFrame( p_quad, m_Bits );
dtmp = p_quad->CurrentFrameMAD * p_quad->m_X1 * p_quad->CurrentFrameMAD * p_quad->m_X1+ 4 * p_quad->m_X2 * p_quad->CurrentFrameMAD * m_Bits;if ((p_quad->m_X2 == 0.0) || (dtmp < 0) || ((sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD) <= 0.0)) // fall back 1st order modem_Qstep = (float) (p_quad->m_X1 * p_quad->CurrentFrameMAD / (double) m_Bits);else // 2nd order modem_Qstep = (float) ((2 * p_quad->m_X2 * p_quad->CurrentFrameMAD) / (sqrt (dtmp) - p_quad->m_X1 * p_quad->CurrentFrameMAD));p_quad->m_Qc = Qstep2QP(m_Qstep, p_quad->bitdepth_qp_scale);p_quad->m_Qc = iClip3(p_Vid->RCMinQP + p_quad->bitdepth_qp_scale, p_Vid->RCMaxQP + p_quad->bitdepth_qp_scale, p_quad->m_Qc); // clippingp_quad->m_Qc = iClip3(m_Qp-MaxQpChange, m_Qp+MaxQpChange, p_quad->m_Qc); // control variation}
更新MAD线性预测模型的参数
main-encode_sequence-encode_one_frame-rc_update_pict_frame-rc_update_picture-rc_update_pict-updateRCModel-updateMADModel
更新二次R-D模型的参数
main-encode_sequence-encode_one_frame-rc_update_pict_frame-rc_update_picture-rc_update_pict-updateRCModel
更新缓冲充盈度
mainencode_sequenceencode_one_framerc_update_pict_framerc_update_picturerc_update_pict
int delta_bits = (nbits - (int)floor(p_quad->bit_rate / p_quad->frame_rate + 0.5F) );// remaining # of bits in GOPp_gen->RemainingBits -= nbits; p_gen->CurrentBufferFullness += delta_bits;
关注下方公众号,回复“264代码分析”,查看本文完整pdf文档下载链接
关注公众号,掌握更多多媒体领域知识与资讯
文章帮到你了?可以扫描如下二维码进行打赏~,打赏多少您随意~
H.264码率控制算法研究及JM相应代码分析(二)相关推荐
- H.264视频编解码的FPGA源码分析(二)帧内预测1
目录 帧内预测算法原理 基于论文的普通介绍 硬件实现 亮度块与色度块的划分 4×4亮度预测模块 如何产生预测像素与残差像素? 垂直模式`INTRA4x4_V` 水平模式`INTRA4x4_H` 直流模 ...
- H.264/AVC标准参考软件 JM
H.264是由ISO(国际标准化组织的缩写)和ITU(国际电信联盟的缩写)共同制定的视频压缩标准,J是Joint的缩写,意思就是ISO和ITU共同组成的联合专家组,M是Model的缩写,JM放一起就是 ...
- 【H.264/AVC视频编解码技术详解】二十三、帧间预测编码(1):帧间预测编码的基本原理
<H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...
- 【H.264/AVC视频编解码技术详解】二. 主流视频编码标准的发展
<H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...
- H.264视频编解码的FPGA源码分析(一)输入数据分析
目录 概要 输入数据 宏块 概要 本文的源码基于复旦大学的开源芯片-开源H.265/H.264视频编码器项目,本文的工作主要是在梳理源码的同时学习H.264视频编解码的原理及其硬件实现. 输入数据 C ...
- 【H.264/AVC视频编解码技术详解】二十六、帧间预测编码(4):宏块的帧间预测解码
<H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...
- H.264的码率控制策略
码率控制实际上是一种编码的优化算法,它用于实现对视频流码流大小的控制.那么它控制的目的是什么呢? 我们可以试想一下,同样的视频编码格式,码流大,它包含的信息也就越多,那么对应的图像也就越清晰, ...
- 数据压缩12 | 实验8 | H.264视频编解码
目录 一.实验准备 1. H.264编码过程 2. 调试和编码(参考JM Reference Software Manual (JVT-AE010)) 3. 编码参数(参考JM Reference S ...
- 四. 常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码
常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码 我们已经知道,H.264是一种视频压缩标准,其只规定了符合标准的码流的格式,以及码流中各个语法元素的解析方法.H.264标准并 ...
最新文章
- MySQL中,当 update 修改数据与原数据相同时会再次执行吗?
- javascript动态创建可拖动、最大化、最小化的层
- mongodb save和insert区别
- 设计模式学习笔记——模板(Template)模式
- 跟我学ModelArts丨探索ModelArts平台个性化联邦学习API
- 神经网络之感知器算法简单介绍和MATLAB简单实现
- Java读取word中表格
- 恢复Cisco路由器密码
- 在VUE中实现城市及对应的地区的联动渲染
- 两种常用电容式麦克风 MEMS还是ECM
- 2021-08-24
- Win10提示“为了对电脑进行保护,已经阻止此应用”如何解决
- Mindjet MindManager2022完整版思维导图v22.1.234版本
- PhpStorm修改字体和主题
- 改文拽少爷的校花女友33
- FPGA系列:ZCU102开发板上的第一个工程(MIG控制器)
- React之npm发布Antd样式的组件
- 2013年08月威海之旅
- JAVA用一维数组生成福彩双色球中奖号码
- SAP China 招聘