分析h264,看了文档只是有了大概的印象,今天通过具体的文件来分析h264格式(使用FFmpeg),首先要了解关于h264的基础知识

一、基础知识

h264有两种封装,
一种是annexb模式,传统模式,有startcode,SPS和PPS是在ES中
一种是mp4模式,一般mp4 mkv会有,没有startcode,SPS和PPS以及其它信息被封装在container中,每一个frame前面是这个frame的长度
很多解码器只支持annexb这种模式,因此需要将mp4做转换:

H264编码过程中的三种不同的数据形式:

SODB        数据比特串 ---->最原始的编码数据,即VCL数据;

RBSP      原始字节序列载荷 ---->在SODB的后面填加了结尾比特(RBSP trailing bits 一个bit“1”)若干比特“0”,以便字节对齐;

EBSP      扩展字节序列载荷 ---- > 在RBSP基础上填加了仿校验字节(0X03)它的原因是: 在NALU加到Annexb上时,需要添加每组NALU之前的开始码StartCodePrefix,如果该NALU对应的slice为一帧的开始则用4位字节表示,ox00000001,否则用3位字节表示ox000001(是一帧的一部分)。另外,为了使NALU主体中不包括与开始码相冲突的,在编码时,每遇到两个字节连续为0,就插入一个字节的0x03。解码时将0x03去掉。也称为脱壳操作。

H264/AVC 的分层结构

在实际文件中分析h264

NALU头结构

长度:1byte
forbidden_bit(1bit) + nal_reference_bit(2bit) + nal_unit_type(5bit)
 

1.forbidden_bit:                             禁止位,初始为0,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。

2.nal_reference_bit:                   nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。


不同类型的NALU的重要性指示如下表所示。 

nal_unit_type

NAL类型

nal_reference_bit

0

未使用

 0

1

非IDR的片

此片属于参考帧,则不等于0,

不属于参考帧,则等与0

2

片数据A分区

同上

3

片数据B分区

同上

4

片数据C分区

同上

5

IDR图像的片(关键帧)

5

6

补充增强信息单元(SEI)

0

7

序列参数集sps

非0

8

图像参数集pps

非0

9

分界符

0

10

序列结束

0

11

码流结束

0

12

填充

0

13..23

保留

 0

24..31

不保留

 0

       所谓参考帧,就是在其他帧解码时需要参照的帧。比如一个I帧可能被一个或多个B帧参考,一个B帧可能被某个P帧参考。

       从这个表我们也可以看出来,DIR的I帧是非常重要的,他一丢,那么这个序列的所有帧都没办法解码了;

       序列参数集和图像参数集也很重要,没有序列参数集,这个序列的帧就没法解;

       没有图像参数集,那用到这个图像参数集的帧都没法解。

二、MP4

1.读取h264的大致流程:

open_input_file-->find video stream index-->av_read_frame-->if(packet.index == video)-->write(file)-->close file

输入文件为h264(Main)+aac(HE-AAC)

保存的直接从文件中获取的h264的packet内容如下,截取片段:

在实际文件中分析h264在实际文件中分析h264

在实际文件中分析h264

MP4中的h264是不带startcode_prefix和sps、pps的’,前四个字节代表该帧的长度。

2.下面我们将对MP4格式的h264转化为annexb模式,在ffmpeg中用h264_mp4toannexb_filter可以做转换

实现:

char nal_start[]={0,0,0,1}; 

FILE *fp = fopen(outFilename, "wb");
注册filter
avcbsfc = av_bitstream_filter_init("h264_mp4toannexb");
转换bitstream
av_bitstream_filter_filter(bsfc, ic, NULL, &dummy,&dummy_len, packet.data,packet.size, 0);  

fwrite(ic->extradata,ic->extradata_size,1,fp); 

fwrite(nal_start,4,1,fp);  
fwrite(packet.data+4,packet.size-4,1,fp); 

其实对于MP4和mkv等格式来讲,sps、pps等信息都存放在了container中,使用bitstream可以将sps提取到AVCodecContext的extradata中,

将extradata中的sps和pps写到输出文件中,再将原本帧前面代表帧长度的四个字节替换为startcode,这样就可以被大多数播放器识别了。

输出文件:

在实际文件中分析h264

在实际文件中分析h264

红色框中为extradata中的数据。


相关文章:

  • 2021-05-14
  • 2022-01-17
  • 2021-07-28
  • 2022-12-23
  • 2021-08-06
  • 2021-11-15
  • 2022-01-12
猜你喜欢
  • 2021-12-06
  • 2022-12-23
  • 2022-12-23
  • 2021-09-18
相关资源
相似解决方案