This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit f6fa0d3fdafc8cd8224c5fe9dc974bd5ad2c9a5e
Author:     Kacper Michajłow <[email protected]>
AuthorDate: Wed Jul 8 03:57:33 2026 +0200
Commit:     Kacper Michajłow <[email protected]>
CommitDate: Tue Jul 28 18:01:18 2026 +0200

    avformat/hls: refine first_timestamp from the first segment's packets
    
    first_timestamp is taken from the first packet in mux order, which is
    not necessarily the earliest timestamp in the segment. Lower it while
    packets of that segment are still arriving, so the seek timeline is
    anchored at the true segment start.
    
    Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavformat/hls.c | 50 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/libavformat/hls.c b/libavformat/hls.c
index 569bc889e8..e8533aa93f 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -131,6 +131,7 @@ struct playlist {
     int broken;
     int64_t cur_seq_no;
     int64_t last_seq_no;
+    int64_t first_read_seq_no;
     int m3u8_hold_counters;
     int64_t cur_seg_offset;
     int64_t last_load_time;
@@ -232,6 +233,7 @@ typedef struct HLSContext {
     int first_packet;
     int64_t first_timestamp;
     struct playlist *first_timestamp_pls;
+    int first_timestamp_locked;
     int64_t cur_timestamp;
     AVIOInterruptCB *interrupt_callback;
     AVDictionary *avio_opts;
@@ -355,6 +357,7 @@ static struct playlist *new_playlist(HLSContext *c, const 
char *url,
     av_strlcpy(pls->url, abs_url, sizeof(pls->url));
     pls->ts_offset = AV_NOPTS_VALUE;
     pls->seek_timestamp = AV_NOPTS_VALUE;
+    pls->first_read_seq_no = -1;
 
     pls->is_id3_timestamped = -1;
     pls->id3_mpegts_timestamp = AV_NOPTS_VALUE;
@@ -1812,6 +1815,8 @@ restart:
         }
         segment_retries = 0;
         just_opened = 1;
+        if (v->first_read_seq_no < 0)
+            v->first_read_seq_no = v->cur_seq_no;
     }
 
     if (c->http_multiple == -1) {
@@ -1901,6 +1906,8 @@ static int read_data_subtitle_segment(void *opaque, 
uint8_t *buf, int buf_size)
                    v->index);
             return ret;
         }
+        if (v->first_read_seq_no < 0)
+            v->first_read_seq_no = v->cur_seq_no;
     }
 
     return read_from_url(v, seg, buf, buf_size);
@@ -2294,6 +2301,7 @@ static int hls_read_header(AVFormatContext *s)
     c->first_packet = 1;
     c->first_timestamp = AV_NOPTS_VALUE;
     c->first_timestamp_pls = NULL;
+    c->first_timestamp_locked = 0;
     c->cur_timestamp = AV_NOPTS_VALUE;
 
     if ((ret = ffio_copy_url_options(s->pb, &c->avio_opts)) < 0)
@@ -2621,6 +2629,7 @@ static int recheck_discard_flags(AVFormatContext *s, int 
first)
             pls->needed = 1;
             changed = 1;
             pls->cur_seq_no = select_cur_seq_no(c, pls);
+            pls->first_read_seq_no = -1;
             pls->pb.pub.eof_reached = 0;
             if (c->cur_timestamp != AV_NOPTS_VALUE) {
                 /* catch up */
@@ -2744,10 +2753,14 @@ static int hls_read_packet(AVFormatContext *s, AVPacket 
*pkt)
                     fill_timing_for_id3_timestamped_stream(pls);
                 }
 
-                if (pls->ts_offset == AV_NOPTS_VALUE &&
-                    pls->pkt->dts    != AV_NOPTS_VALUE) {
+                if (pls->pkt->dts != AV_NOPTS_VALUE &&
+                    (pls->ts_offset == AV_NOPTS_VALUE ||
+                     (!c->first_timestamp_locked &&
+                      c->first_timestamp_pls == pls &&
+                      pls->cur_seq_no <= pls->first_read_seq_no + 1))) {
+                    int adjusted = 0;
                     /* Packet timestamp rebased onto the start of the segment 
list. */
-                    int64_t seg_idx = pls->cur_seq_no - pls->start_seq_no;
+                    int64_t seg_idx = pls->first_read_seq_no - 
pls->start_seq_no;
                     int64_t ts = av_rescale_q(pls->pkt->pts != AV_NOPTS_VALUE ?
                                               pls->pkt->pts : pls->pkt->dts,
                                               get_timebase(pls), 
AV_TIME_BASE_Q);
@@ -2758,18 +2771,35 @@ static int hls_read_packet(AVFormatContext *s, AVPacket 
*pkt)
                     if (c->first_timestamp == AV_NOPTS_VALUE) {
                         c->first_timestamp = ts;
                         c->first_timestamp_pls = pls;
+                        adjusted = 1;
+                    } else if (c->first_timestamp_pls == pls) {
+                        /* first_timestamp came from the first packet in mux
+                         * order, but another stream in the same segment may
+                         * start earlier. Lower the estimate while packets of
+                         * that segment are still arriving. */
+                        int64_t delta = c->first_timestamp - ts;
+
+                        if (delta > 0 && delta <= pls->target_duration) {
+                            c->first_timestamp = ts;
+                            for (int k = 0; k < c->n_playlists; k++)
+                                if (c->playlists[k]->ts_offset != 
AV_NOPTS_VALUE)
+                                    c->playlists[k]->ts_offset += delta;
+                            adjusted = 1;
+                        }
+                    }
+
+                    if (pls->ts_offset == AV_NOPTS_VALUE)
+                        pls->ts_offset = ts - c->first_timestamp;
 
-                        /* start_time is the start of the segment list, the
-                         * origin of the timeline used for seeking. */
+                    if (adjusted) {
                         for (unsigned k = 0; k < s->nb_streams; k++)
                             s->streams[k]->start_time =
-                                av_rescale_q(ts, AV_TIME_BASE_Q,
+                                av_rescale_q(c->first_timestamp, 
AV_TIME_BASE_Q,
                                              s->streams[k]->time_base);
                         av_log(s, AV_LOG_DEBUG,
                                "First timestamp %"PRId64" from playlist %d\n",
-                               ts, pls->index);
+                               c->first_timestamp, pls->index);
                     }
-                    pls->ts_offset = ts - c->first_timestamp;
                 }
 
                 seg = current_segment(pls);
@@ -2960,6 +2990,9 @@ static int hls_read_seek(AVFormatContext *s, int 
stream_index,
            " of playlist %d (start %"PRId64")\n",
            seek_timestamp, seq_no, seek_pls->index, seg_start_ts);
 
+    /* Keep timeline start fixed from now on. */
+    c->first_timestamp_locked = 1;
+
     /* 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;
@@ -2975,6 +3008,7 @@ static int hls_read_seek(AVFormatContext *s, int 
stream_index,
         AVIOContext *const pb = &pls->pb.pub;
         ff_format_io_close(pls->parent, &pls->input);
         pls->input_read_done = 0;
+        pls->first_read_seq_no = -1;
         pls->input_reuse = 0;
         ff_format_io_close(pls->parent, &pls->input_next);
         pls->input_next_requested = 0;

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to