diff --git a/README.md b/README.md index ca4a8b4..f4b5247 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,26 @@ ## nginx-rtmp-module -### Project blog: +### Project blog http://rarut.wordpress.com -### Wiki manual: +### Wiki manual https://github.com/arut/nginx-rtmp-module/wiki/Directives -### Features: +### Google group + + https://groups.google.com/group/nginx-rtmp + + https://groups.google.com/group/nginx-rtmp-ru (Russian) + +### Features * Live streaming of video/audio -* Video on demand FLV/MP4 +* Video on demand FLV/MP4, + playing from local filesystem or HTTP * Stream relay support for distributed streaming: push & pull models @@ -29,10 +36,12 @@ requires recent libavformat (>= 53.31.100) from ffmpeg (ffmpeg.org) -* HTTP callbacks (publish/play/record etc) +* HTTP callbacks (publish/play/record/update etc) * Running external programs on certain events (exec) +* HTTP control module for recording audio/video and dropping clients + * Advanced buffering techniques to keep memory allocations at a minimum level for faster streaming and low @@ -49,7 +58,7 @@ * Linux/FreeBSD/MacOS -### Build: +### Build cd to NGINX source directory & run this: @@ -57,8 +66,11 @@ cd to NGINX source directory & run this: make make install +See this article about building nginx-rtmp with HLS support: +https://github.com/arut/nginx-rtmp-module/wiki/Building-nginx-rtmp-with-HLS-support -### RTMP URL format: + +### RTMP URL format rtmp://rtmp.example.com/app[/name] @@ -77,7 +89,7 @@ to nginx workers. This option is toggled with rtmp_auto_push directive. -### Example nginx.conf: +### Example nginx.conf rtmp { @@ -262,8 +274,8 @@ rtmp_auto_push directive. } +### Multi-worker streaming example - # Multi-worker streaming rtmp_auto_push on; rtmp { diff --git a/ngx_rtmp_enotify_module.c b/ngx_rtmp_enotify_module.c index 4acea5f..87fe586 100644 --- a/ngx_rtmp_enotify_module.c +++ b/ngx_rtmp_enotify_module.c @@ -19,7 +19,7 @@ static ngx_rtmp_publish_pt next_publish; static ngx_rtmp_play_pt next_play; -static ngx_rtmp_delete_stream_pt next_delete_stream; +static ngx_rtmp_close_stream_pt next_close_stream; static ngx_rtmp_record_done_pt next_record_done; @@ -402,7 +402,7 @@ next: static ngx_int_t -ngx_rtmp_enotify_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t +ngx_rtmp_enotify_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_enotify_ctx_t *ctx; @@ -450,7 +450,7 @@ ngx_rtmp_enotify_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t ctx->flags = 0; next: - return next_delete_stream(s, v); + return next_close_stream(s, v); } @@ -567,8 +567,8 @@ ngx_rtmp_enotify_postconfiguration(ngx_conf_t *cf) next_play = ngx_rtmp_play; ngx_rtmp_play = ngx_rtmp_enotify_play; - next_delete_stream = ngx_rtmp_delete_stream; - ngx_rtmp_delete_stream = ngx_rtmp_enotify_delete_stream; + next_close_stream = ngx_rtmp_close_stream; + ngx_rtmp_close_stream = ngx_rtmp_enotify_close_stream; next_record_done = ngx_rtmp_record_done; ngx_rtmp_record_done = ngx_rtmp_enotify_record_done; diff --git a/ngx_rtmp_exec_module.c b/ngx_rtmp_exec_module.c index b3b8308..1c26bcd 100644 --- a/ngx_rtmp_exec_module.c +++ b/ngx_rtmp_exec_module.c @@ -13,7 +13,7 @@ static ngx_rtmp_publish_pt next_publish; -static ngx_rtmp_delete_stream_pt next_delete_stream; +static ngx_rtmp_close_stream_pt next_close_stream; static ngx_int_t ngx_rtmp_exec_postconfiguration(ngx_conf_t *cf); @@ -431,7 +431,7 @@ ngx_rtmp_exec_run(ngx_rtmp_session_t *s, size_t n) static ngx_int_t -ngx_rtmp_exec_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v) +ngx_rtmp_exec_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_exec_app_conf_t *eacf; ngx_rtmp_exec_ctx_t *ctx; @@ -457,7 +457,7 @@ ngx_rtmp_exec_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v) } next: - return next_delete_stream(s, v); + return next_close_stream(s, v); } @@ -599,8 +599,8 @@ ngx_rtmp_exec_postconfiguration(ngx_conf_t *cf) next_publish = ngx_rtmp_publish; ngx_rtmp_publish = ngx_rtmp_exec_publish; - next_delete_stream = ngx_rtmp_delete_stream; - ngx_rtmp_delete_stream = ngx_rtmp_exec_delete_stream; + next_close_stream = ngx_rtmp_close_stream; + ngx_rtmp_close_stream = ngx_rtmp_exec_close_stream; return NGX_OK; } diff --git a/ngx_rtmp_mp4_module.c b/ngx_rtmp_mp4_module.c index fbb2661..ba679ff 100644 --- a/ngx_rtmp_mp4_module.c +++ b/ngx_rtmp_mp4_module.c @@ -880,7 +880,7 @@ ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->chunks = (ngx_rtmp_mp4_chunks_t *) pos; - if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->times->entry_count) * + if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) * sizeof(t->chunks->entries[0]) <= last) { diff --git a/ngx_rtmp_netcall_module.c b/ngx_rtmp_netcall_module.c index 1f52b32..cc97333 100644 --- a/ngx_rtmp_netcall_module.c +++ b/ngx_rtmp_netcall_module.c @@ -55,14 +55,14 @@ static ngx_command_t ngx_rtmp_netcall_commands[] = { { ngx_string("netcall_timeout"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, - NGX_RTMP_SRV_CONF_OFFSET, + NGX_RTMP_APP_CONF_OFFSET, offsetof(ngx_rtmp_netcall_app_conf_t, timeout), NULL }, { ngx_string("netcall_buffer"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, - NGX_RTMP_SRV_CONF_OFFSET, + NGX_RTMP_APP_CONF_OFFSET, offsetof(ngx_rtmp_netcall_app_conf_t, bufsize), NULL }, @@ -491,58 +491,76 @@ ngx_rtmp_netcall_send(ngx_event_t *wev) ngx_chain_t * -ngx_rtmp_netcall_http_format_header(ngx_int_t method, ngx_str_t *uri, - ngx_str_t *host, ngx_pool_t *pool, - size_t content_length, - ngx_str_t *content_type) +ngx_rtmp_netcall_http_format_request(ngx_int_t method, ngx_str_t *host, + ngx_str_t *uri, ngx_chain_t *args, + ngx_chain_t *body, ngx_pool_t *pool, + ngx_str_t *content_type) { - ngx_chain_t *cl; + ngx_chain_t *al, *bl, *ret; ngx_buf_t *b; - const char *method_s; + size_t content_length; + static const char *methods[2] = { "GET", "POST" }; + static const char rq_tmpl[] = " HTTP/1.0\r\n" + "Host: %V\r\n" + "Content-Type: %V\r\n" + "Connection: Close\r\n" + "Content-Length: %uz\r\n" + "\r\n"; - static char rq_tmpl[] = - "%s %V HTTP/1.0\r\n" - "Host: %V\r\n" - "Content-Type: %V\r\n" - "Connection: Close\r\n" - "Content-Length: %uz\r\n" - "\r\n" - ; + content_length = 0; + for (al = body; al; al = al->next) { + b = al->buf; + content_length += (b->last - b->pos); + } - cl = ngx_alloc_chain_link(pool); - if (cl == NULL) { + /* create first buffer */ + + al = ngx_alloc_chain_link(pool); + if (al == NULL) { return NULL; } - - b = ngx_create_temp_buf(pool, sizeof(rq_tmpl) - + sizeof("POST") - 1 /* longest method */ - + uri->len - + host->len - + content_type->len - + 5); + b = ngx_create_temp_buf(pool, sizeof("POST") + /* longest method + 1 */ + uri->len); if (b == NULL) { return NULL; } - cl->buf = b; - cl->next = NULL; + b->last = ngx_snprintf(b->last, b->end - b->last, "%s %V", + methods[method], uri); - switch (method) { - case NGX_RTMP_NETCALL_HTTP_GET: - method_s = "GET"; - break; - case NGX_RTMP_NETCALL_HTTP_POST: - method_s = "POST"; - break; - default: - return NULL; + al->buf = b; + + ret = al; + + if (args) { + *b->last++ = '?'; + al->next = args; + for (al = args; al->next; al = al->next); } - b->last = ngx_snprintf(b->last, b->end - b->last, rq_tmpl, - method_s, uri, host, content_type, content_length); + /* create second buffer */ - return cl; + bl = ngx_alloc_chain_link(pool); + if (bl == NULL) { + return NULL; + } + + b = ngx_create_temp_buf(pool, sizeof(rq_tmpl) + host->len + + content_type->len + NGX_OFF_T_LEN); + if (b == NULL) { + return NULL; + } + + bl->buf = b; + + b->last = ngx_snprintf(b->last, b->end - b->last, rq_tmpl, + host, content_type, content_length); + + al->next = bl; + bl->next = body; + + return ret; } @@ -551,6 +569,9 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool) { ngx_chain_t *cl; ngx_buf_t *b; + ngx_str_t *addr_text; + + addr_text = &s->connection->addr_text; cl = ngx_alloc_chain_link(pool); if (cl == NULL) { @@ -562,7 +583,8 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool) sizeof("&flashver=") - 1 + s->flashver.len * 3 + sizeof("&swfurl=") - 1 + s->swf_url.len * 3 + sizeof("&tcurl=") - 1 + s->tc_url.len * 3 + - sizeof("&pageurl=") - 1 + s->page_url.len * 3 + sizeof("&pageurl=") - 1 + s->page_url.len * 3 + + sizeof("&addr=") - 1 + addr_text->len * 3 ); if (b == NULL) { @@ -570,29 +592,35 @@ ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool) } cl->buf = b; + cl->next = NULL; - b->last = ngx_cpymem(b->last, (u_char*)"app=", sizeof("app=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, s->app.data, s->app.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "app=", sizeof("app=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, s->app.data, s->app.len, + NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&flashver=", - sizeof("&flashver=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, s->flashver.data, - s->flashver.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&flashver=", + sizeof("&flashver=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, s->flashver.data, + s->flashver.len, NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&swfurl=", - sizeof("&swfurl=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, s->swf_url.data, - s->swf_url.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&swfurl=", + sizeof("&swfurl=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, s->swf_url.data, + s->swf_url.len, NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&tcurl=", - sizeof("&tcurl=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, s->tc_url.data, - s->tc_url.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&tcurl=", + sizeof("&tcurl=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, s->tc_url.data, + s->tc_url.len, NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&pageurl=", - sizeof("&pageurl=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, s->page_url.data, - s->page_url.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&pageurl=", + sizeof("&pageurl=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, s->page_url.data, + s->page_url.len, NGX_ESCAPE_ARGS); + + b->last = ngx_cpymem(b->last, (u_char*) "&addr=", sizeof("&addr=") -1); + b->last = (u_char*) ngx_escape_uri(b->last, addr_text->data, + addr_text->len, NGX_ESCAPE_ARGS); return cl; } diff --git a/ngx_rtmp_netcall_module.h b/ngx_rtmp_netcall_module.h index c6c03ab..5a85894 100644 --- a/ngx_rtmp_netcall_module.h +++ b/ngx_rtmp_netcall_module.h @@ -20,8 +20,8 @@ typedef ngx_int_t (*ngx_rtmp_netcall_sink_pt)(ngx_rtmp_session_t *s, typedef ngx_int_t (*ngx_rtmp_netcall_handle_pt)(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in); -#define NGX_RTMP_NETCALL_HTTP_GET 1 -#define NGX_RTMP_NETCALL_HTTP_POST 2 +#define NGX_RTMP_NETCALL_HTTP_GET 0 +#define NGX_RTMP_NETCALL_HTTP_POST 1 /* If handle is NULL then netcall is created detached @@ -51,9 +51,9 @@ ngx_int_t ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, /* HTTP handling */ ngx_chain_t * ngx_rtmp_netcall_http_format_session(ngx_rtmp_session_t *s, ngx_pool_t *pool); -ngx_chain_t * ngx_rtmp_netcall_http_format_header(ngx_int_t method, - ngx_str_t *uri, ngx_str_t *host, ngx_pool_t *pool, - size_t content_length, ngx_str_t *content_type); +ngx_chain_t * ngx_rtmp_netcall_http_format_request(ngx_int_t method, + ngx_str_t *host, ngx_str_t *uri, ngx_chain_t *args, ngx_chain_t *body, + ngx_pool_t *pool, ngx_str_t *content_type); ngx_chain_t * ngx_rtmp_netcall_http_skip_header(ngx_chain_t *in); diff --git a/ngx_rtmp_notify_module.c b/ngx_rtmp_notify_module.c index 263b0fa..afee7c7 100644 --- a/ngx_rtmp_notify_module.c +++ b/ngx_rtmp_notify_module.c @@ -14,18 +14,20 @@ static ngx_rtmp_publish_pt next_publish; static ngx_rtmp_play_pt next_play; -static ngx_rtmp_delete_stream_pt next_delete_stream; +static ngx_rtmp_close_stream_pt next_close_stream; static ngx_rtmp_record_done_pt next_record_done; static char *ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_rtmp_notify_method(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_int_t ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_notify_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_notify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname, - ngx_url_t *url); + ngx_uint_t url_idx); ngx_str_t ngx_rtmp_notify_urlencoded = @@ -43,6 +45,7 @@ enum { NGX_RTMP_NOTIFY_PUBLISH_DONE, NGX_RTMP_NOTIFY_DONE, NGX_RTMP_NOTIFY_RECORD_DONE, + NGX_RTMP_NOTIFY_UPDATE, NGX_RTMP_NOTIFY_MAX }; @@ -50,6 +53,9 @@ enum { typedef struct { ngx_url_t *url[NGX_RTMP_NOTIFY_MAX]; ngx_flag_t active; + ngx_uint_t method; + ngx_msec_t update_timeout; + ngx_flag_t update_strict; } ngx_rtmp_notify_app_conf_t; @@ -57,12 +63,13 @@ typedef struct { ngx_uint_t flags; u_char name[NGX_RTMP_MAX_NAME]; u_char args[NGX_RTMP_MAX_ARGS]; + ngx_event_t update_evt; } ngx_rtmp_notify_ctx_t; typedef struct { u_char *cbname; - ngx_url_t *url; + ngx_uint_t url_idx; } ngx_rtmp_notify_done_t; @@ -111,6 +118,34 @@ static ngx_command_t ngx_rtmp_notify_commands[] = { 0, NULL }, + { ngx_string("on_update"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_rtmp_notify_on_event, + NGX_RTMP_APP_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("notify_method"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_rtmp_notify_method, + NGX_RTMP_APP_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("notify_update_timeout"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_RTMP_APP_CONF_OFFSET, + offsetof(ngx_rtmp_notify_app_conf_t, update_timeout), + NULL }, + + { ngx_string("notify_update_strict"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_conf_set_flag_slot, + NGX_RTMP_APP_CONF_OFFSET, + offsetof(ngx_rtmp_notify_app_conf_t, update_strict), + NULL }, + ngx_null_command }; @@ -158,6 +193,10 @@ ngx_rtmp_notify_create_app_conf(ngx_conf_t *cf) nacf->url[n] = NGX_CONF_UNSET_PTR; } + nacf->method = NGX_CONF_UNSET; + nacf->update_timeout = NGX_CONF_UNSET; + nacf->update_strict = NGX_CONF_UNSET; + return nacf; } @@ -180,35 +219,60 @@ ngx_rtmp_notify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) prev->active = 1; } + ngx_conf_merge_uint_value(conf->method, prev->method, + NGX_RTMP_NETCALL_HTTP_POST); + ngx_conf_merge_msec_value(conf->update_timeout, prev->update_timeout, + 30000); + ngx_conf_merge_value(conf->update_strict, prev->update_strict, 0); + return NGX_CONF_OK; } +static ngx_chain_t * +ngx_rtmp_notify_create_request(ngx_rtmp_session_t *s, ngx_pool_t *pool, + ngx_uint_t url_idx, ngx_chain_t *args) +{ + ngx_rtmp_notify_app_conf_t *nacf; + ngx_chain_t *al, *bl, *cl; + ngx_url_t *url; + + nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); + + url = nacf->url[url_idx]; + + al = ngx_rtmp_netcall_http_format_session(s, pool); + if (al == NULL) { + return NULL; + } + + al->next = args; + + bl = NULL; + + if (nacf->method == NGX_RTMP_NETCALL_HTTP_POST) { + cl = al; + al = bl; + bl = cl; + } + + return ngx_rtmp_netcall_http_format_request(nacf->method, &url->host, + &url->uri, al, bl, pool, + &ngx_rtmp_notify_urlencoded); +} + + static ngx_chain_t * ngx_rtmp_notify_publish_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool) { ngx_rtmp_publish_t *v = arg; - ngx_rtmp_notify_app_conf_t *nacf; - ngx_chain_t *hl, *cl, *pl; + ngx_chain_t *pl; ngx_buf_t *b; - ngx_str_t *addr_text; - ngx_url_t *url; size_t name_len, type_len, args_len; - nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - - /* common variables */ - cl = ngx_rtmp_netcall_http_format_session(s, pool); - - if (cl == NULL) { - return NULL; - } - - /* publish variables */ pl = ngx_alloc_chain_link(pool); - if (pl == NULL) { return NULL; } @@ -216,54 +280,36 @@ ngx_rtmp_notify_publish_create(ngx_rtmp_session_t *s, void *arg, name_len = ngx_strlen(v->name); type_len = ngx_strlen(v->type); args_len = ngx_strlen(v->args); - addr_text = &s->connection->addr_text; b = ngx_create_temp_buf(pool, - sizeof("&call=publish") + - sizeof("&addr=") + addr_text->len *3 + - sizeof("&name=") + name_len * 3 + - sizeof("&type=") + type_len * 3 + - 1 + args_len); + sizeof("&call=publish") + + sizeof("&name=") + name_len * 3 + + sizeof("&type=") + type_len * 3 + + 1 + args_len); if (b == NULL) { return NULL; } pl->buf = b; + pl->next = NULL; - b->last = ngx_cpymem(b->last, (u_char*)"&call=publish", - sizeof("&call=publish") - 1); + b->last = ngx_cpymem(b->last, (u_char*) "&call=publish", + sizeof("&call=publish") - 1); - b->last = ngx_cpymem(b->last, (u_char*)"&addr=", sizeof("&addr=") -1); - b->last = (u_char*)ngx_escape_uri(b->last, addr_text->data, - addr_text->len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, v->name, name_len, + NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&name=", sizeof("&name=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, v->name, name_len, 0); - - b->last = ngx_cpymem(b->last, (u_char*)"&type=", sizeof("&type=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, v->type, type_len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&type=", sizeof("&type=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, v->type, type_len, + NGX_ESCAPE_ARGS); if (args_len) { *b->last++ = '&'; - b->last = (u_char *)ngx_cpymem(b->last, v->args, args_len); + b->last = (u_char *) ngx_cpymem(b->last, v->args, args_len); } - /* HTTP header */ - url = nacf->url[NGX_RTMP_NOTIFY_PUBLISH]; - hl = ngx_rtmp_netcall_http_format_header(NGX_RTMP_NETCALL_HTTP_POST, - &url->uri, &url->host, - pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos), - &ngx_rtmp_notify_urlencoded); - - if (hl == NULL) { - return NULL; - } - - hl->next = cl; - cl->next = pl; - pl->next = NULL; - - return hl; + return ngx_rtmp_notify_create_request(s, pool, NGX_RTMP_NOTIFY_PUBLISH, pl); } @@ -273,80 +319,48 @@ ngx_rtmp_notify_play_create(ngx_rtmp_session_t *s, void *arg, { ngx_rtmp_play_t *v = arg; - ngx_rtmp_notify_app_conf_t *nacf; - ngx_chain_t *hl, *cl, *pl; + ngx_chain_t *pl; ngx_buf_t *b; - ngx_str_t *addr_text; - ngx_url_t *url; size_t name_len, args_len; - nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - - /* common variables */ - cl = ngx_rtmp_netcall_http_format_session(s, pool); - - if (cl == NULL) { - return NULL; - } - - /* play variables */ pl = ngx_alloc_chain_link(pool); - if (pl == NULL) { return NULL; } name_len = ngx_strlen(v->name); args_len = ngx_strlen(v->args); - addr_text = &s->connection->addr_text; b = ngx_create_temp_buf(pool, - sizeof("&call=play") + - sizeof("&addr=") + addr_text->len * 3 + - sizeof("&name=") + name_len * 3 + - sizeof("&start=&duration=&reset=") + NGX_OFF_T_LEN * 3 - + 1 + args_len); + sizeof("&call=play") + + sizeof("&name=") + name_len * 3 + + sizeof("&start=&duration=&reset=") + + NGX_OFF_T_LEN * 3 + 1 + args_len); if (b == NULL) { return NULL; } pl->buf = b; + pl->next = NULL; - b->last = ngx_cpymem(b->last, (u_char*)"&call=play", - sizeof("&call=play") - 1); + b->last = ngx_cpymem(b->last, (u_char*) "&call=play", + sizeof("&call=play") - 1); - b->last = ngx_cpymem(b->last, (u_char*)"&addr=", sizeof("&addr=") -1); - b->last = (u_char*)ngx_escape_uri(b->last, addr_text->data, - addr_text->len, 0); - - b->last = ngx_cpymem(b->last, (u_char*)"&name=", sizeof("&name=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, v->name, name_len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, v->name, name_len, + NGX_ESCAPE_ARGS); b->last = ngx_snprintf(b->last, b->end - b->last, - "&start=%uD&duration=%uD&reset=%d", - (uint32_t)v->start, (uint32_t)v->duration, v->reset & 1); + "&start=%uD&duration=%uD&reset=%d", + (uint32_t) v->start, (uint32_t) v->duration, + v->reset & 1); if (args_len) { *b->last++ = '&'; - b->last = (u_char *)ngx_cpymem(b->last, v->args, args_len); + b->last = (u_char *) ngx_cpymem(b->last, v->args, args_len); } - /* HTTP header */ - url = nacf->url[NGX_RTMP_NOTIFY_PLAY]; - hl = ngx_rtmp_netcall_http_format_header(NGX_RTMP_NETCALL_HTTP_POST, - &url->uri, &url->host, - pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos), - &ngx_rtmp_notify_urlencoded); - - if (hl == NULL) { - return NULL; - } - - hl->next = cl; - cl->next = pl; - pl->next = NULL; - - return hl; + return ngx_rtmp_notify_create_request(s, pool, NGX_RTMP_NOTIFY_PLAY, pl); } @@ -356,23 +370,14 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg, { ngx_rtmp_notify_done_t *ds = arg; - ngx_chain_t *hl, *cl, *pl; + ngx_chain_t *pl; ngx_buf_t *b; size_t cbname_len, name_len, args_len; - ngx_str_t *addr_text; ngx_rtmp_notify_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module); - /* common variables */ - cl = ngx_rtmp_netcall_http_format_session(s, pool); - - if (cl == NULL) { - return NULL; - } - pl = ngx_alloc_chain_link(pool); - if (pl == NULL) { return NULL; } @@ -380,51 +385,91 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg, cbname_len = ngx_strlen(ds->cbname); name_len = ctx ? ngx_strlen(ctx->name) : 0; args_len = ctx ? ngx_strlen(ctx->args) : 0; - addr_text = &s->connection->addr_text; b = ngx_create_temp_buf(pool, - sizeof("&call=") + cbname_len + - sizeof("&addr=") + addr_text->len * 3 + - sizeof("&name=") + name_len * 3 - + 1 + args_len); + sizeof("&call=") + cbname_len + + sizeof("&name=") + name_len * 3 + + 1 + args_len); if (b == NULL) { return NULL; } pl->buf = b; + pl->next = NULL; - b->last = ngx_cpymem(b->last, (u_char*)"&call=", sizeof("&call=") - 1); + b->last = ngx_cpymem(b->last, (u_char*) "&call=", sizeof("&call=") - 1); b->last = ngx_cpymem(b->last, ds->cbname, cbname_len); - b->last = ngx_cpymem(b->last, (u_char*)"&addr=", sizeof("&addr=") -1); - b->last = (u_char*)ngx_escape_uri(b->last, addr_text->data, - addr_text->len, 0); - if (name_len) { - b->last = ngx_cpymem(b->last, (u_char*)"&name=", sizeof("&name=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, ctx->name, name_len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, ctx->name, name_len, + NGX_ESCAPE_ARGS); } if (args_len) { *b->last++ = '&'; - b->last = (u_char *)ngx_cpymem(b->last, ctx->args, args_len); + b->last = (u_char *) ngx_cpymem(b->last, ctx->args, args_len); } - /* HTTP header */ - hl = ngx_rtmp_netcall_http_format_header(NGX_RTMP_NETCALL_HTTP_POST, - &ds->url->uri, &ds->url->host, - pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos), - &ngx_rtmp_notify_urlencoded); + return ngx_rtmp_notify_create_request(s, pool, ds->url_idx, pl); +} - if (hl == NULL) { + +static ngx_chain_t * +ngx_rtmp_notify_update_create(ngx_rtmp_session_t *s, void *arg, + ngx_pool_t *pool) +{ + ngx_chain_t *pl; + ngx_buf_t *b; + size_t name_len, args_len; + ngx_rtmp_notify_ctx_t *ctx; + ngx_str_t sfx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module); + + pl = ngx_alloc_chain_link(pool); + if (pl == NULL) { return NULL; } - hl->next = cl; - cl->next = pl; + if (ctx->flags & NGX_RTMP_NOTIFY_PUBLISHING) { + ngx_str_set(&sfx, "_publish"); + } else if (ctx->flags & NGX_RTMP_NOTIFY_PLAYING) { + ngx_str_set(&sfx, "_play"); + } else { + ngx_str_null(&sfx); + } + + name_len = ctx ? ngx_strlen(ctx->name) : 0; + args_len = ctx ? ngx_strlen(ctx->args) : 0; + + b = ngx_create_temp_buf(pool, + sizeof("&call=update") + sfx.len + + sizeof("&name=") + name_len * 3 + + 1 + args_len); + if (b == NULL) { + return NULL; + } + + pl->buf = b; pl->next = NULL; - return hl; + b->last = ngx_cpymem(b->last, (u_char*) "&call=update", + sizeof("&call=update") - 1); + b->last = ngx_cpymem(b->last, sfx.data, sfx.len); + + if (name_len) { + b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, ctx->name, name_len, + NGX_ESCAPE_ARGS); + } + + if (args_len) { + *b->last++ = '&'; + b->last = (u_char *) ngx_cpymem(b->last, ctx->args, args_len); + } + + return ngx_rtmp_notify_create_request(s, pool, NGX_RTMP_NOTIFY_UPDATE, pl); } @@ -434,88 +479,57 @@ ngx_rtmp_notify_record_done_create(ngx_rtmp_session_t *s, void *arg, { ngx_rtmp_record_done_t *v = arg; - ngx_rtmp_notify_app_conf_t *nacf; ngx_rtmp_notify_ctx_t *ctx; - ngx_chain_t *hl, *cl, *pl; + ngx_chain_t *pl; ngx_buf_t *b; - ngx_str_t *addr_text; - ngx_url_t *url; size_t name_len, args_len; - nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module); - /* common variables */ - cl = ngx_rtmp_netcall_http_format_session(s, pool); - - if (cl == NULL) { - return NULL; - } - - /* publish variables */ pl = ngx_alloc_chain_link(pool); - if (pl == NULL) { return NULL; } name_len = ngx_strlen(ctx->name); args_len = ngx_strlen(ctx->args); - addr_text = &s->connection->addr_text; b = ngx_create_temp_buf(pool, sizeof("&call=record_done") + sizeof("&recorder=") + v->recorder.len + - sizeof("&addr=") + addr_text->len *3 + sizeof("&name=") + name_len * 3 + sizeof("&path=") + v->path.len * 3 + - + 1 + args_len); + 1 + args_len); if (b == NULL) { return NULL; } pl->buf = b; + pl->next = NULL; - b->last = ngx_cpymem(b->last, (u_char*)"&call=record_done", + b->last = ngx_cpymem(b->last, (u_char*) "&call=record_done", sizeof("&call=record_done") - 1); - b->last = ngx_cpymem(b->last, (u_char *)"&recorder=", + b->last = ngx_cpymem(b->last, (u_char *) "&recorder=", sizeof("&recorder=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, v->recorder.data, - v->recorder.len, 0); + b->last = (u_char*) ngx_escape_uri(b->last, v->recorder.data, + v->recorder.len, NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&addr=", sizeof("&addr=") -1); - b->last = (u_char*)ngx_escape_uri(b->last, addr_text->data, - addr_text->len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, ctx->name, name_len, + NGX_ESCAPE_ARGS); - b->last = ngx_cpymem(b->last, (u_char*)"&name=", sizeof("&name=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, ctx->name, name_len, 0); - - b->last = ngx_cpymem(b->last, (u_char*)"&path=", sizeof("&path=") - 1); - b->last = (u_char*)ngx_escape_uri(b->last, v->path.data, v->path.len, 0); + b->last = ngx_cpymem(b->last, (u_char*) "&path=", sizeof("&path=") - 1); + b->last = (u_char*) ngx_escape_uri(b->last, v->path.data, v->path.len, + NGX_ESCAPE_ARGS); if (args_len) { *b->last++ = '&'; - b->last = (u_char *)ngx_cpymem(b->last, ctx->args, args_len); + b->last = (u_char *) ngx_cpymem(b->last, ctx->args, args_len); } - /* HTTP header */ - url = nacf->url[NGX_RTMP_NOTIFY_RECORD_DONE]; - hl = ngx_rtmp_netcall_http_format_header(NGX_RTMP_NETCALL_HTTP_POST, - &url->uri, &url->host, - pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos), - &ngx_rtmp_notify_urlencoded); - - if (hl == NULL) { - return NULL; - } - - hl->next = cl; - cl->next = pl; - pl->next = NULL; - - return hl; + return ngx_rtmp_notify_create_request(s, pool, NGX_RTMP_NOTIFY_RECORD_DONE, + pl); } @@ -550,9 +564,15 @@ ngx_rtmp_notify_parse_http_retcode(ngx_rtmp_session_t *s, } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: invalid HTTP response"); + "notify: empty or broken HTTP response"); - return NGX_ERROR; + /* + * not enough data; + * it can happen in case of empty or broken reply; + * let the caller decide if that's an error or not + */ + + return NGX_DONE; } @@ -580,6 +600,74 @@ ngx_rtmp_notify_play_handle(ngx_rtmp_session_t *s, } +static ngx_int_t +ngx_rtmp_notify_update_handle(ngx_rtmp_session_t *s, + void *arg, ngx_chain_t *in) +{ + ngx_rtmp_notify_app_conf_t *nacf; + ngx_rtmp_notify_ctx_t *ctx; + ngx_int_t rc; + + nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); + + rc = ngx_rtmp_notify_parse_http_retcode(s, in); + + if ((!nacf->update_strict && rc == NGX_ERROR) || + (nacf->update_strict && rc != NGX_OK)) + { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: update failed"); + + return NGX_ERROR; + } + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module); + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "notify: schedule update %Mms", + nacf->update_timeout); + + ngx_add_timer(&ctx->update_evt, nacf->update_timeout); + + return NGX_OK; +} + + +static void +ngx_rtmp_notify_update(ngx_event_t *e) +{ + ngx_connection_t *c; + ngx_rtmp_session_t *s; + ngx_rtmp_notify_app_conf_t *nacf; + ngx_rtmp_netcall_init_t ci; + ngx_url_t *url; + + c = e->data; + s = c->data; + + nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); + + url = nacf->url[NGX_RTMP_NOTIFY_UPDATE]; + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: update '%V'", &url->url); + + ngx_memzero(&ci, sizeof(ci)); + + ci.url = url; + ci.create = ngx_rtmp_notify_update_create; + ci.handle = ngx_rtmp_notify_update_handle; + + if (ngx_rtmp_netcall_create(s, &ci) == NGX_OK) { + return; + } + + /* schedule next update on connection error */ + + ngx_rtmp_notify_update_handle(s, NULL, NULL); +} + + static void ngx_rtmp_notify_init(ngx_rtmp_session_t *s, u_char name[NGX_RTMP_MAX_NAME], u_char args[NGX_RTMP_MAX_ARGS], @@ -587,9 +675,9 @@ ngx_rtmp_notify_init(ngx_rtmp_session_t *s, { ngx_rtmp_notify_ctx_t *ctx; ngx_rtmp_notify_app_conf_t *nacf; + ngx_event_t *e; nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - if (!nacf->active) { return; } @@ -609,6 +697,28 @@ ngx_rtmp_notify_init(ngx_rtmp_session_t *s, ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS); ctx->flags |= flags; + + if (nacf->url[NGX_RTMP_NOTIFY_UPDATE] == NULL || + nacf->update_timeout == 0) + { + return; + } + + if (ctx->update_evt.timer_set) { + return; + } + + e = &ctx->update_evt; + + e->data = s->connection; + e->log = s->connection->log; + e->handler = ngx_rtmp_notify_update; + + ngx_add_timer(e, nacf->update_timeout); + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "notify: schedule initial update %Mms", + nacf->update_timeout); } @@ -617,30 +727,31 @@ ngx_rtmp_notify_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_notify_app_conf_t *nacf; ngx_rtmp_netcall_init_t ci; + ngx_url_t *url; if (s->auto_pushed) { goto next; } nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - if (nacf == NULL) { goto next; } + url = nacf->url[NGX_RTMP_NOTIFY_PUBLISH]; + ngx_rtmp_notify_init(s, v->name, v->args, NGX_RTMP_NOTIFY_PUBLISHING); - if (nacf->url[NGX_RTMP_NOTIFY_PUBLISH] == NULL) { + if (url == NULL) { goto next; } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: publish '%V'", - &nacf->url[NGX_RTMP_NOTIFY_PUBLISH]->url); + "notify: publish '%V'", &url->url); ngx_memzero(&ci, sizeof(ci)); - ci.url = nacf->url[NGX_RTMP_NOTIFY_PUBLISH]; + ci.url = url; ci.create = ngx_rtmp_notify_publish_create; ci.handle = ngx_rtmp_notify_publish_handle; ci.arg = v; @@ -658,30 +769,31 @@ ngx_rtmp_notify_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_notify_app_conf_t *nacf; ngx_rtmp_netcall_init_t ci; + ngx_url_t *url; if (s->auto_pushed) { goto next; } nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); - if (nacf == NULL) { goto next; } + url = nacf->url[NGX_RTMP_NOTIFY_PLAY]; + ngx_rtmp_notify_init(s, v->name, v->args, NGX_RTMP_NOTIFY_PLAYING); - - if (nacf->url[NGX_RTMP_NOTIFY_PLAY] == NULL) { + + if (url == NULL) { goto next; } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: play '%V'", - &nacf->url[NGX_RTMP_NOTIFY_PLAY]->url); + "notify: play '%V'", &url->url); ngx_memzero(&ci, sizeof(ci)); - ci.url = nacf->url[NGX_RTMP_NOTIFY_PLAY]; + ci.url = url; ci.create = ngx_rtmp_notify_play_create; ci.handle = ngx_rtmp_notify_play_handle; ci.arg = v; @@ -695,8 +807,8 @@ next: static ngx_int_t -ngx_rtmp_notify_delete_stream(ngx_rtmp_session_t *s, - ngx_rtmp_delete_stream_t *v) +ngx_rtmp_notify_close_stream(ngx_rtmp_session_t *s, + ngx_rtmp_close_stream_t *v) { ngx_rtmp_notify_ctx_t *ctx; ngx_rtmp_notify_app_conf_t *nacf; @@ -717,28 +829,26 @@ ngx_rtmp_notify_delete_stream(ngx_rtmp_session_t *s, goto next; } - if (nacf->url[NGX_RTMP_NOTIFY_PUBLISH_DONE] && - (ctx->flags & NGX_RTMP_NOTIFY_PUBLISHING)) - { - ngx_rtmp_notify_done(s, "publish_done", - nacf->url[NGX_RTMP_NOTIFY_PUBLISH_DONE]); + if (ctx->flags & NGX_RTMP_NOTIFY_PUBLISHING) { + ngx_rtmp_notify_done(s, "publish_done", NGX_RTMP_NOTIFY_PUBLISH_DONE); } - if (nacf->url[NGX_RTMP_NOTIFY_PLAY_DONE] && - (ctx->flags & NGX_RTMP_NOTIFY_PLAYING)) - { - ngx_rtmp_notify_done(s, "play_done", - nacf->url[NGX_RTMP_NOTIFY_PLAY_DONE]); + if (ctx->flags & NGX_RTMP_NOTIFY_PLAYING) { + ngx_rtmp_notify_done(s, "play_done", NGX_RTMP_NOTIFY_PLAY_DONE); } - if (nacf->url[NGX_RTMP_NOTIFY_DONE] && ctx->flags) { - ngx_rtmp_notify_done(s, "done", nacf->url[NGX_RTMP_NOTIFY_DONE]); + if (ctx->flags) { + ngx_rtmp_notify_done(s, "done", NGX_RTMP_NOTIFY_DONE); + } + + if (ctx->update_evt.timer_set) { + ngx_del_timer(&ctx->update_evt); } ctx->flags = 0; next: - return next_delete_stream(s, v); + return next_close_stream(s, v); } @@ -776,16 +886,25 @@ next: static ngx_int_t -ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname, ngx_url_t *url) +ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname, ngx_uint_t url_idx) { ngx_rtmp_netcall_init_t ci; ngx_rtmp_notify_done_t ds; + ngx_rtmp_notify_app_conf_t *nacf; + ngx_url_t *url; + + nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); + + url = nacf->url[url_idx]; + if (url == NULL) { + return NGX_OK; + } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "notify: %s '%V'", cbname, &url->url); ds.cbname = (u_char *) cbname; - ds.url = url; + ds.url_idx = url_idx; ngx_memzero(&ci, sizeof(ci)); @@ -800,7 +919,8 @@ ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname, ngx_url_t *url) static char * ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { - ngx_rtmp_notify_app_conf_t *nacf; + ngx_rtmp_notify_app_conf_t *nacf = conf; + ngx_str_t *url, *name; ngx_url_t *u; size_t add; @@ -835,8 +955,6 @@ ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_ERROR; } - nacf = ngx_rtmp_conf_get_module_app_conf(cf, ngx_rtmp_notify_module); - n = 0; switch (name->len) { @@ -848,6 +966,10 @@ ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } break; + case sizeof("on_update") - 1: + n = NGX_RTMP_NOTIFY_UPDATE; + break; + case sizeof("on_publish") - 1: n = NGX_RTMP_NOTIFY_PUBLISH; break; @@ -871,6 +993,34 @@ ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) } +static char * +ngx_rtmp_notify_method(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_rtmp_notify_app_conf_t *nacf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + value++; + + if (value->len == sizeof("get") - 1 && + ngx_strncasecmp(value->data, (u_char *) "get", value->len) == 0) + { + nacf->method = NGX_RTMP_NETCALL_HTTP_GET; + + } else if (value->len == sizeof("post") - 1 && + ngx_strncasecmp(value->data, (u_char *) "post", value->len) == 0) + { + nacf->method = NGX_RTMP_NETCALL_HTTP_POST; + + } else { + return "got unexpected method"; + } + + return NGX_CONF_OK; +} + + static ngx_int_t ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf) { @@ -880,8 +1030,8 @@ ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf) next_play = ngx_rtmp_play; ngx_rtmp_play = ngx_rtmp_notify_play; - next_delete_stream = ngx_rtmp_delete_stream; - ngx_rtmp_delete_stream = ngx_rtmp_notify_delete_stream; + next_close_stream = ngx_rtmp_close_stream; + ngx_rtmp_close_stream = ngx_rtmp_notify_close_stream; next_record_done = ngx_rtmp_record_done; ngx_rtmp_record_done = ngx_rtmp_notify_record_done; diff --git a/ngx_rtmp_play_module.c b/ngx_rtmp_play_module.c index b114a41..5751ed1 100644 --- a/ngx_rtmp_play_module.c +++ b/ngx_rtmp_play_module.c @@ -642,7 +642,6 @@ ngx_rtmp_play_remote_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool) ngx_rtmp_play_app_conf_t *pacf; ngx_rtmp_play_ctx_t *ctx; - ngx_chain_t *hl; ngx_str_t *addr_text, uri; u_char *p; size_t args_len, len; @@ -674,7 +673,8 @@ ngx_rtmp_play_remote_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool) p = ngx_cpymem(p, ctx->name.data, ctx->name.len); p = ngx_cpymem(p, (u_char*)"?addr=", sizeof("&addr=") -1); - p = (u_char*)ngx_escape_uri(p, addr_text->data, addr_text->len, 0); + p = (u_char*)ngx_escape_uri(p, addr_text->data, addr_text->len, + NGX_ESCAPE_ARGS); if (args_len) { *p++ = '&'; p = (u_char *) ngx_cpymem(p, v->args, args_len); @@ -682,15 +682,9 @@ ngx_rtmp_play_remote_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool) uri.len = p - uri.data; - /* HTTP header */ - hl = ngx_rtmp_netcall_http_format_header(NGX_RTMP_NETCALL_HTTP_GET, - &uri, &pacf->url->host, pool, 0, &text_plain); - - if (hl == NULL) { - return NULL; - } - - return hl; + return ngx_rtmp_netcall_http_format_request(NGX_RTMP_NETCALL_HTTP_GET, + &pacf->url->host, &uri, + NULL, NULL, pool, &text_plain); } diff --git a/ngx_rtmp_record_module.c b/ngx_rtmp_record_module.c index d355e23..f535d52 100644 --- a/ngx_rtmp_record_module.c +++ b/ngx_rtmp_record_module.c @@ -385,6 +385,19 @@ ngx_rtmp_record_make_path(ngx_rtmp_session_t *s, } +static void +ngx_rtmp_record_notify_error(ngx_rtmp_session_t *s, + ngx_rtmp_record_app_conf_t *rracf) +{ + if (!rracf->notify) { + return; + } + + ngx_rtmp_send_status(s, "NetStream.Record.Failed", "error", + rracf->id.data ? (char *) rracf->id.data : ""); +} + + static ngx_int_t ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx) @@ -425,6 +438,8 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, &rracf->id, &path); } + ngx_rtmp_record_notify_error(s, rracf); + return NGX_OK; } @@ -572,6 +587,8 @@ ngx_rtmp_record_node_close(ngx_rtmp_session_t *s, err = ngx_errno; ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V error closing file", &rracf->id); + + ngx_rtmp_record_notify_error(s, rracf); } rctx->file.fd = NGX_INVALID_FILE; @@ -673,6 +690,10 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s, if (ngx_write_file(&rctx->file, hdr, ph - hdr, rctx->file.offset) == NGX_ERROR) { + ngx_rtmp_record_notify_error(s, rracf); + + ngx_close_file(rctx->file.fd); + return NGX_ERROR; } diff --git a/ngx_rtmp_stat_module.c b/ngx_rtmp_stat_module.c index eb8ec5c..9ea8995 100644 --- a/ngx_rtmp_stat_module.c +++ b/ngx_rtmp_stat_module.c @@ -100,6 +100,44 @@ ngx_module_t ngx_rtmp_stat_module = { #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_http_request_t *r, 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(r->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_http_request_t *r, ngx_chain_t ***lll, void *data, size_t len, ngx_uint_t escape) @@ -112,6 +150,13 @@ ngx_rtmp_stat_output(ngx_http_request_t *r, ngx_chain_t ***lll, return; } + if (escape) { + data = ngx_rtmp_stat_escape(r, data, len); + if (data == NULL) { + return; + } + } + real_len = escape ? len + ngx_escape_html(NULL, data, len) : len;