mirror of https://github.com/mpv-player/mpv
sub: move all subtitle timestamp messing code to a central place
It was split at least across osd.c and sd_ass.c/sd_lavc.c. sd_lavc.c actually ignored most of the more obscure subtitle timing things. There's no reason for this - just move it all to dec_sub.c (mostly from sd_ass.c, because it has some of the most complex stuff). Now timestamps are transformed as they enter or leave dec_sub.c. There appear to have been some subtle mismatches about how subtitle timestamps were transformed, e.g. sd_functions.accepts_packet didn't apply the subtitle speed to the timestamp. This patch should fix them, although it's not clear if they caused actual misbehavior. The semantics of SD_CTRL_SUB_STEP are slightly changed, which is the reason for the changes in command.c and sd_lavc.c.
This commit is contained in:
parent
828bd2963c
commit
3bf7df4a5e
|
@ -3081,8 +3081,6 @@ static int mp_property_sub_text(void *ctx, struct m_property *prop,
|
|||
if (!sub || pts == MP_NOPTS_VALUE)
|
||||
return M_PROPERTY_UNAVAILABLE;
|
||||
|
||||
pts -= mpctx->opts->sub_delay;
|
||||
|
||||
char *text = sub_get_text(sub, pts);
|
||||
if (!text)
|
||||
text = "";
|
||||
|
@ -5078,11 +5076,11 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
|||
double refpts = get_current_time(mpctx);
|
||||
if (sub && refpts != MP_NOPTS_VALUE) {
|
||||
double a[2];
|
||||
a[0] = refpts - opts->sub_delay;
|
||||
a[0] = refpts;
|
||||
a[1] = cmd->args[0].v.i;
|
||||
if (sub_control(sub, SD_CTRL_SUB_STEP, a) > 0) {
|
||||
if (cmd->id == MP_CMD_SUB_STEP) {
|
||||
opts->sub_delay -= a[0];
|
||||
opts->sub_delay -= a[0] - refpts;
|
||||
osd_changed(mpctx->osd);
|
||||
show_property_osd(mpctx, "sub-delay", on_osd);
|
||||
} else {
|
||||
|
@ -5092,9 +5090,9 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
|||
// rounding for the mess of it.
|
||||
a[0] += 0.01 * (a[1] >= 0 ? 1 : -1);
|
||||
mark_seek(mpctx);
|
||||
queue_seek(mpctx, MPSEEK_RELATIVE, a[0], MPSEEK_EXACT,
|
||||
queue_seek(mpctx, MPSEEK_ABSOLUTE, a[0], MPSEEK_EXACT,
|
||||
MPSEEK_FLAG_DELAY);
|
||||
set_osd_function(mpctx, (a[0] > 0) ? OSD_FFW : OSD_REW);
|
||||
set_osd_function(mpctx, (a[0] > refpts) ? OSD_FFW : OSD_REW);
|
||||
if (bar_osd)
|
||||
mpctx->add_osd_seek_info |= OSD_SEEK_INFO_BAR;
|
||||
if (msg_or_nobar_osd)
|
||||
|
|
|
@ -84,7 +84,6 @@ void uninit_sub_all(struct MPContext *mpctx)
|
|||
static bool update_subtitle(struct MPContext *mpctx, double video_pts,
|
||||
struct track *track)
|
||||
{
|
||||
struct MPOpts *opts = mpctx->opts;
|
||||
struct dec_sub *dec_sub = track ? track->d_sub : NULL;
|
||||
|
||||
if (!dec_sub || video_pts == MP_NOPTS_VALUE)
|
||||
|
@ -96,8 +95,6 @@ static bool update_subtitle(struct MPContext *mpctx, double video_pts,
|
|||
sub_control(dec_sub, SD_CTRL_SET_VIDEO_PARAMS, ¶ms);
|
||||
}
|
||||
|
||||
video_pts -= opts->sub_delay;
|
||||
|
||||
if (track->demuxer->fully_read && sub_can_preload(dec_sub)) {
|
||||
// Assume fully_read implies no interleaved audio/video streams.
|
||||
// (Reading packets will change the demuxer position.)
|
||||
|
|
|
@ -57,6 +57,8 @@ struct dec_sub {
|
|||
struct sh_stream *sh;
|
||||
double last_pkt_pts;
|
||||
bool preload_attempted;
|
||||
double video_fps;
|
||||
double sub_speed;
|
||||
|
||||
struct mp_codec_params *codec;
|
||||
double start, end;
|
||||
|
@ -67,6 +69,44 @@ struct dec_sub {
|
|||
struct demux_packet *new_segment;
|
||||
};
|
||||
|
||||
static void update_subtitle_speed(struct dec_sub *sub)
|
||||
{
|
||||
struct MPOpts *opts = sub->opts;
|
||||
sub->sub_speed = 1.0;
|
||||
|
||||
if (sub->video_fps > 0 && sub->codec->frame_based > 0) {
|
||||
MP_VERBOSE(sub, "Frame based format, dummy FPS: %f, video FPS: %f\n",
|
||||
sub->codec->frame_based, sub->video_fps);
|
||||
sub->sub_speed *= sub->codec->frame_based / sub->video_fps;
|
||||
}
|
||||
|
||||
if (opts->sub_fps && sub->video_fps)
|
||||
sub->sub_speed *= opts->sub_fps / sub->video_fps;
|
||||
|
||||
sub->sub_speed *= opts->sub_speed;
|
||||
}
|
||||
|
||||
// Return the subtitle PTS used for a given video PTS.
|
||||
static double pts_to_subtitle(struct dec_sub *sub, double pts)
|
||||
{
|
||||
struct MPOpts *opts = sub->opts;
|
||||
|
||||
if (pts != MP_NOPTS_VALUE)
|
||||
pts = (pts - opts->sub_delay) / sub->sub_speed;
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
static double pts_from_subtitle(struct dec_sub *sub, double pts)
|
||||
{
|
||||
struct MPOpts *opts = sub->opts;
|
||||
|
||||
if (pts != MP_NOPTS_VALUE)
|
||||
pts = pts * sub->sub_speed + opts->sub_delay;
|
||||
|
||||
return pts;
|
||||
}
|
||||
|
||||
void sub_lock(struct dec_sub *sub)
|
||||
{
|
||||
pthread_mutex_lock(&sub->lock);
|
||||
|
@ -140,8 +180,10 @@ struct dec_sub *sub_create(struct mpv_global *global, struct sh_stream *sh,
|
|||
mpthread_mutex_init_recursive(&sub->lock);
|
||||
|
||||
sub->sd = init_decoder(sub);
|
||||
if (sub->sd)
|
||||
if (sub->sd) {
|
||||
update_subtitle_speed(sub);
|
||||
return sub;
|
||||
}
|
||||
|
||||
talloc_free(sub);
|
||||
return NULL;
|
||||
|
@ -164,6 +206,7 @@ static void update_segment(struct dec_sub *sub)
|
|||
sub->sd->driver->uninit(sub->sd);
|
||||
talloc_free(sub->sd);
|
||||
sub->sd = new;
|
||||
update_subtitle_speed(sub);
|
||||
} else {
|
||||
// We'll just keep the current decoder, and feed it possibly
|
||||
// invalid data (not our fault if it crashes or something).
|
||||
|
@ -214,6 +257,7 @@ bool sub_read_packets(struct dec_sub *sub, double video_pts)
|
|||
{
|
||||
bool r = true;
|
||||
pthread_mutex_lock(&sub->lock);
|
||||
video_pts = pts_to_subtitle(sub, video_pts);
|
||||
while (1) {
|
||||
bool read_more = true;
|
||||
if (sub->sd->driver->accepts_packet)
|
||||
|
@ -272,6 +316,8 @@ void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, int format,
|
|||
{
|
||||
struct MPOpts *opts = sub->opts;
|
||||
|
||||
pts = pts_to_subtitle(sub, pts);
|
||||
|
||||
sub->last_vo_pts = pts;
|
||||
update_segment(sub);
|
||||
|
||||
|
@ -291,6 +337,8 @@ char *sub_get_text(struct dec_sub *sub, double pts)
|
|||
struct MPOpts *opts = sub->opts;
|
||||
char *text = NULL;
|
||||
|
||||
pts = pts_to_subtitle(sub, pts);
|
||||
|
||||
sub->last_vo_pts = pts;
|
||||
update_segment(sub);
|
||||
|
||||
|
@ -324,8 +372,28 @@ int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
|
|||
{
|
||||
int r = CONTROL_UNKNOWN;
|
||||
pthread_mutex_lock(&sub->lock);
|
||||
if (sub->sd->driver->control)
|
||||
r = sub->sd->driver->control(sub->sd, cmd, arg);
|
||||
switch (cmd) {
|
||||
case SD_CTRL_SET_VIDEO_DEF_FPS:
|
||||
sub->video_fps = *(double *)arg;
|
||||
update_subtitle_speed(sub);
|
||||
break;
|
||||
case SD_CTRL_UPDATE_SPEED:
|
||||
update_subtitle_speed(sub);
|
||||
break;
|
||||
case SD_CTRL_SUB_STEP: {
|
||||
double *a = arg;
|
||||
double arg2[2] = {a[0], a[1]};
|
||||
arg2[0] = pts_to_subtitle(sub, arg2[0]);
|
||||
if (sub->sd->driver->control)
|
||||
r = sub->sd->driver->control(sub->sd, cmd, arg2);
|
||||
if (r == CONTROL_OK)
|
||||
a[0] = pts_from_subtitle(sub, arg2[0]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (sub->sd->driver->control)
|
||||
r = sub->sd->driver->control(sub->sd, cmd, arg);
|
||||
}
|
||||
pthread_mutex_unlock(&sub->lock);
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -273,12 +273,8 @@ static void render_object(struct osd_state *osd, struct osd_object *obj,
|
|||
check_obj_resize(osd, res, obj);
|
||||
|
||||
if (obj->type == OSDTYPE_SUB || obj->type == OSDTYPE_SUB2) {
|
||||
if (obj->sub) {
|
||||
double sub_pts = video_pts;
|
||||
if (sub_pts != MP_NOPTS_VALUE)
|
||||
sub_pts -= opts->sub_delay;
|
||||
sub_get_bitmaps(obj->sub, obj->vo_res, format, sub_pts, out_imgs);
|
||||
}
|
||||
if (obj->sub)
|
||||
sub_get_bitmaps(obj->sub, obj->vo_res, format, video_pts, out_imgs);
|
||||
} else if (obj->type == OSDTYPE_EXTERNAL2) {
|
||||
if (obj->external2 && obj->external2->format) {
|
||||
*out_imgs = *obj->external2;
|
||||
|
|
35
sub/sd_ass.c
35
sub/sd_ass.c
|
@ -49,7 +49,6 @@ struct sd_ass_priv {
|
|||
char last_text[500];
|
||||
struct mp_image_params video_params;
|
||||
struct mp_image_params last_params;
|
||||
double sub_speed, video_fps, frame_fps;
|
||||
int64_t *seen_packets;
|
||||
int num_seen_packets;
|
||||
bool duration_unknown;
|
||||
|
@ -147,24 +146,6 @@ static void enable_output(struct sd *sd, bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
static void update_subtitle_speed(struct sd *sd)
|
||||
{
|
||||
struct MPOpts *opts = sd->opts;
|
||||
struct sd_ass_priv *ctx = sd->priv;
|
||||
ctx->sub_speed = 1.0;
|
||||
|
||||
if (ctx->video_fps > 0 && ctx->frame_fps > 0) {
|
||||
MP_VERBOSE(sd, "Frame based format, dummy FPS: %f, video FPS: %f\n",
|
||||
ctx->frame_fps, ctx->video_fps);
|
||||
ctx->sub_speed *= ctx->frame_fps / ctx->video_fps;
|
||||
}
|
||||
|
||||
if (opts->sub_fps && ctx->video_fps)
|
||||
ctx->sub_speed *= opts->sub_fps / ctx->video_fps;
|
||||
|
||||
ctx->sub_speed *= opts->sub_speed;
|
||||
}
|
||||
|
||||
static int init(struct sd *sd)
|
||||
{
|
||||
struct MPOpts *opts = sd->opts;
|
||||
|
@ -212,9 +193,6 @@ static int init(struct sd *sd)
|
|||
ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1);
|
||||
#endif
|
||||
|
||||
ctx->frame_fps = sd->codec->frame_based;
|
||||
update_subtitle_speed(sd);
|
||||
|
||||
enable_output(sd, true);
|
||||
|
||||
ctx->packer = mp_ass_packer_alloc(ctx);
|
||||
|
@ -387,8 +365,6 @@ static long long find_timestamp(struct sd *sd, double pts)
|
|||
if (pts == MP_NOPTS_VALUE)
|
||||
return 0;
|
||||
|
||||
pts /= priv->sub_speed;
|
||||
|
||||
long long ts = llrint(pts * 1000);
|
||||
|
||||
if (!sd->opts->sub_fix_timing || sd->opts->ass_style_override == 0)
|
||||
|
@ -679,11 +655,11 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
|
|||
switch (cmd) {
|
||||
case SD_CTRL_SUB_STEP: {
|
||||
double *a = arg;
|
||||
long long ts = llrint(a[0] * (1000.0 / ctx->sub_speed));
|
||||
long long ts = llrint(a[0] * 1000.0);
|
||||
long long res = ass_step_sub(ctx->ass_track, ts, a[1]);
|
||||
if (!res)
|
||||
return false;
|
||||
a[0] = res / (1000.0 / ctx->sub_speed);
|
||||
a[0] += res / 1000.0;
|
||||
return true;
|
||||
}
|
||||
case SD_CTRL_SET_VIDEO_PARAMS:
|
||||
|
@ -692,13 +668,6 @@ static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
|
|||
case SD_CTRL_SET_TOP:
|
||||
ctx->on_top = *(bool *)arg;
|
||||
return CONTROL_OK;
|
||||
case SD_CTRL_SET_VIDEO_DEF_FPS:
|
||||
ctx->video_fps = *(double *)arg;
|
||||
update_subtitle_speed(sd);
|
||||
return CONTROL_OK;
|
||||
case SD_CTRL_UPDATE_SPEED:
|
||||
update_subtitle_speed(sd);
|
||||
return CONTROL_OK;
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
|
|
@ -561,7 +561,7 @@ static double step_sub(struct sd *sd, double now, int movement)
|
|||
movement -= direction;
|
||||
} while (movement);
|
||||
|
||||
return best < 0 ? 0 : priv->seekpoints[best].pts - now;
|
||||
return best < 0 ? now : priv->seekpoints[best].pts;
|
||||
}
|
||||
|
||||
static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
|
||||
|
|
Loading…
Reference in New Issue