@@ -29,7 +29,6 @@ typedef struct {
29
29
30
30
unsigned publishing :1 ;
31
31
unsigned opened :1 ;
32
- unsigned epoch_valid :1 ;
33
32
34
33
ngx_file_t file ;
35
34
@@ -38,17 +37,15 @@ typedef struct {
38
37
ngx_str_t stream ;
39
38
ngx_str_t name ;
40
39
41
- ngx_int_t frag ;
40
+ uint64_t frag ;
42
41
43
42
ngx_uint_t audio_cc ;
44
43
ngx_uint_t video_cc ;
45
44
46
45
int64_t aframe_base ;
47
46
int64_t aframe_num ;
48
47
49
- uint32_t epoch ;
50
- uint64_t basetime ;
51
- uint64_t timestamp ;
48
+ uint64_t offset ;
52
49
} ngx_rtmp_hls_ctx_t ;
53
50
54
51
@@ -62,6 +59,7 @@ typedef struct {
62
59
ngx_uint_t winfrags ;
63
60
ngx_uint_t nfrags ;
64
61
ngx_flag_t continuous ;
62
+ ngx_flag_t nodelete ;
65
63
ngx_rtmp_hls_ctx_t * * ctx ;
66
64
ngx_uint_t nbuckets ;
67
65
ngx_str_t path ;
@@ -119,6 +117,13 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
119
117
offsetof(ngx_rtmp_hls_app_conf_t , continuous ),
120
118
NULL },
121
119
120
+ { ngx_string ("hls_nodelete" ),
121
+ NGX_RTMP_MAIN_CONF |NGX_RTMP_SRV_CONF |NGX_RTMP_APP_CONF |NGX_CONF_TAKE1 ,
122
+ ngx_conf_set_flag_slot ,
123
+ NGX_RTMP_APP_CONF_OFFSET ,
124
+ offsetof(ngx_rtmp_hls_app_conf_t , nodelete ),
125
+ NULL },
126
+
122
127
{ ngx_string ("hls_playlist_factor" ),
123
128
NGX_RTMP_MAIN_CONF |NGX_RTMP_SRV_CONF |NGX_RTMP_APP_CONF |NGX_CONF_TAKE1 ,
124
129
ngx_conf_set_num_slot ,
@@ -161,6 +166,9 @@ ngx_module_t ngx_rtmp_hls_module = {
161
166
};
162
167
163
168
169
+ #define ngx_rtmp_hls_frag (hacf , f ) (hacf->nfrags ? (f) % hacf->nfrags : (f))
170
+
171
+
164
172
static void
165
173
ngx_rtmp_hls_chain2buffer (ngx_buf_t * out , ngx_chain_t * in , size_t skip )
166
174
{
@@ -190,7 +198,7 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
190
198
u_char * p ;
191
199
ngx_rtmp_hls_ctx_t * ctx ;
192
200
ssize_t n ;
193
- ngx_int_t ffrag ;
201
+ uint64_t ffrag ;
194
202
ngx_rtmp_hls_app_conf_t * hacf ;
195
203
ngx_int_t nretry ;
196
204
@@ -223,17 +231,15 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
223
231
return NGX_ERROR ;
224
232
}
225
233
226
- ffrag = ctx -> frag - hacf -> winfrags ;
227
- if (ffrag < 1 ) {
228
- ffrag = 1 ;
229
- }
234
+ ffrag = ctx -> frag > hacf -> winfrags ?
235
+ ctx -> frag - (uint64_t ) hacf -> winfrags : 1 ;
230
236
231
237
p = ngx_snprintf (buffer , sizeof (buffer ),
232
238
"#EXTM3U\r\n"
233
- "#EXT-X-MEDIA-SEQUENCE:%i \r\n"
234
- "#EXT-X-TARGETDURATION:%i \r\n"
239
+ "#EXT-X-MEDIA-SEQUENCE:%uL \r\n"
240
+ "#EXT-X-TARGETDURATION:%ui \r\n"
235
241
"#EXT-X-ALLOW-CACHE:NO\r\n\r\n" ,
236
- ctx -> frag , (ngx_int_t ) (hacf -> fraglen / 1000 ));
242
+ ctx -> frag , (ngx_uint_t ) (hacf -> fraglen / 1000 ));
237
243
238
244
n = write (fd , buffer , p - buffer );
239
245
if (n < 0 ) {
@@ -246,9 +252,9 @@ ngx_rtmp_hls_update_playlist(ngx_rtmp_session_t *s)
246
252
for (; ffrag < ctx -> frag ; ++ ffrag ) {
247
253
p = ngx_snprintf (buffer , sizeof (buffer ),
248
254
"#EXTINF:%i,\r\n"
249
- "%V-%i .ts\r\n" ,
250
- (ngx_int_t ) (hacf -> fraglen / 1000 ),
251
- & ctx -> name , ffrag % hacf -> nfrags );
255
+ "%V-%uL .ts\r\n" ,
256
+ (ngx_int_t ) (hacf -> fraglen / 1000 ), & ctx -> name ,
257
+ ngx_rtmp_hls_frag ( hacf , ffrag ) );
252
258
253
259
n = write (fd , buffer , p - buffer );
254
260
if (n < 0 ) {
@@ -469,13 +475,28 @@ ngx_rtmp_hls_next_frag(ngx_rtmp_session_t *s)
469
475
ctx -> opened = 0 ;
470
476
}
471
477
478
+ if (hacf -> nfrags == 0 && ctx -> frag > 2 * hacf -> winfrags &&
479
+ !hacf -> nodelete )
480
+ {
481
+ * ngx_sprintf (ctx -> stream .data + ctx -> stream .len , "-%uL.ts" ,
482
+ ngx_rtmp_hls_frag (hacf , ctx -> frag - 2 * hacf -> winfrags ))
483
+ = 0 ;
484
+
485
+ ngx_log_debug1 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
486
+ "hls: delete frag '%s'" , ctx -> stream .data );
487
+
488
+ ngx_delete_file (ctx -> stream .data );
489
+
490
+ }
491
+
472
492
ctx -> frag ++ ;
473
493
474
- * ngx_sprintf (ctx -> stream .data + ctx -> stream .len , "-%i .ts" ,
475
- ctx -> frag % hacf -> nfrags ) = 0 ;
494
+ * ngx_sprintf (ctx -> stream .data + ctx -> stream .len , "-%uL .ts" ,
495
+ ngx_rtmp_hls_frag ( hacf , ctx -> frag ) ) = 0 ;
476
496
477
- ngx_log_debug1 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
478
- "hls: next frag='%s'" , ctx -> stream .data );
497
+ ngx_log_debug2 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
498
+ "hls: open frag '%s', frag=%uL" ,
499
+ ctx -> stream .data , ctx -> frag );
479
500
480
501
ngx_memzero (& ctx -> file , sizeof (ctx -> file ));
481
502
@@ -505,16 +526,17 @@ ngx_rtmp_hls_next_frag(ngx_rtmp_session_t *s)
505
526
}
506
527
507
528
529
+ #define NGX_RTMP_HLS_RESTORE_PREFIX "#EXTM3U\r\n#EXT-X-MEDIA-SEQUENCE:"
530
+
531
+
508
532
static void
509
533
ngx_rtmp_hls_restore_stream (ngx_rtmp_session_t * s )
510
534
{
511
- ngx_rtmp_hls_app_conf_t * hacf ;
512
535
ngx_rtmp_hls_ctx_t * ctx ;
513
536
ngx_file_t file ;
514
537
ssize_t ret ;
515
- u_char buffer [31 + NGX_OFF_T_LEN ];
516
-
517
- hacf = ngx_rtmp_get_module_app_conf (s , ngx_rtmp_hls_module );
538
+ u_char buffer [sizeof (NGX_RTMP_HLS_RESTORE_PREFIX ) -
539
+ 1 + NGX_OFF_T_LEN ];
518
540
519
541
ctx = ngx_rtmp_get_module_ctx (s , ngx_rtmp_hls_module );
520
542
@@ -535,20 +557,25 @@ ngx_rtmp_hls_restore_stream(ngx_rtmp_session_t *s)
535
557
goto done ;
536
558
}
537
559
538
- /* read media sequence number */
560
+ if ((size_t ) ret < sizeof (NGX_RTMP_HLS_RESTORE_PREFIX )) {
561
+ goto done ;
562
+ }
539
563
540
- if (ret <= 31 ) {
564
+ if (ngx_strncmp (buffer , NGX_RTMP_HLS_RESTORE_PREFIX ,
565
+ sizeof (NGX_RTMP_HLS_RESTORE_PREFIX ) - 1 ))
566
+ {
567
+ ngx_log_error (NGX_LOG_ERR , s -> connection -> log , 0 ,
568
+ "hls: failed to restore stream" );
541
569
goto done ;
542
570
}
543
571
544
572
buffer [ret ] = 0 ;
545
573
546
- ctx -> frag = atoi ((const char * ) & buffer [ 31 ]);
547
- ctx -> basetime = ( uint64_t ) ctx -> frag * hacf -> fraglen * 90 ;
574
+ ctx -> frag = strtod ((const char * )
575
+ & buffer [ sizeof ( NGX_RTMP_HLS_RESTORE_PREFIX ) - 1 ], NULL ) ;
548
576
549
- ngx_log_debug2 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
550
- "hls: restored frag=%i, basetime=%uL" ,
551
- ctx -> frag , ctx -> basetime );
577
+ ngx_log_debug1 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
578
+ "hls: restored frag=%uL" , ctx -> frag );
552
579
553
580
done :
554
581
ngx_close_file (file .fd );
@@ -629,6 +656,7 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
629
656
p = ngx_cpymem (p , ".bak" , sizeof (".bak" ) - 1 );
630
657
631
658
ctx -> playlist_bak .len = p - ctx -> playlist_bak .data ;
659
+
632
660
* p = 0 ;
633
661
634
662
ngx_log_debug3 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
@@ -728,21 +756,29 @@ ngx_rtmp_hls_parse_aac_header(ngx_rtmp_session_t *s, ngx_uint_t *objtype,
728
756
729
757
730
758
static void
731
- ngx_rtmp_hls_switch_frag (ngx_rtmp_session_t * s , uint64_t ts )
759
+ ngx_rtmp_hls_set_frag (ngx_rtmp_session_t * s , uint64_t ts )
732
760
{
733
761
ngx_rtmp_hls_app_conf_t * hacf ;
734
762
ngx_rtmp_hls_ctx_t * ctx ;
735
- ngx_uint_t frag ;
763
+ uint64_t frag ;
736
764
737
765
hacf = ngx_rtmp_get_module_app_conf (s , ngx_rtmp_hls_module );
738
766
739
767
ctx = ngx_rtmp_get_module_ctx (s , ngx_rtmp_hls_module );
740
768
741
769
frag = ts / hacf -> fraglen / 90 ;
742
770
743
- while (( ctx -> frag - frag ) % hacf -> nfrags ) {
744
- ngx_rtmp_hls_next_frag ( s ) ;
771
+ if ( frag == ctx -> frag && ctx -> opened ) {
772
+ return ;
745
773
}
774
+
775
+ if (frag != ctx -> frag + 1 ) {
776
+ ctx -> offset = (ctx -> frag + 1 ) * (uint64_t ) hacf -> fraglen * 90 - ts ;
777
+ ngx_log_debug1 (NGX_LOG_DEBUG_RTMP , s -> connection -> log , 0 ,
778
+ "hls: time gap offset=%uL" , ctx -> offset );
779
+ }
780
+
781
+ ngx_rtmp_hls_next_frag (s );
746
782
}
747
783
748
784
@@ -778,14 +814,9 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
778
814
return NGX_OK ;
779
815
}
780
816
781
- if (!ctx -> epoch_valid ) {
782
- ctx -> epoch = h -> timestamp ;
783
- ctx -> epoch_valid = 1 ;
784
- }
785
-
786
817
ngx_memzero (& frame , sizeof (frame ));
787
818
788
- frame .dts = (h -> timestamp - ctx -> epoch ) * 90L + ctx -> basetime ;
819
+ frame .dts = (uint64_t ) h -> timestamp * 90L + ctx -> offset ;
789
820
frame .cc = ctx -> audio_cc ;
790
821
frame .pid = 0x101 ;
791
822
frame .sid = 0xc0 ;
@@ -855,7 +886,7 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
855
886
* However if video stream is missing then do it here */
856
887
857
888
if (codec_ctx -> avc_header == NULL ) {
858
- ngx_rtmp_hls_switch_frag (s , frame .dts );
889
+ ngx_rtmp_hls_set_frag (s , frame .dts );
859
890
}
860
891
861
892
if (!ctx -> opened ) {
@@ -871,7 +902,6 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
871
902
}
872
903
873
904
ctx -> audio_cc = frame .cc ;
874
- ctx -> timestamp = frame .dts ;
875
905
876
906
return NGX_OK ;
877
907
}
@@ -1081,22 +1111,17 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1081
1111
out .last += (len - 1 );
1082
1112
}
1083
1113
1084
- if (!ctx -> epoch_valid ) {
1085
- ctx -> epoch = h -> timestamp ;
1086
- ctx -> epoch_valid = 1 ;
1087
- }
1088
-
1089
1114
ngx_memzero (& frame , sizeof (frame ));
1090
1115
1091
1116
frame .cc = ctx -> video_cc ;
1092
- frame .dts = (h -> timestamp - ctx -> epoch ) * 90L + ctx -> basetime ;
1117
+ frame .dts = (uint64_t ) h -> timestamp * 90L + ctx -> offset ;
1093
1118
frame .pts = frame .dts + cts * 90 ;
1094
1119
frame .pid = 0x100 ;
1095
1120
frame .sid = 0xe0 ;
1096
1121
frame .key = (ftype == 1 );
1097
1122
1098
1123
if (frame .key ) {
1099
- ngx_rtmp_hls_switch_frag (s , frame .dts );
1124
+ ngx_rtmp_hls_set_frag (s , frame .dts );
1100
1125
}
1101
1126
1102
1127
if (!ctx -> opened ) {
@@ -1112,7 +1137,6 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1112
1137
}
1113
1138
1114
1139
ctx -> video_cc = frame .cc ;
1115
- ctx -> timestamp = frame .dts ;
1116
1140
1117
1141
return NGX_OK ;
1118
1142
}
@@ -1134,6 +1158,7 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
1134
1158
conf -> sync = NGX_CONF_UNSET ;
1135
1159
conf -> playlen = NGX_CONF_UNSET ;
1136
1160
conf -> continuous = NGX_CONF_UNSET ;
1161
+ conf -> nodelete = NGX_CONF_UNSET ;
1137
1162
conf -> factor = NGX_CONF_UNSET ;
1138
1163
conf -> nbuckets = 1024 ;
1139
1164
@@ -1154,6 +1179,7 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
1154
1179
ngx_conf_merge_msec_value (conf -> playlen , prev -> playlen , 30000 );
1155
1180
ngx_conf_merge_str_value (conf -> path , prev -> path , "" );
1156
1181
ngx_conf_merge_value (conf -> continuous , prev -> continuous , 1 );
1182
+ ngx_conf_merge_value (conf -> nodelete , prev -> nodelete , 0 );
1157
1183
ngx_conf_merge_value (conf -> factor , prev -> factor , 2 );
1158
1184
1159
1185
conf -> ctx = ngx_pcalloc (cf -> pool , sizeof (ngx_rtmp_hls_ctx_t * ) *
0 commit comments