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.
This commit is contained in:
Kacper Michajłow committed 2026-08-26 01:47:41 +02:00
1 parent 4a110f39cf
commit abbbffe0dd
3 files changed
+46 -15

No files matched your search

+26 -3
View File
@@ -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("-", &params, demuxer->cancel, demuxer->global);
if (!p->slave)
return -1;
+5 -12
View File
@@ -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.
+15
View File
@@ -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,