场景
HTTP-FLV: 基于HTTP流式IO传输FLV,依赖浏览器支持播放FLV。但是由于同源的限制问题,浏览器只能播放六路视频,因此采用WebSocket-FLV
WebSocket-FLV: 基于WebSocket传输FLV,依赖浏览器支持播放FLV。WebSocket建立在HTTP之上,建立WebSocket连接前还要先建立HTTP连接。
视频参数代码
H264
SPS十六进制数值如下:
67 4d 00 1e 96 35 41 60 24 d3 70 10 10 14 00 00 1c 20 00 05 7e 40 10
char* pCodecTagData = (char*)calloc(512, sizeof(char));
pCodecTagData[0] = 0x17;
pCodecTagData[1] = 0x00;// AVC sequence header
pCodecTagData[2] = 0x00;
pCodecTagData[3] = 0x00;
pCodecTagData[4] = 0x00;// composition time
pCodecTagData[5] = 0x01;/* version */
pCodecTagData[6] = m_strSPS[1]; /* profile */
pCodecTagData[7] = m_strSPS[2];/* profile compat */
pCodecTagData[8] = m_strSPS[3]; /* level */
pCodecTagData[9] = 0xff;/* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */
pCodecTagData[10] = 0xe0 | nSPSNumber;/* 3 bits reserved (111) + 5 bits number of sps */
avio_wb16(&pCodecTagData[11], m_strSPS.length());
memcpy(&pCodecTagData[13], m_strSPS.c_str(), m_strSPS.length());
pCodecTagData[13 + m_strSPS.length()] = nPPSNumber;
avio_wb16(&pCodecTagData[14 + m_strSPS.length()], m_strPPS.length());
memcpy(&pCodecTagData[16+ m_strSPS.length()], m_strPPS.c_str(), m_strPPS.length());
int nCodecLen = 16 + m_strSPS.length() + m_strPPS.length();
H265
SPS十六进制数值如下:
42 01 01 01 60 00 00 03 00 b0 00 00 03 00 00 03 00 5a a0 05 82 00 90 58 da e4 93 2f 4d c0 40 40 40 20
std::string strData;
strData.push_back(0x1c);
strData.push_back(0x00);
strData.push_back(0x00);
strData.push_back(0x00);
strData.push_back(0x00);
//8bit configurationVersion (always 0x01)
strData.push_back(0x01);
//8bit [2bit general_profile_space 1bit general_tier_flag 5bit general_profile_idc]
strData.push_back(m_strSPS[1]);
//32bit general_profile_compatibility_flags
strData.push_back(m_strSPS[2]);
strData.push_back(m_strSPS[3]);
strData.push_back(m_strSPS[4]);
strData.push_back(m_strSPS[5]);
//48bit general_constraint_indicator_flags NULL nothing deal in rtmp
strData.push_back(m_strSPS[6]);
strData.push_back(m_strSPS[7]);
strData.push_back(m_strSPS[8]);
strData.push_back(m_strSPS[9]);
strData.push_back(m_strSPS[10]);
strData.push_back(m_strSPS[11]);
//8bit general_level_idc
strData.push_back(m_strSPS[12]);
//48bit NULL nothing deal in rtmp
//4 reserved('1111') 12 min_spatial_segmentation_idc
strData.push_back(0x00);
strData.push_back(0x00);
//6bit reserved('111111') 2bit parallismType
strData.push_back(0x00);
//5bit reserved('111111') 2bit chromaFormat
strData.push_back(0x00);
//5bit reserved('111111') 3bit bitDepthLumaMinus8
strData.push_back(0x00);
//6bit reserved('111111') 3bit bitDepthChromaMinus8
strData.push_back(0x00);
// bit(16) avgFrameRate;
strData.push_back(0x00);
strData.push_back(0x00);
/* bit(2) constantFrameRate */
/* bit(3) numTemporalLayers */
/* bit(1) temporalIdNested */
/* bit(2) lengthSizeMinusOne*/
strData.push_back(0x83);
/* unsigned int(8) numOfArrays; 03 */
strData.push_back(0x03);
WebSocket 的诞生
做客户端开发时,接触最多的应用层网络协议,就是 HTTP 协议,而今天介绍的 WebSocket,下层和 HTTP 一样也是基于 TCP 协议,这是一种轻量级网络通信协议,也属于应用层协议。
WebSocket 与 HTTP/2 一样,其实都是为了解决 HTTP/1.1 的一些缺陷而诞生的,而 WebSocket 针对的就是「请求-应答」这种"半双工"的模式的通信缺陷。
「请求-应答」是"半双工"的通信模式,数据的传输必须经过一次请求应答,这个完整的通信过程,通信的同一时刻数据只能在一个方向上传递。它最大的问题在于,HTTP 是一种被动的通信模式,服务端必须等待客户端请求才可以返回数据,无法主动向客户端发送数据。
这也导致在 WebSocket 出现之前,一些对实时性有要求的服务,通常是基于轮询(Polling)这种简单的模式来实现。轮询就是由客户端定时发起请求,如果服务端有需要传递的数据,可以借助这个请求去响应数据。轮询的缺点也非常明显,大量空闲的时间,其实是在反复发送无效的请求,这显然是一种资源的损耗。
虽然在之后的 HTTP/2、HTTP/3 中,针对这种半双工的缺陷新增了 Stream、Server Push 等特性,但是「请求-应答」依然是 HTTP 协议主要的通信方式。
WebSocket 协议是由 HTML5 规范定义的,原本是为了浏览器而设计的,可以避免同源的限制,浏览器可以与任意服务端通信,现代浏览器基本上都已经支持 WebSocket。
虽然 WebSocket 原本是被定义在 HTML5 中,但它也适用于移动端,尽管移动端也可以直接通过 Socket 与服务端通信,但借助 WebSocket,可以利用 80(HTTP) 或 443(HTTPS)端口通信,有效的避免一些防火墙的拦截。
WebSocket 是真正意义上的全双工模式,也就是我们俗称的「长连接」。当完成握手连接后,客户端和服务端均可以主动的发起请求,回复响应,并且两边的传输都是相互独立的。
WebSocket 的特点
WebSocket 的数据传输,是基于 TCP 协议,但是在传输之前,还有一个握手的过程,双方确认过眼神,才能够正式的传输数据。
WebSocket 的握手过程,符合其 "Web" 的特性,是利用 HTTP 本身的 "协议升级" 来实现。
在建立连接前,客户端还需要知道服务端的地址,WebSocket 并没有另辟蹊径,而是沿用了 HTTP 的 URL 格式,但协议标识符变成了 "ws" 或者 "wss",分别表示明文和加密的 WebSocket 协议,这一点和 HTTP 与 HTTPS 的关系类似。
ws://cxmydev.com/some/path
ws://cxmydev.com:8080/some/path
wss://cxmydev.com:443?uid=xxx
Websocket的世界
通信协议格式是WebSocket格式,服务器端采用Tcp Socket方式接收数据,进行解析,协议格式如下:
首先我们需要知道数据在物理层,数据链路层是以二进制进行传递的,而在应用层是以16进制字节流进行传输的。
第一个字节:
FIN:1位,用于描述消息是否结束,如果为1则该消息为消息尾部,如果为零则还有后续数据包;
RSV1,RSV2,RSV3:各1位,用于扩展定义的,如果没有扩展约定的情况则必须为0
OPCODE:4位,用于表示消息接收类型,如果接收到未知的opcode,接收端必须关闭连接。长连接探活包就是这里标识的。
OPCODE定义的范围:
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
第二个字节以及以后字节:
MASK:1位,用于标识PayloadData是否经过掩码处理,客户端发出的数据帧需要进行掩码处理,所以此位是1。数据需要解码。
Payload length === x,如果
如果 x值在0-125,则是payload的真实长度。
如果 x值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。
如果 x值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。
此外,如果payload length占用了多个字节的话,payload length的二进制表达采用网络序(big endian,重要的位在前)。
参考
WebSocket协议理解-数据包格式解析 - zhangmingda - 博客园 (cnblogs.com)
标签:pCodecTagData,WebSocket,H264,H265,strData,back,strSPS,push From: https://blog.51cto.com/fengyuzaitu/9232753