We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
看了一个简单的 RTMP 服务(threaded_rtmp_server)代码,在比较高的封装层级了解了数据的处理与转发, 之前学习 RTMP 协议,结合网上的文章与自己抓包采集到的数据。
推流使用的:FFmpeg
RTMP 服务使用的:threaded_rtmp_server https://github.com/KallDrexx/rust-media-libs/tree/master/examples/threaded_rtmp_server
Wireshark 抓包文件:FFmpeg推送日志.pcapng
FFmpeg推送日志.pcapng
ffmpeg -re -i salv.mp4 -c copy -f flv rtmp://192.168.3.188:1935/live/stream
Wireshark 过滤参数
ip.addr == 192.168.3.188 && !(tcp.port == 22) && not tcp.len==0
过滤出和指定 IP 通信的数据,过滤掉 22 端口及 tcp 长度为 0 的心跳数据。
握手
由图可知,客户端发送 C0 + C1,服务端回复 S0 + S1 + S2,客户端回复 C2,握手完成。
wireshark rtmp handshake
协议握手规则:客户端发起 C0,C1 服务端回复 S0,S1,当客户端收到 S0 和 S1 后发送 C2,当服务端收到 C0 和 C1 后发送 S2,当双方各受到 C2,S2 后握手完成,实现时为了降低延时提升效率,就像上图看到的,C0 + C1 一起发送,S0 + S1 + S2 一起回复。
握手时做了两件事儿,确定了协议版本号及发送随机数据校验网络可达性。
握手包的格式分为简单握手和复杂握手,本次的示例是简单握手。
上图是客户端发起握手请求的 C0 + C1 数据,根据协议,C0 就一个字节,最前边那个 03,接着紫色框中是四字节的 time 字段,一般都为 0,橙色内的四个字节为 zero 字段,根据协议来说,也应该用 0 来填充,看起来 FFmpeg 没有完全按照协议实现,不过没什么影响。后续的数据就是 1528 个字节的随机数了。
继续查看 S0 + S1 + S2 服务端返回的数据
看一些网上关于 RTMP 协议解析的文章写,S2 的随机数据部分应该为 C1 的随机数据。可是根据抓包看到的数据,C1 随机数据开头部分是 f7 78 55 ... 而 S2 的随机数据起始为 c6 62 04 ... 显然是不同的,那只有一个解释,客户端没有对 C1 与 S2 的数据部分进行校验。
最后看一下客户端回复的 C2 数据
依旧跳过前8个字节(time + time2),可以看到 C2 回复的包符合规范的要求,把服务端生成的 S1 随机数据部分 65 aa 37... 按照规范进行了回复,也难怪,自己可以不校验偷懒,C2 要是不规范,服务端进行校验握手就失败了。
PS:规范和实现存在偏差无处不在,能用就行呗的理论果真世界通用。
RTMP 中的块内容由 RTMP Header 和 RTMP Body 组成,RTMP Header 由基本头和消息头扩展时间戳组成。
阅读块流格式更详尽的解释,访问:https://chenlichao.gitbooks.io/rtmp-zh_cn/content/5.3-chunking.html
直接看抓包的数据,这是一个通知对端,其发送的数据块最大大小的协议控制消息。
上图绿色方框中的数据就是基础头,块类型决定块消息头的格式(上图橙色框部分),总共有四种,此处的块消息头类型为 0,表示块消息头共占用 11 个字节(不包含基础头),也就是 00 00 00 00 00 04 01 00 00 00 00 。
上图为块消息头为0的详细格式,可以跟抓包数据进行对照,其它类型详细格式查看上边的文档网址。
继续看上边的抓包图中的绿色框内部分,它此时的长度为 1 字节(8位),前两位 00 表示消息块的格式类型,后 6 位表示 CS_ID,值为 2,可以得到两个信息,即基本头长度为一个字节,这是一条控制消息。
需要注意,基本头的长度不是固定的,可能为1,2,3个字节,如果后 6 位的值为 0,那么基本头的长度为 2 个字节,后 6 位值为 1,则基本头长度为 3 个字节。如果后 6 位的值为 2 ,则表示数据用于协议控制消息和命令。
紫色圆框内的为块流数据部分,00 00 10 00 值为 4096,因为 message type id 值为 1,根据协议控制消息类型,它是设置块最大大小的控制命令。
既然了解了 CS_ID,那就有必要进一步的知道流的数量和范围。
块流ID(Chunk Stream ID)用于标识一个数据流,比如通过 RTMP 协议推送一个视频流,在持续推送的过程,它的块流ID是不变的。
RTMP最多支持65597个流。
接下来详细的计算一下这个数量是怎么计算出来的。
1)基本头长度为 1 字节
前 2 位用于表示消息头格式类型,后 6 位能用来表示 CS_ID,别忘记 0,1,2 上边说了有特殊意义。
6 位能表示 2^6 = 64 个 CS_ID,排除特殊的 3 个,即 1 字节的基本头能最多能容纳 61 个流,CS_ID 范围为 [3, 63]
2)基本头长度为 2 字节
第一个字节的后 6 位为 000000,表示基本头长度为 2 字节长度,第二个字节 8 位,2^8 = 256 个 CS_ID,范围 [0, 255],因为和基本头长度为 1 位表示的 CS_ID 重复了,所以整体要加 64,即两个字节情况下,CS_ID 的范围为 [64, 319]
3)基本头长度为 3 字节
第一个字节的后 6 位为 000001,表示基本头长度为 3 字节长度,后两个字节都可以表示流,2^16 = 65536 个 CS_ID,范围 [0, 65535],同理,跟 1 字节的表示重复,整体加 64,即三字节情况下,CS_ID 的取值范围为 [64, 65599]
可以看到三个字节包含了两个字节的情况,即 [64, 319] 的 CS_ID 用两个字节或三个字节都能够表示,三个字节长度的基本头,当第二个字节全为 0 的,其效果等同于二字节长度基本头,协议就是这么设计的,可能为了简化计算和理解,毕竟也不差那 256 个 ID。
不过我们在写程序的时候,应该合理的进行选择使用,毕竟能用两个字节的时候为什么要用三个字节呢~
协议实现方应该用能够承载块流ID的最短表示法来表示块流ID。
RTMP 协议最大能支持的流数量此时可以很容易计算出来:[3, 65599] = 65597 个。
回到协议控制消息,在正式推送媒体数据之前,必然有一些命令请求,协商编码之类的操作,协议控制消息就是做这个用的。
RTMP 块流使用消息类型 ID 1、2、3、5、6 作为控制消息。这些消息包含了必要的 RTMP 块流协议信息。
这些协议控制消息必须使用 0 作为消息流ID(作为已知的控制流ID),同时使用 2 作为块流ID。协议控制消息接收立即生效; 解析时,时间戳字段被忽略。
类型为 1 的协议控制消息,在上图的 “Set Chunk Size 4096” 抓包图的时候已将看到了。
另外的几种简要描述如下:
控制消息类型 名称 简要简述 1 设置块大小 用来通知对方新的最大的块大小。 2 中断消息 通知对方可以丢弃指定 CS_ID 块流消息,一般应用关闭前发送。 3 确认消息 客户端和服务器在接收到与接收窗口大小相等的数据后,必须发送应答消息给对方。 5 视窗大小确认 客户端和服务器发送这个消息来通知对方应答窗口的大小。 6 设置对等带宽 客户端或服务端发送该消息来限制对端的输出带宽。
此处没有详细记录控制消息的格式,较为详细的解释参考:https://chenlichao.gitbooks.io/rtmp-zh_cn/content/5.4-protocol-control-message.html
后续查看抓包数据想必会遇到很多控制消息,再详细对照学习。
用户控制消息
在协议控制消息中,消息类型少了 4 类型,这个类型就是用户控制消息。
暂时未按照 Wireshark 抓包顺序阅读,我选择了一个用户控制消息,可以看到在消息头中,消息类型 “User Control Message(0x04)” 为用户控制消息。
在 RTMP Body 中,橙色框中,前边固定的两个字节是 Event Type,Event Data 数据是变长的,在消息头中的 “Body size: 6” 可以知道用户控制消息消息体中的 Event Data 为 4 字节长度,00 00 00 01,值为 1。
以下是摘自 -> 用户控制消息事件 https://chenlichao.gitbooks.io/rtmp-zh_cn/content/7.1-message-types.html
上一张抓包图中,可以看到橙色圈中 Event Type 值为 0,对照表格代表服务端已经准备好,通知客户端可以推流。四个字节的 Event Data 值为 1,表示可用的流ID。
了解了 CS_ID,协议控制消息,用户控制消息等先行知识后,之后还是需要回到 Wireshark 抓包文件,按照顺序继续学习 RTMP 推流流程,掌握更多的细节。
参考:
深入理解rtmp(三)之手把手实现握手协议 https://juejin.im/post/6844904052325613575 rtmp 协议详解 https://www.cnblogs.com/jimodetiantang/p/8974075.html RTMP协议规范 https://chenlichao.gitbooks.io/rtmp-zh_cn/content/ RTMP 英文原版协议文档 https://wwwimages2.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf
The text was updated successfully, but these errors were encountered:
No branches or pull requests
看了一个简单的 RTMP 服务(threaded_rtmp_server)代码,在比较高的封装层级了解了数据的处理与转发, 之前学习 RTMP 协议,结合网上的文章与自己抓包采集到的数据。
环境说明
推流使用的:FFmpeg
RTMP 服务使用的:threaded_rtmp_server
https://github.com/KallDrexx/rust-media-libs/tree/master/examples/threaded_rtmp_server
Wireshark 抓包文件:FFmpeg推送日志.pcapng
FFmpeg推送日志.pcapng
推流命令
Wireshark 过滤参数
过滤出和指定 IP 通信的数据,过滤掉 22 端口及 tcp 长度为 0 的心跳数据。
握手
由图可知,客户端发送 C0 + C1,服务端回复 S0 + S1 + S2,客户端回复 C2,握手完成。
wireshark rtmp handshake
协议握手规则:客户端发起 C0,C1 服务端回复 S0,S1,当客户端收到 S0 和 S1 后发送 C2,当服务端收到 C0 和 C1 后发送 S2,当双方各受到 C2,S2 后握手完成,实现时为了降低延时提升效率,就像上图看到的,C0 + C1 一起发送,S0 + S1 + S2 一起回复。
握手时做了两件事儿,确定了协议版本号及发送随机数据校验网络可达性。
握手包的格式分为简单握手和复杂握手,本次的示例是简单握手。
上图是客户端发起握手请求的 C0 + C1 数据,根据协议,C0 就一个字节,最前边那个 03,接着紫色框中是四字节的 time 字段,一般都为 0,橙色内的四个字节为 zero 字段,根据协议来说,也应该用 0 来填充,看起来 FFmpeg 没有完全按照协议实现,不过没什么影响。后续的数据就是 1528 个字节的随机数了。
继续查看 S0 + S1 + S2 服务端返回的数据
看一些网上关于 RTMP 协议解析的文章写,S2 的随机数据部分应该为 C1 的随机数据。可是根据抓包看到的数据,C1 随机数据开头部分是 f7 78 55 ... 而 S2 的随机数据起始为 c6 62 04 ... 显然是不同的,那只有一个解释,客户端没有对 C1 与 S2 的数据部分进行校验。
最后看一下客户端回复的 C2 数据
依旧跳过前8个字节(time + time2),可以看到 C2 回复的包符合规范的要求,把服务端生成的 S1 随机数据部分 65 aa 37... 按照规范进行了回复,也难怪,自己可以不校验偷懒,C2 要是不规范,服务端进行校验握手就失败了。
PS:规范和实现存在偏差无处不在,能用就行呗的理论果真世界通用。
RTMP 块流
RTMP 中的块内容由 RTMP Header 和 RTMP Body 组成,RTMP Header 由基本头和消息头扩展时间戳组成。
阅读块流格式更详尽的解释,访问:https://chenlichao.gitbooks.io/rtmp-zh_cn/content/5.3-chunking.html
直接看抓包的数据,这是一个通知对端,其发送的数据块最大大小的协议控制消息。
上图绿色方框中的数据就是基础头,块类型决定块消息头的格式(上图橙色框部分),总共有四种,此处的块消息头类型为 0,表示块消息头共占用 11 个字节(不包含基础头),也就是 00 00 00 00 00 04 01 00 00 00 00 。
上图为块消息头为0的详细格式,可以跟抓包数据进行对照,其它类型详细格式查看上边的文档网址。
继续看上边的抓包图中的绿色框内部分,它此时的长度为 1 字节(8位),前两位 00 表示消息块的格式类型,后 6 位表示 CS_ID,值为 2,可以得到两个信息,即基本头长度为一个字节,这是一条控制消息。
需要注意,基本头的长度不是固定的,可能为1,2,3个字节,如果后 6 位的值为 0,那么基本头的长度为 2 个字节,后 6 位值为 1,则基本头长度为 3 个字节。如果后 6 位的值为 2 ,则表示数据用于协议控制消息和命令。
紫色圆框内的为块流数据部分,00 00 10 00 值为 4096,因为 message type id 值为 1,根据协议控制消息类型,它是设置块最大大小的控制命令。
既然了解了 CS_ID,那就有必要进一步的知道流的数量和范围。
块流ID的数量与范围
块流ID(Chunk Stream ID)用于标识一个数据流,比如通过 RTMP 协议推送一个视频流,在持续推送的过程,它的块流ID是不变的。
RTMP最多支持65597个流。
接下来详细的计算一下这个数量是怎么计算出来的。
1)基本头长度为 1 字节
前 2 位用于表示消息头格式类型,后 6 位能用来表示 CS_ID,别忘记 0,1,2 上边说了有特殊意义。
6 位能表示 2^6 = 64 个 CS_ID,排除特殊的 3 个,即 1 字节的基本头能最多能容纳 61 个流,CS_ID 范围为 [3, 63]
2)基本头长度为 2 字节
第一个字节的后 6 位为 000000,表示基本头长度为 2 字节长度,第二个字节 8 位,2^8 = 256 个 CS_ID,范围 [0, 255],因为和基本头长度为 1 位表示的 CS_ID 重复了,所以整体要加 64,即两个字节情况下,CS_ID 的范围为 [64, 319]
3)基本头长度为 3 字节
第一个字节的后 6 位为 000001,表示基本头长度为 3 字节长度,后两个字节都可以表示流,2^16 = 65536 个 CS_ID,范围 [0, 65535],同理,跟 1 字节的表示重复,整体加 64,即三字节情况下,CS_ID 的取值范围为 [64, 65599]
可以看到三个字节包含了两个字节的情况,即 [64, 319] 的 CS_ID 用两个字节或三个字节都能够表示,三个字节长度的基本头,当第二个字节全为 0 的,其效果等同于二字节长度基本头,协议就是这么设计的,可能为了简化计算和理解,毕竟也不差那 256 个 ID。
不过我们在写程序的时候,应该合理的进行选择使用,毕竟能用两个字节的时候为什么要用三个字节呢~
协议实现方应该用能够承载块流ID的最短表示法来表示块流ID。
RTMP 协议最大能支持的流数量此时可以很容易计算出来:[3, 65599] = 65597 个。
协议控制消息
回到协议控制消息,在正式推送媒体数据之前,必然有一些命令请求,协商编码之类的操作,协议控制消息就是做这个用的。
RTMP 块流使用消息类型 ID 1、2、3、5、6 作为控制消息。这些消息包含了必要的 RTMP 块流协议信息。
这些协议控制消息必须使用 0 作为消息流ID(作为已知的控制流ID),同时使用 2 作为块流ID。协议控制消息接收立即生效; 解析时,时间戳字段被忽略。
类型为 1 的协议控制消息,在上图的 “Set Chunk Size 4096” 抓包图的时候已将看到了。
另外的几种简要描述如下:
此处没有详细记录控制消息的格式,较为详细的解释参考:https://chenlichao.gitbooks.io/rtmp-zh_cn/content/5.4-protocol-control-message.html
后续查看抓包数据想必会遇到很多控制消息,再详细对照学习。
用户控制消息
在协议控制消息中,消息类型少了 4 类型,这个类型就是用户控制消息。
暂时未按照 Wireshark 抓包顺序阅读,我选择了一个用户控制消息,可以看到在消息头中,消息类型 “User Control Message(0x04)” 为用户控制消息。
在 RTMP Body 中,橙色框中,前边固定的两个字节是 Event Type,Event Data 数据是变长的,在消息头中的 “Body size: 6” 可以知道用户控制消息消息体中的 Event Data 为 4 字节长度,00 00 00 01,值为 1。
以下是摘自 -> 用户控制消息事件
https://chenlichao.gitbooks.io/rtmp-zh_cn/content/7.1-message-types.html
上一张抓包图中,可以看到橙色圈中 Event Type 值为 0,对照表格代表服务端已经准备好,通知客户端可以推流。四个字节的 Event Data 值为 1,表示可用的流ID。
了解了 CS_ID,协议控制消息,用户控制消息等先行知识后,之后还是需要回到 Wireshark 抓包文件,按照顺序继续学习 RTMP 推流流程,掌握更多的细节。
参考:
深入理解rtmp(三)之手把手实现握手协议 https://juejin.im/post/6844904052325613575
rtmp 协议详解 https://www.cnblogs.com/jimodetiantang/p/8974075.html
RTMP协议规范 https://chenlichao.gitbooks.io/rtmp-zh_cn/content/
RTMP 英文原版协议文档 https://wwwimages2.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf
The text was updated successfully, but these errors were encountered: