options: add secondary-sub-delay

Add --secondary-sub-delay option and decouple --sub-delay from secondary
subtitles. This produces desirable behavior in most cases as secondary
and primary subtitles tracks tend to be timed independently of one
another.

This feature is implemented by turning the sub_delay field in
mp_subtitle_opts into an array of 2 floats. From here the track index is
either passed around or derived when sub_delay is needed. There are some
cases in dec_sub.c where it is possible for dec_sub.order (equivalent to
track index) to be -1. In these cases, sub_delay is inferred as 0.
This commit is contained in:
Ripose 2023-11-22 20:13:57 -07:00 committed by sfan5
parent a3f505d4cb
commit dea512ea38
7 changed files with 33 additions and 12 deletions

View File

@ -28,6 +28,8 @@ Interface changes
--- mpv 0.38.0 ---
- remove shared-script-properties (user-data is a replacement)
- add `--secondary-sub-delay`, decouple secondary subtitles from
`--sub-delay`
--- mpv 0.37.0 ---
- `--save-position-on-quit` and its associated commands now store state files
in %LOCALAPPDATA% instead of %APPDATA% directory by default on Windows.

View File

@ -2312,7 +2312,10 @@ Subtitles
printed by ``--sub-demuxer=help``.
``--sub-delay=<sec>``
Delays subtitles by ``<sec>`` seconds. Can be negative.
Delays primary subtitles by ``<sec>`` seconds. Can be negative.
``--secondary-sub-delay=<sec>``
Delays secondary subtitles by ``<sec>`` seconds. Can be negative.
``--sub-files=<file-list>``, ``--sub-file=<filename>``
Add a subtitle file to the list of external subtitles.

View File

@ -281,7 +281,8 @@ const struct m_sub_options mp_sub_filter_opts = {
const struct m_sub_options mp_subtitle_sub_opts = {
.opts = (const struct m_option[]){
{"sub-delay", OPT_FLOAT(sub_delay)},
{"sub-delay", OPT_FLOAT(sub_delay[0])},
{"secondary-sub-delay", OPT_FLOAT(sub_delay[1])},
{"sub-fps", OPT_FLOAT(sub_fps)},
{"sub-speed", OPT_FLOAT(sub_speed)},
{"sub-visibility", OPT_BOOL(sub_visibility)},

View File

@ -85,7 +85,7 @@ struct mp_subtitle_opts {
bool sub_visibility;
bool sec_sub_visibility;
float sub_pos;
float sub_delay;
float sub_delay[2];
float sub_fps;
float sub_speed;
bool sub_forced_events_only;

View File

@ -2874,9 +2874,10 @@ static int mp_property_sub_delay(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
int track_ind = *(int *)prop->priv;
switch (action) {
case M_PROPERTY_PRINT:
*(char **)arg = format_delay(opts->subs_rend->sub_delay);
*(char **)arg = format_delay(opts->subs_rend->sub_delay[track_ind]);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@ -3929,7 +3930,9 @@ static const struct m_property mp_properties_base[] = {
{"sid", property_switch_track, .priv = (void *)(const int[]){0, STREAM_SUB}},
{"secondary-sid", property_switch_track,
.priv = (void *)(const int[]){1, STREAM_SUB}},
{"sub-delay", mp_property_sub_delay},
{"sub-delay", mp_property_sub_delay, .priv = (void *)&(const int){0}},
{"secondary-sub-delay", mp_property_sub_delay,
.priv = (void *)&(const int){1}},
{"sub-speed", mp_property_sub_speed},
{"sub-pos", mp_property_sub_pos},
{"sub-ass-extradata", mp_property_sub_ass_extradata},
@ -4246,6 +4249,7 @@ static const struct property_osd_display {
{"secondary-sid", "Secondary subtitles"},
{"sub-pos", "Sub position"},
{"sub-delay", "Sub delay"},
{"secondary-sub-delay", "Secondary sub delay"},
{"sub-speed", "Sub speed"},
{"sub-visibility",
.msg = "Subtitles ${!sub-visibility==yes:hidden}"
@ -5380,10 +5384,13 @@ static void cmd_sub_step_seek(void *p)
a[1] = cmd->args[0].v.i;
if (sub_control(sub, SD_CTRL_SUB_STEP, a) > 0) {
if (step) {
mpctx->opts->subs_rend->sub_delay -= a[0] - refpts;
mpctx->opts->subs_rend->sub_delay[track_ind] -= a[0] - refpts;
m_config_notify_change_opt_ptr_notify(mpctx->mconfig,
&mpctx->opts->subs_rend->sub_delay);
show_property_osd(mpctx, "sub-delay", cmd->on_osd);
&mpctx->opts->subs_rend->sub_delay[track_ind]);
show_property_osd(
mpctx,
track_ind == 0 ? "sub-delay" : "secondary-sub-delay",
cmd->on_osd);
} else {
// We can easily seek/step to the wrong subtitle line (because
// video frame PTS and sub PTS rarely match exactly). Add an

View File

@ -147,7 +147,12 @@ double get_track_seek_offset(struct MPContext *mpctx, struct track *track)
if (track->type == STREAM_AUDIO)
return -opts->audio_delay;
if (track->type == STREAM_SUB)
return -opts->subs_rend->sub_delay;
{
for (int n = 0; n < num_ptracks[STREAM_SUB]; n++) {
if (mpctx->current_track[n][STREAM_SUB] == track)
return -opts->subs_rend->sub_delay[n];
}
}
}
return 0;
}

View File

@ -92,9 +92,10 @@ static void update_subtitle_speed(struct dec_sub *sub)
static double pts_to_subtitle(struct dec_sub *sub, double pts)
{
struct mp_subtitle_opts *opts = sub->opts;
float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order];
if (pts != MP_NOPTS_VALUE)
pts = (pts * sub->play_dir - opts->sub_delay) / sub->sub_speed;
pts = (pts * sub->play_dir - delay) / sub->sub_speed;
return pts;
}
@ -102,9 +103,10 @@ static double pts_to_subtitle(struct dec_sub *sub, double pts)
static double pts_from_subtitle(struct dec_sub *sub, double pts)
{
struct mp_subtitle_opts *opts = sub->opts;
float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order];
if (pts != MP_NOPTS_VALUE)
pts = (pts * sub->sub_speed + opts->sub_delay) * sub->play_dir;
pts = (pts * sub->sub_speed + delay) * sub->play_dir;
return pts;
}
@ -291,7 +293,8 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts, bool force)
break;
// (Use this mechanism only if sub_delay matters to avoid corner cases.)
double min_pts = sub->opts->sub_delay < 0 || force ? video_pts : MP_NOPTS_VALUE;
float delay = sub->order < 0 ? 0.0f : sub->opts->sub_delay[sub->order];
double min_pts = delay < 0 || force ? video_pts : MP_NOPTS_VALUE;
struct demux_packet *pkt;
int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt);