httpflv 格式分析
Httpflv协议分析
Httpflv是一种将rtmp等负载信息携带在http协议之上的码流传递协议,特性是可以更好的穿透防火墙。取流的时候需要向服务端发送一个http请求,请求包格式如下:
GET /live/2778_00_1 HTTP/1.1
Host: 42.48.29.11:1935
Accept: */*
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36
X-Requested-With: ShockwaveFlash/32.0.0.142
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
响应包:
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/octet-stream
Server: Microsoft-HTTPAPI/2.0
Date: Thu, 21 Feb 2019 06:52:01 GMT
20
FLV ¯
类似于websocket,httpflv也是利用http协议做了连接,然后丢弃掉http协议直接利用该tcp通道进行码流传输。需要注意的是,httpflv传输码流的部分中间会有 \r\n**\r\n来分割一个tagsize和它的tagheader所以格式解析需要注意。
PS:大部分传输会采用http chunked 方法,将码流分包传输。chunk方式
\r\n[chunksize]\r\n[chunkdata]
Flv格式解析:flv文件格式主要包含flv文件头和flv文件体两部分。如图:
其中flv文件头占9个字节,分四个字段。字段定义说明如下:
typedef struct FLV_HEADER
{
BYTE btSignature[3];//文件标记 总是以”flv”开头 0x46 0x4c 0x56
BYTE btVersion;// flv版本说明
BYTE btFlags;//0x01 video,0x04 audio,0x05 video and audio
BYTE btDataOffset[4];//表示整个header的长度。通常是9
};
文件体说明:
文件体由一系列的tag组成,其中每个tag前面又包含了Previous Tag size字段,占用4个字节,表示前面一个tag的大小。组织如图:
每个tag又分为tag Header和tag data两个部分。Tag header占11个字节,分5个字段
typedef struct TAG_HEADER
{
BYTE btPreviousTagSize[4];//前面一个tag长度
BYTE btTagType;//0x08视频 0x09视频 0x12脚本
BYTE btDataSize[3];//负载数据包长
BYTE btTimeStamp[3];//时间戳
BYTE btReserved;//时间戳扩展
BYTE btStreamID[3];
};
负载数据格式介绍—这里只做h264格式数据分析:
第一个数据包分析:第一个字节是视频参数,前后4位分别表示两个字段,前4位表示帧类型,1表示I帧,2p帧..,后4位表示编码ID,7表示AVC(h264).
第二个字节表示ACVPacketType,00 表示第一个包,01表示之后连续的包,02结束包,后面三个字节表示时间戳。
接下来是sps和pps
Sps和pps的作用说明:
在h264的各类语法元素中,sps中的信息至关重要,如果其中的数据丢失可能会导致解码过程失败,sps全名Sequence paramater set,又称作序列参数集,sps中保留了一组编码视频序列的全局参数,一般sps和pps位于整个编码流的起始位置,字段说明如下:
- profile_idc :占用一个字节,标志当前h264码流的profile
66 baseline profile
77 main profile
88 extended profile
- level_idc 标志当前码流的level,编码level定义了某种条件下最大视频分辨率,最大视频帧率等参数
- Seq_parametrer_set_idc 当前序列参数id
- Log2_max_frame_num_minus4 用于计算maxframenum值
- Pic_order_cnt_type 表示解码picture order count的方法
- Log2_max_pic_order_cnt_lsb_minus4 用于计算MaxpicOrderCntLsb的值,用于表示poc上限。
- Max_num_ref_frames 表示参考帧的最大数目
- Gaps_in_frame_num_value_allowed_flag 说明frame_num是否允许不连续的值
- Pic_width_in_mbs_minus1 用于计算图像宽度
- Pic_height_in_map_units_minus1 用以度量视频中一帧图像的高度
- Frame_mbs_only_flag 说明宏块的编码方式,0 宏块可能为帧编码或者场编码,为1宏块都采用帧编码
- Mb_adaptive_frame_field_flag 标志位,说明是否采用宏块级的帧场自适应编码。
- Direct_8x8_inference_flag 标记位,用于B_Skip,B_Direct模式运动适量
- Frame_cropping_flag 标记位 是否对输出的图像进行裁剪
- Vui_parameters_present_flag 标记位 说明sps中是否存在VUI信息。
Pps是类似于sps的参数集合为图像参数集,保留在另一个Nal Unit中,在封装上pps通常与sps一起,保留在码流头部。
从第二帧开始tag包格式如下:
跳过tag头,数据部分前5个字节是固定包头,分三个字段frametype1字节,avcPackettype1字节,和Composition Time3个字节,然后主要分两个部分,第一部分4个字节,表示NALU len,第二部分是NALU Data。格式如图
至此flv文件格式分析完毕。接下来做h264的格式分析
H264码流一般分两种:字节流格式和rtp包格式,rtp包格式里,NALU并不需要一个StartCode来进行标记,只在NALU开始的地方用,若干字节表示其长度。这里我主要分析字节流
一个原始的h264由StartCode,NaluHeader,NaluPayload三部分组成,
StartCode用于标记一个NALU单元的开始,必须是0x00 0x00 0x00 0x01或者0x00 0x00 0x01开始。NaluHeader占一个字节,分三个字段,forbidden_bit(1bit),nal_reference_bit(2bit),nal_uint_type(5bit),
forbidden_bit 是禁止位,nal_reference_bit表示当前NAL的优先级,值越大其重要程度越大,nal_uint_type说明如图:
对于负载数据部分主要就是原始编码数据的解析。负载部分为RBSP,它主要包含两个部分SODB,RBSP尾部。大部分的RBSP尾部只占一个字节,分两个字段rbsp_stop_one_bit占1位,值为1,其他为rbsp_alignment_zero值为0。另外一种当NALU类型为条带时,增加一个或多个0x0000.所以按照以上,只要去掉尾部rbsp,就可以拿到SODB,然后对照类型做对应的解析