mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-06-13 18:49:32 +02:00
Merge branch 'codec'
This commit is contained in:
commit
077a2a7c67
3
config
3
config
|
@ -11,6 +11,7 @@ CORE_MODULES="$CORE_MODULES
|
||||||
ngx_rtmp_notify_module \
|
ngx_rtmp_notify_module \
|
||||||
ngx_rtmp_relay_module \
|
ngx_rtmp_relay_module \
|
||||||
ngx_rtmp_exec_module \
|
ngx_rtmp_exec_module \
|
||||||
|
ngx_rtmp_codec_module \
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,8 +39,8 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
|
||||||
$ngx_addon_dir/ngx_rtmp_stat_module.c \
|
$ngx_addon_dir/ngx_rtmp_stat_module.c \
|
||||||
$ngx_addon_dir/ngx_rtmp_relay_module.c \
|
$ngx_addon_dir/ngx_rtmp_relay_module.c \
|
||||||
$ngx_addon_dir/ngx_rtmp_bandwidth.c \
|
$ngx_addon_dir/ngx_rtmp_bandwidth.c \
|
||||||
$ngx_addon_dir/ngx_rtmp_codecs.c \
|
|
||||||
$ngx_addon_dir/ngx_rtmp_exec_module.c \
|
$ngx_addon_dir/ngx_rtmp_exec_module.c \
|
||||||
|
$ngx_addon_dir/ngx_rtmp_codec_module.c \
|
||||||
"
|
"
|
||||||
CFLAGS="$CFLAGS -I$ngx_addon_dir"
|
CFLAGS="$CFLAGS -I$ngx_addon_dir"
|
||||||
|
|
||||||
|
|
229
ngx_rtmp_codec_module.c
Normal file
229
ngx_rtmp_codec_module.c
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Roman Arutyunyan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ngx_rtmp_codec_module.h"
|
||||||
|
#include "ngx_rtmp_live_module.h"
|
||||||
|
#include "ngx_rtmp_cmd_module.h"
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf);
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_rtmp_module_t ngx_rtmp_codec_module_ctx = {
|
||||||
|
NULL, /* preconfiguration */
|
||||||
|
ngx_rtmp_codec_postconfiguration, /* postconfiguration */
|
||||||
|
NULL, /* create main configuration */
|
||||||
|
NULL, /* init main configuration */
|
||||||
|
NULL, /* create server configuration */
|
||||||
|
NULL, /* merge server configuration */
|
||||||
|
NULL, /* create app configuration */
|
||||||
|
NULL /* merge app configuration */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ngx_module_t ngx_rtmp_codec_module = {
|
||||||
|
NGX_MODULE_V1,
|
||||||
|
&ngx_rtmp_codec_module_ctx, /* module context */
|
||||||
|
NULL, /* 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 const char *
|
||||||
|
audio_codecs[] = {
|
||||||
|
"",
|
||||||
|
"ADPCM",
|
||||||
|
"MP3",
|
||||||
|
"LinearLE",
|
||||||
|
"Nellymoser16",
|
||||||
|
"Nellymoser8",
|
||||||
|
"Nellymoser",
|
||||||
|
"G711A",
|
||||||
|
"G711U",
|
||||||
|
"",
|
||||||
|
"AAC",
|
||||||
|
"Speex",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"MP3-8K",
|
||||||
|
"DeviceSpecific",
|
||||||
|
"Uncompressed"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
video_codecs[] = {
|
||||||
|
"",
|
||||||
|
"Jpeg",
|
||||||
|
"Sorenson-H263",
|
||||||
|
"ScreenVideo",
|
||||||
|
"On2-VP6",
|
||||||
|
"On2-VP6-Alpha",
|
||||||
|
"ScreenVideo2",
|
||||||
|
"H264",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
u_char *
|
||||||
|
ngx_rtmp_get_audio_codec_name(ngx_uint_t id)
|
||||||
|
{
|
||||||
|
return (u_char *)(id < sizeof(audio_codecs) / sizeof(audio_codecs[0])
|
||||||
|
? audio_codecs[id]
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u_char *
|
||||||
|
ngx_rtmp_get_video_codec_name(ngx_uint_t id)
|
||||||
|
{
|
||||||
|
return (u_char *)(id < sizeof(video_codecs) / sizeof(video_codecs[0])
|
||||||
|
? video_codecs[id]
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_rtmp_codec_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
|
ngx_chain_t *in)
|
||||||
|
{
|
||||||
|
ngx_rtmp_codec_ctx_t *ctx;
|
||||||
|
ngx_rtmp_core_srv_conf_t *cscf;
|
||||||
|
|
||||||
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
||||||
|
|
||||||
|
if (ctx->avc_header) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, ctx->avc_header);
|
||||||
|
ctx->avc_header = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->aac_header) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, ctx->aac_header);
|
||||||
|
ctx->aac_header = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->avc_pheader) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, ctx->avc_pheader);
|
||||||
|
ctx->avc_pheader = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->aac_pheader) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, ctx->aac_pheader);
|
||||||
|
ctx->aac_pheader = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
|
ngx_chain_t *in)
|
||||||
|
{
|
||||||
|
ngx_rtmp_core_srv_conf_t *cscf;
|
||||||
|
ngx_rtmp_codec_ctx_t *ctx;
|
||||||
|
ngx_chain_t **header, **pheader;
|
||||||
|
uint8_t fmt;
|
||||||
|
ngx_rtmp_header_t ch, lh;
|
||||||
|
|
||||||
|
/* save AVC/AAC header */
|
||||||
|
if ((h->type != NGX_RTMP_MSG_AUDIO
|
||||||
|
&& h->type != NGX_RTMP_MSG_VIDEO)
|
||||||
|
|| in->buf->last - in->buf->pos < 2)
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no conf */
|
||||||
|
if (in->buf->pos[1]) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
||||||
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||||
|
if (ctx == NULL) {
|
||||||
|
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_codec_ctx_t));
|
||||||
|
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_codec_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = in->buf->pos[0];
|
||||||
|
header = NULL;
|
||||||
|
if (h->type == NGX_RTMP_MSG_AUDIO) {
|
||||||
|
if (((fmt & 0xf0) >> 4) == NGX_RTMP_AUDIO_AAC) {
|
||||||
|
header = &ctx->aac_header;
|
||||||
|
pheader = &ctx->aac_pheader;
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"codec: AAC header arrived");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((fmt & 0x0f) == NGX_RTMP_VIDEO_H264) {
|
||||||
|
header = &ctx->avc_header;
|
||||||
|
pheader = &ctx->avc_pheader;
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"codec: AVC/H264 header arrived");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header == NULL) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*header) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, *header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pheader) {
|
||||||
|
ngx_rtmp_free_shared_chain(cscf, *pheader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* equal headers; timeout diff is zero */
|
||||||
|
ngx_memzero(&ch, sizeof(ch));
|
||||||
|
ngx_memzero(&lh, sizeof(lh));
|
||||||
|
ch.timestamp = h->timestamp;
|
||||||
|
ch.msid = NGX_RTMP_LIVE_MSID;
|
||||||
|
ch.type = h->type;
|
||||||
|
ch.csid = (h->type == NGX_RTMP_MSG_VIDEO
|
||||||
|
? NGX_RTMP_LIVE_CSID_VIDEO
|
||||||
|
: NGX_RTMP_LIVE_CSID_AUDIO);
|
||||||
|
lh = ch;
|
||||||
|
*header = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||||
|
*pheader = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||||
|
ngx_rtmp_prepare_message(s, &ch, &lh, *pheader);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf)
|
||||||
|
{
|
||||||
|
ngx_rtmp_core_main_conf_t *cmcf;
|
||||||
|
ngx_rtmp_handler_pt *h;
|
||||||
|
|
||||||
|
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
|
||||||
|
|
||||||
|
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]);
|
||||||
|
*h = ngx_rtmp_codec_av;
|
||||||
|
|
||||||
|
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_VIDEO]);
|
||||||
|
*h = ngx_rtmp_codec_av;
|
||||||
|
|
||||||
|
h = ngx_array_push(&cmcf->events[NGX_RTMP_DISCONNECT]);
|
||||||
|
*h = ngx_rtmp_codec_disconnect;
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
62
ngx_rtmp_codec_module.h
Normal file
62
ngx_rtmp_codec_module.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Roman Arutyunyan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _NGX_RTMP_CODEC_H_INCLUDED_
|
||||||
|
#define _NGX_RTMP_CODEC_H_INCLUDED_
|
||||||
|
|
||||||
|
|
||||||
|
#include "ngx_rtmp.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Audio codecs */
|
||||||
|
enum {
|
||||||
|
/* Uncompressed codec id is actually 0,
|
||||||
|
* but we use another value for consistency */
|
||||||
|
NGX_RTMP_AUDIO_UNCOMPRESSED = 16,
|
||||||
|
NGX_RTMP_AUDIO_ADPCM = 1,
|
||||||
|
NGX_RTMP_AUDIO_MP3 = 2,
|
||||||
|
NGX_RTMP_AUDIO_LINEAR_LE = 3,
|
||||||
|
NGX_RTMP_AUDIO_NELLY16 = 4,
|
||||||
|
NGX_RTMP_AUDIO_NELLY8 = 5,
|
||||||
|
NGX_RTMP_AUDIO_NELLY = 6,
|
||||||
|
NGX_RTMP_AUDIO_G711A = 7,
|
||||||
|
NGX_RTMP_AUDIO_G711U = 8,
|
||||||
|
NGX_RTMP_AUDIO_AAC = 10,
|
||||||
|
NGX_RTMP_AUDIO_SPEEX = 11,
|
||||||
|
NGX_RTMP_AUDIO_MP3_8 = 14,
|
||||||
|
NGX_RTMP_AUDIO_DEVSPEC = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Video codecs */
|
||||||
|
enum {
|
||||||
|
NGX_RTMP_VIDEO_JPEG = 1,
|
||||||
|
NGX_RTMP_VIDEO_SORENSON_H263 = 2,
|
||||||
|
NGX_RTMP_VIDEO_SCREEN = 3,
|
||||||
|
NGX_RTMP_VIDEO_ON2_VP6 = 4,
|
||||||
|
NGX_RTMP_VIDEO_ON2_VP6_ALPHA = 5,
|
||||||
|
NGX_RTMP_VIDEO_SCREEN2 = 6,
|
||||||
|
NGX_RTMP_VIDEO_H264 = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
u_char * ngx_rtmp_get_audio_codec_name(ngx_uint_t id);
|
||||||
|
u_char * ngx_rtmp_get_video_codec_name(ngx_uint_t id);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ngx_chain_t *avc_header;
|
||||||
|
ngx_chain_t *aac_header;
|
||||||
|
|
||||||
|
/* prepared headers (for live streaming) */
|
||||||
|
ngx_chain_t *avc_pheader;
|
||||||
|
ngx_chain_t *aac_pheader;
|
||||||
|
} ngx_rtmp_codec_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern ngx_module_t ngx_rtmp_codec_module;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _NGX_RTMP_LIVE_H_INCLUDED_ */
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012 Roman Arutyunyan
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "ngx_rtmp_codecs.h"
|
|
||||||
|
|
||||||
|
|
||||||
const char * audio_codecs[] = {
|
|
||||||
"",
|
|
||||||
"ADPCM",
|
|
||||||
"MP3",
|
|
||||||
"LinearLE",
|
|
||||||
"Nellymoser16",
|
|
||||||
"Nellymoser8",
|
|
||||||
"Nellymoser",
|
|
||||||
"G711A",
|
|
||||||
"G711U",
|
|
||||||
"",
|
|
||||||
"AAC",
|
|
||||||
"Speex",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"MP3-8K",
|
|
||||||
"DeviceSpecific",
|
|
||||||
"Uncompressed"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const char * video_codecs[] = {
|
|
||||||
"",
|
|
||||||
"Jpeg",
|
|
||||||
"Sorenson-H263",
|
|
||||||
"ScreenVideo",
|
|
||||||
"On2-VP6",
|
|
||||||
"On2-VP6-Alpha",
|
|
||||||
"ScreenVideo2",
|
|
||||||
"H264",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
u_char *
|
|
||||||
ngx_rtmp_get_audio_codec_name(ngx_uint_t id)
|
|
||||||
{
|
|
||||||
return (u_char *)(id < sizeof(audio_codecs) / sizeof(audio_codecs[0])
|
|
||||||
? audio_codecs[id]
|
|
||||||
: "");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
u_char *
|
|
||||||
ngx_rtmp_get_video_codec_name(ngx_uint_t id)
|
|
||||||
{
|
|
||||||
return (u_char *)(id < sizeof(video_codecs) / sizeof(video_codecs[0])
|
|
||||||
? video_codecs[id]
|
|
||||||
: "");
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012 Roman Arutyunyan
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _NGX_RTMP_CODECS_H_INCLUDED_
|
|
||||||
#define _NGX_RTMP_CODECS_H_INCLUDED_
|
|
||||||
|
|
||||||
|
|
||||||
#include <ngx_core.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio codecs */
|
|
||||||
enum {
|
|
||||||
/* Uncompressed codec id is actually 0,
|
|
||||||
* but we use another value for consistency */
|
|
||||||
NGX_RTMP_AUDIO_UNCOMPRESSED = 12,
|
|
||||||
NGX_RTMP_AUDIO_ADPCM = 1,
|
|
||||||
NGX_RTMP_AUDIO_MP3 = 2,
|
|
||||||
NGX_RTMP_AUDIO_NELLY8 = 5,
|
|
||||||
NGX_RTMP_AUDIO_NELLY = 6,
|
|
||||||
NGX_RTMP_AUDIO_HE_ACC = 10,
|
|
||||||
NGX_RTMP_AUDIO_SPEEX = 11
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Video codecs */
|
|
||||||
enum {
|
|
||||||
NGX_RTMP_VIDEO_SORENSON_H263 = 2,
|
|
||||||
NGX_RTMP_VIDEO_SCREEN = 3,
|
|
||||||
NGX_RTMP_VIDEO_ON2_VP6 = 4,
|
|
||||||
NGX_RTMP_VIDEO_ON2_VP6_ALPHA = 5,
|
|
||||||
NGX_RTMP_VIDEO_H264 = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
u_char * ngx_rtmp_get_audio_codec_name(ngx_uint_t id);
|
|
||||||
u_char * ngx_rtmp_get_video_codec_name(ngx_uint_t id);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NGX_RTMP_CODECS_H_INCLUDED_ */
|
|
||||||
|
|
|
@ -379,7 +379,7 @@ ngx_rtmp_exec_run(ngx_rtmp_session_t *s, size_t n)
|
||||||
args[n + 1] = ngx_rtmp_exec_prepare_arg(s, arg);
|
args[n + 1] = ngx_rtmp_exec_prepare_arg(s, arg);
|
||||||
}
|
}
|
||||||
args[n + 1] = NULL;
|
args[n + 1] = NULL;
|
||||||
if (execve((char *)ec->cmd.data, args, environ) == -1) {
|
if (execv((char *)ec->cmd.data, args) == -1) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "ngx_rtmp_live_module.h"
|
#include "ngx_rtmp_live_module.h"
|
||||||
#include "ngx_rtmp_cmd_module.h"
|
#include "ngx_rtmp_cmd_module.h"
|
||||||
#include "ngx_rtmp_codecs.h"
|
#include "ngx_rtmp_codec_module.h"
|
||||||
|
|
||||||
|
|
||||||
static ngx_rtmp_publish_pt next_publish;
|
static ngx_rtmp_publish_pt next_publish;
|
||||||
|
@ -13,12 +13,6 @@ static ngx_rtmp_play_pt next_play;
|
||||||
static ngx_rtmp_delete_stream_pt next_delete_stream;
|
static ngx_rtmp_delete_stream_pt next_delete_stream;
|
||||||
|
|
||||||
|
|
||||||
/* Chunk stream ids for output */
|
|
||||||
#define NGX_RTMP_LIVE_CSID_AUDIO 6
|
|
||||||
#define NGX_RTMP_LIVE_CSID_VIDEO 7
|
|
||||||
#define NGX_RTMP_LIVE_MSID 1
|
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t ngx_rtmp_live_postconfiguration(ngx_conf_t *cf);
|
static ngx_int_t ngx_rtmp_live_postconfiguration(ngx_conf_t *cf);
|
||||||
static void * ngx_rtmp_live_create_app_conf(ngx_conf_t *cf);
|
static void * ngx_rtmp_live_create_app_conf(ngx_conf_t *cf);
|
||||||
static char * ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf,
|
static char * ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf,
|
||||||
|
@ -268,10 +262,6 @@ ngx_rtmp_live_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"live: delete empty stream '%s'", ctx->stream->name);
|
"live: delete empty stream '%s'", ctx->stream->name);
|
||||||
|
|
||||||
if (ctx->stream->avc_header) {
|
|
||||||
ngx_rtmp_free_shared_chain(cscf, ctx->stream->avc_header);
|
|
||||||
}
|
|
||||||
|
|
||||||
stream = ngx_rtmp_live_get_stream(s, ctx->stream->name, 0);
|
stream = ngx_rtmp_live_get_stream(s, ctx->stream->name, 0);
|
||||||
if (stream == NULL) {
|
if (stream == NULL) {
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
|
@ -292,14 +282,14 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
ngx_chain_t *in)
|
ngx_chain_t *in)
|
||||||
{
|
{
|
||||||
ngx_rtmp_live_ctx_t *ctx, *pctx;
|
ngx_rtmp_live_ctx_t *ctx, *pctx;
|
||||||
ngx_chain_t *out, *out_abs;
|
ngx_rtmp_codec_ctx_t *codec_ctx;
|
||||||
|
ngx_chain_t *out, *peer_out;
|
||||||
ngx_rtmp_core_srv_conf_t *cscf;
|
ngx_rtmp_core_srv_conf_t *cscf;
|
||||||
ngx_rtmp_live_app_conf_t *lacf;
|
ngx_rtmp_live_app_conf_t *lacf;
|
||||||
ngx_rtmp_session_t *ss;
|
ngx_rtmp_session_t *ss;
|
||||||
ngx_rtmp_header_t ch, lh;
|
ngx_rtmp_header_t ch, lh;
|
||||||
ngx_uint_t prio, peer_prio;
|
ngx_uint_t prio, peer_prio;
|
||||||
ngx_uint_t peers, dropped_peers;
|
ngx_uint_t peers, dropped_peers;
|
||||||
uint8_t flv_fmt;
|
|
||||||
|
|
||||||
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
|
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
|
||||||
if (lacf == NULL) {
|
if (lacf == NULL) {
|
||||||
|
@ -358,28 +348,6 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
peers = 0;
|
peers = 0;
|
||||||
dropped_peers = 0;
|
dropped_peers = 0;
|
||||||
|
|
||||||
/* parse meta */
|
|
||||||
if (in->buf->last - in->buf->pos >= 1) {
|
|
||||||
flv_fmt = *in->buf->pos;
|
|
||||||
if (h->type == NGX_RTMP_MSG_AUDIO) {
|
|
||||||
ctx->stream->meta.audio_codec_id = (flv_fmt & 0xf0) >> 4;
|
|
||||||
} else {
|
|
||||||
ctx->stream->meta.video_codec_id = (flv_fmt & 0x0f);
|
|
||||||
if (ctx->stream->meta.video_codec_id == 7
|
|
||||||
&& in->buf->last - in->buf->pos >= 2
|
|
||||||
&& in->buf->pos[1] == 0)
|
|
||||||
{
|
|
||||||
/* AVC/H264 sequence header;
|
|
||||||
* save it for future */
|
|
||||||
if (ctx->stream->avc_header) {
|
|
||||||
ngx_rtmp_free_shared_chain(cscf, ctx->stream->avc_header);
|
|
||||||
}
|
|
||||||
ctx->stream->avc_header = out;
|
|
||||||
ngx_rtmp_acquire_shared_chain(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* broadcast to all subscribers */
|
/* broadcast to all subscribers */
|
||||||
for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) {
|
for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) {
|
||||||
if (pctx == ctx) {
|
if (pctx == ctx) {
|
||||||
|
@ -395,27 +363,38 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
"live: av: abs %s timestamp=%uD",
|
"live: av: abs %s timestamp=%uD",
|
||||||
h->type == NGX_RTMP_MSG_VIDEO ? "video" : "audio",
|
h->type == NGX_RTMP_MSG_VIDEO ? "video" : "audio",
|
||||||
ch.timestamp);
|
ch.timestamp);
|
||||||
out_abs = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
peer_out = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||||
ngx_rtmp_prepare_message(s, &ch, NULL, out_abs);
|
ngx_rtmp_prepare_message(s, &ch, NULL, peer_out);
|
||||||
pctx->msg_mask |= (1 << h->type);
|
pctx->msg_mask |= (1 << h->type);
|
||||||
ngx_rtmp_send_message(ss, out_abs, prio);
|
ngx_rtmp_send_message(ss, peer_out, prio);
|
||||||
ngx_rtmp_free_shared_chain(cscf, out_abs);
|
ngx_rtmp_free_shared_chain(cscf, peer_out);
|
||||||
|
|
||||||
/* send AVC/H264 header */
|
/* send AVC/H264 header */
|
||||||
if (ctx->stream->avc_header) {
|
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||||
/*TODO: make it absolute & send instead of ythe above frame */
|
if (codec_ctx) {
|
||||||
ngx_rtmp_send_message(ss, ctx->stream->avc_header, prio);
|
peer_out = NULL;
|
||||||
|
if (h->type == NGX_RTMP_MSG_AUDIO) {
|
||||||
|
if (codec_ctx->aac_pheader) {
|
||||||
|
peer_out = codec_ctx->aac_pheader;
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log,
|
||||||
|
0, "live: sending AAC header");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (codec_ctx->avc_pheader) {
|
||||||
|
peer_out = codec_ctx->avc_pheader;
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log,
|
||||||
|
0, "live: sending AVC/H264 header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (peer_out) {
|
||||||
|
ngx_rtmp_send_message(ss, peer_out, prio);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* push buffered data */
|
/* push buffered data */
|
||||||
peer_prio = prio;
|
peer_prio = prio;
|
||||||
/*
|
|
||||||
if (lacf->buflen && h->timestamp >= pctx->next_push) {
|
|
||||||
peer_prio = 0;
|
|
||||||
pctx->next_push = h->timestamp + lacf->buflen;
|
|
||||||
}*/
|
|
||||||
if (ngx_rtmp_send_message(ss, out, peer_prio) != NGX_OK) {
|
if (ngx_rtmp_send_message(ss, out, peer_prio) != NGX_OK) {
|
||||||
++pctx->dropped;
|
++pctx->dropped;
|
||||||
++dropped_peers;
|
++dropped_peers;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "ngx_rtmp.h"
|
#include "ngx_rtmp.h"
|
||||||
|
#include "ngx_rtmp_cmd_module.h"
|
||||||
#include "ngx_rtmp_bandwidth.h"
|
#include "ngx_rtmp_bandwidth.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +16,12 @@
|
||||||
#define NGX_RTMP_LIVE_PUBLISHING 0x01
|
#define NGX_RTMP_LIVE_PUBLISHING 0x01
|
||||||
|
|
||||||
|
|
||||||
|
/* Chunk stream ids for output */
|
||||||
|
#define NGX_RTMP_LIVE_CSID_AUDIO 6
|
||||||
|
#define NGX_RTMP_LIVE_CSID_VIDEO 7
|
||||||
|
#define NGX_RTMP_LIVE_MSID 1
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ngx_uint_t width;
|
ngx_uint_t width;
|
||||||
ngx_uint_t height;
|
ngx_uint_t height;
|
||||||
|
@ -46,7 +53,7 @@ struct ngx_rtmp_live_ctx_s {
|
||||||
|
|
||||||
|
|
||||||
struct ngx_rtmp_live_stream_s {
|
struct ngx_rtmp_live_stream_s {
|
||||||
u_char name[256];
|
u_char name[NGX_RTMP_MAX_NAME];
|
||||||
ngx_rtmp_live_stream_t *next;
|
ngx_rtmp_live_stream_t *next;
|
||||||
ngx_rtmp_live_ctx_t *ctx;
|
ngx_rtmp_live_ctx_t *ctx;
|
||||||
ngx_uint_t flags;
|
ngx_uint_t flags;
|
||||||
|
@ -54,7 +61,6 @@ struct ngx_rtmp_live_stream_s {
|
||||||
ngx_rtmp_bandwidth_t bw_out;
|
ngx_rtmp_bandwidth_t bw_out;
|
||||||
ngx_rtmp_live_meta_t meta;
|
ngx_rtmp_live_meta_t meta;
|
||||||
ngx_msec_t epoch;
|
ngx_msec_t epoch;
|
||||||
ngx_chain_t *avc_header;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "ngx_rtmp.h"
|
#include "ngx_rtmp.h"
|
||||||
#include "ngx_rtmp_cmd_module.h"
|
#include "ngx_rtmp_cmd_module.h"
|
||||||
#include "ngx_rtmp_netcall_module.h"
|
#include "ngx_rtmp_netcall_module.h"
|
||||||
|
#include "ngx_rtmp_codec_module.h"
|
||||||
|
|
||||||
|
|
||||||
static ngx_rtmp_publish_pt next_publish;
|
static ngx_rtmp_publish_pt next_publish;
|
||||||
|
@ -23,6 +24,8 @@ 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 void * ngx_rtmp_record_create_app_conf(ngx_conf_t *cf);
|
||||||
static char * ngx_rtmp_record_merge_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);
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -231,17 +234,12 @@ ngx_rtmp_record_open(ngx_rtmp_session_t *s)
|
||||||
"record: failed to open file" " \"%s\" failed",
|
"record: failed to open file" " \"%s\" failed",
|
||||||
ctx->path);
|
ctx->path);
|
||||||
}
|
}
|
||||||
return NGX_ERROR;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"record: opened '%s'", ctx->path);
|
"record: opened '%s'", ctx->path);
|
||||||
|
|
||||||
if (ngx_rtmp_record_write_header(&ctx->file) != NGX_OK) {
|
|
||||||
ngx_rtmp_record_close(s);
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,66 +416,20 @@ ngx_rtmp_record_delete_stream(ngx_rtmp_session_t *s,
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
ngx_chain_t *in)
|
ngx_chain_t *in)
|
||||||
{
|
{
|
||||||
ngx_rtmp_record_ctx_t *ctx;
|
|
||||||
ngx_rtmp_record_app_conf_t *racf;
|
|
||||||
u_char hdr[11], *p, *ph;
|
u_char hdr[11], *p, *ph;
|
||||||
uint32_t timestamp, tag_size;
|
uint32_t timestamp, tag_size;
|
||||||
ngx_time_t next;
|
ngx_rtmp_record_ctx_t *ctx;
|
||||||
|
ngx_rtmp_record_app_conf_t *racf;
|
||||||
|
|
||||||
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
|
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
|
||||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
|
||||||
|
|
||||||
if (racf == NULL || ctx == NULL || racf->flags & NGX_RTMP_RECORD_OFF) {
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->file.fd == NGX_INVALID_FILE && racf->interval
|
|
||||||
&& (ctx->last.sec || ctx->last.msec))
|
|
||||||
{
|
|
||||||
next = ctx->last;
|
|
||||||
next.msec += racf->interval;
|
|
||||||
next.sec += (next.msec / 1000);
|
|
||||||
next.msec %= 1000;
|
|
||||||
if (ngx_cached_time->sec > next.sec
|
|
||||||
|| (ngx_cached_time->sec == next.sec
|
|
||||||
&& ngx_cached_time->msec > next.msec))
|
|
||||||
{
|
|
||||||
if (ngx_rtmp_record_open(s) != NGX_OK) {
|
|
||||||
ngx_log_error(NGX_LOG_CRIT, s->connection->log, 0,
|
|
||||||
"record: '%s' failed", ctx->path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->file.fd == NGX_INVALID_FILE) {
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* filter frames */
|
|
||||||
if (h->type == NGX_RTMP_MSG_AUDIO &&
|
|
||||||
(racf->flags & NGX_RTMP_RECORD_AUDIO) == 0)
|
|
||||||
{
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (h->type == NGX_RTMP_MSG_VIDEO &&
|
|
||||||
(racf->flags & NGX_RTMP_RECORD_VIDEO) == 0 &&
|
|
||||||
((racf->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0
|
|
||||||
|| ngx_rtmp_get_video_frame_type(in) != NGX_RTMP_VIDEO_KEY_FRAME))
|
|
||||||
{
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"record: av: mlen=%uD", h->mlen);
|
"record: av: mlen=%uD", h->mlen);
|
||||||
|
|
||||||
if (ctx->file.offset == 0) {
|
|
||||||
ctx->epoch = h->timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = h->timestamp - ctx->epoch;
|
timestamp = h->timestamp - ctx->epoch;
|
||||||
|
|
||||||
/* write tag header */
|
/* write tag header */
|
||||||
|
@ -554,6 +506,118 @@ ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
ngx_rtmp_record_get_chain_mlen(ngx_chain_t *in)
|
||||||
|
{
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
for (ret = 0; in; in = in->next) {
|
||||||
|
ret += (in->buf->last - in->buf->pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_rtmp_record_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
|
ngx_chain_t *in)
|
||||||
|
{
|
||||||
|
ngx_rtmp_record_ctx_t *ctx;
|
||||||
|
ngx_rtmp_record_app_conf_t *racf;
|
||||||
|
ngx_time_t next;
|
||||||
|
ngx_rtmp_header_t ch;
|
||||||
|
ngx_rtmp_codec_ctx_t *codec_ctx;
|
||||||
|
|
||||||
|
racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module);
|
||||||
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module);
|
||||||
|
|
||||||
|
if (racf == NULL || ctx == NULL || racf->flags & NGX_RTMP_RECORD_OFF) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->file.fd == NGX_INVALID_FILE && racf->interval
|
||||||
|
&& (ctx->last.sec || ctx->last.msec))
|
||||||
|
{
|
||||||
|
next = ctx->last;
|
||||||
|
next.msec += racf->interval;
|
||||||
|
next.sec += (next.msec / 1000);
|
||||||
|
next.msec %= 1000;
|
||||||
|
if (ngx_cached_time->sec > next.sec
|
||||||
|
|| (ngx_cached_time->sec == next.sec
|
||||||
|
&& ngx_cached_time->msec > next.msec))
|
||||||
|
{
|
||||||
|
if (ngx_rtmp_record_open(s) != NGX_OK) {
|
||||||
|
ngx_log_error(NGX_LOG_CRIT, s->connection->log, 0,
|
||||||
|
"record: '%s' failed", ctx->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->file.fd == NGX_INVALID_FILE) {
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* filter frames */
|
||||||
|
if (h->type == NGX_RTMP_MSG_AUDIO &&
|
||||||
|
(racf->flags & NGX_RTMP_RECORD_AUDIO) == 0)
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h->type == NGX_RTMP_MSG_VIDEO &&
|
||||||
|
(racf->flags & NGX_RTMP_RECORD_VIDEO) == 0 &&
|
||||||
|
((racf->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0
|
||||||
|
|| ngx_rtmp_get_video_frame_type(in) != NGX_RTMP_VIDEO_KEY_FRAME))
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->file.offset == 0) {
|
||||||
|
ctx->epoch = h->timestamp;
|
||||||
|
|
||||||
|
if (ngx_rtmp_record_write_header(&ctx->file) != NGX_OK) {
|
||||||
|
ngx_rtmp_record_close(s);
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||||
|
if (codec_ctx) {
|
||||||
|
ch = *h;
|
||||||
|
|
||||||
|
if (codec_ctx->aac_header && (racf->flags & NGX_RTMP_RECORD_AUDIO))
|
||||||
|
{
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"record: writing AAC header");
|
||||||
|
ch.type = NGX_RTMP_MSG_AUDIO;
|
||||||
|
ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->aac_header);
|
||||||
|
if (ngx_rtmp_record_write_frame(s, &ch, codec_ctx->aac_header)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codec_ctx->avc_header && (racf->flags
|
||||||
|
& (NGX_RTMP_RECORD_VIDEO|NGX_RTMP_RECORD_KEYFRAMES)))
|
||||||
|
{
|
||||||
|
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"record: writing AVC header");
|
||||||
|
ch.type = NGX_RTMP_MSG_VIDEO;
|
||||||
|
ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->avc_header);
|
||||||
|
if (ngx_rtmp_record_write_frame(s, &ch, codec_ctx->avc_header)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ngx_rtmp_record_write_frame(s, h, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
ngx_rtmp_notify_on_record_done(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
ngx_rtmp_notify_on_record_done(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include "ngx_rtmp.h"
|
#include "ngx_rtmp.h"
|
||||||
#include "ngx_rtmp_live_module.h"
|
#include "ngx_rtmp_live_module.h"
|
||||||
#include "ngx_rtmp_codecs.h"
|
#include "ngx_rtmp_codec_module.h"
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf);
|
static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf);
|
||||||
|
|
Loading…
Reference in a new issue