Commit def599cc17 for ffmpeg

commit def599cc17d1d250172f2afc7626733423b07a6d
Author: Kacper Michajłow <kasper93@gmail.com>
Date:   Wed Oct 28 04:09:54 2026 +0100

    avformat/hls: parse the EXT-X-START attributes in any order

    TIME-OFFSET was only read as the first attribute of EXT-X-START, so a
    tag with PRECISE first was ignored. RFC 8216 4.2 does not order the
    pairs of an attribute-list. Parse it with ff_parse_key_value() as the
    other attribute lists are.

    Signed-off-by: Kacper Michajłow <kasper93@gmail.com>

diff --git a/libavformat/hls.c b/libavformat/hls.c
index c68b5e88cf..23c7fbafd5 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -578,6 +578,20 @@ static void handle_init_section_args(void *context, const char *key,
     }
 }

+struct start_info {
+    char time_offset[MAX_FIELD_LEN];
+};
+
+static void handle_start_args(void *context, const char *key,
+                              int key_len, char **dest, int *dest_len)
+{
+    struct start_info *info = context;
+    if (!strncmp(key, "TIME-OFFSET=", key_len)) {
+        *dest     =        info->time_offset;
+        *dest_len = sizeof(info->time_offset);
+    }
+}
+
 struct rendition_info {
     char type[16];
     char uri[MAX_URL_SIZE];
@@ -1142,9 +1156,10 @@ static int parse_playlist(HLSContext *c, const char *url,
             }

         } else if (av_strstart(line, "#EXT-X-START:", &ptr)) {
-            const char *time_offset_value = NULL;
-            if (av_strstart(ptr, "TIME-OFFSET=", &time_offset_value)) {
-                double offset = strtod(time_offset_value, NULL) * AV_TIME_BASE;
+            struct start_info info = {0};
+            ff_parse_key_value(ptr, handle_start_args, &info);
+            if (info.time_offset[0]) {
+                double offset = strtod(info.time_offset, NULL) * AV_TIME_BASE;
                 if (offset >= -0x1p63 && offset < 0x1p63) {
                     start_time_offset = offset;
                     time_offset_flag  = 1;
diff --git a/tests/fate/demux.mak b/tests/fate/demux.mak
index e178a8883a..aa6f2795ba 100644
--- a/tests/fate/demux.mak
+++ b/tests/fate/demux.mak
@@ -243,7 +243,7 @@ tests/data/hls-start-master.m3u8: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data
 	$(Q)printf '#EXTM3U\n#EXT-X-START:TIME-OFFSET=0.5\n#EXT-X-TARGETDURATION:0\n#EXTINF:0.144,\nhls-start-0.ts\n#EXTINF:0.144,\nhls-start-1.ts\n#EXTINF:0.144,\nhls-start-2.ts\n#EXTINF:0.144,\nhls-start-3.ts\n' > tests/data/hls-start-media.m3u8
 	$(Q)printf '#EXTM3U\n#EXT-X-START:TIME-OFFSET=0.5\n#EXT-X-ENDLIST\n#EXT-X-TARGETDURATION:0\n#EXTINF:0.144,\nhls-start-0.ts\n#EXTINF:0.144,\nhls-start-1.ts\n#EXTINF:0.144,\nhls-start-2.ts\n#EXTINF:0.144,\nhls-start-3.ts\n' > tests/data/hls-start-endlist.m3u8
 	$(Q)printf '#EXTM3U\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a",NAME="a",URI="hls-start-rendition.m3u8"\n#EXT-X-STREAM-INF:BANDWIDTH=64000,AUDIO="a"\nhls-start-media.m3u8\n' > tests/data/hls-start-variant.m3u8
-	$(Q)printf '#EXTM3U\n#EXT-X-START:TIME-OFFSET=0.1\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a",NAME="a",URI="hls-start-rendition.m3u8"\n#EXT-X-STREAM-INF:BANDWIDTH=64000,AUDIO="a"\nhls-start-media.m3u8\n' > $@
+	$(Q)printf '#EXTM3U\n#EXT-X-START:PRECISE=NO,TIME-OFFSET=0.1\n#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="a",NAME="a",URI="hls-start-rendition.m3u8"\n#EXT-X-STREAM-INF:BANDWIDTH=64000,AUDIO="a"\nhls-start-media.m3u8\n' > $@

 FATE_HLS_START = fate-hls-start-master-demux  \
                  fate-hls-start-variant-demux \
diff --git a/tests/ref/fate/hls-start-master-demux b/tests/ref/fate/hls-start-master-demux
index c358cb243a..fd698bea2d 100644
--- a/tests/ref/fate/hls-start-master-demux
+++ b/tests/ref/fate/hls-start-master-demux
@@ -16,4 +16,4 @@ packet|codec_type=audio|stream_index=0|pts=45360|pts_time=0.504000|dts=45360|dts
 packet|codec_type=audio|stream_index=1|pts=45360|pts_time=0.504000|dts=45360|dts_time=0.504000|duration=6480|duration_time=0.072000|size=1440|pos=12784|flags=K__|data_hash=CRC32:cb00dd5b|side_datum/mpegts_stream_id:side_data_type=MPEGTS Stream ID|side_datum/mpegts_stream_id:id=192
 stream|index=0|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[4][0][0][0]|codec_tag=0x0004|mime_codec_string=mp4a.40.33|sample_fmt=fltp|sample_rate=16000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|seek_preroll=0|id=0x0|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=160000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=8|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000|tag:comment=a
 stream|index=1|codec_name=mp2|profile=unknown|level=-99|codec_type=audio|codec_tag_string=[4][0][0][0]|codec_tag=0x0004|mime_codec_string=mp4a.40.33|sample_fmt=unknown|sample_rate=16000|channels=1|channel_layout=mono|bits_per_sample=0|initial_padding=0|seek_preroll=0|id=0x1|r_frame_rate=0/0|avg_frame_rate=0/0|time_base=1/90000|start_pts=0|start_time=0.000000|duration_ts=N/A|duration=N/A|bit_rate=160000|max_bit_rate=N/A|bits_per_raw_sample=N/A|nb_frames=N/A|nb_read_frames=N/A|nb_read_packets=8|disposition:default=0|disposition:dub=0|disposition:original=0|disposition:comment=0|disposition:lyrics=0|disposition:karaoke=0|disposition:forced=0|disposition:hearing_impaired=0|disposition:visual_impaired=0|disposition:clean_effects=0|disposition:attached_pic=0|disposition:timed_thumbnails=0|disposition:non_diegetic=0|disposition:captions=0|disposition:descriptions=0|disposition:metadata=0|disposition:dependent=0|disposition:still_image=0|disposition:multilayer=0|tag:variant_bitrate=64000
-format|filename=hls-start-master.m3u8|nb_streams=2|nb_programs=1|nb_stream_groups=0|format_name=hls|start_time=0.000000|duration=N/A|size=179|bit_rate=320000|probe_score=100
+format|filename=hls-start-master.m3u8|nb_streams=2|nb_programs=1|nb_stream_groups=0|format_name=hls|start_time=0.000000|duration=N/A|size=190|bit_rate=320000|probe_score=100