深入浅出H.264码流分析
一、H.264是啥?
H.264就像视频的快递打包术:
- 作用:把原始视频压缩成小包裹(节省空间/带宽)
 - 地位:最常用的视频编码标准(监控/直播/视频网站都在用)
 - 压缩原理:只存储画面变化的部分(关键帧+差异帧)
 
二、H.264码流结构
1. 三层结构
graph TD
    A[H.264码流] --> B[NALU单元]
    B --> C[Slice切片]
    C --> D[宏块MB]
2. 关键概念
| 术语 | 作用 | 类比 | 
|---|---|---|
| NALU | 数据基本单元 | 快递包裹 | 
| SPS/PPS | 解码参数说明书 | 快递面单 | 
| I帧 | 完整画面 | 完整商品 | 
| P帧 | 与前一帧的差异 | 商品补发件 | 
| B帧 | 前后帧的差异 | 需要前后包裹才能组装 | 
三、NALU单元解析
1. NALU头部格式
            
            
              diff
              
              
            
          
          0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|F|NRI| Type   |
        - F(1bit):错误标志(0=正常)
 - NRI(2bit):重要性(11=最重要)
 - Type(5bit):类型代码
 
2. 常见NALU类型
| 类型值 | 名称 | 作用 | 
|---|---|---|
| 1 | 非IDR Slice | 普通帧数据 | 
| 5 | IDR Slice | 关键帧(I帧) | 
| 6 | SEI | 补充信息(如时间戳) | 
| 7 | SPS | 解码参数集 | 
| 8 | PPS | 图像参数集 | 
四、手工分析H.264文件
1. 用xxd查看二进制
            
            
              bash
              
              
            
          
          xxd -g 1 test.h264 | less
        输出示例:
            
            
              makefile
              
              
            
          
          00000000: 00 00 00 01 67 64 00 1e ac d9 40 50 05 bb 01 10  ....gd....@P....
00000010: 00 00 00 01 68 eb e3 cb 22 c0 00 00 00 01 65 b8  ....h...".....e.
        00 00 00 01:NALU起始码67:SPS(type=7)68:PPS(type=8)65:IDR帧(type=5)
2. 使用工具分析
            
            
              bash
              
              
            
          
          # 使用ffprobe查看
ffprobe -v error -show_frames test.h264
# 输出示例:
[FRAME]
media_type=video
key_frame=1  # 关键帧
pkt_pts=0
pkt_size=28543
[/FRAME]
        五、安卓代码解析H.264
1. 提取SPS/PPS
            
            
              java
              
              
            
          
          // 从MediaCodec获取参数
ByteBuffer sps = format.getByteBuffer("csd-0"); // SPS
ByteBuffer pps = format.getByteBuffer("csd-1"); // PPS
// 转16进制打印
Log.d("H264", "SPS: " + bytesToHex(sps));
Log.d("H264", "PPS: " + bytesToHex(pps));
        2. 实时解析帧类型
            
            
              java
              
              
            
          
          MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int outputIndex = decoder.dequeueOutputBuffer(info, timeout);
if (outputIndex >= 0) {
    ByteBuffer buffer = outputBuffers[outputIndex];
    byte[] data = new byte[info.size];
    buffer.get(data);
    
    // 检查NALU类型
    int nalType = data[4] & 0x1F; // 取第5字节的低5位
    String frameType;
    switch (nalType) {
        case 5: frameType = "I帧"; break;
        case 1: frameType = "P帧"; break;
        default: frameType = "其他帧";
    }
    Log.d("H264", "帧类型: " + frameType);
}
        六、H.264关键帧结构
1. I帧(关键帧)
- 
特点:完整图像,解码不依赖其他帧
 - 
结构 :
cssSPS + PPS + SEI + I Slice 
2. GOP(图像组)
graph LR
    I --> P --> B --> B --> P --> B --> I
- GOP长度:两个I帧之间的间隔(直播通常2秒)
 
七、码流分析工具推荐
| 工具 | 用途 | 平台 | 
|---|---|---|
| Elecard StreamEye | 可视化分析 | Windows | 
| H264Visa | 逐帧查看 | Windows | 
| FFmpeg/FFprobe | 命令行分析 | 全平台 | 
| CodecVisa | 安卓端分析 | Android | 
八、避坑指南
1. 常见问题
- 花屏:丢失I帧或SPS/PPS
 - 无法解码 :NALU缺少起始码
00 00 00 01 - 同步问题:时间戳(PTS/DTS)错乱
 
2. 优化建议
- 关键帧间隔:直播设2秒(GOP=帧率×2)
 - SPS/PPS:每个I帧前重复发送
 - 码率控制:根据分辨率调整
 
九、终极分析口诀
"H.264码流分三层,NALU里藏乾坤
SPS/PPS是配置,I帧完整P帧差
起始码头要认准,0x000001是标准
分析工具辅助看,编解码问题无处藏"
掌握H.264码流分析,你就能像"视频医生"一样精准诊断问题! 🩺📹