Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

merged master into sync

o sync

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
  • Loading branch information...
commit ac13bbf1af345532a009a88d93ac4b7869ffe456 2 parents 9652689 + 17886de
@arut arut authored
View
18 README.md
@@ -2,13 +2,13 @@
## nginx-rtmp-module
-### Project page:
+### Project blog:
- http://arut.github.com/nginx-rtmp-module
+ http://rarut.wordpress.com
### Wiki manual:
- https://github.com/arut/nginx-rtmp-module/wiki
+ https://github.com/arut/nginx-rtmp-module/wiki/Directives
### Features:
@@ -19,17 +19,19 @@
* Stream relay support for distributed
streaming: push & pull models
-* Recording published streams in FLV file
+* Recording streams in multiple FLVs
* H264/AAC support
* Online transcoding with FFmpeg
* HLS (HTTP Live Streaming) support;
- experimental; requires recent libavformat
+ requires recent libavformat
(>= 53.31.100) from ffmpeg (ffmpeg.org)
-* HTTP callbacks on publish/play/record
+* HTTP callbacks (publish/play/record etc)
+
+* Running external programs on certain events (exec)
* Advanced buffering techniques
to keep memory allocations at a minimum
@@ -44,6 +46,8 @@
* Statistics in XML/XSL in machine- & human-
readable form
+* Linux/FreeBSD/MacOS
+
### Build:
@@ -197,7 +201,7 @@ rtmp_auto_push directive.
}
- # HLS (experimental)
+ # HLS
# HLS requires libavformat & should be configured as a separate
# NGINX module in addition to nginx-rtmp-module:
View
12 config
@@ -5,16 +5,18 @@ CORE_MODULES="$CORE_MODULES
ngx_rtmp_core_module \
ngx_rtmp_cmd_module \
ngx_rtmp_access_module \
+ ngx_rtmp_record_module \
ngx_rtmp_live_module \
ngx_rtmp_play_module \
ngx_rtmp_flv_module \
ngx_rtmp_mp4_module \
- ngx_rtmp_record_module \
ngx_rtmp_netcall_module \
- ngx_rtmp_notify_module \
ngx_rtmp_relay_module \
ngx_rtmp_exec_module \
ngx_rtmp_codec_module \
+ ngx_rtmp_auto_push_module \
+ ngx_rtmp_enotify_module \
+ ngx_rtmp_notify_module \
"
@@ -31,23 +33,25 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
$ngx_addon_dir/ngx_rtmp_amf.c \
$ngx_addon_dir/ngx_rtmp_send.c \
$ngx_addon_dir/ngx_rtmp_shared.c \
+ $ngx_addon_dir/ngx_rtmp_eval.c \
$ngx_addon_dir/ngx_rtmp_receive.c \
$ngx_addon_dir/ngx_rtmp_core_module.c \
$ngx_addon_dir/ngx_rtmp_cmd_module.c \
$ngx_addon_dir/ngx_rtmp_access_module.c \
+ $ngx_addon_dir/ngx_rtmp_record_module.c \
$ngx_addon_dir/ngx_rtmp_live_module.c \
$ngx_addon_dir/ngx_rtmp_play_module.c \
$ngx_addon_dir/ngx_rtmp_flv_module.c \
$ngx_addon_dir/ngx_rtmp_mp4_module.c \
- $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_relay_module.c \
$ngx_addon_dir/ngx_rtmp_bandwidth.c \
$ngx_addon_dir/ngx_rtmp_exec_module.c \
$ngx_addon_dir/ngx_rtmp_codec_module.c \
$ngx_addon_dir/ngx_rtmp_auto_push_module.c \
+ $ngx_addon_dir/ngx_rtmp_enotify_module.c \
+ $ngx_addon_dir/ngx_rtmp_notify_module.c \
"
CFLAGS="$CFLAGS -I$ngx_addon_dir"
View
9 ngx_rtmp.c
@@ -317,6 +317,7 @@ ngx_rtmp_merge_applications(ngx_conf_t *cf, ngx_array_t *applications,
ngx_rtmp_conf_ctx_t *ctx, saved;
ngx_rtmp_core_app_conf_t **cacfp;
ngx_uint_t n;
+ ngx_rtmp_core_app_conf_t *cacf;
if (applications == NULL) {
return NGX_CONF_OK;
@@ -335,6 +336,14 @@ ngx_rtmp_merge_applications(ngx_conf_t *cf, ngx_array_t *applications,
if (rv != NGX_CONF_OK) {
return rv;
}
+
+ cacf = (*cacfp)->app_conf[ngx_rtmp_core_module.ctx_index];
+ rv = ngx_rtmp_merge_applications(cf, &cacf->applications,
+ (*cacfp)->app_conf,
+ module, ctx_index);
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
}
*ctx = saved;
View
3  ngx_rtmp.h
@@ -209,6 +209,7 @@ typedef struct {
/* auto-pushed? */
unsigned auto_pushed:1;
+ unsigned relay:1;
/* input stream 0 (reserved by RTMP spec)
* is used as free chain link */
@@ -294,6 +295,7 @@ typedef struct ngx_rtmp_core_srv_conf_s {
typedef struct {
+ ngx_array_t applications; /* ngx_rtmp_core_app_conf_t */
ngx_str_t name;
void **app_conf;
} ngx_rtmp_core_app_conf_t;
@@ -326,6 +328,7 @@ typedef struct {
#define NGX_RTMP_MAIN_CONF 0x02000000
#define NGX_RTMP_SRV_CONF 0x04000000
#define NGX_RTMP_APP_CONF 0x08000000
+#define NGX_RTMP_REC_CONF 0x10000000
#define NGX_RTMP_MAIN_CONF_OFFSET offsetof(ngx_rtmp_conf_ctx_t, main_conf)
View
2  ngx_rtmp_auto_push_module.c
@@ -400,7 +400,7 @@ ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
ngx_rtmp_auto_push_conf_t *apcf;
ngx_rtmp_auto_push_ctx_t *ctx;
- if (s->auto_pushed) {
+ if (s->auto_pushed || s->relay) {
goto next;
}
View
27 ngx_rtmp_cmd_module.c
@@ -200,7 +200,7 @@ ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
- ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: app='%s' flashver='%s' swf_url='%s' "
"tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD "
"object_encoding=%ui",
@@ -327,7 +327,7 @@ ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s, ngx_rtmp_create_stream_t *v)
h.csid = NGX_RTMP_CMD_CSID_AMF_INI;
h.type = NGX_RTMP_MSG_AMF_CMD;
- ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"createStream");
/* send result with standard stream */
@@ -415,6 +415,9 @@ ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
{
ngx_rtmp_close_stream_t cv;
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "deleteStream");
+
/* chain close_stream */
cv.stream = 0;
return ngx_rtmp_close_stream
@@ -523,7 +526,7 @@ ngx_rtmp_cmd_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
out_inf, sizeof(out_inf) },
};
- ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"publish: name='%s' args='%s' type=%s silent=%d",
v->name, v->args, v->type, v->silent);
@@ -621,7 +624,7 @@ ngx_rtmp_cmd_fcpublish(ngx_rtmp_session_t *s, ngx_rtmp_fcpublish_t *v)
out_inf, sizeof(out_inf) },
};
- ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"fcpublish: name='%s'", v->name);
/* send onFCPublish reply */
@@ -801,8 +804,8 @@ ngx_rtmp_cmd_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
out4_inf, sizeof(out4_inf) },
};
- ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "cmd: play name='%s' args='%s' start=%i duration=%i "
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "play name='%s' args='%s' start=%i duration=%i "
"reset=%i silent=%i",
v->name, v->args, (ngx_int_t) v->start,
(ngx_int_t) v->duration, (ngx_int_t) v->reset,
@@ -934,7 +937,7 @@ ngx_rtmp_cmd_fcsubscribe(ngx_rtmp_session_t *s, ngx_rtmp_fcsubscribe_t *v)
sizeof(out_inf) },
};
- ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"fcsubscribe: name='%s'", v->name);
/* send onFCSubscribe reply */
@@ -1038,6 +1041,10 @@ ngx_rtmp_cmd_pause(ngx_rtmp_session_t *s, ngx_rtmp_pause_t *v)
out_inf, sizeof(out_inf) },
};
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "pause: state='%i' position=%i",
+ v->pause, (ngx_int_t) v->position);
+
/* send onStatus reply */
ngx_memzero(&h, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
@@ -1068,6 +1075,9 @@ static ngx_int_t
ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "disconnect");
+
return ngx_rtmp_delete_stream
? ngx_rtmp_delete_stream(s, NULL)
: NGX_OK;
@@ -1152,6 +1162,9 @@ ngx_rtmp_cmd_seek(ngx_rtmp_session_t *s, ngx_rtmp_seek_t *v)
out_inf, sizeof(out_inf) },
};
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "seek: offset=%i", (ngx_int_t) v->offset);
+
/* send onStatus reply */
ngx_memzero(&h, sizeof(h));
h.type = NGX_RTMP_MSG_AMF_CMD;
View
7 ngx_rtmp_core_module.c
@@ -282,6 +282,13 @@ ngx_rtmp_core_create_app_conf(ngx_conf_t *cf)
return NULL;
}
+ if (ngx_array_init(&conf->applications, cf->pool, 1,
+ sizeof(ngx_rtmp_core_app_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
return conf;
}
View
564 ngx_rtmp_enotify_module.c
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2012 Roman Arutyunyan
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include "ngx_rtmp.h"
+
+#include "ngx_rtmp_eval.h"
+#include "ngx_rtmp_cmd_module.h"
+#include "ngx_rtmp_record_module.h"
+
+#include <stdlib.h>
+#ifdef NGX_LINUX
+#include <unistd.h>
+#endif
+
+
+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_record_done_pt next_record_done;
+
+
+static char *ngx_rtmp_enotify_on_event(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_rtmp_enotify_postconfiguration(ngx_conf_t *cf);
+static void * ngx_rtmp_enotify_create_app_conf(ngx_conf_t *cf);
+static char * ngx_rtmp_enotify_merge_app_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
+#define NGX_RTMP_ENOTIFY_PUBLISHING 0x01
+#define NGX_RTMP_ENOTIFY_PLAYING 0x02
+
+
+enum {
+ NGX_RTMP_ENOTIFY_PUBLISH,
+ NGX_RTMP_ENOTIFY_PLAY,
+ NGX_RTMP_ENOTIFY_PUBLISH_DONE,
+ NGX_RTMP_ENOTIFY_PLAY_DONE,
+ NGX_RTMP_ENOTIFY_RECORD_DONE,
+ NGX_RTMP_ENOTIFY_MAX
+};
+
+
+typedef struct {
+ ngx_str_t cmd;
+ ngx_array_t args; /* ngx_str_t */
+} ngx_rtmp_enotify_conf_t;
+
+
+typedef struct {
+ ngx_rtmp_enotify_conf_t *event[NGX_RTMP_ENOTIFY_MAX];
+ ngx_flag_t active;
+} ngx_rtmp_enotify_app_conf_t;
+
+
+typedef struct {
+ ngx_uint_t flags;
+ u_char name[NGX_RTMP_MAX_NAME];
+ u_char args[NGX_RTMP_MAX_ARGS];
+ ngx_str_t path;
+ ngx_str_t recorder;
+} ngx_rtmp_enotify_ctx_t;
+
+
+static ngx_command_t ngx_rtmp_enotify_commands[] = {
+
+ { ngx_string("exec_publish"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ ngx_rtmp_enotify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("exec_play"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ ngx_rtmp_enotify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("exec_publish_done"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ ngx_rtmp_enotify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("exec_play_done"),
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ ngx_rtmp_enotify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ 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,
+ ngx_rtmp_enotify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_rtmp_module_t ngx_rtmp_enotify_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_rtmp_enotify_postconfiguration, /* postconfiguration */
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+ ngx_rtmp_enotify_create_app_conf, /* create app configuration */
+ ngx_rtmp_enotify_merge_app_conf /* merge app configuration */
+};
+
+
+ngx_module_t ngx_rtmp_enotify_module = {
+ NGX_MODULE_V1,
+ &ngx_rtmp_enotify_module_ctx, /* module context */
+ ngx_rtmp_enotify_commands, /* module directives */
+ NGX_RTMP_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
+};
+
+
+static void
+ngx_rtmp_enotify_eval_astr(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret)
+{
+ ngx_rtmp_enotify_ctx_t *ctx;
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
+ if (ctx == NULL) {
+ ret->len = 0;
+ return;
+ }
+
+ ret->data = (u_char *) ctx + e->offset;
+ ret->len = ngx_strlen(ret->data);
+}
+
+
+static void
+ngx_rtmp_enotify_eval_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret)
+{
+ ngx_rtmp_enotify_ctx_t *ctx;
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
+ if (ctx == NULL) {
+ ret->len = 0;
+ return;
+ }
+
+ *ret = *(ngx_str_t *) ((u_char *) ctx + e->offset);
+}
+
+
+static ngx_rtmp_eval_t ngx_rtmp_enotify_eval[] = {
+
+ { ngx_string("name"),
+ ngx_rtmp_enotify_eval_astr,
+ offsetof(ngx_rtmp_enotify_ctx_t, name) },
+
+ { ngx_string("args"),
+ ngx_rtmp_enotify_eval_astr,
+ offsetof(ngx_rtmp_enotify_ctx_t, args) },
+
+ { ngx_string("path"),
+ ngx_rtmp_enotify_eval_str,
+ offsetof(ngx_rtmp_enotify_ctx_t, path) },
+
+ { ngx_string("recorder"),
+ ngx_rtmp_enotify_eval_str,
+ offsetof(ngx_rtmp_enotify_ctx_t, recorder) },
+
+ ngx_rtmp_null_eval
+};
+
+
+static ngx_rtmp_eval_t * ngx_rtmp_enotify_eval_p[] = {
+ ngx_rtmp_eval_session,
+ ngx_rtmp_enotify_eval,
+ NULL
+};
+
+
+static void *
+ngx_rtmp_enotify_create_app_conf(ngx_conf_t *cf)
+{
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_uint_t n;
+
+ enacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_enotify_app_conf_t));
+ if (enacf == NULL) {
+ return NULL;
+ }
+
+ for (n = 0; n < NGX_RTMP_ENOTIFY_MAX; ++n) {
+ enacf->event[n] = NGX_CONF_UNSET_PTR;
+ }
+
+ return enacf;
+}
+
+
+static char *
+ngx_rtmp_enotify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_rtmp_enotify_app_conf_t *prev = parent;
+ ngx_rtmp_enotify_app_conf_t *conf = child;
+ ngx_uint_t n;
+
+ for (n = 0; n < NGX_RTMP_ENOTIFY_MAX; ++n) {
+ ngx_conf_merge_ptr_value(conf->event[n], prev->event[n], NULL);
+ if (conf->event[n]) {
+ conf->active = 1;
+ }
+ }
+
+ if (conf->active) {
+ prev->active = 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_exec(ngx_rtmp_session_t *s, ngx_rtmp_enotify_conf_t *ec)
+{
+#ifndef NGX_WIN32
+ int pid;
+ ngx_str_t a, *arg;
+ char **args;
+ ngx_uint_t n;
+
+ pid = fork();
+
+ switch (pid) {
+ case -1:
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, ngx_errno,
+ "enotify: fork failed");
+ return NGX_ERROR;
+
+ case 0:
+ /* child */
+ args = ngx_palloc(s->connection->pool,
+ (ec->args.nelts + 2) * sizeof(char *));
+ if (args == NULL) {
+ exit(1);
+ }
+ arg = ec->args.elts;
+ args[0] = (char *)ec->cmd.data;
+ for (n = 0; n < ec->args.nelts; ++n, ++arg) {
+ ngx_rtmp_eval(s, arg, ngx_rtmp_enotify_eval_p, &a);
+ args[n + 1] = (char *) a.data;
+ }
+ args[n + 1] = NULL;
+ if (execvp((char *)ec->cmd.data, args) == -1) {
+ exit(1);
+ }
+ break;
+
+ default:
+ /* parent */
+ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "enotify: child '%V' started pid=%ui",
+ &ec->cmd, (ngx_uint_t)pid);
+ break;
+ }
+#endif /* NGX_WIN32 */
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_rtmp_enotify_init(ngx_rtmp_session_t *s,
+ u_char name[NGX_RTMP_MAX_NAME], u_char args[NGX_RTMP_MAX_ARGS],
+ ngx_uint_t flags)
+{
+ ngx_rtmp_enotify_ctx_t *ctx;
+ ngx_rtmp_enotify_app_conf_t *enacf;
+
+ enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
+
+ if (!enacf->active) {
+ return;
+ }
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_enotify_ctx_t));
+ if (ctx == NULL) {
+ return;
+ }
+
+ ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_enotify_module);
+ }
+
+ ngx_memcpy(ctx->name, name, NGX_RTMP_MAX_NAME);
+ ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS);
+
+ ctx->flags |= flags;
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
+{
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_rtmp_enotify_conf_t *ec;
+
+ if (s->auto_pushed) {
+ goto next;
+ }
+
+ enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
+
+ if (enacf == NULL) {
+ goto next;
+ }
+
+ ngx_rtmp_enotify_init(s, v->name, v->args, NGX_RTMP_ENOTIFY_PUBLISHING);
+
+ ec = enacf->event[NGX_RTMP_ENOTIFY_PUBLISH];
+
+ if (ec == NULL) {
+ goto next;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "enotify: publish '%V'", &ec->cmd);
+
+ ngx_rtmp_enotify_exec(s, ec);
+
+next:
+ return next_publish(s, v);
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
+{
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_rtmp_enotify_conf_t *ec;
+
+ if (s->auto_pushed) {
+ goto next;
+ }
+
+ enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
+
+ if (enacf == NULL) {
+ goto next;
+ }
+
+ ngx_rtmp_enotify_init(s, v->name, v->args, NGX_RTMP_ENOTIFY_PLAYING);
+
+ ec = enacf->event[NGX_RTMP_ENOTIFY_PLAY];
+
+ if (ec == NULL) {
+ goto next;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "enotify: play '%V'", &ec->cmd);
+
+ ngx_rtmp_enotify_exec(s, ec);
+
+next:
+ return next_play(s, v);
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t
+ *v)
+{
+ ngx_rtmp_enotify_ctx_t *ctx;
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_rtmp_enotify_conf_t *ec;
+
+ if (s->auto_pushed) {
+ goto next;
+ }
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
+
+ if (ctx == NULL) {
+ goto next;
+ }
+
+ enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
+
+ if (enacf == NULL) {
+ goto next;
+ }
+
+ if (enacf->event[NGX_RTMP_ENOTIFY_PUBLISH_DONE] &&
+ (ctx->flags & NGX_RTMP_ENOTIFY_PUBLISHING))
+ {
+ ec = enacf->event[NGX_RTMP_ENOTIFY_PUBLISH_DONE];
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "enotify: publish_done '%V'", &ec->cmd);
+
+ ngx_rtmp_enotify_exec(s, ec);
+ }
+
+ if (enacf->event[NGX_RTMP_ENOTIFY_PLAY_DONE] &&
+ (ctx->flags & NGX_RTMP_ENOTIFY_PLAYING))
+ {
+ ec = enacf->event[NGX_RTMP_ENOTIFY_PLAY_DONE];
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "enotify: play_done '%V'", &ec->cmd);
+
+ ngx_rtmp_enotify_exec(s, ec);
+ }
+
+ ctx->flags = 0;
+
+next:
+ return next_delete_stream(s, v);
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_t *v)
+{
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_rtmp_enotify_conf_t *ec;
+ ngx_rtmp_enotify_ctx_t *ctx;
+
+ if (s->auto_pushed) {
+ goto next;
+ }
+
+ enacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_enotify_module);
+ if (enacf == NULL || enacf->event[NGX_RTMP_ENOTIFY_RECORD_DONE] == NULL) {
+ goto next;
+ }
+
+ ec = enacf->event[NGX_RTMP_ENOTIFY_RECORD_DONE];
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "enotify: record_done %V recorder=%V path='%V'",
+ &ec->cmd, &v->recorder, &v->path);
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_enotify_module);
+ if (ctx == NULL) {
+ goto next;
+ }
+
+ ctx->recorder = v->recorder;
+ ctx->path = v->path;
+
+ ngx_rtmp_enotify_exec(s, ec);
+
+next:
+ return next_record_done(s, v);
+}
+
+
+static char *
+ngx_rtmp_enotify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_rtmp_enotify_app_conf_t *enacf;
+ ngx_rtmp_enotify_conf_t *ec;
+ ngx_str_t *name, *value, *s;
+ size_t nargs;
+ ngx_uint_t n;
+
+ value = cf->args->elts;
+ name = &value[0];
+
+ ec = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_enotify_conf_t));
+
+ if (ec == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ec->cmd = value[1];
+
+ nargs = cf->args->nelts - 2;
+
+ if (nargs) {
+ if (ngx_array_init(&ec->args, cf->pool, nargs, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ s = ngx_array_push_n(&ec->args, nargs);
+ for (n = 2; n < cf->args->nelts; ++n, ++s) {
+ *s = value[n];
+ }
+ }
+
+ enacf = ngx_rtmp_conf_get_module_app_conf(cf, ngx_rtmp_enotify_module);
+
+ n = 0;
+
+ switch (name->len) {
+ case sizeof("exec_play") - 1:
+ n = NGX_RTMP_ENOTIFY_PLAY;
+ break;
+
+ case sizeof("exec_publish") - 1:
+ n = NGX_RTMP_ENOTIFY_PUBLISH;
+ break;
+
+ case sizeof("exec_play_done") - 1:
+ n = NGX_RTMP_ENOTIFY_PLAY_DONE;
+ break;
+
+ case sizeof("exec_record_done") - 1:
+ n = NGX_RTMP_ENOTIFY_RECORD_DONE;
+ break;
+
+ case sizeof("exec_publish_done") - 1:
+ n = NGX_RTMP_ENOTIFY_PUBLISH_DONE;
+ break;
+ }
+
+ enacf->event[n] = ec;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_rtmp_enotify_postconfiguration(ngx_conf_t *cf)
+{
+ next_publish = ngx_rtmp_publish;
+ ngx_rtmp_publish = ngx_rtmp_enotify_publish;
+
+ 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_record_done = ngx_rtmp_record_done;
+ ngx_rtmp_record_done = ngx_rtmp_enotify_record_done;
+
+ return NGX_OK;
+}
View
188 ngx_rtmp_eval.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2012 Roman Arutyunyan
+ */
+
+
+#include "ngx_rtmp_eval.h"
+
+
+#define NGX_RTMP_EVAL_BUFLEN 16
+
+
+static void
+ngx_rtmp_eval_session_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret)
+{
+ *ret = *(ngx_str_t *) ((u_char *) s + e->offset);
+}
+
+
+static void
+ngx_rtmp_eval_connection_str(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret)
+{
+ *ret = *(ngx_str_t *) ((u_char *) s->connection + e->offset);
+}
+
+
+ngx_rtmp_eval_t ngx_rtmp_eval_session[] = {
+
+ { ngx_string("app"),
+ ngx_rtmp_eval_session_str,
+ offsetof(ngx_rtmp_session_t, app) },
+
+ { ngx_string("flashver"),
+ ngx_rtmp_eval_session_str,
+ offsetof(ngx_rtmp_session_t, flashver) },
+
+ { ngx_string("swfurl"),
+ ngx_rtmp_eval_session_str,
+ offsetof(ngx_rtmp_session_t, swf_url) },
+
+ { ngx_string("tcurl"),
+ ngx_rtmp_eval_session_str,
+ offsetof(ngx_rtmp_session_t, tc_url) },
+
+ { ngx_string("pageurl"),
+ ngx_rtmp_eval_session_str,
+ offsetof(ngx_rtmp_session_t, page_url) },
+
+ { ngx_string("addr"),
+ ngx_rtmp_eval_connection_str,
+ offsetof(ngx_connection_t, addr_text) },
+
+ ngx_rtmp_null_eval
+};
+
+
+static void
+ngx_rtmp_eval_append(ngx_rtmp_session_t *s, ngx_buf_t *b,
+ void *data, size_t len)
+{
+ size_t buf_len;
+
+ if (b->last + len > b->end) {
+ buf_len = 2 * (b->last - b->pos) + len;
+
+ b->start = ngx_palloc(s->connection->pool, buf_len);
+ if (b->start == NULL) {
+ return;
+ }
+
+ b->last = ngx_cpymem(b->start, b->pos, b->last - b->pos);
+ b->pos = b->start;
+ b->end = b->start + buf_len;
+ }
+
+ b->last = ngx_cpymem(b->last, data, len);
+}
+
+
+static void
+ngx_rtmp_eval_append_var(ngx_rtmp_session_t *s, ngx_buf_t *b,
+ ngx_rtmp_eval_t **e, ngx_str_t *name)
+{
+ ngx_uint_t k;
+ ngx_str_t v;
+ ngx_rtmp_eval_t *ee;
+
+ for (; *e; ++e) {
+ for (k = 0, ee = *e; ee->handler; ++k, ++ee) {
+ if (ee->name.len == name->len &&
+ ngx_memcmp(ee->name.data, name->data, name->len) == 0)
+ {
+ ee->handler(s, ee, &v);
+ ngx_rtmp_eval_append(s, b, v.data, v.len);
+ }
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in, ngx_rtmp_eval_t **e,
+ ngx_str_t *out)
+{
+ u_char c, *p;;
+ ngx_str_t name;
+ ngx_buf_t b;
+ ngx_uint_t n;
+
+ enum {
+ NORMAL,
+ ESCAPE,
+ NAME,
+ SNAME
+ } state = NORMAL;
+
+ b.pos = b.last = b.start = ngx_palloc(s->connection->pool,
+ NGX_RTMP_EVAL_BUFLEN);
+ if (b.pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ b.end = b.pos + NGX_RTMP_EVAL_BUFLEN;
+
+ for (n = 0; n < in->len; ++n) {
+ p = &in->data[n];
+ c = *p;
+
+ switch (state) {
+ case SNAME:
+ if (c != '}') {
+ continue;
+ }
+
+ name.len = p - name.data;
+ ngx_rtmp_eval_append_var(s, &b, e, &name);
+
+ state = NORMAL;
+
+ continue;
+
+ case NAME:
+ if (c == '{' && name.data == p) {
+ ++name.data;
+ state = SNAME;
+ continue;
+ }
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ continue;
+ }
+
+ name.len = p - name.data;
+ ngx_rtmp_eval_append_var(s, &b, e, &name);
+
+ case NORMAL:
+ switch (c) {
+ case '$':
+ name.data = p + 1;
+ state = NAME;
+ continue;
+ case '\\':
+ state = ESCAPE;
+ continue;
+ }
+
+ case ESCAPE:
+ ngx_rtmp_eval_append(s, &b, &c, 1);
+ state = NORMAL;
+ break;
+
+ }
+ }
+
+ if (state == NAME) {
+ p = &in->data[n];
+ name.len = p - name.data;
+ ngx_rtmp_eval_append_var(s, &b, e, &name);
+ }
+
+ c = 0;
+ ngx_rtmp_eval_append(s, &b, &c, 1);
+
+ out->data = b.pos;
+ out->len = b.last - b.pos - 1;
+
+ return NGX_OK;
+}
View
37 ngx_rtmp_eval.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2012 Roman Arutyunyan
+ */
+
+
+#ifndef _NGX_RTMP_EVAL_H_INCLUDED_
+#define _NGX_RTMP_EVAL_H_INCLUDED_
+
+#include "ngx_rtmp.h"
+
+
+typedef struct ngx_rtmp_eval_s ngx_rtmp_eval_t;
+
+
+typedef void (* ngx_rtmp_eval_pt)(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret);
+
+
+struct ngx_rtmp_eval_s {
+ ngx_str_t name;
+ ngx_rtmp_eval_pt handler;
+ ngx_uint_t offset;
+};
+
+
+#define ngx_rtmp_null_eval { ngx_null_string, NULL, 0 }
+
+
+/* standard session eval variables */
+extern ngx_rtmp_eval_t ngx_rtmp_eval_session[];
+
+
+ngx_int_t ngx_rtmp_eval(ngx_rtmp_session_t *s, ngx_str_t *in,
+ ngx_rtmp_eval_t **e, ngx_str_t *out);
+
+
+#endif /* _NGX_RTMP_EVAL_H_INCLUDED_ */
View
115 ngx_rtmp_exec_module.c
@@ -4,10 +4,8 @@
#include "ngx_rtmp_cmd_module.h"
+#include "ngx_rtmp_eval.h"
#include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
#ifdef NGX_LINUX
#include <unistd.h>
@@ -121,6 +119,40 @@ ngx_module_t ngx_rtmp_exec_module = {
};
+static void
+ngx_rtmp_exec_eval_astr(ngx_rtmp_session_t *s, ngx_rtmp_eval_t *e,
+ ngx_str_t *ret)
+{
+ ngx_rtmp_exec_ctx_t *ctx;
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_exec_module);
+ if (ctx == NULL) {
+ ret->len = 0;
+ return;
+ }
+
+ ret->data = (u_char *) ctx + e->offset;
+ ret->len = ngx_strlen(ret->data);
+}
+
+
+static ngx_rtmp_eval_t ngx_rtmp_exec_eval[] = {
+
+ { ngx_string("name"),
+ ngx_rtmp_exec_eval_astr,
+ offsetof(ngx_rtmp_exec_ctx_t, name) },
+
+ ngx_rtmp_null_eval
+};
+
+
+static ngx_rtmp_eval_t * ngx_rtmp_exec_eval_p[] = {
+ ngx_rtmp_eval_session,
+ ngx_rtmp_exec_eval,
+ NULL
+};
+
+
static void *
ngx_rtmp_exec_create_app_conf(ngx_conf_t *cf)
{
@@ -250,73 +282,6 @@ ngx_rtmp_exec_kill(ngx_rtmp_session_t *s, ngx_rtmp_exec_t *e, ngx_int_t term)
}
-static void
-ngx_rtmp_exec_append(ngx_str_t *result, u_char *data, size_t len)
-{
- if (len == 0) {
- len = ngx_strlen(data);
- }
-
- /* use malloc in child */
- if (result->len == 0) {
- result->data = malloc(len + 1);
- result->len = len;
- ngx_memcpy(result->data, data, len);
- result->data[len] = 0;
- return;
- }
-
- result->data = realloc(result->data, result->len + len + 1);
- ngx_memcpy(result->data + result->len, data, len);
- result->len += len;
- result->data[result->len] = 0;
-}
-
-
-static char *
-ngx_rtmp_exec_prepare_arg(ngx_rtmp_session_t *s, ngx_str_t *arg)
-{
- ngx_rtmp_core_app_conf_t *cacf;
- ngx_rtmp_exec_ctx_t *ctx;
- u_char *p, *pp;
- ngx_str_t result;
-
- cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_core_module);
- ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_exec_module);
-
- /* substitute $app/${app} & $name/${name} */
- ngx_str_set(&result, "");
- pp = arg->data;
- for ( ;; ) {
- p = (u_char *)ngx_strchr(pp, '$');
- ngx_rtmp_exec_append(&result, pp, p ? p - pp : 0);
- if (p == NULL) {
- return (char *)result.data;
- }
- pp = p + 1;
- if (p != arg->data && p[-1] == '\\') {
- goto dollar;
- }
- if (!ngx_strncmp(p + 1, "app", sizeof("app") - 1)
- || !ngx_strncmp(p + 1, "{app}", sizeof("{app}") - 1))
- {
- ngx_rtmp_exec_append(&result, cacf->name.data, cacf->name.len);
- pp += (p[1] == '{' ? sizeof("{app}") - 1 : sizeof("app") - 1);
- continue;
- }
- if (!ngx_strncmp(p + 1, "name", sizeof("name") - 1)
- || !ngx_strncmp(p + 1, "{name}", sizeof("{name}") - 1))
- {
- ngx_rtmp_exec_append(&result, ctx->name, 0);
- pp += (p[1] == '{' ? sizeof("{name}") - 1 : sizeof("name") - 1);
- continue;
- }
-dollar:
- ngx_rtmp_exec_append(&result, (u_char *)"$", 1);
- }
-}
-
-
static ngx_int_t
ngx_rtmp_exec_run(ngx_rtmp_session_t *s, size_t n)
{
@@ -328,7 +293,7 @@ ngx_rtmp_exec_run(ngx_rtmp_session_t *s, size_t n)
int ret;
ngx_rtmp_exec_conf_t *ec;
ngx_rtmp_exec_t *e;
- ngx_str_t *arg;
+ ngx_str_t *arg, a;
char **args;
eacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_exec_module);
@@ -372,17 +337,19 @@ ngx_rtmp_exec_run(ngx_rtmp_session_t *s, size_t n)
case 0:
/* child */
- args = malloc((ec->args.nelts + 2) * sizeof(char *));
+ args = ngx_palloc(s->connection->pool,
+ (ec->args.nelts + 2) * sizeof(char *));
if (args == NULL) {
exit(1);
}
arg = ec->args.elts;
args[0] = (char *)ec->cmd.data;
for (n = 0; n < ec->args.nelts; ++n, ++arg) {
- args[n + 1] = ngx_rtmp_exec_prepare_arg(s, arg);
+ ngx_rtmp_eval(s, arg, ngx_rtmp_exec_eval_p, &a);
+ args[n + 1] = (char *) a.data;
}
args[n + 1] = NULL;
- if (execv((char *)ec->cmd.data, args) == -1) {
+ if (execvp((char *)ec->cmd.data, args) == -1) {
exit(1);
}
break;
View
367 ngx_rtmp_notify_module.c
@@ -9,34 +9,59 @@
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_netcall_module.h"
+#include "ngx_rtmp_record_module.h"
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_record_done_pt next_record_done;
static char *ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd,
- void *conf);
+ 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);
+ void *parent, void *child);
+static ngx_int_t ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname,
+ ngx_url_t *url);
+
+
+#define NGX_RTMP_NOTIFY_PUBLISHING 0x01
+#define NGX_RTMP_NOTIFY_PLAYING 0x02
+
+
+enum {
+ NGX_RTMP_NOTIFY_PLAY,
+ NGX_RTMP_NOTIFY_PUBLISH,
+ NGX_RTMP_NOTIFY_PLAY_DONE,
+ NGX_RTMP_NOTIFY_PUBLISH_DONE,
+ NGX_RTMP_NOTIFY_DONE,
+ NGX_RTMP_NOTIFY_RECORD_DONE,
+ NGX_RTMP_NOTIFY_MAX
+};
typedef struct {
- ngx_url_t *publish_url;
- ngx_url_t *play_url;
- ngx_url_t *done_url;
+ ngx_url_t *url[NGX_RTMP_NOTIFY_MAX];
+ ngx_flag_t active;
} ngx_rtmp_notify_app_conf_t;
typedef struct {
+ ngx_uint_t flags;
u_char name[NGX_RTMP_MAX_NAME];
u_char args[NGX_RTMP_MAX_ARGS];
} ngx_rtmp_notify_ctx_t;
+typedef struct {
+ u_char *cbname;
+ ngx_url_t *url;
+} ngx_rtmp_notify_done_t;
+
+
static ngx_command_t ngx_rtmp_notify_commands[] = {
{ ngx_string("on_publish"),
@@ -53,6 +78,20 @@ static ngx_command_t ngx_rtmp_notify_commands[] = {
0,
NULL },
+ { ngx_string("on_publish_done"),
+ 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("on_play_done"),
+ 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("on_done"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_rtmp_notify_on_event,
@@ -60,6 +99,14 @@ static ngx_command_t ngx_rtmp_notify_commands[] = {
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,
+ ngx_rtmp_notify_on_event,
+ NGX_RTMP_APP_CONF_OFFSET,
+ 0,
+ NULL },
+
ngx_null_command
};
@@ -95,13 +142,18 @@ ngx_module_t ngx_rtmp_notify_module = {
static void *
ngx_rtmp_notify_create_app_conf(ngx_conf_t *cf)
{
- ngx_rtmp_notify_app_conf_t *nacf;
+ ngx_rtmp_notify_app_conf_t *nacf;
+ ngx_uint_t n;
nacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_notify_app_conf_t));
if (nacf == NULL) {
return NULL;
}
+ for (n = 0; n < NGX_RTMP_NOTIFY_MAX; ++n) {
+ nacf->url[n] = NGX_CONF_UNSET_PTR;
+ }
+
return nacf;
}
@@ -111,9 +163,18 @@ ngx_rtmp_notify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_notify_app_conf_t *prev = parent;
ngx_rtmp_notify_app_conf_t *conf = child;
+ ngx_uint_t n;
+
+ for (n = 0; n < NGX_RTMP_NOTIFY_MAX; ++n) {
+ ngx_conf_merge_ptr_value(conf->url[n], prev->url[n], NULL);
+ if (conf->url[n]) {
+ conf->active = 1;
+ }
+ }
- ngx_conf_merge_ptr_value(conf->publish_url, prev->publish_url, 0);
- ngx_conf_merge_ptr_value(conf->play_url, prev->play_url, 0);
+ if (conf->active) {
+ prev->active = 1;
+ }
return NGX_CONF_OK;
}
@@ -183,8 +244,8 @@ ngx_rtmp_notify_publish_create(ngx_rtmp_session_t *s, void *arg,
}
/* HTTP header */
- hl = ngx_rtmp_netcall_http_format_header(nacf->publish_url, pool,
- cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
+ hl = ngx_rtmp_netcall_http_format_header(nacf->url[NGX_RTMP_NOTIFY_PUBLISH],
+ pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
&ngx_rtmp_netcall_content_type_urlencoded);
if (hl == NULL) {
@@ -263,8 +324,8 @@ ngx_rtmp_notify_play_create(ngx_rtmp_session_t *s, void *arg,
}
/* HTTP header */
- hl = ngx_rtmp_netcall_http_format_header(nacf->play_url, pool,
- cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
+ hl = ngx_rtmp_netcall_http_format_header(nacf->url[NGX_RTMP_NOTIFY_PLAY],
+ pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
&ngx_rtmp_netcall_content_type_urlencoded);
if (hl == NULL) {
@@ -283,14 +344,14 @@ static ngx_chain_t *
ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg,
ngx_pool_t *pool)
{
- ngx_rtmp_notify_app_conf_t *nacf;
+ ngx_rtmp_notify_done_t *ds = arg;
+
ngx_chain_t *hl, *cl, *pl;
ngx_buf_t *b;
- size_t name_len, args_len;
+ size_t cbname_len, name_len, args_len;
ngx_str_t *addr_text;
ngx_rtmp_notify_ctx_t *ctx;
- 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 */
@@ -306,12 +367,13 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg,
return NULL;
}
+ 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=done") +
+ sizeof("&call=") + cbname_len +
sizeof("&addr=") + addr_text->len +
sizeof("&name=") + name_len * 3
+ 1 + args_len);
@@ -321,8 +383,8 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg,
pl->buf = b;
- b->last = ngx_cpymem(b->last, (u_char*)"&call=done",
- sizeof("&call=done") - 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,
@@ -339,7 +401,7 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg,
}
/* HTTP header */
- hl = ngx_rtmp_netcall_http_format_header(nacf->done_url, pool,
+ hl = ngx_rtmp_netcall_http_format_header(ds->url, pool,
cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
&ngx_rtmp_netcall_content_type_urlencoded);
@@ -355,6 +417,95 @@ ngx_rtmp_notify_done_create(ngx_rtmp_session_t *s, void *arg,
}
+static ngx_chain_t *
+ngx_rtmp_notify_record_done_create(ngx_rtmp_session_t *s, void *arg,
+ ngx_pool_t *pool)
+{
+ 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_buf_t *b;
+ ngx_str_t *addr_text;
+ 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 +
+ sizeof("&name=") + name_len * 3 +
+ sizeof("&path=") + v->path.len * 3 +
+ + 1 + args_len);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ pl->buf = b;
+
+ 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=",
+ sizeof("&recorder=") - 1);
+ b->last = (u_char*)ngx_escape_uri(b->last, v->recorder.data,
+ v->recorder.len, 0);
+
+ 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, 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);
+
+ if (args_len) {
+ *b->last++ = '&';
+ b->last = (u_char *)ngx_cpymem(b->last, ctx->args, args_len);
+ }
+
+ /* HTTP header */
+ hl = ngx_rtmp_netcall_http_format_header(
+ nacf->url[NGX_RTMP_NOTIFY_RECORD_DONE],
+ pool, cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
+ &ngx_rtmp_netcall_content_type_urlencoded);
+
+ if (hl == NULL) {
+ return NULL;
+ }
+
+ hl->next = cl;
+ cl->next = pl;
+ pl->next = NULL;
+
+ return hl;
+}
+
+
static ngx_int_t
ngx_rtmp_notify_parse_http_retcode(ngx_rtmp_session_t *s,
ngx_chain_t *in)
@@ -417,22 +568,34 @@ ngx_rtmp_notify_play_handle(ngx_rtmp_session_t *s,
static void
-ngx_rtmp_notify_save_name_args(ngx_rtmp_session_t *s,
- u_char name[NGX_RTMP_MAX_NAME], u_char args[NGX_RTMP_MAX_ARGS])
+ngx_rtmp_notify_init(ngx_rtmp_session_t *s,
+ u_char name[NGX_RTMP_MAX_NAME], u_char args[NGX_RTMP_MAX_ARGS],
+ ngx_uint_t flags)
{
ngx_rtmp_notify_ctx_t *ctx;
+ ngx_rtmp_notify_app_conf_t *nacf;
+
+ nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
+
+ if (!nacf->active) {
+ return;
+ }
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module);
+
if (ctx == NULL) {
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_notify_ctx_t));
if (ctx == NULL) {
return;
}
+
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_notify_module);
}
ngx_memcpy(ctx->name, name, NGX_RTMP_MAX_NAME);
ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS);
+
+ ctx->flags |= flags;
}
@@ -447,20 +610,24 @@ ngx_rtmp_notify_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
}
nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
+
if (nacf == NULL) {
goto next;
}
- if (nacf->done_url) {
- ngx_rtmp_notify_save_name_args(s, v->name, v->args);
- }
+ ngx_rtmp_notify_init(s, v->name, v->args, NGX_RTMP_NOTIFY_PUBLISHING);
- if (nacf->publish_url == NULL) {
+ if (nacf->url[NGX_RTMP_NOTIFY_PUBLISH] == NULL) {
goto next;
}
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "notify: publish '%V'",
+ &nacf->url[NGX_RTMP_NOTIFY_PUBLISH]->url);
+
ngx_memzero(&ci, sizeof(ci));
- ci.url = nacf->publish_url;
+
+ ci.url = nacf->url[NGX_RTMP_NOTIFY_PUBLISH];
ci.create = ngx_rtmp_notify_publish_create;
ci.handle = ngx_rtmp_notify_publish_handle;
ci.arg = v;
@@ -479,21 +646,29 @@ 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;
+ if (s->auto_pushed) {
+ goto next;
+ }
+
nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
+
if (nacf == NULL) {
goto next;
}
- if (nacf->done_url) {
- ngx_rtmp_notify_save_name_args(s, v->name, v->args);
- }
+ ngx_rtmp_notify_init(s, v->name, v->args, NGX_RTMP_NOTIFY_PLAYING);
- if (nacf->play_url == NULL) {
+ if (nacf->url[NGX_RTMP_NOTIFY_PLAY] == NULL) {
goto next;
}
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "notify: play '%V'",
+ &nacf->url[NGX_RTMP_NOTIFY_PLAY]->url);
+
ngx_memzero(&ci, sizeof(ci));
- ci.url = nacf->play_url;
+
+ ci.url = nacf->url[NGX_RTMP_NOTIFY_PLAY];
ci.create = ngx_rtmp_notify_play_create;
ci.handle = ngx_rtmp_notify_play_handle;
ci.arg = v;
@@ -507,25 +682,105 @@ ngx_rtmp_notify_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
static ngx_int_t
-ngx_rtmp_notify_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t
- *v)
+ngx_rtmp_notify_delete_stream(ngx_rtmp_session_t *s,
+ ngx_rtmp_delete_stream_t *v)
{
+ ngx_rtmp_notify_ctx_t *ctx;
ngx_rtmp_notify_app_conf_t *nacf;
+
+ if (s->auto_pushed) {
+ goto next;
+ }
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module);
+
+ if (ctx == NULL) {
+ goto next;
+ }
+
+ nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
+
+ if (nacf == NULL) {
+ 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 (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 (nacf->url[NGX_RTMP_NOTIFY_DONE] && ctx->flags) {
+ ngx_rtmp_notify_done(s, "done", nacf->url[NGX_RTMP_NOTIFY_DONE]);
+ }
+
+ ctx->flags = 0;
+
+next:
+ return next_delete_stream(s, v);
+}
+
+
+static ngx_int_t
+ngx_rtmp_notify_record_done(ngx_rtmp_session_t *s, ngx_rtmp_record_done_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->done_url == NULL) {
+ if (nacf == NULL || nacf->url[NGX_RTMP_NOTIFY_RECORD_DONE] == NULL) {
goto next;
}
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "notify: record_done 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->done_url;
- ci.create = ngx_rtmp_notify_done_create;
+
+ ci.url = nacf->url[NGX_RTMP_NOTIFY_RECORD_DONE];
+ ci.create = ngx_rtmp_notify_record_done_create;
+ ci.arg = v;
ngx_rtmp_netcall_create(s, &ci);
next:
- return next_delete_stream(s, v);
+ return next_record_done(s, v);
+}
+
+
+static ngx_int_t
+ngx_rtmp_notify_done(ngx_rtmp_session_t *s, char *cbname, ngx_url_t *url)
+{
+ ngx_rtmp_netcall_init_t ci;
+ ngx_rtmp_notify_done_t ds;
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "notify: %s '%V'", cbname, &url->url);
+
+ ds.cbname = (u_char *) cbname;
+ ds.url = url;
+
+ ngx_memzero(&ci, sizeof(ci));
+
+ ci.url = url;
+ ci.arg = &ds;
+ ci.create = ngx_rtmp_notify_done_create;
+
+ return ngx_rtmp_netcall_create(s, &ci);
}
@@ -537,6 +792,7 @@ ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
ngx_url_t *u;
size_t add;
ngx_str_t *value;
+ ngx_uint_t n;
value = cf->args->elts;
name = &value[0];
@@ -568,17 +824,36 @@ ngx_rtmp_notify_on_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
nacf = ngx_rtmp_conf_get_module_app_conf(cf, ngx_rtmp_notify_module);
- switch (name->data[4]) {
- case 'l': /* on_pLay */
- nacf->play_url = u;
+ n = 0;
+
+ switch (name->len) {
+ case sizeof("on_done") - 1: /* and on_play */
+ if (name->data[3] == 'd') {
+ n = NGX_RTMP_NOTIFY_DONE;
+ } else {
+ n = NGX_RTMP_NOTIFY_PLAY;
+ }
+ break;
+
+ case sizeof("on_publish") - 1:
+ n = NGX_RTMP_NOTIFY_PUBLISH;
+ break;
+
+ case sizeof("on_play_done") - 1:
+ n = NGX_RTMP_NOTIFY_PLAY_DONE;
break;
- case 'u': /* on_pUblish */
- nacf->publish_url = u;
+
+ case sizeof("on_record_done") - 1:
+ n = NGX_RTMP_NOTIFY_RECORD_DONE;
+ break;
+
+ case sizeof("on_publish_done") - 1:
+ n = NGX_RTMP_NOTIFY_PUBLISH_DONE;
break;
- case 'o': /* on_dOne */
- nacf->done_url = u;
}
+ nacf->url[n] = u;
+
return NGX_CONF_OK;
}
@@ -595,6 +870,8 @@ ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf)
next_delete_stream = ngx_rtmp_delete_stream;
ngx_rtmp_delete_stream = ngx_rtmp_notify_delete_stream;
+ next_record_done = ngx_rtmp_record_done;
+ ngx_rtmp_record_done = ngx_rtmp_notify_record_done;
+
return NGX_OK;
}
-
View
4 ngx_rtmp_play_module.c
@@ -440,8 +440,8 @@ ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
goto next;
}
- ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "play: fmt found: '%V'", &ctx->fmt->name);
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "play: %V", &ctx->fmt->name);
sfx = &ctx->fmt->sfx;
View
770 ngx_rtmp_record_module.c
@@ -12,24 +12,33 @@
#include "ngx_rtmp_record_module.h"
+ngx_rtmp_record_done_pt ngx_rtmp_record_done;
+
+
static ngx_rtmp_publish_pt next_publish;
static ngx_rtmp_delete_stream_pt next_delete_stream;
-static char * ngx_rtmp_notify_on_record_done(ngx_conf_t *cf,
- ngx_command_t *cmd, void *conf);
+static char *ngx_rtmp_record_recorder(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
static ngx_int_t ngx_rtmp_record_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_record_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf,
- void *parent, void *child);
+ void *parent, void *child);
static ngx_int_t ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s,
- ngx_rtmp_header_t *h, ngx_chain_t *in);
-
-
-#define NGX_RTMP_RECORD_OFF 0x01
-#define NGX_RTMP_RECORD_AUDIO 0x02
-#define NGX_RTMP_RECORD_VIDEO 0x04
-#define NGX_RTMP_RECORD_KEYFRAMES 0x08
+ ngx_rtmp_record_rec_ctx_t *rctx,
+ ngx_rtmp_header_t *h, ngx_chain_t *in);
+static ngx_int_t ngx_rtmp_record_av(ngx_rtmp_session_t *s,
+ ngx_rtmp_header_t *h, ngx_chain_t *in);
+static ngx_int_t ngx_rtmp_record_node_av(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx, ngx_rtmp_header_t *h, ngx_chain_t *in);
+static ngx_int_t ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx);
+static ngx_int_t ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx);
+static void ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx, ngx_str_t *path);
+static ngx_int_t ngx_rtmp_record_init(ngx_rtmp_session_t *s);
static ngx_conf_bitmask_t ngx_rtmp_record_mask[] = {
@@ -39,6 +48,7 @@ static ngx_conf_bitmask_t ngx_rtmp_record_mask[] = {
{ ngx_string("audio"), NGX_RTMP_RECORD_AUDIO },
{ ngx_string("video"), NGX_RTMP_RECORD_VIDEO },
{ ngx_string("keyframes"), NGX_RTMP_RECORD_KEYFRAMES },
+ { ngx_string("manual"), NGX_RTMP_RECORD_MANUAL },
{ ngx_null_string, 0 }
};
@@ -46,61 +56,69 @@ static ngx_conf_bitmask_t ngx_rtmp_record_mask[] = {
static ngx_command_t ngx_rtmp_record_commands[] = {
{ ngx_string("record"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, flags),
ngx_rtmp_record_mask },
{ ngx_string("record_path"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, path),
NULL },
{ ngx_string("record_suffix"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, suffix),
NULL },
{ ngx_string("record_unique"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, unique),
NULL },
{ ngx_string("record_max_size"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, max_size),
NULL },
{ ngx_string("record_max_frames"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, max_frames),
NULL },
{ ngx_string("record_interval"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
+ NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|
+ NGX_RTMP_REC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_record_app_conf_t, interval),
NULL },
- { ngx_string("on_record_done"),
- NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
- ngx_rtmp_notify_on_record_done,
+ { ngx_string("recorder"),
+ NGX_RTMP_APP_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+ ngx_rtmp_record_recorder,
NGX_RTMP_APP_CONF_OFFSET,
0,
NULL },
+
ngx_null_command
};
@@ -139,14 +157,22 @@ ngx_rtmp_record_create_app_conf(ngx_conf_t *cf)
ngx_rtmp_record_app_conf_t *racf;
racf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_record_app_conf_t));
+
if (racf == NULL) {
return NULL;
}
- racf->max_size = NGX_CONF_UNSET;
+ racf->max_size = NGX_CONF_UNSET;
racf->max_frames = NGX_CONF_UNSET;
- racf->interval = NGX_CONF_UNSET;
- racf->unique = NGX_CONF_UNSET;
+ racf->interval = NGX_CONF_UNSET;
+ racf->unique = NGX_CONF_UNSET;
+ racf->url = NGX_CONF_UNSET_PTR;
+
+ ngx_str_set(&racf->id, "default");
+
+ if (ngx_array_init(&racf->rec, cf->pool, 1, sizeof(void *)) != NGX_OK) {
+ return NULL;
+ }
return racf;
}
@@ -155,18 +181,28 @@ ngx_rtmp_record_create_app_conf(ngx_conf_t *cf)
static char *
ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
- ngx_rtmp_record_app_conf_t *prev = parent;
- ngx_rtmp_record_app_conf_t *conf = child;
+ ngx_rtmp_record_app_conf_t *prev = parent;
+ ngx_rtmp_record_app_conf_t *conf = child;
+ ngx_rtmp_record_app_conf_t **rracf;
- ngx_conf_merge_bitmask_value(conf->flags, prev->flags,
- (NGX_CONF_BITMASK_SET|NGX_RTMP_RECORD_OFF));
ngx_conf_merge_str_value(conf->path, prev->path, "");
ngx_conf_merge_str_value(conf->suffix, prev->suffix, ".flv");
ngx_conf_merge_size_value(conf->max_size, prev->max_size, 0);
ngx_conf_merge_size_value(conf->max_frames, prev->max_frames, 0);
ngx_conf_merge_value(conf->unique, prev->unique, 0);
ngx_conf_merge_msec_value(conf->interval, prev->interval,
- (ngx_msec_t)NGX_CONF_UNSET);
+ (ngx_msec_t) NGX_CONF_UNSET);
+ ngx_conf_merge_bitmask_value(conf->flags, prev->flags, 0);
+ ngx_conf_merge_ptr_value(conf->url, prev->url, NULL);
+
+ if (conf->flags) {
+ rracf = ngx_array_push(&conf->rec);
+ if (rracf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *rracf = conf;
+ }
return NGX_CONF_OK;
}
@@ -192,296 +228,380 @@ ngx_rtmp_record_write_header(ngx_file_t *file)
};
return ngx_write_file(file, flv_header, sizeof(flv_header), 0) == NGX_ERROR
- ? NGX_ERROR
- : NGX_OK;
+ ? NGX_ERROR
+ : NGX_OK;
+}
+
+
+static ngx_rtmp_record_rec_ctx_t *
+ngx_rtmp_record_get_node_ctx(ngx_rtmp_session_t *s, ngx_uint_t n)
+{
+ ngx_rtmp_record_ctx_t *ctx;
+ ngx_rtmp_record_rec_ctx_t *rctx;
+
+ if (ngx_rtmp_record_init(s) != NGX_OK) {
+ return NULL;
+ }
+
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
+
+ if (n >= ctx->rec.nelts) {
+ return NULL;
+ }
+
+ rctx = ctx->rec.elts;
+
+ return &rctx[n];
+}
+
+
+ngx_int_t
+ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n, ngx_str_t *path)
+{
+ ngx_rtmp_record_rec_ctx_t *rctx;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: #%ui manual open", n);
+
+ rctx = ngx_rtmp_record_get_node_ctx(s, n);
+
+ if (rctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_rtmp_record_node_open(s, rctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (path) {
+ ngx_rtmp_record_make_path(s, rctx, path);
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_rtmp_record_close(ngx_rtmp_session_t *s, ngx_uint_t n, ngx_str_t *path)
+{
+ ngx_rtmp_record_rec_ctx_t *rctx;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: #%ui manual close", n);
+
+ rctx = ngx_rtmp_record_get_node_ctx(s, n);
+
+ if (rctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_rtmp_record_node_close(s, rctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (path) {
+ ngx_rtmp_record_make_path(s, rctx, path);
+ }
+
+ return NGX_OK;
}
/* This funcion returns pointer to a static buffer */
-u_char *
-ngx_rtmp_record_make_path(ngx_rtmp_session_t *s)
+static void
+ngx_rtmp_record_make_path(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx, ngx_str_t *path)
{
ngx_rtmp_record_ctx_t *ctx;
- ngx_rtmp_record_app_conf_t *racf;
+ ngx_rtmp_record_app_conf_t *rracf;
u_char *p, *l;
+
static u_char buf[NGX_TIME_T_LEN + 1];
- static u_char path[NGX_MAX_PATH + 1];
+ static u_char pbuf[NGX_MAX_PATH + 1];
- racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
+ rracf = rctx->conf;
+
/* create file path */
- p = path;
- l = path + sizeof(path) - 1;
- p = ngx_cpymem(p, racf->path.data,
- ngx_min(racf->path.len, (size_t)(l - p - 1)));
+ p = pbuf;
+ l = pbuf + sizeof(pbuf) - 1;
+
+ p = ngx_cpymem(p, rracf->path.data,
+ ngx_min(rracf->path.len, (size_t)(l - p - 1)));
*p++ = '/';
p = (u_char *)ngx_escape_uri(p, ctx->name, ngx_min(ngx_strlen(ctx->name),
(size_t)(l - p)), NGX_ESCAPE_URI_COMPONENT);
- if (racf->unique) {
- /* append timestamp */
+
+ /* append timestamp */
+ if (rracf->unique) {
p = ngx_cpymem(p, buf, ngx_min(ngx_sprintf(buf, "-%T",
- ctx->timestamp) - buf, l - p));
+ rctx->timestamp) - buf, l - p));
}
- p = ngx_cpymem(p, racf->suffix.data,
- ngx_min(racf->suffix.len, (size_t)(l - p)));
+
+ p = ngx_cpymem(p, rracf->suffix.data,
+ ngx_min(rracf->suffix.len, (size_t)(l - p)));
*p = 0;
- return path;
+ path->data = pbuf;
+ path->len = p - pbuf;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: %V path: '%V'", &rracf->id, path);
}
-ngx_int_t
-ngx_rtmp_record_open(ngx_rtmp_session_t *s)
+static ngx_int_t
+ngx_rtmp_record_node_open(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx)
{
- ngx_rtmp_record_ctx_t *ctx;
- ngx_err_t err;
- u_char *path;
+ ngx_rtmp_record_app_conf_t *rracf;
+ ngx_err_t err;
+ ngx_str_t path;
- ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
- if (ctx == NULL || ctx->file.fd != NGX_INVALID_FILE) {
- return NGX_ERROR;
+ rracf = rctx->conf;
+
+ if (rctx->file.fd != NGX_INVALID_FILE) {
+ return NGX_OK;
}
- ctx->timestamp = ngx_cached_time->sec;
- path = ngx_rtmp_record_make_path(s);
+
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "record: path '%s'", path);
-
- /* open file */
- ctx->nframes = 0;
- ngx_memzero(&ctx->file, sizeof(ctx->file));
- ctx->file.offset = 0;
- ctx->file.log = s->connection->log;
- ctx->file.fd = ngx_open_file(path, NGX_FILE_WRONLY,
- NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
- ctx->last = *ngx_cached_time;
- if (ctx->file.fd == NGX_INVALID_FILE) {
+ "record: %V opening", &rracf->id);
+
+ rctx->timestamp = ngx_cached_time->sec;
+
+ ngx_rtmp_record_make_path(s, rctx, &path);
+
+ rctx->nframes = 0;
+
+ ngx_memzero(&rctx->file, sizeof(rctx->file));
+
+ rctx->last = *ngx_cached_time;
+ rctx->file.offset = 0;
+ rctx->file.log = s->connection->log;
+ rctx->file.fd = ngx_open_file(path.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (rctx->file.fd == NGX_INVALID_FILE) {
err = ngx_errno;
+
if (err != NGX_ENOENT) {
ngx_log_error(NGX_LOG_CRIT, s->connection->log, err,
- "record: failed to open file '%s'", path);
+ "record: %V failed to open file '%V'",
+ &rracf->id, &path);
}
+
return NGX_OK;
}
- ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "record: opened '%s'", path);
+ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: %V opened '%V'", &rracf->id, &path);
return NGX_OK;
}
static ngx_int_t
-ngx_rtmp_record_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
+ngx_rtmp_record_init(ngx_rtmp_session_t *s)
{
- ngx_rtmp_record_app_conf_t *racf;
+ ngx_rtmp_record_app_conf_t *racf, **rracf;
+ ngx_rtmp_record_rec_ctx_t *rctx;
ngx_rtmp_record_ctx_t *ctx;
- u_char *p;
+ ngx_uint_t n;
- if (s->auto_pushed) {
- goto next;
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
+
+ if (ctx) {
+ return NGX_OK;
}
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
- if (racf == NULL || racf->flags & NGX_RTMP_RECORD_OFF) {
- goto next;
+ if (racf == NULL || racf->rec.nelts == 0) {
+ return NGX_OK;
}
- ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
+ ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_record_ctx_t));
if (ctx == NULL) {
- ctx = ngx_pcalloc(s->connection->pool,
- sizeof(ngx_rtmp_record_ctx_t));
- if (ctx == NULL) {
- return NGX_ERROR;
- }
- ctx->file.fd = NGX_INVALID_FILE;
- ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_record_module);
+ return NGX_ERROR;
}
- ngx_memcpy(ctx->name, v->name, sizeof(ctx->name));
- ngx_memcpy(ctx->args, v->args, sizeof(ctx->args));
+ ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_record_module);
- /* terminate name on /../ */
- for (p = ctx->name; *p; ++p) {
- if (ngx_path_separator(p[0]) &&
- p[1] == '.' && p[2] == '.' &&
- ngx_path_separator(p[3]))
- {
- *p = 0;
- break;
- }
+ if (ngx_array_init(&ctx->rec, s->connection->pool, racf->rec.nelts,
+ sizeof(ngx_rtmp_record_rec_ctx_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
}
- if (ngx_rtmp_record_open(s) != NGX_OK) {
+ rracf = racf->rec.elts;
+
+ rctx = ngx_array_push_n(&ctx->rec, racf->rec.nelts);
+
+ if (rctx == NULL) {
return NGX_ERROR;
}
-next:
- return next_publish(s, v);
+ for (n = 0; n < racf->rec.nelts; ++n, ++rracf, ++rctx) {
+ ngx_memzero(rctx, sizeof(*rctx));
+
+ rctx->conf = *rracf;
+ rctx->file.fd = NGX_INVALID_FILE;
+ }
+
+ return NGX_OK;
}
-static ngx_chain_t *
-ngx_rtmp_record_notify_create(ngx_rtmp_session_t *s, void *arg,
- ngx_pool_t *pool)
+static ngx_int_t
+ngx_rtmp_record_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
{
ngx_rtmp_record_app_conf_t *racf;
ngx_rtmp_record_ctx_t *ctx;
- ngx_chain_t *hl, *cl, *pl;
- ngx_buf_t *b;
- ngx_str_t *addr_text;
- size_t path_len, name_len, args_len;
- u_char *path;
-
- racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
+ u_char *p;
+ ngx_uint_t n;
+ ngx_rtmp_record_rec_ctx_t *rctx;
- ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
- if (ctx == NULL) {
- return NULL;
+ if (s->auto_pushed) {
+ goto next;
}
- /* common variables */
- cl = ngx_rtmp_netcall_http_format_session(s, pool);
+ racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
- if (cl == NULL) {
- return NULL;
+ if (racf == NULL || racf->rec.nelts == 0) {
+ goto next;
}
- /* publish variables */
- pl = ngx_alloc_chain_link(pool);
-
- if (pl == NULL) {
- return NULL;
+ if (ngx_rtmp_record_init(s) != NGX_OK) {
+ return NGX_ERROR;
}
- path = ngx_rtmp_record_make_path(s);
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: publish %ui nodes",
+ racf->rec.nelts);
- path_len = ngx_strlen(path);
- name_len = ngx_strlen(ctx->name);
- args_len = ngx_strlen(ctx->args);
- addr_text = &s->connection->addr_text;
+ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
- b = ngx_create_temp_buf(pool,
- sizeof("&call=record_done") +
- sizeof("&addr=") + addr_text->len +
- sizeof("&name=") + name_len * 3 +
- sizeof("&path=") + path_len * 3 +
- + 1 + args_len);
- if (b == NULL) {
- return NULL;
+ ngx_memcpy(ctx->name, v->name, sizeof(ctx->name));
+ ngx_memcpy(ctx->args, v->args, sizeof(ctx->args));
+
+ /* terminate name on /../ */
+ for (p = ctx->name; *p; ++p) {
+ if (ngx_path_separator(p[0]) &&
+ p[1] == '.' && p[2] == '.' &&
+ ngx_path_separator(p[3]))
+ {
+ *p = 0;
+ break;
+ }
}
- pl->buf = b;
+ rctx = ctx->rec.elts;
- b->last = ngx_cpymem(b->last, (u_char*)"&call=record_done",
- sizeof("&call=record_done") - 1);
+ for (n = 0; n < ctx->rec.nelts; ++n, ++rctx) {
+ if (rctx->conf->flags & (NGX_RTMP_RECORD_OFF|NGX_RTMP_RECORD_MANUAL)) {
+ continue;
+ }
- 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);
+ ngx_rtmp_record_node_open(s, rctx);
+ }
- 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);
+next:
+ return next_publish(s, v);
+}
- b->last = ngx_cpymem(b->last, (u_char*)"&path=", sizeof("&path=") - 1);
- b->last = (u_char*)ngx_escape_uri(b->last, path, path_len, 0);
- if (args_len) {
- *b->last++ = '&';
- b->last = (u_char *)ngx_cpymem(b->last, ctx->args, args_len);
- }
+static ngx_int_t
+ngx_rtmp_record_node_close(ngx_rtmp_session_t *s,
+ ngx_rtmp_record_rec_ctx_t *rctx)
+{
+ ngx_rtmp_record_app_conf_t *rracf;
+ ngx_err_t err;
+ void **app_conf;
+ ngx_int_t rc;
+ ngx_rtmp_record_done_t v;
- /* HTTP header */
- hl = ngx_rtmp_netcall_http_format_header(racf->url, pool,
- cl->buf->last - cl->buf->pos + (pl->buf->last - pl->buf->pos),
- &ngx_rtmp_netcall_content_type_urlencoded);
+ rracf = rctx->conf;
- if (hl == NULL) {
- return NULL;
+ if (rctx->file.fd == NGX_INVALID_FILE) {
+ return NGX_OK;
}
- hl->next = cl;
- cl->next = pl;
- pl->next = NULL;
+ if (ngx_close_file(rctx->file.fd) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+ ngx_log_error(NGX_LOG_CRIT, s->connection->log, err,
+ "record: %V error closing file", &rracf->id);
+ }
- return hl;
-}
+ rctx->file.fd = NGX_INVALID_FILE;
+ ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
+ "record: %V closed", &rracf->id);
-static ngx_int_t
-ngx_rtmp_record_notify(ngx_rtmp_session_t *s)
-{
- ngx_rtmp_record_app_conf_t *racf;
- ngx_rtmp_netcall_init_t ci;
+ app_conf = s->app_conf;
- racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
- if (racf == NULL || racf->url == NULL) {
- return NGX_OK;
+ if (rracf->rec_conf) {
+ s->app_conf = rracf->rec_conf;
}
- ngx_memzero(&ci, sizeof(ci));
+ v.recorder = rracf->id;
+ ngx_rtmp_record_make_path(s, rctx, &v.path);
- ci.url = racf->url;
- ci.create = ngx_rtmp_record_notify_create;
+ rc = ngx_rtmp_record_done(s, &v);
- return ngx_rtmp_netcall_create(s, &ci);
+ s->app_conf = app_conf;
+
+ return rc;
}
-ngx_int_t
-ngx_rtmp_record_close(ngx_rtmp_session_t *s)
+static ngx_int_t
+ngx_rtmp_record_delete_stream(ngx_rtmp_session_t *s,
+ ngx_rtmp_delete_stream_t *v)
{
- ngx_rtmp_record_ctx_t *ctx;
- ngx_err_t err;
+ ngx_rtmp_record_ctx_t *ctx;
+ ngx_rtmp_record_rec_ctx_t *rctx;
+ ngx_uint_t n;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
- if (ctx == NULL || ctx->file.fd == NGX_INVALID_FILE) {
- return NGX_OK;
- }
-
- if (ngx_close_file(ctx->file.fd) == NGX_FILE_ERROR) {
- err = ngx_errno;
- ngx_log_error(NGX_LOG_CRIT, s->connection->log, err,
- "record: error closing file");
+ if (ctx == NULL) {
+ goto next;
}
- ctx->file.fd = NGX_INVALID_FILE;
- ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
- "record: closed");
-
- return ngx_rtmp_record_notify(s);
-}