Nginx Rtmp Learing 之基本數(shù)據(jù)結(jié)構(gòu)ngx_module_t
1. ngx_module_t的基本結(jié)構(gòu)
對(duì)于開發(fā)一個(gè)模塊來說,我們都需要定義一個(gè)ngx_module_t類型的變量來說明這個(gè)模塊本身的信息,從某種意義上來說,這是這個(gè)模塊最重要的一個(gè)信息,它告訴了nginx這個(gè)模塊的一些信息,配置信息,還有模塊上下文信息,都是通過這個(gè)結(jié)構(gòu)來告訴nginx系統(tǒng)的,也就是加載模塊的上層代碼,都需要通過定義的這個(gè)結(jié)構(gòu),來獲取這些信息。
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
// 下面的幾個(gè)成員通常使用宏NGX_MODULE_V1填充
// 每類(http/event)模塊各自的index, 初始化為-1
ngx_uint_t ctx_index;
// 在ngx_modules數(shù)組里的唯一索引,main()里賦值
// 使用計(jì)數(shù)器變量ngx_max_module
ngx_uint_t index;
// 1.10,模塊的名字,標(biāo)識(shí)字符串,默認(rèn)是空指針
// 由腳本生成ngx_module_names數(shù)組,然后在ngx_preinit_modules里填充
// 動(dòng)態(tài)模塊在ngx_load_module里設(shè)置名字
char *name;
// 兩個(gè)保留字段,1.9之前有4個(gè)
ngx_uint_t spare0;
ngx_uint_t spare1;
// nginx.h:#define nginx_version 1010000
ngx_uint_t version;
// 模塊的二進(jìn)制兼容性簽名,即NGX_MODULE_SIGNATURE
const char *signature;
// 模塊不同含義不同,通常是函數(shù)指針表,是在配置解析的某個(gè)階段調(diào)用的函數(shù)
// core模塊的ctx
//typedef struct {
// ngx_str_t name;
// void *(*create_conf)(ngx_cycle_t *cycle);
// char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
//} ngx_core_module_t;
void *ctx;
// 模塊支持的指令,數(shù)組形式,最后用空對(duì)象表示結(jié)束
ngx_command_t *commands;
// 模塊的類型標(biāo)識(shí),相當(dāng)于RTTI,如CORE/HTTP/STRM/MAIL等
ngx_uint_t type;
// 以下7個(gè)函數(shù)會(huì)在進(jìn)程的啟動(dòng)或結(jié)束階段被調(diào)用
// init_master目前nginx不會(huì)調(diào)用
ngx_int_t (*init_master)(ngx_log_t *log);
// 在ngx_init_cycle里被調(diào)用
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
// 在ngx_single_process_cycle/ngx_worker_process_init里調(diào)用
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
// init_thread目前nginx不會(huì)調(diào)用
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
// exit_thread目前nginx不會(huì)調(diào)用
void (*exit_thread)(ngx_cycle_t *cycle);
// 在ngx_worker_process_exit調(diào)用
void (*exit_process)(ngx_cycle_t *cycle);
// 在ngx_master_process_exit(os/unix/ngx_process_cycle.c)里調(diào)用
void (*exit_master)(ngx_cycle_t *cycle);
// 下面8個(gè)成員通常用用NGX_MODULE_V1_PADDING填充
// 暫時(shí)無任何用處
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
2. ngx_modules數(shù)組
2.1 ngx_modules數(shù)組
ngx_modules數(shù)組是在執(zhí)行configure腳本后自動(dòng)生成的,在objs/ngx_modules.c文件中。該數(shù)組即當(dāng)前編譯版本中的所有Nginx模塊。
如果想讓nginx支持Rtmp的話,需要在執(zhí)行configure腳本時(shí)添加第三方庫nginx-rtmp-module
./configure --add-module=/path/to/nginx-rtmp-module
此時(shí),objs/ngx_modules.c的內(nèi)容為:
ngx_module_t *ngx_modules[] = {
&ngx_core_module,
&ngx_errlog_module,
&ngx_conf_module,
&ngx_rtmp_module,
&ngx_rtmp_core_module,
&ngx_rtmp_cmd_module,
&ngx_rtmp_billing_module,
&ngx_rtmp_codec_module,
&ngx_rtmp_access_module,
&ngx_rtmp_record_module,
&ngx_rtmp_live_module,
&ngx_rtmp_hdl_module,
&ngx_rtmp_hls_delay_module,
&ngx_rtmp_hls_vod_module,
&ngx_rtmp_hls_module,
&ngx_rtmp_play_module,
&ngx_rtmp_flv_module,
&ngx_rtmp_mp4_module,
&ngx_rtmp_netcall_module,
&ngx_rtmp_relay_module,
&ngx_rtmp_exec_module,
&ngx_rtmp_auto_push_module,
&ngx_rtmp_notify_module,
&ngx_rtmp_listener_init_module,
&ngx_rtmp_log_module,
&ngx_rtmp_limit_module,
&ngx_rtmp_dash_module,
&ngx_openssl_module,
&ngx_regex_module,
&ngx_events_module,
&ngx_event_core_module,
&ngx_epoll_module,
&ngx_http_module,
&ngx_http_core_module,
&ngx_http_log_module,
&ngx_http_upstream_module,
&ngx_http_static_module,
&ngx_http_autoindex_module,
&ngx_http_index_module,
&ngx_http_auth_basic_module,
&ngx_http_access_module,
&ngx_http_limit_conn_module,
&ngx_http_limit_req_module,
&ngx_http_geo_module,
&ngx_http_map_module,
&ngx_http_split_clients_module,
&ngx_http_referer_module,
&ngx_http_rewrite_module,
&ngx_http_proxy_module,
&ngx_http_fastcgi_module,
&ngx_http_uwsgi_module,
&ngx_http_scgi_module,
&ngx_http_memcached_module,
&ngx_http_empty_gif_module,
&ngx_http_browser_module,
&ngx_http_upstream_hash_module,
&ngx_http_upstream_ip_hash_module,
&ngx_http_upstream_least_conn_module,
&ngx_http_upstream_keepalive_module,
&ngx_http_upstream_zone_module,
&ngx_rtmp_http_hls_module,
&ngx_rtmp_http_hdl_module,
&ngx_rtmp_stat_module,
&ngx_rtmp_control_module,
&ngx_http_write_filter_module,
&ngx_http_header_filter_module,
&ngx_http_chunked_filter_module,
&ngx_http_range_header_filter_module,
&ngx_http_gzip_filter_module,
&ngx_http_postpone_filter_module,
&ngx_http_ssi_filter_module,
&ngx_http_charset_filter_module,
&ngx_http_userid_filter_module,
&ngx_http_headers_filter_module,
&ngx_http_copy_filter_module,
&ngx_http_range_body_filter_module,
&ngx_http_not_modified_filter_module,
NULL
};
如上所述,ngx_modules數(shù)組代表了當(dāng)前Nginx中的所有模塊。每一個(gè)Nginx模塊,即ngx_module_t類型的變量,其ctx變量一般為如下幾個(gè)類型之一:
ngx_core_module_t
ngx_event_module_t
ngx_http_module_t
ngx_mail_module_t
所以,可以對(duì)所有的Nginx模塊按其ctx類型進(jìn)行分類,當(dāng)然并不是所有的nginx模塊都有ctx成員,例如ngx_conf_module模塊的ctx成員為NULL,但可以認(rèn)為絕大部分模塊都屬于上述四類模塊之一。
每一類模塊下的所有模塊,由于其ctx結(jié)構(gòu)是一樣的,因而在程序執(zhí)行邏輯上會(huì)有共同點(diǎn)。具體來說,我們可以遍歷ngx_modules數(shù)組成員,判斷其屬于上述四類的哪一類模塊,再調(diào)用其對(duì)應(yīng)的ctx成員中的函數(shù)指針,這樣就會(huì)屏蔽掉具體的模塊名稱,抽象到框架的層面上。
那么,如何判斷一個(gè)模塊屬于上述四類模塊中的哪一類呢?這就是模塊變量的type成員的作用了。
所有的ngx_core_module_t類型的模塊,其type成員為NGX_CORE_MODULE
所有的ngx_event_module_t類型的模塊,其type成員為NGX_EVENT_MODULE
所有的ngx_http_module_t類型的模塊,其type成員為NGX_HTTP_MODULE
所有的ngx_mail_module_t類型的模塊,其type成員為NGX_MAIL_MODULE
所以在遍歷ngx_modules數(shù)組時(shí),即可根據(jù)每一個(gè)數(shù)組成員的type成員,來判斷該模塊屬于哪種類型的模塊,進(jìn)而執(zhí)行該模塊對(duì)應(yīng)的鉤子函數(shù)。
2.2 ngx_module_t的分層模塊結(jié)構(gòu)
Nginx 采用了分層模塊結(jié)構(gòu)設(shè)計(jì),頂層模塊由 Nginx 自身管理。比如, 在 ngx_init_cycle 有一段代碼為:
ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
ngx_uint_t i, n;
// ...
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
// ...
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->init_conf) {
if (module->init_conf(cycle,
cycle->conf_ctx[cycle->modules[i]->index])
== NGX_CONF_ERROR)
{
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
// ...
return NULL;
}
類型為 NGX_CORE_MODULE 的模塊的 create_conf 和 init_conf 會(huì)被調(diào)用。 類型為 NGX_CORE_MODULE 的摸塊即屬于 Nginx 中的頂層模塊。 頂層模塊除負(fù)責(zé)像日志打印、配置解析等核心業(yè)務(wù)外,也需要管理二級(jí)模塊的接入。 實(shí)際上,上層模塊負(fù)責(zé)下層模塊的接入,這是一種很自然的設(shè)計(jì)。 假如,我們自己在 Nginx 中擴(kuò)展了二級(jí)模塊,而由于業(yè)務(wù)復(fù)雜,我們需要進(jìn)一步進(jìn)行模塊劃分。 而新劃分出的模塊則屬于三級(jí)模塊,那這三級(jí)模塊的接入不由我們自己定義的二級(jí)模塊接入又該由誰負(fù)責(zé)呢?
rtmp模塊的類型為 NGX_RTMP_MODULE,Nginx中與rtmp相關(guān)的模塊有: ngx_rtmp_module, ngx_rtmp_core_module, ngx_rmtp_***_module。 其中 ngx_rtmp_module 屬于頂層模塊,但它負(fù)責(zé)二級(jí)事件模塊的接入; ngx_rtmp_core_module 是核心的事件模塊,它負(fù)責(zé)server,application配置文件的解析和管理、事件的掛載、初始化listen等; ngx_rtmp_**_module 是nginx下的其它所有的rtmp模塊。 我們來ngx_rtmp_module這個(gè)頂層模塊的實(shí)現(xiàn):
2.3 ngx_rtmp_module(ngx_rtmp.c文件)
2.3.1 ngx_rtmp_module的定義
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, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
2.2.2 ngx_rtmp_module的上下文(ngx_rtmp_module_ctx)
// 核心模塊的ctx結(jié)構(gòu),比較簡單,只有創(chuàng)建和初始化配置結(jié)構(gòu)函數(shù) .
// create_conf函數(shù)返回的是void*指針
static ngx_core_module_t ngx_rtmp_module_ctx = {
ngx_string("rtmp"), /*name*/
NULL, /*create_conf*/
NULL /*init_conf*/
};
2.2.3 模塊配置指令(ngx_rtmp_commands)
// 指令結(jié)構(gòu)體,用于定義nginx指令
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
};
看這個(gè)定義,基本能看出來一些信息。例如,我們是定義了一個(gè)配置指令叫rtmp,不接受任何參數(shù)。
接下來看下ngx_command_t的定義,位于src/core/ngx_conf_file.h中。
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
name: 配置指令的名稱。
type: 該配置的類型,其實(shí)更準(zhǔn)確一點(diǎn)說,是該配置指令屬性的集合。
set: 這是一個(gè)函數(shù)指針,當(dāng)nginx在解析配置的時(shí)候,如果遇到這個(gè)配置指令,將會(huì)把讀取到的值傳遞給這個(gè)函數(shù)進(jìn)行分解處理。因?yàn)榫唧w每個(gè)配置指令的值如何處理,只有定義這個(gè)配置指令的人是最清楚的。來看一下這個(gè)函數(shù)指針要求的函數(shù)原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
先看該函數(shù)的返回值,處理成功時(shí),返回NGX_OK,否則返回NGX_CONF_ERROR或者是一個(gè)自定義的錯(cuò)誤信息的字符串。
再看一下這個(gè)函數(shù)被調(diào)用的時(shí)候,傳入的三個(gè)參數(shù)。
- cf: 該參數(shù)里面保存從配置文件讀取到的原始字符串以及相關(guān)的一些信息。特別注意的是這個(gè)參數(shù)的args字段是一個(gè)
ngx_str_t類型的數(shù)組,該數(shù)組的首個(gè)元素是這個(gè)配置指令本身,第二個(gè)元素是指令的第一個(gè)參數(shù),第三個(gè)元素是第二個(gè)參數(shù),依次類推。 - cmd: 這個(gè)配置指令對(duì)應(yīng)的
ngx_command_t結(jié)構(gòu)。 - conf: 就是定義的存儲(chǔ)這個(gè)配置值的結(jié)構(gòu)體。用戶在處理的時(shí)候可以使用類型轉(zhuǎn)換,轉(zhuǎn)換成自己知道的類型,再進(jìn)行字段的賦值。
// 空指令,用于在指令數(shù)組的最后當(dāng)做哨兵,結(jié)束數(shù)組,避免指定長度,類似NULL的作用
#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
2.3 ngx_rtmp_block配置解析函數(shù)
這個(gè)函數(shù)可以理解為整個(gè)ngx_rtmp_module的入口函數(shù):
// 解析rtmp{}配置塊,里面有server{}/application{}等
// 只有出現(xiàn)這個(gè)指令才會(huì)在conf_ctx里創(chuàng)建rtmp配置,避免內(nèi)存浪費(fèi)
// 統(tǒng)計(jì)rtmp模塊的數(shù)量,設(shè)置rtmp模塊的ctx_index,即rtmp模塊自己的序號(hào)
// 調(diào)用每個(gè)rtmp模塊的create_xxx_conf函數(shù),創(chuàng)建配置結(jié)構(gòu)體
// 初始化rtmp處理引擎的階段數(shù)組,調(diào)用ngx_array_init
// 整理所有的rtmp handler模塊,填入引擎數(shù)組
// 調(diào)用ngx_create_listening添加到cycle的監(jiān)聽端口數(shù)組,只是添加,沒有其他動(dòng)作
// 設(shè)置有連接發(fā)生時(shí)的回調(diào)函數(shù)ngx_rtmp_init_connection
static char *
ngx_rtmp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_uint_t m, mi, s;
ngx_conf_t pcf;
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;
/* the main rtmp context */
// ngx_rtmp_conf_ctx_t里有三個(gè)void*數(shù)組,存儲(chǔ)三個(gè)層次的模塊配置
// in ngx_rtmp.h
//
// typedef struct {
// void **main_conf;
// void **srv_conf;
// void **loc_conf;
// } ngx_rtmp_conf_ctx_t;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*(ngx_rtmp_conf_ctx_t **) conf = ctx;
/* count the number of the rtmp modules and set up their indices */
// 統(tǒng)計(jì)rtmp模塊的數(shù)量
// 設(shè)置rtmp模塊的ctx_index,即rtmp模塊自己的序號(hào)
ngx_rtmp_max_module = 0;
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++;
}
/* the rtmp main_conf context, it is the same in the all rtmp contexts */
// main配置數(shù)組,所有rtmp模塊只有一個(gè)
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
*/
// srv配置數(shù)組,在rtmp main層次存儲(chǔ)server基本的配置,用于合并
// 本身并無實(shí)際意義
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
*/
// location配置數(shù)組,在rtmp main層次存儲(chǔ)location基本的配置,用于合并
// 本身并無實(shí)際意
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
*/
// 調(diào)用每個(gè)rtmp模塊的create_xxx_conf函數(shù),創(chuàng)建配置結(jié)構(gòu)體
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
// 創(chuàng)建每個(gè)模塊的main_conf
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
// 創(chuàng)建每個(gè)模塊的srv_conf
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
// 創(chuàng)建每個(gè)模塊的loc_conf
if (module->create_app_conf) {
ctx->app_conf[mi] = module->create_app_conf(cf);
if (ctx->app_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
// 初始的解析環(huán)境已經(jīng)準(zhǔn)備好,下面開始解析rtmp{}配置
// 暫存當(dāng)前的解析上下文
// cf是函數(shù)入口傳遞來的上下文
pcf = *cf;
// 設(shè)置事件模塊的新解析上下文
// 即ngx_http_conf_ctx_t結(jié)構(gòu)體
cf->ctx = ctx;
// 解析之前,調(diào)用preconfiguration,可以添加變量定義
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
/* parse inside the rtmp{} block */
// 設(shè)置解析的類型等信息
// NGX_HTTP_MODULE用來檢查是否是http模塊,防止用錯(cuò)了指令
cf->module_type = NGX_RTMP_MODULE;
cf->cmd_type = NGX_RTMP_MAIN_CONF;
// 遞歸解析rtmp模塊
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{}里定義的server{}塊
cmcf = ctx->main_conf[ngx_rtmp_core_module.ctx_index];
// 所有server{}定義的配置都保存在core module main conf的serevrs數(shù)組里
cscfp = cmcf->servers.elts;
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
mi = ngx_modules[m]->ctx_index;
/* init rtmp{} main_conf's */
cf->ctx = ctx;
// 初始化main配置
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;
// 合并srv配置
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;
}
}
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;
}
}
}
}
if (ngx_rtmp_init_events(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_RTMP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
// 執(zhí)行每個(gè)rtmp模塊的postconfiguration函數(shù)指針
//** 通常是向phase數(shù)組里添加handler**
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
// 恢復(fù)之前保存的解析上下文
*cf = pcf;
if (ngx_rtmp_init_event_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* optimize the lists of ports, addresses and server names */
// 類似stream模塊,對(duì)已經(jīng)整理好的監(jiān)聽端口數(shù)組排序
// 調(diào)用ngx_create_listening添加到cycle的監(jiān)聽端口數(shù)組,只是添加,沒有其他動(dòng)作
// 設(shè)置有連接發(fā)生時(shí)的回調(diào)函數(shù)ngx_rtmp_init_connection
return ngx_rtmp_optimize_servers(cf, cmcf, &cmcf->ports);
}
參考文章:
https://github.com/chronolaw/annotated_nginx
http://tengine.taobao.org/book/