引用
rtmp.veriskope.com/pdf/video_f… cloud.tencent.com/developer/a… www.jianshu.com/p/b90c6ef59… www.cnblogs.com/chyingp/p/f…
基本介绍
通过将音视频数据封装成flv, 并通过http协议将其传输至客户端,故称之为http-flv。该协议是目前直播拉流端的主流协议了,虎牙,斗鱼等都使用该协议进行拉流。
基本原理
众所周知,HTTP协议会通过一个Content-length字段来标识响应体的内容大小,故有以下两种情况
- 服务器回复 http 请求的时候如果有这个字段,客户端就接收这个长度的数据然后就认为数据传输完成了。
- 如果服务器回复 http 请求中没有这个字段,客户端就一直接收数据,直到服务器跟客户端的 socket 连接断开。 (流式传输)
基本定义
| 类型 | 定义 |
|---|---|
| 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协议格式
FLV = FLV header + FLV file body
FLV file body = PreviousTagSize0 + Tag1 + PreviousTagSize1 + Tag2 + ... + PreviousTagSizeN-1 + TagN
以下使用flv.js连接服务器并抓包所得
FLV header
HTTP-FLV无法直接抓取,故只能查看TCP的包体
其中,0x46, 0x4c, 0x56分别表示FLV为协议魔数,后跟0x01表示版本号;
后跟0b00000101 = 0x05 表示TypeFlagsReserved、TypeFlagsAudio、TypeFlagsReserved、TypeFlagsVideo; 之后跟了DataOffset = 0x00000009 = 9标识了header的byte长度
总而言之,FLV协议的头及其简单
FLV body
FLV file body很有规律,由一系列的TagSize和Tag组成,其中:
- PreviousTagSize0 总是为0;
- tag 由tag header、tag body组成;
- 对FLV版本1,tag header固定为11个字节,因此,PreviousTagSize(除第1个)的值为 11 + 前一个tag 的 tag body的大小;
FLV PreviousTagSizeN
这个字段就像名字,主要用于标识前一个Tag的长度
FLV tag
tag = tag header + tag body
tag header
tag header如下,总共占据11个字节:
(查资料时发现网上有些资料都有错误,缺少一些字段,导致和抓包结果与文档注释对不上,故修正结果如下)
| 字段 | 字段类型 | 字段含义 | |
|---|---|---|---|
| Reserved | UB[2] | ||
| Filter | UB[1] | ||
| TagType | UB5 | 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 | |
| AudioTagHeader | IF TagType==8 | AudioTagHeader | |
| VideoTagHeader | IF TagType==9 | VideoTagHeader | |
| EncryptionHeader | IF Filter==1 | EncryptionTagHeader | |
| FilterParams | IF Filter==1 | FilterParams | |
| Data | IFTagType ==8 AUDIODATA; IF TagType == 9 VIDEODATA;IF TagType == 18 SCRIPTDATA | Data |
继续使用之前的抓包图,(先把flv header抹掉
首先,0x00000000 = 0 为PreviousTagSize0(默认为零);0x12 = 0b000 10010, 前三位为Reserved + Filter,后五位为TagType; DataSize = 0x000183; Timestamp = 0x003a98(有误,待查证); TimestampExtened = 0x00; StreamID = 0x000000; 之后就是具体的数据信息了
tag body
script data
当TagType == 18时,后面是SCRIPTDATA
Script Data Tags通常用来存放跟FLV中音视频相关的元数据信息(onMetaData),比如时长、长度、宽度等。它的定义相对复杂些,采用AMF(Action Message Format)封装了一系列数据类型,比如字符串、数值、数组等。
下面是ScriptTagBody的一种
ScriptTagBody = Name(ScriptDataValue) -> Value(ScriptDataValue); 更准确来说,ScriptData使用的是AMF来组织,具体类型可查询本文在上方的PDF
onMetaData的格式
第一个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-7个字节:0x0008,表示长度为8个字节;
- 第8-13个字节:0x6475 7261 7469,表示 duration 这个字符串;
- 第14个字节:0x00,表示为数值类型;
- 第15...个字节:0x...,表示具体的时长;
具体应该去查看AMF0相关文章
aduio data
当TagType == 8时,后面是 AudioTagHeader + AUDIODATA
AudioTagHeader存放了音频的元信息
AUDIODATA则是具体的音频负载
如果SoundFormat == 10的话,则数据是 AACAUDIODATA,否则为数据
在AACAUDIODATA中,如果AACPackType==0,则表示 AudioSpecificConfig;否则为AAC行数据
伪代码如下:参考这里
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 data
当TagType == 9时,后面是 VideoTagHeader + VIDEODATA
如果 CodecID == 7,则为AVCVIDEOPACKET
在AVCVIDEOPACKET中,如果 AVCPacketType == 0,则为AVCDecoderConfigurationRecord