@@ -26,7 +26,6 @@ static char * ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf,
26
26
27
27
typedef struct {
28
28
ngx_uint_t flags ;
29
- ngx_msec_t frag_start ;
30
29
31
30
unsigned publishing :1 ;
32
31
unsigned opened :1 ;
@@ -59,7 +58,9 @@ typedef struct {
59
58
ngx_msec_t muxdelay ;
60
59
ngx_msec_t sync ;
61
60
ngx_msec_t playlen ;
62
- size_t nfrags ;
61
+ ngx_int_t factor ;
62
+ ngx_uint_t winfrags ;
63
+ ngx_uint_t nfrags ;
63
64
ngx_flag_t continuous ;
64
65
ngx_rtmp_hls_ctx_t * * ctx ;
65
66
ngx_uint_t nbuckets ;
@@ -118,6 +119,13 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
118
119
offsetof(ngx_rtmp_hls_app_conf_t , continuous ),
119
120
NULL },
120
121
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
+
121
129
ngx_null_command
122
130
};
123
131
@@ -195,8 +203,8 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
195
203
196
204
fd = ngx_open_file (ctx -> playlist_bak .data , NGX_FILE_WRONLY ,
197
205
NGX_FILE_TRUNCATE , NGX_FILE_DEFAULT_ACCESS );
198
- if (fd == NGX_INVALID_FILE ) {
199
206
207
+ if (fd == NGX_INVALID_FILE ) {
200
208
ngx_log_error (NGX_LOG_ERR , s -> connection -> log , ngx_errno ,
201
209
"hls: open failed: '%V'" , & ctx -> playlist_bak );
202
210
@@ -215,19 +223,17 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
215
223
return NGX_ERROR ;
216
224
}
217
225
218
- ffrag = ctx -> frag - hacf -> nfrags ;
226
+ ffrag = ctx -> frag - hacf -> winfrags ;
219
227
if (ffrag < 1 ) {
220
228
ffrag = 1 ;
221
229
}
222
230
223
231
p = ngx_snprintf (buffer , sizeof (buffer ),
224
232
"#EXTM3U\r\n"
225
- "# %i %uL \r\n"
233
+ "#EXT-X-MEDIA-SEQUENCE:%i \r\n"
226
234
"#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 ));
231
237
232
238
n = write (fd , buffer , p - buffer );
233
239
if (n < 0 ) {
@@ -242,12 +248,12 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
242
248
"#EXTINF:%i,\r\n"
243
249
"%V-%i.ts\r\n" ,
244
250
(ngx_int_t ) (hacf -> fraglen / 1000 ),
245
- & ctx -> name , ffrag );
251
+ & ctx -> name , ffrag % hacf -> nfrags );
246
252
247
253
n = write (fd , buffer , p - buffer );
248
254
if (n < 0 ) {
249
255
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 );
251
257
ngx_close_file (fd );
252
258
return NGX_ERROR ;
253
259
}
@@ -445,7 +451,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
445
451
446
452
447
453
static void
448
- ngx_rtmp_hls_restart (ngx_rtmp_session_t * s )
454
+ ngx_rtmp_hls_next_frag (ngx_rtmp_session_t * s )
449
455
{
450
456
ngx_rtmp_hls_app_conf_t * hacf ;
451
457
ngx_rtmp_hls_ctx_t * ctx ;
@@ -458,35 +464,18 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
458
464
return ;
459
465
}
460
466
461
- ngx_log_debug1 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
462
- "hls: restart frag=%i" , ctx -> frag );
463
-
464
467
if (ctx -> opened ) {
465
468
ngx_close_file (ctx -> file .fd );
466
469
ctx -> opened = 0 ;
467
470
}
468
471
469
- /*
470
- * Erase old file
471
- * We should keep old fragments available whole next cycle
472
- */
472
+ ctx -> frag ++ ;
473
473
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 ;
477
476
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 );
490
479
491
480
ngx_memzero (& ctx -> file , sizeof (ctx -> file ));
492
481
@@ -506,6 +495,7 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
506
495
if (ngx_rtmp_mpegts_write_header (& ctx -> file ) != NGX_OK ) {
507
496
ngx_log_error (NGX_LOG_ERR , s -> connection -> log , ngx_errno ,
508
497
"hls: error writing fragment header" );
498
+ ngx_close_file (ctx -> file .fd );
509
499
return ;
510
500
}
511
501
@@ -518,12 +508,13 @@ ngx_rtmp_hls_restart(ngx_rtmp_session_t *s)
518
508
static void
519
509
ngx_rtmp_hls_restore_stream (ngx_rtmp_session_t * s )
520
510
{
511
+ ngx_rtmp_hls_app_conf_t * hacf ;
521
512
ngx_rtmp_hls_ctx_t * ctx ;
522
513
ngx_file_t file ;
523
514
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 ) ;
527
518
528
519
ctx = ngx_rtmp_get_module_ctx (s , ngx_rtmp_hls_module );
529
520
@@ -544,38 +535,16 @@ ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
544
535
goto done ;
545
536
}
546
537
547
- /*
548
- * # FRAG TIME
549
- */
538
+ /* read media sequence number */
550
539
551
- if (ret < 11 ) {
540
+ if (ret <= 31 ) {
552
541
goto done ;
553
542
}
554
543
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 ;
560
545
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 ;
579
548
580
549
ngx_log_debug2 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
581
550
"hls: restored frag=%i, basetime=%uL" ,
@@ -671,7 +640,6 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
671
640
}
672
641
673
642
ctx -> publishing = 1 ;
674
- ctx -> frag_start = ngx_current_msec - hacf -> fraglen - 1 ;
675
643
676
644
next :
677
645
return next_publish (s , v );
@@ -759,6 +727,25 @@ ngx_rtmp_hls_parse_aac_header(ngx_rtmp_session_t *s, ngx_uint_t *objtype,
759
727
}
760
728
761
729
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
+
762
749
static ngx_int_t
763
750
ngx_rtmp_hls_audio (ngx_rtmp_session_t * s , ngx_rtmp_header_t * h ,
764
751
ngx_chain_t * in )
@@ -791,19 +778,6 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
791
778
return NGX_OK ;
792
779
}
793
780
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
-
807
781
if (!ctx -> epoch_valid ) {
808
782
ctx -> epoch = h -> timestamp ;
809
783
ctx -> epoch_valid = 1 ;
@@ -877,9 +851,19 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
877
851
878
852
frame .pts = frame .dts ;
879
853
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 );
883
867
884
868
if (ngx_rtmp_mpegts_write_frame (& ctx -> file , & frame , & out ) != NGX_OK ) {
885
869
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,
951
935
codec_ctx = ngx_rtmp_get_module_ctx (s , ngx_rtmp_codec_module );
952
936
953
937
if (hacf == NULL || !hacf -> hls || ctx == NULL || codec_ctx == NULL ||
954
- h -> mlen < 1 )
938
+ codec_ctx -> avc_header == NULL || h -> mlen < 1 )
955
939
{
956
940
return NGX_OK ;
957
941
}
@@ -1000,14 +984,6 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1000
984
out .pos = out .start ;
1001
985
out .last = out .pos ;
1002
986
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
-
1011
987
rc = ngx_rtmp_hls_get_nal_bytes (s );
1012
988
if (rc < 0 ) {
1013
989
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,
1119
1095
frame .sid = 0xe0 ;
1120
1096
frame .key = (ftype == 1 );
1121
1097
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
+
1122
1109
if (ngx_rtmp_mpegts_write_frame (& ctx -> file , & frame , & out ) != NGX_OK ) {
1123
1110
ngx_log_error (NGX_LOG_ERR , s -> connection -> log , 0 ,
1124
1111
"hls: video frame failed" );
@@ -1147,6 +1134,7 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
1147
1134
conf -> sync = NGX_CONF_UNSET ;
1148
1135
conf -> playlen = NGX_CONF_UNSET ;
1149
1136
conf -> continuous = NGX_CONF_UNSET ;
1137
+ conf -> factor = NGX_CONF_UNSET ;
1150
1138
conf -> nbuckets = 1024 ;
1151
1139
1152
1140
return conf ;
@@ -1166,6 +1154,7 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
1166
1154
ngx_conf_merge_msec_value (conf -> playlen , prev -> playlen , 30000 );
1167
1155
ngx_conf_merge_str_value (conf -> path , prev -> path , "" );
1168
1156
ngx_conf_merge_value (conf -> continuous , prev -> continuous , 1 );
1157
+ ngx_conf_merge_value (conf -> factor , prev -> factor , 2 );
1169
1158
1170
1159
conf -> ctx = ngx_pcalloc (cf -> pool , sizeof (ngx_rtmp_hls_ctx_t * ) *
1171
1160
conf -> nbuckets );
@@ -1174,7 +1163,8 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
1174
1163
}
1175
1164
1176
1165
if (conf -> fraglen ) {
1177
- conf -> nfrags = conf -> playlen / conf -> fraglen ;
1166
+ conf -> winfrags = conf -> playlen / conf -> fraglen ;
1167
+ conf -> nfrags = conf -> winfrags * conf -> factor ;
1178
1168
}
1179
1169
1180
1170
return NGX_CONF_OK ;
0 commit comments