summaryrefslogtreecommitdiffstats
path: root/sub/sd_ass.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-12-05 23:56:07 +0100
committerwm4 <wm4@nowhere>2015-12-05 23:56:07 +0100
commit04934e86dd154d1824253ba468c8f76eadc3076a (patch)
treef6b245f0951e72e35a61934e0a6d43aaaee88d30 /sub/sd_ass.c
parentff1eaea3e7f9ac2ab45b298326b96d256f487dc3 (diff)
downloadmpv-04934e86dd154d1824253ba468c8f76eadc3076a.tar.bz2
mpv-04934e86dd154d1824253ba468c8f76eadc3076a.tar.xz
sub: move --sub-fix-timing handling to renderer
Instead of messing with the subtitle packet timestamps, do it on output. We work on the libass event list. If there is an unwanted gap or overlap, we render the timestamp at another position where there is no gap or overlap. This is somewhat more robust, and even works with demuxed subs (to some degree - depends whether the subs are prefected soon enough). It's active even for native ASS subs. I wonder if this is a problem with extended type setting. If it is, the heuristic that tries to avoid interrupting such cases has to be improved. While it probably would be ideal to do this after the subtitle decoder, certain aspects are at least currently handled better in this place.
Diffstat (limited to 'sub/sd_ass.c')
-rw-r--r--sub/sd_ass.c71
1 files changed, 69 insertions, 2 deletions
diff --git a/sub/sd_ass.c b/sub/sd_ass.c
index 55c8d760e8..11943613f9 100644
--- a/sub/sd_ass.c
+++ b/sub/sd_ass.c
@@ -243,6 +243,72 @@ static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
ass_set_line_spacing(priv, set_line_spacing);
}
+#define END(ev) ((ev)->Start + (ev)->Duration)
+
+static long long find_timestamp(struct sd *sd, double pts)
+{
+ struct sd_ass_priv *priv = sd->priv;
+ if (pts == MP_NOPTS_VALUE)
+ return 0;
+
+ long long ts = llrint(pts * 1000);
+
+ if (!sd->opts->sub_fix_timing)
+ return ts;
+
+ // Try to fix small gaps and overlaps.
+ ASS_Track *track = priv->ass_track;
+ int threshold = SUB_GAP_THRESHOLD * 1000;
+ int keep = SUB_GAP_KEEP * 1000;
+
+ // Find the "current" event.
+ ASS_Event *ev[2] = {0};
+ int n_ev = 0;
+ for (int n = 0; n < track->n_events; n++) {
+ ASS_Event *event = &track->events[n];
+ if (ts >= event->Start - threshold && ts <= END(event) + threshold) {
+ if (n_ev >= MP_ARRAY_SIZE(ev))
+ return ts; // multiple overlaps - give up (probably complex subs)
+ ev[n_ev++] = event;
+ }
+ }
+
+ if (n_ev != 2)
+ return ts;
+
+ // Simple/minor heuristic against destroying typesetting.
+ if (ev[0]->Style != ev[1]->Style)
+ return ts;
+
+ // Sort by start timestamps.
+ if (ev[0]->Start > ev[1]->Start)
+ MPSWAP(ASS_Event*, ev[0], ev[1]);
+
+ // We want to fix partial overlaps only.
+ if (END(ev[0]) >= END(ev[1]))
+ return ts;
+
+ if (ev[0]->Duration < keep || ev[1]->Duration < keep)
+ return ts;
+
+ // Gap between the events -> move ts to show the end of the first event.
+ if (ts >= END(ev[0]) && ts < ev[1]->Start && END(ev[0]) < ev[1]->Start &&
+ END(ev[0]) + threshold >= ev[1]->Start)
+ return END(ev[0]) - 1;
+
+ // Overlap -> move ts to the (exclusive) end of the first event.
+ // Relies on the fact that the ASS_Renderer has no overlap registered, even
+ // if there is one. This happens to work because we never render the
+ // overlapped state, and libass never resolves a collision.
+ if (ts >= ev[1]->Start && ts <= END(ev[0]) && END(ev[0]) > ev[1]->Start &&
+ END(ev[0]) <= ev[1]->Start + threshold)
+ return END(ev[0]);
+
+ return ts;
+}
+
+#undef END
+
static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
struct sub_bitmaps *res)
{
@@ -280,7 +346,8 @@ static void get_bitmaps(struct sd *sd, struct mp_osd_res dim, double pts,
}
if (no_ass)
fill_plaintext(sd, pts);
- mp_ass_render_frame(renderer, track, pts * 1000 + .5, &ctx->parts, res);
+ long long ts = find_timestamp(sd, pts);
+ mp_ass_render_frame(renderer, track, ts, &ctx->parts, res);
talloc_steal(ctx, ctx->parts);
if (!converted)
@@ -368,7 +435,7 @@ static char *get_text(struct sd *sd, double pts)
if (pts == MP_NOPTS_VALUE)
return NULL;
- long long ipts = pts * 1000 + 0.5;
+ long long ipts = find_timestamp(sd, pts);
struct buf b = {ctx->last_text, sizeof(ctx->last_text) - 1};