在前一篇文章的基础上,现在先看一下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;}

由此,该方案按如下步骤进行:

  1. 用流体传输模型和线性跟踪理论来计算当前帧的目标比特数。
  2. 平均分配剩余的比特数给当前帧中其他没有编码的基本单元。
  3. 通过MAD线性预测模型用前一帧相同位置处的基本单元的实际MAD值来预测当前帧的当前基本单元的MAD值。
  4. 用二次R-D模型计算相应的QP值。
  5. 用从步骤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相应代码分析(二)相关推荐

  1. H.264视频编解码的FPGA源码分析(二)帧内预测1

    目录 帧内预测算法原理 基于论文的普通介绍 硬件实现 亮度块与色度块的划分 4×4亮度预测模块 如何产生预测像素与残差像素? 垂直模式`INTRA4x4_V` 水平模式`INTRA4x4_H` 直流模 ...

  2. H.264/AVC标准参考软件 JM

    H.264是由ISO(国际标准化组织的缩写)和ITU(国际电信联盟的缩写)共同制定的视频压缩标准,J是Joint的缩写,意思就是ISO和ITU共同组成的联合专家组,M是Model的缩写,JM放一起就是 ...

  3. 【H.264/AVC视频编解码技术详解】二十三、帧间预测编码(1):帧间预测编码的基本原理

    <H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...

  4. 【H.264/AVC视频编解码技术详解】二. 主流视频编码标准的发展

    <H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...

  5. H.264视频编解码的FPGA源码分析(一)输入数据分析

    目录 概要 输入数据 宏块 概要 本文的源码基于复旦大学的开源芯片-开源H.265/H.264视频编码器项目,本文的工作主要是在梳理源码的同时学习H.264视频编解码的原理及其硬件实现. 输入数据 C ...

  6. 【H.264/AVC视频编解码技术详解】二十六、帧间预测编码(4):宏块的帧间预测解码

    <H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...

  7. H.264的码率控制策略

    码率控制实际上是一种编码的优化算法,它用于实现对视频流码流大小的控制.那么它控制的目的是什么呢?     我们可以试想一下,同样的视频编码格式,码流大,它包含的信息也就越多,那么对应的图像也就越清晰, ...

  8. 数据压缩12 | 实验8 | H.264视频编解码

    目录 一.实验准备 1. H.264编码过程 2. 调试和编码(参考JM Reference Software Manual (JVT-AE010)) 3. 编码参数(参考JM Reference S ...

  9. 四. 常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码

    常见H.264视频编解码器(X264和JM)及参考软件JM的下载与编解码 我们已经知道,H.264是一种视频压缩标准,其只规定了符合标准的码流的格式,以及码流中各个语法元素的解析方法.H.264标准并 ...

最新文章

  1. MySQL中,当 update 修改数据与原数据相同时会再次执行吗?
  2. javascript动态创建可拖动、最大化、最小化的层
  3. mongodb save和insert区别
  4. 设计模式学习笔记——模板(Template)模式
  5. 跟我学ModelArts丨探索ModelArts平台个性化联邦学习API
  6. 神经网络之感知器算法简单介绍和MATLAB简单实现
  7. Java读取word中表格
  8. 恢复Cisco路由器密码
  9. 在VUE中实现城市及对应的地区的联动渲染
  10. 两种常用电容式麦克风 MEMS还是ECM
  11. 2021-08-24
  12. Win10提示“为了对电脑进行保护,已经阻止此应用”如何解决
  13. Mindjet MindManager2022完整版思维导图v22.1.234版本
  14. PhpStorm修改字体和主题
  15. 改文拽少爷的校花女友33
  16. FPGA系列:ZCU102开发板上的第一个工程(MIG控制器)
  17. React之npm发布Antd样式的组件
  18. 2013年08月威海之旅
  19. JAVA用一维数组生成福彩双色球中奖号码
  20. SAP China 招聘

热门文章

  1. How to exploit MySQL index optimizations
  2. windows10U盘启动
  3. 2022电赛声源定位(基础篇)
  4. 基本等离子体波的色散关系
  5. Spring和Spring Boot区别
  6. 50个热门语义分割数据集免费、高速下载资源分享,涵盖通用视觉、遥感、自动驾驶、医疗等多种场景题
  7. WLAN组网详细步骤
  8. 段正淳是否是一个卑劣的人?
  9. 推荐使用多年的这些高效的工具网站,每一个都值得收藏
  10. 干测试三年,我对数据库那可是一脸懵逼!