目标:
上一节分析了SRS针对推流客户端的处理逻辑,这里接下来分析针对拉流客户端的处理逻辑。
SRS拉流端处理逻辑简单说就是SrsRtmpConn::do_playing()协程从SrsLiveConsumer对象中不断读取数据并转发给拉流客户端的过程。另外,在这个过程中,如果服务器处于edge模式,还有一个回源拉流的处理过程需要重点分析。

内容:
1、拉流端整体处理流程
拉流客户端与服务器之间的协议交互流程:

SrsRtmpConn::stream_service_cycle()函数中通过SrsRtmpServer::identify_client()识别客户端类型,如果是拉流客户端,则进入SrsRtmpConn::playing()处理
srs_error_t SrsRtmpConn::stream_service_cycle()
{
rtmp->identify_client(&info->type);
// 对于推流端,每个推流端通过fetch_or_create函数生成一个对应的SrsLiveSource对象
// 对于拉流端,
// 1)如果本机已经有对应推流端的SrsLiveSource对象,则根据拉流地址字符串(/live/stream)直接获取
// 2)如果本机没有对应推流端的SrsLiveSource对象,这里则会创建一个SrsLiveSource对象
// 并在后续的SrsRtmpConn::playing()函数中,根据edge模式有一个回源拉流的操作
_srs_sources->fetch_or_create(req, server, &source);
switch (info->type) {
case SrsRtmpConnPlay:
rtmp->start_play(info->res->stream_id); // 发送拉流响应报文
http_hooks_on_play(); // 执行HTTP回调处理,对外通知客户端拉流开始
playing(source); // 拉流处理逻辑
http_hooks_on_stop(); //执行HTTP回调处理,对外通知客户端拉流结束
return err;
}
}
SrsRtmpConn::playing()处理的逻辑:
- 1、如果是源站,支持源站集群,且本地没有当前需要的拉流,则向配置的协同源站查询,最终将查询结果通知拉流客户端
- 2、为每个拉流端创建一个SrsLiveConsumer消费者对象,并与推流端SrsLiveSource对象绑定,将source中缓存的meta和GOP数据发送到消费者队列
- 3、创建并启动一个拉流端接收协程,此协程主要工作是接收拉流客户端的播放控制命令(暂停、继续)
- 4、真正的拉流协程do_playing()就阻塞在条件变量mw_wait,等待有新数据时被唤醒。
srs_error_t SrsRtmpConn::playing(SrsLiveSource* source)
{
// 这里判断本机是源站模式,且支持源集群,且当前source对象并没有客户端推流,才进入此分支
// 这个分支基本上包括了源站集群的全部处理逻辑
if (!info->edge && _srs_config->get_vhost_origin_cluster(req->vhost) && source->inactive()) {
// 从配置文件中读取其它协同工作源站的地址
vector<string> coworkers = _srs_config->get_vhost_coworkers(req->vhost);
// 向每个协同工作源站查询是否存在指定的流(/live/stream)
for (int i = 0; i < (int)coworkers.size(); i++) {
// 向配置的协同源站发起HTTP查询请求
SrsHttpHooks::discover_co_workers(url, host, port);
// 根据协同工作源站返回的信息生成新URL
string rurl = srs_generate_rtmp_url(host, port, req->host, req->vhost, );
if (host.empty() || port == 0) {
continue; } // 如果是无效URL,则继续查询
// 使用新生成的URL向客户端返回重定向响应
rtmp->redirect(req, rurl, accepted);
return srs_error_new(ERROR_CONTROL_REDIRECT, "redirected");
}
}
// 为每个拉流端创建一个SrsLiveConsumer消费者对象,并与SrsLiveSource对象绑定
// 这个函数内部包括了edge模式下,edge站点回源拉流的详细处理,这是一个非常重要的特性,需要单独分析
// 这里我们暂时认为,通过此函数的处理,拉流端需要的数据流,已经被推送到了本站点
source->create_consumer(consumer);
// 这里每次创建一个新的消费者对象时,都会首先将source中缓存的meta和GOP数据发送到消费者队列
source->consumer_dumps(consumer);
// 创建并启动一个拉流端接收协程,此协程主要工作是接收客户端的播放控制命令(暂停、继续)
SrsQueueRecvThread trd(consumer, rtmp, SRS_PERF_MW_SLEEP, );
trd.start();
do_playing(source, consumer, &trd); // 进入拉流处理逻辑
trd.stop(); // 拉流结束
return err;
}
拉流端接收协程SrsQueueRecvThread trd的处理逻辑和推流端接收协程完全一致(在srs_app_recv_thread.cpp文件中)
- 1、调用SrsRtmpServer::recv_message()->SrsProtocol::recv_message()接收一个完整的RTMP数据包。
- 2、调用ISrsMessageConsumer::consume()虚接口向外复制数据,此时实际调用的接口是SrsQueueRecvThread::consume()。
- 3、最终数据包被保存到SrsQueueRecvThread对象的std::vector<SrsCommonMessage*>queue队列,并调用SrsLiveConsumer::wakeup()接口,唤醒条件变量mw_wait,因为真正的拉流协程do_playing()就阻塞在条件变量mw_wait,等待有新数据时被唤醒。
srs_error_t SrsRecvThread::do_cycle()
{
<

本文详细解析了SRS 4.0中拉流客户端与服务器之间的交互流程,包括拉流端处理逻辑、源站集群协同、边缘模式下回源拉流的步骤,并探讨了RTMP推拉流的延迟优化。深入剖析了SRS如何通过SrsLiveConsumer与SrsLiveSource协调数据传输和控制命令处理。
最低0.47元/天 解锁文章
2204

被折叠的 条评论
为什么被折叠?



