stream_bluray: treat unannounced repositions as jumps

BD-J applets seek their player and restart playlists without any event
reaching the read loop, menu loop points do this constantly. Compare
bd_tell against the end of the previous read and treat a mismatch as a
jump. Checking before the read keeps the new position's head out of
the old slave demuxer, the forward-only stream cannot re-read it. A
read delivered together with a jump event can already carry data of
the new position, the same check drops it.
This commit is contained in:
Kacper Michajłow committed 2026-08-26 01:47:41 +02:00
1 parent 0c87e46c83
commit a47452aac0
1 file changed
+36
+36
View File
@@ -151,6 +151,8 @@ 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.
uint64_t next_read_pos; // expected bd_tell() of the next read
bool read_pos_known; // next_read_pos is valid
bool resync_owed; // jump settled, resync seek not acked yet
// Disc-driven audio/sub selection, mirrored from BD_EVENT_AUDIO_STREAM
@@ -505,6 +507,18 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len)
bool hopped = false;
while (!mp_cancel_test(s->cancel)) {
BD_EVENT ev;
// BD-J applets reposition the stream, sometimes with no event.
// Detect it before reading so the new position's head survives.
if (!hopped && b->read_pos_known && bd_tell(b->bd) != b->next_read_pos) {
MP_VERBOSE(s, "position warped (%"PRIu64" -> %"PRIu64"), "
"treating as a jump\n", b->next_read_pos, bd_tell(b->bd));
b->read_pos_known = false;
mp_mutex_lock(&b->overlay_lock);
if (b->discontinuity_id == disc_id)
b->discontinuity_id++;
mp_mutex_unlock(&b->overlay_lock);
hopped = true;
}
// Once a jump was detected, stop consuming data (it belongs to the
// new position and the slave demuxer must be reopened first); a
// zero-length read only polls the remaining events.
@@ -640,6 +654,26 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len)
}
if (n > 0) {
// A jump event popped by this read can mean the data already
// belongs to the new position. Drop non-contiguous reads and
// drain like any other jump.
uint64_t pos = bd_tell(b->bd);
if (b->read_pos_known && pos - n != b->next_read_pos) {
MP_VERBOSE(s, "read not contiguous (expected %"PRIu64", "
"got %"PRIu64"), treating as a jump\n",
b->next_read_pos, pos - n);
b->read_pos_known = false;
mp_mutex_lock(&b->overlay_lock);
if (b->discontinuity_id == disc_id)
b->discontinuity_id++;
mp_mutex_unlock(&b->overlay_lock);
hopped = true;
continue;
}
if (!b->read_pos_known)
MP_DBG(s, "reads resume at %"PRIu64"\n", pos - n);
b->next_read_pos = pos;
b->read_pos_known = true;
if (b->still_active) {
mp_mutex_lock(&b->overlay_lock);
b->still_active = false;
@@ -669,6 +703,7 @@ static int bluray_stream_fill_buffer(stream_t *s, void *buf, int len)
if (hopped) {
if (ev.event != BD_EVENT_NONE)
continue;
b->read_pos_known = false;
mp_mutex_lock(&b->overlay_lock);
b->resync_owed = true;
mp_mutex_unlock(&b->overlay_lock);
@@ -786,6 +821,7 @@ static int bluray_stream_control(stream_t *s, int cmd, void *arg)
double pts = *((double *) arg);
bd_seek_time(b->bd, BD_TIME_FROM_S(pts));
stream_drop_buffers(s);
b->read_pos_known = false;
// API makes it hard to determine seeking success
return STREAM_OK;
}