StreamAnalyzer — 详细学习与日志解析指南

0

目标:帮助初学者从 bit/byte 层理解实时视频码流(以 H.264/H.265 为主),并配合 StreamAnalyzer.java 将各种信息以“因为 → 所以”的方式清晰记录下来,方便调试与学习。


目录

  1. 简介

  2. 从回调拿到的数据是什么?(总体概念)

  3. 码流的层级结构(从 bit 到帧)

  4. Start code(起始码)与如何分割 NALU

  5. NAL header(NAL 单元头)详解

  6. SPS / PPS 究竟是什么,为什么要解析它们

  7. RBSP 与 emulation-prevention(为什么要去除 0x03)

  8. 比特级解析:Exp-Golomb、BitReader 的作用

  9. H.264 SPS 中如何计算宽高(具体公式)

  10. H.265 / HEVC 的基本区别与注意事项

  11. 如何在回调中使用 StreamAnalyzer(实战步骤)

  12. 验证与工具:ffplay / ffprobe 的常用命令

  13. 示例日志与逐行“因为 — 所以”解释

  14. 常见问题与调试建议

  15. 后续进阶(你可以继续做的事)


1. 简介

你在摄像头/设备的实时预览回调里拿到的是压缩后的视频码流,不是 RGB 像素。要理解它,需要把注意力放在比特结构上(NAL 单元、SPS/PPS、slice、IDR 等)。本文档以尽量通俗的语言解释“看到某些字节 → 代表什么 → 我应该做什么”,并配合 StreamAnalyzer.java 输出的真实日志示例帮助你快速上手。


2. 从回调拿到的数据是什么?(总体概念)

  • 回调给你的是一段 byte[](长度 dwDataLen)。

  • 这段数据通常包含若干个 NALU (Network Abstraction Layer Unit)

  • NALU 是 H.264/H.265 的最小编码单位:每个 NALU 里要么是描述流结构的元数据(SPS/PPS/VPS),要么是帧数据(slice、IDR)等。

因为 摄像头对视频进行压缩,所以一帧在网络上可能只有几百字节而不是数 MB。


3. 码流的层级结构(从 bit 到帧)

比特流 (Bitstream)
 └── NALU(以 start code 开始)
      ├── NAL header(1 或 2 字节)
      └── RBSP(去掉 emulation 字节的有效载荷)
             └── 编码语法元素(SPS, PPS, slice 等)

4. Start code(起始码)与如何分割 NALU

常见的 start code:

  • 00 00 01

  • 00 00 00 01

因为 start code 标志 NALU 的开始,所以扫描流并按照这些字节切分就能把流拆为单个 NALU。

注意:有些流(如 MP4 内部)使用长度前缀而不是 start code,这种情况需要不同处理。


5. NAL header(NAL 单元头)详解

H.264(AVC)

  • NAL header 为 1 字节

  • 格式(高位到低位):forbidden_zero_bit (1) | nal_ref_idc (2) | nal_unit_type (5)

nal_unit_type 常见值:

  • 1:non-IDR slice(普通帧)

  • 5:IDR(关键帧)

  • 6:SEI

  • 7:SPS

  • 8:PPS

因为 type 告诉你这是 metadata 还是图像数据,所以会决定后续如何解析(例如遇到 SPS 要 bit 解析)。

H.265(HEVC)

  • NAL header 更复杂(通常 2 字节);类型通过 (firstByte & 0x7E) >> 1 获取。常见:VPS(32)、SPS(33)、PPS(34)、IDR(19/20) 等。


6. SPS / PPS 究竟是什么,为什么要解析它们

  • SPS (Sequence Parameter Set):描述整个视频序列的参数:profile、level、分辨率、色彩、参考帧等。解码器必须先知道 SPS 才能正确解码后续帧。

  • PPS (Picture Parameter Set):描述图片级别的编码参数。

因此:解析 SPS 可以让你得到 widthheightprofile_idclevel_idc —— 这些是判断能否解码或如何分配 buffer 的关键。


7. RBSP 与 emulation-prevention(为什么要去除 0x03)

为了避免 payload 中出现伪 start code,编码器会在 00 00 后插入 0x03

  • 这种 0x03 称作 emulation_prevention_three_byte

  • 在按比特解析(例如读取 Exp-Golomb)前必须把这些 0x03 去掉,得到 RBSP(Raw Byte Sequence Payload)。

因为 若不去除,bit 对齐会错位,导致解析结果错误,所以 removeEmulationPreventionBytes 是解析前的必要步骤。


8. 比特级解析:Exp-Golomb、BitReader 的作用

  • H.264 的许多语法元素使用 Exp-Golomb 编码(UE/SE)表示可变长度整数。

  • 解析 Exp-Golomb 需要按 bit 读取:先读 0 的个数,然后读后续 info bits。

示例流程(UE):

  1. 读取连续的 0 比特,记为 k

  2. 读一个 1 比特(终止位)。

  3. 再读 k 个比特作为信息位 info

  4. 值 = 2^k - 1 + info

因此,BitReader(按位读取)是解析 SPS 的核心工具。


9. H.264 SPS 中如何计算宽高(具体公式)

SPS 里关键字段:

  • pic_width_in_mbs_minus1

  • pic_height_in_map_units_minus1

  • frame_mbs_only_flag

  • frame_cropping_flag 和 crop 数值

计算步骤:

pic_width_in_mbs = pic_width_in_mbs_minus1 + 1
pic_height_in_map_units = pic_height_in_map_units_minus1 + 1
frameHeightInMbs = (2 - frame_mbs_only_flag) * pic_height_in_map_units
width = pic_width_in_mbs * 16
height = frameHeightInMbs * 16
# 若存在 crop,则:
width -= (crop_left + crop_right) * crop_unit_x
height -= (crop_top + crop_bottom) * crop_unit_y

通常摄像头使用 4:2:0 色彩(chroma_format_idc=1),此时 crop_unit_x = 2crop_unit_y = 2 - frame_mbs_only_flag


10. H.265 / HEVC 的基本区别与注意事项

  • HEVC 的 NAL header 与类型判定规则不同,SPS 语法也更复杂(profile_tier_level 结构)。

  • HEVC SPS 解析更长、更细,若你需要同样级别的自动解析(包括 width/height),可以把样本 SPS 发给我,我会为你补齐解析代码。


11. 如何在回调中使用 StreamAnalyzer(实战步骤)

  1. 在回调里把 Pointer/ByteBuffer 的内容拷贝为 byte[]

  2. 调用 StreamAnalyzer.analyze(bytes, true):第二个参数决定是否把每个 NAL 写成文件(便于 ffplay 校验)。

  3. 检查日志输出:SPS/PPS 信息、IDR (keyframe)、以及每个 NAL 的 RBSP hex/bits。


12. 验证与工具:ffplay / ffprobe 的常用命令

  • 播放裸 H.264 文件(包含 SPS/PPS):

ffplay -f h264 nal.bin
  • 使用 ffprobe 查看帧信息:

ffprobe -show_frames -show_entries frame,pkt_pts_time -i nal.bin

若文件是 HEVC,替换 -f h264-f hevc


13. 示例日志与逐行“因为 — 所以”解释

示例(摘录):

Found NALU at pos=0 len=28 (includes startcode 4 bytes). So we'll parse header and RBSP.
  HEX: 00 00 00 01 67 42 00 1F ...
  Heuristic: looks like H.264 (nal_unit_type = 7).
  [H.264] nal_ref_idc=3, nal_unit_type=7 -> SPS
  Because emulation prevention bytes may exist, removed them. rbsp=21 bytes
  SPS parsed: profile_idc=66(Baseline) level_idc=31
  Derived resolution: width=1280 height=720

解释:

  • 因为 hex 中 67 出现,所以它是 SPS(H.264 的标志);

  • 因为是 SPS,所以我们按 bit 解析以获得 profile/level/width/height

  • 因为 width=1280 height=720,所以解码器应分配对应大小的输出缓冲区。


14. 常见问题与调试建议

  • 找不到 start code? 可能是 length-prefixed NAL(如 MP4),用另一种解析方法:读取 4 字节长度,再按长度切片。

  • SPS 解析出错? 可能 RBSP 去 0x03 不完全,或遇到罕见 profile(含 scaling lists),可把 RBSP hex 发来让人帮你解析。

  • HEVC 输出不完整? 提供一段完整包含 VPS+SPS+PPS 的样本数据,我会实现 HEVC SPS 解析。


15. 后续进阶(你可以继续让助手做的事)

  • A:补全 HEVC SPS 解析(提取宽高、profile、chroma 等)。

  • B:把日志改为结构化 JSON(方便上报到 ELK/Prometheus)。

  • C:在 StreamAnalyzer 中统计 GOP(关键帧间隔)、丢帧率估算、帧率统计。

  • D:提供一套脚本批量用 ffprobe 解析 dump 出来的 NAL 文件并生成 CSV 报表。


如果你希望我把这份文档导出为 Markdown 文件(.md)并打包或做成 HTML/打印版,我也可以直接生成并提供下载链接。