3、FLV格式深度解析:FLV的Tag结构、Script Tag与Metadata、FLV的头部开销、FLV在RTMP中的低延迟优势
FLV这个格式,说实话,年纪比不少在座的工程师都大。但你别看它老,在直播领域,尤其是RTMP推流这块,它依然是绝对的主力。我刚开始做流媒体那会儿,第一个接触的封装就是FLV。当时觉得这格式太简单了,后来踩了坑才发现,简单背后全是细节。
今天我们就把它彻底拆开看看。为什么FLV能成为低延迟直播的标配?它的结构到底有多轻量?Metadata又该怎么写才不会坑人?
3.1 FLV的Tag结构:三个核心成员
FLV文件说白了就是一个文件头加上一串连续的Tag。每个Tag都是一个独立的数据包。我习惯把Tag想象成快递包裹——每个包裹都有明确的标签,告诉你里面装的是视频、音频还是脚本。
一个标准的FLV Tag由三部分组成:
- Tag Header(11字节):固定长度,包含Tag类型、数据大小、时间戳、StreamID。
- Tag Data(可变长度):真正的音视频数据或脚本数据。
- Previous Tag Size(4字节):前一个Tag的总大小,用于文件随机访问。
Tag类型就三种,用1个字节标识:
| Tag类型值 | 类型名称 | 说明 |
|---|---|---|
| 0x08 | Audio Tag | 音频数据包 |
| 0x09 | Video Tag | 视频数据包 |
| 0x12 | Script Tag | 脚本/元数据包 |
嗯,这里要注意:时间戳是3字节的,最大只能表示约4.66小时。超过怎么办?FLV在扩展时间戳字段里补了高位。我在项目中遇到过直播推流超过5小时,结果播放器时间轴乱跳的情况——就是扩展时间戳没处理好。
核心结论:FLV的Tag结构极其简单,解析开销极低。每个Tag头部只有11字节,加上4字节的Previous Tag Size,总开销仅15字节。相比MP4的Box嵌套,这简直是清流。
3.2 Script Tag与Metadata:藏坑最多的地方
Script Tag,类型值0x12,是FLV里最特殊的一个Tag。它不传音视频数据,而是传AMF(Action Message Format)编码的脚本数据。说白了,就是用来放Metadata的。
Metadata里通常包含什么?
- onMetaData:一个固定的回调函数名,播放器读到这个就知道后面是元数据了。
- duration:时长(秒)
- width / height:视频宽高
- videodatarate / audiodatarate:码率
- framerate:帧率
- videocodecid / audiocodecid:编码器ID
我见过最离谱的一次,是某个推流端把duration写成了0。结果播放器一读到duration=0,直接认为文件是空的,黑屏不播。你想想看,一个Metadata字段就能让整个直播挂掉。
避坑指南:我曾经在对接某厂商的编码器时,发现它的Script Tag里塞了一个自定义字段"encoder=xxx",但AMF编码格式写错了,导致后续所有Tag解析失败。后来我强制在服务端过滤掉非标准字段,才解决问题。建议你在服务端做一层Metadata校验,别完全信任推流端。
Script Tag还有一个关键作用:音视频参数协商。在RTMP握手完成后,客户端和服务端会通过Script Tag交换能力信息。比如客户端告诉服务端“我支持AAC和高帧率”,服务端根据这个调整编码参数。
3.3 FLV的头部开销:为什么它天生低延迟
我们来算一笔账。一个典型的FLV文件,头部开销是多少?
FLV Header: 9字节
- Signature: 3字节 ("FLV")
- Version: 1字节 (0x01)
- Flags: 1字节 (音频/视频标记)
- HeaderSize: 4字节 (固定为9)
每个Tag开销: 15字节
- Tag Header: 11字节
- Previous Tag Size: 4字节
假设一个GOP为2秒,帧率30fps,音频采样率44.1kHz:
- 视频Tag数: 60个
- 音频Tag数: ~86个 (每23ms一个)
- 总Tag开销: (60+86) * 15 = 2190字节 ≈ 2.1KB
- 总头部开销: 9 + 2.1KB ≈ 2.1KB
对比一下MP4:一个MP4文件光ftyp+moov两个Box就轻松超过2KB,而且moov通常放在文件末尾,必须下载完整个文件才能开始播放。FLV呢?头部9字节,Tag顺序排列,边下载边播放,延迟天然低。
个人经验:我做过一个对比测试,同样一段10秒的视频,FLV从接收到首帧解码的时间比MP4快了约300ms。这300ms在直播场景里就是能不能做到1秒内开播的关键。
3.4 FLV在RTMP中的低延迟优势
RTMP选择FLV作为传输格式,不是偶然的。说白了,RTMP本身就是一个基于块的协议,而FLV的Tag结构正好和RTMP的块结构完美匹配。
RTMP的传输流程是这样的:
- 编码器输出H.264/H.265视频帧和AAC音频帧。
- 每个帧被打包成一个FLV Tag。
- FLV Tag被分割成RTMP Chunk(默认128字节或64字节)发送。
- 接收端收到Chunk后重组为FLV Tag,直接送入解码器。
整个过程没有文件级别的索引构建,没有Box嵌套解析,没有moov等待。每个Tag都是独立的,到了就能解码。
我画了一张图,帮你理解FLV在RTMP中的位置:
从图上你能看到,FLV在整个链路里扮演的是“中间人”角色。它不参与编码,也不参与解码,但它提供了一种极轻量的打包方式,让数据能在编码器和传输层之间高效流转。
我个人的经验是,FLV在RTMP中的低延迟优势,核心就三点:
- 无索引依赖:不需要像MP4那样等moov box,到了就能播。
- Tag独立性强:每个Tag都是自描述的,丢了一个Tag不影响其他Tag的解析。
- 分片友好:FLV Tag可以无缝切分成RTMP Chunk,网络适应性极好。
一句话总结:FLV不是最先进的封装格式,但它在低延迟直播场景下,是最务实的选择。简单、轻量、可靠——这三个词在实时系统里比什么都重要。