demux_disc: keep one-shot DVD menu subpictures deliverable
Keep DVD SPU streams always enabled at the slave level and retain the last subpicture per substream. Re-deliver the retained copy when a sub track is (re)selected and when the player resets playback state.
This commit is contained in:
1 parent
47a53910a5
commit
8356c7f674
5 files changed
+167
-23
No files matched your search
@@ -243,6 +243,7 @@ struct demux_internal {
|
||||
bool back_any_need_recheck; // at least 1 ds->back_need_recheck set
|
||||
|
||||
bool tracks_switched; // thread needs to inform demuxer of this
|
||||
bool nav_refresh; // thread needs to call desc->nav_refresh
|
||||
|
||||
atomic_bool seeking; // there's a seek queued; read unlocked
|
||||
// by demux_read_interrupted()
|
||||
@@ -1270,6 +1271,21 @@ void demux_drive_nav(struct demuxer *demuxer)
|
||||
mp_mutex_unlock(&in->lock);
|
||||
}
|
||||
|
||||
// Re-queue sticky packets (e.g. the DVD menu subpicture) for re-delivery.
|
||||
void demux_nav_refresh(struct demuxer *demuxer)
|
||||
{
|
||||
struct demux_internal *in = demuxer->in;
|
||||
mp_assert(demuxer == in->d_user);
|
||||
|
||||
mp_mutex_lock(&in->lock);
|
||||
if (in->d_thread->desc->nav_refresh) {
|
||||
in->nav_refresh = true;
|
||||
in->reading = true;
|
||||
mp_cond_signal(&in->wakeup);
|
||||
}
|
||||
mp_mutex_unlock(&in->lock);
|
||||
}
|
||||
|
||||
const char *stream_type_name(enum stream_type type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -2550,6 +2566,18 @@ static void execute_trackswitch(struct demux_internal *in)
|
||||
mp_mutex_lock(&in->lock);
|
||||
}
|
||||
|
||||
static void execute_nav_refresh(struct demux_internal *in)
|
||||
{
|
||||
in->nav_refresh = false;
|
||||
|
||||
mp_mutex_unlock(&in->lock);
|
||||
|
||||
if (in->d_thread->desc->nav_refresh)
|
||||
in->d_thread->desc->nav_refresh(in->d_thread);
|
||||
|
||||
mp_mutex_lock(&in->lock);
|
||||
}
|
||||
|
||||
static void execute_seek(struct demux_internal *in)
|
||||
{
|
||||
int flags = in->seek_flags;
|
||||
@@ -2668,6 +2696,10 @@ static bool thread_work(struct demux_internal *in)
|
||||
execute_trackswitch(in);
|
||||
return true;
|
||||
}
|
||||
if (in->nav_refresh) {
|
||||
execute_nav_refresh(in);
|
||||
return true;
|
||||
}
|
||||
if (in->need_back_seek) {
|
||||
perform_backward_seek(in);
|
||||
return true;
|
||||
|
||||
@@ -147,6 +147,8 @@ typedef struct demuxer_desc {
|
||||
// will be repeated.
|
||||
bool (*read_packet)(struct demuxer *demuxer, struct demux_packet **pkt);
|
||||
void (*drop_buffers)(struct demuxer *demuxer);
|
||||
// Optional. Re-queue retained sticky packets (DVD menu subpicture).
|
||||
void (*nav_refresh)(struct demuxer *demuxer);
|
||||
void (*close)(struct demuxer *demuxer);
|
||||
void (*seek)(struct demuxer *demuxer, double rel_seek_secs, int flags);
|
||||
void (*switched_tracks)(struct demuxer *demuxer);
|
||||
@@ -326,6 +328,7 @@ void demux_stop_thread(struct demuxer *demuxer);
|
||||
void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *ctx);
|
||||
void demux_start_prefetch(struct demuxer *demuxer);
|
||||
void demux_drive_nav(struct demuxer *demuxer);
|
||||
void demux_nav_refresh(struct demuxer *demuxer);
|
||||
|
||||
bool demux_cancel_test(struct demuxer *demuxer);
|
||||
bool demux_read_interrupted(struct demuxer *demuxer);
|
||||
|
||||
+93
-3
@@ -25,6 +25,7 @@
|
||||
#include "stream/stream.h"
|
||||
#include "video/mp_image.h"
|
||||
#include "demux.h"
|
||||
#include "packet.h"
|
||||
#include "stheader.h"
|
||||
|
||||
#include "video/csputils.h"
|
||||
@@ -52,6 +53,15 @@ struct priv {
|
||||
// 0x20, carrying the disc-level CLUT as extradata.
|
||||
struct sh_stream *dvd_subs[MAX_DVD_SPU_STREAMS];
|
||||
|
||||
// DVD-only: retain the last SPU packet per substream. The menu subpicture
|
||||
// is one-shot on an unseekable stream; re-deliver it on (re)selection and
|
||||
// demux_nav_refresh(), like a refresh seek for ordinary streams.
|
||||
struct dvd_sub_hold {
|
||||
struct demux_packet *pkt; // clone, with playback-rebased timestamps
|
||||
struct sh_stream *sh; // outer stream it belongs to
|
||||
bool pending; // re-deliver on next read
|
||||
} dvd_sub_hold[MAX_DVD_SPU_STREAMS];
|
||||
|
||||
// Used to rewrite the raw MPEG timestamps to playback time.
|
||||
double base_time; // playback display start time of current segment
|
||||
double base_dts; // packet DTS that maps to base_time
|
||||
@@ -63,6 +73,33 @@ struct priv {
|
||||
bool is_dvd, is_cdda, is_bd;
|
||||
};
|
||||
|
||||
static void clear_dvd_sub_holds(struct priv *p)
|
||||
{
|
||||
for (int i = 0; i < MAX_DVD_SPU_STREAMS; i++) {
|
||||
talloc_free(p->dvd_sub_hold[i].pkt);
|
||||
p->dvd_sub_hold[i] = (struct dvd_sub_hold){0};
|
||||
}
|
||||
}
|
||||
|
||||
// Mark retained subpictures of selected streams for re-delivery.
|
||||
static void arm_dvd_sub_holds(struct priv *p)
|
||||
{
|
||||
for (int i = 0; i < MAX_DVD_SPU_STREAMS; i++) {
|
||||
struct dvd_sub_hold *h = &p->dvd_sub_hold[i];
|
||||
if (h->pkt && h->sh && demux_stream_is_selected(h->sh))
|
||||
h->pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Whether the slave stream mapped to this outer stream should be enabled.
|
||||
// DVD SPU streams always stay enabled: the one-shot menu subpicture must be
|
||||
// captured into dvd_sub_hold even before any sub track is selected.
|
||||
static bool slave_stream_enabled(struct priv *p, struct sh_stream *outer)
|
||||
{
|
||||
return demux_stream_is_selected(outer) ||
|
||||
(p->is_dvd && outer->type == STREAM_SUB);
|
||||
}
|
||||
|
||||
static void reselect_streams(demuxer_t *demuxer)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
@@ -72,8 +109,15 @@ static void reselect_streams(demuxer_t *demuxer)
|
||||
for (int n = 0; n < num_slave && n < p->slave_to_outer_count; n++) {
|
||||
struct sh_stream *outer = p->slave_to_outer[n];
|
||||
demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
|
||||
MP_NOPTS_VALUE, outer && demux_stream_is_selected(outer));
|
||||
MP_NOPTS_VALUE, outer && slave_stream_enabled(p, outer));
|
||||
}
|
||||
arm_dvd_sub_holds(p);
|
||||
}
|
||||
|
||||
static void d_nav_refresh(demuxer_t *demuxer)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
arm_dvd_sub_holds(p);
|
||||
}
|
||||
|
||||
static void get_disc_lang(struct stream *stream, struct sh_stream *sh, bool dvd)
|
||||
@@ -266,7 +310,7 @@ static void sync_streams(struct demuxer *demuxer)
|
||||
for (int n = 0; n < num_slave; n++) {
|
||||
struct sh_stream *outer = p->slave_to_outer[n];
|
||||
demuxer_select_track(p->slave, demux_get_stream(p->slave, n),
|
||||
MP_NOPTS_VALUE, outer && demux_stream_is_selected(outer));
|
||||
MP_NOPTS_VALUE, outer && slave_stream_enabled(p, outer));
|
||||
}
|
||||
|
||||
// Mirror slave sh_stream_group onto the outer sh_streams. This is needed
|
||||
@@ -318,6 +362,8 @@ static void d_seek(demuxer_t *demuxer, double seek_pts, int flags)
|
||||
if (p->slave && p->slave->desc->drop_buffers)
|
||||
p->slave->desc->drop_buffers(p->slave);
|
||||
|
||||
clear_dvd_sub_holds(p);
|
||||
|
||||
p->seek_reinit = true;
|
||||
}
|
||||
|
||||
@@ -384,6 +430,7 @@ static bool reopen_slave(struct demuxer *demuxer)
|
||||
|
||||
MP_VERBOSE(demuxer, "reopening slave demuxer\n");
|
||||
demux_free(p->slave);
|
||||
clear_dvd_sub_holds(p);
|
||||
// Discard anything the stream wrapper buffered before the disc-nav
|
||||
// discontinuity, and restart byte positions at 0: the post-jump data is
|
||||
// a new stream, and the fresh slave expects to probe it from the start
|
||||
@@ -417,6 +464,7 @@ static bool process_discontinuity(struct demuxer *demuxer, uint32_t new_id)
|
||||
if (!reopen_slave(demuxer))
|
||||
return false;
|
||||
} else {
|
||||
clear_dvd_sub_holds(p);
|
||||
if (p->slave->desc->drop_buffers)
|
||||
p->slave->desc->drop_buffers(p->slave);
|
||||
refresh_disc_metadata(demuxer);
|
||||
@@ -430,8 +478,10 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
|
||||
bool menu_active = false;
|
||||
struct stream_nav_state nav = {0};
|
||||
if (stream_control(demuxer->stream, STREAM_CTRL_GET_NAV_STATE, &nav) >= 1) {
|
||||
menu_active = nav.menu_active;
|
||||
if (nav.nav_active != p->nav_active) {
|
||||
p->nav_active = nav.nav_active;
|
||||
demux_set_nav_active(demuxer, nav.nav_active);
|
||||
@@ -444,6 +494,26 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
|
||||
}
|
||||
}
|
||||
|
||||
// Re-deliver a retained menu subpicture.
|
||||
if (menu_active) {
|
||||
for (int i = 0; i < MAX_DVD_SPU_STREAMS; i++) {
|
||||
struct dvd_sub_hold *h = &p->dvd_sub_hold[i];
|
||||
if (!h->pending || !h->pkt || !h->sh ||
|
||||
!demux_stream_is_selected(h->sh))
|
||||
continue;
|
||||
h->pending = false;
|
||||
struct demux_packet *rp = demux_copy_packet(demuxer->packet_pool,
|
||||
h->pkt);
|
||||
if (!rp)
|
||||
continue;
|
||||
MP_DBG(demuxer, "re-delivering held dvd sub 0x%x (pts=%f)\n",
|
||||
h->sh->demuxer_id, rp->pts);
|
||||
rp->stream = h->sh->index;
|
||||
*out_pkt = rp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// A discontinuity reopen failed and the retry above hasn't succeeded
|
||||
// yet; report EOF until the stream settles and the reopen goes through.
|
||||
if (!p->slave)
|
||||
@@ -487,7 +557,8 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
|
||||
|
||||
struct sh_stream *sh = slave_index < p->slave_to_outer_count
|
||||
? p->slave_to_outer[slave_index] : NULL;
|
||||
if (!sh || !demux_stream_is_selected(sh)) {
|
||||
bool dvd_sub = sh && p->is_dvd && sh->type == STREAM_SUB;
|
||||
if (!sh || (!demux_stream_is_selected(sh) && !dvd_sub)) {
|
||||
talloc_free(pkt);
|
||||
return true;
|
||||
}
|
||||
@@ -543,6 +614,23 @@ static bool d_read_packet(struct demuxer *demuxer, struct demux_packet **out_pkt
|
||||
MP_TRACE(demuxer, "mapped pkt: type=%d pts=%f dts=%f\n",
|
||||
sh->type, pkt->pts, pkt->dts);
|
||||
|
||||
if (dvd_sub) {
|
||||
int idx = sh->demuxer_id - 0x20;
|
||||
if (idx >= 0 && idx < MAX_DVD_SPU_STREAMS) {
|
||||
struct dvd_sub_hold *h = &p->dvd_sub_hold[idx];
|
||||
talloc_free(h->pkt);
|
||||
h->pkt = demux_copy_packet(demuxer->packet_pool, pkt);
|
||||
h->sh = sh;
|
||||
h->pending = false;
|
||||
}
|
||||
MP_DBG(demuxer, "dvd sub 0x%x packet: pts=%f selected=%d (held)\n",
|
||||
sh->demuxer_id, pkt->pts, demux_stream_is_selected(sh));
|
||||
if (!demux_stream_is_selected(sh)) {
|
||||
talloc_free(pkt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*out_pkt = pkt;
|
||||
return 1;
|
||||
}
|
||||
@@ -665,6 +753,7 @@ static int d_open(demuxer_t *demuxer, enum demux_check check)
|
||||
static void d_close(demuxer_t *demuxer)
|
||||
{
|
||||
struct priv *p = demuxer->priv;
|
||||
clear_dvd_sub_holds(p);
|
||||
demux_free(p->slave);
|
||||
}
|
||||
|
||||
@@ -676,4 +765,5 @@ const demuxer_desc_t demuxer_desc_disc = {
|
||||
.close = d_close,
|
||||
.seek = d_seek,
|
||||
.switched_tracks = reselect_streams,
|
||||
.nav_refresh = d_nav_refresh,
|
||||
};
|
||||
+36
-20
@@ -56,11 +56,10 @@ struct disc_nav_state {
|
||||
uint32_t last_discontinuity_id;
|
||||
bool discontinuity_seen;
|
||||
|
||||
// DVD-only: when a menu opens with no DVD sub track selected, we
|
||||
// transiently select one so the menu graphic renders through the normal
|
||||
// sd_lavc path. menu_selected_track remembers what we selected so we
|
||||
// can deselect it again when the menu closes.
|
||||
// DVD-only: dvd_subtitle track we force-selected for the menu, and the
|
||||
// selection it displaced (restored on menu close).
|
||||
struct track *menu_selected_track;
|
||||
struct track *menu_saved_track;
|
||||
|
||||
// Last disc-driven audio/sub/angle we acted on.
|
||||
int last_audio_id;
|
||||
@@ -76,6 +75,13 @@ struct disc_nav_state {
|
||||
bool menu_active_seen;
|
||||
};
|
||||
|
||||
static bool is_dvd_sub_track(struct track *t)
|
||||
{
|
||||
return t && t->type == STREAM_SUB && t->stream && t->stream->codec &&
|
||||
t->stream->codec->codec &&
|
||||
strcmp(t->stream->codec->codec, "dvd_subtitle") == 0;
|
||||
}
|
||||
|
||||
static struct disc_nav_state *get_state(struct MPContext *mpctx)
|
||||
{
|
||||
if (!mpctx->disc_nav)
|
||||
@@ -146,12 +152,13 @@ bool disc_nav_mouse_pos_to_src(struct MPContext *mpctx, int src_w, int src_h,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Push the current menu/highlight state to the dvd_subtitle decoders.
|
||||
static void push_dvd_overlay(struct MPContext *mpctx,
|
||||
struct stream_nav_state *nav, bool visible)
|
||||
{
|
||||
struct mp_dvdnav_hli hli = {
|
||||
.show = visible,
|
||||
.change_id = visible ? nav->change_id : 0,
|
||||
.change_id = nav->change_id,
|
||||
};
|
||||
if (visible) {
|
||||
hli.x = nav->hl_x;
|
||||
@@ -162,9 +169,7 @@ static void push_dvd_overlay(struct MPContext *mpctx,
|
||||
}
|
||||
for (int n = 0; n < mpctx->num_tracks; n++) {
|
||||
struct track *t = mpctx->tracks[n];
|
||||
if (t->type != STREAM_SUB || !t->d_sub || !t->stream ||
|
||||
!t->stream->codec ||
|
||||
strcmp(t->stream->codec->codec, "dvd_subtitle") != 0)
|
||||
if (!is_dvd_sub_track(t) || !t->d_sub)
|
||||
continue;
|
||||
sub_control(t->d_sub, SD_CTRL_APPLY_DVDNAV, &hli);
|
||||
}
|
||||
@@ -290,6 +295,8 @@ static void check_async_discontinuity(struct MPContext *mpctx, struct stream *s,
|
||||
st->last_discontinuity_id = nav->discontinuity_id;
|
||||
reset_playback_state(mpctx);
|
||||
demux_flush(mpctx->demuxer);
|
||||
// Re-queue the retained menu subpicture the flush may have destroyed.
|
||||
demux_nav_refresh(mpctx->demuxer);
|
||||
}
|
||||
|
||||
static struct track *find_track_by_demuxer_id(struct MPContext *mpctx,
|
||||
@@ -393,38 +400,45 @@ static void ensure_menu_sub_selection(struct MPContext *mpctx, bool menu_on)
|
||||
struct track *cur = mpctx->current_track[0][STREAM_SUB];
|
||||
|
||||
// If the track list got rebuilt under us (e.g. another file/disc loaded
|
||||
// mid-session) the cached pointer could be stale. Trust only what we
|
||||
// mid-session) the cached pointers could be stale. Trust only what we
|
||||
// can still see in mpctx->tracks.
|
||||
if (st->menu_selected_track) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
struct track **slot = i ? &st->menu_saved_track : &st->menu_selected_track;
|
||||
if (!*slot)
|
||||
continue;
|
||||
bool still_present = false;
|
||||
for (int n = 0; n < mpctx->num_tracks; n++) {
|
||||
if (mpctx->tracks[n] == st->menu_selected_track) {
|
||||
if (mpctx->tracks[n] == *slot) {
|
||||
still_present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!still_present)
|
||||
st->menu_selected_track = NULL;
|
||||
*slot = NULL;
|
||||
}
|
||||
|
||||
if (menu_on) {
|
||||
if (cur || st->menu_selected_track)
|
||||
// Re-checked every frame; other selectors (stream auto-select, slave
|
||||
// reopens) can change the sub under us.
|
||||
if (st->menu_selected_track && cur == st->menu_selected_track)
|
||||
return;
|
||||
// A dvd_subtitle track is already active; nothing to force.
|
||||
if (is_dvd_sub_track(cur))
|
||||
return;
|
||||
if (mpctx->opts->stream_id[0][STREAM_SUB] == -2)
|
||||
return;
|
||||
struct track *pick = NULL;
|
||||
for (int n = 0; n < mpctx->num_tracks; n++) {
|
||||
struct track *t = mpctx->tracks[n];
|
||||
if (t->type == STREAM_SUB && t->stream && t->stream->codec &&
|
||||
t->stream->codec->codec &&
|
||||
strcmp(t->stream->codec->codec, "dvd_subtitle") == 0)
|
||||
{
|
||||
pick = t;
|
||||
if (is_dvd_sub_track(mpctx->tracks[n])) {
|
||||
pick = mpctx->tracks[n];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pick)
|
||||
return;
|
||||
// Remember the displaced selection only on the first override.
|
||||
if (!st->menu_selected_track)
|
||||
st->menu_saved_track = cur;
|
||||
mp_switch_track_n(mpctx, 0, STREAM_SUB, pick, 0);
|
||||
st->menu_selected_track = pick;
|
||||
} else {
|
||||
@@ -432,8 +446,9 @@ static void ensure_menu_sub_selection(struct MPContext *mpctx, bool menu_on)
|
||||
return;
|
||||
// Only revert if our override is still the active selection.
|
||||
if (cur == st->menu_selected_track)
|
||||
mp_switch_track_n(mpctx, 0, STREAM_SUB, NULL, 0);
|
||||
mp_switch_track_n(mpctx, 0, STREAM_SUB, st->menu_saved_track, 0);
|
||||
st->menu_selected_track = NULL;
|
||||
st->menu_saved_track = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +474,7 @@ void disc_nav_update(struct MPContext *mpctx)
|
||||
st->bd_last_change_id = 0;
|
||||
st->bd_last_vo_res = (struct mp_osd_res){0};
|
||||
st->menu_selected_track = NULL;
|
||||
st->menu_saved_track = NULL;
|
||||
st->overlay_change_seen = false;
|
||||
if (st->menu_active_seen && st->last_menu_active) {
|
||||
st->last_menu_active = false;
|
||||
|
||||
@@ -246,6 +246,9 @@ void reset_playback_state(struct MPContext *mpctx)
|
||||
reset_audio_state(mpctx);
|
||||
reset_subtitle_state(mpctx);
|
||||
|
||||
if (mpctx->demuxer)
|
||||
demux_nav_refresh(mpctx->demuxer);
|
||||
|
||||
for (int n = 0; n < mpctx->num_tracks; n++) {
|
||||
struct track *t = mpctx->tracks[n];
|
||||
// (Often, but not always, this is redundant and also done elsewhere.)
|
||||
|
||||
Reference in new issue
Block a user