Skip to content

Commit b96bcfc

Browse files
committed
implemented round-robin hls
1 parent 8cc2a64 commit b96bcfc

File tree

1 file changed

+81
-91
lines changed

1 file changed

+81
-91
lines changed

hls/ngx_rtmp_hls_module.c

Lines changed: 81 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ static char * ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf,
2626

2727
typedef struct {
2828
ngx_uint_t flags;
29-
ngx_msec_t frag_start;
3029

3130
unsigned publishing:1;
3231
unsigned opened:1;
@@ -59,7 +58,9 @@ typedef struct {
5958
ngx_msec_t muxdelay;
6059
ngx_msec_t sync;
6160
ngx_msec_t playlen;
62-
size_t nfrags;
61+
ngx_int_t factor;
62+
ngx_uint_t winfrags;
63+
ngx_uint_t nfrags;
6364
ngx_flag_t continuous;
6465
ngx_rtmp_hls_ctx_t **ctx;
6566
ngx_uint_t nbuckets;
@@ -118,6 +119,13 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
118119
offsetof(ngx_rtmp_hls_app_conf_t, continuous),
119120
NULL },
120121

122+
{ ngx_string("hls_playlist_factor"),
123+
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
124+
ngx_conf_set_num_slot,
125+
NGX_RTMP_APP_CONF_OFFSET,
126+
offsetof(ngx_rtmp_hls_app_conf_t, factor),
127+
NULL },
128+
121129
ngx_null_command
122130
};
123131

@@ -195,8 +203,8 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
195203

196204
fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY,
197205
NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
198-
if (fd == NGX_INVALID_FILE) {
199206

207+
if (fd == NGX_INVALID_FILE) {
200208
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
201209
"hls: open failed: '%V'", &ctx->playlist_bak);
202210

@@ -215,19 +223,17 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
215223
return NGX_ERROR;
216224
}
217225

218-
ffrag = ctx->frag - hacf->nfrags;
226+
ffrag = ctx->frag - hacf->winfrags;
219227
if (ffrag < 1) {
220228
ffrag = 1;
221229
}
222230

223231
p = ngx_snprintf(buffer, sizeof(buffer),
224232
"#EXTM3U\r\n"
225-
"# %i %uL\r\n"
233+
"#EXT-X-MEDIA-SEQUENCE:%i\r\n"
226234
"#EXT-X-TARGETDURATION:%i\r\n"
227-
"#EXT-X-ALLOW-CACHE:NO\r\n"
228-
"#EXT-X-MEDIA-SEQUENCE:%i\r\n\r\n",
229-
ctx->frag, ctx->timestamp,
230-
(ngx_int_t) (hacf->fraglen / 1000), ffrag);
235+
"#EXT-X-ALLOW-CACHE:NO\r\n\r\n",
236+
ctx->frag, (ngx_int_t) (hacf->fraglen / 1000));
231237

232238
n = write(fd, buffer, p - buffer);
233239
if (n < 0) {
@@ -242,12 +248,12 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
242248
"#EXTINF:%i,\r\n"
243249
"%V-%i.ts\r\n",
244250
(ngx_int_t) (hacf->fraglen / 1000),
245-
&ctx->name, ffrag);
251+
&ctx->name, ffrag % hacf->nfrags);
246252

247253
n = write(fd, buffer, p - buffer);
248254
if (n < 0) {
249255
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
250-
"hls: write failed: '%V'", &ctx->playlist_bak);
256+
"hls: write failed '%V'", &ctx->playlist_bak);
251257
ngx_close_file(fd);
252258
return NGX_ERROR;
253259
}
@@ -445,7 +451,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
445451

446452

447453
static void
448-
ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
454+
ngx_rtmp_hls_next_frag(ngx_rtmp_session_t *s)
449455
{
450456
ngx_rtmp_hls_app_conf_t *hacf;
451457
ngx_rtmp_hls_ctx_t *ctx;
@@ -458,35 +464,18 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
458464
return;
459465
}
460466

461-
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
462-
"hls: restart frag=%i", ctx->frag);
463-
464467
if (ctx->opened) {
465468
ngx_close_file(ctx->file.fd);
466469
ctx->opened = 0;
467470
}
468471

469-
/*
470-
* Erase old file
471-
* We should keep old fragments available whole next cycle
472-
*/
472+
ctx->frag++;
473473

474-
if (ctx->frag > (ngx_int_t) hacf->nfrags * 2) {
475-
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%i.ts",
476-
ctx->frag - hacf->nfrags * 2) = 0;
474+
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%i.ts",
475+
ctx->frag % hacf->nfrags) = 0;
477476

478-
ngx_delete_file(ctx->stream.data);
479-
480-
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
481-
"hls: delete stream file '%s'", ctx->stream.data);
482-
}
483-
484-
++ctx->frag;
485-
ctx->frag_start = ngx_current_msec;
486-
487-
/* we have preallocated memory in ctx->stream */
488-
489-
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "-%i.ts", ctx->frag) = 0;
477+
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
478+
"hls: next frag='%s'", ctx->stream.data);
490479

491480
ngx_memzero(&ctx->file, sizeof(ctx->file));
492481

@@ -506,6 +495,7 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
506495
if (ngx_rtmp_mpegts_write_header(&ctx->file) != NGX_OK) {
507496
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
508497
"hls: error writing fragment header");
498+
ngx_close_file(ctx->file.fd);
509499
return;
510500
}
511501

@@ -518,12 +508,13 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
518508
static void
519509
ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
520510
{
511+
ngx_rtmp_hls_app_conf_t *hacf;
521512
ngx_rtmp_hls_ctx_t *ctx;
522513
ngx_file_t file;
523514
ssize_t ret;
524-
ngx_int_t n;
525-
u_char buffer[9 + 2 + 1 +
526-
NGX_OFF_T_LEN + NGX_INT64_LEN];
515+
u_char buffer[31 + NGX_OFF_T_LEN];
516+
517+
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
527518

528519
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
529520

@@ -544,38 +535,16 @@ ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
544535
goto done;
545536
}
546537

547-
/*
548-
* # FRAG TIME
549-
*/
538+
/* read media sequence number */
550539

551-
if (ret < 11) {
540+
if (ret <= 31) {
552541
goto done;
553542
}
554543

555-
if (buffer[9] != '#' || buffer[10] != ' ') {
556-
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
557-
"hls: failed to restore stream");
558-
goto done;
559-
}
544+
buffer[ret] = 0;
560545

561-
n = 11;
562-
563-
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
564-
"hls: restore from '%*s'", ret - n, &buffer[n]);
565-
566-
567-
while (n < ret && buffer[n] != ' ') {
568-
n++;
569-
}
570-
571-
if (n + 1 >= ret) {
572-
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
573-
"hls: failed to restore stream");
574-
goto done;
575-
}
576-
577-
ctx->frag = ngx_atoi(&buffer[11], n - 11);
578-
ctx->basetime = strtod((const char *) &buffer[n + 1], NULL);
546+
ctx->frag = atoi((const char *) &buffer[31]);
547+
ctx->basetime = (uint64_t) ctx->frag * hacf->fraglen * 90;
579548

580549
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
581550
"hls: restored frag=%i, basetime=%uL",
@@ -671,7 +640,6 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
671640
}
672641

673642
ctx->publishing = 1;
674-
ctx->frag_start = ngx_current_msec - hacf->fraglen - 1;
675643

676644
next:
677645
return next_publish(s, v);
@@ -759,6 +727,25 @@ ngx_rtmp_hls_parse_aac_header(ngx_rtmp_session_t *s, ngx_uint_t *objtype,
759727
}
760728

761729

730+
static void
731+
ngx_rtmp_hls_switch_frag(ngx_rtmp_session_t *s, uint64_t ts)
732+
{
733+
ngx_rtmp_hls_app_conf_t *hacf;
734+
ngx_rtmp_hls_ctx_t *ctx;
735+
ngx_uint_t frag;
736+
737+
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
738+
739+
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module);
740+
741+
frag = ts / hacf->fraglen / 90;
742+
743+
while ((ctx->frag - frag) % hacf->nfrags) {
744+
ngx_rtmp_hls_next_frag(s);
745+
}
746+
}
747+
748+
762749
static ngx_int_t
763750
ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
764751
ngx_chain_t *in)
@@ -791,19 +778,6 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
791778
return NGX_OK;
792779
}
793780

794-
/* Fragment is restarted in video handler.
795-
* However if video stream is missing then do it here */
796-
797-
if (codec_ctx->avc_header == NULL &&
798-
ngx_current_msec - ctx->frag_start > hacf->fraglen)
799-
{
800-
ngx_rtmp_hls_restart(s);
801-
}
802-
803-
if (!ctx->opened) {
804-
return NGX_OK;
805-
}
806-
807781
if (!ctx->epoch_valid) {
808782
ctx->epoch = h->timestamp;
809783
ctx->epoch_valid = 1;
@@ -877,9 +851,19 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
877851

878852
frame.pts = frame.dts;
879853

880-
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
881-
"hls: audio dts=%uL, timestamp=%uD",
882-
frame.dts, h->timestamp);
854+
/* Fragment is restarted in video handler.
855+
* However if video stream is missing then do it here */
856+
857+
if (codec_ctx->avc_header == NULL) {
858+
ngx_rtmp_hls_switch_frag(s, frame.dts);
859+
}
860+
861+
if (!ctx->opened) {
862+
return NGX_OK;
863+
}
864+
865+
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
866+
"hls: audio dts=%uL", frame.dts);
883867

884868
if (ngx_rtmp_mpegts_write_frame(&ctx->file, &frame, &out) != NGX_OK) {
885869
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
@@ -951,7 +935,7 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
951935
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
952936

953937
if (hacf == NULL || !hacf->hls || ctx == NULL || codec_ctx == NULL ||
954-
h->mlen < 1)
938+
codec_ctx->avc_header == NULL || h->mlen < 1)
955939
{
956940
return NGX_OK;
957941
}
@@ -1000,14 +984,6 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1000984
out.pos = out.start;
1001985
out.last = out.pos;
1002986

1003-
if (ftype == 1 && ngx_current_msec - ctx->frag_start > hacf->fraglen) {
1004-
ngx_rtmp_hls_restart(s);
1005-
}
1006-
1007-
if (!ctx->opened || codec_ctx->avc_header == NULL) {
1008-
return NGX_OK;
1009-
}
1010-
1011987
rc = ngx_rtmp_hls_get_nal_bytes(s);
1012988
if (rc < 0) {
1013989
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
@@ -1119,6 +1095,17 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
11191095
frame.sid = 0xe0;
11201096
frame.key = (ftype == 1);
11211097

1098+
if (frame.key) {
1099+
ngx_rtmp_hls_switch_frag(s, frame.dts);
1100+
}
1101+
1102+
if (!ctx->opened) {
1103+
return NGX_OK;
1104+
}
1105+
1106+
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1107+
"hls: video pts=%uL, dts=%uL", frame.pts, frame.dts);
1108+
11221109
if (ngx_rtmp_mpegts_write_frame(&ctx->file, &frame, &out) != NGX_OK) {
11231110
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
11241111
"hls: video frame failed");
@@ -1147,6 +1134,7 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
11471134
conf->sync = NGX_CONF_UNSET;
11481135
conf->playlen = NGX_CONF_UNSET;
11491136
conf->continuous = NGX_CONF_UNSET;
1137+
conf->factor = NGX_CONF_UNSET;
11501138
conf->nbuckets = 1024;
11511139

11521140
return conf;
@@ -1166,6 +1154,7 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
11661154
ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000);
11671155
ngx_conf_merge_str_value(conf->path, prev->path, "");
11681156
ngx_conf_merge_value(conf->continuous, prev->continuous, 1);
1157+
ngx_conf_merge_value(conf->factor, prev->factor, 2);
11691158

11701159
conf->ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_hls_ctx_t *) *
11711160
conf->nbuckets);
@@ -1174,7 +1163,8 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
11741163
}
11751164

11761165
if (conf->fraglen) {
1177-
conf->nfrags = conf->playlen / conf->fraglen;
1166+
conf->winfrags = conf->playlen / conf->fraglen;
1167+
conf->nfrags = conf->winfrags * conf->factor;
11781168
}
11791169

11801170
return NGX_CONF_OK;

0 commit comments

Comments
 (0)