SRS总结 - 2
4.RTMP推流到SRS流媒体服务器消息处理
参考:https://www.yuque.com/wahaha-0yfyj/mnfloz/gm3skg?#
https://www.cnblogs.com/jimodetiantang/p/9072455.html
握手成功后,SRS和客户端进行消息交换,对应wiresharek这部分截图:
流程图(文件夹中:srs和客户端建联流程,从connect开始.png)
4.1. connect消息
4.1.1截图
4.1.2. 客户端生成connect消息发送到SRS服务器
对应FFmpeg代码:
gen_connect(s, rt)
static int gen_connect(URLContext *s, RTMPContext *rt)
//部分代码如下:
ff_amf_write_string(&p, "connect");
ff_amf_write_number(&p, ++rt->nb_invokes);
ff_amf_write_object_start(&p);
ff_amf_write_field_name(&p, "app");
ff_amf_write_string2(&p, rt->app, rt->auth_params);
4.1.3. SRS服务端接收connect消息并解析
1. SrsRtmpConn::do_cycle()
2.p (SrsRequest* req)
//解析connect消息,从流式数据->chunk->message->packet
3. SrsProtocol::decode_message—>SrsProtocol::do_decode_message
从2步骤如何到3
srs_error_t expect_message(SrsCommonMessage** pmsg, T** ppacket)
if ((err = decode_message(msg, &packet)) != srs_success)
4.在do_decode_message中解析packet代码。
5. SrsRtmpServer: expect_message
位于srs_rtmp_stack.hpp。
这是一个定义在 SrsRtmpServer 类中的模板函数。该函数指定了一个期待接收的消息,若接收到的是其他消息则丢弃,直到获取到指定的消息为止。
接着调用 SrsProtocol 类定义的同名模板函数 expect_message.
在分析 SrsProtocol: expect_message 之前,先看 SrsRtmpServer 类中类型为 SrsProtocol 类的成员 protocol 是如何构造的。
6. SrsProtocol 的构造函数
位于 srs_rtmp_stack.hpp。
SrsProtocol 类提供了 RTMP 消息协议服务,从 RTMP chunk stream 中接收 RTMP 消息,或者通过 RTMP chunk stream 发送 RTMP 消息。
7. SrsFastBuffer 类为协议提供字节缓存,将从 socket 接收到的数据存放到该类中,然后解码为 RTMP 消息。
8.得到一些连接信息,回复没有找到。
4.2. Window Acknowledgement Size消息
SRS服务器发送Window Acknowledgement Size消息给客户端,默认250000。250000从配置文件得到out_ack_size,没有配置文件则用默认值250000。ZLMediaKit设置为sendAcknowledgementSize(5000000);
4.2.1. Window Acknowledgement Size消息解析
1.wiresharek截图
2.Type Id:0x5 对应ffmepg的RTMP_PT_WINDOW_ACK_SIZE
3.Window Acknowledgement Size⽤于设置窗⼝确认⼤⼩,⽐如这⾥是服务器发送给客户端的,当客户端收到该size就要Acknowledgement (0x3)。
4.对于ffmpeg,在接收到Window Acknowledgement Size的⼀半后发送确认包(Acknowledgement),以确保对等⽅可以继续发送⽽不等待确认。
5.注意点:
- 客户端作为推流端时,⼀般即使没有收到服务器的ack,客户端也不会停⽌码流的推送。
- 当客户端作为拉流端时,⼀般即使拉流端不回应ack,服务器也不会停⽌码流的发送。
- 但彼此如果作为接收⽅时,收到1/2Windows size的数据后对会ack对⽅。
4.2.2. Window Acknowledgement Size消息代码分析
SrsRtmpConn::service_cycle()---rtmp->set_window_ack_size(out_ack_size)
SrsRtmpServer::set_window_ack_size(int ack_size)-- protocol->send_and_free_packet(pkt, 0)
SrsProtocol::send_and_free_packet--do_send_and_free_packet
1.rtmp header如何来?
do_send_and_free_packet--packet->to_msg-- shared_msg->create—》SrsSharedPtrMessage::create
4.3. Peer Bandiwdth消息
srs服务器发送Peer Bandiwdth消息给客户端,默认2500000。
4.3.1. Peer Bandiwdth消息解析
1.wiresharek截图:
2.客户端或服务器端发送此消息更新对端的输出带宽,作用是限制对端的输出带宽。和Window Acknowledgement Size相⽐,重点是更新。
3.Type ID:0x6 对应ffmpeg的RTMP_PT_SET_PEER_BW
4.如果消息中的Window ACK Size与上⼀次发送给发送端的size不同的话要回馈⼀个Window Acknowledgement Size的控制消息
- 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
5. 与Window Acknowledgement Size相比,只是多了个Limit Type
4.3.2.Peer Bandiwdth消息代码分析
流程同Window Acknowledgement Size,
SrsRtmpConn::service_cycle()---rtmp->set_peer_bandwidth((int)(2.5 * 1000 * 1000), 2))
SrsRtmpServer:: set_peer_bandwidth-- protocol->send_and_free_packet(pkt, 0)
SrsProtocol::send_and_free_packet--do_send_and_free_packet
4.4. set Chunk Size消息
srs服务器发送Set Chunk Size消息给客户端
4.4.1. Set Chunk Size消息解析
1.wiresharek截图:
2.Set Chunk Size(Message Type ID=1):设置chunk中Data字段所能承载的最⼤字节数,默认为128B,通信过程中可以通过发送该消息来设置chunk Size的⼤⼩(不得⼩于128B),⽽且通信双⽅会各⾃维护⼀个chunkSize,两端的chunkSize是独⽴的。
- ⽐如当A想向B发送⼀个200B的Message,但默认的chunkSize是128B,因此就要将该消息拆分为Data分别为128B和72B的两个chunk发送,如果此时先发送⼀个设置chunkSize为256B的消息,再发送Data为200B的chunk,本地不再划分Message,B接受到Set Chunk Size的协议控制消息时会调整的接受的chunk的Data的⼤⼩,也不⽤再将两个chunk组成为⼀个Message。
- 在实际中⼀般会把chunk size设置的很⼤,有的会设置为4096,FFMPEG推流的时候设置的是 60*1000,这样设置的好处是避免了频繁的拆包组包,占⽤过多的CPU。
3.以下为代表Set Chunk Size消息的chunk的Data:
其中第⼀位必须为0,chunk Size占31个位,最⼤可代表2147483647=0x7FFFFFFF -1,但实际上所有⼤于16777215=0xFFFFFF的值都⽤不上,因为chunk size不能⼤于Message的度,表示Message的长度字段是⽤3个字节表示的,最⼤只能为0xFFFFFF。
在代码里是从配置文件中设置的。没有配置时返回#define SRS_CONSTS_RTMP_SRS_CHUNK_SIZE 60000。
4.4.2. Set Chunk Size消息代码分析
流程同Window Acknowledgement Size和Peer Bandiwdth
SrsRtmpConn::service_cycle()---rtmp->set_chunk_size(chunk_size)
SrsRtmpServer:: set_chunk_size-- protocol->send_and_free_packet(pkt, 0)
SrsProtocol::send_and_free_packet--do_send_and_free_packet
4.5.SRS服务器发送response_connect_app消息给客户端
SRS服务器是会发送response_connect_app消息给客户端用来响应客户端发送的connect消息,但这部分wiresharek是没有抓取到此包的。
通过tcpdump命令查看包信息:
sudo tcpdump -i any port 1935 -XX and dst 36.112.32.2 #不是公网地址
流程同Window Acknowledgement Size、Peer Bandiwdth和Set Chunk Size
SrsRtmpConn::service_cycle()---rtmp->response_connect_app
SrsRtmpServer::response_connect_app-- protocol->send_and_free_packet(pkt, 0)
SrsProtocol::send_and_free_packet--do_send_and_free_packet
4.6.客户端发送releaseStream、FCPublish、CreateStream和checkbw消息给SRS服务器
客户端还需要发送 releaseStream , FCPublish,CreateStream和checkbw消息给SRS服务器,具体可以参考ffmpeg的handle_invoke_result函数和gen_check_bw函数。
4.6.1 releaseStream消息
客户端发送releaseStream消息给srs服务器。
对应的值liveStream是要处理的流,作用是生成'releaseStream'调用并发送到服务器,让服务器为媒体流释放一些通道。
1.客户端生成releaseStream消息发送给SRS服务端,函数为int gen_release_stream (URLContext *s, RTMPContext *rt)。
2.SRS服务端接收到后会解析成对应的packet,然后返回_result消息。如下wiresharek所示
3.SRS服务器解析releaseStream消息代码:
do_decode_message(SrsMessageHeader& header, SrsBuffer* stream, SrsPacket** ppacket)
else if (command == RTMP_AMF0_COMMAND_FC_PUBLISH) {
*ppacket = packet = new SrsFMLEStartPacket();
return packet->decode(stream);
}
4.根据packet类型返回对应响应,ReleaseStream/PublishStream/FCPublish/FCUnpublish都是用SrsRtmpServer::identify_fmle_publish_client函数返回_result消息。
4.6.2客户端发送FCPublish消息给srs服务器
1.FCPublish消息作用是使服务器为接收媒体流做好准备。
- 客户端生成FCPublish消息发送给SRS服务器 ,函数为int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
- SRS服务器解析FCPublish消息:
4. 调用栈:
FCPublish、createStream、publish、onFCPublish、onStatus都是通过SrsRtmpServer::start_fmle_publish函数进行处理
5.SRS服务器发送_result消息给客户端进行响应FCPublish消息,见如上代码:SrsRtmpServer::identify_fmle_publish_client
4.7客户端发送CreateStream消息给srs服务器
4.7.1协议解析
1.Create Stream:创建传递具体信息的通道,从⽽可以在这个流中传递具体信息,传输信息单元为Chunk。
2.当发送完createStream消息之后,解析服务器返回的消息会得到⼀个stream ID,这个ID也就是以后和服务器通信的 message stream ID,⼀般返回的是1,不固定。
4.7.2. CreateStream消息代码解析
1.客户端生成CreateStream消息发送给SRS服务器,函数为
static int gen_create_stream(URLContext *s, RTMPContext *rt)。
2.SRS服务器解析CreateStream消息:
else if (command == RTMP_AMF0_COMMAND_CREATE_STREAM)
- SRS服务器发送_result消息给客户端进行响应CreateStream消息,见如上代码:SrsRtmpServer::identify_fmle_publish_client
- 大致流程
SrsRtmpConn::service_cycle()🡪SrsRtmpConn::stream_service_cycle🡪 SrsRtmpServer::start_fmle_publish(expect_message<SrsCreateStreamPacket>)🡪 srs_rtmp_stack.hpp: expect_message🡪 SrsProtocol::decode_message🡪 SrsProtocol::do_decode_message🡪 packet = new SrsCreateStreamPacket(); 🡪 SrsRtmpServer::start_fmle_publish(SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket)
4.8服务器向客户端发送onBWDone消息
在客户端向服务器端发送CreateStream消息后,服务器向客户端发送onBWDone消息。只在SrsOnBWDonePacket构造函数中赋值。当带宽测试完成后,通知客户端,实际代码中没有发现什么特殊代码。
SrsRtmpConn::service_cycle--rtmp->on_bw_done()—》SrsRtmpServer::on_bw_done--protocol->send_and_free_packet(pkt, 0)
4. 9客户端发送checkbw消息给srs服务器
1.客户端生成检查带宽消息发送给服务器。
客户端生成checkbw消息发送给SRS服务器,客户端ffmpeg函数 gen_check_bw。
2.执行过程过程
SrsRtmpConn::cycle—service_cycle()—》SrsRtmpConn::service_cycle-- stream_service_cycle()—》
SrsRtmpConn::stream_service_cycle()--rtmp->start_fmle_publish(info->res->stream_id)—》SrsRtmpServer::start_fmle_publish-- expect_message<SrsPublishPacket>(&msg, &pkt)—》SrsRtmpServer- expect_message—》SrsProtocol- expect_message-- recv_message(&msg)🡪 SrsProtocol::recv_message-- on_recv_message(msg)🡪 SrsProtocol::on_recv_message🡪 switch (msg->header.message_type)—default🡪 SrsProtocol- expect_message-( recv_message执行完) decode_message—》do_decode_message-- ppacket = packet = new SrsCallPacket()🡪SrsCallPacket::decode—对各字段进解析,没做更多的工作。
3.根据分析的过程,只是行行了解析,没有进行过多的操作。
4.10.publish消息
推流客户端使用publish消息向SRS服务器端发布一个命名的流,发布之后,任意客户端都可以通过该名称请求视频、音频和数据。
1.客户端生成publish消息并发送到SRS服务器,函数为int gen_publish(URLContext *s, RTMPContext *rt)。
2. SRS服务器解析publish消息:
在SrsProtocol::do_decode_message
3. 调用栈
4.解析成功后SRS服务器会生成onFCPublish消息返回给客户端。
4.11. onFCPublish消息解析
SRS服务器向客户端发送onFCPublish消息
1. onFCPublish消息是回应publish消息。
2. SRS服务器生成onFCPublish消息并发送到客户端:
在SrsRtmpServer::start_fmle_publish(int stream_id)函数中,收到publis消息后,进行回复。
4.12. onStatue消息解析
1.SRS服务器还会生成onStatus消息向客户端发送,描述的状态内容中code为NetStream.Publish.Start,description为Start publishing,目的就是告诉推流客户端,现在可以推流了。
2. SRS服务器生成onStatue消息并发送到客户端:
在SrsRtmpServer::start_fmle_publish(int stream_id)函数中,收到publish消息后,进行回复。