H.264码流分析实验

H.264 码流分析实验

实验目标

选择一个.mp4或者.264文件,在码流分析仪软件中打开,从以下几个层次分析:

  1. 分析SPSPPS里都包含哪些主要的信息,给出参数值。(例如:分辨率、帧率、GOP结构信息等)。
  2. 以一个GOP为例,分析以下信息:
    • 每个图像帧的类型及采用的编码比特数、QP值;并以图像帧号为横坐标,每帧所用比特数为纵坐标画出曲线图;以图像帧号为横坐标、每帧所用QP为纵坐标画出曲线图。
    • 以第一个I帧作为分析对象,基于该帧图像的空间特性,分析每个宏块所采用的编码类型及其比例。
    • 以第一个P帧作为分析对象,基于该帧图像的空间和时间特性,分析每个宏块所采用的编码类型及其比例。
    • 以某一个B帧作为分析对象,基于该帧图像的空间和时间特性,分析每个宏块所采用的编码类型及其比例。

实验过程

分析SPS

SPS即Sequence Paramater Set,又称为序列参数集。SPS中保存了一组编码视频序列的全局参数。通过码流分析软件打开.264文件后可以查看到该文件的SPS信息如下:
在这里插入图片描述

profile_idc

标识当前H.264码流的profile。我们知道H.264中定义了三种常用档次的Profile

  • 基准档次:baseline profile
  • 主要档次:main profile
  • 扩展档次:extended profile
    H.264的SPS中,第一个字节就表示为profile_idc,根据它的值就可以确定码流符合那种档次:
    在这里插入图片描述

根据新的标准规定,我们的实验码流中, p r o f i l e _ i d c = 100 profile\_idc = 100 profile_idc=100,说明码流的档次为High

level_idc

标识当前码流的Level,编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流遵从的LevelLevel_idc决定。
在这里插入图片描述
由上表对照可得, 实验码流中 l e v e l _ i d c = 31 level\_idc = 31 level_idc=31,表示该码流支持720P高清格式,支持隔行扫描,每秒采样点数27648000。

seq_parameter_set_id

表示当前的序列参数集的id。通过该id值,图像参数集PPS可以引用其代表的SPS中的参数。

log2_max_frame_num_minus4

用于计算MaxFrameNum的值,公式如下:
M a x F r a m e N u m = 2 ( l o g 2 _ m a x _ f r a m e _ n u m _ m i n u s 4 + 4 ) MaxFrameNum = 2^{(log2\_max\_frame\_num\_minus4 + 4)} MaxFrameNum=2(log2_max_frame_num_minus4+4)
MaxFrameNumframe_num的上限值,frame_num是图像序号的一种表示方法,在帧间编码中常用作一种参考帧标记的手段。
在实验码流中: M a x F r a m e N u m = 2 6 = 64 MaxFrameNum = 2 ^ {6} = 64 MaxFrameNum=26=64

pic_order_cnt_type

表示解码picture order count(POC)的方法。POC是另一种计量图像序号的方式,与frame_num有着不同的计算方法。该语法元素的取值为0、1或2。
实验码流中为该值为0。

log2_max_pic_order_cnt_lsb_minus4

用于计算MaxPicOrderCntLsb的值,该值表示POC的上限,计算公式如下:
M a x P i c O r d e r C n t L s b = 2 ( l o g 2 _ m a x _ p i c _ o r d e r _ c n t _ l s b _ m i n u s 4 + 4 ) MaxPicOrderCntLsb = 2^{(log2\_max\_pic\_order\_cnt\_lsb\_minus4 + 4)} MaxPicOrderCntLsb=2(log2_max_pic_order_cnt_lsb_minus4+4)
实验码流中, M a x P i c O r d e r C n t L s b = 2 7 = 128 MaxPicOrderCntLsb = 2 ^ 7 = 128 MaxPicOrderCntLsb=27=128

num_ref_frames

表示参考帧的最大数目,实验码流中为16。

gaps_in_frame_num_value_allowed_flag

标识位,说明frame_num中是否允许不连续的值。实验码流中为0,说明不允许不连续的值。

pic_width_in_mbs_minus1

用于计算图像的宽度。单位为宏块个数,因此图像的实际宽度为:
f r a m e w i d t h = 16 × ( p i c _ w i d t h _ i n _ m b s _ m i n u s 1 + 1 ) ; frame_width = 16 × (pic\_width\_in\_mbs\_minus1 + 1); framewidth=16×(pic_width_in_mbs_minus1+1);
实验图像的实际宽度为: f r a m e _ w i d t h = 16 × 40 = 640 frame\_width = 16 \times 40 = 640 frame_width=16×40=640

pic_height_in_map_units_minus1

使用PicHeightInMapUnits来度量视频中一帧图像的高度。PicHeightInMapUnits并非图像明确的以像素或宏块为单位的高度,而需要考虑该宏块是帧编码或场编码PicHeightInMapUnits的计算方式为:
P i c H e i g h t I n M a p U n i t s = p i c _ h e i g h t _ i n _ m a p _ u n i t s _ m i n u s 1 + 1 ; PicHeightInMapUnits = pic\_height\_in\_map\_units\_minus1 + 1; PicHeightInMapUnits=pic_height_in_map_units_minus1+1;
实验图像的值为22,故 P i c H e i g h t M a p U n i t s = 23 PicHeightMapUnits = 23 PicHeightMapUnits=23

frame_mbs_only_flag

标识位,说明宏块的编码方式。当该标识位为0时,宏块可能为帧编码或场编码;该标识位为1时,所有宏块都采用帧编码。根据该标识位取值不同,PicHeightInMapUnits的含义也不同,为0时表示一场数据按宏块计算的高度,为1时表示一帧数据按宏块计算的高度。按照宏块计算的图像实际高度FrameHeightInMbs的计算方法为:
F r a m e H e i g h t I n M b s = ( 2 − f r a m e _ m b s _ o n l y _ f l a g ) ∗ P i c H e i g h t I n M a p U n i t s FrameHeightInMbs = ( 2 − frame\_mbs\_only\_flag ) * PicHeightInMapUnits FrameHeightInMbs=(2frame_mbs_only_flag)PicHeightInMapUnits
故实验图像的实际高度仍为23。

direct_8x8_inference_flag

标识位,用于B_SkipB_Direct模式运动矢量的推导计算。

frame_cropping_flag

标识位,说明是否需要对输出的图像帧进行裁剪。

vui_parameters_present_flag

标识位,说明SPS中是否存在VUI信息。

分析PPS

除了序列参数集SPS之外,H.264中另一重要的参数集合为图像参数集Picture Paramater Set(PPS)
在这里插入图片描述

pic_parameter_set_id

表示当前PPS的id。某个PPS在码流中会被相应的slice引用,slice引用PPS的方式就是在Slice header中保存PPS的id值。该值的取值范围为 [ 0 , 255 ] [0,255] [0,255]

seq_parameter_set_id

表示当前PPS所引用的激活的SPS的id。通过这种方式,PPS中也可以取到对应SPS中的参数。该值的取值范围为 [ 0 , 31 ] [0,31] [0,31]

entropy_coding_mode_flag

熵编码模式标识,该标识位表示码流中熵编码/解码选择的算法。当该值为0时,通常为指数哥伦布编码或者CAVLC;当该值为1时,通常为CABAC。

bottom_field_pic_order_in_frame_present_flag

标识位,用于表示另外条带头中的两个语法元素delta_pic_order_cnt_bottomdelta_pic_order_cn是否存在的标识。这两个语法元素表示了某一帧的底场的POC的计算方法。

num_slice_groups_minus1

表示某一帧中slice group的个数。当该值为0时,一帧中所有的slice都属于一个slice group
实验码流中此值为0,表示图像帧中只有一个Slice group

num_ref_idx_l0_default_active_minus1、num_ref_idx_l0_default_active_minus1

表示当Slice Header中的num_ref_idx_active_override_flag标识位为0时,P/SP/B slice的语法元素num_ref_idx_l0_active_minus1num_ref_idx_l1_active_minus1的默认值。

weighted_pred_flag

标识位,表示在P/SP slice中是否开启加权预测。

weighted_bipred_idc

表示在B Slice中加权预测的方法,取值范围为 [ 0 , 2 ] [0,2] [0,2]。0表示默认加权预测,1表示显式加权预测,2表示隐式加权预测。

pic_init_qp_minus26和pic_init_qs_minus26

表示初始的量化参数。实际的量化参数由该参数、slice header中的 s l i c e _ q p _ d e l t a s l i c e _ q s _ d e l t a \frac{slice\_qp\_delta}{slice\_qs\_delta} slice_qs_deltaslice_qp_delta计算得到。

chroma_qp_index_offset

用于计算色度分量的量化参数,取值范围为 [ − 12 , 12 ] [-12,12] [12,12]

deblocking_filter_control_present_flag

标识位,用于表示Slice header中是否存在用于去块滤波器控制的信息。当该标志位为1时,slice header中包含去块滤波相应的信息;当该标识位为0时,slice header中没有相应的信息。

constrained_intra_pred_flag

若该标识为1,表示I宏块在进行帧内预测时只能使用来自I和SI类型宏块的信息;若该标识位0,表示I宏块可以使用来自Inter类型宏块的信息。

redundant_pic_cnt_present_flag

标识位,用于表示Slice header中是否存在redundant_pic_cnt语法元素。当该标志位为1时,slice header中包含redundant_pic_cnt;当该标识位为0时,slice header中没有相应的信息。

可视化部分

以一个GOP为例,以图像帧号为横坐标、每帧所用比特数为纵坐标画出曲线图。
在这里插入图片描述

从上图可以看出,第一个GOP长度为250。我们导出他的statistic数据:
在这里插入图片描述
选取0-249的数据进行绘图:
在这里插入图片描述

以一个GOP为例进行分析

以帧作为分析对象,基于该帧图像的空间特性,分析每个宏块所采用的编码类型及其比例。

第一个I帧

在这里插入图片描述
在这里插入图片描述
可以看出,I帧本身作为帧内预测帧,无参考帧,编码时多采用 8 × 8 8\times8 8×8 16 × 16 16\times16 16×16的宏块,细节部分也有采用 4 × 4 4 \times 4 4×4的宏块,平均每个宏块编码比特数为37.480,QP值为27.328.

第一个B帧

在这里插入图片描述
在这里插入图片描述
可以看出,B帧作为双向参考帧,压缩比较高,有很多Skip宏块,平均每个宏块编码比特数为1.596,QP值为23.333。

第一个P帧

在这里插入图片描述
在这里插入图片描述
可以看出,P帧作为前向参考帧,压缩比比I帧高但比B帧低,有很多Skip宏块,也有部分 16 × 16 16 \times 16 16×16 8 × 8 8 \times 8 8×8的宏块,平均每个宏块编码比特数为10.870,QP值为30.558。

参考文献

https://blog.csdn.net/hdkks/article/details/117994216?spm=1001.2014.3001.5501
https://zhuanlan.zhihu.com/p/27896239
https://blog.csdn.net/heanyu/article/details/6204414

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
词法分析是编译器的第一个阶段,也称为扫描器。该阶段的任务是将源程序中的字符流转换为记号流,即将字符序列分解为有意义的单元,并生成相应的记号。 下面是一个简单的词法分析器的示例,可以用来识别C语言中的关键字和标识符。 ```c #include <stdio.h> #include <ctype.h> #include <string.h> int main() { char input[100]; printf("Enter C program:\n"); fgets(input, 100, stdin); // 读入一行输入 char *keywords[32] = {"auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"}; int num_keywords = 32; char identifier[100]; int index = 0; for (int i = 0; i < strlen(input); i++) { // 跳过空格、制表符和换行符 if (isspace(input[i])) continue; // 如果是字母,可能是关键字或标识符 if (isalpha(input[i])) { identifier[index++] = input[i]; while (isalnum(input[++i])) identifier[index++] = input[i]; identifier[index] = '\0'; // 对比关键字 for (int j = 0; j < num_keywords; j++) { if (strcmp(identifier, keywords[j]) == 0) { printf("%s is a keyword\n", identifier); break; } } // 如果不是关键字,就是标识符 if (!isalpha(input[i])) { printf("%s is an identifier\n", identifier); index = 0; } i--; } } return 0; } ``` 该程序通过读取一行输入,逐个字符进行词法分析。它首先定义了所有C语言关键字的字符串数组,然后逐个读入字符并进行处理。如果读入的是空格、制表符或换行符,则跳过。如果读入的是字母,则可能是关键字或标识符,程序会一直读入字母和数字,直到遇到一个非字母非数字的字符。然后对比关键字数组,如果匹配上了,就输出该关键字;否则,就输出标识符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CUCKyrie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值