flv tag header需手动逐字节解析:tagtype(1字节)、datasize(3字节大端)、timestamp(3字节+1字节扩展)、streamid(3字节固定为0),严禁指针强转,须用std::array读取后拼装整数。

FLV Tag Header 的字节解析必须手动处理,没有标准库可依赖
FLV 是二进制封装格式,Tag Header 不是文本或 JSON,不能用 std::getline 或 rapidjson 解析。你得从文件头开始逐字节读取、按规范位移解包。关键字段包括:TagType(1 字节)、DataSize(3 字节大端)、Timestamp(3 字节时间戳低 24 位 + 1 字节扩展)、StreamID(3 字节固定为 0)。常见错误是直接用 uint32_t* 强转指针读取 —— 这在非对齐地址或大小端不一致平台(如 ARM macOS)会触发未定义行为或读错值。
实操建议:
- 用
std::ifstream::read()按字节顺序读入std::array<uint8_t></uint8_t>,再手动拼装整数:例如DataSize = (buf[1] - Timestamp 要组合低 24 位(
buf[4..6])和高 8 位(buf[7]),注意 FLV 时间戳单位是毫秒,但实际值可能溢出,需用uint32_t并处理回绕(比如前后帧差 > 10s 则视为重置) - 跳过
StreamID(恒为 0)但不能跳过——它占 3 字节,少读会导致后续所有 Tag 偏移错乱
Metadata Tag(AMF0 类型)需实现最小 AMF0 解析器,不能只靠字符串匹配
Metadata 总是第一个 TagType == 18 的 Tag,其 Data 区域以 AMF0 编码(不是 JSON)。常见误判是看到 "onMetaData" 就认为是 Metadata —— 实际上该字符串只是 AMF0 中的一个 String 类型字段值,前面还有 type marker(0x02)、长度(2 字节)、内容;后面还跟着真正的对象键值对(0x03 开头的对象标记 + 多组 String + Value + 0x00 0x00 0x09 结束符)。只用 strstr() 扫描会漏掉嵌套结构,也读不到 duration、width 等数值字段。
实操建议:
- 先确认 Tag Header 中
TagType == 18且DataSize >= 13(AMF0 最小对象开销),再进入 AMF0 解析流程 - AMF0 type marker
0x02后紧跟 2 字节 length,然后才是 UTF-8 字符串内容;0x03后是 object,每对 key/value 中 key 是0x02string,value 是任意 type(0x00number、0x01boolean、0x08date 等) - 重点处理
0x00(double,8 字节 IEEE 754)、0x01(boolean,1 字节)、0x08(date,8 字节 double + 2 字节 timezone,常被忽略导致creationDate解错)
音频/视频 Tag 的 Data 区域需按 CodecID 分支解析,不能统一 memcpy
TagType == 8(音频)或 9(视频)时,Data 区域开头是 Codec-specific header,不是裸流。例如 H.264 视频 Tag 的第一个字节是 AvcPacketType(0x00=SPS/PPS,0x01=NALU),紧接着是 CompositionTime(24 位有符号整数,单位毫秒,用于 B 帧 DTS/PTS 计算);AAC 音频则以 AacPacketType(0x00=sequence header,0x01=raw data)开头,之后才是 ADTS 或原始 AAC 帧。直接把整个 Data 区域当媒体帧传给解码器会失败。
实操建议:
- 先读
CodecID(音频:buf[0] & 0x0F;视频:buf[0] & 0x0F),再根据值决定后续解析逻辑:如CodecID == 7→ H.264 → 检查AvcPacketType;CodecID == 10→ H.265 → 注意HevcPacketType定义不同 - H.264 SPS/PPS 提取后需重组为 Annex-B 格式(添加
0x00 0x00 0x00 0x01前缀),否则大多数 FFmpeg AVCodecContext 初始化失败 - AAC sequence header(
AacPacketType == 0)包含AudioSpecificConfig,需提取并 Base64 编码后填入AVCodecParameters::extradata,不能丢弃
文件级 offset 与 Tag size 易被忽略,导致解析中途崩溃
FLV 文件头后是 4 字节的 PreviousTagSize0(恒为 0),之后每个 Tag 结尾都有一个 PreviousTagSize(4 字节大端,等于本 Tag 的 Header+Data 总长度)。很多实现只读 Tag Header 和 Data,却没读最后这 4 字节 —— 下一个 Tag 的起始位置就错了。更隐蔽的问题是:当 Timestamp 跨越 0x00FFFFFF(约 4.6 小时)时,FLV 规范要求写入扩展字节(TagHeader[7]),但某些生成器会漏写,导致后续所有时间戳偏移 1 字节,整个解析链崩坏。
实操建议:
- 每次读完一个 Tag(Header + Data)后,必须用
read()再读 4 字节PreviousTagSize,并与当前 Tag 实际长度校验(误差 > 1 字节即报 warning) - 维护全局
file_offset,每次读操作前检查file_offset + bytes_to_read ,避免 std::ifstream::eof() 滞后导致越界读 - 对
Timestamp做单调性检查:若当前Timestamp比前一个突降 > 30000ms(30 秒),大概率是扩展字节错位,应尝试回退 1 字节重新解析该 Tag Header
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











