@@ -1123,26 +1123,47 @@ async fn run_hls_command_with_streaming_uploads(
11231123}
11241124
11251125async fn normalize_hls_init_fragments ( hls_dir : & Path , renditions : & [ HlsRendition ] ) -> Result < ( ) > {
1126- for rendition in renditions {
1126+ for ( variant_index , rendition) in renditions. iter ( ) . enumerate ( ) {
11271127 let variant_dir = hls_dir. join ( rendition. name ) ;
11281128 let ffmpeg_init_path = variant_dir. join ( HLS_FFMPEG_INIT_FILENAME ) ;
1129+ let indexed_init_name = format ! ( "init_{variant_index}.mp4" ) ;
1130+ let indexed_init_path = variant_dir. join ( & indexed_init_name) ;
11291131 let normalized_init_name = format ! ( "init_{}.mp4" , rendition. name) ;
11301132 let normalized_init_path = variant_dir. join ( & normalized_init_name) ;
1131- let ffmpeg_init_exists = fs:: try_exists ( & ffmpeg_init_path) . await ?;
1132- let normalized_init_exists = fs:: try_exists ( & normalized_init_path) . await ?;
1133+
1134+ // FFmpeg adds the numeric var_stream_map index even when the configured
1135+ // init filename does not contain `%v`. Single-variant builds may still
1136+ // emit the configured `init.mp4`, while retried normalization tests can
1137+ // already contain Rend's rendition-named form. Accept exactly one of
1138+ // those valid shapes and normalize it before uploading.
1139+ let candidate_paths = [
1140+ ffmpeg_init_path,
1141+ indexed_init_path,
1142+ normalized_init_path. clone ( ) ,
1143+ ] ;
1144+ let mut existing_paths = Vec :: new ( ) ;
1145+ for candidate_path in candidate_paths {
1146+ if fs:: try_exists ( & candidate_path) . await ? {
1147+ existing_paths. push ( candidate_path) ;
1148+ }
1149+ }
11331150 anyhow:: ensure!(
1134- ffmpeg_init_exists ^ normalized_init_exists,
1135- "ffmpeg created an invalid {} HLS init file set" ,
1136- rendition. name
1151+ existing_paths. len( ) == 1 ,
1152+ "ffmpeg created an invalid {} HLS init file set; expected exactly one of {}, {}, or {}" ,
1153+ rendition. name,
1154+ HLS_FFMPEG_INIT_FILENAME ,
1155+ indexed_init_name,
1156+ normalized_init_name,
11371157 ) ;
1138- if ffmpeg_init_exists {
1139- fs:: rename ( & ffmpeg_init_path, & normalized_init_path)
1158+ let source_init_path = existing_paths. pop ( ) . expect ( "one init path was verified" ) ;
1159+ if source_init_path != normalized_init_path {
1160+ fs:: rename ( & source_init_path, & normalized_init_path)
11401161 . await
11411162 . with_context ( || {
11421163 format ! (
11431164 "failed to normalize {} HLS init file {}" ,
11441165 rendition. name,
1145- ffmpeg_init_path . display( )
1166+ source_init_path . display( )
11461167 )
11471168 } ) ?;
11481169 }
@@ -1151,7 +1172,8 @@ async fn normalize_hls_init_fragments(hls_dir: &Path, renditions: &[HlsRendition
11511172 let playlist = fs:: read_to_string ( & playlist_path)
11521173 . await
11531174 . with_context ( || format ! ( "failed to read {} HLS playlist" , rendition. name) ) ?;
1154- let normalized = normalize_hls_variant_playlist_init ( & playlist, rendition. name ) ?;
1175+ let normalized =
1176+ normalize_hls_variant_playlist_init ( & playlist, rendition. name , variant_index) ?;
11551177 if normalized != playlist {
11561178 fs:: write ( & playlist_path, normalized)
11571179 . await
@@ -1161,8 +1183,13 @@ async fn normalize_hls_init_fragments(hls_dir: &Path, renditions: &[HlsRendition
11611183 Ok ( ( ) )
11621184}
11631185
1164- fn normalize_hls_variant_playlist_init ( playlist : & str , rendition : & str ) -> Result < String > {
1186+ fn normalize_hls_variant_playlist_init (
1187+ playlist : & str ,
1188+ rendition : & str ,
1189+ variant_index : usize ,
1190+ ) -> Result < String > {
11651191 let ffmpeg_reference = format ! ( "URI=\" {HLS_FFMPEG_INIT_FILENAME}\" " ) ;
1192+ let indexed_reference = format ! ( "URI=\" init_{variant_index}.mp4\" " ) ;
11661193 let normalized_reference = format ! ( "URI=\" init_{rendition}.mp4\" " ) ;
11671194 let init_maps = playlist
11681195 . lines ( )
@@ -1174,11 +1201,15 @@ fn normalize_hls_variant_playlist_init(playlist: &str, rendition: &str) -> Resul
11741201 ) ;
11751202 anyhow:: ensure!(
11761203 init_maps. iter( ) . all( |line| {
1177- line. contains( & ffmpeg_reference) || line. contains( & normalized_reference)
1204+ line. contains( & ffmpeg_reference)
1205+ || line. contains( & indexed_reference)
1206+ || line. contains( & normalized_reference)
11781207 } ) ,
11791208 "{rendition} HLS playlist referenced an unexpected init file"
11801209 ) ;
1181- Ok ( playlist. replace ( & ffmpeg_reference, & normalized_reference) )
1210+ Ok ( playlist
1211+ . replace ( & ffmpeg_reference, & normalized_reference)
1212+ . replace ( & indexed_reference, & normalized_reference) )
11821213}
11831214
11841215async fn upload_finalized_hls_fragments (
@@ -2976,7 +3007,7 @@ segment_00000.m4s
29763007#EXT-X-ENDLIST
29773008"# ;
29783009
2979- let normalized = normalize_hls_variant_playlist_init ( playlist, "360p" ) . unwrap ( ) ;
3010+ let normalized = normalize_hls_variant_playlist_init ( playlist, "360p" , 0 ) . unwrap ( ) ;
29803011
29813012 assert ! ( normalized. contains( "#EXT-X-MAP:URI=\" init_360p.mp4\" " ) ) ;
29823013 assert ! ( !normalized. contains( "URI=\" init.mp4\" " ) ) ;
@@ -2990,13 +3021,13 @@ segment_00000.m4s
29903021segment_00000.m4s
29913022"# ;
29923023
2993- let error = normalize_hls_variant_playlist_init ( playlist, "360p" ) . unwrap_err ( ) ;
3024+ let error = normalize_hls_variant_playlist_init ( playlist, "360p" , 0 ) . unwrap_err ( ) ;
29943025
29953026 assert ! ( error. to_string( ) . contains( "unexpected init file" ) ) ;
29963027 }
29973028
29983029 #[ tokio:: test]
2999- async fn hls_init_fragment_normalization_renames_file_and_playlist_reference ( ) {
3030+ async fn hls_init_fragment_normalization_renames_indexed_ffmpeg_output ( ) {
30003031 let unique = SystemTime :: now ( )
30013032 . duration_since ( UNIX_EPOCH )
30023033 . unwrap ( )
@@ -3005,36 +3036,47 @@ segment_00000.m4s
30053036 "rend-hls-init-normalization-{}-{unique}" ,
30063037 std:: process:: id( )
30073038 ) ) ;
3008- let variant_dir = root. join ( "360p" ) ;
3009- fs:: create_dir_all ( & variant_dir) . await . unwrap ( ) ;
3010- fs:: write ( variant_dir. join ( HLS_FFMPEG_INIT_FILENAME ) , b"init" )
3039+ let renditions = & HLS_RENDITIONS [ ..4 ] ;
3040+ for ( variant_index, rendition) in renditions. iter ( ) . enumerate ( ) {
3041+ let variant_dir = root. join ( rendition. name ) ;
3042+ fs:: create_dir_all ( & variant_dir) . await . unwrap ( ) ;
3043+ fs:: write (
3044+ variant_dir. join ( format ! ( "init_{variant_index}.mp4" ) ) ,
3045+ b"init" ,
3046+ )
30113047 . await
30123048 . unwrap ( ) ;
3013- fs:: write (
3014- variant_dir. join ( "index.m3u8" ) ,
3015- b"#EXTM3U\n #EXT-X-MAP:URI=\" init.mp4\" \n segment_00000.m4s\n " ,
3016- )
3017- . await
3018- . unwrap ( ) ;
3019-
3020- normalize_hls_init_fragments ( & root, & [ HLS_RENDITIONS [ 0 ] ] )
3049+ fs:: write (
3050+ variant_dir. join ( "index.m3u8" ) ,
3051+ format ! (
3052+ "#EXTM3U\n #EXT-X-MAP:URI=\" init_{variant_index}.mp4\" \n segment_00000.m4s\n "
3053+ ) ,
3054+ )
30213055 . await
30223056 . unwrap ( ) ;
3057+ }
30233058
3024- assert ! (
3025- !fs:: try_exists( variant_dir. join( HLS_FFMPEG_INIT_FILENAME ) )
3026- . await
3027- . unwrap( )
3028- ) ;
3029- assert ! (
3030- fs:: try_exists( variant_dir. join( "init_360p.mp4" ) )
3031- . await
3032- . unwrap( )
3033- ) ;
3034- let playlist = fs:: read_to_string ( variant_dir. join ( "index.m3u8" ) )
3059+ normalize_hls_init_fragments ( & root, renditions)
30353060 . await
30363061 . unwrap ( ) ;
3037- assert ! ( playlist. contains( "#EXT-X-MAP:URI=\" init_360p.mp4\" " ) ) ;
3062+
3063+ for ( variant_index, rendition) in renditions. iter ( ) . enumerate ( ) {
3064+ let variant_dir = root. join ( rendition. name ) ;
3065+ assert ! (
3066+ !fs:: try_exists( variant_dir. join( format!( "init_{variant_index}.mp4" ) ) )
3067+ . await
3068+ . unwrap( )
3069+ ) ;
3070+ assert ! (
3071+ fs:: try_exists( variant_dir. join( format!( "init_{}.mp4" , rendition. name) ) )
3072+ . await
3073+ . unwrap( )
3074+ ) ;
3075+ let playlist = fs:: read_to_string ( variant_dir. join ( "index.m3u8" ) )
3076+ . await
3077+ . unwrap ( ) ;
3078+ assert ! ( playlist. contains( & format!( "#EXT-X-MAP:URI=\" init_{}.mp4\" " , rendition. name) ) ) ;
3079+ }
30383080
30393081 fs:: remove_dir_all ( root) . await . unwrap ( ) ;
30403082 }
0 commit comments