【Go】FLV文件解析(一)
这是一个系列教程,一是为了解释FLV文件的结构,二是为了练习Go语言,希望大家多多支持。
在实战编码之前,我们需要首先了解FLV文件的格式。FLV是adobe出品的视频封装格式,注意它只是封装格式,不是编码格式。做为第一节的内容,我们不会过度深入音视频数据的编码,这部分内容以后会再讲。
FLV文件格式
FLV文件由FLV Header和FLV Body组成,FLV Body又由许多Tag组成,Tag里面可能是视频、音频或脚本。这里所说的脚本并不是可执行脚本,而是视频的一些元信息。在每一个Tag的前面还有一个重要信息,就是前一个Tag的大小。它们看起来如下图。
为什么Tag之间会记录上一个Tag大小呢?为的是方便回溯,有了前一个Tag大小,我们就可以轻松回溯到前一个Tag了。
FLV Header
1版本的FLV Header共9个字节,目前应该都是这个版本。FLV Header结构如下图。
FLV头的前三个字节分别是FLV三个字母的ASIIC码,可以由此来判断一个文件是否是FLV文件。紧接着的一个字节是FLV文件版本,下一个字节是是一个flag标志,其中第8个比特表示是否包含视频,第6个比特表示是否包含音频,其余比特必须是0,最后是FLV头的大小,占4个字节。
FLV Tag
FLV的Tag也由Tag Header和Tag Data组成,Tag Header共计11字节,结构如下图。
FLV Tag有三种类型,它们有相同的Tag Header,不同的是Tag Data中的数据类型。
8:音频Tag,Tag Data中是音频数据。
9:视频Tag,Tag Data中是视频数据。
18:元数据Tag,Tag Data中是视频元信息。
Tag Data大小占3字节,表示Tag Data的长度,不包括Tag Header在内。
关于时间戳,我们要搞清楚以下3个问题:
时间戳的单位是是什么?
时间戳的含义是什么?
时间戳如何计算?
第一个问题,根据官方文档的描述,时间戳的单位是毫秒。
第二个问题,时间戳表示的是相对于第一个Tag时间戳的偏移量,因此第一个Tag的时间戳永远是0。
第三个问题,时间戳在这里被分成了两部分,一个3字节的时间戳和1字节的扩展时间戳,完整的时间戳应该是一个32位无符号整数,计算的时候,扩展时间戳是高8位,时间戳是低24位,在拼时间戳的时候要注意这一点。
流ID实际上源于RTMP协议的多路复用,因为FLV只支持一路流,因此流ID总是0。
基本的FLV结构就介绍到这里,下面开始代码实操。
实战
首先我们需要一个工具提供基础的解码功能,你可以通过导入"gitee.com/lJOSVDE/stream"
这个库来使用这个工具。另外我们使用的系统是小端序。
新建一个Go项目以及flv.go
文件,首先我们定义两个全局变量。
var (_FLV_ = [3]byte{'F', 'L', 'V'}FLV_FMT_ERROR = errors.New("flv format error")
)
接下来定义FLV Header和FLV Tag的结构。
// flv头部
type FlvHeader struct {flv [3]byteVersion uint8HasAudio boolHasVideo boolSize uint32
}// flv tag
type FlvTag struct {Header FlvTagHeaderData stream.Stream
}// flv tag头部
type FlvTagHeader struct {TagType uint8DataSize uint32Timestamp uint32StreamID uint32PreTagSzie uint32
}
这里我们将FlvTag
的Data
字段也定义为Stream
类型,方便之后进一步的解析。然后需要准备一个FLV文件,然后写一个读取文件的函数。
func ReadFlv(path string) (Stream, error) {if name == "" {return nil, errors.New("empty flv name")}bs, err := ioutil.ReadFile(name)if err != nil {return nil, err}return stream.NewByteStream(bs), nil
}
下面来读取FLV头部。
// 读取FLV Header
func DecodeFlvHeader(s stream.Stream) (h FlvHeader, err error) {// 读取FLVerr = s.Byte(&h.flv[0]).Byte(&h.flv[1]).Byte(&h.flv[2]).Error()if h.flv != _FLV_ || err != nil {err = FLV_FMT_ERRORreturn}var flag byte// 读取FLV版本、标识符、头部大小err = s.U8(&h.Version).Byte(&flag).U32(&h.Size).Error()h.HasAudio = flag&0x04 == 0x04h.HasVideo = flag&0x01 == 0x01return
}
接下来读取FLV Tag。
// 读取一个FLV Tag
func DecodeFlvTag(s stream.Stream) (t FlvTag, err error) {var externTimestamp uint8err = s.U32(&t.Header.PreTagSzie). //前一个Tag大小U8(&t.Header.TagType). //Tag类型U24(&t.Header.DataSize). //Tag Data大小U24(&t.Header.Timestamp). //时间戳U8(&externTimestamp). //扩展时间戳U24(&t.Header.StreamID). //流IDError()if err != nil {return}//拼完整时间戳if externTimestamp != 0 {t.Header.Timestamp |= uint32(externTimestamp) << 24}//读取Tag Datat.Data, err = s.Produce(int(t.Header.DataSize))return
}
为了方便观察和调试,我们再给FlvTag
实现Stringer
接口。
func (f FlvTag) String() string {var sb strings.Buildersb.WriteString(fmt.Sprintf("tag header: %+v\ntag body:", f.Header))for i, b := range f.Data.Raw() {if i&0x0F == 0 {sb.WriteByte('\n')}sb.WriteString(fmt.Sprintf("%2X ", b))}return sb.String()
}
到这里,flv.go
的内容就基本完成了,接下来写个main
函数来试试吧。
func main() {s, _ := ReadFlv("b.flv")h, _ := DecodeFlvHeader(s)fmt.Printf("%+v\n", h)st, _ := DecodeFlvTag(s)fmt.Println(st)
}
以上就是本期的全部内容,你可以亲手试一试如何循环读完所有Tag。
下一期我们探索Tag Data的内部乾坤。
【Go】FLV文件解析(一)相关推荐
- 【Go】FLV文件解析(二)
书接上回,我们继续来解析FLV文件的内容,这次要解析的是元数据Tag的内容,需要注意的是不是每个FLV文件都有这个Tag的. 在有些教程中,元数据Tag也被称作Script Tag.在官方文档中其实是 ...
- 【Go】FLV文件解析(三)
生命不息,编程不止.本章我们继续解析FLV文件中的音频Tag的内容. Audio Tag Data 在[Go]FLV文件解析(一)中我们讲了FLV Tag的基本结构是Tag Header加Tag Da ...
- 【Go】FLV文件解析(四)
终于到了完结的时刻,这期我们来解析FLV中的视频Tag. Video Tag Data 视频Tag的总体结构和音频Tag相似,视频的Tag Data结构如下. 第一个字节的前4比特表示帧类型,共5种. ...
- (转)rtmp协议简单解析以及用其发送h264的flv文件
Adobe公司太坑人了,官方文档公布的信息根本就不全,如果只按照他上面的写的话,是没法用的.按照文档上面的流程,server和client连接之后首先要进行握手,握手成功之后进行一些交互,其实就是交互 ...
- 分析FLV文件分析和解析器的开源代码
分析一下GitHub上一份FLV文件分析和解析器的开源代码 GitHub源码地址:功能强大的 FLV 文件分析和解析器 :可以将flv文件的视频tag中的h264类型数据和音频tag中的aac类型数据 ...
- 音视频之解析flv文件实战
参考文章看的是 http://www.360doc.com/content/16/1013/17/474846_598171645.shtml 测试文件 相关文件和部分工具 csdn下载链接 cuc_ ...
- FLV文件中VideoPacket的详解
FLV文件中VideoPacket的详解 众所周知,FLV文件体主要有三种Tag组成:AUDIODATA.VIDEODATA和SCRIPTDATA,其中SCRIPTDATA中包含了MetaData,通 ...
- 分析flv文件的信息
上一篇博文"FLV文件格式解析"提到的小工具FLVParse,是由于工作需要,我自己写的一个小程序. 这个工具的主要功能是查看FLV的文件结构,帮助我们理解FLV格式.另外,如果涉 ...
- 音视频 FLV格式解析
前言 本文介绍FLV封装格式,直播场景下拉流比较常见的是http-flv直播流,具有延时低.易传输等特点. 格式概览 总体上看,FLV包括文件头(File Header)和文件体(File Body) ...
最新文章
- timer 公司内部用法
- apache 修改最大连接数
- C/C++线程基本函数
- 利用计算机语言实现ID3算法,机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf...
- azure备份存储层分类_如何配置Azure SQL数据库长期保留(LTR)备份
- java 中文 转义_java html中文汉字 反转义
- Python --链接MYSQL数据库与简单操作 含SSH链接
- WSL2构建android10+kernel源码适配pixel
- 部分苹果 macOS Monterey 用户遇到“内存泄漏”问题,应用后台运行消耗上百 GB 内存
- 睡眠阶段分期——SVM和ELM分别与粒子群算法结合(main)
- ntdll.dll 0xc0000005
- 聚焦基因组学研究,JMP参加第七届国际统计遗传学与基因组学高峰论坛
- 燕麦粉行业调研报告 - 市场现状分析与发展前景预测
- mysql 实例和数据库名_oracle 实例名和服务名以及数据库名区别
- 简单讲讲前端路由概念
- mysql jdbc驱动 批量更新_jdbc批量插入,删除,更新
- 电脑睡眠后启动 耳机没有声音 手动启动 -已解决
- 垂直搜索引擎的存在意义
- 修改3389远程端口号
- 洛谷 P3216 [HNOI2011]数学作业
热门文章
- python微信公众号开发教程_python微信公众号开发简单流程实现
- 最落魄的时候,身上带着《毛选》
- mysql drop table 卡死
- Beego学习入门基础框架搭建
- MongoDB常用语句小册
- GeForce笔记本电脑产品 显卡计算能力
- 机器学习系列2 机器学习的公平性
- 明明连上网络了,微信也可以使用,但是浏览器不能上网:修复浏览器不能上网问题
- goland 2021.2 配置go( go1.17.6)
- echarts label加边框_ECharts官方教程(十三)【富文本标签】