这是一个系列教程,一是为了解释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个问题:

  1. 时间戳的单位是是什么?

  2. 时间戳的含义是什么?

  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
}

这里我们将FlvTagData字段也定义为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文件解析(一)相关推荐

  1. 【Go】FLV文件解析(二)

    书接上回,我们继续来解析FLV文件的内容,这次要解析的是元数据Tag的内容,需要注意的是不是每个FLV文件都有这个Tag的. 在有些教程中,元数据Tag也被称作Script Tag.在官方文档中其实是 ...

  2. 【Go】FLV文件解析(三)

    生命不息,编程不止.本章我们继续解析FLV文件中的音频Tag的内容. Audio Tag Data 在[Go]FLV文件解析(一)中我们讲了FLV Tag的基本结构是Tag Header加Tag Da ...

  3. 【Go】FLV文件解析(四)

    终于到了完结的时刻,这期我们来解析FLV中的视频Tag. Video Tag Data 视频Tag的总体结构和音频Tag相似,视频的Tag Data结构如下. 第一个字节的前4比特表示帧类型,共5种. ...

  4. (转)rtmp协议简单解析以及用其发送h264的flv文件

    Adobe公司太坑人了,官方文档公布的信息根本就不全,如果只按照他上面的写的话,是没法用的.按照文档上面的流程,server和client连接之后首先要进行握手,握手成功之后进行一些交互,其实就是交互 ...

  5. 分析FLV文件分析和解析器的开源代码

    分析一下GitHub上一份FLV文件分析和解析器的开源代码 GitHub源码地址:功能强大的 FLV 文件分析和解析器 :可以将flv文件的视频tag中的h264类型数据和音频tag中的aac类型数据 ...

  6. 音视频之解析flv文件实战

    参考文章看的是 http://www.360doc.com/content/16/1013/17/474846_598171645.shtml 测试文件 相关文件和部分工具 csdn下载链接 cuc_ ...

  7. FLV文件中VideoPacket的详解

    FLV文件中VideoPacket的详解 众所周知,FLV文件体主要有三种Tag组成:AUDIODATA.VIDEODATA和SCRIPTDATA,其中SCRIPTDATA中包含了MetaData,通 ...

  8. 分析flv文件的信息

    上一篇博文"FLV文件格式解析"提到的小工具FLVParse,是由于工作需要,我自己写的一个小程序. 这个工具的主要功能是查看FLV的文件结构,帮助我们理解FLV格式.另外,如果涉 ...

  9. 音视频 FLV格式解析

    前言 本文介绍FLV封装格式,直播场景下拉流比较常见的是http-flv直播流,具有延时低.易传输等特点. 格式概览 总体上看,FLV包括文件头(File Header)和文件体(File Body) ...

最新文章

  1. timer 公司内部用法
  2. apache 修改最大连接数
  3. C/C++线程基本函数
  4. 利用计算机语言实现ID3算法,机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf...
  5. azure备份存储层分类_如何配置Azure SQL数据库长期保留(LTR)备份
  6. java 中文 转义_java html中文汉字 反转义
  7. Python --链接MYSQL数据库与简单操作 含SSH链接
  8. WSL2构建android10+kernel源码适配pixel
  9. 部分苹果 macOS Monterey 用户遇到“内存泄漏”问题,应用后台运行消耗上百 GB 内存
  10. 睡眠阶段分期——SVM和ELM分别与粒子群算法结合(main)
  11. ntdll.dll 0xc0000005
  12. 聚焦基因组学研究,JMP参加第七届国际统计遗传学与基因组学高峰论坛
  13. 燕麦粉行业调研报告 - 市场现状分析与发展前景预测
  14. mysql 实例和数据库名_oracle 实例名和服务名以及数据库名区别
  15. 简单讲讲前端路由概念
  16. mysql jdbc驱动 批量更新_jdbc批量插入,删除,更新
  17. 电脑睡眠后启动 耳机没有声音 手动启动 -已解决
  18. 垂直搜索引擎的存在意义
  19. 修改3389远程端口号
  20. 洛谷 P3216 [HNOI2011]数学作业

热门文章

  1. python微信公众号开发教程_python微信公众号开发简单流程实现
  2. 最落魄的时候,身上带着《毛选》
  3. mysql drop table 卡死
  4. Beego学习入门基础框架搭建
  5. MongoDB常用语句小册
  6. GeForce笔记本电脑产品 显卡计算能力
  7. 机器学习系列2 机器学习的公平性
  8. 明明连上网络了,微信也可以使用,但是浏览器不能上网:修复浏览器不能上网问题
  9. goland 2021.2 配置go( go1.17.6)
  10. echarts label加边框_ECharts官方教程(十三)【富文本标签】