From 6b8155cf3b32740ce0a55c57582d02799fb20820 Mon Sep 17 00:00:00 2001 From: Diego Stamigni Date: Wed, 8 Feb 2017 18:44:44 +0000 Subject: [PATCH] "exec_record_started" event and "on_record_started" notification added fired when the system starts the recording process --- .gitignore | 1 + doc/directives.md | 24 +++++++++ ngx_rtmp_exec_module.c | 73 +++++++++++++++++++++++++ ngx_rtmp_notify_module.c | 113 +++++++++++++++++++++++++++++++++++++++ ngx_rtmp_record_module.c | 15 ++++++ ngx_rtmp_record_module.h | 13 +++++ 6 files changed, 239 insertions(+) diff --git a/.gitignore b/.gitignore index 4de62e1..5030bb8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.settings /.project /.cproject +/.vscode diff --git a/doc/directives.md b/doc/directives.md index 4ad5436..9ad1f25 100644 --- a/doc/directives.md +++ b/doc/directives.md @@ -35,6 +35,7 @@ Table of Contents * [exec_play](#exec_play) * [exec_play_done](#exec_play_done) * [exec_publish_done](#exec_publish_done) + * [exec_record_started](#exec_record_started) * [exec_record_done](#exec_record_done) * [Live](#live) * [live](#live) @@ -76,6 +77,7 @@ Table of Contents * [on_done](#on_done) * [on_play_done](#on_play_done) * [on_publish_done](#on_publish_done) + * [on_record_started](#on_record_started) * [on_record_done](#on_record_done) * [on_update](#on_update) * [notify_update_timeout](#notify_update_timeout) @@ -479,6 +481,18 @@ Specifies external command with arguments to be executed on publish_done event. Return code is not analyzed. Substitution list is the same as for `exec_publish`. +#### exec_record_started +Syntax: `exec_record_started command arg*` +Context: rtmp, server, application, recorder + +Specifies external command with arguments to be executed when +recording is started. +* `recorder` - recorder name +* `path` - recorded file path (`/tmp/rec/mystream-1389499351.flv`) +* `filename` - path with directory omitted (`mystream-1389499351.flv`) +* `basename` - file name with extension omitted (`mystream-1389499351`) +* `dirname` - directory path (`/tmp/rec`) + #### exec_record_done Syntax: `exec_record_done command arg*` Context: rtmp, server, application, recorder @@ -1106,6 +1120,16 @@ Context: rtmp, server, application Same behavior as `on_done` but only for publish end event. +#### on_record_started +syntax: `on_record_started url` +context: rtmp, server, application, recorder + +Set record_started callback. In addition to common HTTP callback +variables it receives the following values +* recorder - recorder name in config or empty string for inline recorder +* path - recording file path + + #### on_record_done syntax: `on_record_done url` context: rtmp, server, application, recorder diff --git a/ngx_rtmp_exec_module.c b/ngx_rtmp_exec_module.c index 6f298a7..5d1f340 100644 --- a/ngx_rtmp_exec_module.c +++ b/ngx_rtmp_exec_module.c @@ -20,6 +20,7 @@ static ngx_rtmp_publish_pt next_publish; static ngx_rtmp_play_pt next_play; static ngx_rtmp_close_stream_pt next_close_stream; +static ngx_rtmp_record_started_pt next_record_started; static ngx_rtmp_record_done_pt next_record_done; #endif @@ -55,6 +56,7 @@ enum { NGX_RTMP_EXEC_PUBLISH_DONE, NGX_RTMP_EXEC_PLAY, NGX_RTMP_EXEC_PLAY_DONE, + NGX_RTMP_EXEC_RECORD_STARTED, NGX_RTMP_EXEC_RECORD_DONE, NGX_RTMP_EXEC_MAX, @@ -208,6 +210,15 @@ static ngx_command_t ngx_rtmp_exec_commands[] = { NGX_RTMP_EXEC_PLAY_DONE * sizeof(ngx_array_t), NULL }, + { ngx_string("exec_record_started"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF| + NGX_CONF_1MORE, + ngx_rtmp_exec_conf, + NGX_RTMP_APP_CONF_OFFSET, + offsetof(ngx_rtmp_exec_app_conf_t, conf) + + NGX_RTMP_EXEC_RECORD_STARTED * sizeof(ngx_array_t), + NULL }, + { ngx_string("exec_record_done"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF| NGX_CONF_1MORE, @@ -1307,6 +1318,64 @@ next: } +static ngx_int_t +ngx_rtmp_exec_record_started(ngx_rtmp_session_t *s, ngx_rtmp_record_started_t *v) +{ + u_char c; + ngx_uint_t ext, dir; + ngx_rtmp_exec_ctx_t *ctx; + ngx_rtmp_exec_app_conf_t *eacf; + + if (s->auto_pushed) { + goto next; + } + + eacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_exec_module); + if (eacf == NULL || !eacf->active) { + goto next; + } + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_exec_module); + if (ctx == NULL) { + goto next; + } + + ctx->recorder = v->recorder; + ctx->path = v->path; + + ctx->dirname.data = ctx->path.data; + ctx->dirname.len = 0; + + for (dir = ctx->path.len; dir > 0; dir--) { + c = ctx->path.data[dir - 1]; + if (c == '/' || c == '\\') { + ctx->dirname.len = dir - 1; + break; + } + } + + ctx->filename.data = ctx->path.data + dir; + ctx->filename.len = ctx->path.len - dir; + + ctx->basename = ctx->filename; + + for (ext = ctx->filename.len; ext > 0; ext--) { + if (ctx->filename.data[ext - 1] == '.') { + ctx->basename.len = ext - 1; + break; + } + } + + ngx_rtmp_exec_unmanaged(s, &eacf->conf[NGX_RTMP_EXEC_RECORD_STARTED], + "record_started"); + + ngx_str_null(&v->recorder); + ngx_str_null(&v->path); + +next: + return next_record_started(s, v); +} + static ngx_int_t ngx_rtmp_exec_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v) { @@ -1364,6 +1433,7 @@ ngx_rtmp_exec_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v) next: return next_record_done(s, v); } + #endif /* NGX_WIN32 */ @@ -1603,6 +1673,9 @@ ngx_rtmp_exec_postconfiguration(ngx_conf_t *cf) next_record_done = ngx_rtmp_record_done; ngx_rtmp_record_done = ngx_rtmp_exec_record_done; + next_record_started = ngx_rtmp_record_started; + ngx_rtmp_record_started = ngx_rtmp_exec_record_started; + #endif /* NGX_WIN32 */ return NGX_OK; diff --git a/ngx_rtmp_notify_module.c b/ngx_rtmp_notify_module.c index 6813624..1780d2c 100644 --- a/ngx_rtmp_notify_module.c +++ b/ngx_rtmp_notify_module.c @@ -19,6 +19,7 @@ static ngx_rtmp_disconnect_pt next_disconnect; static ngx_rtmp_publish_pt next_publish; static ngx_rtmp_play_pt next_play; static ngx_rtmp_close_stream_pt next_close_stream; +static ngx_rtmp_record_started_pt next_record_started; static ngx_rtmp_record_done_pt next_record_done; static ngx_rtmp_playlist_pt next_playlist; @@ -56,6 +57,7 @@ enum { NGX_RTMP_NOTIFY_PLAY_DONE, NGX_RTMP_NOTIFY_PUBLISH_DONE, NGX_RTMP_NOTIFY_DONE, + NGX_RTMP_NOTIFY_RECORD_STARTED, NGX_RTMP_NOTIFY_RECORD_DONE, NGX_RTMP_NOTIFY_UPDATE, NGX_RTMP_NOTIFY_PLAYLIST, @@ -154,6 +156,14 @@ static ngx_command_t ngx_rtmp_notify_commands[] = { 0, NULL }, + { ngx_string("on_record_started"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF| + NGX_CONF_TAKE1, + ngx_rtmp_notify_on_app_event, + NGX_RTMP_APP_CONF_OFFSET, + 0, + NULL }, + { ngx_string("on_record_done"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_RTMP_REC_CONF| NGX_CONF_TAKE1, @@ -823,6 +833,69 @@ ngx_rtmp_notify_update_create(ngx_rtmp_session_t *s, void *arg, } +static ngx_chain_t * +ngx_rtmp_notify_record_started_create(ngx_rtmp_session_t *s, void *arg, + ngx_pool_t *pool) +{ + ngx_rtmp_record_started_t *v = arg; + + ngx_rtmp_notify_ctx_t *ctx; + ngx_chain_t *pl; + ngx_buf_t *b; + size_t name_len, args_len; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module); + + pl = ngx_alloc_chain_link(pool); + if (pl == NULL) { + return NULL; + } + + name_len = ngx_strlen(ctx->name); + args_len = ngx_strlen(ctx->args); + + b = ngx_create_temp_buf(pool, + sizeof("call=record_done") + + sizeof("&recorder=") + v->recorder.len + + sizeof("&name=") + name_len * 3 + + sizeof("&path=") + v->path.len * 3 + + 1 + args_len + 1); + + if (b == NULL) { + return NULL; + } + + pl->buf = b; + pl->next = NULL; + + if (args_len) { + b->last = (u_char *) ngx_cpymem(b->last, ctx->args, args_len); + *b->last++ = '&'; + } + + b->last = ngx_cpymem(b->last, (u_char*) "call=record_started", + sizeof("call=record_started") - 1); + + 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, 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, + NGX_ESCAPE_ARGS); + + 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); + + *b->last++ = '&'; + + return ngx_rtmp_notify_create_request(s, pool, NGX_RTMP_NOTIFY_RECORD_STARTED, + pl); +} + + static ngx_chain_t * ngx_rtmp_notify_record_done_create(ngx_rtmp_session_t *s, void *arg, ngx_pool_t *pool) @@ -1862,6 +1935,39 @@ next: } +static ngx_int_t +ngx_rtmp_notify_record_started(ngx_rtmp_session_t *s, ngx_rtmp_record_started_t *v) +{ + ngx_rtmp_netcall_init_t ci; + ngx_rtmp_notify_app_conf_t *nacf; + + if (s->auto_pushed) { + goto next; + } + + nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module); + if (nacf == NULL || nacf->url[NGX_RTMP_NOTIFY_RECORD_STARTED] == NULL) { + goto next; + } + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: record_started recorder=%V path='%V' url='%V'", + &v->recorder, &v->path, + &nacf->url[NGX_RTMP_NOTIFY_RECORD_DONE]->url); + + ngx_memzero(&ci, sizeof(ci)); + + ci.url = nacf->url[NGX_RTMP_NOTIFY_RECORD_STARTED]; + ci.create = ngx_rtmp_notify_record_started_create; + ci.arg = v; + + ngx_rtmp_netcall_create(s, &ci); + +next: + return next_record_started(s, v); +} + + static ngx_int_t ngx_rtmp_notify_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v) { @@ -2070,6 +2176,10 @@ ngx_rtmp_notify_on_app_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) n = NGX_RTMP_NOTIFY_PLAY_DONE; break; + case sizeof("on_record_started") - 1: + n = NGX_RTMP_NOTIFY_RECORD_STARTED; + break; + case sizeof("on_record_done") - 1: n = NGX_RTMP_NOTIFY_RECORD_DONE; break; @@ -2166,6 +2276,9 @@ ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf) next_close_stream = ngx_rtmp_close_stream; ngx_rtmp_close_stream = ngx_rtmp_notify_close_stream; + next_record_started = ngx_rtmp_record_started; + ngx_rtmp_record_started = ngx_rtmp_notify_record_started; + next_record_done = ngx_rtmp_record_done; ngx_rtmp_record_done = ngx_rtmp_notify_record_done; diff --git a/ngx_rtmp_record_module.c b/ngx_rtmp_record_module.c index fce9a76..eebcf1c 100644 --- a/ngx_rtmp_record_module.c +++ b/ngx_rtmp_record_module.c @@ -13,6 +13,7 @@ #include "ngx_rtmp_record_module.h" +ngx_rtmp_record_started_pt ngx_rtmp_record_started; ngx_rtmp_record_done_pt ngx_rtmp_record_done; @@ -660,6 +661,7 @@ ngx_rtmp_record_start(ngx_rtmp_session_t *s) ngx_rtmp_record_app_conf_t *racf; ngx_rtmp_record_rec_ctx_t *rctx; ngx_rtmp_record_ctx_t *ctx; + ngx_rtmp_record_started_t v; ngx_uint_t n; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module); @@ -682,6 +684,11 @@ ngx_rtmp_record_start(ngx_rtmp_session_t *s) } ngx_rtmp_record_node_open(s, rctx); } + + v.recorder = racf->id; + ngx_rtmp_record_make_path(s, rctx, &v.path); + + ngx_rtmp_record_started(s, &v); } @@ -1201,6 +1208,12 @@ ngx_rtmp_record_node_avd(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx, } +static ngx_int_t +ngx_rtmp_record_started_init(ngx_rtmp_session_t *s, ngx_rtmp_record_started_t *v) +{ + return NGX_OK; +} + static ngx_int_t ngx_rtmp_record_done_init(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v) { @@ -1301,6 +1314,8 @@ ngx_rtmp_record_postconfiguration(ngx_conf_t *cf) ngx_rtmp_core_main_conf_t *cmcf; ngx_rtmp_handler_pt *h; + ngx_rtmp_record_started = ngx_rtmp_record_started_init; + ngx_rtmp_record_done = ngx_rtmp_record_done_init; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); diff --git a/ngx_rtmp_record_module.h b/ngx_rtmp_record_module.h index d9223e5..4b8546f 100644 --- a/ngx_rtmp_record_module.h +++ b/ngx_rtmp_record_module.h @@ -84,10 +84,23 @@ typedef struct { } ngx_rtmp_record_done_t; +typedef struct { + ngx_str_t recorder; + ngx_str_t path; +} ngx_rtmp_record_started_t; + + +typedef ngx_int_t (*ngx_rtmp_record_started_pt)(ngx_rtmp_session_t *s, + ngx_rtmp_record_started_t *v); + + typedef ngx_int_t (*ngx_rtmp_record_done_pt)(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v); +extern ngx_rtmp_record_started_pt ngx_rtmp_record_started; + + extern ngx_rtmp_record_done_pt ngx_rtmp_record_done;