mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-05-09 06:01:08 +02:00
implemented http/xml/xsl RTMP stats
This commit is contained in:
parent
f6ccfb6fa1
commit
7b88858b44
9
TODO
9
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
|
||||
|
|
8
config
8
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"
|
||||
|
|
13
ngx_rtmp.h
13
ngx_rtmp.h
|
@ -13,6 +13,7 @@
|
|||
#include <ngx_event_connect.h>
|
||||
|
||||
#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_ */
|
||||
|
|
23
ngx_rtmp_bandwidth.c
Normal file
23
ngx_rtmp_bandwidth.c
Normal file
|
@ -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;
|
||||
}
|
30
ngx_rtmp_bandwidth.h
Normal file
30
ngx_rtmp_bandwidth.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_RTMP_BANDWIDTH_H_INCLUDED_
|
||||
#define _NGX_RTMP_BANDWIDTH_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
/* 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_ */
|
||||
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
59
ngx_rtmp_live_module.h
Normal file
59
ngx_rtmp_live_module.h
Normal file
|
@ -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_ */
|
439
ngx_rtmp_stat_module.c
Normal file
439
ngx_rtmp_stat_module.c
Normal file
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* Copyright (c) 2012 Roman Arutyunyan
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_http.h>
|
||||
|
||||
#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("<in>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", bw_in->bytes) - buf);
|
||||
NGX_RTMP_STAT_L("</in>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("<out>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", bw_out->bytes) - buf);
|
||||
NGX_RTMP_STAT_L("</out>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("<bwin>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", bw_in->bandwidth * 8) - buf);
|
||||
NGX_RTMP_STAT_L("</bwin>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("<bwout>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", bw_out->bandwidth * 8) - buf);
|
||||
NGX_RTMP_STAT_L("</bwout>\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("<live>\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("<stream>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("<name>");
|
||||
NGX_RTMP_STAT_ECS(stream->name);
|
||||
NGX_RTMP_STAT_L("</name>\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("<client>");
|
||||
|
||||
NGX_RTMP_STAT_L("<address>");
|
||||
NGX_RTMP_STAT_S(&s->connection->addr_text);
|
||||
NGX_RTMP_STAT_L("</address>");
|
||||
|
||||
NGX_RTMP_STAT_L("<dropped>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", ctx->dropped) - buf);
|
||||
NGX_RTMP_STAT_L("</dropped>");
|
||||
|
||||
if (s->flashver.len) {
|
||||
NGX_RTMP_STAT_L("<flashver>");
|
||||
NGX_RTMP_STAT_ES(&s->flashver);
|
||||
NGX_RTMP_STAT_L("</flashver>");
|
||||
}
|
||||
|
||||
if (s->page_url.len) {
|
||||
NGX_RTMP_STAT_L("<pageurl>");
|
||||
NGX_RTMP_STAT_ES(&s->page_url);
|
||||
NGX_RTMP_STAT_L("</pageurl>");
|
||||
}
|
||||
|
||||
if (ctx->flags & NGX_RTMP_LIVE_PUBLISHING) {
|
||||
NGX_RTMP_STAT_L("<publishing/>");
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("</client>\r\n");
|
||||
}
|
||||
if (ctx->flags & NGX_RTMP_LIVE_PUBLISHING) {
|
||||
publishing = 1;
|
||||
}
|
||||
}
|
||||
total_nclients += nclients;
|
||||
|
||||
NGX_RTMP_STAT_L("<nclients>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
if (publishing) {
|
||||
NGX_RTMP_STAT_L("<publishing/>\r\n");
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("</stream>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("<nclients>");
|
||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||
"%uz", total_nclients) - buf);
|
||||
NGX_RTMP_STAT_L("</nclients>\r\n");
|
||||
|
||||
NGX_RTMP_STAT_L("</live>\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("<application>\r\n");
|
||||
NGX_RTMP_STAT_L("<name>");
|
||||
NGX_RTMP_STAT_ES(&cacf->name);
|
||||
NGX_RTMP_STAT_L("</name>\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("</application>\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("<server>\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("</server>\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("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n");
|
||||
if (slcf->stylesheet.len) {
|
||||
NGX_RTMP_STAT_L("<?xml-stylesheet type=\"text/xsl\" href=\"");
|
||||
NGX_RTMP_STAT_ES(&slcf->stylesheet);
|
||||
NGX_RTMP_STAT_L("\" ?>\r\n");
|
||||
}
|
||||
|
||||
NGX_RTMP_STAT_L("<rtmp>\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("</rtmp>\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;
|
||||
}
|
||||
|
92
stat.xsl
Normal file
92
stat.xsl
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<xsl:template match="/">
|
||||
<html>
|
||||
<head><title>RTMP statistics</title></head>
|
||||
<body>
|
||||
<xsl:apply-templates select="rtmp"/>
|
||||
<hr/>
|
||||
Generated by <a href='https://github.com/arut/nginx-rtmp-module'>NGINX RTMP module</a>
|
||||
</body></html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="rtmp">
|
||||
<table cellspacing="1" cellpadding="5">
|
||||
<tr bgcolor="#999999">
|
||||
<th>RTMP</th>
|
||||
<th>#clients</th>
|
||||
<th>In bytes</th>
|
||||
<th>Out bytes</th>
|
||||
<th>In Kbps</th>
|
||||
<th>Out Kbps</th>
|
||||
<th>State</th>
|
||||
<th>Address</th>
|
||||
<th>Flash version</th>
|
||||
<th>Page URL</th>
|
||||
<th>Dropped</th>
|
||||
<th bgcolor="#ffffff"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"/>
|
||||
<td><xsl:value-of select="in"/></td>
|
||||
<td><xsl:value-of select="out"/></td>
|
||||
<td><xsl:value-of select="round(bwin div 1024)"/></td>
|
||||
<td><xsl:value-of select="round(bwout div 1024)"/></td>
|
||||
<td><xsl:apply-templates select="publishing"/></td>
|
||||
</tr>
|
||||
<xsl:apply-templates select="server"/>
|
||||
</table>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="server">
|
||||
<xsl:apply-templates select="application"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="application">
|
||||
<tr bgcolor="#999999">
|
||||
<td><b><xsl:value-of select="name"/></b></td>
|
||||
</tr>
|
||||
<xsl:apply-templates select="live"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="live">
|
||||
<tr bgcolor="#aaaaaa">
|
||||
<td><i>live streams</i></td>
|
||||
<td align="middle"><xsl:value-of select="nclients"/></td>
|
||||
</tr>
|
||||
<xsl:apply-templates select="stream"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="stream">
|
||||
<tr valign="top" bgcolor="#cccccc">
|
||||
<td><xsl:value-of select="name"/></td>
|
||||
<td align="middle"><xsl:value-of select="nclients"/></td>
|
||||
<td><xsl:value-of select="in"/></td>
|
||||
<td><xsl:value-of select="out"/></td>
|
||||
<td><xsl:value-of select="round(bwin div 1024)"/></td>
|
||||
<td><xsl:value-of select="round(bwout div 1024)"/></td>
|
||||
<td><xsl:apply-templates select="publishing"/></td>
|
||||
<td bgcolor="#ffffff">
|
||||
<xsl:apply-templates select="client"/>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="client">
|
||||
<tr bgcolor="#eeeeee">
|
||||
<td colspan="6" bgcolor="#ffffff"/>
|
||||
<td><xsl:apply-templates select="publishing"/></td>
|
||||
<td><xsl:value-of select="address"/></td>
|
||||
<td><xsl:value-of select="flashver"/></td>
|
||||
<td><xsl:value-of select="pageurl"/></td>
|
||||
<td><xsl:value-of select="dropped"/></td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="publishing">
|
||||
publishing
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue