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的传输流程是这样的:

  1. 编码器输出H.264/H.265视频帧和AAC音频帧。
  2. 每个帧被打包成一个FLV Tag。
  3. FLV Tag被分割成RTMP Chunk(默认128字节或64字节)发送。
  4. 接收端收到Chunk后重组为FLV Tag,直接送入解码器。

整个过程没有文件级别的索引构建,没有Box嵌套解析,没有moov等待。每个Tag都是独立的,到了就能解码。

我画了一张图,帮你理解FLV在RTMP中的位置:

FLV在RTMP传输中的角色 FLV在RTMP传输中的角色 编码器 FLV Tag打包 RTMP Chunk分片 网络 Chunk重组 FLV Tag解析 解码器 关键延迟优势 1. 编码器输出帧 → 直接打包为FLV Tag,无索引构建开销 2. FLV Tag → RTMP Chunk:1对N映射,分片粒度可调(默认128字节) 3. 接收端收到Chunk即可重组Tag,无需等待完整GOP 4. 每个Tag独立解码,无moov/box依赖,首帧延迟可控制在500ms内

从图上你能看到,FLV在整个链路里扮演的是“中间人”角色。它不参与编码,也不参与解码,但它提供了一种极轻量的打包方式,让数据能在编码器和传输层之间高效流转。

我个人的经验是,FLV在RTMP中的低延迟优势,核心就三点:

  • 无索引依赖:不需要像MP4那样等moov box,到了就能播。
  • Tag独立性强:每个Tag都是自描述的,丢了一个Tag不影响其他Tag的解析。
  • 分片友好:FLV Tag可以无缝切分成RTMP Chunk,网络适应性极好。

一句话总结:FLV不是最先进的封装格式,但它在低延迟直播场景下,是最务实的选择。简单、轻量、可靠——这三个词在实时系统里比什么都重要。