From 3bf7df4a5e1f46248c78e9e596cd8dab6ff57356 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 29 Dec 2017 15:39:38 +0100 Subject: 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. --- player/command.c | 10 +++----- player/sub.c | 3 --- sub/dec_sub.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- sub/osd.c | 8 ++---- sub/sd_ass.c | 35 ++------------------------- sub/sd_lavc.c | 2 +- 6 files changed, 80 insertions(+), 52 deletions(-) diff --git a/player/command.c b/player/command.c index 412afc5e11..61d47a2319 100644 --- a/player/command.c +++ b/player/command.c @@ -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) diff --git a/player/sub.c b/player/sub.c index 2702e7bf55..0de02ea61b 100644 --- a/player/sub.c +++ b/player/sub.c @@ -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.) diff --git a/sub/dec_sub.c b/sub/dec_sub.c index 11ab879211..b9fdc7adb9 100644 --- a/sub/dec_sub.c +++ b/sub/dec_sub.c @@ -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; } diff --git a/sub/osd.c b/sub/osd.c index 0d594cd24d..d2351b7213 100644 --- a/sub/osd.c +++ b/sub/osd.c @@ -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; diff --git a/sub/sd_ass.c b/sub/sd_ass.c index 4bc9e15f8a..74d500be8d 100644 --- a/sub/sd_ass.c +++ b/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; } diff --git a/sub/sd_lavc.c b/sub/sd_lavc.c index e805d5c34c..81d8ccfa7d 100644 --- a/sub/sd_lavc.c +++ b/sub/sd_lavc.c @@ -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) -- cgit v1.2.3