This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit ede944b3622dd2bcd414f6e522ce7fbeb5f7974a Author: Kacper Michajłow <[email protected]> AuthorDate: Wed Jul 8 03:51:44 2026 +0200 Commit: Kacper Michajłow <[email protected]> CommitDate: Tue Jul 28 18:01:18 2026 +0200 avformat/hls: allow seeking within live playlists Live (and EVENT) playlists are seekable within the currently known segment list. Seeks before the list start clamp to the oldest segment, past the end to the live edge. Live playlist can evict old segments, we track that by reading EXT-X-MEDIA-SEQUENCE. Previusly the code assumed that the live playlist, don't have past segments, however some servers has very deep usable timeshift. Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/hls.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/libavformat/hls.c b/libavformat/hls.c index fd40800ca8..8ac4b35daa 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -1165,8 +1165,7 @@ fail: ff_format_io_close(c->ctx, &in); c->ctx->ctx_flags = c->ctx->ctx_flags & ~(unsigned)AVFMTCTX_UNSEEKABLE; if (!c->n_variants || !c->variants[0]->n_playlists || - !(c->variants[0]->playlists[0]->finished || - c->variants[0]->playlists[0]->type == PLS_TYPE_EVENT)) + !c->variants[0]->playlists[0]->n_segments) c->ctx->ctx_flags |= AVFMTCTX_UNSEEKABLE; if (c->n_variants && c->variants[0]->n_playlists && @@ -2070,6 +2069,12 @@ static int find_timestamp_in_playlist(HLSContext *c, struct playlist *pls, *seq_no = pls->start_seq_no + pls->n_segments - 1; + if (!pls->finished && pls->n_segments > 0) { + if (seg_start_ts) + *seg_start_ts = pos - pls->segments[pls->n_segments - 1]->duration; + return 1; + } + return 0; } @@ -2907,12 +2912,6 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, s->streams[stream_index]->time_base, AV_TIME_BASE_Q, AV_ROUND_DOWN); - duration = s->duration == AV_NOPTS_VALUE ? - 0 : s->duration; - - if (0 < duration && duration < seek_timestamp - first_timestamp) - return AVERROR(EIO); - /* find the playlist with the specified stream */ for (i = 0; i < c->n_playlists; i++) { struct playlist *pls = c->playlists[i]; @@ -2924,9 +2923,20 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, } } } + if (!seek_pls) + return AVERROR(EIO); + + duration = s->duration == AV_NOPTS_VALUE ? + 0 : s->duration; + + /* Only finished playlists cannot grow past the known duration. */ + if (seek_pls->finished && + 0 < duration && duration < seek_timestamp - first_timestamp) + return AVERROR(EIO); + /* check if the timestamp is valid for the playlist with the * specified stream index */ - if (!seek_pls || !find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no, &seg_start_ts)) + if (!find_timestamp_in_playlist(c, seek_pls, seek_timestamp, &seq_no, &seg_start_ts)) return AVERROR(EIO); if (s->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && @@ -2936,6 +2946,10 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, seek_timestamp = seg_start_ts; } + av_log(s, AV_LOG_DEBUG, "Seek to %"PRId64" mapped to segment %"PRId64 + " of playlist %d (start %"PRId64")\n", + seek_timestamp, seq_no, seek_pls->index, seg_start_ts); + /* set segment now so we do not need to search again below */ seek_pls->cur_seq_no = seq_no; seek_pls->seek_stream_index = stream_subdemuxer_index; _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
