Commit 8191fa0b8c for ffmpeg
commit 8191fa0b8c05da7919f5ffecf631a7aa669b9203
Author: Romain Beauxis <romain.beauxis@gmail.com>
Date: Sat Oct 17 06:32:53 2026 +0200
avformat/hls: accept EXT-X-START in Master Playlists
EXT-X-START may appear in both Master and Media Playlists (RFC 8216
4.3.5.2). Parsing it allocated a Media Playlist, so a Master Playlist
carrying it before its EXT-X-STREAM-INF entries was rejected since
0e6eef3551.
Record the offset while parsing and apply it once the playlist type is
known. A value from a Master Playlist applies to all its Media
Playlists, which then ignore their own, as RFC 8216 4.3.5 recommends.
Defer EXT-X-ENDLIST the same way, so that it is not lost when it
precedes the first tag allocating the Media Playlist.
diff --git a/libavformat/hls.c b/libavformat/hls.c
index 519d3d852f..c68b5e88cf 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -264,6 +264,7 @@ typedef struct HLSContext {
int m3u8_hold_counters;
int live_start_index;
int prefer_x_start;
+ int master_time_offset_flag; ///< EXT-X-START was set by the Master Playlist
int first_packet;
int64_t first_timestamp;
struct playlist *first_timestamp_pls;
@@ -979,6 +980,9 @@ static int parse_playlist(HLSContext *c, const char *url,
int prev_n_segments = 0;
int64_t prev_start_seq_no = -1;
int64_t load_start = av_gettime_relative();
+ int64_t start_time_offset = 0;
+ int time_offset_flag = 0;
+ int finished = 0;
if (is_http && !in && c->http_persistent && c->playlist_pb) {
in = c->playlist_pb;
@@ -1139,15 +1143,11 @@ static int parse_playlist(HLSContext *c, const char *url,
} else if (av_strstart(line, "#EXT-X-START:", &ptr)) {
const char *time_offset_value = NULL;
- ret = ensure_playlist(c, &pls, url);
- if (ret < 0) {
- goto fail;
- }
if (av_strstart(ptr, "TIME-OFFSET=", &time_offset_value)) {
double offset = strtod(time_offset_value, NULL) * AV_TIME_BASE;
if (offset >= -0x1p63 && offset < 0x1p63) {
- pls->start_time_offset = offset;
- pls->time_offset_flag = 1;
+ start_time_offset = offset;
+ time_offset_flag = 1;
} else {
av_log(c->ctx, AV_LOG_WARNING, "TIME-OFFSET value is"
"invalid, it will be ignored");
@@ -1158,8 +1158,7 @@ static int parse_playlist(HLSContext *c, const char *url,
continue;
}
} else if (av_strstart(line, "#EXT-X-ENDLIST", &ptr)) {
- if (pls)
- pls->finished = 1;
+ finished = 1;
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
double d = atof(ptr) * AV_TIME_BASE;
if (d < 0 || d > INT64_MAX || isnan(d)) {
@@ -1290,6 +1289,27 @@ static int parse_playlist(HLSContext *c, const char *url,
}
}
+ /* Media Playlist tags may precede the first tag that allocates the
+ * playlist. */
+ if (pls && finished)
+ pls->finished = 1;
+
+ /* EXT-X-START is valid in both playlist types. A value from the Master
+ * Playlist applies to all its Media Playlists, which then ignore their
+ * own (RFC 8216 4.3.5). */
+ if (time_offset_flag && !c->master_time_offset_flag) {
+ if (pls) {
+ pls->start_time_offset = start_time_offset;
+ pls->time_offset_flag = 1;
+ } else {
+ c->master_time_offset_flag = 1;
+ for (int i = 0; i < c->n_playlists; i++) {
+ c->playlists[i]->start_time_offset = start_time_offset;
+ c->playlists[i]->time_offset_flag = 1;
+ }
+ }
+ }
+
fail:
free_segment_dynarray(prev_segments, prev_n_segments);
av_freep(&prev_segments);
diff --git a/tests/ref/fate/hls-parse-master-start b/tests/ref/fate/hls-parse-master-start
index 401c7af49d..57e88ecc7d 100644
--- a/tests/ref/fate/hls-parse-master-start
+++ b/tests/ref/fate/hls-parse-master-start
@@ -1 +1,3 @@
-error: Invalid data found when processing input
+2 variant(s), 2 playlist(s)
+a.m3u8: live, start offset 1500000
+b.m3u8: live, start offset 1500000
diff --git a/tests/ref/fate/hls-parse-master-start-media b/tests/ref/fate/hls-parse-master-start-media
index 401c7af49d..57e88ecc7d 100644
--- a/tests/ref/fate/hls-parse-master-start-media
+++ b/tests/ref/fate/hls-parse-master-start-media
@@ -1 +1,3 @@
-error: Invalid data found when processing input
+2 variant(s), 2 playlist(s)
+a.m3u8: live, start offset 1500000
+b.m3u8: live, start offset 1500000
diff --git a/tests/ref/fate/hls-parse-master-start-override b/tests/ref/fate/hls-parse-master-start-override
index 401c7af49d..57e88ecc7d 100644
--- a/tests/ref/fate/hls-parse-master-start-override
+++ b/tests/ref/fate/hls-parse-master-start-override
@@ -1 +1,3 @@
-error: Invalid data found when processing input
+2 variant(s), 2 playlist(s)
+a.m3u8: live, start offset 1500000
+b.m3u8: live, start offset 1500000