When seeking in a long HLS stream (>26.5 hours), the 33-bit MPEG-TS PTS
clock wraps, causing the raw DTS in seeked segments to be lower than the
seek target. The seek packet filter used plain subtraction, so ts_diff
was always negative and every packet was dropped, producing empty output.

Fix by using av_compare_mod() in the 90kHz timebase (matching the existing
compare_ts_with_wrapdetect logic) instead of a microsecond diff.

Note: the analysis and code was generated mostly by Claude Code. I've updated
the Trac ticket (https://trac.ffmpeg.org/ticket/7673) with a way to reproduce
the issue, which this patch fixes. I've also used the patch on my actual
usecase, a 48-hour long stream from Twitch.

---
 libavformat/hls.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/libavformat/hls.c b/libavformat/hls.c
index b03201c690..f9083bc75f 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -2544,11 +2544,11 @@ static AVRational get_timebase(struct playlist *pls)
     return pls->ctx->streams[pls->pkt->stream_index]->time_base;
 }
 
-static int compare_ts_with_wrapdetect(int64_t ts_a, struct playlist *pls_a,
-                                      int64_t ts_b, struct playlist *pls_b)
+static int compare_ts_with_wrapdetect(int64_t ts_a, AVRational tb_a,
+                                      int64_t ts_b, AVRational tb_b)
 {
-    int64_t scaled_ts_a = av_rescale_q(ts_a, get_timebase(pls_a), 
MPEG_TIME_BASE_Q);
-    int64_t scaled_ts_b = av_rescale_q(ts_b, get_timebase(pls_b), 
MPEG_TIME_BASE_Q);
+    int64_t scaled_ts_a = av_rescale_q(ts_a, tb_a, MPEG_TIME_BASE_Q);
+    int64_t scaled_ts_b = av_rescale_q(ts_b, tb_b, MPEG_TIME_BASE_Q);
 
     return av_compare_mod(scaled_ts_a, scaled_ts_b, 1LL << 33);
 }
@@ -2626,9 +2626,8 @@ static int hls_read_packet(AVFormatContext *s, AVPacket 
*pkt)
                     }
 
                     tb = get_timebase(pls);
-                    ts_diff = av_rescale_rnd(pls->pkt->dts, AV_TIME_BASE,
-                                            tb.den, AV_ROUND_DOWN) -
-                            pls->seek_timestamp;
+                    ts_diff = compare_ts_with_wrapdetect(pls->pkt->dts, tb,
+                                                         pls->seek_timestamp, 
AV_TIME_BASE_Q);
                     if (ts_diff >= 0 && (pls->seek_flags  & AVSEEK_FLAG_ANY ||
                                         pls->pkt->flags & AV_PKT_FLAG_KEY)) {
                         pls->seek_timestamp = AV_NOPTS_VALUE;
@@ -2649,7 +2648,7 @@ static int hls_read_packet(AVFormatContext *s, AVPacket 
*pkt)
                 int64_t mindts  = minpls->pkt->dts;
 
                 if (dts == AV_NOPTS_VALUE ||
-                    (mindts != AV_NOPTS_VALUE && 
compare_ts_with_wrapdetect(dts, pls, mindts, minpls) < 0))
+                    (mindts != AV_NOPTS_VALUE && 
compare_ts_with_wrapdetect(dts, get_timebase(pls), mindts, 
get_timebase(minpls)) < 0))
                     minplaylist = i;
             }
         }
-- 
2.48.1

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

Reply via email to