容器格式之FLV介绍
📅 2026/7/7 15:32:45
👁️ 阅读次数
📝 编程学习
FLV(Flash Video)是 Adobe 推出的流媒体封装格式,主要承载 H.263/H.264 视频、MP3/AAC 音频,结构极简,适合网络边下载边播放。整体分为两大块:FLV Header(文件头)+FLV Body(数据体,由一个个 Tag 组成)。
一、FLV文件格式
FLV文件 = [FLV Header] + [PreviousTagSize0(4字节)] + [Tag1] + [PreviousTagSize1(4字节)] + [Tag2] + ...
- Header:固定 9 字节,描述文件版本、是否含音视频
- PreviousTagSize:每个 Tag 后紧跟 4 字节,记录上一个 Tag 总长度(用于反向遍历)
- Tag:真正存储音视频 / 脚本元数据的单元
1.1 文件头
+----------------+----------------+----------------+----------------+ | Signature(3B) | Version(1B) | Flags(1B) | DataOffset(4B) | | "FLV" | 0x01 | 音频/视频标志 | 文件数据偏移 | +----------------+----------------+----------------+----------------+关键字段说明:
| 字段 | 大小 | 说明 |
|---|---|---|
| Signature | 3 字节 | 固定为FLV |
| Version | 1 字节 | FLV 版本,通常为 1 |
| Flags | 1 字节 | bit0: 保留, bit2: 音频标志, bit4: 视频标志 |
| DataOffset | 4 字节 | 文件头大小,通常为 9 |
1.2 文件体结构
文件体由一系列FLV Tag+PreviousTagSize组成:
+-------------+------------------+-------------+------------------+ | PreviousTagSize0 | tag1 | PreviousTagSize1 | tag2 | | (4 字节) | (可变长度) | (4 字节) | (可变长度) | +-------------+------------------+-------------+------------------+每个 Tag 结束后追加 4 字节无符号大端整数,存储前一个 Tag 的完整字节长度。
- 文件第一个 Tag 前固定为
00 00 00 00(无前序 Tag) - 作用:播放器可从文件末尾倒序读取 Tag,快速定位最后一帧
1.2.1 tag
+-----------+-----------+-----------+-----------+-----------+ | Type(1B) | Size(3B) | Time(3B) | TimeExt(1B)| StreamID(3B)| | 标签类型 | 数据大小 | 时间戳 | 时间戳扩展 | 流 ID(0) | +-----------+-----------+-----------+-----------+-----------+ | Tag Data (可变长度) | +-----------------------------------------------------------+| 字段 | 大小 | 说明 | 示例 |
|---|---|---|---|
| Tag Type | 1B | Tag 类型标识 | 0x08=音频,0x09=视频,0x12=元数据 |
| Data Size | 3B | Tag Data 的字节长度(大端序) | 0x00 00 10= 16 字节 |
| Timestamp | 3B | 时间戳低 24 位(毫秒) | 0x00 00 64= 100ms |
| Timestamp Extension | 1B | 时间戳高 8 位 | 与低 24 位组合成完整时间戳 |
| Stream ID | 3B | 流标识,通常为 0 | 0x00 00 00 |
| Tag Data | 可变 | 实际数据内容 | 音频/视频/元数据 |
时间戳计算
// 时间戳由 3 字节低位 + 1 字节高位组成 dts = avio_rb24(s->pb); // 读取低 24 位 dts |= (unsigned)avio_r8(s->pb) << 24; // 组合高 8 位二、tag详解
2.1音频tag结构
音频数据前有一个标志字节:
+---------------------------------------------------+ | CodecID(4bit) | SampleRate(2bit) | Size(1bit) | Channel(1bit) | +---------------------------------------------------+音频编码格式映射:
| Codec ID | 值 | FFmpeg CodecID | 说明 |
|---|---|---|---|
| PCM | 0x00 | PCM_S16BE/PCM_U8 | 无压缩 PCM |
| ADPCM | 0x10 | ADPCM_SWF | ADPCM 压缩 |
| MP3 | 0x20 | MP3 | MPEG-1 Layer 3 |
| PCM_LE | 0x30 | PCM_S16LE/PCM_U8 | 小端 PCM |
| Nellymoser 16kHz | 0x40 | NELLYMOSER | Nellymoser 编码 |
| Nellymoser 8kHz | 0x50 | NELLYMOSER | 8kHz 采样 |
| Nellymoser | 0x60 | NELLYMOSER | 标准 Nellymoser |
| PCM_ALAW | 0x70 | PCM_ALAW | A-law 编码 |
| PCM_MULAW | 0x80 | PCM_MULAW | μ-law 编码 |
| AAC | 0xA0 | AAC | 高级音频编码 |
| Speex | 0xB0 | SPEEX | Speex 编码 |
采样率映射:
| 采样率标志 | 值 | 实际采样率 |
|---|---|---|
| Special | 0x00 | 特殊(Nellymoser 用) |
| 11025 Hz | 0x04 | 11.025 kHz |
| 22050 Hz | 0x08 | 22.05 kHz |
| 44100 Hz | 0x0C | 44.1 kHz |
2.2 视频tag结构
视频数据前有一个标志字节:
+-----------------------------------------+ | Enhanced FLV Flag(1bit)|FrameType(3bit) | CodecID(4bit) | +-----------------------------------------+视频帧类型:
| 帧类型标志 | 值 | 说明 |
|---|---|---|
| Key Frame | 0x10 | 关键帧(I帧),可独立解码 |
| Inter Frame | 0x20 | 预测帧(P帧) |
| Disposable Inter | 0x30 | 可丢弃帧 |
| Generated Key | 0x40 | 生成关键帧(保留) |
| Video Info | 0x50 | 视频信息/命令帧 |
视频编码格式映射:
| Codec ID | 值 | FFmpeg CodecID | 说明 |
|---|---|---|---|
| H.263 | 0x02 | FLV1 | Sorenson H.263 |
| Screen | 0x03 | FLASHSV | Screen Video |
| VP6 | 0x04 | VP6F | On2 VP6 |
| VP6 Alpha | 0x05 | VP6A | VP6 带 Alpha |
| Screen 2 | 0x06 | FLASHSV2 | Screen Video 2 |
| H.264 | 0x07 | H264 | AVC |
| Real H.263 | 0x08 | H263 | 真正的 H.263 |
| MPEG-4 | 0x09 | MPEG4 | MPEG-4 Part 2 |
| HEVC | 0x0C | HEVC | H.265(增强 FLV) |
增强FLV
增强 FLV 扩展了视频编码支持,通过 FourCC 标识:
// 增强 FLV 的编码检测 if (enhanced_flv) { codec_id = avio_rb32(s->pb); // 读取 FourCC switch (codec_id) { case MKBETAG('h', 'v', 'c', '1'): // "hvc1" par->codec_id = AV_CODEC_ID_HEVC; break; case MKBETAG('a', 'v', '0', '1'): // "av01" par->codec_id = AV_CODEC_ID_AV1; break; case MKBETAG('v', 'v', 'c', '1'): // "vvc1" par->codec_id = AV_CODEC_ID_VVC; break; } }2.3 元数据tag
┌─────────────────────────────────────────────────────────────────┐ │ 元数据 Tag Data 结构 │ ├─────────────┬──────────────────┬───────────────────────────────┤ │ Type(1B) │ String Length │ String Value │ AMF Object │ │ (AMF类型) │ (2B) │ (可变) │ (可变) │ └─────────────┴──────────────────┴───────────────────────────────┘2.3.1 元数据类型
typedef enum { AMF_DATA_TYPE_NUMBER = 0x00, // 8字节浮点数 AMF_DATA_TYPE_BOOL = 0x01, // 布尔值 AMF_DATA_TYPE_STRING = 0x02, // 字符串 AMF_DATA_TYPE_OBJECT = 0x03, // 对象 AMF_DATA_TYPE_NULL = 0x05, // 空值 AMF_DATA_TYPE_UNDEFINED = 0x06, // 未定义 AMF_DATA_TYPE_MIXEDARRAY = 0x08, // 混合数组 AMF_DATA_TYPE_ARRAY = 0x0a, // 数组 AMF_DATA_TYPE_DATE = 0x0b, // 日期 } AMFDataType;2.3.2 元数据关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
| duration | Number | 视频时长(秒) |
| width | Number | 视频宽度(像素) |
| height | Number | 视频高度(像素) |
| framerate | Number | 帧率(fps) |
| videodatarate | Number | 视频码率(kbps) |
| audiodatarate | Number | 音频码率(kbps) |
| audiocodecid | Number | 音频编码 ID |
| videocodecid | Number | 视频编码 ID |
| audiosamplerate | Number | 音频采样率 |
| audiosamplesize | Number | 音频位深 |
| stereo | Boolean | 是否立体声 |
| encoder | String | 编码器信息 |
2.3.3 关键帧索引(keyframes)
元数据中包含关键帧索引,用于快速 Seek:
// keyframes 对象结构 { "keyframes": { "times": [0, 1000, 2000, ...], // 关键帧时间戳数组 "filepositions": [100, 5000, 12000, ...] // 关键帧文件偏移数组 } }2.3.4 tag比较
| ag 类型 | 标识 | 主要内容 | 特殊处理 |
|---|---|---|---|
| 音频 | 0x08 | 音频采样数据 | 解析编码格式、采样率、声道 |
| 视频 | 0x09 | 视频帧数据 | 解析帧类型、编码格式、CTS |
| 元数据 | 0x12 | AMF 格式元信息 | 解析视频参数、关键帧索引 |
编程学习
技术分享
实战经验