FLV协议5分钟入门浅析
FLV协议简介
FLV(Flash Video)是一种流媒体格式,因其体积小、协议相对简单,很快便流行开来,并得到广泛的支持。
常见的HTTP-FLV直播协议,就是使用HTTP流式传输通过FLV封装的音视频数据。对想要了解HTTP-FLV的同学来说,了解FLV协议很有必要。
概括地说,FLV 由 FLV header 跟 FLV file body 两部分组成,而 FLV file body 又由多个 FLV tag组成。
FLV = FLV header + FLV file body
FLV file body = PreviousTagSize0 + Tag1 + PreviousTagSize1 + Tag2 + ... + PreviousTagSizeN-1 + TagN
FLV tag又分为3种类型:
- Video Tag:存放视频相关数据;
- Audio Tag:存放音频相关数据;
- Script Tag:存放音视频元数据;
在实际讲解FLV协议前,首先对单位进行约定:
| 类型 | 定义 |
|---|---|
| 0x... | 16进制数据 |
| SI8 | 有符号8位整数 |
| SI16 | 有符号16位整数 |
| SI24 | 有符号24位整数 |
| SI32 | 有符号32位整数 |
| STRING | Sequence of Unicode 8-bit characters (UTF-8), terminated with 0x00 (unless otherwise specified) |
| UI8 | 无符号8位整数 |
| UI16 | 无符号16位整数 |
| UI24 | 无符号24位整数 |
| UI32 | 无符号32位整数 |
| xxx [ ] | 类型为xxx的数组 |
| xxx [n] | 类型为xxx的数组,数组长度为n |
FLV header
FLV header由如下字段组成,其中:
- 前三个字节内容固定是FLV
- 最后4个字节内容固定是9(对FLV版本1来说)
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| Signature | UI8 | 签名,固定为'F' (0x46) |
| Signature | UI8 | 签名,固定为'L' (0x4c) |
| Signature | UI8 | 签名,固定为'V' (0x56) |
| Version | UI8 | 版本,比如 0x01 表示 FLV 版本 1 |
| TypeFlagsReserved | UB[5] | 全为0 |
| TypeFlagsAudio | UB[1] | 1表示有audio tag,0表示没有 |
| TypeFlagsReserved | UB[1] | 全为0 |
| TypeFlagsVideo | UB[1] | 1表示有video tag,0表示没有 |
| DataOffset | UI32 | FLV header的大小,单位是字节 |
FLV file body
FLV file body很有规律,由一系列的TagSize和Tag组成,其中:
- PreviousTagSize0 总是为0;
- tag 由tag header、tag body组成;
- 对FLV版本1,tag header固定为11个字节,因此,PreviousTagSize(除第1个)的值为 11 + 前一个tag 的 tag body的大小;
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| PreviousTagSize0 | UI32 | 总是0 |
| Tag1 | FLVTAG | 第1个tag |
| PreviousTagSize1 | UI32 | 前一个tag的大小,包括tag header |
| Tag2 | FLVTAG | 第2个tag |
| ... | ... | ... |
| PreviousTagSizeN-1 | UI32 | 第N-1个tag的大小 |
| TagN | FLVTAG | 第N个tag |
| PreviousTagSizeN | UI32 | 第N个tag的大小,包含tag header |
FLV tags
FLV tag由 tag header + tag body组成。
tag header如下,总共占据11个字节:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| TagType | UI8 | tag类型 8:audio 9:video 18:script data 其他:保留 |
| DataSize | UI24 | tag body的大小 |
| Timestamp | UI24 | 相对于第一个tag的时间戳(单位是毫秒) 第一个tag的Timestamp为0 |
| TimestampExtended | UI8 | 时间戳的扩展字段,当 Timestamp 3个字节不够时,会启用这个字段,代表高8位 |
| StreamID | UI24 | 总是0 |
| Data | 取决于根据TagType | TagType=8,则为AUDIODATA TagType=9,则为VIDEODATA TagType=18,则为SCRIPTDATAOBJECT |
In playback, the time sequencing of FLV tags depends on the FLV timestamps only. Any timing mechanisms built into the payload data format are ignored.
Audio tags
定义如下所示:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| SoundFormat | UB[4] | 音频格式,重点关注 **10 = AAC ** 0 = Linear PCM, platform endian 1 = ADPCM 2 = MP3 3 = Linear PCM, little endian 4 = Nellymoser 16-kHz mono 5 = Nellymoser 8-kHz mono 6 = Nellymoser 7 = G.711 A-law logarithmic PCM 8 = G.711 mu-law logarithmic PCM 9 = reserved 10 = AAC 11 = Speex 14 = MP3 8-Khz 15 = Device-specific sound |
| SoundRate | UB[2] | 采样率,对AAC来说,永远等于3 0 = 5.5-kHz 1 = 11-kHz 2 = 22-kHz 3 = 44-kHz |
| SoundSize | UB[1] | 采样精度,对于压缩过的音频,永远是16位 0 = snd8Bit 1 = snd16Bit |
| SoundType | UB[1] | 声道类型,对Nellymoser来说,永远是单声道;对AAC来说,永远是双声道; 0 = sndMono 单声道 1 = sndStereo 双声道 |
| SoundData | UI8[size of sound data] | 如果是AAC,则为 AACAUDIODATA; 其他请参考规范; |
备注:
If the SoundFormat indicates AAC, the SoundType should be set to 1 (stereo) and the SoundRate should be set to 3 (44 kHz). However, this does not mean that AAC audio in FLV is always stereo, 44 kHz data. Instead, the Flash Player ignores these values and extracts the channel and sample rate data is encoded in the AAC bitstream.
AACAUDIODATA
当 SoundFormat 为10时,表示音频采AAC进行编码,此时,SoundData的定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| AACPacketType | UI8 | 0: AAC sequence header 1: AAC raw |
| Data | UI8[n] | 如果AACPacketType为0,则为AudioSpecificConfig 如果AACPacketType为1,则为AAC帧数据 |
The AudioSpecificConfig is explained in ISO 14496-3. Note that it is not the same as the contents of the esds box from an MP4/F4V file. This structure is more deeply embedded.
关于AudioSpecificConfig
伪代码如下:参考这里
5 bits: object type
if (object type == 31)
6 bits + 32: object type
4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
4 bits: channel configuration
var bits: AOT Specific Config
定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| AudioObjectType | UB[5] | 编码器类型,比如2表示AAC-LC |
| SamplingFrequencyIndex | UB[4] | 采样率索引值,比如4表示44100 |
| SamplingFrequencyIndex | UB[4] | 采样率索引值,比如4表示44100 |
| ChannelConfiguration | UB[4] | 声道配置,比如2代表双声道,front-left, front-right |
Video tags
定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| FrameType | UB[4] | 重点关注1、2: 1: keyframe (for AVC, a seekable frame) —— 即H.264的IDR帧; 2: inter frame (for AVC, a non- seekable frame) —— H.264的普通I帧; 3: disposable inter frame (H.263 only) 4: generated keyframe (reserved for server use only) 5: video info/command frame |
| CodecID | UB[4] | 编解码器,主要关注 7(AVC) 1: JPEG (currently unused) 2: Sorenson H.263 3: Screen video 4: On2 VP6 5: On2 VP6 with alpha channel 6: Screen video version 2 7: AVC |
| VideoData | 取决于CodecID | 实际的媒体类型,主要关注 7:AVCVIDEOPACKE 2: H263VIDEOPACKET 3: SCREENVIDEOPACKET 4: VP6FLVVIDEOPACKET 5: VP6FLVALPHAVIDEOPACKET 6: SCREENV2VIDEOPACKET 7: AVCVIDEOPACKE |
AVCVIDEOPACKE
当 CodecID 为 7 时,VideoData 为 AVCVIDEOPACKE,也即 H.264媒体数据。
AVCVIDEOPACKE 的定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| AVCPacketType | UI8 | 0: AVC sequence header 1: AVC NALU 2: AVC end of sequence |
| CompositionTime | SI24 | 如果AVCPacketType=1,则为时间cts偏移量;否则,为0 |
| Data | UI8[n] | 1、如果如果AVCPacketType=1,则为AVCDecoderConfigurationRecord 2、如果AVCPacketType=1=2,则为NALU(一个或多个) 3、如果AVCPacketType=2,则为空 |
这里有几点稍微解释下:
- NALU:H.264中,将数据按照特定规则格式化后得到的抽象逻辑单元,称为NALU。这里的数据既包括了编码后的视频数据,也包括视频解码需要用到的参数集(PPS、SPS)。
- AVCDecoderConfigurationRecord:H.264 视频解码所需要的参数集(SPS、PPS)
- CTS:当B帧的存在时,视频解码呈现过程中,dts、pts可能不同,cts的计算公式为 pts - dts/90,单位为毫秒;如果B帧不存在,则cts固定为0;
PPS、SPS这里先不展开。
Script Data Tags
Script Data Tags通常用来存放跟FLV中音视频相关的元数据信息(onMetaData),比如时长、长度、宽度等。它的定义相对复杂些,采用AMF(Action Message Format)封装了一系列数据类型,比如字符串、数值、数组等。
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| Objects | SCRIPTDATAOBJECT[] | 任意数目的 SCRIPTDATAOBJECT |
| SCRIPTDATAOBJECTEND | UI24 | 永远是9,标识着Script Data的结束 |
SCRIPTDATAOBJECT 定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| ObjectName | SCRIPTDATASTRING | 对象的名字 |
| ObjectData | SCRIPTDATAVALUE | 对象的值 |
SCRIPTDATAVALUE 的定义如下:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| Type | SCRIPTDATASTRING | 变量类型: 0 = Number type 1 = Boolean type 2 = String type 3 = Object type 4 = MovieClip type 5 = Null type 6 = Undefined type 7 = Reference type 8 = ECMA array type 10 = Strict array type 11 = Date type 12 = Long string type |
| ECMAArrayLength | 如果Type为8(数组),则为UI32 | 数组长度 |
| ScriptDataValue | If Type == 0 DOUBLE If Type == 1 UI8 If Type == 2 SCRIPTDATASTRING ...(有点长,可以参考规范) |
变量的值 |
| ScriptDataValueTerminator | 如果Type3,则为SCRIPTDATAOBJECTEND 如果 Type8,则为SCRIPTDATAVARIABLEEND |
Object、Array的结束符 |
可以看到,Script Data Tag 的定义相对复杂,下面通过onMetaData进行进一步讲解。
onMetaData
onMetaData中包含了音视频相关的元数据,封装在Script Data Tag中,它包含了两个AMF。
第一个AMF:
- 第1个字节:0x02,表示字符串类型
- 第2-3个字节:UI16类型,值为0x000A,表示字符串的长度为10(onMetaData的长度);
- 第4-13个字节:字符串onMetaData对应的16进制数字(0x6F 0x6E 0x4D 0x65 0x74 0x61 0x44 0x61 0x74 0x61);
第二个AMF:
- 第1个字节:0x08,表示数组类型;
- 第2-5个字节:UI32类型,表示数组的长度,onMetaData中具体包含哪些属性是不固定的。
- 第6个字节+:比如duration,则:
- 第6-9个字节:0x0008,表示长度为8个字节;
- 第10-17个字节:0x6475 7261 7469,表示 duration 这个字符串;
- 第18个字节:0x00,表示为数值类型;
- 第19-26个字节:0x...,表示具体的时长;
更多onMetaData字段的定义:
| 字段 | 字段类型 | 字段含义 |
|---|---|---|
| duration | DOUBLE | 文件的时长 |
| width | DOUBLE | 视频宽度(px) |
| height | DOUBLE | 视频高度(px) |
| videodatarate | DOUBLE | 视频比特率(kb/s) |
| framerate | DOUBLE | 视频帧率(帧/s) |
| videocodecid | DOUBLE | 视频编解码器ID(参考Video Tag) |
| audiosamplerate | DOUBLE | 音频采样率 |
| audiosamplesize | DOUBLE | 音频采样精度(参考Audio Tag) |
| stereo | BOOL | 是否立体声 |
| audiocodecid | DOUBLE | 音频编解码器ID(参考Audio Tag) |
| filesize | DOUBLE | 文件总得大小(字节) |
写在后面
FLV协议本身不算复杂,理解上的困难,更多时候来自音视频编解码相关的知识,比如H.264、AAC相关知识,建议不懂的时候自行查下。此外,FLV的字节序为大端序,在做协议解析的时候一定要注意。
本文为讲解方便,部分内容可能不够严谨,如有错漏敬请指出。
相关链接
video_file_format_spec_v10.pdf
https://www.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10.pdf
MPEG-4 Part 3
https://en.wikipedia.org/wiki/MPEG-4_Part_3#Audio_Profiles
flv文件分析
https://www.jianshu.com/p/e290dca02979
H.264再学习 -- 详解 H.264 NALU语法结构
https://blog.csdn.net/qq_29350001/article/details/78226286
FLV协议5分钟入门浅析的更多相关文章
- Objective-C 30分钟入门教程
Objective-C 30分钟入门教程 我第一次看OC觉得这个语言的语法有些怪异,为什么充满了@符号,[]符号,函数调用没有()这个,但是面向对象的高级语言也不外乎类,接口,多态,封装,继承等概念. ...
- Shell脚本编程30分钟入门
Shell脚本编程30分钟入门 转载地址: Shell脚本编程30分钟入门 什么是Shell脚本 示例 看个例子吧: #!/bin/sh cd ~ mkdir shell_tut cd shell_t ...
- Apache Shiro系列三,概述 —— 10分钟入门
一.介绍 看完这个10分钟入门之后,你就知道如何在你的应用程序中引入和使用Shiro.以后你再在自己的应用程序中使用Shiro,也应该可以在10分钟内搞定. 二.概述 关于Shiro的废话就不多说了 ...
- JavaScript 10分钟入门
JavaScript 10分钟入门 随着公司内部技术分享(JS进阶)投票的失利,先译一篇不错的JS入门博文,方便不太了解JS的童鞋快速学习和掌握这门神奇的语言. 以下为译文,原文地址:http://w ...
- 十分钟入门less(翻译自:Learn lESS in 10 Minutes(or less))
十分钟入门less(翻译自:Learn lESS in 10 Minutes(or less)) 注:本文为翻译文章,因翻译水平有限,难免有缺漏不足之处,可查看原文. 我们知道写css代码是非常枯燥的 ...
- 30分钟入门Java8之方法引用
30分钟入门Java8之方法引用 前言 之前两篇文章分别介绍了Java8的lambda表达式和默认方法和静态接口方法.今天我们继续学习Java8的新语言特性--方法引用(Method Referenc ...
- 30分钟入门Java8之默认方法和静态接口方法
30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...
- 【原创】30分钟入门 github
很久没更新了,这篇文章重点在github的入门使用,读者可以下载github for windows shell,边看边操作,加深印象. 好了,30分钟的愉快之旅开始吧: 一.github使用的注意事 ...
- 正则表达式30分钟入门教程<转载>
来园子之前写的一篇正则表达式教程,部分翻译自codeproject的The 30 Minute Regex Tutorial. 由于评论里有过长的URL,所以本页排版比较混乱,推荐你到原处查看,看完了 ...
随机推荐
- c++学习书籍推荐《深度探索C++对象模型》下载
百度云及其他网盘下载地址:点我 百度云及其他网盘下载地址:点我 编辑推荐 如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么这本<深度探索C++对象模型>正适合你 作者简介 ...
- Mybatis辅助神器-MyBatis Log Plugin
1. 问题描述 Java操作数据库的两台流行天王-mybatis和hibernate,mytabis和hibernate的区别不想废话了,以前用hibernate,最近几年一直用的mybatis,目前 ...
- Socket编程(C语言实现):bind()函数英文翻译
本篇翻译的bind()函数,我参考的国外网站是: bind 朋友们可以自由转载我对英文的中文翻译,但是对于"作者注:"的描述,转载时请注明出处和作者,否则视为侵权. 下面是翻译的正 ...
- spark 源码分析之十六 -- Spark内存存储剖析
上篇spark 源码分析之十五 -- Spark内存管理剖析 讲解了Spark的内存管理机制,主要是MemoryManager的内容.跟Spark的内存管理机制最密切相关的就是内存存储,本篇文章主要介 ...
- Appium+python自动化(二十一)- 让猴子按你指令大闹手机,让你成为耍猴高手 - Monkey(猴子) - MonkeyScript(超详解)
简介 一年一度的暑假如期而至,每年必不可少的,便是<西游记>这部经典电视连续剧的播出,作为一名90后,对于这部经典剧的情谊,就是观看已成为一种习惯.依然深刻的记得,小时候妈妈为了催促我睡觉 ...
- MySQL常见操作指令
1:使用SHOW语句找出在服务器上当前存在什么数据库: mysql> SHOW DATABASES; 2:创建一个数据库MYSQLDATA mysql> CREATE DATABASE M ...
- 【HDU - 3533】Escape(bfs)
Escape Descriptions: 一个人从(0,0)跑到(n,m),只有k点能量,一秒消耗一点,在图中有k个炮塔,给出炮塔的射击方向c,射击间隔t,子弹速度v,坐标x,y问这个人能不能安全到 ...
- 《VR入门系列教程》之4---运行平台
运行平台 大多数的VR应用都可以在目前多数的PC和手机上运行,基本上一个不太旧的PC或者配置好点的笔记本电脑都可以正常运行Oculus Rift,如果手机的CPU和显卡不错的话也可以有很好的V ...
- jsp数据交互(一).2
01.什么是JSP内置对象(jsp核心)? Java 内置对象 Java 作用域 解析:jsp内置对象是web容器创建的一组对象.我们都知道tomcat这款软件可以看成是一种web容器,所以我们可以 ...
- web设计_1_思路总览
核心思想:结构和样式分离 HTML与CSS 只有充分将页面核心内容和外观设计相分离而获得的灵活性,才能顺利构建出能够满足每个web用户需要的最佳设计方案. 核心要求:灵活性 适应不同的浏览器,适应各种 ...