demux_disc: soft-reload the slave demuxer on DVD discontinuities

A full slave reopen makes libavformat re-probe the stream, which eats
packets around the jump (losing the one-shot menu SPU) and re-bases the
timestamps. DVD cannot change codecs across a jump, so flushing the
slave and refreshing duration/chapters/edition is enough. BD keeps the
full reopen because codecs can change across titles.
This commit is contained in:
Kacper Michajłow committed 2026-08-26 01:47:41 +02:00
1 parent 582810ac1d
commit b0c4e8cab6
1 file changed
+44 -27
+44 -27
View File
@@ -353,6 +353,22 @@ static void sync_initial_edition(struct demuxer *demuxer)
}
}
static void refresh_disc_metadata(struct demuxer *demuxer)
{
double len;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) >= 1)
demux_set_duration(demuxer, len);
else
demux_set_duration(demuxer, -1);
// Old chapter metadata is not freed here, they are parented to demuxer.
demuxer->chapters = NULL;
demuxer->num_chapters = 0;
add_stream_chapters(demuxer);
sync_initial_edition(demuxer);
demux_lists_changed(demuxer);
}
static bool reopen_slave(struct demuxer *demuxer)
{
struct priv *p = demuxer->priv;
@@ -386,23 +402,27 @@ static bool reopen_slave(struct demuxer *demuxer)
p->slave_to_outer[n] = NULL;
sync_streams(demuxer);
refresh_disc_metadata(demuxer);
// Refresh duration / chapters / edition for the new playlist.
double len;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_TIME_LENGTH, &len) >= 1)
demux_set_duration(demuxer, len);
else
demux_set_duration(demuxer, -1);
return true;
}
for (int n = 0; n < demuxer->num_chapters; n++)
talloc_free(demuxer->chapters[n].metadata);
demuxer->num_chapters = 0;
add_stream_chapters(demuxer);
sync_initial_edition(demuxer);
demux_lists_changed(demuxer);
// Handle a disc-nav discontinuity (title/menu/cell jump)
static bool process_discontinuity(struct demuxer *demuxer, uint32_t new_id)
{
struct priv *p = demuxer->priv;
if (!p->is_dvd) {
// BD needs a full reopen (codecs can change across titles).
if (!reopen_slave(demuxer))
return false;
} else {
if (p->slave->desc->drop_buffers)
p->slave->desc->drop_buffers(p->slave);
refresh_disc_metadata(demuxer);
}
p->last_discontinuity_id = new_id;
p->seek_reinit = true;
return true;
}
@@ -416,14 +436,11 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
p->nav_active = nav.nav_active;
demux_set_nav_active(demuxer, nav.nav_active);
}
if (nav.discontinuity_id != p->last_discontinuity_id) {
MP_VERBOSE(demuxer, "discontinuity %u->%u, reopening slave\n",
if (!p->is_dvd && nav.discontinuity_id != p->last_discontinuity_id) {
MP_VERBOSE(demuxer, "discontinuity %u->%u, handling\n",
p->last_discontinuity_id, nav.discontinuity_id);
if (!reopen_slave(demuxer))
if (!process_discontinuity(demuxer, nav.discontinuity_id))
return false;
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav) >= 1)
p->last_discontinuity_id = nav.discontinuity_id;
p->seek_reinit = true;
}
}
@@ -434,18 +451,16 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
struct demux_packet *pkt = demux_read_any_packet(p->slave);
if (!pkt) {
// The slave can hit EOF mid-playback when the stream layer breaks
// its read at a disc-driven discontinuity.
// EOF is either a real still (hold the frame) or the one-shot drain
// EOF at a jump boundary; in the latter case resync and continue.
struct stream_nav_state nav2 = {0};
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav2) >= 1
&& nav2.discontinuity_id != p->last_discontinuity_id)
{
MP_VERBOSE(demuxer, "discontinuity %u->%u at EOF, reopening slave\n",
MP_VERBOSE(demuxer, "discontinuity %u->%u at EOF, handling\n",
p->last_discontinuity_id, nav2.discontinuity_id);
if (!reopen_slave(demuxer))
if (!process_discontinuity(demuxer, nav2.discontinuity_id))
return false;
p->last_discontinuity_id = nav2.discontinuity_id;
p->seek_reinit = true;
pkt = demux_read_any_packet(p->slave);
}
if (!pkt) {
@@ -457,8 +472,10 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
demux_update(p->slave, MP_NOPTS_VALUE);
if (p->seek_reinit)
if (p->seek_reinit) {
reset_pts(demuxer);
refresh_disc_metadata(demuxer);
}
int slave_index = pkt->stream;
if (demux_get_num_stream(p->slave) > p->slave_to_outer_count ||