当前位置: 首页 > news >正文

H264编码结构和解析

H.264(又称 MPEG-4 AVC)是目前应用最广泛的视频编码标准之一,其高效的压缩性能和良好的网络适配性使其成为流媒体、安防监控、视频会议等场景的核心技术。理解 H.264 的编码结构和解析过程,需要从其分层设计、核心数据单元及解码流程逐步展开。

一、H.264 的编码结构

H.264 的编码结构采用分层设计,核心分为视频编码层(VCL)网络抽象层(NAL),两者协同实现视频压缩与网络传输的适配。

1. 网络抽象层(NAL,Network Abstraction Layer)

NAL 的核心作用是将 VCL 生成的压缩数据封装为适合网络传输的格式,解决不同网络(如 IP、广播、存储)的适配问题。其基本单元是NAL 单元(NALU,NAL Unit)。
(1)NALU 的结构
每个 NALU 由1 字节的头部(NAL Header) 和负载(RBSP) 组成,结构如下:

[NAL Header (1字节)] + [RBSP (可变长度)]

NAL Header(头部):共 8 位,包含 3 个关键字段,决定 NALU 的类型和传输优先级:

  • forbidden_zero_bit(1 位):禁止位,通常为 0;若为 1,表示该 NALU 存在严重错误,需丢弃。
  • nal_ref_idc(2 位):参考重要性指示,值越大表示该 NALU 对解码的重要性越高(如 I 帧的 NALU 通常为 3,B 帧可能为 0)。
  • nal_unit_type(5 位):NALU 类型标识,决定负载的内容,常见类型如下:
    • 0-23:VCL 相关(如 Slice 数据);
    • 24-31:非 VCL 相关(如参数集、补充增强信息)。
      例如:nal_unit_type=7表示序列参数集(SPS),=8表示图像参数集(PPS),=5表示 I 帧的 Slice 数据。

负载(RBSP,Raw Byte Sequence Payload):VCL 层输出的压缩数据经处理后的形式。VCL 输出的原始数据称为SODB(String of Data Bits),为了避免字节对齐问题,会在 SODB 末尾添加停止位(1 个或多个 0),形成 RBSP。

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

VCL 负责核心的视频压缩逻辑(如预测、变换、量化、熵编码),生成压缩后的视频数据。其数据结构按层次分为:序列(Sequence)→ 图像(Picture)→ Slice(片)→ 宏块(Macroblock)→ 子块(Subblock)
(1)序列(Sequence)
一个 “序列” 是由一系列连续的图像组成的视频片段,其全局参数由序列参数集(SPS,Sequence Parameter Set) 定义。SPS 是解码的 “全局配置”,包含影响整个序列的关键参数:

  • profile_idc和 level_idc:定义编码的 “能力等级”,限制最大码率、分辨率、帧率等,确保不同设备的兼容性。
  • 图像尺寸:pic_width_in_mbs_minus1(宽度,以宏块为单位)、pic_height_in_mbs_minus1(高度,以宏块为单位),需结合宏块大小(16x16)计算实际像素尺寸。
  • 帧 / 场编码模式:指示视频是按 “帧”(Frame)还是 “场”(Field)编码(场编码用于隔行扫描视频)。
  • 参考帧数量:max_num_ref_frames 定义最大参考帧数目,影响帧间预测的复杂度。

(2)图像(Picture)
一个 “图像” 对应一帧(Frame)或一场(Field)视频,其参数由图像参数集(PPS,Picture Parameter Set) 定义。PPS 依赖于 SPS,包含单幅图像的局部参数:
熵编码方式:entropy_coding_mode_flag 指示使用 CAVLC(0)还是 CABAC(1)(CAVLC 简单但压缩率低,CABAC 复杂但压缩率高)。
宏块划分:pic_init_qp_minus26 定义量化参数基准,影响画质与码率的平衡。
加权预测参数:用于 B 帧或 P 帧的参考帧加权,提升预测精度。
一个 SPS 可对应多个 PPS,一个 PPS 可对应多个图像(即同一参数配置可复用)。

(3)Slice(片)
每个图像由 1 个或多个 Slice 组成,Slice 是独立解码的最小单元(目的是限制误码扩散:若一个 Slice 出错,仅影响该 Slice,不扩散到其他 Slice)。

  • Slice 的组成:每个 Slice 包含Slice 头部和一系列连续的宏块(按光栅扫描顺序排列)。
  • Slice 头部:包含解码关键信息,如:
    • slice_type:指示 Slice 类型(I、P、B 等,决定预测方式)。
    • pic_order_cnt_lsb:图像顺序计数(POC),用于确定解码顺序与显示顺序的映射(尤其是 B 帧,解码顺序与显示顺序不同)。
    • ref_pic_list_reordering_flag_l0/l1:指示是否重排序参考帧列表(优化预测精度)。

(4)宏块(Macroblock)
宏块是编码的基本单元,对应原始图像中的一块区域。对于 4:2:0 色度采样(最常用),一个宏块包含:
1 个 16x16 的亮度块(Y);
2 个 8x8 的色度块(Cb、Cr)。
宏块的类型由其所属的 Slice 类型决定,核心分类如下:

  • I 宏块:仅依赖自身像素进行帧内预测(无需参考其他帧),适用于关键帧(I 帧)。
  • P 宏块:依赖前向参考帧(已解码的 I/P 帧)进行帧间预测,适用于预测帧(P 帧)。
  • B 宏块:依赖双向参考帧(前向和后向的 I/P 帧)进行帧间预测,压缩率最高,适用于双向预测帧(B 帧)。

每个宏块还包含残差数据(原始像素与预测值的差值,经变换和量化后的数据)和运动矢量(对 P/B 宏块,指示参考帧中匹配块的位置偏移)。

(5)子块(Subblock)
为提升预测精度,宏块可进一步划分为更小的子块(如 8x8、4x4),尤其是运动补偿时:
P/B 宏块的运动补偿单元(PU)可分为 16x16、16x8、8x16、8x8(8x8 还可细分为 4x4),灵活适配不同运动强度的区域(如快速移动的物体用小尺寸子块,静止区域用大尺寸)。

3. 熵编码

VCL 层的最后一步是熵编码,将量化后的残差、运动矢量等数据转换为二进制比特流(进一步压缩)。H.264 支持两种熵编码方式:
CAVLC(基于上下文的自适应可变长编码):用变长码表(VLC)编码,根据上下文动态选择码表,实现简单(适合低算力设备)。
CABAC(基于上下文的自适应二进制算术编码):将数据转换为二进制后,用算术编码压缩,结合上下文概率模型动态更新编码参数,压缩率比 CAVLC 高 5-10%,但复杂度更高(适合高性能设备)。

二、H.264 的解析过程(解码流程)

H.264 的解析(解码)是编码的逆过程,核心是从 NALU 流中恢复原始视频帧,步骤如下:

1. 码流读取与 NALU 提取

H.264 码流是连续的 NALU 序列,NALU 之间通过起始码(Start Code) 分隔:
短起始码:0x000001(3 字节);
长起始码:0x00000001(4 字节,通常用于码流开头或关键参数集前)。
解析时需先检测起始码,分割出单个 NALU,同时处理防竞争字节(Emulation Prevention Bytes):若 NALU 内部出现0x000000、0x000001(可能被误判为起始码),编码时会插入0x03(如0x000000→0x00000300),解析时需移除0x03以恢复原始数据。

2. NALU 类型判断与参数集解析

提取 NALU 后,先解析NAL Header,通过nal_unit_type判断 NALU 类型:
若为 SPS(nal_unit_type=7)或 PPS(nal_unit_type=8),则解析并存储参数集(SPS/PPS 需优先解析,后续 Slice 解码依赖这些参数)。
若为 Slice 数据(nal_unit_type=1-5等),则进入 Slice 解析流程。

3. Slice 解析

Slice 解析是核心步骤,依赖已解析的 SPS 和 PPS,流程如下:
解析 Slice 头部:获取slice_type(确定宏块类型)、POC(确定显示顺序)、参考帧列表等信息。
宏块解析:按顺序解析 Slice 内的每个宏块:
若为 I 宏块:执行帧内预测(根据 SPS 中的帧内预测模式,如 16x16、8x8 或 4x4 模式,用相邻已重构像素预测当前块)。
若为 P/B 宏块:执行帧间预测(根据运动矢量从参考帧中找到匹配块,计算预测值)。
残差恢复:对量化后的残差数据执行反量化和反变换(H.264 用 4x4 或 8x8 整数 DCT 变换,避免浮点运算),得到原始残差。
像素重构:将预测值与残差相加,得到重构像素。

4. 去块滤波(Deblocking Filter)

重构后的图像可能因宏块边界的量化误差产生 “方块效应”(块边缘明显),需通过去块滤波平滑边界:对宏块边缘的像素进行加权平均,在不模糊细节的前提下减少块效应,提升视觉质量。

5. 图像合成与输出

所有 Slice 解析完成后,合成完整的图像,根据 POC 调整显示顺序(尤其是 B 帧,解码顺序可能晚于显示顺序),最终输出解码后的视频帧。

总结

编码结构:H.264 通过 VCL(负责压缩)和 NAL(负责网络适配)分层设计,核心数据单元包括 NALU、参数集(SPS/PPS)、Slice、宏块,形成从序列到像素的多级结构。
解析过程:从码流中提取 NALU,解析 SPS/PPS 获取全局参数,再按 Slice→宏块的顺序解析,通过预测、残差恢复、去块滤波等步骤重构原始视频帧,最终输出连续的解码图像。
这种结构既保证了高效的压缩性能(通过帧内 / 帧间预测、变换量化),又具备良好的网络适应性(通过 NAL 的封装和 Slice 的独立解码),使其成为视频编码领域的经典标准。

http://www.dtcms.com/a/279941.html

相关文章:

  • 第四章 uniapp实现兼容多端的树状族谱关系图,剩余组件
  • ESP32 OTA升级详解:使用Arduino OTA库实现无线固件更新
  • HTML 文本格式化标签
  • java--ThreadLocal创建以及get源码解析
  • http常见状态码
  • 苦练Python第18天:Python异常处理锦囊
  • 【论文阅读】Masked Autoencoders Are Effective Tokenizers for Diffusion Models
  • rsyslog简单应用
  • STM32F769I-DISCO 串口调试
  • Linux上基于C/C++头文件查找对应的依赖开发库
  • SAP B1认证资料-题目
  • 分布式系统中实现临时节点授权的高可用性与一致性
  • 哈希扩展 --- 海量数据处理
  • CISSP知识点汇总- 通信与网络安全
  • 15.Python 列表元素的偏移
  • Java学习————————ThreadLocal
  • python Gui界面小白入门学习二
  • python高阶调试技巧,替代print
  • 14.推荐使用 dict.get(key) 而不是 dict[key]
  • redis配置(Xshell连接centos7的基础上)
  • Modbus 开发工具实战:ModScan32 与 Wireshark 抓包分析(一
  • Python `WeakValueDictionary` 用法详解
  • 调用 System.runFinalizersOnExit() 的风险与解决方法
  • C语言基础5——控制语句2(循环)
  • TypeScript枚举类型应用:前后端状态码映射的最简方案
  • 深入学习前端 Proxy 和 Reflect:现代 JavaScript 元编程核心
  • Java并发编程之线程池详解
  • openGL学习(Shader)
  • 【面板数据】全国地级市逐日空气质量指数AQI数据集(2013-2024年)
  • 代码随想录算法训练营第四十九天|单调栈part2