/* * Copyright (c) 2012 Roman Arutyunyan */ #include #include #include "ngx_rtmp.h" #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_play_module.h" #include "ngx_rtmp_codec_module.h" static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_stat_create_loc_conf(ngx_conf_t *cf); static char * ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_rtmp_stat_init_process(ngx_cycle_t *cycle); static void ngx_rtmp_stat_main(ngx_pool_t *pool, ngx_chain_t ***lll); static time_t start_time; static ngx_str_t shm_name = ngx_string("rtmp_stat"); static ngx_shm_zone_t *shm_zone; static ngx_slab_pool_t *shm_pool; static ngx_buf_t *shm_map; static ngx_event_t shm_evt; static ngx_connection_t shm_dummy_conn; static size_t shm_size = 1024 * 1024; /*TODO:tune*/ #define shm_map_size (sizeof(ngx_buf_t) * NGX_MAX_PROCESSES) #define shm_update_timeout 1000 #define NGX_RTMP_STAT_ALL 0xff #define NGX_RTMP_STAT_GLOBAL 0x01 #define NGX_RTMP_STAT_LIVE 0x02 #define NGX_RTMP_STAT_CLIENTS 0x04 #define NGX_RTMP_STAT_PLAY 0x08 /* * global: stat-{bufs-{total,free,used}, total bytes in/out, bw in/out} - cscf */ typedef struct { ngx_uint_t stat; ngx_str_t stylesheet; } ngx_rtmp_stat_loc_conf_t; static ngx_conf_bitmask_t ngx_rtmp_stat_masks[] = { { ngx_string("all"), NGX_RTMP_STAT_ALL }, { ngx_string("global"), NGX_RTMP_STAT_GLOBAL }, { ngx_string("live"), NGX_RTMP_STAT_LIVE }, { ngx_string("clients"), NGX_RTMP_STAT_CLIENTS }, { ngx_null_string, 0 } }; static ngx_command_t ngx_rtmp_stat_commands[] = { { ngx_string("rtmp_stat"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_rtmp_stat_loc_conf_t, stat), ngx_rtmp_stat_masks }, { ngx_string("rtmp_stat_stylesheet"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_rtmp_stat_loc_conf_t, stylesheet), NULL }, ngx_null_command }; static ngx_http_module_t ngx_rtmp_stat_module_ctx = { NULL, /* preconfiguration */ ngx_rtmp_stat_postconfiguration, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_rtmp_stat_create_loc_conf, /* create location configuration */ ngx_rtmp_stat_merge_loc_conf, /* merge location configuration */ }; ngx_module_t ngx_rtmp_stat_module = { NGX_MODULE_V1, &ngx_rtmp_stat_module_ctx, /* module context */ ngx_rtmp_stat_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ ngx_rtmp_stat_init_process, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static void ngx_rtmp_stat_update(ngx_event_t *ev) { ngx_buf_t *b; ngx_pool_t *pool; ngx_chain_t *l, *cl, **ll, ***lll; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ev->log, 0, "stat: updating slot %d", (ngx_int_t) ngx_process_slot); b = &shm_map[ngx_process_slot]; b->pos = b->last = b->start; pool = ngx_create_pool(4096, ev->log); if (pool == NULL) { return; } cl = NULL; ll = &cl; lll = ≪ ngx_rtmp_stat_main(pool, lll); for (l = cl; l; l = l->next) { if (b->end - b->last < l->buf->last - l->buf->end) { b->last = b->pos; ngx_log_error(NGX_LOG_ERR, ev->log, 0, "stat: not enough space for worker stat"); goto next; } b->last = ngx_cpymem(b->last, l->buf->pos, (size_t) (l->buf->last - l->buf->pos)); } next: ngx_destroy_pool(pool); ngx_add_timer(ev, shm_update_timeout); } static ngx_int_t ngx_rtmp_stat_init_process(ngx_cycle_t *cycle) { ngx_buf_t *b; size_t size; ngx_core_conf_t *ccf; start_time = ngx_cached_time->sec; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); if (ccf->worker_processes == 1) { return NGX_OK; } if (shm_map == NULL) { ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "stat: NULL shm_map"); return NGX_ERROR; } b = &shm_map[ngx_process_slot]; if (b->start) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0, "stat: slot %d already allocated", (ngx_int_t) ngx_process_slot); goto schedule; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0, "stat: allocating slot %d", (ngx_int_t) ngx_process_slot); size = ((shm_size - shm_map_size) / ccf->worker_processes) & ~(ngx_pagesize - 1); b->start= ngx_slab_alloc(shm_pool, size); if (b->start == NULL) { ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "stat: error allocating %d slot", ngx_process_slot); return NGX_ERROR; } b->end = b->start + size; schedule: b->pos = b->last = b->start; shm_evt.log = cycle->log; shm_evt.data = &shm_dummy_conn; shm_evt.handler = ngx_rtmp_stat_update; ngx_add_timer(&shm_evt, shm_update_timeout); return NGX_OK; } static ngx_int_t ngx_rtmp_stat_shm_init(ngx_shm_zone_t *shm_zone, void *data) { shm_pool = (ngx_slab_pool_t *) shm_zone->shm.addr; shm_map = ngx_slab_alloc(shm_pool, shm_map_size); if (shm_map == NULL) { return NGX_ERROR; } ngx_memzero(shm_map, shm_map_size); return NGX_OK; } #define NGX_RTMP_STAT_BUFSIZE 256 /* ngx_escape_html does not escape characters out of ASCII range * which are bad for xslt */ static void * ngx_rtmp_stat_escape(ngx_pool_t *pool, void *data, size_t len) { u_char *p, *np; void *new_data; size_t n; p = data; for (n = 0; n < len; ++n, ++p) { if (*p < 0x20 || *p >= 0x7f) { break; } } if (n == len) { return data; } new_data = ngx_palloc(pool, len); if (new_data == NULL) { return NULL; } p = data; np = new_data; for (n = 0; n < len; ++n, ++p, ++np) { *np = (*p < 0x20 || *p >= 0x7f) ? (u_char) ' ' : *p; } return new_data; } static void ngx_rtmp_stat_output(ngx_pool_t *pool, ngx_chain_t ***lll, void *data, size_t len, ngx_uint_t escape) { ngx_chain_t *cl; ngx_buf_t *b; size_t real_len; if (len == 0) { return; } if (escape) { data = ngx_rtmp_stat_escape(pool, data, len); if (data == NULL) { return; } } real_len = escape ? len + ngx_escape_html(NULL, data, len) : len; cl = **lll; if (cl && cl->buf->last + real_len > cl->buf->end) { *lll = &cl->next; } if (**lll == NULL) { cl = ngx_alloc_chain_link(pool); if (cl == NULL) { return; } b = ngx_create_temp_buf(pool, ngx_max(NGX_RTMP_STAT_BUFSIZE, real_len)); if (b == NULL || b->pos == NULL) { return; } cl->next = NULL; cl->buf = b; **lll = cl; } b = (**lll)->buf; if (escape) { b->last = (u_char *)ngx_escape_html(b->last, data, len); } else { b->last = ngx_cpymem(b->last, data, len); } } /* These shortcuts assume 2 variables exist in current context: * ngx_pool_t *pool * ngx_chain_t ***lll */ /* plain data */ #define NGX_RTMP_STAT(data, len) ngx_rtmp_stat_output(pool, lll, data, len,\ 0) /* escaped data */ #define NGX_RTMP_STAT_E(data, len) ngx_rtmp_stat_output(pool, lll, data, len,\ 1) /* literal */ #define NGX_RTMP_STAT_L(s) NGX_RTMP_STAT((s), sizeof(s) - 1) /* ngx_str_t */ #define NGX_RTMP_STAT_S(s) NGX_RTMP_STAT((s)->data, (s)->len) /* escaped ngx_str_t */ #define NGX_RTMP_STAT_ES(s) NGX_RTMP_STAT_E((s)->data, (s)->len) /* C string */ #define NGX_RTMP_STAT_CS(s) NGX_RTMP_STAT((s), ngx_strlen(s)) /* escaped C string */ #define NGX_RTMP_STAT_ECS(s) NGX_RTMP_STAT_E((s), ngx_strlen(s)) static void ngx_rtmp_stat_bw(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_bandwidth_t *bw_in, ngx_rtmp_bandwidth_t *bw_out) { u_char buf[NGX_OFF_T_LEN + 1]; ngx_rtmp_update_bandwidth(bw_in, 0); ngx_rtmp_update_bandwidth(bw_out, 0); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", bw_in->bytes) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", bw_out->bytes) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", bw_in->bandwidth * 8) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", bw_out->bandwidth * 8) - buf); NGX_RTMP_STAT_L("\r\n"); } #ifdef NGX_RTMP_POOL_DEBUG static void ngx_rtmp_stat_get_pool_size(ngx_pool_t *pool, ngx_uint_t *nlarge, size_t *size) { ngx_pool_large_t *l; ngx_pool_t *p, *n; *nlarge = 0; for (l = pool->large; l; l = l->next) { ++*nlarge; } *size = 0; for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { *size += (p->d.last - (u_char *)p); if (n == NULL) { break; } } } static void ngx_rtmp_stat_dump_pool(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_pool_t *p) { ngx_uint_t nlarge; size_t size; u_char buf[NGX_OFF_T_LEN + 1]; size = 0; nlarge = 0; ngx_rtmp_stat_get_pool_size(p, &nlarge, &size); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", nlarge) - buf); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", size) - buf); NGX_RTMP_STAT_L("\r\n"); } #endif static void ngx_rtmp_stat_client(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_session_t *s) { u_char buf[NGX_OFF_T_LEN + 1]; #ifdef NGX_RTMP_POOL_DEBUG ngx_rtmp_stat_dump_pool(pool, lll, s->connection->pool); #endif NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", (ngx_uint_t) s->connection->number) - buf); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_L("
"); NGX_RTMP_STAT_S(&s->connection->addr_text); NGX_RTMP_STAT_L("
"); NGX_RTMP_STAT_L(""); if (s->flashver.len) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ES(&s->flashver); NGX_RTMP_STAT_L(""); } if (s->page_url.len) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ES(&s->page_url); NGX_RTMP_STAT_L(""); } if (s->swf_url.len) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ES(&s->swf_url); NGX_RTMP_STAT_L(""); } } static void ngx_rtmp_stat_live(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_live_app_conf_t *lacf) { ngx_rtmp_live_stream_t *stream; ngx_rtmp_codec_ctx_t *codec; ngx_rtmp_live_ctx_t *ctx; ngx_rtmp_session_t *s; ngx_int_t n; size_t nclients, total_nclients; u_char buf[NGX_OFF_T_LEN + 1]; u_char *cname; if (!lacf->live) { return; } NGX_RTMP_STAT_L("\r\n"); total_nclients = 0; for (n = 0; n < lacf->nbuckets; ++n) { for (stream = lacf->streams[n]; stream; stream = stream->next) { NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ECS(stream->name); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); ngx_rtmp_stat_bw(pool, lll, &stream->bw_in, &stream->bw_out); nclients = 0; codec = NULL; for (ctx = stream->ctx; ctx; ctx = ctx->next, ++nclients) { s = ctx->session; NGX_RTMP_STAT_L(""); ngx_rtmp_stat_client(pool, lll, s); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uD/%uD", ctx->cs[1].dropped, ctx->cs[0].dropped) - buf); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_L(""); if (!lacf->interleave) { NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%L", (int64_t) ctx->cs[1].timestamp - (int64_t) ctx->cs[0].timestamp) - buf); } NGX_RTMP_STAT_L(""); if (ctx->publishing) { NGX_RTMP_STAT_L(""); } if (ctx->active) { NGX_RTMP_STAT_L(""); } NGX_RTMP_STAT_L("\r\n"); if (ctx->publishing) { codec = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); } } total_nclients += nclients; if (codec) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", codec->width) - buf); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", codec->height) - buf); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui", codec->frame_rate) - buf); NGX_RTMP_STAT_L(""); cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id); if (*cname) { NGX_RTMP_STAT_L(""); } cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id); if (*cname) { NGX_RTMP_STAT_L(""); } if (*codec->profile) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ECS(codec->profile); NGX_RTMP_STAT_L(""); } if (*codec->level) { NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ECS(codec->level); NGX_RTMP_STAT_L(""); } NGX_RTMP_STAT_L("\r\n"); } NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", nclients) - buf); NGX_RTMP_STAT_L("\r\n"); if (stream->publishing) { NGX_RTMP_STAT_L("\r\n"); } if (stream->active) { NGX_RTMP_STAT_L("\r\n"); } NGX_RTMP_STAT_L("\r\n"); } } NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", total_nclients) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L("\r\n"); } static void ngx_rtmp_stat_play(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_play_app_conf_t *pacf) { ngx_rtmp_play_ctx_t *ctx, *sctx; ngx_rtmp_session_t *s; ngx_uint_t n; size_t nclients, total_nclients; u_char buf[NGX_OFF_T_LEN + 1]; if (pacf->root.len == 0 && pacf->url == NULL) { return; } NGX_RTMP_STAT_L("\r\n"); total_nclients = 0; for (n = 0; n < pacf->nbuckets; ++n) { for (ctx = pacf->ctx[n]; ctx; ) { NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ECS(ctx->name); NGX_RTMP_STAT_L("\r\n"); nclients = 0; sctx = ctx; for (; ctx; ctx = ctx->next) { if (ngx_strcmp(ctx->name, sctx->name)) { break; } ++nclients; s = ctx->session; NGX_RTMP_STAT_L(""); ngx_rtmp_stat_client(pool, lll, s); NGX_RTMP_STAT_L("\r\n"); } total_nclients += nclients; NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", nclients) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L("\r\n"); } } NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", total_nclients) - buf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L("\r\n"); } static void ngx_rtmp_stat_application(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_core_app_conf_t *cacf) { NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT_ES(&cacf->name); NGX_RTMP_STAT_L("\r\n"); ngx_rtmp_stat_live(pool, lll, cacf->app_conf[ngx_rtmp_live_module.ctx_index]); ngx_rtmp_stat_play(pool, lll, cacf->app_conf[ngx_rtmp_play_module.ctx_index]); NGX_RTMP_STAT_L("\r\n"); } static void ngx_rtmp_stat_server(ngx_pool_t *pool, ngx_chain_t ***lll, ngx_rtmp_core_srv_conf_t *cscf) { ngx_rtmp_core_app_conf_t **cacf; size_t n; NGX_RTMP_STAT_L("\r\n"); #ifdef NGX_RTMP_POOL_DEBUG ngx_rtmp_stat_dump_pool(pool, lll, cscf->pool); #endif cacf = cscf->applications.elts; for (n = 0; n < cscf->applications.nelts; ++n, ++cacf) { ngx_rtmp_stat_application(pool, lll, *cacf); } NGX_RTMP_STAT_L("\r\n"); } static void ngx_rtmp_stat_main(ngx_pool_t *pool, ngx_chain_t ***lll) { ngx_rtmp_core_main_conf_t *cmcf = ngx_rtmp_core_main_conf; ngx_rtmp_core_srv_conf_t **cscf; size_t n; static u_char nbuf[NGX_OFF_T_LEN + 1]; static u_char tbuf[NGX_TIME_T_LEN + 1]; NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(tbuf, ngx_snprintf(tbuf, sizeof(tbuf), "%T", ngx_cached_time->sec - start_time) - tbuf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf), "%ui", (ngx_uint_t) ngx_getpid()) - nbuf); NGX_RTMP_STAT_L("\r\n"); ngx_rtmp_stat_bw(pool, lll, &ngx_rtmp_bw_in, &ngx_rtmp_bw_out); cscf = cmcf->servers.elts; for (n = 0; n < cmcf->servers.nelts; ++n, ++cscf) { ngx_rtmp_stat_server(pool, lll, *cscf); } } static void ngx_rtmp_stat_get(ngx_pool_t *pool, ngx_chain_t ***lll) { ngx_buf_t *b; ngx_uint_t n; static u_char nbuf[NGX_OFF_T_LEN + 1]; b = &shm_map[ngx_process_slot]; if (b->start == NULL) { /* single-worker */ NGX_RTMP_STAT_L("\r\n"); ngx_rtmp_stat_main(pool, lll); NGX_RTMP_STAT_L("\r\n"); return; } /* multi-worker */ b = &shm_map[0]; for (n = 0; n < NGX_MAX_PROCESSES; ++n, ++b) { if (b->start == NULL) { continue; } NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf), "%ui", n + 1) - nbuf); NGX_RTMP_STAT_L("\r\n"); NGX_RTMP_STAT(b->pos, b->last - b->pos); NGX_RTMP_STAT_L("\r\n"); } } static ngx_int_t ngx_rtmp_stat_handler(ngx_http_request_t *r) { ngx_rtmp_stat_loc_conf_t *slcf; ngx_chain_t *cl, *l, **ll, ***lll; ngx_pool_t *pool; off_t len; pool = r->pool; r->keepalive = 0; slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module); if (slcf->stat == 0) { return NGX_DECLINED; } if (ngx_rtmp_core_main_conf == NULL) { goto error; } cl = NULL; ll = &cl; lll = ≪ NGX_RTMP_STAT_L("\r\n"); if (slcf->stylesheet.len) { NGX_RTMP_STAT_L("stylesheet); NGX_RTMP_STAT_L("\" ?>\r\n"); } NGX_RTMP_STAT_L("\r\n"); #ifdef NGINX_VERSION NGX_RTMP_STAT_L("" NGINX_VERSION "\r\n"); #endif #ifdef NGX_COMPILER NGX_RTMP_STAT_L("" NGX_COMPILER "\r\n"); #endif NGX_RTMP_STAT_L("" __DATE__ " " __TIME__ "\r\n"); ngx_rtmp_stat_get(r->pool, lll); NGX_RTMP_STAT_L("\r\n"); len = 0; for (l = cl; l; l = l->next) { len += (l->buf->last - l->buf->pos); } ngx_str_set(&r->headers_out.content_type, "text/xml"); r->headers_out.content_length_n = len; r->headers_out.status = NGX_HTTP_OK; ngx_http_send_header(r); (*ll)->buf->last_buf = 1; return ngx_http_output_filter(r, cl); error: r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR; r->headers_out.content_length_n = 0; return ngx_http_send_header(r); } static void * ngx_rtmp_stat_create_loc_conf(ngx_conf_t *cf) { ngx_rtmp_stat_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_stat_loc_conf_t)); if (conf == NULL) { return NULL; } conf->stat = 0; return conf; } static char * ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_rtmp_stat_loc_conf_t *prev = parent; ngx_rtmp_stat_loc_conf_t *conf = child; ngx_conf_merge_bitmask_value(conf->stat, prev->stat, 0); ngx_conf_merge_str_value(conf->stylesheet, prev->stylesheet, ""); return NGX_CONF_OK; } static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf) { ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_rtmp_stat_handler; if (shm_zone == NULL) { shm_zone = ngx_shared_memory_add(cf, &shm_name, shm_size + ngx_pagesize, &ngx_rtmp_stat_module); if (shm_zone == NULL) { return NGX_ERROR; } shm_zone->init = ngx_rtmp_stat_shm_init; } return NGX_OK; }