网站首页 > 精选教程 正文
0.引言
阅读本文前,可以先阅读前面文章,能够帮助你更好理解本篇文章。文章列表如下:
1.SRS源码常用类介绍
SrsCommonMessage:封装rtmp消息。
SrsPacket:读取到的数据,先存到数据包中。对应的派生类如下:
SrsConnectAppPacket
SrsConnectAppResPacket
SrsCallPacket
SrsCallResPacket
SrsCreateStreamPacket
SrsCreateStreamResPacket
SrsCloseStreamPacket
SrsFMLEStartPacket
SrsFMLEStartResPacket
SrsPublishPacket
SrsPausePacket
SrsPlayPacket
SrsPlayResPacket
SrsOnBWDonePacket
SrsOnStatusCallPacket
SrsBandwidthPacket
SrsOnStatusDataPacket
SrsSampleAccessPacket
SrsOnMetaDataPacket
SrsSetWindowAckSizePacket
SrsAcknowledgementPacket
SrsSetChunkSizePacket
SrsSetPeerBandwidthPacket
SrsUserControlPacket
2.断点调试
输入命令
gdb ./objs/srs
界面如下:
界面如下:
输入命令:
set args -c ./conf/srs.conf
界面如下:
运行
输入命令:
c
r
界面如下:
开启推流命令:
ffmpeg -re -i xxx.flv -t 20 -vcodec copy -acodec copy -f flv -y rtmp://xxx.xxx.xxx.xxx/live/livestream -loglevel 66
断点调试
输入命令
b srs_app_rtmp_conn.cpp:183
b SrsRtmpServer::connect_app(SrsRequest*)
界面如下:
tcp传送的是以chunk数据块的流式数据,到达服务器后把chunk组成message形式,对应更上层的就是packet,这些message都是存放到Srs流媒体服务器中的SrsCommonMessage。获取到message后,Srs流媒体服务器会调用decode_message去解码。源码如下:
根据不同的消息类型,使用不同的处理。
在Srs流媒体服务器源码中,在SrsRtmpConn::service_cycle()函数中,Srs流媒体服务器会发送回复的消息(就是WireShark抓包回应消息)客户端,源码如下:
在客户端会有处理服务器发过来的消息,源码如下:
Srs流媒体服务器通过_result()返回消息,客户端读取stream_id,然后调用gen_publish()给服务器发送消息,客户端ffmpeg源码如下:
可以看到这里,发送的消息,就与WireShark抓包得到的信息是一样。
3.源码介绍
ffmpeg源码中关于RTMP协议的源码在Rtmpproto.c。
还有个是Rtmppkt.h,也是要重点分析。
握手函数rtmp_handshake在Rtmpproto.c文件中,对应源码如下:
在rtmp_open中会调用rtmp_handshake。
创建对应的连接,调用gen_connect(推流端给服务器发送参数)。源码如下:
在前面文章有讲过,收到一定数据后,回应给服务器,使用gen_bytes_read()。源码如下:
ffmpeg源码中,释放码流连接的资源。源码如下:
ffmpeg源码中,客户端向服务器发送一个连接包,源码如下:
对应SRS流媒体服务器,在握手成功后,客户端肯定会发送“connect相关信息”。SRS流媒体服务器源码如下:
SRS流媒体服务器流媒体服务器读取的信息都会存放到SrsRequest* req所指向的内存中。
根据调试消息,输入命令:
print *req
可以看到客户端一些信息,如下界面:
4.抓包分析
通过WireShark抓取拉流客户端数据包,如下界面:
也可以使用tcpdump来抓取服务器回应的数据。
sudo tcpdump -i any port 1935 -XX and dst xxx.xxx.xxx.xxx
Srs流媒体服务器发送一个Publish命令后,就可以开启推流。Srs流媒体服务器,源码如下:
在ffmpeg的源码中,客户端一般处理服务器的消息的函数都是handle_XXX形式的,客户端发送给服务器的函数都是gen_xxx形式。
上面举例的这些函数,在wireshark中都是能抓包看到。如下图:
客户端与SRS流媒体服务器端,handshake握手解决了RTMP一致性的问题。如下界面:
客户端发送数据到SRS流媒体服务器端,使用connect命令连接。如下界面:
Chunk stream Id:接收端根据相同的chunk stream id拼装出message,stream id=3,对应ffmpeg的枚举RTMP_SYSTEM_CHANNEL。
Type ID (即是message type id):如8表示音频,9表示视频,20表示命令消息。命令消息对应ffmpeg源码中的枚举为RTMP_PT_INVOKE。
Stream ID:建立连接后,由服务器返回给客户端。
握?之后先发送?个connect 命令消息,这些信息是以AMF格式发送的,消息的结构如下:
第三个字段中的Command Object中会涉及到很多键值对,可以参考协议的官??档。服务器回应客户端的消息有两种,_result表示接受连接,_error表示连接失败。以下是连接命令对象中使?的名称-值对的描述。
SRS流媒体服务端回应客户端消息,Window Acknowledgement Size。客户端在收到一定字节数后,需要回应服务器。
Type Id:0x05,对应ffmpeg源码中,RTMP_PT_WINDOW_ACK_SIZE。
Window Acknowledgement Size:?于设置窗?确认??,如这?是服务器发送给客户端,每次客户端收到该size就要Acknowledgement(0x3)。
注意:对于ffmpeg源码而言,充当的客户端,在接收到Window Acknowledgement Size的?半后发送确认包(Acknowledgement),ffmpeg对应的枚举是RTMP_PT_BYTES_READ。,以确保对等?可以继续发送?不等待确认。源码如下所示:
static int handle_window_ack_size(URLContext *s, RTMPPacket *pkt)
{
RTMPContext *rt = s->priv_data;
if (pkt->size < 4) {
av_log(s, AV_LOG_ERROR,
"Too short window acknowledgement size packet (%d)\n" ,
pkt->size);
return AVERROR_INVALIDDATA;
}
rt->receive_report_size = AV_RB32(pkt->data);
if (rt->receive_report_size <= 0) {
av_log(s, AV_LOG_ERROR, "Incorrect window acknowledgement si ze %d\n",
rt->receive_report_size);
return AVERROR_INVALIDDATA;
}
av_log(s, AV_LOG_DEBUG, "Window acknowledgement size = %d\n", rt ->receive_report_size);
// Send an Acknowledgement packet after receiving half the maxim um
// size, to make sure the peer can keep on sending without waiti ng
// for acknowledgements.
rt->receive_report_size >>= 1; // 收到?半??就确认
return 0;
}
在实际使用情况,与spec协议中,有些不一样的,如下:
注意:
当客户端作为推流端时,一般即使没有收到服务器的ack,客户端也不会停止码流的推送。
当客户端作为拉流端时,一般即使拉流端不回应ack,服务器也不会停止码流的发送。
如果客户端和服务端都是接收方,收到1/2的Windows size后,需要发送ack给对方。
SRS流媒体服务端给客户端发送Set Peer Bandwidth。
客户端或服务器端发送此消息更新对端(谁发送谁就是这?的对端)的输出带宽。和Window Acknowledgement Size相?,重点是更新。
Set Peer Bandwidth(Message Type ID=6):目的是为了限制对端的输出带宽。接收端接收到该消息后会通过设置消息中的Window ACK Size,来限制已发送但未接受到反馈的消息的??,最终限制发送端的发送带宽。如果消息中的Window ACK Size与上?次发送给发送端的size不同的话,要回馈?个WindowAcknowledgement Size的控制消息。spec协议如下图所示:
Hard(Limit Type=0):接收端应该将Window Ack Size设置为消息中的值。
Soft(Limit Type=1):接收端可以讲Window Ack Size设为消息中的值,也可以保存原来的值(前提是原来的Size?与该控制消息中的Window Ack Size一致)。
Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit type 为0,本次也按Hard处理,否则忽略本消息,不去设置Window Ack Size。
Srs流媒体服务端发送客户端 Set Chunk Size
如下图所示:
Set Chunk Size(Message Type ID=1):设置chunk中Data字段所能承载的最大字节数,默认为128Bytes,通信过程中可以通过发送该消息来设置chunk Size的??(不得?于128Bytes),?且通信双?会各?维护?个chunkSize,两端的chunkSize是独?的。如当A想向B发送?个200B的Message,但默认的chunkSize是128Bytes,因此就要将该消息拆分为Data分别为128Bytes和72Bytes的两个chunk发送,如果此时已经先发送?个设置chunkSize为256Bytes的消息,再发送Data为200Bytes的chunk,本地不再划分Message,B接收到SetChunk Size的协议控制消息时,会调整接受的chunk的Data的??,也不?再将两个chunk组成为?个Message。
注意:在实际工程中,一般会把chunk size设置的很大,如4096Bytes,FFmpeg推流的时候一般设置60*1000,好处就是避免频繁拆包和组包,占用过多的CPU。
在spec协议中,代表Set Chunk Size消息的chunk的Data:
其中第?位必须为0,chunk Size占31个位,最?可代表2147483647=0x7FFFFFFF=231-1,但实际上所有?于16777215=0xFFFFFF的值都?不上,因为chunk size不能?于Message的?度,表示Message的?度字段是?3个字节表示,最?只能为0xFFFFFF。
客户端发送Set Chunk Size给SRS服务端
抓包过程,如下图所示:
客户端发送服务端 releaseStream
抓包过程,如下图所示:
其中liveStream是要处理的流。
客户端发送服务端FCPublish
抓包过程,如下图所示:
客户端发送服务端createStream
抓包过程,如下图所示:
Create Stream:创建传递具体信息的通道,从?可以在这个流中传递具体信息,传输信息单元为Chunk。
当发送完createStream消息之后,解析服务器返回的消息会得到?个stream ID, 这个ID也就是以后和服务器通信的 message stream ID, ?般返回的是1,不固定。
客户端发送服务端_checkbw
服务器并没有做任何处理。
服务端发送客户端_result
服务端向客户端发送_result的应答消息。
客户端发送服务端publish
推流
服务端发送客户端onFCPublish
响应客户端,是否准备好接收流。
查看Srs流媒体服务器的log信息,与WireShark的抓包信息一致。如下。
[2020-07-31 19:48:19.032][Trace][1761][412] RTMP client ip=175.0.54.116, fd=10
[2020-07-31 19:48:19.058][Trace][1761][412] complex handshake success
[2020-07-31 19:48:19.079][Trace][1761][412] connect app, tcUrl=rtmp://111.229.231.225:1935/live, pageUrl=, swfUrl=, schema=rtmp, vhost=111.229.231.225, port=1935, app=live, args=null
[2020-07-31 19:48:19.079][Trace][1761][412] protocol in.buffer=0, in.ack=0, out.ack=0, in.chunk=128, out.chunk=128
[2020-07-31 19:48:19.221][Trace][1761][412] client identified, type=fmle-publish, vhost=111.229.231.225, app=live, stream=livestream, param=, duration=0ms
[2020-07-31 19:48:19.221][Trace][1761][412] connected stream, tcUrl=rtmp://111.229.231.225:1935/live, pageUrl=, swfUrl=, schema=rtmp, vhost=__defaultVhost__, port=1935, app=live, stream=livestream, param=, args=null
[2020-07-31 19:48:19.221][Trace][1761][412] source url=/live/livestream, ip=175.0.54.116, cache=1, is_edge=0, source_id=-1[-1]
[2020-07-31 19:48:19.361][Trace][1761][412] hls: win=60000ms, frag=10000ms, prefix=, path=./objs/nginx/html, m3u8=[app]/[stream].m3u8, ts=[app]/[stream]-[seq].ts, aof=2.00, floor=0, clean=1, waitk=1, dispose=0ms, dts_directly=1
[2020-07-31 19:48:19.361][Trace][1761][412] ignore disabled exec for vhost=__defaultVhost__
[2020-07-31 19:48:19.361][Trace][1761][412] start publish mr=0/350, p1stpt=20000, pnt=5000, tcp_nodelay=0
[2020-07-31 19:48:19.501][Trace][1761][412] got metadata, width=1920, height=832, vcodec=7, acodec=10
[2020-07-31 19:48:19.501][Trace][1761][412] 50B video sh, codec(7, profile=High, level=4, 1920x832, 0kbps, 0.0fps, 0.0s)
[2020-07-31 19:48:19.501][Trace][1761][412] 4B audio sh, codec(10, profile=LC, 2channels, 0kbps, 48000HZ), flv(16bits, 2channels, 44100HZ)
[2020-07-31 19:48:19.514][Trace][1761][412] -> HLS time=76056186ms, sno=2, ts=livestream- 1.ts, dur=0.00, dva=0p
[2020-07-31 19:48:28.567][Trace][1761][412] -> HLS time=86059973ms, sno=2, ts=livestream- 1.ts, dur=0.00, dva=9120p
[2020-07-31 19:48:38.606][Trace][1761][412] -> HLS time=96062310ms, sno=3, ts=livestream- 2.ts, dur=0.00, dva=6320p
[2020-07-31 19:48:39.449][Warn][1761][412][11] VIDEO: stream not monotonically increase, please open mix_correct.
[2020-07-31 19:48:39.450][Trace][1761][412] cleanup when unpublish
[2020-07-31 19:48:39.450][Warn][1761][412][104] client disconnect peer. ret=1007
5.总结
关于源码和抓包分析就讲解到这里,深入分析RTMP协议在实际工程的流程。本篇文章就分析到这里,欢迎关注,转发,点赞,收藏,分享,评论区讨论。
后期关于项目的知识,会在微信公众号上更新,如果想要学习项目,可以关注微信公众号“记录世界 from antonio”
猜你喜欢
- 2024-10-05 连AI都在看《英雄联盟》的游戏直播
- 2024-10-05 为什么一个AI要看《英雄联盟》的游戏直播?
- 2024-10-05 docker指令详解 docker常见指令
- 2024-10-05 rtsp通过ffmpeg+nginx发布成rtmp和http-flv部署文档
- 2024-10-05 OBS直播多平台同时推流解决方法,简单粗暴
- 2024-10-05 RTMP服务器搭建 RTMP服务器搭建
- 2024-10-05 搭建Nginx+rtmp+hls直播推流服务器
你 发表评论:
欢迎- 最近发表
-
- 我的世界光影MOD下载(我的世界光影mod下载安装)
- 我的世界1.7/1.8VoxelMap小地图MOD下载
- 我的世界1.7.10多世界 整合包(我的世界1.7.10forge整合包)
- 我的世界1.8最好用的修改器下载(我的世界1.8最好用的修改器下载安装)
- 我的世界更多弯曲动作MOD下载(我的世界更多弯曲动作mod下载手机版)
- 我的世界龙珠MOD下载(我的世界龙珠模组整合包下载)
- 我的世界1.7.10以太2 下载(我的世界以太2mod1.12.2)
- 我的世界虚拟人生MOD下载分享(我的世界虚拟人生下载安装)
- 我的世界无正版账号的简单联机方法(非网易版,仅适用于局域网)
- “我的语言极限,即是我的世界的极限。” ——《On Java》书籍推荐
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)