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,