mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-05-19 09:41:07 +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_relay_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_relay_module.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_codec_module.c \
|
||||
"
|
||||
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] = NULL;
|
||||
if (execve((char *)ec->cmd.data, args, environ) == -1) {
|
||||
if (execv((char *)ec->cmd.data, args) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "ngx_rtmp_live_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;
|
||||
|
@ -13,12 +13,6 @@ static ngx_rtmp_play_pt next_play;
|
|||
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 void * ngx_rtmp_live_create_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,
|
||||
"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);
|
||||
if (stream == NULL) {
|
||||
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_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_live_app_conf_t *lacf;
|
||||
ngx_rtmp_session_t *ss;
|
||||
ngx_rtmp_header_t ch, lh;
|
||||
ngx_uint_t prio, peer_prio;
|
||||
ngx_uint_t peers, dropped_peers;
|
||||
uint8_t flv_fmt;
|
||||
|
||||
lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module);
|
||||
if (lacf == NULL) {
|
||||
|
@ -358,28 +348,6 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
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 */
|
||||
for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) {
|
||||
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",
|
||||
h->type == NGX_RTMP_MSG_VIDEO ? "video" : "audio",
|
||||
ch.timestamp);
|
||||
out_abs = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||
ngx_rtmp_prepare_message(s, &ch, NULL, out_abs);
|
||||
peer_out = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
|
||||
ngx_rtmp_prepare_message(s, &ch, NULL, peer_out);
|
||||
pctx->msg_mask |= (1 << h->type);
|
||||
ngx_rtmp_send_message(ss, out_abs, prio);
|
||||
ngx_rtmp_free_shared_chain(cscf, out_abs);
|
||||
ngx_rtmp_send_message(ss, peer_out, prio);
|
||||
ngx_rtmp_free_shared_chain(cscf, peer_out);
|
||||
|
||||
/* send AVC/H264 header */
|
||||
if (ctx->stream->avc_header) {
|
||||
/*TODO: make it absolute & send instead of ythe above frame */
|
||||
ngx_rtmp_send_message(ss, ctx->stream->avc_header, prio);
|
||||
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
||||
if (codec_ctx) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* push buffered data */
|
||||
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) {
|
||||
++pctx->dropped;
|
||||
++dropped_peers;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
|
||||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_bandwidth.h"
|
||||
|
||||
|
||||
|
@ -15,6 +16,12 @@
|
|||
#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 {
|
||||
ngx_uint_t width;
|
||||
ngx_uint_t height;
|
||||
|
@ -46,7 +53,7 @@ struct ngx_rtmp_live_ctx_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_ctx_t *ctx;
|
||||
ngx_uint_t flags;
|
||||
|
@ -54,7 +61,6 @@ struct ngx_rtmp_live_stream_s {
|
|||
ngx_rtmp_bandwidth_t bw_out;
|
||||
ngx_rtmp_live_meta_t meta;
|
||||
ngx_msec_t epoch;
|
||||
ngx_chain_t *avc_header;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "ngx_rtmp.h"
|
||||
#include "ngx_rtmp_cmd_module.h"
|
||||
#include "ngx_rtmp_netcall_module.h"
|
||||
#include "ngx_rtmp_codec_module.h"
|
||||
|
||||
|
||||
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 char * ngx_rtmp_record_merge_app_conf(ngx_conf_t *cf,
|
||||
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 {
|
||||
|
@ -231,17 +234,12 @@ ngx_rtmp_record_open(ngx_rtmp_session_t *s)
|
|||
"record: failed to open file" " \"%s\" failed",
|
||||
ctx->path);
|
||||
}
|
||||
return NGX_ERROR;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -418,66 +416,20 @@ ngx_rtmp_record_delete_stream(ngx_rtmp_session_t *s,
|
|||
|
||||
|
||||
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_rtmp_record_ctx_t *ctx;
|
||||
ngx_rtmp_record_app_conf_t *racf;
|
||||
u_char hdr[11], *p, *ph;
|
||||
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);
|
||||
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,
|
||||
"record: av: mlen=%uD", h->mlen);
|
||||
|
||||
if (ctx->file.offset == 0) {
|
||||
ctx->epoch = h->timestamp;
|
||||
}
|
||||
|
||||
timestamp = h->timestamp - ctx->epoch;
|
||||
|
||||
/* 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 *
|
||||
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_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);
|
||||
|
|
Loading…
Reference in a new issue