三、slice头相关的一些细节

1.关于POC的计算

图像序列号(POC)主要用于标识图象的播放顺序,同时还用于在对帧间预测片解码时,标记参考图像的初始图像序号。

对于每个编码帧有两个图像序列号,分别称为顶场序列号(TopFieldOrderCnt)和底场序列号(BottomFieldOrderCnt );对于每个编码场有一个图像序列号,对于一个编码顶场其称为TopFieldOrderCnt,对于编码底场,其称为 BottomFieldOrderCnt ;对于每个编码场对有两个图像序列号,TopFieldOrderCnt 和BottomFieldOrderCnt 分别用于标记该场对的顶场和底场。

opFieldOrderCnt 和BottomFieldOrderCnt 分别指明了相应的顶场/ 底场相对于前一个IDR 图像,或解码顺序中前一个包含memory_management_control_operation=5的参考图像(此值为5表示清空参考帧队列,因此有着跟IDR同样的效果),的第一个输出场的相对位置。这也意味着,只要遇到IDR图像或memory_management_control_operation=5的参考图像,相应的POC就等于零。

在H.264中,由于B帧可以进行双向预测,因此图像的解码顺序可以不同于播放顺序。

前面已经提到,计算POC(也就是计算TopFieldOrderCnt和BottomFieldOrderCnt),有三种方法,具体使用哪种由序列参数集中的pic_order_cnt_type元素指定。下面一次介绍三种方法。

(1)      pic_order_cnt_type=0

本过程的输入是在本节规定的解码顺序中前一图像的PicOrderCntMsb,也即prevOrderCntMsb 。 关于Msb:POC由高位Msb和低位Lsb两部分组成,当Lsb发生溢出时,会向Msb进位,Msb+Lsb=POC。

本过程的输出是TopFieldOrderCnt  和BottomFieldOrderCnt ,或者其中之一。

大致流程:首先计算变量 prevPicOrderCntMsb,然后计算当前图像的PicOrderCntMsb (刚刚提到POC=Msb+Lsb,Lsb已经在码流的slice_header中由pic_order_cnt_lsb指定,因此主要任务其实就是计算Msb),最后计算当前图像的TopFieldOrderCnt  和(或) BottomFieldOrderCnt。

流程图:

其中,MaxPicOrderCntLsb由序列参数集中的log2_max_pic_order_cnt_lsb_minus4元素确定;delta_pic_order_cnt_bottom在片头中指定,上文介绍POC时已介绍过。

图中所提到的“乱序”,指的是,解码顺序是否与播放顺序不一致,准确的说,如果当前图像的播放顺序POC比上一个解码图像的播放顺序prePOC小,就是发生了乱序。那如何判断是否发生乱序了呢?一般情况下,Msb在解码顺序相邻的两个图片间不发生变化时(即Lsb不发生溢出或借位),只要判断当前解码的Lsb是否比之前解码的Lsb小即可,如果比之前的小,说明乱序发生;但是如果Lsb发生溢出或借位时,就不能这样简单的判断了(前后两帧的Msb将不再相同),那如何判断Lsb是否发生了溢出或借位呢?这就要用到一个规则:解码顺序相邻的两个图像,他们的播放顺序POC之差(的绝对值)不会超过MaxPicOrderCntLsb / 2,根据这个规则,计算解码顺序相邻的两幅图像的Lsb之差,并与MaxPicOrderCntLsb/2进行比较,就可判断Lsb是否发生了溢出或借位。在标准中,关于Msb的计算是这样描述的:

if( ( pic_order_cnt_lsb  < prevPicOrderCntLsb ) &&

( ( prevPicOrderCntLsb − pic_order_cnt_lsb )  >= ( MaxPicOrderCntLsb / 2 ) ) )

PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb

else if( ( pic_order_cnt_lsb  > prevPicOrderCntLsb )  &&

( ( pic_order_cnt_lsb − prevPicOrderCntLsb )  >  (MaxPicOrderCntLsb / 2 ) ) )

PicOrderCntMsb = prevPicOrderCntMsb − MaxPicOrderCntLsb (这一步乱序发生)

else

PicOrderCntMsb = prevPicOrderCntMsb

(1)      pic_order_cnt_type=1

本过程的输入是在本节规定的解码顺序中前一图像的FrameNumOffset。

本过程的输出是TopFieldOrderCnt  和BottomFieldOrderCnt ,或者其中之一。

计算过程中涉及到两个变量prevFrameNum  和prevFrameNumOffset ,其中prevFrameNum 是前一图像的frame_num  ,而对于prevFrameNumOffset ,如当前图像不是IDR ,而前一图像的memory_management_control_operation等于5 ,prevFrameNumOffset 设为0;否则,prevFrameNumOffset 设置等于前一图像的FrameNumOffset;注意,当序列参数及中的gaps_in_frame_num_value_allowed_flag 等于1 时(表示相邻解码图像的frame_num可以出现间隔),通过frame_num 间隔的解码过程可能会推断出解码顺序中的前一幅图像为“不存在”帧。

大致流程:

毕厚杰书中的插图并没有完全解释清楚,有很多细节没有说明,看了这个图还是感觉什么都没看懂,所以这部分还是结合标准中的说明来看比较好,但标准中只是说了每个值该如何计算,而没有详细讲解,因此要想搞懂每一步的意义也要费点脑细胞才行。

a.关于absFrameNum:可以理解成“绝对帧序号”,而原来的frame_num则应理解为相对帧序号。前面讲frame_num时提到,当一个序列中的参考帧数量超过MaxFramenum时,frame_num在达到MaxFramenum后会重新从0开始循环计数,这样的话,一个序列中可能会存在两个或多个参考图像拥有相同的“相对帧序号(即frame_num)”的情况 。因此,如果需要一个符号来唯一地标识一个序列中的所有参考帧,用相对帧序号是不行的,于是就需要为每个参考帧分配一个绝对帧序号:absFrameNum=FrameNumOffset + frame_num,FrameNumOffset代表当前序列中frame_num已经循环的次数与MaxFrameNum的积。absFrameNum的具体计算过程如下:

if(num_ref_frames_in_pic_order_cnt_cycle !=  0 )

absFrameNum = FrameNumOffset +frame_num

else

absFrameNum = 0

if( nal_ref_idc  = = 0  &&  absFrameNum >  0 )

absFrameNum = absFrameNum − 1

其中,num_ref_frames_in_pic_order_cnt_cycle在序列参数集中指定,其取值范围[0,255],它是数组offset_for_ref_frame[]的变化周期(或者说数组长度),这个数组也是在序列参数集中指定的,这个周期和这个数组到底什么意思呢?在用第二种POC计算方法时,一个参考帧跟下一个参考帧,他们POC的差值不能是任意的,必须是周期变化的,即每隔num_ref_frames_in_pic_order_cnt_cycle个参考帧,相邻参考帧之间POC的差值循环一次,而每个周期中,第i个差值即为offset_for_ref_frame[i],正是这个规律,才使得第二种POC算法变得可行,在步骤c、d中将会看到这个数组的作用。

nal_ref_idc  = =  0 表示当前图像不是参考图像。当当前图像不是参考图像时,absFrameNum要额外减1。

b. picOrderCntCycleCnt 和 frameNumInPicOrderCntCycle 这俩分别代表absFrameNum对num_ref_frames_in_pic_order_cnt_cycle(前面说到的周期值)取模和取余。当absFrameNum 大于0 时,二者的值由如下方法得到:

if(absFrameNum  >  0 ) {

picOrderCntCycleCnt=(absFrameNum− 1 ) / num_ref_frames_in_pic_order_cnt_cycle ;

//得到完整周期数。

frameNumInPicOrderCntCycle=(absFrameNum−1)%num_ref_frames_in_pic_order_cnt_cycle;

//得到最后一个不完整的周期中参考帧的数量

}

c.关于expectedDeltaPerPicOrderCntCycle:代表每隔一个完整周期,POC总的变化量。求这个值只需将上面说到的数组中各个元素相加即可。

expectedDeltaPerPicOrderCntCycle = 0

for( i = 0;  i <num_ref_frames_in_pic_order_cnt_cycle; i++ )

expectedDeltaPerPicOrderCntCycle += offset_for_ref_frame[ i ]

d.关于expectedPicOrderCnt:期望的POC值。要得到这个值,只需用POC在一个完整周期内的总变化量乘以周期数,再加上最后一个不完整周期跨越的POC数量即可。

if(absFrameNum > 0 ){

expectedPicOrderCnt=picOrderCntCycleCnt* expectedDeltaPerPicOrderCntCycle

for( i = 0; i <= frameNumInPicOrderCntCycle; i++ )

//循环计算最后一个不完整周期所跨越的POC数量。

expectedPicOrderCnt = expectedPicOrderCnt + offset_for_ref_frame[ i]

} else

expectedPicOrderCnt = 0

if( nal_ref_idc  = =  0 )      //对于非参考图像

expectedPicOrderCnt = expectedPicOrderCnt + offset_for_non_ref_pic

其中,offset_for_ref_frame[ i ](前面已经说过)和offset_for_non_ref_pic都是在序列参数集中指定,他们的取值范围都是[-2^31,2^31-1]。

e.变量TopFieldOrderCnt  或 BottomFieldOrderCnt 的值由如下方法得到:

if( !field_pic_flag ) {    //当前图像不是场图像

TopFieldOrderCnt = expectedPicOrderCnt + delta_pic_order_cnt[ 0 ]

BottomFieldOrderCnt = TopFieldOrderCnt +

offset_for_top_to_bottom_field + delta_pic_order_cnt[ 1 ]

} else if( !bottom_field_flag )

TopFieldOrderCnt = expectedPicOrderCnt + delta_pic_order_cnt[ 0 ]

else

BottomFieldOrderCnt =  expectedPicOrderCnt + offset_for_top_to_bottom_field +delta_pic_order_cnt[ 0 ]

其中,offset_for_top_to_bottom_field在序列参数集中指定,delta_pic_order_cnt[ 0或1 ]在片头指定。

补充:从上面的过程中可以看到,当图像序列中出现两个或多个连续的非参考帧时,这些非参考帧将具有相同的POC期望值(即expectedPicOrderCnt),但是他们的顶场序号和底场序号可以通过各自的delta_pic_order_cnt[0和1 ]加以区别。因此第二种POC计算方法也是支持图像序列中出现连续非参考帧的。而下面将介绍的第三种POC计算方法则不支持连续的非参考帧。

(1)      pic_order_cnt_type=2

第三种POC计算方法所依赖的额外参数最少(可以节省片头的比特数),它只根据frame_num就可以得到顶、底场的序列号。但是缺点是,用这种方法时,不允许图像序列中出现连续的非参考帧。

大致流程:

这个方法不像第二种那么麻烦,从图示中已可以看出完整的过程。唯一需要说的一点是,最后计算顶、底场序号时的规则(这个规则在三种方法中各不相同):

if(!field_pic_flag ) {

TopFieldOrderCnt = tempPicOrderCnt

BottomFieldOrderCnt = tempPicOrderCnt

} else if(bottom_field_flag )

BottomFieldOrderCnt = tempPicOrderCnt

else

TopFieldOrderCnt = tempPicOrderCnt

在这种方法中,如果解码顺序相邻的两个帧具有相同的frame_num,那么其中必有一个是参考帧,而另一个则是非参考帧,而且参考帧总是在非参考帧之前进行编解码(和传输),但非参考帧比参考帧先采样和播放(即非参考帧的POC更靠前)。

从Slice_Header学习H.264(三.1)--相关细节之 POC的计算相关推荐

  1. 从Slice_Header学习H.264--相关细节之 POC的计算

    博客地址:http://blog.csdn.net/newthinker_wei/article/details/8784720 参考网址:H.264中POC类型之探讨:http://www.360d ...

  2. H.264再学习 -- H.264视频压缩标准

    如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78221863 H.264 这部分一直在讲,但是却没有系统的来说.接下来要详细 ...

  3. H.264 中的相关问题

    帧内解码时,在解码端,首先通过当前宏块左边.上边已经解码完成的宏块使用当前宏块的预测模式(预测模式计算过程请参见我的论文<H.264数字视频差错控制技术的研究>,在群FTP"本群 ...

  4. SpringMVC学习笔记:springMVC中相关细节

    SpringMVC中相关细节 1.什么是MVC? MVC是一种软件架构思想,将软件按照模型.视图.控制器来划分. ①M:模型层:指工程中的javaBean,作用是处理数据.javaBean分为两类:一 ...

  5. 计算同比 环比_PowerBI学习教程(三)时间累积同比环比计算

    YOY 同比增长率( Year Over Year): 按年度计的增长率 MOM 环比增长率 (Month Over Month): 按月度计的增长率 YTD 年累计 (Year to Date): ...

  6. H.264学习历程(天之骄子)

    半年前,我知道了H.264这个名词.那个时候决定学习H.264,可是我连资料都不知道如何收集.而且整个学校就只有我一个人在学习H.264,找不到人交流,所以那个时候学得真的是举步维艰,很痛苦,而能在网 ...

  7. H.264编解码标准的核心技术(提供相关流程图)

    最近在学习H.264编解码知识,上网搜了不少资料看,发现大多数中文资料中都缺少相应的图片,例如编解码流程图.编码模板等,这对加深理解是很有帮助 的.木有办法,只好回去潜心阅读<H.264_MPE ...

  8. Android音视频开发基础(六):学习MediaCodec API,完成视频H.264的解码

    前言 在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了<Android 音视频从入门到提高 - 任务列表>.本文是Android音视 ...

  9. 视音频编解码学习工程:H.264分析器

    ===================================================== 视音频编解码学习工程系列文章列表: 视音频编解码学习工程:H.264分析器 视音频编解码学习 ...

  10. 视音频编解码学习工程 H 264分析器

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! ==== ...

最新文章

  1. Python爬一下抖音上小姐姐的视频~
  2. mysql 预编译_PHP中MySQL的预处理(预编译)执行方式
  3. vue之node.js的简单介绍
  4. java 抽象工厂模式简单实例
  5. Kubernetes Ingress 高可靠部署最佳实践
  6. 我是WPF菜鸟之(4)---关于XAML与逻辑代码
  7. STLC++(队列queue、栈stack、set\multiset 的用法)
  8. Android页面传值b,android数据传递(一)之activityA传递到activityB
  9. android密度计算器,密度计算器
  10. 计算机配置音箱便宜,5款性价比高的电脑小音箱推荐(每一款音质都相当ok
  11. show-busy-java-threads
  12. 如何实现报表高精度打印——套打
  13. 新星计划·能够 120% 提升博文美感的表情包,你们确定不心动吗?
  14. 租房买房行业报告上线,为房产服务数字化转型添砖加瓦
  15. 用matlab对相机进行标定获取相机内参
  16. gtsam Overview
  17. 使用idea将dev分支合并到master分支
  18. Windows远程桌面连接报错【无法连接到远程计算机】
  19. ubuntu 安装 navicat
  20. 中鑫优配:黄金高位震荡等破位,原油顺势做空看跌!

热门文章

  1. mysql8.0.15源码linux_源码安装mysql8.0.20
  2. python ddos 伪装_Python进行DDOS攻击
  3. Alibaba秋招前端测试题
  4. MySQL · 特性分析 · 执行计划缓存设计与实现
  5. Android图片压缩
  6. 自定义Inspector检视面板
  7. Symfony2插件FOSUserBundle的使用说明
  8. VMware:虚拟化技术为运营商消除隐患
  9. ZPCategory
  10. LeetCode 951. Flip Equivalent Binary Trees