403 lines
10 KiB
C
403 lines
10 KiB
C
/*
|
|
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
|
|
*/
|
|
|
|
|
|
#include <ngx_config.h>
|
|
#include <ngx_core.h>
|
|
#include "ngx_rtmp.h"
|
|
#include "ngx_rbuf.h"
|
|
|
|
|
|
static void *ngx_rtmp_shared_create_conf(ngx_cycle_t *cycle);
|
|
static char *ngx_rtmp_shared_init_conf(ngx_cycle_t *cycle, void *conf);
|
|
|
|
|
|
/* 1316 == 188 * 7 RTP pack 7 MPEG-TS packets as a RTP package */
|
|
#define NGX_MPEGTS_BUF_SIZE 1316
|
|
|
|
typedef struct {
|
|
ngx_rtmp_frame_t *free_frame;
|
|
ngx_mpegts_frame_t *free_mpegts_frame;
|
|
ngx_pool_t *pool;
|
|
|
|
ngx_uint_t nalloc_frame;
|
|
ngx_uint_t nfree_frame;
|
|
} ngx_rtmp_shared_conf_t;
|
|
|
|
|
|
static ngx_command_t ngx_rtmp_shared_commands[] = {
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
static ngx_core_module_t ngx_rtmp_shared_module_ctx = {
|
|
ngx_string("rtmp_shared"),
|
|
ngx_rtmp_shared_create_conf, /* create conf */
|
|
ngx_rtmp_shared_init_conf /* init conf */
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_rtmp_shared_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_rtmp_shared_module_ctx, /* module context */
|
|
ngx_rtmp_shared_commands, /* module directives */
|
|
NGX_CORE_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_shared_create_conf(ngx_cycle_t *cycle)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
|
|
rscf = ngx_pcalloc(cycle->pool, sizeof(ngx_rtmp_shared_conf_t));
|
|
if (rscf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return rscf;
|
|
}
|
|
|
|
|
|
static char *
|
|
ngx_rtmp_shared_init_conf(ngx_cycle_t *cycle, void *conf)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf = conf;
|
|
|
|
rscf->pool = ngx_create_pool(4096, cycle->log);
|
|
if (rscf->pool == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_rtmp_prepare_merge_frame(ngx_rtmp_session_t *s)
|
|
{
|
|
ngx_rtmp_core_app_conf_t *cacf;
|
|
ngx_chain_t *ll, **ln;
|
|
ngx_uint_t n;
|
|
|
|
// merge frame not send completely
|
|
if (s->out_chain) {
|
|
return NGX_OK;
|
|
}
|
|
|
|
cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_core_module);
|
|
|
|
ngx_rtmp_free_merge_frame(s);
|
|
|
|
ln = &s->out_chain;
|
|
|
|
for (n = 0; n < cacf->merge_frame && s->out_pos != s->out_last; ++n) {
|
|
// save frame chain with rtmp chunk or flv tag
|
|
s->merge[n] = s->prepare_handler(s);
|
|
|
|
if (s->out_pos == s->out_last) {
|
|
break;
|
|
}
|
|
|
|
// normal link to chain all merge frames
|
|
for (ll = s->merge[n]; ll; ll = ll->next, ln = &(*ln)->next) {
|
|
*ln = ngx_alloc_chain_link(s->pool);
|
|
if (*ln == NULL) {
|
|
s->nframe = n;
|
|
return NGX_ERROR;
|
|
}
|
|
(*ln)->next = NULL;
|
|
|
|
(*ln)->buf = ll->buf;
|
|
}
|
|
|
|
// save frame prepare to send
|
|
if (s->live_type == NGX_MPEGTS_LIVE) {
|
|
s->prepare_mpegts_frame[n] = s->mpegts_out[s->out_pos];
|
|
} else {
|
|
s->prepare_frame[n] = s->out[s->out_pos];
|
|
}
|
|
|
|
++s->out_pos;
|
|
s->out_pos %= s->out_queue;
|
|
}
|
|
|
|
s->nframe = n;
|
|
|
|
return NGX_OK;
|
|
}
|
|
|
|
void
|
|
ngx_rtmp_free_merge_frame(ngx_rtmp_session_t *s)
|
|
{
|
|
ngx_uint_t n;
|
|
|
|
if (s->live_type == NGX_MPEGTS_LIVE) {
|
|
for (n = 0; n < s->nframe; ++n) {
|
|
ngx_put_chainbufs(s->merge[n]);
|
|
s->merge[n] = NULL;
|
|
|
|
ngx_rtmp_shared_free_mpegts_frame(s->prepare_mpegts_frame[n]);
|
|
s->prepare_mpegts_frame[n] = NULL;
|
|
}
|
|
} else {
|
|
for (n = 0; n < s->nframe; ++n) {
|
|
ngx_put_chainbufs(s->merge[n]);
|
|
s->merge[n] = NULL;
|
|
|
|
ngx_rtmp_shared_free_frame(s->prepare_frame[n]);
|
|
s->prepare_frame[n] = NULL;
|
|
}
|
|
}
|
|
|
|
s->nframe = 0;
|
|
}
|
|
|
|
void
|
|
ngx_rtmp_shared_append_chain(ngx_rtmp_frame_t *frame, size_t size,
|
|
ngx_chain_t *cl, ngx_flag_t mandatory)
|
|
{
|
|
ngx_chain_t **ll;
|
|
u_char *p;
|
|
size_t len;
|
|
|
|
for (ll = &frame->chain; (*ll) && (*ll)->next; ll = &(*ll)->next);
|
|
|
|
if (cl == NULL) {
|
|
if (mandatory) {
|
|
*ll = ngx_get_chainbuf(size, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
p = cl->buf->pos;
|
|
|
|
for (;;) {
|
|
if ((*ll) && (*ll)->buf->last == (*ll)->buf->end) {
|
|
ll = &(*ll)->next;
|
|
}
|
|
|
|
if (*ll == NULL) {
|
|
*ll = ngx_get_chainbuf(size, 1);
|
|
}
|
|
|
|
while ((*ll)->buf->end - (*ll)->buf->last >= cl->buf->last - p) {
|
|
len = cl->buf->last - p;
|
|
(*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
|
|
cl = cl->next;
|
|
if (cl == NULL) {
|
|
return;
|
|
}
|
|
p = cl->buf->pos;
|
|
}
|
|
|
|
len = (*ll)->buf->end - (*ll)->buf->last;
|
|
(*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
|
|
p += len;
|
|
}
|
|
}
|
|
|
|
ngx_rtmp_frame_t *
|
|
ngx_rtmp_shared_alloc_frame(size_t size, ngx_chain_t *cl, ngx_flag_t mandatory)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
ngx_rtmp_frame_t *frame;
|
|
|
|
rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_rtmp_shared_module);
|
|
|
|
frame = rscf->free_frame;
|
|
if (frame) {
|
|
rscf->free_frame = frame->next;
|
|
frame->chain = NULL;
|
|
--rscf->nfree_frame;
|
|
} else {
|
|
frame = ngx_pcalloc(rscf->pool, sizeof(ngx_rtmp_frame_t));
|
|
if (frame == NULL) {
|
|
return NULL;
|
|
}
|
|
++rscf->nalloc_frame;
|
|
}
|
|
|
|
frame->ref = 1;
|
|
frame->next = NULL;
|
|
|
|
ngx_rtmp_shared_append_chain(frame, size, cl, mandatory);
|
|
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
ngx_rtmp_shared_free_frame(ngx_rtmp_frame_t *frame)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
ngx_chain_t *cl;
|
|
|
|
rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_rtmp_shared_module);
|
|
|
|
if (frame == NULL || --frame->ref) {
|
|
return;
|
|
}
|
|
|
|
/* recycle chainbuf */
|
|
cl = frame->chain;
|
|
while (cl) {
|
|
frame->chain = cl->next;
|
|
ngx_put_chainbuf(cl);
|
|
cl = frame->chain;
|
|
}
|
|
|
|
/* recycle frame */
|
|
frame->next = rscf->free_frame;
|
|
rscf->free_frame = frame;
|
|
++rscf->nfree_frame;
|
|
}
|
|
|
|
void
|
|
ngx_mpegts_shared_append_chain(ngx_mpegts_frame_t *frame, ngx_chain_t *cl,
|
|
ngx_flag_t mandatory)
|
|
{
|
|
ngx_chain_t **ll;
|
|
u_char *p;
|
|
size_t len;
|
|
|
|
for (ll = &frame->chain; (*ll) && (*ll)->next; ll = &(*ll)->next);
|
|
|
|
if (cl == NULL) {
|
|
if (mandatory) {
|
|
*ll = ngx_get_chainbuf(NGX_MPEGTS_BUF_SIZE, 1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
p = cl->buf->pos;
|
|
|
|
for (;;) {
|
|
if ((*ll) && (*ll)->buf->last == (*ll)->buf->end) {
|
|
ll = &(*ll)->next;
|
|
}
|
|
|
|
if (*ll == NULL) {
|
|
*ll = ngx_get_chainbuf(NGX_MPEGTS_BUF_SIZE, 1);
|
|
}
|
|
|
|
while ((*ll)->buf->end - (*ll)->buf->last >= cl->buf->last - p) {
|
|
len = cl->buf->last - p;
|
|
(*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
|
|
cl = cl->next;
|
|
if (cl == NULL) {
|
|
return;
|
|
}
|
|
p = cl->buf->pos;
|
|
}
|
|
|
|
len = (*ll)->buf->end - (*ll)->buf->last;
|
|
(*ll)->buf->last = ngx_cpymem((*ll)->buf->last, p, len);
|
|
p += len;
|
|
}
|
|
}
|
|
|
|
ngx_mpegts_frame_t *
|
|
ngx_rtmp_shared_alloc_mpegts_frame(ngx_chain_t *cl, ngx_flag_t mandatory)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
ngx_mpegts_frame_t *frame;
|
|
|
|
rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_rtmp_shared_module);
|
|
|
|
frame = rscf->free_mpegts_frame;
|
|
if (frame) {
|
|
rscf->free_mpegts_frame = frame->next;
|
|
--rscf->nfree_frame;
|
|
} else {
|
|
frame = ngx_pcalloc(rscf->pool, sizeof(ngx_mpegts_frame_t));
|
|
if (frame == NULL) {
|
|
return NULL;
|
|
}
|
|
++rscf->nalloc_frame;
|
|
}
|
|
|
|
ngx_memset(frame, 0, sizeof(ngx_mpegts_frame_t));
|
|
frame->ref = 1;
|
|
frame->next = NULL;
|
|
|
|
ngx_mpegts_shared_append_chain(frame, cl, mandatory);
|
|
|
|
return frame;
|
|
}
|
|
|
|
void
|
|
ngx_rtmp_shared_free_mpegts_frame(ngx_mpegts_frame_t *frame)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
ngx_chain_t *cl;
|
|
|
|
rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_rtmp_shared_module);
|
|
|
|
if (frame == NULL || --frame->ref) {
|
|
return;
|
|
}
|
|
|
|
/* recycle chainbuf */
|
|
cl = frame->chain;
|
|
while (cl) {
|
|
frame->chain = cl->next;
|
|
ngx_put_chainbuf(cl);
|
|
cl = frame->chain;
|
|
}
|
|
|
|
/* recycle frame */
|
|
frame->next = rscf->free_mpegts_frame;
|
|
rscf->free_mpegts_frame = frame;
|
|
++rscf->nfree_frame;
|
|
}
|
|
|
|
ngx_chain_t *
|
|
ngx_rtmp_shared_state(ngx_http_request_t *r)
|
|
{
|
|
ngx_rtmp_shared_conf_t *rscf;
|
|
ngx_chain_t *cl;
|
|
ngx_buf_t *b;
|
|
size_t len;
|
|
|
|
rscf = (ngx_rtmp_shared_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_rtmp_shared_module);
|
|
|
|
len = sizeof("##########rtmp shared state##########\n") - 1
|
|
+ sizeof("ngx_rtmp_shared alloc frame: \n") - 1 + NGX_OFF_T_LEN
|
|
+ sizeof("ngx_rtmp_shared free frame: \n") - 1 + NGX_OFF_T_LEN;
|
|
|
|
cl = ngx_alloc_chain_link(r->pool);
|
|
if (cl == NULL) {
|
|
return NULL;
|
|
}
|
|
cl->next = NULL;
|
|
|
|
b = ngx_create_temp_buf(r->pool, len);
|
|
if (b == NULL) {
|
|
return NULL;
|
|
}
|
|
cl->buf = b;
|
|
|
|
b->last = ngx_snprintf(b->last, len,
|
|
"##########rtmp shared state##########\n"
|
|
"ngx_rtmp_shared alloc frame: %ui\n"
|
|
"ngx_rtmp_shared free frame: %ui\n",
|
|
rscf->nalloc_frame, rscf->nfree_frame);
|
|
|
|
return cl;
|
|
}
|