From 16896f33c03ef09d43717c21cc0b944ea6543660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sun, 23 Aug 2026 02:48:45 +0200 Subject: [PATCH] stream_dvdnav: fix seek positioning Menu-domain seeks: menu PGCs sequence intro/transition segments and the looping button segment as programs of one PGC, and the menu subpicture is a one-shot packet at a cell start. dvdnav's time search lands mid-cell (erratically so in menu VOBs, which often lack usable time maps), skipping the subpicture and leaving the menu without visible buttons until its next loop. Map menu seeks to program skips. Title seeks: dvdnav_time_search() interpolates the target from cell durations. On VBR content the landing can fall further short than the seek step, so repeated relative seeks converge to a fixed point and never advance. Use dvdnav_jump_to_sector_by_time(), which resolves through the VTS time maps, with dvdnav_time_search() as the fallback for titles without one. Accurate landings also let the hr-seek margin shrink. --- stream/stream_dvdnav.c | 63 ++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/stream/stream_dvdnav.c b/stream/stream_dvdnav.c index 8c0af5ea0b..879a500dab 100644 --- a/stream/stream_dvdnav.c +++ b/stream/stream_dvdnav.c @@ -777,6 +777,16 @@ static int fill_buffer(stream_t *s, void *buf, int max_len) return 0; } +static int64_t seek_landing_ticks(struct priv *priv, double d, double margin) +{ + int64_t tm = DVD_TIME_FROM_S(d - margin); + if (tm < 0) + tm = 0; + if (priv->duration > 0 && tm >= priv->duration) + tm = priv->duration - 1; + return tm; +} + static int control(stream_t *stream, int cmd, void *arg) { struct priv *priv = stream->priv; @@ -885,19 +895,43 @@ static int control(stream_t *stream, int cmd, void *arg) double *args = arg; double d = args[0]; // absolute target timestamp int flags = args[1]; // from SEEK_* flags (demux.h) - if (flags & SEEK_HR) - d -= 10; // fudge offset; it's a hack, because fuck libdvd* - int64_t tm = DVD_TIME_FROM_S(d); - if (tm < 0) - tm = 0; - if (priv->duration > 0 && tm >= priv->duration) - tm = priv->duration - 1; - uint32_t pos, len; - if (dvdnav_get_position(dvdnav, &pos, &len) != DVDNAV_STATUS_OK) - break; - MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm); - if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK) - break; + if (in_menu_domain(dvdnav)) { + // The menu subpicture (buttons, highlight) is a one-shot packet + // at its cell start, and dvdnav's time search lands mid-cell, + // past it, so the menu would show without buttons until its next + // loop. Map seeks to program skips, every landing is a program + // start where the display state is established. + double cur = DVD_TIME_TO_S(dvdnav_get_current_time(dvdnav)); + MP_VERBOSE(stream, "menu seek to %f (cur %f) -> %s program\n", + d, cur, d >= cur ? "next" : "prev"); + dvdnav_status_t r = d >= cur ? dvdnav_next_pg_search(dvdnav) + : dvdnav_prev_pg_search(dvdnav); + if (r != DVDNAV_STATUS_OK) + break; + } else { + uint32_t pos, len; + if (dvdnav_get_position(dvdnav, &pos, &len) != DVDNAV_STATUS_OK) + break; + // hr-seeks decode from the landing up to the exact target, so + // the landing must not overshoot it. Time-map landings are + // accurate to one map entry, the time_search fallback + // interpolates from cell durations and can be off by several + // seconds on VBR content. + int64_t tm = seek_landing_ticks(priv, d, flags & SEEK_HR ? 2 : 0); + MP_VERBOSE(stream, "seek to PTS %f (%"PRId64")\n", d, tm); + // The disc's time maps give accurate landings. Fall back to + // dvdnav's cell-duration interpolation for titles that lack + // them. + bool jumped = false; +#if DVDNAV_VERSION >= DVDNAV_VERSION_CODE(7, 0, 0) + jumped = dvdnav_jump_to_sector_by_time(dvdnav, tm, 0) == DVDNAV_STATUS_OK; +#endif + if (!jumped) { + tm = seek_landing_ticks(priv, d, flags & SEEK_HR ? 10 : 0); + if (dvdnav_time_search(dvdnav, tm) != DVDNAV_STATUS_OK) + break; + } + } // The seek reinitializes everything itself. Clear any held boundary // and coalesce the HOP_CHANNEL libdvdnav emits for the seek. mp_mutex_lock(&priv->lock); @@ -907,8 +941,9 @@ static int control(stream_t *stream, int cmd, void *arg) stream_drop_buffers(stream); d = DVD_TIME_TO_S(dvdnav_get_current_time(dvdnav)); MP_VERBOSE(stream, "landed at: %f\n", d); + uint32_t pos, len; if (dvdnav_get_position(dvdnav, &pos, &len) == DVDNAV_STATUS_OK) - MP_VERBOSE(stream, "block: %lu\n", (unsigned long)pos); + MP_VERBOSE(stream, "block: %" PRIu32 "\n", pos); return STREAM_OK; } case STREAM_CTRL_GET_NUM_ANGLES: {