一、H.264概述与基本架构

H.264是由国际标准化组织(ISO/IEC)和国际电信联盟(ITU-T)联合认证的视频编码标准,于2003年正式发布。作为MPEG-2之后的重要升级,H.264在相同画质下较H.263、MPEG-2等标准减少50%以上的带宽需求,成为视频编码领域的主流方案。

H.264的编解码流程主要包括五个关键部分:

  1. 帧间和帧内预测(Estimation)
  2. 变换(Transform)和反变换
  3. 量化(Quantization)和反量化
  4. 环路滤波(Loop Filter)
  5. 熵编码(Entropy Coding)

H.264码流由一系列NALU(网络抽象层单元)组成,分为两层结构:

  • VCL(视频编码层):包含核心压缩引擎和块、宏块和片的语法级别定义
  • NAL(网络提取层):负责将VCL产生的比特字符串适配到各种网络和多元环境中

H.264相关概念解析

1. 序列(Coded Video Sequence)

在H.264中,序列指一段连续编码的视频数据流,以IDR帧开始,到下一个IDR帧结束。序列参数集(SPS)包含该序列所有图像的共享编码参数,如分辨率、帧率等。序列的划分基于内容变化程度:运动平缓时序列较长(多个P/B帧参考一个I帧),运动剧烈时序列较短(可能仅含I帧和少量P帧)。

2. 帧类型

H.264定义了三种帧类型:

  • I帧(帧内编码帧):通过帧内预测独立编码,不依赖其他帧,压缩率约7:1(类似JPEG)。每个GOP的第一个I帧必须是IDR帧。
  • P帧(前向预测帧):参考前一个I/P帧进行运动补偿,仅存储差异信息,压缩率约20:1。解码需依赖参考帧。
  • B帧(双向预测帧):同时参考前后帧的差异,压缩率可达50:1。解码复杂度最高,需缓存前后参考帧。
3. GOP(Group of Pictures)

GOP是两个IDR帧之间的图像组,包含一个IDR帧和若干P/B帧。其结构参数包括:

  • N值:两个IDR帧间的总帧数(如N=24表示1秒GOP,假设帧率24fps)。
  • M值:I帧与P帧间距(如M=3表示IBBPBBP结构)。 GOP越长压缩率越高,但随机访问和错误恢复能力越差。开放GOP允许跨GOP参考(无IDR帧),闭合GOP则禁止(以IDR帧起始)。
4. IDR帧(关键帧)

在编码解码中为了方便,将 GOP 中首个 I 帧要和其他 I 帧区别开,把第一 个 I 帧叫 IDR,这样方便控制编码和解码流程,所以 IDR 帧一定是 I 帧,但 I 帧不一定是 IDR 帧;IDR 帧的作用是立刻刷新,使错误不致传播,从 IDR 帧开始算新的序列开始编码。I 帧有被跨帧参考的可能,IDR 帧不会。IDR 帧不允许这样,例如:

IDR1 P4 B2 B3 P7 B5 B6 I10 B8 B9 P13 B11 B12 P16 B14 B15

  • 这里的 B8 可以跨过 I10 去参考 P7

IDR1 P4 B2 B3 P7 B5 B6 IDR8 P11 B9 B10 P14 B11 B12

  • 这里的 B9 就只能参照 IDR8 和 P11,不可以参考 IDR8 前面的帧

IDR帧是特殊I帧(NAL类型为5),其作用:

H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR 图像之后的图像永远不会使用 IDR 之前的图像的数据来解码。

H264 压缩方式

H264 采用的核心算法是帧内压缩和帧间压缩,帧内压缩是生成 I 帧的算法,帧 间压缩是生成 B 帧和 P 帧的算法。

帧内(Intraframe)压缩也称为空间压缩(Spatialcompression)。当压缩一帧 图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,这实际上与静态图 像压缩类似。帧内一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像, 所以可以独立的解码、显示。帧内压缩一般达不到很高的压缩,跟编码 jpeg 差 不多。

帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。也即连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。 帧间压缩也称为时间压缩(Temporalcompression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩一般是无损的。帧差值(Framedifferencing) 算法是一种典型的时间压缩法,它通过比较本帧与相邻帧之间的差异,仅记录本 帧与其相邻帧的差值,这样可以大大减少数据量。

压缩方式说明
  1. 分组,也就是将一系列变换不大的图像归为一个组,也就是一个序列, 也可以叫 GOP(画面组);
  2. 定义帧,将每组的图像帧归分为 I 帧、P 帧和 B 帧三种类型;
  3. 预测帧, 以 I 帧做为基础帧,以 I 帧预测 P 帧,再由 I 帧和 P 帧预测 B 帧;
  4. 数据传输, 最后将 I 帧数据与预测的差值信息进行存储和传输。

H264 分层结构详解

H.264(又称AVC)的分层结构是其高效压缩和网络适应性的核心设计,主要分为视频编码层(VCL)和网络抽象层(NAL)两层。这种分层设计既保证了编码效率,又增强了数据在不同网络环境下的传输特性。

以下是详细解析:

1. 视频编码层(VCL, Video Coding Layer)

VCL负责视频数据的核心压缩,通过复杂的算法将原始像素数据转换为高效的压缩比特流。其特点包括:

  • 功能:处理宏块(Macroblock)、片(Slice)的编码,包括帧内预测、帧间预测、变换量化、熵编码等。
  • 输出数据:生成SODB(String of Data Bits),即原始的比特流,长度可能不是8的整数倍。
  • 关键单元:
    • 宏块(Macroblock):16×16像素的基本编码单元,包含亮度和色度信息(如YUV格式)。
    • 片(Slice):由多个宏块组成,分为I片、P片、B片等类型,确保编码独立性以限制误码扩散。
2. 网络抽象层(NAL, Network Abstraction Layer)

NAL将VCL输出的数据适配到不同网络环境,提供封装和传输支持:

  • 功能:将SODB转换为适合传输的NALU(NAL Unit),添加头部信息以标识数据类型和重要性。
  • 关键处理:
    • RBSP(Raw Byte Sequence Payload):对SODB进行字节对齐,末尾补1和若干0以凑整。
    • EBSP(Encapsulated Byte Sequence Payload):在RBSP中插入防竞争字节(0x03),避免与起始码冲突。
  • NALU结构:
    • NALU Header(1字节):包含 forbidden_zero_bit 、 nal_ref_idc (重要性标识)和 nal_unit_type (数据类型,如SPS、PPS、IDR帧等)。
    • NALU Body:即EBSP或RBSP,承载实际编码数据。
3. 码流组织方式

H.264码流有两种主流格式,适应不同应用场景:

  • AnnexB格式:
    • 使用起始码(0x000001或0x00000001)分隔NALU,常见于实时流(如TS流、RTP传输)。
    • 通过防竞争字节(0x03)避免数据与起始码混淆。
  • AVCC(MP4格式):
    • 使用4字节长度前缀标识NALU大小,便于随机访问(如MP4文件)。
4. H264的层次化数据划分

从视频内容到传输单元的逻辑分层:

  • 序列(GOP):一组连续帧,以IDR帧开头,包含I/P/B帧。
  • 帧(Picture):一帧图像可划分为多个片(Slice),片间独立编码。
  • 片(Slice):由宏块组成,类型包括I/P/B片,决定预测方式。
  • 宏块(Macroblock):16×16像素单元,承载具体图像数据。

二、H264码流结构解析

H264 码流封装格式

H.264(AVC)码流的封装格式主要有两种:Annex B 和 AVCC。它们在结构、应用场景及解析方式上有显著差异,以下是详细对比与分析:

Annex B 封装(基于起始码)

1. 核心特点

  • 起始码分隔:每个 NALU(Network Abstraction Layer Unit)以固定的起始码( 0x000001  或  0x00000001 )开头,无显式长度字段。
    • 3字节起始码( 0x000001 )用于普通NALU;
    • 4字节起始码( 0x00000001 )用于SPS/PPS或码流开头。
  • 自包含性:NALU可独立解析,无需依赖其他数据。
  • 防止竞争:通过防竞争字节(0x03)避免数据与起始码混淆。
    • NALU的起始码为0x000001或0x00000001,同时H264规定,当检测到0x000000时,也可以表示当前NALU的结束。那这样就会产生一个问题,就是如果在NALU的内部,出现了0x000001或0x000000时该怎么办?因此,H264就提出了“防止竞争”这样一种机制,当编码器编码完一个NAL时,应该检测NALU内部,是否出现如下左侧的四个序列。当检测到它们存在时,编码器就在最后一个字节前,插入一个新的字节:0x03。

  • 这样一来,当我们拿到EBSP时,就需要检测EBSP内是否有序列:0x000003, 如果有,则去掉其中的0x03。这样一来,我们就能得到原始字节序列载荷:RBSP。

2. 结构示例

[0x00000001][SPS NALU][0x00000001][PPS NALU][0x00000001][IDR帧 NALU][0x000001][P帧 NALU]...

SPS/PPS作为独立NALU存在于码流中,通常位于视频开头或关键帧前。

3. 应用场景

  • 实时流媒体(如RTSP、RTP、TS流);
  • 文件存储(.264裸流、.mkv等);
  • 硬件设备(安防摄像头、机顶盒直接输出)。

4. 优缺点

  • 优点:兼容性好,支持流式传输,无需预先解析完整配置。
  • 缺点:起始码占用额外空间(每个NALU至少3字节开销),随机访问效率低(需扫描起始码定位)。
AVCC 封装(基于长度字段)

1. 核心特点

  • 长度前缀:每个NALU前用4字节(大端序)表示其长度,替代起始码。
  • 集中配置:SPS/PPS存储在文件头部的 extradata (如MP4的AVCC盒子),而非分散在码流中。

2. 结构示例

[extradata头部][NALU长度(4字节)][NALU数据][NALU长度(4字节)][NALU数据]...

 extradata 包含编码参数(如profile、level、SPS/PPS)。

3. 应用场景

  • 文件存储(MP4、MOV、FLV等容器);
  • 点播平台(如YouTube、Netflix);
  • API接口(Android MediaMuxer、iOS AVAssetWriter)。

4. 优缺点

  • 优点:节省空间(无起始码),支持快速随机访问(通过长度字段直接跳转)。
  • 缺点:需先解析 extradata 才能解码,不适合无完整头部的实时流。
关键差异对比

维度

Annex B

AVCC

NALU分隔方式

起始码( 0x000001 或 0x00000001 )

4字节长度前缀(大端序)

参数集存储

分散在码流中(SPS/PPS NALU)

集中在文件头( extradata )

随机访问性能

较差(需扫描起始码)

更优(直接跳转长度字段)

典型应用

实时流、TS流、.h264裸流

MP4、FLV、MKV等容器

格式转换与工具

两种封装常需相互转换:

  • Annex B → AVCC:提取SPS/PPS到 extradata ,替换起始码为长度前缀。
  • AVCC → Annex B:从 extradata 提取SPS/PPS并添加起始码,长度字段替换为起始码。 FFmpeg实现h264_mp4toannexb滤镜确实能自动完成该转换:
ffmpeg -i input.mp4 -c:v copy -bsf:v h264_mp4toannexb output.h264

H264的NAL单元

H.264的NALU(Network Abstraction Layer Unit)是视频码流的基本传输单元,负责将编码后的视频数据封装为适合网络传输或存储的格式。

以下是其核心要点:

1. H.264 NALU (网络抽象层单元) 结构详解

一个 NALU 是 H.264 码流的基本组成单元。其结构主要包含三个部分:

NALU Header由三部分组成:

  • 起始码(Start Code):标识NALU的开始,通常为3字节 0x000001 (流媒体)或4字节 0x00000001 (文件存储如MP4)。解码器通过起始码分割NALU单元。
  • 头部(NALU Header):1字节,包含以下字段:
    • forbidden_zero_bit(1 bit):固定为0,若为1表示传输错误需丢弃。
    • nal_ref_idc(2 bits):重要性标识(0~3),值越大优先级越高(如SPS、PPS、IDR帧通常为3)。
    • nal_unit_type(5 bits):NALU类型(见下表)。
  • 有效载荷(Payload):实际视频数据或参数集(如SPS、PPS、帧数据等)。

常见NALU类型:

Type

说明

1

非IDR帧(P/B帧)

5

IDR帧(关键帧)

6

SEI(增强信息)

7

SPS(序列参数集)

8

PPS(图像参数集)

2. NALU在码流中的组织

典型H.264码流顺序为:

 SPS → PPS → IDR帧 → P帧 → B帧 → ....

  • SPS/PPS:全局参数(分辨率、帧率等),需在IDR帧前传输以确保解码正确性。
  • IDR帧:关键帧,触发解码器刷新参考帧队列。
  • P/B帧:依赖参考帧进行解码,压缩率更高。

H.264(AVC)视频编码标准中的SPS(序列参数集)、PPS(图像参数集)和SEI(补充增强信息)是码流中的关键元数据,分别承担不同的功能。以下是它们的简要说明:

1. SPS(Sequence Parameter Set,序列参数集)

  • 作用:存储整个视频序列的全局参数,是解码器初始化的基础。
  • 核心内容:
    • 视频属性:分辨率(通过 pic_width_in_mbs_minus1 和  pic_height_in_map_units_minus1 计算)、帧率、色度格式(如4:2:0)。
    • 编码配置:编码档次(Profile,如Baseline/Main/High)、级别(Level,限制码率和复杂度)、参考帧数量( max_num_ref_frames )。
    • 其他参数:帧编号方式( log2_max_frame_num_minus4 )、场编码标志( frame_mbs_only_flag )等。
  • 位置:通常位于码流开头或关键帧(IDR帧)前,NALU类型为7。

2. PPS(Picture Parameter Set,图像参数集)

  • 作用:定义单帧或一组帧的解码参数,需引用SPS。
  • 核心内容:
    • 熵编码模式: entropy_coding_mode_flag (0=CAVLC,1=CABAC)。
    • 量化参数:初始QP值( pic_init_qp_minus26 ),影响画质与码率平衡。
    • 分片控制:分片组数量( num_slice_groups_minus1 )及去块滤波设置。
  • 位置:紧邻SPS或周期性插入,NALU类型为8。

3. SEI(Supplemental Enhancement Information,补充增强信息)

  • 作用:提供非必需但有助于解码或应用的附加信息。
  • 核心内容:
    • 常见类型:用户自定义数据(如 user_data_unregistered )、时间戳、版权信息、编码器参数等。
    • 特性:不参与核心解码流程,但可用于同步(如直播答题场景)或增强体验。
  • 位置:NALU类型为6,可出现在码流任意位置。

4. 关系与协同

  • 层级依赖:PPS通过 seq_parameter_set_id 关联SPS,帧数据通过 pic_parameter_set_id 关联PPS。
  • 传输要求:在实时流(如RTSP)中,SPS/PPS需周期性重复发送以确保解码容错性。

示例码流结构

(起始码 00 00 00 01 标识NALU边界)
00 00 00 01 67 ...  // SPS(NALU类型7)
00 00 00 01 68 ...  // PPS(NALU类型8)
00 00 00 01 06 ...  // SEI(NALU类型6)
00 00 00 01 65 ...  // IDR帧(NALU类型5)
3. NALU 主体部分

H.264的NALU(Network Abstraction Layer Unit)主体部分是编码数据的核心承载区域,其结构层次和数据处理机制如下:

1. NALU主体的组成结构

NALU的主体由三层数据单元构成,从原始编码数据到传输适配的封装形式依次为:

  • SODB(String of Data Bits):最原始的编码数据比特流,由视频编码层(VCL)直接生成,长度可能不是8的整数倍。
  • RBSP(Raw Byte Sequence Payload):对SODB进行字节对齐处理,在末尾添加一个比特“1”和若干比特“0”以补齐字节。例如,若SODB最后一个字节不足8位,会在最高位补1后填充0。
  • EBSP(Encapsulation Byte Sequence Payload):在RBSP基础上插入防竞争字节(0x03),防止NALU内部出现与起始码(0x000001或0x00000001)冲突的序列。例如,遇到“00 00 00”会插入0x03变为“00 00 03 00”。

2. 防竞争机制(Emulation Prevention)

H264 中规定,当检测到 0x000000 时,也可以表示当前 NALU 的结束。那这样就会产生一个问题,就是如果在 NALU 的内部,出现了 0x000001 或 0x000000 时该怎么办?

在 RBSP 基础上填加了仿校验字节(0x03)它的原因是:

在 NALU 加到 Annexb 上时,需要填加每组 NALU 之前的起始码 StartCodePrefix,如果该 NALU 对应的 Slice 为一帧的开始则用 4 位字节表示 0x00000001;否则用 3 位字节表示 0x000001。为了使 NALU 主体中的数据不与起始码相冲突,在编码时,每遇到两个字节连续为 0,就插入一个字节的 0x03。解码时将 0x03 去掉。也称为脱壳操作。

因此,为防止NALU主体内的数据被误解析为起始码或结束码,H.264规定在以下序列中插入0x03:

  • “00 00 00” → “00 00 03 00”
  • “00 00 01” → “00 00 03 01”
  • “00 00 02” → “00 00 03 02”
  • “00 00 03” → “00 00 03 03”

解码时需移除这些插入的0x03以恢复原始RBSP数据。

4. NALU主体的数据类型

NALU主体(RBSP)承载的数据类型由NALU头部的 nal_unit_type 决定,主要包括:

  • VCL数据:如片(Slice)数据(类型1-5),包含I帧、P帧、B帧的编码内容。
  • 非VCL数据:如序列参数集(SPS,类型7)、图像参数集(PPS,类型8)、补充增强信息(SEI,类型6)等,用于传递解码配置或辅助信息。
5. 层次化处理目的
  • VCL与NAL分离:VCL负责视频压缩(生成SODB),NAL负责网络适配(封装为RBSP/EBSP),便于在不同网络环境中传输。
  • 兼容性:通过起始码和防竞争机制,确保NALU在流式传输(如Annex B格式)或分片传输(如RTP打包)中的正确解析。

NALU主体通过SODB→RBSP→EBSP的转换,结合防竞争机制,实现了编码数据的安全封装与传输。其内容既包含视频压缩数据(Slice),也包含关键参数集(SPS/PPS),是H.264码流中连接编码与解码的核心环节。

Logo

邀请您加入社区

更多推荐