From 7b88858b44371ae03d29052fe6d398750fba851e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 7 May 2012 15:41:03 +0400 Subject: [PATCH] implemented http/xml/xsl RTMP stats --- TODO | 9 +- config | 8 + ngx_rtmp.h | 13 +- ngx_rtmp_bandwidth.c | 23 +++ ngx_rtmp_bandwidth.h | 30 +++ ngx_rtmp_cmd_module.c | 2 +- ngx_rtmp_core_module.c | 5 + ngx_rtmp_handler.c | 6 + ngx_rtmp_live_module.c | 57 ++---- ngx_rtmp_live_module.h | 59 ++++++ ngx_rtmp_stat_module.c | 439 +++++++++++++++++++++++++++++++++++++++++ stat.xsl | 92 +++++++++ test/nginx.conf | 12 +- 13 files changed, 701 insertions(+), 54 deletions(-) create mode 100644 ngx_rtmp_bandwidth.c create mode 100644 ngx_rtmp_bandwidth.h create mode 100644 ngx_rtmp_live_module.h create mode 100644 ngx_rtmp_stat_module.c create mode 100644 stat.xsl diff --git a/TODO b/TODO index 36081c4..9243ddc 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,8 @@ -- implement json http module for accessing stats - * memory stats (shared bufs num/bytes, private pools - * global (connections, IO bytes, bw) - * live (stream name, flags, clients, bytes, bw, drops) - -- add global & per-room in/out byte counters - - add per-client audio/video bias - write complete WIKI docs -- rename netcall to upstream +- global rename: get rid of '_module' suffixes in files && rename netcall to upstream - add support for flv file streaming (NGINX supports streaming flvs via HTTP diff --git a/config b/config index 4c38cf3..fc943e4 100644 --- a/config +++ b/config @@ -11,6 +11,12 @@ CORE_MODULES="$CORE_MODULES ngx_rtmp_notify_module \ " + +HTTP_MODULES="$HTTP_MODULES \ + ngx_rtmp_stat_module \ + " + + NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_rtmp.c \ $ngx_addon_dir/ngx_rtmp_amf.c \ @@ -25,5 +31,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_rtmp_record_module.c \ $ngx_addon_dir/ngx_rtmp_netcall_module.c \ $ngx_addon_dir/ngx_rtmp_notify_module.c \ + $ngx_addon_dir/ngx_rtmp_stat_module.c \ + $ngx_addon_dir/ngx_rtmp_bandwidth.c \ " CFLAGS="$CFLAGS -I$ngx_addon_dir" diff --git a/ngx_rtmp.h b/ngx_rtmp.h index 9259e95..f8a1300 100644 --- a/ngx_rtmp.h +++ b/ngx_rtmp.h @@ -13,6 +13,7 @@ #include #include "ngx_rtmp_amf.h" +#include "ngx_rtmp_bandwidth.h" typedef struct { @@ -261,6 +262,10 @@ typedef struct { } ngx_rtmp_core_main_conf_t; +/* global main conf for stats */ +extern ngx_rtmp_core_main_conf_t *ngx_rtmp_core_main_conf; + + typedef struct ngx_rtmp_core_srv_conf_s { ngx_array_t applications; /* ngx_rtmp_core_app_conf_t */ @@ -467,8 +472,12 @@ ngx_rtmp_get_video_frame_type(ngx_chain_t *in) } -extern ngx_uint_t ngx_rtmp_max_module; -extern ngx_module_t ngx_rtmp_core_module; +extern ngx_rtmp_bandwidth_t ngx_rtmp_bw_out; +extern ngx_rtmp_bandwidth_t ngx_rtmp_bw_in; + + +extern ngx_uint_t ngx_rtmp_max_module; +extern ngx_module_t ngx_rtmp_core_module; #endif /* _NGX_RTMP_H_INCLUDED_ */ diff --git a/ngx_rtmp_bandwidth.c b/ngx_rtmp_bandwidth.c new file mode 100644 index 0000000..a494b15 --- /dev/null +++ b/ngx_rtmp_bandwidth.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012 Roman Arutyunyan + */ + + +#include "ngx_rtmp_bandwidth.h" + + +void +ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes) +{ + if (ngx_cached_time->sec > bw->intl_end) { + bw->bandwidth = ngx_cached_time->sec > + bw->intl_end + NGX_RTMP_BANDWIDTH_INTERVAL + ? 0 + : bw->intl_bytes / NGX_RTMP_BANDWIDTH_INTERVAL; + bw->intl_bytes = 0; + bw->intl_end = ngx_cached_time->sec + NGX_RTMP_BANDWIDTH_INTERVAL; + } + + bw->bytes += bytes; + bw->intl_bytes += bytes; +} diff --git a/ngx_rtmp_bandwidth.h b/ngx_rtmp_bandwidth.h new file mode 100644 index 0000000..d09c110 --- /dev/null +++ b/ngx_rtmp_bandwidth.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012 Roman Arutyunyan + */ + + +#ifndef _NGX_RTMP_BANDWIDTH_H_INCLUDED_ +#define _NGX_RTMP_BANDWIDTH_H_INCLUDED_ + + +#include + + +/* Bandwidth update interval in seconds */ +#define NGX_RTMP_BANDWIDTH_INTERVAL 60 + + +typedef struct { + uint64_t bytes; + uint64_t bandwidth; /* bytes/sec */ + + time_t intl_end; + uint64_t intl_bytes; +} ngx_rtmp_bandwidth_t; + + +void ngx_rtmp_update_bandwidth(ngx_rtmp_bandwidth_t *bw, uint32_t bytes); + + +#endif /* _NGX_RTMP_BANDWIDTH_H_INCLUDED_ */ + diff --git a/ngx_rtmp_cmd_module.c b/ngx_rtmp_cmd_module.c index a30f252..d855751 100644 --- a/ngx_rtmp_cmd_module.c +++ b/ngx_rtmp_cmd_module.c @@ -70,7 +70,7 @@ ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, v.app, sizeof(v.app) }, { NGX_RTMP_AMF_STRING, - ngx_string("flashver"), + ngx_string("flashVer"), v.flashver, sizeof(v.flashver) }, { NGX_RTMP_AMF_STRING, diff --git a/ngx_rtmp_core_module.c b/ngx_rtmp_core_module.c index 6eca498..ffb296c 100644 --- a/ngx_rtmp_core_module.c +++ b/ngx_rtmp_core_module.c @@ -25,6 +25,9 @@ static char *ngx_rtmp_core_application(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +ngx_rtmp_core_main_conf_t *ngx_rtmp_core_main_conf; + + static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = { ngx_conf_deprecated, "so_keepalive", "so_keepalive\" parameter of the \"listen" @@ -160,6 +163,8 @@ ngx_rtmp_core_create_main_conf(ngx_conf_t *cf) return NULL; } + ngx_rtmp_core_main_conf = cmcf; + if (ngx_array_init(&cmcf->servers, cf->pool, 4, sizeof(ngx_rtmp_core_srv_conf_t *)) != NGX_OK) diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index 1e5259a..73f4a2a 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -26,6 +26,10 @@ static ngx_int_t ngx_rtmp_receive_message(ngx_rtmp_session_t *s, static ngx_int_t ngx_rtmp_finalize_set_chunk_size(ngx_rtmp_session_t *s); +ngx_rtmp_bandwidth_t ngx_rtmp_bw_out; +ngx_rtmp_bandwidth_t ngx_rtmp_bw_in; + + #ifdef NGX_DEBUG char* ngx_rtmp_message_type(uint8_t type) { @@ -570,6 +574,7 @@ ngx_rtmp_recv(ngx_event_t *rev) return; } + ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_in, n); b->last += n; s->in_bytes += n; @@ -825,6 +830,7 @@ ngx_rtmp_send(ngx_event_t *wev) return; } + ngx_rtmp_update_bandwidth(&ngx_rtmp_bw_out, n); s->out_bpos += n; if (s->out_bpos == s->out_chain->buf->last) { s->out_chain = s->out_chain->next; diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c index e579c45..0f66593 100644 --- a/ngx_rtmp_live_module.c +++ b/ngx_rtmp_live_module.c @@ -3,9 +3,7 @@ */ -#include -#include -#include "ngx_rtmp.h" +#include "ngx_rtmp_live_module.h" #include "ngx_rtmp_cmd_module.h" @@ -19,9 +17,6 @@ static ngx_rtmp_delete_stream_pt next_delete_stream; #define NGX_RTMP_LIVE_CSID_VIDEO 7 #define NGX_RTMP_LIVE_MSID 1 -/* session flags */ -#define NGX_RTMP_LIVE_PUBLISHING 0x01 - static ngx_int_t ngx_rtmp_live_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_live_create_app_conf(ngx_conf_t *cf); @@ -29,41 +24,6 @@ static char * ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); -typedef struct ngx_rtmp_live_ctx_s ngx_rtmp_live_ctx_t; -typedef struct ngx_rtmp_live_stream_s ngx_rtmp_live_stream_t; - - -struct ngx_rtmp_live_ctx_s { - ngx_rtmp_session_t *session; - ngx_rtmp_live_stream_t *stream; - ngx_rtmp_live_ctx_t *next; - ngx_uint_t flags; - ngx_uint_t msg_mask; - uint32_t csid; - uint32_t next_push; - uint32_t last_audio; - uint32_t last_video; -}; - - -struct ngx_rtmp_live_stream_s { - u_char name[256]; - ngx_rtmp_live_stream_t *next; - ngx_rtmp_live_ctx_t *ctx; - ngx_uint_t flags; -}; - - -typedef struct { - ngx_int_t nbuckets; - ngx_rtmp_live_stream_t **streams; - ngx_flag_t live; - ngx_msec_t buflen; - ngx_pool_t *pool; - ngx_rtmp_live_stream_t *free_streams; -} ngx_rtmp_live_app_conf_t; - - #define NGX_RTMP_LIVE_TIME_ABSOLUTE 0x01 #define NGX_RTMP_LIVE_TIME_RELATIVE 0x02 @@ -331,6 +291,7 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_session_t *ss; ngx_rtmp_header_t ch, lh; ngx_uint_t prio, peer_prio; + ngx_uint_t peers, dropped_peers; c = s->connection; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); @@ -387,11 +348,15 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, out = ngx_rtmp_append_shared_bufs(cscf, NULL, in); ngx_rtmp_prepare_message(s, &ch, &lh, out); + peers = 0; + dropped_peers = 0; + /* broadcast to all subscribers */ for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx == ctx) { continue; } + ++peers; ss = pctx->session; /* send absolute frame */ @@ -411,14 +376,22 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, /* push buffered data */ peer_prio = prio; + /* if (lacf->buflen && h->timestamp >= pctx->next_push) { peer_prio = 0; pctx->next_push = h->timestamp + lacf->buflen; + }*/ + if (ngx_rtmp_send_message(ss, out, peer_prio) != NGX_OK) { + ++pctx->dropped; + ++dropped_peers; } - ngx_rtmp_send_message(ss, out, peer_prio); } ngx_rtmp_free_shared_chain(cscf, out); + ngx_rtmp_update_bandwidth(&ctx->stream->bw_in, h->mlen); + ngx_rtmp_update_bandwidth(&ctx->stream->bw_out, + h->mlen * (peers - dropped_peers)); + return NGX_OK; } diff --git a/ngx_rtmp_live_module.h b/ngx_rtmp_live_module.h new file mode 100644 index 0000000..4077c85 --- /dev/null +++ b/ngx_rtmp_live_module.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2012 Roman Arutyunyan + */ + + +#ifndef _NGX_RTMP_LIVE_H_INCLUDED_ +#define _NGX_RTMP_LIVE_H_INCLUDED_ + + +#include "ngx_rtmp.h" +#include "ngx_rtmp_bandwidth.h" + + +/* session flags */ +#define NGX_RTMP_LIVE_PUBLISHING 0x01 + + +typedef struct ngx_rtmp_live_ctx_s ngx_rtmp_live_ctx_t; +typedef struct ngx_rtmp_live_stream_s ngx_rtmp_live_stream_t; + + +struct ngx_rtmp_live_ctx_s { + ngx_rtmp_session_t *session; + ngx_rtmp_live_stream_t *stream; + ngx_rtmp_live_ctx_t *next; + ngx_uint_t flags; + ngx_uint_t msg_mask; + ngx_uint_t dropped; + uint32_t csid; + uint32_t next_push; + uint32_t last_audio; + uint32_t last_video; +}; + + +struct ngx_rtmp_live_stream_s { + u_char name[256]; + ngx_rtmp_live_stream_t *next; + ngx_rtmp_live_ctx_t *ctx; + ngx_uint_t flags; + ngx_rtmp_bandwidth_t bw_in; + ngx_rtmp_bandwidth_t bw_out; +}; + + +typedef struct { + ngx_int_t nbuckets; + ngx_rtmp_live_stream_t **streams; + ngx_flag_t live; + ngx_msec_t buflen; + ngx_pool_t *pool; + ngx_rtmp_live_stream_t *free_streams; +} ngx_rtmp_live_app_conf_t; + + +extern ngx_module_t ngx_rtmp_live_module; + + +#endif /* _NGX_RTMP_LIVE_H_INCLUDED_ */ diff --git a/ngx_rtmp_stat_module.c b/ngx_rtmp_stat_module.c new file mode 100644 index 0000000..2b5a93b --- /dev/null +++ b/ngx_rtmp_stat_module.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2012 Roman Arutyunyan + */ + + +#include + +#include "ngx_rtmp.h" +#include "ngx_rtmp_live_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); + + +#define NGX_RTMP_STAT_ALL 0xff +#define NGX_RTMP_STAT_GLOBAL 0x01 +#define NGX_RTMP_STAT_LIVE 0x02 +#define NGX_RTMP_STAT_CLIENTS 0x04 + +/* + * 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 */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +#define NGX_RTMP_STAT_BUFSIZE 256 + + +static void +ngx_rtmp_stat_output(ngx_http_request_t *r, 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; + } + + 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(r->pool); + if (cl == NULL) { + return; + } + b = ngx_create_temp_buf(r->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_http_request_t *r + * ngx_chain_t ***lll */ + +/* plain data */ +#define NGX_RTMP_STAT(data, len) ngx_rtmp_stat_output(r, lll, data, len, 0) + +/* escaped data */ +#define NGX_RTMP_STAT_E(data, len) ngx_rtmp_stat_output(r, 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_http_request_t *r, 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"); +} + + +static void +ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll, + ngx_rtmp_live_app_conf_t *lacf) +{ + ngx_rtmp_live_stream_t *stream; + ngx_rtmp_live_ctx_t *ctx; + ngx_rtmp_session_t *s; + ngx_int_t n; + size_t nclients, total_nclients; + ngx_int_t publishing; + u_char buf[NGX_OFF_T_LEN + 1]; + ngx_rtmp_stat_loc_conf_t *slcf; + + slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module); + + 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) { + publishing = 0; + 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_bw(r, lll, &stream->bw_in, &stream->bw_out); + + nclients = 0; + for (ctx = stream->ctx; ctx; ctx = ctx->next, ++nclients) { + s = ctx->session; + /* TODO: add + * 1) session start time + * 2) drop stats */ + if (slcf->stat & NGX_RTMP_STAT_CLIENTS) { + NGX_RTMP_STAT_L(""); + + NGX_RTMP_STAT_L("
"); + NGX_RTMP_STAT_S(&s->connection->addr_text); + NGX_RTMP_STAT_L("
"); + + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), + "%uz", ctx->dropped) - buf); + 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 (ctx->flags & NGX_RTMP_LIVE_PUBLISHING) { + NGX_RTMP_STAT_L(""); + } + + NGX_RTMP_STAT_L("
\r\n"); + } + if (ctx->flags & NGX_RTMP_LIVE_PUBLISHING) { + publishing = 1; + } + } + total_nclients += nclients; + + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), + "%uz", nclients) - buf); + NGX_RTMP_STAT_L("\r\n"); + + if (publishing) { + 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_http_request_t *r, ngx_chain_t ***lll, + ngx_rtmp_core_app_conf_t *cacf) +{ + ngx_rtmp_stat_loc_conf_t *slcf; + + NGX_RTMP_STAT_L("\r\n"); + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT_ES(&cacf->name); + NGX_RTMP_STAT_L("\r\n"); + + slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module); + + if (slcf->stat & NGX_RTMP_STAT_LIVE) { + ngx_rtmp_stat_live(r, lll, + cacf->app_conf[ngx_rtmp_live_module.ctx_index]); + } + + NGX_RTMP_STAT_L("\r\n"); +} + + +static void +ngx_rtmp_stat_server(ngx_http_request_t *r, 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"); + + cacf = cscf->applications.elts; + for (n = 0; n < cscf->applications.nelts; ++n, ++cacf) { + ngx_rtmp_stat_application(r, lll, *cacf); + } + + 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_rtmp_core_main_conf_t *cmcf; + ngx_rtmp_core_srv_conf_t **cscf; + ngx_chain_t *cl, *l, **ll, ***lll; + size_t n; + off_t len; + + r->keepalive = 0; + slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module); + if (slcf->stat == 0) { + return NGX_DECLINED; + } + + cmcf = ngx_rtmp_core_main_conf; + if (cmcf == 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"); + + ngx_rtmp_stat_bw(r, 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(r, lll, *cscf); + } + + 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; + + return NGX_OK; +} + diff --git a/stat.xsl b/stat.xsl new file mode 100644 index 0000000..2ae6e43 --- /dev/null +++ b/stat.xsl @@ -0,0 +1,92 @@ + + + + + + +RTMP statistics + + +
+Generated by NGINX RTMP module + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
RTMP#clientsIn bytesOut bytesIn KbpsOut KbpsStateAddressFlash versionPage URLDropped
+
+
+ + + + + + + + + + + + + + +live streams + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +publishing + + +
diff --git a/test/nginx.conf b/test/nginx.conf index af479fa..542b3eb 100644 --- a/test/nginx.conf +++ b/test/nginx.conf @@ -48,7 +48,7 @@ rtmp { } http { - + server { listen 8080; @@ -65,10 +65,20 @@ http { return 203; } + location /stat { + rtmp_stat all; + rtmp_stat_stylesheet stat.xsl; + } + + location /stat.xsl { + root /home/rarutyunyan/nginx-rtmp-module/; + } + location / { root /home/rarutyunyan/nginx-rtmp-module/test/www; } + } }