nginx-rtmp模块初始化

rtmp模块初始化源码剖析

rtmp模块初始化过程主要分为以下几个点: 上下文结构初始化、配置项解析、配置项合并、server 相关端口设置。相关源码文件主要在ngx_rtmp.c和ngx_rtmp.h文件中

下面主要从rtmp框架相关的数据结构,以及整个初始化过程进行分析。

数据结构

nginx-rtmp模块的核心框架定义的通用数据结构主要有以下几个:ngx_rtmp_module_t 、ngx_rtmp_conf_ctx_t

nginx中定义了模块的通用接口ngx_module_t。而对于每个不同类型的模块,又有一个具体的结构体来描述这一类模块的接口,例如:http,event模块,mail等.

而ngx_rtmp_module_t是nginx-rtmp项目模块的rtmp类型的接口。和http 类似,rtmp把rtmp{}, server{}, app{}块的配置项分别称为main、srv、app 级别配置项

typedef struct {
    /* 解析rtmp{} 块内的配置项之前的回调 */
    ngx_int_t             (*preconfiguration)(ngx_conf_t *cf);
    /* 解析rtmp{} 块内的配置项之后的回调 */
    ngx_int_t             (*postconfiguration)(ngx_conf_t *cf);
    /* 用于创建rtmp块内main级别的配置  */
    void                 *(*create_main_conf)(ngx_conf_t *cf);
    /* 解析完main配置项后回调 */
    char                 *(*init_main_conf)(ngx_conf_t *cf, void *conf);
    /*
    * 创建用于同时出现在main、srv级别配置项的结构体;
    * 该结构体中的成员与server配置是相关联的;
    */
    void                 *(*create_srv_conf)(ngx_conf_t *cf);
    /*
    * 由create_srv_conf产生的结构体所要解析的配置项,
    * 可能同时出现在main、srv级别中,
    * merge_srv_conf 方法可以将出现在main级别中的配置项值合并到srv级别的配置项中;
    */
    char                 *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
                                    void *conf);
    /*
    * 创建用于同时出现在main、srv、app级别配置项的结构体,
    * 该结构体成员与app级别配置相关联;
    */
    void                 *(*create_app_conf)(ngx_conf_t *cf);
    /*
    * merge_app_conf 产生的结构体所要解析的配置项,
    * 可能同时出现在main、srv、app级别的配置项中,
    * merge_loc_conf 方法将出现在main、srv级别的配置项值合并到loc级别的配置项中;
    */
    char                 *(*merge_app_conf)(ngx_conf_t *cf, void *prev,
                                    void *conf);
} ngx_rtmp_module_t;

在 RTMP 模块中,管理 RTMP 模块配置项的结构由 ngx_rtmp_conf_ctx_t 实现,该结构有三个成员,分别指向三个指针数组,指针数组是由相应地 RTMP 模块create_main_conf、create_srv_conf、create_app_conf 方法创建的结构体指针组成的数组

typedef struct {
    /*
    * 数组中的每个成员都是由所有RTMP模块create_main_conf方法创建的存放全局配置项的结构体,
    * 它们存放着解析直属于rtmp{}块内main级别的配置项参数;
    */
    void                  **main_conf;
    /*
    * 数组中的每个成员都是由所有RTMP模块create_srv_conf方法创建的与server相关的配置项结构体,
    * 它们存放着main级别,或srv级别的配置项参数;
    * 这与当前的ngx_rtmp_conf_ctx_t是在解析rtmp{}或server{}块时创建有关;
    */
    void                  **srv_conf;
    /*
    * 数组中的每个成员都是由所有HTTP模块create_loc_conf方法创建的与location有关的配置项结构体,
    * 它们存放着main级别、srv级别、loc级别的配置项参数;
    * 这与当前的ngx_rtmp_conf_ctx_t是在解析rtmp{}、server{}或app{}块时创建有关;
    */
    void                  **app_conf;
} ngx_rtmp_conf_ctx_t;

ngx_rtmp_module是rtmp模块的核心模块,该模块的功能是:

  • 1)定义新的 RTMP 模块类型,
  • 2)每个 RTMP 模块定义通用接口ngx_rtmp_module_t 结构体,管理RTMP 模块生成的配置项结构体,并解析RTMP类配置项
   ngx_module_t  ngx_rtmp_module = {
       NGX_MODULE_V1,
       &ngx_rtmp_module_ctx,                  /* module context */
       ngx_rtmp_commands,                     /* module directives */
       NGX_CORE_MODULE,                       /* module type */
       NULL,                                  /* init master */
       NULL,                                  /* init module */
       ngx_rtmp_init_process,                 /* worker进程启动的初始化操作,主要初始化ngx_rtmp_init_queue队列 */
       NULL,                                  /* init thread */
       NULL,                                  /* exit thread */
       NULL,                                  /* exit process */
       NULL,                                  /* exit master */
       NGX_MODULE_V1_PADDING
   };

ngx_rtmp_module作为核心模块之一,必须要定义核心模块通用的上下文结构,该结构体只定义模块的名称

static ngx_core_module_t  ngx_rtmp_module_ctx = {
    ngx_string("rtmp"),
    NULL,
    NULL
};

ngx_rtmp_module模块还定义了ngx_rtmp_commands的配置项数组,rtmp{}块的rtmp配置项数组。

该命令数组只定义了一个配置项rtmp,主要用来rtmp模块的初始化作用。

static ngx_command_t  ngx_rtmp_commands[] = {

    { ngx_string("rtmp"),
    NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
    ngx_rtmp_block,
    0,
    0,
    NULL },

    ngx_null_command
};

ngx_rtmp_module 核心模块的初始化

rtmp模块的初始化过程主要在ngx_rtmp_block函数当中完成的,先分析整个函数的初始化rtmp的过程,再整体梳理一下流程。

static char *
ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                        *rv;
    ngx_uint_t                   i, m, mi, s;
    ngx_conf_t                   pcf;
    ngx_array_t                  ports;
    ngx_module_t               **modules;
    ngx_rtmp_listen_t           *listen;
    ngx_rtmp_module_t           *module;
    ngx_rtmp_conf_ctx_t         *ctx;
    ngx_rtmp_core_srv_conf_t    *cscf, **cscfp;
    ngx_rtmp_core_main_conf_t   *cmcf;

     /* 分配HTTP框架的上下文结构ngx_rtmp_conf_ctx_t空间 */
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    /*
     * conf 是结构体ngx_cycle_t 成员conf_ctx数组中的元素,
     * 该元素conf指向ngx_rtmp_module模块所对应的配置项结构信息;
     */
    *(ngx_rtmp_conf_ctx_t **) conf = ctx;

    /* count the number of the rtmp modules and set up their indices */

#if (nginx_version >= 1009011)

    ngx_rtmp_max_module = ngx_count_modules(cf->cycle, NGX_RTMP_MODULE);

#else

    ngx_rtmp_max_module = 0;
    /* 初始化所有RTMP模块的ctx_index序号 */
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
            continue;
        }

        ngx_modules[m]->ctx_index = ngx_rtmp_max_module++;
    }

#endif


    /* the rtmp main_conf context, it is the same in the all rtmp contexts */
    /*
     * 分配存储RTMP模块main级别下的main_conf配置项的空间;
     */
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_rtmp_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * the rtmp null srv_conf context, it is used to merge
     * the server{}s' srv_conf's
     * 分配存储RTMP模块main级别下的srv_conf配置项的空间;
     */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_rtmp_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * the rtmp null app_conf context, it is used to merge
     * the server{}s' app_conf's
     * 分配存储RTMP模块main级别下的app_conf配置项的空间;
     */

    ctx->app_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_rtmp_max_module);
    if (ctx->app_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * create the main_conf's, the null srv_conf's, and the null app_conf's
     * of the all rtmp modules
     * 遍历所有RTMP模块,为每个RTMP模块创建main级别的配置项结构main_conf、srv_conf、app_conf;
     */
    for (m = 0; modules[m]; m++) {
        if (modules[m]->type != NGX_RTMP_MODULE) {
            continue;
        }

        module = modules[m]->ctx;
        mi = modules[m]->ctx_index;

        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_app_conf) {
            ctx->app_conf[mi] = module->create_app_conf(cf);
            if (ctx->app_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }
    /*
     * 保存待解析配置项结构cf的副本为pcf,待解析完毕后恢复cf;
     * 这里备份是由于配置指令解析函数ngx_conf_parse递归调用,因此为了不影响外层的调用环境;
     */
    pcf = *cf;
    /*
     * 把RTMP模块解析指令的上下文参数保存到配置项结构ngx_rtmp_conf_ctx_t ctx中;
     */
    cf->ctx = ctx;
     /* 遍历所有RTMP模块,调用每个模块的preconfiguration回调函数 */
    for (m = 0; modules[m]; m++) {
        if (modules[m]->type != NGX_RTMP_MODULE) {
            continue;
        }

        module = modules[m]->ctx;

        if (module->preconfiguration) {
            if (module->preconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

    /* parse inside the rtmp{} block  调用模块通用配置项解析函数ngx_conf_parse解析rtmp{}块内的指令*/

    cf->module_type = NGX_RTMP_MODULE; /* 模块类型为RTMP模块 */
    cf->cmd_type = NGX_RTMP_MAIN_CONF; /* 指令类型为RTMP模块的main级别指令 */
    /*
     * 开始解析http{}块内的指令;这里必须注意的是:http{}块内可能会包含server{}块,
     * 而server{}可能会包含app{}块,还需注意的是http{}块内可能有多个server{}块,
     * app{}块也可能有多个,还有一些子块,类似于rec这种
     * 因此,配置项解析函数ngx_conf_parse是被递归调用的;
     */
    rv = ngx_conf_parse(cf, NULL);

    if (rv != NGX_CONF_OK) {
        *cf = pcf;
        return rv;
    }


    /* init rtmp{} main_conf's, merge the server{}s' srv_conf's 
     * 到此完成了所有rtmp{}块的配置解析了包括里面嵌套的srv和app
     * /

    /* 获取ngx_rtmp_core_module模块的main_conf配置项结构 */
    cmcf = ctx->main_conf[ngx_rtmp_core_module.ctx_index];
    /* 获取所有srv_conf配置项结构 */
    cscfp = cmcf->servers.elts;

    /*
     * 遍历所有RTMP模块,并初始化每个RTMP模块的main_conf结构,
     * 同时合并srv_conf 结构(当然srv_conf结构里面包含app_conf结构,所有也合并app_conf结构);
     */
    for (m = 0; modules[m]; m++) {
        if (modules[m]->type != NGX_RTMP_MODULE) {
            continue;
        }
       /*
        * ngx_modules[m]是一个ngx_module_t模块结构体,
        * 它的ctx成员相对于RTMP模块来说是ngx_rtmp_module_t接口;
        */
        module = modules[m]->ctx;
        /* 获取当前RTMP模块在RTMP模块类的序号 */
        mi = modules[m]->ctx_index;

        /* init rtmp{} main_conf's */

        cf->ctx = ctx;
        /* 初始化RTMP模块的main_conf结构 */
        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);
            if (rv != NGX_CONF_OK) {
                *cf = pcf;
                return rv;
            }
        }

        for (s = 0; s < cmcf->servers.nelts; s++) {

            /* merge the server{}s' srv_conf's */

            cf->ctx = cscfp[s]->ctx;
            /* 合并当前main和srv中的配置,如果main和srv都配置了同一指令,采用srv里面的,没有则用main里面的 */
            if (module->merge_srv_conf) {
                rv = module->merge_srv_conf(cf,
                                            ctx->srv_conf[mi],
                                            cscfp[s]->ctx->srv_conf[mi]);
                if (rv != NGX_CONF_OK) {
                    *cf = pcf;
                    return rv;
                }
            }
             /* 合并当前main,srv,app中的配置,如果main,srv,app都配置了同一指令,采用app里面的,app > srv > main */
            if (module->merge_app_conf) {

                /* merge the server{}'s app_conf */

                /*ctx->app_conf = cscfp[s]->ctx->loc_conf;*/

                rv = module->merge_app_conf(cf,
                                            ctx->app_conf[mi],
                                            cscfp[s]->ctx->app_conf[mi]);
                if (rv != NGX_CONF_OK) {
                    *cf = pcf;
                    return rv;
                }

                /* merge the applications{}' app_conf's */

                cscf = cscfp[s]->ctx->srv_conf[ngx_rtmp_core_module.ctx_index];

                rv = ngx_rtmp_merge_applications(cf, &cscf->applications,
                                            cscfp[s]->ctx->app_conf,
                                            module, mi);
                if (rv != NGX_CONF_OK) {
                    *cf = pcf;
                    return rv;
                }
            }

        }
    }

    /*  初始化可添加自定义处理方法的7个HTTP阶段的动态数组 */
    if (ngx_rtmp_init_events(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }
    /* 给各个NGX-RTMP-MODULE类型的rtmp模块注册各种消息handler回调,可以去各个模块的 postconfiguration函数当中看注册的rtmp消息回调handler */
    for (m = 0; modules[m]; m++) {
        if (modules[m]->type != NGX_RTMP_MODULE) {
            continue;
        }

        module = modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

    *cf = pcf;
    /* 初始化rtmp模块事件handler数组 */
    if (ngx_rtmp_init_event_handlers(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }
    /* 给监听端口数组进行初始化 */
    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_rtmp_conf_port_t))
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    listen = cmcf->listen.elts;
    /* 将配置当中监听的端口放到端口数组当中 */
    for (i = 0; i < cmcf->listen.nelts; i++) {
        if (ngx_rtmp_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }
     /* 设置server与监听端口的关系,给每个端口和地址进行关联起来,创建监听相关信息 */
    return ngx_rtmp_optimize_servers(cf, &ports);
}

各个 RTMP 模块都选择在 postconfiguration 回调函数中将自定义方法添加到 handler 动态数组中,原因是在所有 postconfiguration 回调函数调用前,nginx 执行了 ngx_rtmp_init_event 初始化 event 结构,然后在调用所有的 postconfiguration 回调函数以后执行了 ngx_rtmp_init_event_handlers 为 cmcf->events事件handler数组赋值。

通过上面函数的分析,大致可以总结rtmp模块初始化整体流程:

  • 在配置文件中遇到 rtmp{} 块配置,则 RTMP 框架开始初始化并启动,其由函数 ngx_rtmp_block() 实现;
  • rtm框架初始化所有 RTMP 模块的序列号,并创建 3 个类型为 ngx_http_conf_ctx_t 结构的数组用于存储所有HTTP 模块的create_main_conf、create_srv_conf、create_loc_conf方法返回的指针地址;
  • 调用每个 RTMP 模块的 preconfiguration 方法;
  • RTMP框架调用函数 ngx_conf_parse() 开始循环解析配置文件 nginx.conf 中的rtmp{}块里面的所有配置项,rtmp{} 块内可嵌套多个server{} 块,而 server{} 块可嵌套多个 application{},因此配置项解析函数是递归调用;
  • RTMP 框架处理完毕 rtmp{} 配置项,根据解析配置项的结果,必要时调用ngx_rtmp_merge_servers 方法进行配置项合并处理,即合并main、srv、app 级别下server、application 相关的配置项;
  • 初始化可添加处理方法的 RTMP 不同rtmp消息命令的动态数组;
  • 调用所有 RTMP 模块的 postconfiguration 方法使RTMP模块可以注册不同的rtmp消息handler,使得不同模块可以处理不同消息
  • 根据 RTMP 模块处理 RTMP 不同消息阶段的方法构造 cmcf->events 数组;
  • 构造 server 相关的监听端口,并设置新连接事件的回调方法为ngx_rtmp_init_connection ;
  • 继续处理其他 rtmp{} 块之外的配置项,直到配置文件解析器处理完所有配置项后通知Nginx 主循环配置项解析完毕。此时,Nginx 才会启动Web 服务器;

猜你喜欢

转载自blog.csdn.net/wu5215080/article/details/91047877