StreamAnalyzer — 详细学习与日志解析指南
目标:帮助初学者从 bit/byte 层理解实时视频码流(以 H.264/H.265 为主),并配合
StreamAnalyzer.java将各种信息以“因为 → 所以”的方式清晰记录下来,方便调试与学习。
目录
简介
从回调拿到的数据是什么?(总体概念)
码流的层级结构(从 bit 到帧)
Start code(起始码)与如何分割 NALU
NAL header(NAL 单元头)详解
SPS / PPS 究竟是什么,为什么要解析它们
RBSP 与 emulation-prevention(为什么要去除 0x03)
比特级解析:Exp-Golomb、BitReader 的作用
H.264 SPS 中如何计算宽高(具体公式)
H.265 / HEVC 的基本区别与注意事项
如何在回调中使用
StreamAnalyzer(实战步骤)验证与工具:ffplay / ffprobe 的常用命令
示例日志与逐行“因为 — 所以”解释
常见问题与调试建议
后续进阶(你可以继续做的事)
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 0100 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 可以让你得到 width、height、profile_idc、level_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):
读取连续的 0 比特,记为
k。读一个
1比特(终止位)。再读
k个比特作为信息位info。值 =
2^k - 1 + info。
因此,BitReader(按位读取)是解析 SPS 的核心工具。
9. H.264 SPS 中如何计算宽高(具体公式)
SPS 里关键字段:
pic_width_in_mbs_minus1pic_height_in_map_units_minus1frame_mbs_only_flagframe_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 = 2,crop_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(实战步骤)
在回调里把
Pointer/ByteBuffer的内容拷贝为byte[]。调用
StreamAnalyzer.analyze(bytes, true):第二个参数决定是否把每个 NAL 写成文件(便于 ffplay 校验)。检查日志输出: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/打印版,我也可以直接生成并提供下载链接。