mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-06-23 14:28:58 +02:00
implemented hls playlist reader & custom fragment sizes
This commit is contained in:
parent
682d24d682
commit
814bcf4825
|
@ -10,7 +10,9 @@
|
|||
|
||||
|
||||
static ngx_rtmp_publish_pt next_publish;
|
||||
static ngx_rtmp_delete_stream_pt next_delete_stream;
|
||||
static ngx_rtmp_close_stream_pt next_close_stream;
|
||||
static ngx_rtmp_stream_begin_pt next_stream_begin;
|
||||
static ngx_rtmp_stream_eof_pt next_stream_eof;
|
||||
|
||||
|
||||
static ngx_int_t ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf);
|
||||
|
@ -25,7 +27,14 @@ static char * ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf,
|
|||
|
||||
|
||||
typedef struct {
|
||||
unsigned publishing:1;
|
||||
uint64_t id;
|
||||
double duration;
|
||||
unsigned active:1;
|
||||
unsigned discont:1; /* after */
|
||||
} ngx_rtmp_hls_frag_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned opened:1;
|
||||
|
||||
ngx_file_t file;
|
||||
|
@ -36,14 +45,15 @@ typedef struct {
|
|||
ngx_str_t name;
|
||||
|
||||
uint64_t frag;
|
||||
uint64_t frag_ts;
|
||||
ngx_uint_t nfrags;
|
||||
ngx_rtmp_hls_frag_t *frags; /* circular 2 * winfrags + 1 */
|
||||
|
||||
ngx_uint_t audio_cc;
|
||||
ngx_uint_t video_cc;
|
||||
|
||||
uint64_t aframe_base;
|
||||
uint64_t aframe_num;
|
||||
|
||||
uint64_t offset;
|
||||
} ngx_rtmp_hls_ctx_t;
|
||||
|
||||
|
||||
|
@ -162,9 +172,6 @@ ngx_module_t ngx_rtmp_hls_module = {
|
|||
};
|
||||
|
||||
|
||||
#define ngx_rtmp_hls_frag(hacf, f) (hacf->nfrags ? (f) % hacf->nfrags : (f))
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_chain2buffer(ngx_buf_t *out, ngx_chain_t *in, size_t skip)
|
||||
{
|
||||
|
@ -207,21 +214,53 @@ ngx_rtmp_hls_create_parent_dir(ngx_rtmp_session_t *s)
|
|||
}
|
||||
|
||||
|
||||
static ngx_rtmp_hls_frag_t *
|
||||
ngx_rtmp_hls_get_frag(ngx_rtmp_session_t *s, ngx_int_t n)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
return &ctx->frags[(ctx->frag + n) % (hacf->winfrags * 2 + 1)];
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_next_frag(ngx_rtmp_session_t *s)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
if (ctx->nfrags == hacf->winfrags) {
|
||||
ctx->frag++;
|
||||
} else {
|
||||
ctx->nfrags++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
|
||||
ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
|
||||
{
|
||||
static u_char buffer[1024];
|
||||
int fd;
|
||||
u_char *p;
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ssize_t n;
|
||||
uint64_t ffrag;
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_int_t nretry;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
ngx_uint_t i;
|
||||
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
nretry = 0;
|
||||
|
||||
retry:
|
||||
|
@ -244,14 +283,14 @@ retry:
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ffrag = ctx->frag > hacf->winfrags ?
|
||||
ctx->frag - (uint64_t) hacf->winfrags : 1;
|
||||
/*TODO set target duration big enough*/
|
||||
|
||||
p = ngx_snprintf(buffer, sizeof(buffer),
|
||||
"#EXTM3U\r\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:%uL\r\n"
|
||||
"#EXT-X-TARGETDURATION:%ui\r\n"
|
||||
"#EXT-X-ALLOW-CACHE:NO\r\n\r\n",
|
||||
"#EXTM3U\n"
|
||||
"#EXT-X-VERSION:3\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:%uL\n"
|
||||
"#EXT-X-TARGETDURATION:%ui\n"
|
||||
"#EXT-X-ALLOW-CACHE:NO\n\n",
|
||||
ctx->frag, (ngx_uint_t) (hacf->fraglen / 1000));
|
||||
|
||||
n = write(fd, buffer, p - buffer);
|
||||
|
@ -262,12 +301,20 @@ retry:
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
for (; ffrag < ctx->frag; ++ffrag) {
|
||||
for (i = 0; i < ctx->nfrags; ++i) {
|
||||
f = ngx_rtmp_hls_get_frag(s, i);
|
||||
|
||||
p = ngx_snprintf(buffer, sizeof(buffer),
|
||||
"#EXTINF:%i,\r\n"
|
||||
"%V-%uL.ts\r\n",
|
||||
(ngx_int_t) (hacf->fraglen / 1000), &ctx->name,
|
||||
ngx_rtmp_hls_frag(hacf, ffrag));
|
||||
"#EXTINF:%.3f,\n"
|
||||
"%V-%uL.ts\n"
|
||||
"%s",
|
||||
f->duration, &ctx->name, f->id,
|
||||
f->discont ? "#EXT-X-DISCONTINUITY\n" : "");
|
||||
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: fragment frag=%uL, n=%ui/%ui, duration=%.3f, "
|
||||
"discont=%i",
|
||||
ctx->frag, i + 1, ctx->nfrags, f->duration, f->discont);
|
||||
|
||||
n = write(fd, buffer, p - buffer);
|
||||
if (n < 0) {
|
||||
|
@ -469,48 +516,96 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_next_frag(ngx_rtmp_session_t *s)
|
||||
static uint64_t
|
||||
ngx_rtmp_hls_get_fragment_id(ngx_rtmp_session_t *s)
|
||||
{
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_int_t nretry;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
/*TODO: implement more methods*/
|
||||
|
||||
if (ctx == NULL || hacf == NULL) {
|
||||
return;
|
||||
/*#define ngx_rtmp_hls_frag(hacf, f) (hacf->nfrags ? (f) % hacf->nfrags : (f))*/
|
||||
|
||||
return ctx->frag + ctx->nfrags;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_delete_fragment(ngx_rtmp_session_t *s, ngx_uint_t n)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
f = ngx_rtmp_hls_get_frag(s, n);
|
||||
|
||||
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%uL.ts", f->id) = 0;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: delete fragment '%s'", ctx->stream.data);
|
||||
|
||||
ngx_delete_file(ctx->stream.data);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s, ngx_int_t discont)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
if (!ctx->opened) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->opened) {
|
||||
ngx_close_file(ctx->file.fd);
|
||||
ctx->opened = 0;
|
||||
}
|
||||
|
||||
if (hacf->nfrags == 0 && ctx->frag > 2 * hacf->winfrags &&
|
||||
!hacf->nodelete)
|
||||
{
|
||||
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%uL.ts",
|
||||
ngx_rtmp_hls_frag(hacf, ctx->frag - 2 * hacf->winfrags))
|
||||
= 0;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: delete frag '%s'", ctx->stream.data);
|
||||
|
||||
ngx_delete_file(ctx->stream.data);
|
||||
|
||||
}
|
||||
|
||||
ctx->frag++;
|
||||
|
||||
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%uL.ts",
|
||||
ngx_rtmp_hls_frag(hacf, ctx->frag)) = 0;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: open frag '%s', frag=%uL",
|
||||
ctx->stream.data, ctx->frag);
|
||||
"hls: close fragment n=%uL, discont=%i",
|
||||
ctx->frag, discont);
|
||||
|
||||
ngx_close_file(ctx->file.fd);
|
||||
|
||||
ctx->opened = 0;
|
||||
ctx->file.fd = NGX_INVALID_FILE;
|
||||
|
||||
ngx_rtmp_hls_next_frag(s);
|
||||
|
||||
ngx_rtmp_hls_write_playlist(s);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
|
||||
ngx_int_t discont)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
ngx_uint_t nretry;
|
||||
uint64_t id;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
if (ctx->opened) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->nfrags && discont) {
|
||||
f = ngx_rtmp_hls_get_frag(s, ctx->nfrags - 1);
|
||||
f->discont = 1;
|
||||
}
|
||||
|
||||
id = ngx_rtmp_hls_get_fragment_id(s);
|
||||
|
||||
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%uL.ts", id) = 0;
|
||||
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: open fragment file='%s', frag=%uL, n=%uL, time=%uL, "
|
||||
"discont=%i",
|
||||
ctx->stream.data, ctx->frag, ctx->nfrags, ts, discont);
|
||||
|
||||
ngx_memzero(&ctx->file, sizeof(ctx->file));
|
||||
|
||||
|
@ -536,33 +631,48 @@ retry:
|
|||
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"hls: error creating fragment file");
|
||||
return;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_rtmp_mpegts_write_header(&ctx->file) != NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||
"hls: error writing fragment header");
|
||||
ngx_close_file(ctx->file.fd);
|
||||
return;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->opened = 1;
|
||||
|
||||
ngx_rtmp_hls_update_playlist(s);
|
||||
f = ngx_rtmp_hls_get_frag(s, ctx->nfrags);
|
||||
|
||||
if (f->active) {
|
||||
ngx_rtmp_hls_delete_fragment(s, ctx->nfrags);
|
||||
}
|
||||
|
||||
ngx_memzero(f, sizeof(*f));
|
||||
|
||||
f->active = 1;
|
||||
f->id = id;
|
||||
|
||||
ctx->frag_ts = ts;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#define NGX_RTMP_HLS_RESTORE_PREFIX "#EXTM3U\r\n#EXT-X-MEDIA-SEQUENCE:"
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
ngx_file_t file;
|
||||
ssize_t ret;
|
||||
u_char buffer[sizeof(NGX_RTMP_HLS_RESTORE_PREFIX) -
|
||||
1 + NGX_INT64_LEN];
|
||||
size_t len;
|
||||
off_t offset;
|
||||
u_char *p, *last, *end, *next, *pa;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
double duration;
|
||||
ngx_uint_t mag;
|
||||
static u_char buffer[4096];
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
|
@ -578,31 +688,116 @@ ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = ngx_read_file(&file, buffer, sizeof(buffer), 0);
|
||||
if (ret <= 0) {
|
||||
goto done;
|
||||
offset = 0;
|
||||
ctx->nfrags = 0;
|
||||
f = NULL;
|
||||
duration = 0;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
ret = ngx_read_file(&file, buffer, sizeof(buffer), offset);
|
||||
if (ret <= 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
p = buffer;
|
||||
end = buffer + ret;
|
||||
|
||||
for ( ;; ) {
|
||||
last = ngx_strlchr(p, end, '\n');
|
||||
|
||||
if (last == NULL) {
|
||||
if (p == buffer) {
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
next = last + 1;
|
||||
offset += (next - p);
|
||||
|
||||
if (p != last && last[-1] == '\r') {
|
||||
last--;
|
||||
}
|
||||
|
||||
len = (size_t) (last - p);
|
||||
|
||||
|
||||
#define NGX_RTMP_MSEQ "#EXT-X-MEDIA-SEQUENCE:"
|
||||
#define NGX_RTMP_MSEQ_LEN (sizeof(NGX_RTMP_MSEQ) - 1)
|
||||
|
||||
|
||||
if (ngx_memcmp(p, NGX_RTMP_MSEQ, NGX_RTMP_MSEQ_LEN) == 0) {
|
||||
|
||||
ctx->frag = strtod((const char *) &p[NGX_RTMP_MSEQ_LEN], NULL);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: restore sequence frag=%uL", ctx->frag);
|
||||
}
|
||||
|
||||
|
||||
#define NGX_RTMP_EXTINF "#EXTINF:"
|
||||
#define NGX_RTMP_EXTINF_LEN (sizeof(NGX_RTMP_EXTINF) - 1)
|
||||
|
||||
|
||||
if (ngx_memcmp(p, NGX_RTMP_EXTINF, NGX_RTMP_EXTINF_LEN) == 0) {
|
||||
|
||||
duration = strtod((const char *) &p[NGX_RTMP_EXTINF_LEN], NULL);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: restore durarion=%.3f", duration);
|
||||
}
|
||||
|
||||
|
||||
#define NGX_RTMP_DISCONT "#EXT-X-DISCONTINUITY"
|
||||
#define NGX_RTMP_DISCONT_LEN (sizeof(NGX_RTMP_DISCONT) - 1)
|
||||
|
||||
|
||||
if (ngx_memcmp(p, NGX_RTMP_DISCONT, NGX_RTMP_DISCONT_LEN) == 0) {
|
||||
|
||||
if (f) {
|
||||
f->discont = 1;
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: discontinuity");
|
||||
}
|
||||
|
||||
/* find '.ts\r' */
|
||||
|
||||
if (p + 4 <= last &&
|
||||
last[-3] == '.' && last[-2] == 't' && last[-1] == 's')
|
||||
{
|
||||
f = ngx_rtmp_hls_get_frag(s, ctx->nfrags);
|
||||
|
||||
ngx_memzero(f, sizeof(*f));
|
||||
|
||||
f->duration = duration;
|
||||
f->active = 1;
|
||||
f->id = 0;
|
||||
|
||||
mag = 1;
|
||||
for (pa = last - 4; pa != p; pa--) {
|
||||
if (*pa < '0' || *pa > '9') {
|
||||
break;
|
||||
}
|
||||
f->id += (*pa - '0') * mag;
|
||||
mag *= 10;
|
||||
}
|
||||
|
||||
ngx_rtmp_hls_next_frag(s);
|
||||
|
||||
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: restore fragment '%*s' id=%uL, "
|
||||
"duration=%.3f, frag=%uL, nfrags=%ui",
|
||||
len, p, f->id, f->duration, ctx->frag,
|
||||
ctx->nfrags);
|
||||
}
|
||||
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
if ((size_t) ret < sizeof(NGX_RTMP_HLS_RESTORE_PREFIX)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(buffer, NGX_RTMP_HLS_RESTORE_PREFIX,
|
||||
sizeof(NGX_RTMP_HLS_RESTORE_PREFIX) - 1))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"hls: failed to restore stream");
|
||||
goto done;
|
||||
}
|
||||
|
||||
buffer[ret] = 0;
|
||||
|
||||
ctx->frag = strtod((const char *)
|
||||
&buffer[sizeof(NGX_RTMP_HLS_RESTORE_PREFIX) - 1], NULL);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: restored frag=%uL", ctx->frag);
|
||||
|
||||
done:
|
||||
ngx_close_file(file.fd);
|
||||
}
|
||||
|
@ -613,8 +808,8 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
|
|||
{
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
size_t len;
|
||||
u_char *p;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
if (hacf == NULL || !hacf->hls || hacf->path.len == 0) {
|
||||
|
@ -630,21 +825,37 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
|
|||
v->name, v->type);
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_hls_ctx_t));
|
||||
ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_hls_ctx_t));
|
||||
ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_hls_module);
|
||||
} else {
|
||||
f = ctx->frags;
|
||||
ngx_memzero(ctx, sizeof(ngx_rtmp_hls_ctx_t));
|
||||
ctx->frags = f;
|
||||
}
|
||||
|
||||
ngx_memzero(ctx, sizeof(ngx_rtmp_hls_ctx_t));
|
||||
if (ctx->frags == NULL) {
|
||||
ctx->frags = ngx_pcalloc(s->connection->pool,
|
||||
sizeof(ngx_rtmp_hls_frag_t) *
|
||||
(hacf->winfrags * 2 + 1));
|
||||
if (ctx->frags == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*TODO: escaping does not solve the problem*/
|
||||
if (ngx_strstr(v->name, "..")) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"hls: bad stream name: '%s'", v->name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
len = ngx_strlen(v->name);
|
||||
ctx->name.len = len + (ngx_uint_t) ngx_escape_uri(NULL, v->name, len,
|
||||
NGX_ESCAPE_URI_COMPONENT);
|
||||
ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len);
|
||||
|
||||
ngx_escape_uri(ctx->name.data, v->name, len, NGX_ESCAPE_URI_COMPONENT);
|
||||
ctx->name.len = ngx_strlen(v->name);
|
||||
ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len + 1);
|
||||
if (ctx->name.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
*ngx_cpymem(ctx->name.data, v->name, ctx->name.len) = 0;
|
||||
|
||||
ctx->playlist.data = ngx_palloc(s->connection->pool,
|
||||
hacf->path.len + 1 + ctx->name.len +
|
||||
|
@ -693,15 +904,13 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
|
|||
ngx_rtmp_hls_restore_stream(s);
|
||||
}
|
||||
|
||||
ctx->publishing = 1;
|
||||
|
||||
next:
|
||||
return next_publish(s, v);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
|
||||
ngx_rtmp_hls_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
|
||||
{
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
|
@ -710,22 +919,17 @@ ngx_rtmp_hls_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
|
|||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
if (hacf == NULL || !hacf->hls || ctx == NULL || !ctx->publishing) {
|
||||
if (hacf == NULL || !hacf->hls || ctx == NULL) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: delete");
|
||||
"hls: delete stream");
|
||||
|
||||
ctx->publishing = 0;
|
||||
|
||||
if (ctx->opened) {
|
||||
ngx_close_file(ctx->file.fd);
|
||||
ctx->opened = 0;
|
||||
}
|
||||
ngx_rtmp_hls_close_fragment(s, 1);
|
||||
|
||||
next:
|
||||
return next_delete_stream(s, v);
|
||||
return next_close_stream(s, v);
|
||||
}
|
||||
|
||||
|
||||
|
@ -762,8 +966,6 @@ ngx_rtmp_hls_parse_aac_header(ngx_rtmp_session_t *s, ngx_uint_t *objtype,
|
|||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
(*objtype)--;
|
||||
|
||||
*srindex = ((b0 << 1) & 0x0f) | ((b1 & 0x80) >> 7);
|
||||
if (*srindex == 0x0f) {
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
|
@ -782,29 +984,35 @@ ngx_rtmp_hls_parse_aac_header(ngx_rtmp_session_t *s, ngx_uint_t *objtype,
|
|||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_set_frag(ngx_rtmp_session_t *s, uint64_t ts)
|
||||
ngx_rtmp_hls_update_fragment(ngx_rtmp_session_t *s, uint64_t ts,
|
||||
ngx_int_t boundary)
|
||||
{
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
uint64_t frag;
|
||||
ngx_rtmp_hls_app_conf_t *hacf;
|
||||
ngx_int_t restart;
|
||||
ngx_rtmp_hls_frag_t *f;
|
||||
|
||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
frag = ts / hacf->fraglen / 90;
|
||||
if (ctx->opened) {
|
||||
f = ngx_rtmp_hls_get_frag(s, ctx->nfrags);
|
||||
f->duration = (ts - ctx->frag_ts) / 90000.;
|
||||
if (f->duration < hacf->fraglen / 1000.) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (frag == ctx->frag && ctx->opened) {
|
||||
if (!boundary) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (frag != ctx->frag + 1) {
|
||||
ctx->offset += (ctx->frag + 1) * (uint64_t) hacf->fraglen * 90 - ts;
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hls: time gap offset=%uL", ctx->offset);
|
||||
}
|
||||
restart = ctx->opened;
|
||||
|
||||
ngx_rtmp_hls_next_frag(s);
|
||||
ngx_rtmp_hls_close_fragment(s, 0);
|
||||
|
||||
ngx_rtmp_hls_open_fragment(s, ts, !restart);
|
||||
}
|
||||
|
||||
|
||||
|
@ -843,7 +1051,7 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
|
||||
ngx_memzero(&frame, sizeof(frame));
|
||||
|
||||
frame.dts = (uint64_t) h->timestamp * 90 + ctx->offset;
|
||||
frame.dts = (uint64_t) h->timestamp * 90;
|
||||
frame.cc = ctx->audio_cc;
|
||||
frame.pid = 0x101;
|
||||
frame.sid = 0xc0;
|
||||
|
@ -870,7 +1078,7 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
|
||||
p[0] = 0xff;
|
||||
p[1] = 0xf1;
|
||||
p[2] = (objtype << 6) | (srindex << 2) | (chconf & 0x04);
|
||||
p[2] = ((objtype - 1) << 6) | (srindex << 2) | ((chconf & 0x04) >> 2);
|
||||
p[3] = ((chconf & 0x03) << 6) | ((size >> 11) & 0x03);
|
||||
p[4] = (size >> 3);
|
||||
p[5] = (size << 5) | 0x1f;
|
||||
|
@ -912,9 +1120,7 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
/* Fragment is restarted in video handler.
|
||||
* However if video stream is missing then do it here */
|
||||
|
||||
if (codec_ctx->avc_header == NULL) {
|
||||
ngx_rtmp_hls_set_frag(s, frame.dts);
|
||||
}
|
||||
ngx_rtmp_hls_update_fragment(s, frame.dts, codec_ctx->avc_header == NULL);
|
||||
|
||||
if (!ctx->opened) {
|
||||
return NGX_OK;
|
||||
|
@ -1141,15 +1347,13 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
ngx_memzero(&frame, sizeof(frame));
|
||||
|
||||
frame.cc = ctx->video_cc;
|
||||
frame.dts = (uint64_t) h->timestamp * 90 + ctx->offset;
|
||||
frame.dts = (uint64_t) h->timestamp * 90;
|
||||
frame.pts = frame.dts + cts * 90;
|
||||
frame.pid = 0x100;
|
||||
frame.sid = 0xe0;
|
||||
frame.key = (ftype == 1);
|
||||
|
||||
if (frame.key) {
|
||||
ngx_rtmp_hls_set_frag(s, frame.dts);
|
||||
}
|
||||
ngx_rtmp_hls_update_fragment(s, frame.dts, frame.key);
|
||||
|
||||
if (!ctx->opened) {
|
||||
return NGX_OK;
|
||||
|
@ -1169,6 +1373,43 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_rtmp_hls_discontinue(ngx_rtmp_session_t *s)
|
||||
{
|
||||
ngx_rtmp_hls_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
|
||||
|
||||
if (ctx == NULL || !ctx->opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||
"hld: discontinue");
|
||||
|
||||
ngx_close_file(ctx->file.fd);
|
||||
ctx->opened = 0;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v)
|
||||
{
|
||||
ngx_rtmp_hls_discontinue(s);
|
||||
|
||||
return next_stream_begin(s, v);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_rtmp_hls_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v)
|
||||
{
|
||||
ngx_rtmp_hls_discontinue(s);
|
||||
|
||||
return next_stream_eof(s, v);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
|
||||
{
|
||||
|
@ -1234,8 +1475,14 @@ ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf)
|
|||
next_publish = ngx_rtmp_publish;
|
||||
ngx_rtmp_publish = ngx_rtmp_hls_publish;
|
||||
|
||||
next_delete_stream = ngx_rtmp_delete_stream;
|
||||
ngx_rtmp_delete_stream = ngx_rtmp_hls_delete_stream;
|
||||
next_close_stream = ngx_rtmp_close_stream;
|
||||
ngx_rtmp_close_stream = ngx_rtmp_hls_close_stream;
|
||||
|
||||
next_stream_begin = ngx_rtmp_stream_begin;
|
||||
ngx_rtmp_stream_begin = ngx_rtmp_hls_stream_begin;
|
||||
|
||||
next_stream_eof = ngx_rtmp_stream_eof;
|
||||
ngx_rtmp_stream_eof = ngx_rtmp_hls_stream_eof;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue