5、SRS4.0源代码分析之RTMP拉流处理

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

目标:

上一节分析了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()
{
   
   <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值