基于nginx-rtmp-module模块的relay功能分析及完善


基于nginx-1.8.1+nginx-rtmp-module方案

多种实时流请求触发中继的实现


1.概述

  • 视频直播方案基于nginx+rtmp;
  • 集群内区分边缘和源务服器,边缘与源是基于rtmp流做同步的,有relay模块实现;
  • 集群内服务器除提供rtmp实时流基础功能外,基于rtmp实时流,还提供了httplive stream(hls)实时流及http live flv(hlf)实时流;
  • hls流实现详见ngx_http_auth_hls_module、ngx_rtmp_hls_module模块;
  • hlf流实现详见ngx_http_hlf_module、ngx_rtmp_hlf_module模块;
  • relay模块实现了当客户向边缘服务器请求某一路rtmp实时流且该流不存在时,会向配置的源服务器请求该rtmp实时流,请求成功后该边缘服务器分发该流给请求的客户,即中继开始;当最后一个请求该rtmp实时流的客户关闭流时,该边缘服务器会自动断开与源服务器该实时流的请求,即中继结束;
  • 现有relay中继联动源是基于rtmp流请求的,针对hls、hlf流的请求源存在缺失,本文档针对该功能的实现进行阐述;

2.现有中继方案剖析

跟中继功能相关模块,目前有三个,分别是notify,auto-push,relay。

其中notify模块暂不介绍。

2.1auto-push

模块名:ngx_rtmp_auto_push_module;

应用于nginx多进程工作方式,当某一进程受理rtmp实时推流时,同时向其它工作进程实时推流,保证所有工作进程都有该实时流数据,以便各工作进程基于该流的服务,从而保证nginx服务的完整性;

各进程流同步是基于本地套接字来实现的;

剖析主要的三个函数:

2.1.1ngx_rtmp_auto_push_init_process

   next_publish= ngx_rtmp_publish;

    ngx_rtmp_publish =ngx_rtmp_auto_push_publish;

 

    next_delete_stream =ngx_rtmp_delete_stream;

    ngx_rtmp_delete_stream = ngx_rtmp_auto_push_delete_stream;

 

初始化本地侦听套接字

1.根据ngx_rtmp_init_connection查找ngx_listening_t实例来复制;

2.sokcet_name:

    saun->sun_family = AF_UNIX;

    *ngx_snprintf((u_char *) saun->sun_path,sizeof(saun->sun_path),

                  "%V/"NGX_RTMP_AUTO_PUSH_SOCKNAME ".%i",

                  &apcf->socket_dir,ngx_process_slot) = 0;

3.该套接字请求产生的ngx_rtmp_session_t *s,其中s->auto_pushed = 1;

2.1.2ngx_rtmp_auto_push_publish

推流事件上来时:

typedefstruct ngx_rtmp_auto_push_ctx_s ngx_rtmp_auto_push_ctx_t;

 

structngx_rtmp_auto_push_ctx_s {

    ngx_int_t                      *slots; /*NGX_MAX_PROCESSES */

    u_char                         name[NGX_RTMP_MAX_NAME];

    u_char                         args[NGX_RTMP_MAX_ARGS];

    ngx_event_t                     push_evt;

};

1.生成ngx_rtmp_auto_push_ctx_t       *ctx;//上下文;

2.调用ngx_rtmp_auto_push_reconnect函数;

3.ngx_rtmp_auto_push_reconnect函数内再调用relay模块的ngx_rtmp_relay_push()函数;

4.实现断开重连的功能;

2.1.3ngx_rtmp_auto_push_delete_stream

推流会话断开时:

1.源session会话(推流)断开时,清除重连事件即可;

2.目标主机session会话(拉流)断开时/* skipnon-relays & publishers */,激活断开重连定时器;

前提是ngx_rtmp_relay_module模块该session的上下文指示的publish存在,若不存在说明源session会话已断开,不需要重连;

2.2relay

模块名:ngx_rtmp_relay_module;

实现基于rtmp流的中继功能;

 

重点剖析以下几部分:

2.2.1数据结构

基于nginx-rtmp-module模块的relay功能分析及完善_第1张图片

typedefstruct {

    ngx_array_t                 pulls;         /* ngx_rtmp_relay_target_t * */

    ngx_array_t                 pushes;        /* ngx_rtmp_relay_target_t * */

    ngx_array_t                 static_pulls;  /* ngx_rtmp_relay_target_t * */

    ngx_array_t                 static_events; /* ngx_event_t* */

    ngx_log_t                  *log;

    ngx_uint_t                  nbuckets;

    ngx_msec_t                  buflen;

    ngx_flag_t                  session_relay;

    ngx_msec_t                  push_reconnect;

    ngx_msec_t                  pull_reconnect;

    ngx_rtmp_relay_ctx_t        **ctx;

}ngx_rtmp_relay_app_conf_t;

 

typedefstruct {

    ngx_url_t                       url;

    ngx_str_t                       app;

    ngx_str_t                       name;

    ngx_str_t                       tc_url;

    ngx_str_t                       page_url;

    ngx_str_t                       swf_url;

    ngx_str_t                       flash_ver;

    ngx_str_t                       play_path;

    ngx_int_t                       live;

    ngx_int_t                       start;

    ngx_int_t                       stop;

 

    void                           *tag;     /* usually module reference */

    void                           *data;    /* module-specific data */

    ngx_uint_t                    counter; /* mutableconnection counter */

}ngx_rtmp_relay_target_t;

 

typedefstruct {

    ngx_rtmp_conf_ctx_t         cctx;

    ngx_rtmp_relay_target_t    *target;

}ngx_rtmp_relay_static_t;

 

typedefstruct ngx_rtmp_relay_ctx_s ngx_rtmp_relay_ctx_t;

structngx_rtmp_relay_ctx_s {

    ngx_str_t                       name;

    ngx_str_t                       url;

    ngx_log_t                       log;

    ngx_rtmp_session_t             *session;

    ngx_rtmp_relay_ctx_t           *publish;

    ngx_rtmp_relay_ctx_t           *play;

    ngx_rtmp_relay_ctx_t           *next;

 

    ngx_str_t                       app;

    ngx_str_t                       tc_url;

    ngx_str_t                       page_url;

    ngx_str_t                       swf_url;

    ngx_str_t                       flash_ver;

    ngx_str_t                       play_path;

    ngx_int_t                       live;

    ngx_int_t                       start;

    ngx_int_t                       stop;

 

    ngx_event_t                     push_evt;

    ngx_event_t                    *static_evt;

    void                                 *tag;

    void                                 *data;

};

2.2.2关联事件

ngx_rtmp_relay_postconfiguration

详见代码

    h = ngx_array_push(&cmcf->events[NGX_RTMP_HANDSHAKE_DONE]);

    *h = ngx_rtmp_relay_handshake_done;

 

 

    next_publish = ngx_rtmp_publish;

    ngx_rtmp_publish = ngx_rtmp_relay_publish;

 

    next_play = ngx_rtmp_play;

    ngx_rtmp_play = ngx_rtmp_relay_play;

 

    next_delete_stream =ngx_rtmp_delete_stream;

    ngx_rtmp_delete_stream =ngx_rtmp_relay_delete_stream;

 

    next_close_stream = ngx_rtmp_close_stream;

    ngx_rtmp_close_stream =ngx_rtmp_relay_close_stream;


    ch = ngx_array_push(&cmcf->amf);

    ngx_str_set(&ch->name,"_result");

    ch->handler = ngx_rtmp_relay_on_result;

 

    ch = ngx_array_push(&cmcf->amf);

    ngx_str_set(&ch->name,"_error");

    ch->handler = ngx_rtmp_relay_on_error;

 

    ch = ngx_array_push(&cmcf->amf);

    ngx_str_set(&ch->name,"onStatus");

    ch->handler = ngx_rtmp_relay_on_status;

2.2.3配置解析

ngx_rtmp_relay_push_pull

1.解析relay相关参数,生成ngx_rtmp_relay_app_conf_t* racf实例;

2.主要解析3部分:

  • push部分,放入racf>pulls;
  • pull部分,放入racf->pushes;
  • static pull部分,放入racf->static_pulls,同时生成ngx_event_t事件放入racf-> static_events,两者相对应;
  • 以上三个数组,其元素类均是ngx_rtmp_relay_target_t,其中static_pulls还带一个ngx_rtmp_relay_static_t实例,有racf->static_events的data指定。

2.2.4控制上下文

ngx_rtmp_relay_ctx_t结构内的三个成员:

    ngx_rtmp_relay_ctx_t           *publish;

    ngx_rtmp_relay_ctx_t           *play;

    ngx_rtmp_relay_ctx_t           *next;

 

hash表(开链法),结点链表结构

pull时:

  • hash结点存放向目标主机拉流session上下文,该session是推流会话;
  • 结点链表连接的是源session的上下文,该session是拉流会话;
push时:
  • hash结点存放源session的上下文,该session是推流会话;
  • 结点链表连接的是向目标主机推流session的上下文,该session是拉流会话;

警告:

push、pull应用中成对配置。若配置了pull(目标主机不存在或流不存在),而没有配置push时,往服务器推流时无法正常拉流,因为此时拉流请求会激活pull操作,且pull操作失败导致ngx_rtmp_relay_close调用,最终关闭拉流请求,虽然此时服务器存在该流。

2.2.5static pull

ngx_rtmp_relay_init_process();

        ngx_rtmp_relay_static_pull_reconnect();

                ngx_rtmp_relay_create_connection():

1.生成ngx_rtmp_relay_ctx_t*rctx;

2.生成ngx_peer_connection_t*pc,同时获取ngx_peer_connection_t *pc;

3.生成ngx_rtmp_session_t*rs,其中设置rs->relay = 1;

返回ngx_rtmp_relay_static_pull_reconnect函数后,设置rs-> static_relay= 1;

静态拉流session,对于本机来说是推流,当该session断开时,会激活断开重连事件;

2.2.6pull部分

ngx_rtmp_relay_pull:

    return ngx_rtmp_relay_create(s, name, target,

            ngx_rtmp_relay_create_remote_ctx,

                    ngx_rtmp_relay_create_local_ctx);

 

ngx_rtmp_relay_create_remote_ctx:publish

ngx_rtmp_relay_create_local_ctx   :play

 

  • ngx_rtmp_relay_create_remote_ctx会调用ngx_rtmp_relay_create_connection函数创建session向目标主机拉流,同时生成该session的上下文(publish_ctx);对于本机来说该session是推流会话;同一路码流,有多个拉流请求上来时,只有第一个才会创建session,并生成上下文;
  • ngx_rtmp_relay_create_local_ctx为本地源session生成上下文(play_ctx),源session是拉流会话;
  • 建立以向目标主机拉流session上下文的名称为hash结点(开链法),结点采用链表方法连接ngx_rtmp_relay_create_local_ctx创建session上下文,即拉流会话上下文;
  • 同一路码流,有多个pull配置时,经测试,只会启动配置中第一个pull目标主机进行连接;

2.2.7push部分

ngx_rtmp_relay_push:

returnngx_rtmp_relay_create(s, name, target,

            ngx_rtmp_relay_create_local_ctx,

            ngx_rtmp_relay_create_remote_ctx);

 

ngx_rtmp_relay_create_local_ctx   : publish

ngx_rtmp_relay_create_remote_ctx: play

 

  • ngx_rtmp_relay_create_remote_ctx会调用ngx_rtmp_relay_create_connection函数创建session向目标主机推流,同时生成该session的上下文(play_ctx);对于本机来说该session是拉流会话;
  • ngx_rtmp_relay_create_local_ctx为本地源session生成上下文(publish_ctx),源session是推流会话;同一路码有多个push目标时,只有第一个时才会生成上下文;
  • 建立以源session上下文的名称为hash结点(开链法),结点采用链表方法连接ngx_rtmp_relay_create_remote_ctx创建session,即拉流会话上下文;
  • 当拉流会话断开时,需要重连,调用ngx_rtmp_relay_push_reconnect函数;

2.2.8会话关闭时的逻辑

ngx_rtmp_relay_close函数

2.2.8.1静态pull会话关闭

静态pull创建的session,对于本机来说是推流会话,该会话的上下文不存放在hash表中,其中上下文的publish,play,next指针均为空。因此关闭时首先筛选静态pull的会话,即static_relay==1时,并激活重连事件。

2.2.8.2拉流会话关闭

详见代码

    /* play end disconnect? */

    if (ctx->publish != ctx) {

        for (cctx =&ctx->publish->play; *cctx; cctx = &(*cctx)->next) {

            if (*cctx == ctx) {

                *cctx = ctx->next;

                break;

            }

        }

 

        ngx_log_debug2(NGX_LOG_DEBUG_RTMP,ctx->session->connection->log, 0,

                "relay: play disconnectapp='%V' name='%V'",

                &ctx->app,&ctx->name);

 

        /* push reconnect */

        if (s->relay && ctx->tag== &ngx_rtmp_relay_module &&

           !ctx->publish->push_evt.timer_set)

        {

           ngx_add_timer(&ctx->publish->push_evt,racf->push_reconnect);

        }

 

        if (ctx->publish->play == NULL&& ctx->publish->session->relay) {

            ngx_log_debug2(NGX_LOG_DEBUG_RTMP,

                ctx->publish->session->connection->log, 0,

                "relay: publish disconnectempty app='%V' name='%V'",

                &ctx->app,&ctx->name);

            ngx_rtmp_finalize_session(ctx->publish->session);

        }

 

        ctx->publish = NULL;

 

        return;

    }

详述:

1).从hash结点链表中移除ctx;

2).如果还是push中向目标主机创建的推流会话,对于本机是拉流会话,激活断开重连事件;

3).结点链表中没有拉流请求,且推流会话是中继,则断开ctx指向的推流会话(调用ngx_rtmp_finalize_session);

4).设置ctx->publish=NULL;

2.2.8.3推流会话关闭

详见代码

    /* publish end disconnect */

    if (ctx->push_evt.timer_set) {

        ngx_del_timer(&ctx->push_evt);

    }

    for (cctx = &ctx->play; *cctx; cctx= &(*cctx)->next) {

       (*cctx)->publish = NULL;

        ngx_rtmp_finalize_session((*cctx)->session);

    }

    ctx->publish = NULL;

 

    hash = ngx_hash_key(ctx->name.data,ctx->name.len);

    cctx = &racf->ctx[hash %racf->nbuckets];

    for (; *cctx && *cctx != ctx; cctx= &(*cctx)->next);

    if (*cctx) {

        *cctx = ctx->next;

    }

详述:

1).如果激活了重连事件,则取消;

2).清除结点链表中所有的拉流会话,publish=NULL且ngx_rtmp_finalize_session;

两类推流

  • 推流会话上来时(一个),生成push会话(可能多个),即拉流会话;
  • 拉流会话上来时(可以多个),生成pull会话(一个),即推流会话;

3).设置ctx->publish=NULL;

4).清除该推流会话的hash结点;

3.流联动中继的实现

3.1relay模块更改部分

3.1.1relay控制上下文

增加联动其它请求源计数功能

详见红色部分,以下类同

struct ngx_rtmp_relay_ctx_s {

   ngx_str_t                      name;

   ngx_str_t                      url;

   ngx_log_t                      log;

   ngx_rtmp_session_t            *session;

   ngx_rtmp_relay_ctx_t          *publish;

   ngx_rtmp_relay_ctx_t          *play;

   ngx_rtmp_relay_ctx_t          *next;


    //hls,hlf relations

    ngx_uint_t                    refcount;

    ngx_rtmp_relay_callback_t      *ls_rcb;

    ngx_rtmp_relay_callback_t      *free_rcb;

 

   ngx_str_t                      app;

   ngx_str_t                      tc_url;

   ngx_str_t                      page_url;

   ngx_str_t                      swf_url;

   ngx_str_t                      flash_ver;

   ngx_str_t                      play_path;

   ngx_int_t                      live;

   ngx_int_t                      start;

   ngx_int_t                      stop;

 

   ngx_event_t                    push_evt;

   ngx_event_t                   *static_evt;

   void                          *tag;

   void                          *data;

};

 

其中

  • refcount表示除rtmp实时流请求外的计数,每类联动请求源仅加1,与该类请求实际数无关;
  • ls_rcb属回调函数,当推流会话关闭时,需要回调处理的逻辑,同请求源的业务相关;主要应用于非rtmp模块需要绑定流会话关闭事件;

typedef ngx_int_t(*ngx_rtmp_publish_delete_stream_pt)

(ngx_str_t *app, ngx_str_t *name, void *conf);

typedef struct ngx_rtmp_relay_ctx_s ngx_rtmp_relay_ctx_t;

struct ngx_rtmp_relay_callback_s

{

         ngx_rtmp_relay_callback_t           *next;

 

         void                              *conf;

         ngx_rtmp_publish_delete_stream_pt    pds;

} ;

  • free_rcb启用实例回收再利用的方法;

 

3.1.2增加接口函数

3.1.2.1某类请求源拉流时

联动中继

规则同rtmp拉流请求时一样,只有第一个请求时才会调用该函数,原型如下:

ngx_int_t

ngx_rtmp_relay_pull_others_open(ngx_str_t*app, ngx_str_t *name, ngx_uint_t srv,

                                ngx_rtmp_relay_callback_t *src_rcb,ngx_rtmp_relay_callback_t **rcb);

 

详述:

1.根据svr,app,name找到ngx_rtmp_conf_ctx_t(ngx_rtmp_relay_get_conf_ctx);

2.从ngx_rtmp_relay_app_conf_t->ctx查找结点:

         2.1.找到,返回ngx_rtmp_relay_ctx_t,并修改refcount;

         2.2.没有找到,调用ngx_rtmp_relay_create_connection返回ngx_rtmp_relay_ctx_t,并添 加hash表中;

3.1.2.2某类请求源拉流全部关闭时

结束中继

同rtmp拉流关闭时一样,当最后一个拉流关闭时才会调用该函数,原型如下:                                       

ngx_int_t

ngx_rtmp_relay_pull_others_close(ngx_str_t*app, ngx_str_t *name,

                                 ngx_uint_tsrv,ngx_rtmp_relay_callback_t *rcb);

 

详述:

1.根据svr,app,name找到ngx_rtmp_conf_ctx_t(ngx_rtmp_relay_get_conf_ctx);

2.找到ngx_rtmp_relay_app_conf_t->ctx,对应hash结点,refcount减1;如果rcb有值,同时移除从ls_rcb移除rcb;

3.若refcount为0且play为null,且是中继推流会话,则断开该会话(relay,推流会话);

4.调用ngx_rtmp_finalize_session断开会话,会转到ngx_rtmp_relay_close函数执行;

3.1.3会话关闭时更改

ngx_rtmp_relay_close函数,需要改动两个地方:

1.拉流断开时,是否关闭推流会话,在原判断的基础上增加refcount==0,例如:

     if (ctx->publish->play == NULL &&

           //hls,hlf relations

           ctx->publish->refcount == 0 &&

           ctx->publish->session->relay)

       {

           ngx_log_debug2(NGX_LOG_DEBUG_RTMP,

                          ctx->publish->session->connection->log, 0,

                           "relay: publishdisconnect empty app='%V' name='%V'",

                           &ctx->app,&ctx->name);

                              

           ngx_rtmp_finalize_session(ctx->publish->session);

       }

2.推流会话关闭时,回调ls_rcb函数;该回调应用于非rtmp模块需要绑定ngx_rtmp_close_stream函数链表,例如:

   //发布流断开回调

   if (ctx->ls_rcb)

    {

       rcb = ctx->ls_rcb;

       for (; rcb ; rcb = rcb->next)

       {

           if (rcb->pds)

           {

               rcb->pds(&ctx->app,&ctx->name,rcb->conf);

           }

 

           ngx_rtmp_relay_free_rcb(rcb,ctx);

       }

 

       ctx->ls_rcb = NULL;

   }   

3.2hls更改部分

文件名:ngx_http_auth_hls_module.c

模块名:ngx_http_auth_hls_module,属http模块。

 

该模块提供了hls流请求session管理,token认证功能,现增加联动中继功能;

以下阐述针对hls请求联动中继功能实现的修改部分。

3.2.1配置结构更改

详见红色部,以下类同

//auth_hls配置

typedef struct

{

         ngx_flag_t                                    enable;         //hls认证启用标识,默认不启用

   ngx_str_t                  url;            //认证url(本地location,其内调用代理                                                                                                             完成认证),默认auth_hls

 

    ngx_flag_t                 hls_relay;      //联动hls中继,默认不启用

    ngx_uint_t                  hls_srv;        //rtmp server序号,默认0

 

   ngx_msec_t                timeout;        //闲时周期,默认30秒

   ngx_uint_t                 nbuckets;       //最大hash数(开链表示,实际容量不                                                                                                                    受限制),默认1024

 

     ngx_pool_t                 *pool;

     ngx_log_t                   *log;

 

   ngx_http_hls_relay_t        *free;

   ngx_http_hls_relay_t       **hls_relay_ref;

} ngx_http_auth_hls_conf_t;

3.2.2增加流请求计数及hls会话更改

//session结构体

struct ngx_http_auth_hls_session_s

{

   ngx_http_auth_hls_session_t    *next;

 

    ngx_str_t                        session;         

   ngx_str_t                       token;           

   ngx_str_t                       host;            

 

    ngx_http_hls_relay_t              *relay;

 

   ngx_pool_t                     *pool;

   ngx_event_t                     idle_timer;

};

 

//relay结构体

structngx_http_hls_relay_s

{

   ngx_http_hls_relay_t        *next;

 

   u_char                     app[NGX_RTMP_MAX_NAME];

   u_char                      name[NGX_RTMP_MAX_NAME];

   ngx_uint_t                   refcount;

   ngx_rtmp_relay_callback_t     *rcb;

 

   ngx_http_auth_hls_conf_t      *conf;

   ngx_http_auth_hls_session_t  **sessions;

};

3.2.3创建、销毁会话时的更改

hls流属http协议的实时流,之前已经提供hls流会话的管理;

  • 在创建hls流会话时,即ngx_http_auth_hls_create_session函数内,增加调用relay模块的ngx_rtmp_relay_pull_others_open接口,例如:

         if(conf->hls_relay && !relay->refcount)

         {

       ngx_memzero(&src_cb,sizeof(ngx_rtmp_relay_callback_t));

       src_cb.conf = conf;

       src_cb.pds = hls_publish_delete_stream;

       ngx_rtmp_relay_pull_others_open(app,name,conf->hls_srv,&src_cb,&relay->rcb);

         }

         relay->refcount++;

  • 在hls流会销毁时,即ngx_rtmp_relay_pull_others_close函数内,增加调用relay模块的ngx_rtmp_relay_pull_others_close接口,例如:

         if(relay->refcount == 0)

        {

             if (conf->hls_relay)

            {

                 ngx_rtmp_relay_pull_others_close(&app,

                                               &name,conf->hls_srv,relay->rcb);

             }

             ...

3.3hlf更改部分

hlf流属http协议的实时流,与hls流不同在于hlf流是基于http协议的长连接;

hlf流有ngx_http_hlf_module,ngx_rtmp_hlf_module两个提供实现;

3.3.1ngx_http_hlf_module

文件:ngx_http_hlf_module.c,属http模块。

模块配置更改

//模块配置

typedef struct

{

   ngx_flag_t    hlf_relay;      //联动hls中继,默认不启用

   ngx_uint_t    hlf_srv;        //rtmp server序号,默认0

} ngx_http_hlf_loc_conf_t;

3.3.2ngx-tmp_hlf_module

文件

ngx_rtmp_hlf_module.h

ngx_rtmp_hlf_module.c

ngx_rtmp_hlf_shared.c

属rtmp模块

配置更改

//hlf配置

typedef struct {

   ngx_flag_t     enable;          //启用标识(其它选项,后续增加)

   ngx_int_t      nbuckets;        //hash桶个数,默认1024

 

   ngx_int_t      queue_max;     //缓冲最大帧数,默认256,最小32

   ngx_msec_t    idle_timeout;    //无流时保持时长 ,默认直接断开(0)

    ngx_flag_t     hlf_relay;        //联动中继,默认off

   

   ngx_rtmp_hlf_ctx_t   **ctxs;

 

   ngx_pool_t          *pool;

   ngx_rtmp_hlf_ctx_t   *free_ctxs;

} ngx_rtmp_hlf_app_conf_t;

ngx_rtmp_hlf_add_stream函数更改

当hlf流请求时,即ngx_rtmp_hlf_add_stream函数内增加调用relay模块的ngx_rtmp_relay_pull_others_open接口,例如:

   //联动中继

   if (conf->hlf_relay && ctx->streams == NULL)

    {

       ngx_rtmp_relay_pull_others_open(app,name,srv,NULL,NULL);

    }

ngx_rtmp_hlf_del_stream函数更改

当hlf请求关闭时,即ngx_rtmp_hlf_del_stream函数内增加调用relay模块的ngx_rtmp_relay_pull_others_close接口,例如:

   //没有请求流,移除ctx

   if (ctx->streams == NULL)

    {

       conf = ctx->conf;

       

       if (conf->hlf_relay)

       {

           ngx_rtmp_relay_pull_others_close(&st->app,&st->name,ctx->hlf_srv,NULL);

       }

...

4.总结

源码详见:

https://download.csdn.net/download/laichanghe/10455405

QQ:19993939

备注:文档内QQ号是错的,在此纠正


你可能感兴趣的:(直播)