450 lines
11 KiB
C
450 lines
11 KiB
C
/*
|
|
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
|
|
*/
|
|
|
|
|
|
#include "ngx_live.h"
|
|
|
|
|
|
static void *ngx_live_create_conf(ngx_cycle_t *cf);
|
|
static char *ngx_live_init_conf(ngx_cycle_t *cycle, void *conf);
|
|
|
|
|
|
static ngx_command_t ngx_live_commands[] = {
|
|
|
|
{ ngx_string("stream_buckets"),
|
|
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
|
|
ngx_conf_set_size_slot,
|
|
0,
|
|
offsetof(ngx_live_conf_t, stream_buckets),
|
|
NULL },
|
|
|
|
{ ngx_string("server_buckets"),
|
|
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
|
|
ngx_conf_set_size_slot,
|
|
0,
|
|
offsetof(ngx_live_conf_t, server_buckets),
|
|
NULL },
|
|
|
|
ngx_null_command
|
|
};
|
|
|
|
|
|
static ngx_core_module_t ngx_live_module_ctx = {
|
|
ngx_string("live"),
|
|
ngx_live_create_conf, /* create conf */
|
|
ngx_live_init_conf /* init conf */
|
|
};
|
|
|
|
|
|
ngx_module_t ngx_live_module = {
|
|
NGX_MODULE_V1,
|
|
&ngx_live_module_ctx, /* module context */
|
|
ngx_live_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_live_create_conf(ngx_cycle_t *cycle)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
|
|
lcf = ngx_pcalloc(cycle->pool, sizeof(ngx_live_conf_t));
|
|
if (lcf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
lcf->stream_buckets = NGX_CONF_UNSET_SIZE;
|
|
lcf->server_buckets = NGX_CONF_UNSET_SIZE;
|
|
|
|
return lcf;
|
|
}
|
|
|
|
static char *
|
|
ngx_live_init_conf(ngx_cycle_t *cycle, void *conf)
|
|
{
|
|
ngx_live_conf_t *lcf = conf;
|
|
|
|
lcf->pool = ngx_create_pool(4096, cycle->log);
|
|
if (lcf->pool == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
ngx_conf_init_size_value(lcf->stream_buckets, 10007);
|
|
ngx_conf_init_size_value(lcf->server_buckets, 1031);
|
|
|
|
lcf->servers = ngx_pcalloc(lcf->pool,
|
|
sizeof(ngx_live_server_t *) * lcf->server_buckets);
|
|
if (lcf->servers == NULL) {
|
|
return NGX_CONF_ERROR;
|
|
}
|
|
|
|
return NGX_CONF_OK;
|
|
}
|
|
|
|
|
|
static ngx_live_server_t **
|
|
ngx_live_find_server(ngx_str_t *serverid)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
ngx_live_server_t **psrv;
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
psrv = &lcf->servers[ngx_hash_key(serverid->data, serverid->len)
|
|
% lcf->server_buckets];
|
|
for (; *psrv; psrv = &(*psrv)->next) {
|
|
if (ngx_strlen((*psrv)->serverid) == serverid->len &&
|
|
ngx_memcmp((*psrv)->serverid, serverid->data, serverid->len) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return psrv;
|
|
}
|
|
|
|
static ngx_live_server_t *
|
|
ngx_live_get_server(ngx_str_t *serverid)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
ngx_live_server_t *srv;
|
|
|
|
if (serverid->len > NGX_LIVE_SERVERID_LEN - 1) {
|
|
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
|
"serverid too long: %ui", serverid->len);
|
|
return NULL;
|
|
}
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
srv = lcf->free_server;
|
|
if (srv == NULL) {
|
|
srv = ngx_pcalloc(lcf->pool, sizeof(ngx_live_server_t));
|
|
if (srv == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
srv->streams = ngx_pcalloc(lcf->pool,
|
|
sizeof(ngx_live_stream_t *) * lcf->stream_buckets);
|
|
if (srv->streams == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
++lcf->alloc_server_count;
|
|
} else {
|
|
lcf->free_server = srv->next;
|
|
--lcf->free_server_count;
|
|
}
|
|
|
|
*ngx_cpymem(srv->serverid, serverid->data, serverid->len) = 0;
|
|
srv->deleted = 0;
|
|
srv->n_stream = 0;
|
|
|
|
return srv;
|
|
}
|
|
|
|
static void
|
|
ngx_live_put_server(ngx_live_server_t *server)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
server->next = lcf->free_server;
|
|
lcf->free_server = server;
|
|
++lcf->free_server_count;
|
|
}
|
|
|
|
static ngx_live_stream_t **
|
|
ngx_live_find_stream(ngx_live_server_t *server, ngx_str_t *stream)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
ngx_live_stream_t **pst;
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
pst = &server->streams[ngx_hash_key(stream->data, stream->len)
|
|
% lcf->stream_buckets];
|
|
for (; *pst; pst = &(*pst)->next) {
|
|
if (ngx_strlen((*pst)->name) == stream->len &&
|
|
ngx_memcmp((*pst)->name, stream->data, stream->len) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pst;
|
|
}
|
|
|
|
static ngx_live_stream_t *
|
|
ngx_live_get_stream(ngx_str_t *stream)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
ngx_live_stream_t *st;
|
|
|
|
if (stream->len > NGX_LIVE_STREAM_LEN - 1) {
|
|
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
|
"stream too long: %ui", stream->len);
|
|
return NULL;
|
|
}
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
st = lcf->free_stream;
|
|
if (st == NULL) {
|
|
st = ngx_pcalloc(lcf->pool, sizeof(ngx_live_stream_t));
|
|
++lcf->alloc_stream_count;
|
|
} else {
|
|
lcf->free_stream = st->next;
|
|
--lcf->free_stream_count;
|
|
ngx_memzero(st, sizeof(ngx_live_stream_t));
|
|
}
|
|
|
|
*ngx_cpymem(st->name, stream->data, stream->len) = 0;
|
|
st->pslot = -1;
|
|
st->epoch = ngx_current_msec;
|
|
ngx_map_init(&st->pubctx, ngx_map_hash_int, ngx_cmp_int);
|
|
|
|
return st;
|
|
}
|
|
|
|
static void
|
|
ngx_live_put_stream(ngx_live_stream_t *st)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
st->next = lcf->free_stream;
|
|
lcf->free_stream = st;
|
|
++lcf->free_stream_count;
|
|
}
|
|
|
|
ngx_live_server_t *
|
|
ngx_live_create_server(ngx_str_t *serverid)
|
|
{
|
|
ngx_live_server_t **psrv;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
if (*psrv) {
|
|
(*psrv)->deleted = 0;
|
|
return *psrv;
|
|
}
|
|
|
|
*psrv = ngx_live_get_server(serverid);
|
|
|
|
return *psrv;
|
|
}
|
|
|
|
ngx_live_server_t *
|
|
ngx_live_fetch_server(ngx_str_t *serverid)
|
|
{
|
|
ngx_live_server_t **psrv;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
|
|
return *psrv;
|
|
}
|
|
|
|
void
|
|
ngx_live_delete_server(ngx_str_t *serverid)
|
|
{
|
|
ngx_live_server_t **psrv, *srv;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
if (*psrv == NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((*psrv)->n_stream != 0) {
|
|
(*psrv)->deleted = 1;
|
|
}
|
|
|
|
if ((*psrv)->n_stream == 0) {
|
|
srv = *psrv;
|
|
*psrv = srv->next;
|
|
ngx_live_put_server(srv);
|
|
}
|
|
}
|
|
|
|
ngx_live_stream_t *
|
|
ngx_live_create_stream(ngx_str_t *serverid, ngx_str_t *stream)
|
|
{
|
|
ngx_live_server_t **psrv;
|
|
ngx_live_stream_t **pst;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
if (*psrv == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
|
"server %V does not exist when create stream", serverid);
|
|
return NULL;
|
|
}
|
|
|
|
pst = ngx_live_find_stream(*psrv, stream);
|
|
|
|
if (*pst) {
|
|
return *pst;
|
|
}
|
|
|
|
*pst = ngx_live_get_stream(stream);
|
|
++(*psrv)->n_stream;
|
|
|
|
return *pst;
|
|
}
|
|
|
|
ngx_live_stream_t *
|
|
ngx_live_fetch_stream(ngx_str_t *serverid, ngx_str_t *stream)
|
|
{
|
|
ngx_live_server_t **psrv;
|
|
ngx_live_stream_t **pst;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
if (*psrv == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
|
"server %V does not exist when fetch stream", serverid);
|
|
return NULL;
|
|
}
|
|
|
|
pst = ngx_live_find_stream(*psrv, stream);
|
|
|
|
return *pst;
|
|
}
|
|
|
|
void
|
|
ngx_live_delete_stream(ngx_str_t *serverid, ngx_str_t *stream)
|
|
{
|
|
ngx_live_server_t **psrv;
|
|
ngx_live_stream_t **pst, *st;
|
|
|
|
psrv = ngx_live_find_server(serverid);
|
|
if (*psrv == NULL) {
|
|
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
|
"server %V does not exist when delete stream", serverid);
|
|
return;
|
|
}
|
|
|
|
pst = ngx_live_find_stream(*psrv, stream);
|
|
|
|
if (*pst == NULL) {
|
|
return;
|
|
}
|
|
|
|
st = *pst;
|
|
|
|
*pst = st->next;
|
|
ngx_live_put_stream(st);
|
|
--(*psrv)->n_stream;
|
|
|
|
if ((*psrv)->deleted && (*psrv)->n_stream == 0) {
|
|
ngx_live_delete_server(serverid);
|
|
}
|
|
}
|
|
|
|
void
|
|
ngx_live_create_ctx(ngx_rtmp_session_t *s, unsigned publishing)
|
|
{
|
|
ngx_rtmp_core_ctx_t *ctx, **pctx;
|
|
|
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_core_module);
|
|
if (ctx == NULL) {
|
|
ctx = ngx_pcalloc(s->pool, sizeof(ngx_rtmp_core_ctx_t));
|
|
if (ctx == NULL) {
|
|
return;
|
|
}
|
|
|
|
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_core_module);
|
|
}
|
|
|
|
ctx->publishing = publishing;
|
|
ctx->session = s;
|
|
if (publishing) {
|
|
pctx = &s->live_stream->publish_ctx;
|
|
} else {
|
|
pctx = &s->live_stream->play_ctx;
|
|
}
|
|
|
|
ctx->next = (*pctx);
|
|
*pctx = ctx;
|
|
}
|
|
|
|
void
|
|
ngx_live_delete_ctx(ngx_rtmp_session_t *s)
|
|
{
|
|
ngx_rtmp_core_ctx_t *ctx, **pctx;
|
|
|
|
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_core_module);
|
|
if (ctx == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (ctx->publishing) {
|
|
pctx = &s->live_stream->publish_ctx;
|
|
} else {
|
|
pctx = &s->live_stream->play_ctx;
|
|
}
|
|
|
|
for (/* void */; *pctx; pctx = &(*pctx)->next) {
|
|
if (*pctx == ctx) {
|
|
*pctx = ctx->next;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ngx_chain_t *
|
|
ngx_live_state(ngx_http_request_t *r)
|
|
{
|
|
ngx_live_conf_t *lcf;
|
|
ngx_chain_t *cl;
|
|
ngx_buf_t *b;
|
|
size_t len;
|
|
|
|
lcf = (ngx_live_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
|
|
ngx_live_module);
|
|
|
|
|
|
len = sizeof("##########ngx live state##########\n") - 1
|
|
+ sizeof("ngx_live nalloc server: \n") - 1 + NGX_OFF_T_LEN
|
|
+ sizeof("ngx_live nfree server: \n") - 1 + NGX_OFF_T_LEN
|
|
+ sizeof("ngx_live nalloc stream: \n") - 1 + NGX_OFF_T_LEN
|
|
+ sizeof("ngx_live nfree stream: \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,
|
|
"##########ngx live state##########\n"
|
|
"ngx_live nalloc server: %ui\nngx_live nfree server: %ui\n"
|
|
"ngx_live nalloc stream: %ui\nngx_live nfree stream: %ui\n",
|
|
lcf->alloc_server_count, lcf->free_server_count,
|
|
lcf->alloc_stream_count, lcf->free_stream_count);
|
|
|
|
return cl;
|
|
}
|