问题背景:
概念:
flv即flash video,是Adobe公司推出的一种音视频封装格式,这家公司在音视频、图像图形领域也算是神一般的存在,多少都用过他们家的产品。常见的Photoshop和Flash palyer就是他们家的。今天要讲到的flv也是他们推出来的,也是Flash palyer播放的标准音视频格式。在HTML5出来之前,想在web上播放音视频,基本都靠flash插件。
发展现状:
最近新闻已经看到:由于flash性能和安全问题,会在2020年底彻底失去支持,浏览器基于flash player播放视频这条路算是在21世纪的第二个十年走完了辉煌的一生。既然这项技术都要淘汰了,为啥还要讲解这种音视频封装格式?接过flash player大旗的是上述新闻说的开源项目flv.js和苹果推出的HLS这两项技术。关于HLS涉及的音视频封装格式ts已经在这篇文章讲过,HLS协议本身会在后续系列看到讲解。
直播方案:
flv.js这个开源项目能做什么呢?它解析flv文件喂给原生HTML5 Video标签播放音视频数据,使浏览器在不借助flash的情况下播放flv成为可能,一般应用在低延时的web直播和一些视频网站的点播。现在直播方案基本就是rtmp flv、http flv和hls ts.其中前两项技术都是Adobe推出的,hls技术目前存在的最大问题是延时比较高,一般延时会达到10秒级别,对于低延时直播不能忍受。rtmp flv目前不能在web上使用,延时基本和http flv保持一致。http flv算是Adobe的一种中间妥协技术,既能方便在web上实现,又能降低延时,协议简单实现容易。同时将流媒体放到http上传输,方便穿越防火墙,网络亲和力好,目前这种技术在爱奇艺,优酷和一些直播网站上都能看到。rtmp目前应用比较广泛的推流端,就是将手机主播端,摄像头IPC录制的视频可以通过rtmp技术推流到各大云服务厂商的CDN上,以前我就做过一个将景区摄像头推流到腾讯云CDN上,然后在小程序观看的项目,大家可以搜索一部手机游云南小程序进行体验。关于传输协议rtmp和hls会在后面文章逐渐推出,敬请期待。
未来展望:
综上所述:我们看到flv目前还算是一门主流技术,真正限制flv发展的不是flash player消亡了,而是flv支持的音视频编码格式有限,flv支持的视频编码格式主要是H264,音频是AAC。虽然MP3也支持,但是AAC的推出明显就是替代MP3的,关于AAC你可以看前面的文章,有详细讲解。但是介于目前H264还是主流的音视频编码标准,市场占有率还在80%以上,所以flv再活个几年也是没啥问题的。
跟f4v关系:
其实flv后面还有升级版就是f4v,现在优酷、腾讯视频下载来的视频虽然有些后缀是flv,其实本身是f4v。f4v和flv最大的区别就是支持的视频码率比较大,所以H264的高码率和高清视频用这种格式封装。f4v另外一个特点是只支持H264 AAC,它其实是MP4这种封装格式的一个子集,MP4是目前最复杂的音视频封装格式,封装和解封装代码基本在万行左右,后面会通过几篇文章讲解。
格式详解:
flv(flash video)这种网络传输的媒体数据存储容器格式,其格式相对简单,不需要很大媒体头部信息。这也是我们学习flv这种音视频封装格式的另外一个目的:就是思考如何自己设计一个简单的音视频封装格式。视频云公司一般为了各个组件和平台音视频数据的自由传输和更好的兼容性,有时会在开发内部用私有格式,这样会减少很多兼容问题和开发成本。海康这种视频监控公司也有类似的操作。
整体封装格式:
flv header flv body
flv header previous size0 tag1 previous size1 tag2 ..... prvious sizen tagn 1
flv header previous size0 tag1 heade tag1 data1 ........ previous sizen tagn header tagn data
详细封装格式:
注意:flv是流媒体封装格式,我们可以将其数据看为二进制字节流。总体上看,flv包括文件头(flv Header)和文件体(flv Body)两部分,其中文件体由一系列的Tag及Tag Size对组成。Tag又可以分成三类:audio,video,script,分别代表音频流,视频流,脚本流(关键字或者文件信息之类)。其中previous size字段是后面各个tag解析的分割符,指示了前一个tag的大小。
用flv parse专业工具分析了一个实际的flv文件,如下图展示:
下面通过分析结果,详细解释flv的各个字段含义,弄明白解封装flv的关键。
1. flv header部分:
十六进制:0x46 4C 56 01 05 00 00 00 09
二进制:0100 0110 0100 1100 0101 0110 0000 0001 0000 0101 0000 0000 0000 0000 0000 0000 0000 1001
字段 | 占位 | 备注 |
---|---|---|
Signature | 1 byte | 必须为’F’(0x46) |
Signature | 1 byte | 必须为’L’(0x4C) |
Signature | 1 byte | 必须为’V’(0x56) |
(版本)Version | 1 byte | 通常为0x01 |
TypeFlagsReserved | 5 bits | 必须为0 |
TypeFlagsAudio | 1 bit | 表示是否含有音频 |
TypeFlagsReserved | 1 bit | 必须为0 |
TypeFlagsVideo | 1 bit | 表示是否含有视频 |
DataOffset | 4 bytes | 文件头部的大小(从文件开始位置到body的偏移量),通常为9 |
说明:
1.Flv header 的前三个字节是固定的FLV的 ASCII 码的值0x46 0x4C 0x56;
2.接下来的一个字节表示 FLV 的版本号,例如 0x01 代表 FLV 版本号为 1;
3.第 5 个字节中的第0位和第2位分别表示video和audio的存在情况(1表示存在,0 表示不存在)其余6位必须为0.最后的4字节表示FLV Header的长度,对于version 1,此处为9;
4. 一般判断格式是不是flv,先从收到数据的第一字节连续查找flv三个字符,如果找到一般可以判断是flv封装格式;
5.Header头数据一般是9字节但是不绝对,所以需要读最后的长度字段来解析;
2. flv tag部分:
一般的flv通常有三种类型的tag,音频、视频和脚本或者又叫音视频元数据。下面我们整体看下tag头数据,然后分析下这三种tag具体内容。
字段 | 占位 | 备注 |
---|---|---|
Tag类型(TagType) | 1 bytes | 1-2bit位:必须为0,保留位;第3bit位: 0表示未加密,1表示加密,一般默认0;4-8bit位:8:音频、9:视频、18:script数据; |
数据大小(DataSize) | 3 bytes | 数据字段的长度,是Tag Data的长度,不包括11字节的Tag Header长度; |
时间戳(Timestamp) | 3 bytes | 毫秒为单位,第一个tag时,该值总是0,单位是毫秒,则意味着要将时间戳单位关系换算好; |
时间戳扩展(TimeStampExtended) | 1 bytes | 时间戳扩展为4bytes,代表高8位,很少用到; |
流ID(Stream ID) | 3bytes | 总是0,暂时未用到,因为flv只封装一路音视频,但是对于TS和PS则有特殊含义; |
数据(Data) | 音频、视频或script | 数据实体,音频、视频和脚本数据; |
说明:
1. 无论那种类型的tag,tag头字节都是11字节,要解析里面的音频帧,视频帧或者元数据需要读取tag头里面的data长度字段;
2. 时间戳很关键,播放过程中,FLV tag的时间信息完全依赖于 FLV 时间戳,内置的其他时间信息都被忽略掉,一般非音视频的tag,时间戳就是0即可;
3. 注意计算好时间戳大小,这里的单位是毫秒,所以一定要根据采样率和视频帧率,音频帧采样多少计算好时间戳,然后还要换算成毫秒,对时间戳的理解参考以前的文章;
4. Tag头解析完后,Tag数据部分不是视频帧和音频帧,还要根据H264和AAC的打包方案解析才能得到真实的音视频裸数据;
3. Script Tag 脚本元数据Tag:
该Tag类型又被称为Metadata Tag,会放一些关于FLV视频和音频的元数据信息:duration、width、height等。该tag一般会在flv header的第一个tag出现,一般只出现一次。
该tag又由两个AMF包组成,AMF这种包的结构也经常出现在Adobe的相关协议和规范里:
AMF1{“on MetaData”}|AMF2{“width height”}
flv parse 解析Script Tag的结果如下:
previous tag0:
十六机制:0x00 00 00 00
因为在Metadata Tag之前,没有tag,所以这个长度定义为0;
Metadata tag header:
十六进制:0x12 00 01 2C 00 00 00 00 00 00 00
Tagtype:
十六进制:0x12
二进制:0001 0010
通过前1-2bit即知道非加密,后面4-8bit10010即十进制为18 则Tag类型Metadata Tag;
DataSize:
十六进制:0x 00 01 2C
十进制:300
说明这个Metdata Tag的data字段长度为300字节;
Timestamp:
十六进制:0x 00 00 00
Metdata Tag该字段一般就是默认为0;
TimeStampExtended:
十六进制:0x 00 01 2C
时间戳扩展字段也是默认为0;
Stream ID:
十六进制0x 00 00 00
默认为0;
Metadata Tag Data:
Metdata Tag有下面两种包组成:
AMF1{“on MetaData”}|AMF2{“width height”}
其中AMF1包:
字段 | 占位 | 备注 |
---|---|---|
AMF Type | 1byte字节 | 固定值为0x02 |
AMF 长度 | 2byte字节 | 一般是“onMetaData”长度即固定值0x00 0A即十字节 |
AMF value值 | 10byte字节 | 一般默认值为:0x6F 6E 4D 65 74 61 44 61 74 61 |
说明:上面红色已经将该包框出来了,具体不在分析,封装时直接构造固定值即可,解装时解析出来丢弃即可,没啥大用;
其中AMF2包:
字段 | 占位 | 备注 |
---|---|---|
AMF Type | 1byte字节 | 固定值为0x08 |
AMF 长度 | 4byte字节 | 数组元素的个数:0x00 00 00 0D |
AMF value值 | 计算得到字节 | 因为在该tag的头已经说明该tag的data有300字节,则用该值减去AMF1长度13字节和AMF2头的5字节即为剩余AMF2的Value值大小:300 - 12 - 5 = 283字节 |
第1个字节表示AMF包类型,一般总是0x08,表示数组。第2-5个字节为四字节,表示数组元素的个数。后面即为各数组元素的封装,数组元素为元素名称和值组成的对。
这个包已经在上图用蓝色框出来了,其中各个元素显示在左边目录上,这些值一般通过H264的SPS PPS帧和AAC的ADTS头部数据解析而来,元素类型的枚举如下表:
值 | 含义 |
---|---|
duration | 时长 |
width | 视频宽度 |
height | 视频高度 |
videodatarate | 视频码率 |
framerate | 视频帧率 |
videocodecid | 视频编码方式 |
audiosamplerate | 音频采样率 |
audiosamplesize | 音频采样精度 |
stereo | 是否为立体声 |
audiocodecid | 音频编码方式 |
filesize | 文件大小 |
为了验证我们的猜想,我又通过MediaInfo工具进行交叉验证下:
对于元素名称和元素值对的解析方法如下:
元素的名称的长度用2个字节表示,假设为L。后面跟着为长度为L的字符串。第L 3个字节表示元素值的类型。后面跟着为对应值,占用字节数取决于值的类型。
说明:上图我用蓝框显示的是durition元素和其值,width元素用红框显示,同时注意这些元素对循环解析方法。
下面会讲解Video Tag和AudioTag,感觉一篇文章有点长,分两次发。
今天就说这么多,祝您心情愉快,工作顺利!
参考文章
flv.js相关:
https://mp.weixin.qq.com/s/M97Krog9VS2C2QqSld9o9w
分析工具下载:
https://github.com/ty6815/AvStackDocs