FLV 文件格式解析 -- 速通版

前排提示:之所以叫 “速通版”,前提是对有一定音视频基础知识的人群,可参考本文快速了解一些重点信息和内容。

目录


一、整体结构

FLV (即 Flash Video 的缩写) ,源于 Adobe 公司定义的一种流媒体的封装格式。

整体上看,由 2 部分组成:

FLV = FLV Header + FLV Body

FLV 文件的数据是以大端字节序存储的。

注意,通常大多数的媒体文件都是 小端字节序存储。

而网络传输的数据通常使用 大端字节序)。

⼀个标准 FLV 文件结构,如下图所示:

可以看到整体结构比较简单 ——

  • FLV Header 是固定长度为 9 Byte 的数据,
  • 而 FLV Body 是由一对接一对的 [ PreviousTagSize ] + [ Tag ] 组成。
     

二、FLV Header 部分

大小占 9 Byte,共有 4 个结构体成员,定义如下:

取值类型名称占位大小说明
char [3]Signature3 ByteFLV 文件标识(固定值 “F” “L” “V” )
unsigned charVersion1 Byte版本 (目前值都是 1)
unsigned char

TypeFlags

1 Byte

表示是否存在音频和视频

第 0 ~ 4 bit:保留位,固定值 0

第 5 bit:是否存在音频

第 6 bit:保留位,固定值 0

第 7 bit:是否存在视频

unsigned intDataOffset4 Byte

表示 FLV Header 本身的长度,单位 Byte

( 目前版本1 规定的值是 9 )

不得不感慨,FLV Header 的设计真的是非常简洁、优雅。
 


三、FLV Body 部分

前面已经介绍过了,FLV Body 是由一对接一对的 [ PreviousTagSize ] + [ Tag ] 组成。

  • Previous Tag Size 字段

排列在 Tag之前,记录 前一个Tag 的大小 (用于逆向读取处理),长度占 4 个字节。显然,FLV Header 后的第⼀个 Pervious Tag Size 的值必定为 0。

  • FLV Tag 字段

FLV Tag 结构上,分为 2 部分:

FLV Tag = [ Tag Header ] + [ Tag Data ]

类型上,分为 3 种:

  1. 脚本(帧)数据 - Script Tag Data

    也被称为 MetaDataTag,存放⼀些关于FLV视频和音频的元信息,比如:duration、width、height等。 通常作为 FLV 文件的第一个 tag,并且只有一个,跟在 File Header 后面。

  2. 音频数据 - Audio Tag Data
    第一个字节包含了 Audio 数据的参数信息, 第二个字节开始为 Audio 数据(RawData or Configration)。
    注意,这两个字节属于 tag 的 data 部分,不是 header 部分。
     
  3. 视频数据 - Video Tag Data
    第一个字节包含了 Video 数据的参数信息, 第二个字节开始为 Video 数据。

注意,⼀个 FLV 文件,每种类型的 Tag 都属于⼀个流,也就说是⼀个 FLV 文件最多只有⼀个音频流和⼀个视频流,不存在多个独立的音视频流封装在在同⼀个 FLV 文件的情况。

FLV Tag Header 结构体如下:

关于 FLV 的时间戳计算:

  • FLV 文件中的 Timestamp 和 TimestampExtended 拼出来的是 DTS -解码时间,单位为 ms。(如果没有 B 帧,当然 DTS 等于 PTS 显示时间)
  • CompositionTime 表示 PTS 相对于 DTS 的偏移值, 在每个视频 tag 的第 14~16 字节,单位也是 ms。 PTS = DTS (tag 的第 5~8 字节) + CompositionTime
     

PS:分享

一个 系统深入学习音视频 知识的课程 —— https://xxetb.xetslk.com/s/45jaSl


(学无止境,文章如有纰漏不足,欢迎您不吝指正!谢谢 ^_^)

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Monibuca v4可以使用Go语言编写插件来实现rtsp协议转为flv文件并使用websocket-flv推送给前端进行播放。 以下是一个可能的实现: ```go package main import ( "fmt" "io" "os/exec" "strings" "sync" "github.com/ahmed-aly/go-ffmpeg" "github.com/Monibuca/engine" "github.com/Monibuca/plugin" ) type RTSP2FLV struct { plugin.Plugin } var r *RTSP2FLV var once sync.Once func (rtsp2flv *RTSP2FLV) Handler(channelID string, args ...interface{}) { rtspUrl := args[0].(string) // Make sure the plugin will only be initialized once once.Do(func() { r = &RTSP2FLV{} r.SetName("RTSP2FLV") plugin.Register(r) }) // Start the RTSP to FLV conversion process cmd := exec.Command("ffmpeg", "-i", rtspUrl, "-c:v", "copy", "-c:a", "aac", "-f", "flv", "-") stdout, err := cmd.StdoutPipe() if err != nil { fmt.Println(err) return } cmd.Start() // Send the FLV data to the Monibuca engine engine.Flowers().ForEach(func(key string, val *engine.Flow) bool { if strings.Contains(key, "websocket-flv") { val.Writer.Write([]byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00}) go io.Copy(val.Writer, stdout) } return true }) cmd.Wait() } func main() {} ``` 这个插件实现了一个 `RTSP2FLV` 结构体,其中 `Handler` 函数接收一个 RTSP URL 作为参数,然后使用 ffmpeg 将 RTSP 转换为 FLV 格式并通过 websocket-flv 推送给前端进行播放。 在处理函数中,我们首先实例化了一个 `RTSP2FLV` 结构体,然后启动了 `ffmpeg` 进程来将 RTSP 流转换为 FLV 格式。接下来,我们通过 `engine.Flowers()` 来获取所有的流,并查找包含 `websocket-flv` 的流,然后将 FLV 数据推送到前端进行播放。 最后,我们需要在 `main` 函数中留一个空函数,这是为了避免编译时出现错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值