本文仅针对为解析并修改SPS以及PPS参数的方便性,记录编程逻辑,不做理论科普
H264官方标准文档
这个baidu可以找到的,或者关注我私信我发 ,即便理论知识到处都可以查到,但是官方文档还是有必要看一下的,里面针对每一位参数都有详细的介绍以及标明限制情况
H264的基本结构
此篇章不赘述理论,有非常多的好的理论科普文章,假设已知基本理论知识
只需要记住再获取SPS文件前,需要剥离容器获取裸流文件(.h264文件);
1.第一步找到SPS
我们如果需要找到SPS,首先应该知道,裸流文件中有非常多的NALU,而只有type=7的NALU含有SPS参数,所以在找到SPS之前应该是找到含有SPS的NALU;
找到NALU:
这一步很简单,无非两种类型0x000001或者0x00000001:
if ((buf[pos] == 0x00 && buf[pos + 1] == 0x00 && buf[pos + 2] == 0x01) ||
(buf[pos] == 0x00 && buf[pos + 1] == 0x00 && buf[pos + 2] == 0x00 && buf[pos + 3] == 0x01))
找到NALU之后再找SPS就很简单了,只需要判断type=7:
uint8_t nalu_type = buf[pos + (is_long_start_code ? 4 : 3)] & 0x1F;
做类型判断就行了
if (nalu_type == 7)
注意!!!关键的来了,为了后续修改SPS参数的方便性,建议直接打印该包含SPS参数的NALU所在的位置,记录该NALU在原H264文件的第几个NALU,具体实现方法不贴了,加个pos记录起始位置,后续打印出来就行了
到这里,我们需要把包含SPS的这个NALU提取出去,也就是获得了sps数据
2.借助工具进行参数解析
在编程解析之前,先借助工具,例如H264Visa.
先通过工具查看详细的SPS参数,作为我们解析的参考
3.SPS参数解析
解析这一步参考标准文档给出的标准格式进行解析
具体涉及到三种编码格式,详细的理论不做介绍这里只介绍编码过程,在我们获取到含SPS参数的NALU后,需要跳过第一个字节,然后就进入真正的seq_parameter_set_data( ),第一个字节便是profile_idc;,紧接着就是constraint_set_flag,再往下便是level_idc;
sps->profile_idc = br_get_u8(&br);/*profile_idc*/
//br_skip_bits(&br, 8); /*constraint_set_flag*/
int constraint_set0_flag = br_get_bit(&br);
int constraint_set1_flag = br_get_bit(&br);
int constraint_set2_flag = br_get_bit(&br);
int constraint_set3_flag = br_get_bit(&br);
int constraint_set4_flag = br_get_bit(&br);
int reserved_zero_3bits = br_get_bits(&br,3);
printf("constraint_set_flag:%d %d %d %d %d %d\n",constraint_set0_flag,constraint_set1_flag,
constraint_set2_flag,constraint_set3_flag,constraint_set4_flag,reserved_zero_3bits);
sps->level_idc = br_get_u8(&br); /*level_idc*/
需要注意的是假如 profile_idc满足条件,会进行扩展
if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122
|| profile_idc == 244 || profile_idc == 44 || profile_idc == 83
|| profile_idc == 86 || profile_idc == 118 )
{
int chroma_format_idc=br_get_ue_golomb(&br);/* chroma_format_idc */
if (chroma_format_idc == 3)
{
br_skip_bit(&br); /* residual_colour_transform_flag */
}
br_skip_ue_golomb(&br); /* bit_depth_luma - 8 */
br_skip_ue_golomb(&br); /* bit_depth_chroma - 8 */
br_skip_bit(&br); /* transform_bypass */
if (br_get_bit(&br)) /* seq_scaling_matrix_present */
for (i = 0; i < ((chroma_format_idc !=3)?8:12); i++)
{
if (br_get_bit(&br))
{
/* seq_scaling_list_present */
int last = 8, next = 8, size = (i<6) ? 16 : 64;
for (j = 0; j < size; j++)
{
if (next)
next = (last + br_get_se_golomb(&br)) & 0xff;
last = next ? next: last;
}
}
}
}
最终可以将SPS参数全部解析出来
这里有一个很关键的地方,就是在解析的过程中加上索引,记录每一位变量在此SPS中的偏移量,实例如下:
int pic_index;/*存储pic_height索引位置*/
int pic_len;/*存储pic_height编码长度*/
int sps_nalu_position;/*存储裸流中有SPS的位置*/
4.SPS参数修改
实际上修改这一步,有两个难点
难点一:精准定位到需要修改的参数
难点二:修改哥伦布编码的数据
针对难点一:
实际上如果前述的找到SPS所在NALU时,记录了该NALU所在的位置,假如为pos=24,
在解析各参数的记录了各参数的索引,即相对SPS首地址的偏移量,假设需要修改的参数index=46,
那么定位就变成很简单,直接在原H264文件中找到pos=24的NALU,在其起始位置的基础上+8+46就是我们需要修改的参数首地址了,+8代表NALU第一个字节(记录type等信息),+46则代表在SPS中的偏移。此时就完成了参数的定位;
针对难点二:
假如需要将一位使用哥伦布编码的参数从31修改为29,那么需要先将29编码成哥伦布编码格式,再重新写入,其中注意记录所占字节长度以便写入。
到这里基本上就完成了SPS的解析与修改,PPS参数类似