FLV文件解析

FLV(Flash Video)文件格式本质上是一个包含 Header(文件头)Body(文件体) 的二进制文件。

与 RTMP 流不同,RTMP 是 Chunk 流式传输,而 FLV 是为了存储将这些 Chunk 按顺序"铺平"并加上文件头和索引信息,存放在硬盘上。

FLV 文件由三大部分组成:

  1. FLV Header(文件头)
  2. FLV Body(文件体,由无数个 Tag 组成)
  3. Previous Tag Size(前置 Tag 大小,位于每个 Tag 之前,用于回读)

1. FLV Header(文件头)

FLV 文件的开头是固定的 9 字节(或扩展后的 13 字节)。

  • Signature (3 bytes) : 固定为 FLV (0x46 0x4C 0x56)。
  • Version (1 byte) : 通常为 1 (表示 FLV 版本 1)。
  • Flags (1 byte) : 标志位,说明文件里有什么。
    • 第 5 位 (0x04): 是否有 Audio
    • 第 6 位 (0x01): 是否有 Video
  • Header Length (4 bytes) : 整个 Header 的长度,通常为 9 (0x00 00 00 09)。

2. FLV Body(文件体)与 Tag 结构

Header 之后,就是连续的 Tag 序列。每一个 Tag 包含一段音频、一段视频或一段脚本数据。

通用 Tag 结构:

  1. Previous Tag Size (4 bytes) : 记录前一个 Tag 的大小。注意:第一个 Tag 前面的这个值全为 0。
  2. Tag Header (11 bytes): 描述这个 Tag 的类型、时间戳和大小。
  3. Tag Data (变长): 真正的音视频数据。
Tag Header 详细解析 (11 字节)
  • TagType (1 byte) :
    • 8: Audio
    • 9: Video
    • 18: Script Data (元数据,如时长、分辨率、宽度、高度等)
  • DataSize (3 bytes): Tag Data 的长度。
  • Timestamp (3 bytes): 时间戳,单位毫秒。
  • TimestampExtended (1 byte): 时间戳扩展,凑成 4 字节整数。当 Timestamp 超过 0xFFFFFF 时使用。
  • StreamID (3 bytes) : 总是 0

3. 结合音视频包的详细数据示例

假设我们截取了一个 FLV 文件的中间一段,这段包含了一个 AAC 音频包 和一个 H.264 关键帧

场景一:AAC 音频 Tag

假设我们抓取到一个音频 Tag 的数据如下:

Hex 数据 含义 详细说明
00 00 00 12 Previous Tag Size 前一个 Tag 大小为 18 字节。
08 Tag Type 0x08 = Audio。
00 00 05 Data Size 数据长度为 5 字节。
00 00 40 Timestamp 时间戳 = 64 ms。
00 Timestamp Extended 扩展时间 = 0。总时间 64ms。
00 00 00 Stream ID 流 ID = 0。
Tag Data 开始
AF Sound Format 参考前文 RTMP 说明:AAC, 44kHz, Stereo, 16-bit。
00 AAC Packet Type 0x00 = AudioSpecificConfig (解码器配置)。
11 90 Data AAC 配置数据的开始(Sampling Index 等)。
56 E5 Data 配置数据的其余部分。

关键点 :如果是 Raw Data(真实歌声),第二个字节会是 01,后面跟的都是压缩后的音频流。


场景二:H.264 视频关键帧

视频 Tag 通常比音频大得多。这是一个 IDR 关键帧的示例:

Hex 数据 含义 详细说明
00 01 23 45 Previous Tag Size 前一个 Tag 大小很大(约 74k 字节,视频数据量大)。
09 Tag Type 0x09 = Video。
00 10 00 Data Size 数据长度为 4096 字节(假设)。
00 04 00 Timestamp 时间戳 = 1024 ms。
00 Timestamp Extended 扩展时间 = 0。总时间 1024ms。
00 00 00 Stream ID 流 ID = 0。
Tag Data 开始
17 Frame & CodecID 关键帧 (1) + AVC (7)。
01 AVCPacketType 0x01 = NALU (这是真正的画面数据)。
00 00 1D CompositionTime CTS = 29ms (DTS与PTS的差值)。
00 00 01 65 NALU Unit 1 [长度4字节] (0x01) + [类型65] (IDR Slice)。
...Raw Data... 视频数据 第一帧画面数据。
00 00 01 67 NALU Unit 2 [长度4字节] (0x01) + [类型67] (SPS)。
...SPS Data... 视频数据 序列参数集(某些流会周期性插入)。

关键点解析:

  1. SPS/PPS 位置
    • 在 FLV 中,SPS/PPS 通常存放在第一个视频 Tag 中(PacketType=0)。
    • 上面的例子展示了 PacketType=1(实际画面),但在某些推流配置(如 AnnexB 模式)下,SPS/PPS 也可能作为 Unit 跟随在 IDR 帧后面发送。标准的 FLV 容器格式倾向于将 SPS/PPS 放在 AVCDecoderConfigurationRecord (PacketType=0) 中,而不是每个 I 帧里都重复发。
  2. Composition Time
    • 这对于正确播放视频至关重要。因为 H.264 编码后的 DTS(解码时间戳)和 PTS(显示时间戳)通常不一致。
    • CompositionTime = PTS - DTS

4. Script Tag (元数据)

FLV 文件的第一个 Tag 通常是一个 Script Tag(onMetaData),它告诉播放器这个视频的基本信息。

  • Tag Type : 18
  • Data Format : AMF (Action Message Format)。
    • AMF 0: String "onMetaData"
    • AMF 3: ECMA Array (包含键值对)

典型数据结构:

复制代码
String: "onMetaData"
Object: {
    "duration": 120.5,    // 总时长
    "width": 1920,        // 宽度
    "height": 1080,       // 高度
    "videodatarate": 2500,// 视频码率
    "framerate": 30,      // 帧率
    "audiodatarate": 128, // 音频码率
    "videosamplerate": 44100 // 音频采样率
}

5. 总结:FLV 文件的物理布局图

复制代码
[ FLV Header (9 bytes) ]
      |
      +--- Previous Tag Size (4 bytes, value = 0)
      |
      +--- [ Tag 1: Script Data (onMetaData) ]
      |      \__ PrevTagSize (4 bytes)
      |
      +--- [ Tag 2: Audio Header (AAC Config) ]
      |      \__ PrevTagSize (4 bytes)
      |
      +--- [ Tag 3: Video Header (SPS/PPS) ]
      |      \__ PrevTagSize (4 bytes)
      |
      +--- [ Tag 4: Audio Frame (Raw Data) ] (Timestamp: 30ms)
      |      \__ PrevTagSize (4 bytes)
      |
      +--- [ Tag 5: Video Frame (I-Frame) ] (Timestamp: 0ms, Low Latency) 
      |      \__ PrevTagSize (4 bytes)
      |
      +--- [ Tag 6: Audio Frame ... ]
      |
      ...

核心区别总结:

  • RTMP : 发送的是 Chunk
  • FLV : 保存的是 Tag
  • 实际上,RTMP 的 Chunk Header 去掉后,剩下的 Body 拼起来就是完整的 FLV Tag Payload。这也是为什么录屏软件录制 RTMP 直播流非常高效,只需要把收到的流按顺序写进文件,加上 File Header 即可。

====================================================================

我提取了一段flv文件十六进制数据如下:

复制代码
00000000  46 4c 56 01 05 00 00 00  09 00 00 00 00 12 00 03  |FLV.............|
00000010  11 00 00 00 00 00 00 00  02 00 0a 6f 6e 4d 65 74  |...........onMet|
00000020  61 44 61 74 61 08 00 00  00 15 00 08 64 75 72 61  |aData.......dura|
00000030  74 69 6f 6e 00 40 4e 07  2b 02 0c 49 ba 00 05 77  |tion.@N.+..I...w|
00000040  69 64 74 68 00 40 84 00  00 00 00 00 00 00 06 68  |idth.@.........h|
00000050  65 69 67 68 74 00 40 7e  00 00 00 00 00 00 00 0d  |eight.@~........|
00000060  76 69 64 65 6f 64 61 74  61 72 61 74 65 00 40 7e  |videodatarate.@~|
00000070  84 80 00 00 00 00 00 09  66 72 61 6d 65 72 61 74  |........framerat|
00000080  65 00 40 3e 00 00 00 00  00 00 00 0c 76 69 64 65  |e.@>........vide|
00000090  6f 63 6f 64 65 63 69 64  00 40 1c 00 00 00 00 00  |ocodecid.@......|
000000a0  00 00 0d 61 75 64 69 6f  64 61 74 61 72 61 74 65  |...audiodatarate|
000000b0  00 40 4f 40 00 00 00 00  00 00 0f 61 75 64 69 6f  |.@O@.......audio|
000000c0  73 61 6d 70 6c 65 72 61  74 65 00 40 e5 88 80 00  |samplerate.@....|
000000d0  00 00 00 00 0f 61 75 64  69 6f 73 61 6d 70 6c 65  |.....audiosample|
000000e0  73 69 7a 65 00 40 30 00  00 00 00 00 00 00 06 73  |size.@0........s|
000000f0  74 65 72 65 6f 01 01 00  0c 61 75 64 69 6f 63 6f  |tereo....audioco|
00000100  64 65 63 69 64 00 40 24  00 00 00 00 00 00 00 0b  |decid.@$........|
00000110  6d 61 6a 6f 72 5f 62 72  61 6e 64 02 00 04 69 73  |major_brand...is|

FLV 格式总览

FLV 文件结构 = Header + PreviousTagSize0 + Tags × N

每个 Tag 结构 = Tag Header + Tag Data + PreviousTagSize


第一部分:FLV 文件头 (0x00-0x08)

复制代码
00000000  46 4c 56 01 05 00 00 00  09
|--------| |-------| |----| |-------|
  签名    版本 标志位  头部长度
偏移 Hex 字段 二进制/ASCII 说明
0x00 46 4c 56 签名 "FLV" 固定标识符
0x03 01 版本 1 FLV 版本 1
0x04 05 标志位 00000101 第0位保留(0)<br>第1位保留(0)<br>第2位有音频(1)<br>第3位有视频(1)<br>第4-7位保留(0)
0x05-0x08 00 00 00 09 头部长度 9 固定为9字节

第二部分:第一个 PreviousTagSize (0x09-0x0C)

复制代码
00000000                      09 00 00 00 00
                               |------------|
                                PreviousTagSize0
偏移 Hex 字段 说明
0x09-0x0C 00 00 00 00 PreviousTagSize0 0 第一个Tag前一定是0

第三部分:第一个 Tag - Script Tag

Tag Header (0x0D-0x17) - 共11字节

复制代码
00000000                                     12 00 03 11 00 00 00 00 00 00 00
                                              |------| |----------| |-------|
                                               长度     时间戳       StreamID
偏移 Hex 字段 说明
0x0D 12 TagType 0x12 Script Data (脚本数据)
0x0E-0x10 00 03 11 DataSize 0x000311 = 785 Tag数据体长度
0x11-0x14 00 00 00 00 Timestamp 0 时间戳0,单位ms
0x15-0x17 00 00 00 StreamID 0 总是0

Tag Data - onMetaData 脚本数据

从 0x18 开始是 AMF 数据:

1. AMF String "onMetaData" (0x18-0x25)
复制代码
00000010                        02 00 0a 6f 6e 4d 65 74 61 44 61 74 61
                         |---| |-----| |----------------------------|
                        String  长度(10)    "onMetaData"(10字符)
偏移 Hex AMF类型 说明
0x18 02 String 字符串类型 AMF String 类型标识
0x19-0x1A 00 0a Length 10 字符串长度10字节
0x1B-0x25 6f 6e... Value "onMetaData" 元数据标识符
2. ECMA Array 开始 (0x26-0x29)
复制代码
00000020                                    08 00 00 00 15
                                        |--| |----------|
                                    ECMA数组    数组元素数(21)
偏移 Hex AMF类型 说明
0x26 08 ECMA Array 混合数组 开始键值对数组
0x27-0x2A 00 00 00 15 Count 21 包含21个元素

3. 键值对示例 - duration

让我们详细解析第一个键值对:

复制代码
00000020                          00 08 64 75 72 61 74 69 6f 6e 00 40 4e 07 2b 02 0c 49 ba
                        |-----| |----------------------------| |----------------------|
                        键长度(8)      键名"duration"         Number值(60.503秒)
结构 字节 解释
键名长度 00 08 8字节长的键名
键名 64 75 72 61 74 69 6f 6e ASCII "duration"
值类型 00 AMF Number 类型
40 4e 07 2b 02 0c 49 ba Double 浮点数 (60.503秒)

4. 其他键值对(快速识别)
复制代码
键值对流程:
[键长度][键名][值类型][值]

从你的数据中可以看到清晰的模式:

  1. duration (偏移 0x30-0x3F)

  2. width (偏移 0x40-0x4F) - 1920像素

    复制代码
    00 05 77 69 64 74 68 00 40 84 00 00 00 00 00 00
    ^^ ^^ ^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^
    长5  "width"     Number  1920.0
  3. height (偏移 0x50-0x5F) - 1080像素

  4. videodatarate (偏移 0x60-0x6F) - 视频码率

  5. framerate (偏移 0x70-0x7F) - 帧率(30fps)

  6. videocodecid (偏移 0x80-0x8F) - 视频编码ID(7=H.264)

  7. audiodatarate (偏移 0x90-0x9F) - 音频码率

  8. audiosamplerate (偏移 0xA0-0xAF) - 采样率(44100Hz)

  9. audiosamplesize (偏移 0xB0-0xBF) - 采样大小(16bit)

  10. stereo (偏移 0xC0-0xC3) - 立体声(true=1)

    复制代码
    00 06 73 74 65 72 65 6f 01 01
                    ^^ ^^
                 Boolean true(1)
  11. audiocodecid (偏移 0xC4-0xD3) - 音频编码ID(10=AAC)

  12. major_brand (偏移 0xD4-...) - 字符串"is"


可视化对应关系

复制代码
FLV 文件结构图示:
╔══════════════════════════════════════════════════════════╗
║ FLV 头 (9字节)                                           ║
║ 46 4c 56 01 05 00 00 00 09                               ║
╠══════════════════════════════════════════════════════════╣
║ PreviousTagSize0 (4字节)                                 ║
║ 00 00 00 00                                              ║
╠══════════════════════════════════════════════════════════╣
║ Tag1 Header (11字节)                                     ║
║ 12 00 03 11 00 00 00 00 00 00 00                         ║
║  │   └───┬───┘ └─────┬─────┘ └─┬─┘                      ║
║  │   数据长度785     时间戳0   流ID0                     ║
║ Script Tag                                             ║
╠══════════════════════════════════════════════════════════╣
║ Tag1 Data (785字节)                                      ║
║ 02 00 0a 6f 6e 4d 65 74 61 44 61 74 61 08 00 00 00 15...║
║  │  │    └─────┬─────┘ │      └─────┬─────┘             ║
║  │  长度10    "onMetaData" ECMA数组  21个键值对           ║
║ String类型                                              ║
╚══════════════════════════════════════════════════════════╝

总结要点

  1. FLV头固定以"FLV"开头
  2. PreviousTagSize总是跟在每个Tag后面(第一个是0)
  3. Tag Header固定11字节,包含类型、长度、时间戳
  4. Script Tag使用AMF格式,包含字符串、数组、键值对
  5. 每个键值对 = [长度][键名][类型][值]

这就是为什么你能看到 00 0a 表示长度10,然后跟着10个ASCII字符 "onMetaData",接着是 08 表示数组开始,00 00 00 15 表示21个元素。

这个Script Tag之后,文件会继续跟着Video Tag和Audio Tag,每个都有类似的Tag Header结构,但Data部分包含的是编码后的音视频数据。

相关推荐
byte轻骑兵3 小时前
【AVRCP】规范精讲[10]:链路管理器LM互操作规则与场景落地
人工智能·音视频·蓝牙·avrcp·音视频控制
JK Chen4 小时前
faster_whisper,视频转文字,并生成字幕文件
python·whisper·音视频
Prannt1 天前
星朗智能语音——语音合成——上传文件配音
ai·音视频·语音识别
byte轻骑兵1 天前
【AVRCP】规范精讲[7]: 打通AVCTP互操作底层,吃透事务标签与分片规则
人工智能·音视频·avrcp·音视频控制
EasyGBS1 天前
国标GB28181视频平台EasyGBS即将重磅新增WHIP推流功能!低延迟直播体验再升级
音视频
jiejiejiejie_1 天前
Flutter for OpenHarmony 萌系实战合集:地图功能 + 音频播放一站式指南
flutter·音视频
jbk33111 天前
10分钟翻译一条视频,实现语音、字幕翻译后与画面同步对齐,视频翻译助手使用教程
人工智能·音视频·剪辑软件·剪映自动化软件
带娃的IT创业者1 天前
DaVinci Resolve – Photo:当视频调色之王,跨界“修图”,意味着什么?
图像处理·音视频·工作流·davinci resolve·后期制作·视频调色·色彩管理
nashane2 天前
HarmonyOS Video组件预览图片优化实践:告别黑屏,提升视频播放体验
华为·音视频·harmonyos·harmonyos 5