From abbbffe0dd24f3e3221e41ebaa55bac2ef7965bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sun, 23 Aug 2026 03:05:23 +0200 Subject: [PATCH] demux_disc: hold BD jump boundaries for the player's resync seek The resync seek that follows a disc-initiated jump must not reposition the VM. On BD the jump is handled out-of-band by reopening the slave demuxer on the packet-read path, and the VM already sits at the jump destination. Repositioning it with the (stale) seek target time would hop to the wrong place. Adopting the discontinuity counter here would leave the old slave demuxer parsing the new playlist's data with the wrong codec context. Flush our side only, and keep the counter stale so the read path still performs the reopen. --- demux/demux_disc.c | 29 ++++++++++++++++++++++++++--- player/discnav.c | 17 +++++------------ stream/stream_bluray.c | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/demux/demux_disc.c b/demux/demux_disc.c index d24e223606..0f9336f3a9 100644 --- a/demux/demux_disc.c +++ b/demux/demux_disc.c @@ -444,12 +444,21 @@ static void d_seek(demuxer_t *demuxer, double seek_pts, int flags) struct stream_nav_state nav = {0}; bool have_nav = stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav) >= 1; bool resync = have_nav && nav.drain_pending; + // The BD equivalent of a jump resync, the slave reopen for it is still + // pending on the packet-read path. + bool bd_jump = have_nav && !p->is_dvd && nav.discontinuity_id != p->last_discontinuity_id; p->skip_audio_until = MP_NOPTS_VALUE; if (resync) { MP_VERBOSE(demuxer, "resync seek at jump boundary (disc id %u)\n", nav.discontinuity_id); + } else if (bd_jump) { + // The disc VM is already at the jump destination. Don't reposition + // it with a stale time, just flush our side. The read path performs + // the slave reopen when it sees the counter mismatch. + MP_VERBOSE(demuxer, "resync seek for disc jump (id %u), " + "slave reopen pending\n", nav.discontinuity_id); } else { // In slideshow titles the target's still lies at the preceding // chapter (cell) start and can't be read backwards from a mid-cell @@ -486,7 +495,11 @@ static void d_seek(demuxer_t *demuxer, double seek_pts, int flags) refresh_disc_metadata(demuxer); } - if (stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav) >= 1) + // A DVD jump is fully handled by this seek, adopt the current counter. + // A BD jump additionally needs the slave reopened, which only the + // packet-read path does. Keep the counter stale so that still happens, + // else the old slave keeps parsing the new playlist's data. + if (p->is_dvd && stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav) >= 1) p->last_discontinuity_id = nav.discontinuity_id; clear_dvd_sub_holds(p); @@ -815,8 +828,11 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt demux_set_nav_active(demuxer, nav.nav_active); } // DVD holds an EOF at jump boundaries and the player resyncs - // through the seek path. BD has no in-band boundary, handle it here. + // through the seek path. BD has no in-band boundary, handle it here, + // after the resync seek. if (!p->is_dvd && nav.discontinuity_id != p->last_discontinuity_id) { + if (nav.drain_pending) + return false; MP_VERBOSE(demuxer, "discontinuity %u->%u, handling\n", p->last_discontinuity_id, nav.discontinuity_id); if (!process_discontinuity(demuxer, nav.discontinuity_id)) @@ -863,7 +879,9 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt // player resyncs it via the seek path), or (BD) an out-of-band jump. struct stream_nav_state nav2 = {0}; bool have_nav2 = stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav2) >= 1; - if (have_nav2 && !p->is_dvd && nav2.discontinuity_id != p->last_discontinuity_id) { + if (have_nav2 && !p->is_dvd && !nav2.drain_pending && + nav2.discontinuity_id != p->last_discontinuity_id) + { MP_VERBOSE(demuxer, "discontinuity %u->%u at EOF, handling\n", p->last_discontinuity_id, nav2.discontinuity_id); if (!process_discontinuity(demuxer, nav2.discontinuity_id)) @@ -1113,6 +1131,11 @@ static int d_open(demuxer_t *demuxer, enum demux_check check) stream_read_peek(demuxer->stream, &(char){0}, 1); reset_pts(demuxer); + // Boundaries settled before the first slave open predate any player + // state to resync, release them so the probe can read. + if (!p->is_dvd) + stream_control(demuxer->stream, STREAM_CTRL_NAV_DRAIN_ACK, NULL); + p->slave = demux_open_url("-", ¶ms, demuxer->cancel, demuxer->global); if (!p->slave) return -1; diff --git a/player/discnav.c b/player/discnav.c index 04b803c1e0..e10f8b9f16 100644 --- a/player/discnav.c +++ b/player/discnav.c @@ -50,11 +50,7 @@ struct disc_nav_state { uint32_t last_overlay_change_id; bool overlay_change_seen; - // Last observed disc-nav discontinuity counter (bumped by the stream - // backend on user nav actions and on libbluray/libdvdnav-internal - // playlist/title/cell transitions). - uint32_t last_discontinuity_id; - bool discontinuity_seen; + bool drain_was_pending; // last poll saw a boundary awaiting resync // DVD-only: dvd_subtitle track we force-selected for the menu, and the // selection it displaced (restored on menu close). @@ -274,14 +270,11 @@ static void check_async_discontinuity(struct MPContext *mpctx, struct stream *s, struct disc_nav_state *st = get_state(mpctx); if (!mpctx->demuxer) return; - if (!st->discontinuity_seen) { - st->last_discontinuity_id = nav->discontinuity_id; - st->discontinuity_seen = true; + + bool new_drain = nav->drain_pending && !st->drain_was_pending; + st->drain_was_pending = nav->drain_pending; + if (!new_drain) return; - } - if (nav->discontinuity_id == st->last_discontinuity_id) - return; - st->last_discontinuity_id = nav->discontinuity_id; // The disc jumped on its own, resync through the regular seek path. // demux_disc releases the held boundary instead of repositioning the VM. diff --git a/stream/stream_bluray.c b/stream/stream_bluray.c index de83df3f31..8daa3b3353 100644 --- a/stream/stream_bluray.c +++ b/stream/stream_bluray.c @@ -151,6 +151,7 @@ struct bluray_priv_s { uint32_t discontinuity_id; // bumped on actions that may hop (SELECT...) bool data_delivered; // any byte returned from fill_buffer yet bool still_active; // holding an indefinite still. + bool resync_owed; // jump settled, resync seek not acked yet // Disc-driven audio/sub selection, mirrored from BD_EVENT_AUDIO_STREAM // and BD_EVENT_PG_TEXTST{,_STREAM}. The numbers are 1-based libbluray @@ -493,8 +494,13 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len) mp_mutex_lock(&b->overlay_lock); uint32_t disc_id = b->discontinuity_id; + bool resync_owed = b->resync_owed; mp_mutex_unlock(&b->overlay_lock); + // Resync pending, skip the read. + if (resync_owed) + return 0; + int idle_reads = 0; bool hopped = false; while (!mp_cancel_test(s->cancel)) { @@ -663,6 +669,9 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len) if (hopped) { if (ev.event != BD_EVENT_NONE) continue; + mp_mutex_lock(&b->overlay_lock); + b->resync_owed = true; + mp_mutex_unlock(&b->overlay_lock); MP_VERBOSE(s, "jump settled (id %u), EOF for demuxer resync\n", b->discontinuity_id); return 0; @@ -775,6 +784,11 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) // API makes it hard to determine seeking success return STREAM_OK; } + case STREAM_CTRL_NAV_DRAIN_ACK: + mp_mutex_lock(&b->overlay_lock); + b->resync_owed = false; + mp_mutex_unlock(&b->overlay_lock); + return STREAM_OK; case STREAM_CTRL_GET_NUM_ANGLES: { mp_mutex_lock(&b->overlay_lock); const BLURAY_TITLE_INFO *ti = b->title_info; @@ -964,6 +978,7 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg) .src_h = MPMAX(b->ig.disp_h, b->pg.disp_h), .change_id = b->nav_change_id, .discontinuity_id = b->discontinuity_id, + .drain_pending = b->resync_owed, .no_audio = no_audio, .active_audio_id = audio_pid, .active_sub_id = sub_pid,