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.
This commit is contained in:
Roman Arutyunyan 2012-09-29 00:07:46 +04:00
commit ac13bbf1af
16 changed files with 1774 additions and 459 deletions

View file

@ -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:

12
config
View file

@ -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 file

@ -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 file

@ -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 file

@ -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 file

@ -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 file

@ -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;
}

564
ngx_rtmp_enotify_module.c Normal file
View file

@ -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;
}

188
ngx_rtmp_eval.c Normal file
View file

@ -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;
}

37
ngx_rtmp_eval.h Normal file
View file

@ -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 file

@ -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 file

@ -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;
ngx_conf_merge_ptr_value(conf->publish_url, prev->publish_url, 0);
ngx_conf_merge_ptr_value(conf->play_url, prev->play_url, 0);
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;
}
}
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 @@ next:
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;
ngx_rtmp_netcall_init_t ci;
nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
if (nacf == NULL || nacf->done_url == NULL) {
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->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 'u': /* on_pUblish */
nacf->publish_url = u;
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 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 file

@ -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;

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,15 @@
#include "ngx_rtmp.h"
#define NGX_RTMP_RECORD_OFF 0x01
#define NGX_RTMP_RECORD_AUDIO 0x02
#define NGX_RTMP_RECORD_VIDEO 0x04
#define NGX_RTMP_RECORD_KEYFRAMES 0x08
#define NGX_RTMP_RECORD_MANUAL 0x10
typedef struct {
ngx_str_t id;
ngx_uint_t flags;
ngx_str_t path;
size_t max_size;
@ -19,25 +27,50 @@ typedef struct {
ngx_str_t suffix;
ngx_flag_t unique;
ngx_url_t *url;
void **rec_conf;
ngx_array_t rec; /* ngx_rtmp_record_app_conf_t * */
} ngx_rtmp_record_app_conf_t;
typedef struct {
ngx_rtmp_record_app_conf_t *conf;
ngx_file_t file;
ngx_uint_t nframes;
uint32_t epoch;
ngx_time_t last;
time_t timestamp;
} ngx_rtmp_record_rec_ctx_t;
typedef struct {
ngx_array_t rec; /* ngx_rtmp_record_rec_ctx_t */
u_char name[NGX_RTMP_MAX_NAME];
u_char args[NGX_RTMP_MAX_ARGS];
} ngx_rtmp_record_ctx_t;
u_char * ngx_rtmp_record_make_path(ngx_rtmp_session_t *s);
ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s);
/* Manual recording control,
* 'n' is record node index in config array.
* Note: these functions allocate path in static buffer */
ngx_int_t ngx_rtmp_record_close(ngx_rtmp_session_t *s);
ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n,
ngx_str_t *path);
ngx_int_t ngx_rtmp_record_close(ngx_rtmp_session_t *s, ngx_uint_t n,
ngx_str_t *path);
typedef struct {
ngx_str_t recorder;
ngx_str_t path;
} ngx_rtmp_record_done_t;
typedef ngx_int_t (*ngx_rtmp_record_done_pt)(ngx_rtmp_session_t *s,
ngx_rtmp_record_done_t *v);
extern ngx_rtmp_record_done_pt ngx_rtmp_record_done;
extern ngx_module_t ngx_rtmp_record_module;

View file

@ -396,6 +396,7 @@ ngx_rtmp_relay_create_remote_ctx(ngx_rtmp_session_t *s, ngx_str_t* name,
return NULL;
}
rs->app_conf = s->app_conf;
rs->relay = 1;
rctx->session = rs;
ngx_rtmp_set_ctx(rs, rctx, ngx_rtmp_relay_module);
ngx_str_set(&rs->flashver, "ngx-local-relay");
@ -503,7 +504,7 @@ ngx_int_t
ngx_rtmp_relay_pull(ngx_rtmp_session_t *s, ngx_str_t *name,
ngx_rtmp_relay_target_t *target)
{
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: create pull name='%V' app='%V' playpath='%V' url='%V'",
name, &target->app, &target->play_path, &target->url.url);
@ -517,7 +518,7 @@ ngx_int_t
ngx_rtmp_relay_push(ngx_rtmp_session_t *s, ngx_str_t *name,
ngx_rtmp_relay_target_t *target)
{
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"relay: create push name='%V' app='%V' playpath='%V' url='%V'",
name, &target->app, &target->play_path, &target->url.url);
@ -536,6 +537,10 @@ ngx_rtmp_relay_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
size_t n;
ngx_rtmp_relay_ctx_t *ctx;
if (s->auto_pushed) {
goto next;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
if (ctx && ctx->relay) {
goto next;