FFMPEG——FLV复合流

一、什么是FLV

FLV 全称 Flash Video, 是美国Adobe公司推出来的一种流媒体协议。FLV流媒体格式的特点是封装过后的音视频数据非常小、并且封装的规范相对更加简单,所以FLV流媒体格式非常适合网络传输。但是FLV格式是Adobe公司的私有协议,所以它支持的网络传输协议比较有限:如RTMP、HTTP-FLV。

二、为什么要封装为FLV复合流

  1. RTMP 协议的原生强制要求

这是最根本的原因。RTMP 是直播推流的通用标准协议,其协议规范本身只能承载 FLV 格式的数据流,传输的最小单元是 FLV Tag。 单独的 H.264 视频裸流、AAC 音频裸流无法直接通过 RTMP 发送,流媒体服务器无法识别和解析,必须封装成标准 FLV 复合流,才能完成推流、接收、分发的全链路传输。

  1. 实现音视频精准同步

单独的 H.264 和 AAC 裸流各自拥有独立的时间基准,没有统一的时序参照,直接传输会导致播放器端出现音画不同步的问题。 封装为 FLV 复合流后,音视频帧被打上同一时间轴的时间戳(PTS/DTS),播放器可以根据 FLV Tag 中的时间信息精准对齐画面和声音,从格式层面保证音画同步。

  1. 天然适配直播流式传输

FLV 是典型的流式容器格式:文件头体积极小,数据按帧逐段打包,支持边传输边播放,不需要加载完整文件即可启动解码,端到端延迟极低。 对比 MP4 这类需要依赖完整文件头(moov 盒)才能播放的格式,FLV 天生适合直播、安防监控这类实时性要求高的场景,不会出现首帧加载等待。

  1. 全链路兼容性最强

H.264 + AAC + FLV 是直播与安防行业的事实标准组合:

  • 服务端:所有主流流媒体服务器(SRS、Nginx-RTMP、EasyDarwin 等)都原生支持;
  • 播放端:PC 播放器、网页播放器、手机端、安防监控平台全部兼容;
  • 设备端:绝大多数硬件编码芯片输出的 H.264/AAC 码流都可以直接封装,无需额外处理。 全链路没有格式兼容障碍,落地和排错成本极低。
  1. 封装开销极低,适配嵌入式硬件

FLV 格式逻辑简单,封装计算量极小:每帧数据仅需添加十几字节的 FLV Tag 包头即可完成封装,CPU 占用可以忽略不计,完全适配 RV1126 这类低功耗嵌入式芯片。 同时因为是纯封装、不做二次编码,全程没有画质和音质损耗,也不会增加额外的性能负担。

  1. 携带完整解码元信息

裸 H.264、AAC 码流本身不携带完整的解码配置信息(如 H.264 的 SPS/PPS、AAC 的音频配置头),播放器拿到裸流无法立刻知道分辨率、采样率、编码规格等参数,会出现首帧花屏、启动慢的问题。 封装为 FLV 后,这些解码配置会统一写入 FLV 头部的配置 Tag 中,播放器接入流后立刻就能获取全部解码参数,快速启动正常播放。

三、FLV流媒体格式

FLV 的结构非常简洁,采用「头部 + 逐帧数据包」的流式设计,天生支持边传边播。

FLV流媒体封装格式一般由两个部分组成,一个是FLV Header、另外一个是FLV Body。其中,FLV Header长度固定式9个字节,FLV BODY则是由一组组的Previous Tags Size + Tag组成。Previous Tag Size一般在整个Tag的前面,它一般记录前一个Tag的大小。Tag的类型一般分为三种、分别是脚本数据帧类型、视频数据类型、音频数据类型。

整体结构示意:

复制代码
[ FLV Header (9字节) ]
[ PreviousTagSize0 (4字节) ]
[ FLV Tag1 (Tag头11字节 + Tag数据体) ]
[ PreviousTagSize1 (4字节) ]
[ FLV Tag2 (Tag头11字节 + Tag数据体) ]
[ PreviousTagSize2 (4字节) ]
... 依次循环

其中 PreviousTagSize 占 4 字节,记录前一个 Tag 的总字节数,用于反向定位、断点解析,是 FLV 结构的标志性设计。

2.1. FLV Header

文件头,固定 9 字节,位于流的最开头,标识文件格式与流类型

FLV头部前三个字节是固定FLV的十六进制,Version是版本号固定为1。其他的根据自己的业务配置。

举个例子:包含音视频的 FLV 头,十六进制通常为 46 4C 56 01 05 00 00 00 09

2.2 FLV BODY

FLV Body :文件体,由若干个「前一个 Tag 大小 + Tag 数据包」的单元循环组成,每一个 Tag 对应一帧视频、一帧音频或一段元数据。FLV Tag由11字节的FLV Tag Header和Tag Data组成。如果是视频的TAG DATA,则FLV Tag Header+ Video Tag Data。而如果是音频的TAG DATA,则是FLV Tag Header + Audio Tag Data。但无论是视频的Tag Data还是音频的Tag Data它们的FLV Tag Header 都是相同的,下面我们来看看公共部分的FLV Tag Header

  • 时间戳采用「24 位基础 + 8 位扩展」的设计,最终组成 32 位无符号整数,单位为毫秒,是音视频同步的核心依据。
  • 三种 Tag 类型各司其职:视频 Tag 承载画面码流,音频 Tag 承载声音码流,脚本 Tag 承载流的元信息。

2.3.1 FLV Script Tag

FLV Script Tag也是由FLV Tag Header + Script Data Tag组成。通常来说Script Tag Data是第一个出现的Tag,并且有且只有一个。Script Tag的类型一般被称为MedtaData Tag,它一般会存储一些关于FLV音视频的参数信息,比方说:分辨率(width、height)、duration。

复制代码
FLV文件
├─9字节全局FileHeader
├─4字节PreviousTagSize=0
└─Script Tag(TagType=0x12)
   ├─11字节Tag Header
   └─Tag Body(全是AMF数据)
      ├─AMF包1:字符串 "onMetaData"
      └─AMF包2:存储宽高、时长、帧率的数组

Script Tag是由两个AMF包组合起来(AMF 包 = 数据类型(看下图) + 数据长度 + 数据)。AMF 包就是二进制封装的键值对数据包 ,FLV 里 Script Tag 固定由两个 AMF 包组成,用来存放视频分辨率、时长等全局元数据,替代文本 JSON,更省流量、解析更快。播放器没有解析 AMF 包,就拿不到分辨率、总时长,无法渲染、进度条失效;

AMF1的第一个字节表示包类型、默认0x02。第2-3个字节代表的是字符串的长度,默认0X00A。 而后面的字节是具体的字符串("onMetaData")用十六进制表示:(6f、6e、4d、65、74、61、44、61、74、61)。

AMF数据类型:

而第二个AMF包,第一个字节是0x08表示数组类型。第2-5个字节表示的是数组元素的个数、而后面的数组则是每个数组的键值对。

AMF2数组对应的键值对:

具体的数据分析:

AMF1 的分析:

AMF2 的分析:

2.3.2 FLV VIDEO TAG 的讲解:

FLV VIDEO TAG是由两部分组成,FLV Tag HEADER + VIDEO DATA TAG。具体的我们来看看图解:

从这张图可以看出,VIDEO DATA TAG 是视频的具体信息,这其中包括:STREAMID视频流ID、FrameType视频帧类型(1: avc keyframe指的是关键帧、2: avc inter frame指的是普通帧)、CODECID编码ID(默认7:AVC编码)、AVCPacketType编码包类型(0: avc sequence hdr、1: NALU类型)、CompositionTime构造时间、Data具体的视频数据

2.3.3 FLV AUDIO TAG 的讲解

FLV AUDIO TAG是由两部分组成,FLV Tag HEADER + VIDEO DATA TAG。具体的我们来看看图解:

从这张图可以看出,AUDIO DATA TAG是视频的具体信息,这其中包括:STREAMID音频流ID、SoundFormat音频类型(10: aac)、SoundRate音频采样率、SoundSize音频采样深度、SoundType音频编码类型、AACPacketType AAC包的类型、Data就是具体的音频数据。下面这个是每一个Audio Data Tag的具体定义:

在 RV1126 硬件编码 + FFmpeg 纯封装推流的架构中:

  1. 我们从 VENC 拿到的 SPS/PPS,最终会被 FFmpeg 封装成 AVC 序列头视频 Tag,写入流的开头;
  2. 每帧 H.264 编码数据,会被封装成 AVC NALU 视频 Tag,打上对应时间戳和关键帧标记;
  3. av_interleaved_write_frame 做的核心工作,就是把 AVPacket 按上述 FLV 规范打包成 Tag,写入 AVIOContext 发送出去;
  4. 时间戳会从流的 time_base 单位,转换成 FLV 要求的毫秒单位,填入 Tag 的时间戳字段。