1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdlib.h>
19 #include <assert.h>
20 #include <string.h>
21 #include <math.h>
22 #include <limits.h>
23
24 #include <libavutil/common.h>
25 #include <ass/ass.h>
26
27 #include "mpv_talloc.h"
28
29 #include "config.h"
30 #include "options/m_config.h"
31 #include "options/options.h"
32 #include "common/common.h"
33 #include "common/msg.h"
34 #include "demux/demux.h"
35 #include "video/csputils.h"
36 #include "video/mp_image.h"
37 #include "dec_sub.h"
38 #include "ass_mp.h"
39 #include "sd.h"
40
41 struct sd_ass_priv {
42 struct ass_library *ass_library;
43 struct ass_renderer *ass_renderer;
44 struct ass_track *ass_track;
45 struct ass_track *shadow_track; // for --sub-ass=no rendering
46 bool is_converted;
47 struct lavc_conv *converter;
48 struct sd_filter **filters;
49 int num_filters;
50 bool clear_once;
51 bool on_top;
52 struct mp_ass_packer *packer;
53 struct sub_bitmap_copy_cache *copy_cache;
54 char last_text[500];
55 struct mp_image_params video_params;
56 struct mp_image_params last_params;
57 int64_t *seen_packets;
58 int num_seen_packets;
59 bool duration_unknown;
60 };
61
62 static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts);
63 static void fill_plaintext(struct sd *sd, double pts);
64
65 static const struct sd_filter_functions *const filters[] = {
66 // Note: list order defines filter order.
67 &sd_filter_sdh,
68 #if HAVE_POSIX
69 &sd_filter_regex,
70 #endif
71 #if HAVE_JAVASCRIPT
72 &sd_filter_jsre,
73 #endif
74 NULL,
75 };
76
77 // Add default styles, if the track does not have any styles yet.
78 // Apply style overrides if the user provides any.
mp_ass_add_default_styles(ASS_Track * track,struct mp_subtitle_opts * opts)79 static void mp_ass_add_default_styles(ASS_Track *track, struct mp_subtitle_opts *opts)
80 {
81 if (opts->ass_styles_file && opts->ass_style_override)
82 ass_read_styles(track, opts->ass_styles_file, NULL);
83
84 if (track->n_styles == 0) {
85 if (!track->PlayResY) {
86 track->PlayResY = MP_ASS_FONT_PLAYRESY;
87 track->PlayResX = track->PlayResY * 4 / 3;
88 }
89 track->Kerning = true;
90 int sid = ass_alloc_style(track);
91 track->default_style = sid;
92 ASS_Style *style = track->styles + sid;
93 style->Name = strdup("Default");
94 mp_ass_set_style(style, track->PlayResY, opts->sub_style);
95 }
96
97 if (opts->ass_style_override)
98 ass_process_force_style(track);
99 }
100
101 static const char *const font_mimetypes[] = {
102 "application/x-truetype-font",
103 "application/vnd.ms-opentype",
104 "application/x-font-ttf",
105 "application/x-font", // probably incorrect
106 "application/font-sfnt",
107 "font/collection",
108 "font/otf",
109 "font/sfnt",
110 "font/ttf",
111 NULL
112 };
113
114 static const char *const font_exts[] = {".ttf", ".ttc", ".otf", ".otc", NULL};
115
attachment_is_font(struct mp_log * log,struct demux_attachment * f)116 static bool attachment_is_font(struct mp_log *log, struct demux_attachment *f)
117 {
118 if (!f->name || !f->type || !f->data || !f->data_size)
119 return false;
120 for (int n = 0; font_mimetypes[n]; n++) {
121 if (strcmp(font_mimetypes[n], f->type) == 0)
122 return true;
123 }
124 // fallback: match against file extension
125 char *ext = strlen(f->name) > 4 ? f->name + strlen(f->name) - 4 : "";
126 for (int n = 0; font_exts[n]; n++) {
127 if (strcasecmp(ext, font_exts[n]) == 0) {
128 mp_warn(log, "Loading font attachment '%s' with MIME type %s. "
129 "Assuming this is a broken Matroska file, which was "
130 "muxed without setting a correct font MIME type.\n",
131 f->name, f->type);
132 return true;
133 }
134 }
135 return false;
136 }
137
add_subtitle_fonts(struct sd * sd)138 static void add_subtitle_fonts(struct sd *sd)
139 {
140 struct sd_ass_priv *ctx = sd->priv;
141 struct mp_subtitle_opts *opts = sd->opts;
142 if (!opts->ass_enabled || !opts->use_embedded_fonts || !sd->attachments)
143 return;
144 for (int i = 0; i < sd->attachments->num_entries; i++) {
145 struct demux_attachment *f = &sd->attachments->entries[i];
146 if (attachment_is_font(sd->log, f))
147 ass_add_font(ctx->ass_library, f->name, f->data, f->data_size);
148 }
149 }
150
filters_destroy(struct sd * sd)151 static void filters_destroy(struct sd *sd)
152 {
153 struct sd_ass_priv *ctx = sd->priv;
154
155 for (int n = 0; n < ctx->num_filters; n++) {
156 struct sd_filter *ft = ctx->filters[n];
157 if (ft->driver->uninit)
158 ft->driver->uninit(ft);
159 talloc_free(ft);
160 }
161 ctx->num_filters = 0;
162 }
163
filters_init(struct sd * sd)164 static void filters_init(struct sd *sd)
165 {
166 struct sd_ass_priv *ctx = sd->priv;
167
168 filters_destroy(sd);
169
170 for (int n = 0; filters[n]; n++) {
171 struct sd_filter *ft = talloc_ptrtype(ctx, ft);
172 *ft = (struct sd_filter){
173 .global = sd->global,
174 .log = sd->log,
175 .opts = mp_get_config_group(ft, sd->global, &mp_sub_filter_opts),
176 .driver = filters[n],
177 .codec = "ass",
178 .event_format = ctx->ass_track->event_format,
179 };
180 if (ft->driver->init(ft)) {
181 MP_TARRAY_APPEND(ctx, ctx->filters, ctx->num_filters, ft);
182 } else {
183 talloc_free(ft);
184 }
185 }
186 }
187
enable_output(struct sd * sd,bool enable)188 static void enable_output(struct sd *sd, bool enable)
189 {
190 struct sd_ass_priv *ctx = sd->priv;
191 if (enable == !!ctx->ass_renderer)
192 return;
193 if (ctx->ass_renderer) {
194 ass_renderer_done(ctx->ass_renderer);
195 ctx->ass_renderer = NULL;
196 } else {
197 ctx->ass_renderer = ass_renderer_init(ctx->ass_library);
198
199 mp_ass_configure_fonts(ctx->ass_renderer, sd->opts->sub_style,
200 sd->global, sd->log);
201 }
202 }
203
assobjects_init(struct sd * sd)204 static void assobjects_init(struct sd *sd)
205 {
206 struct sd_ass_priv *ctx = sd->priv;
207 struct mp_subtitle_opts *opts = sd->opts;
208
209 ctx->ass_library = mp_ass_init(sd->global, sd->log);
210 ass_set_extract_fonts(ctx->ass_library, opts->use_embedded_fonts);
211
212 add_subtitle_fonts(sd);
213
214 if (opts->ass_style_override)
215 ass_set_style_overrides(ctx->ass_library, opts->ass_force_style_list);
216
217 ctx->ass_track = ass_new_track(ctx->ass_library);
218 ctx->ass_track->track_type = TRACK_TYPE_ASS;
219
220 ctx->shadow_track = ass_new_track(ctx->ass_library);
221 ctx->shadow_track->PlayResX = 384;
222 ctx->shadow_track->PlayResY = 288;
223 mp_ass_add_default_styles(ctx->shadow_track, opts);
224
225 char *extradata = sd->codec->extradata;
226 int extradata_size = sd->codec->extradata_size;
227 if (ctx->converter) {
228 extradata = lavc_conv_get_extradata(ctx->converter);
229 extradata_size = extradata ? strlen(extradata) : 0;
230 }
231 if (extradata)
232 ass_process_codec_private(ctx->ass_track, extradata, extradata_size);
233
234 mp_ass_add_default_styles(ctx->ass_track, opts);
235
236 #if LIBASS_VERSION >= 0x01302000
237 ass_set_check_readorder(ctx->ass_track, sd->opts->sub_clear_on_seek ? 0 : 1);
238 #endif
239
240 enable_output(sd, true);
241 }
242
assobjects_destroy(struct sd * sd)243 static void assobjects_destroy(struct sd *sd)
244 {
245 struct sd_ass_priv *ctx = sd->priv;
246
247 ass_free_track(ctx->ass_track);
248 ass_free_track(ctx->shadow_track);
249 enable_output(sd, false);
250 ass_library_done(ctx->ass_library);
251 }
252
init(struct sd * sd)253 static int init(struct sd *sd)
254 {
255 struct sd_ass_priv *ctx = talloc_zero(sd, struct sd_ass_priv);
256 sd->priv = ctx;
257
258 // Note: accept "null" as alias for "ass", so EDL delay_open subtitle
259 // streams work.
260 if (strcmp(sd->codec->codec, "ass") != 0 &&
261 strcmp(sd->codec->codec, "null") != 0)
262 {
263 ctx->is_converted = true;
264 ctx->converter = lavc_conv_create(sd->log, sd->codec->codec,
265 sd->codec->extradata,
266 sd->codec->extradata_size);
267 if (!ctx->converter)
268 return -1;
269
270 if (strcmp(sd->codec->codec, "eia_608") == 0)
271 ctx->duration_unknown = 1;
272 }
273
274 assobjects_init(sd);
275 filters_init(sd);
276
277 ctx->packer = mp_ass_packer_alloc(ctx);
278
279 return 0;
280 }
281
282 // Note: pkt is not necessarily a fully valid refcounted packet.
filter_and_add(struct sd * sd,struct demux_packet * pkt)283 static void filter_and_add(struct sd *sd, struct demux_packet *pkt)
284 {
285 struct sd_ass_priv *ctx = sd->priv;
286 struct demux_packet *orig_pkt = pkt;
287
288 for (int n = 0; n < ctx->num_filters; n++) {
289 struct sd_filter *ft = ctx->filters[n];
290 struct demux_packet *npkt = ft->driver->filter(ft, pkt);
291 if (pkt != npkt && pkt != orig_pkt)
292 talloc_free(pkt);
293 pkt = npkt;
294 if (!pkt)
295 return;
296 }
297
298 ass_process_chunk(ctx->ass_track, pkt->buffer, pkt->len,
299 llrint(pkt->pts * 1000),
300 llrint(pkt->duration * 1000));
301
302 if (pkt != orig_pkt)
303 talloc_free(pkt);
304 }
305
306 // Test if the packet with the given file position (used as unique ID) was
307 // already consumed. Return false if the packet is new (and add it to the
308 // internal list), and return true if it was already seen.
check_packet_seen(struct sd * sd,int64_t pos)309 static bool check_packet_seen(struct sd *sd, int64_t pos)
310 {
311 struct sd_ass_priv *priv = sd->priv;
312 int a = 0;
313 int b = priv->num_seen_packets;
314 while (a < b) {
315 int mid = a + (b - a) / 2;
316 int64_t val = priv->seen_packets[mid];
317 if (pos == val)
318 return true;
319 if (pos > val) {
320 a = mid + 1;
321 } else {
322 b = mid;
323 }
324 }
325 MP_TARRAY_INSERT_AT(priv, priv->seen_packets, priv->num_seen_packets, a, pos);
326 return false;
327 }
328
329 #define UNKNOWN_DURATION (INT_MAX / 1000)
330
decode(struct sd * sd,struct demux_packet * packet)331 static void decode(struct sd *sd, struct demux_packet *packet)
332 {
333 struct sd_ass_priv *ctx = sd->priv;
334 ASS_Track *track = ctx->ass_track;
335 if (ctx->converter) {
336 if (!sd->opts->sub_clear_on_seek && packet->pos >= 0 &&
337 check_packet_seen(sd, packet->pos))
338 return;
339
340 double sub_pts = 0;
341 double sub_duration = 0;
342 char **r = lavc_conv_decode(ctx->converter, packet, &sub_pts,
343 &sub_duration);
344 if (packet->duration < 0 || sub_duration == UINT32_MAX) {
345 if (!ctx->duration_unknown) {
346 MP_WARN(sd, "Subtitle with unknown duration.\n");
347 ctx->duration_unknown = true;
348 }
349 sub_duration = UNKNOWN_DURATION;
350 }
351
352 for (int n = 0; r && r[n]; n++) {
353 struct demux_packet pkt2 = {
354 .pts = sub_pts,
355 .duration = sub_duration,
356 .buffer = r[n],
357 .len = strlen(r[n]),
358 };
359 filter_and_add(sd, &pkt2);
360 }
361 if (ctx->duration_unknown) {
362 for (int n = 0; n < track->n_events - 1; n++) {
363 if (track->events[n].Duration == UNKNOWN_DURATION * 1000) {
364 track->events[n].Duration = track->events[n + 1].Start -
365 track->events[n].Start;
366 }
367 }
368 }
369 } else {
370 // Note that for this packet format, libass has an internal mechanism
371 // for discarding duplicate (already seen) packets.
372 filter_and_add(sd, packet);
373 }
374 }
375
configure_ass(struct sd * sd,struct mp_osd_res * dim,bool converted,ASS_Track * track)376 static void configure_ass(struct sd *sd, struct mp_osd_res *dim,
377 bool converted, ASS_Track *track)
378 {
379 struct mp_subtitle_opts *opts = sd->opts;
380 struct sd_ass_priv *ctx = sd->priv;
381 ASS_Renderer *priv = ctx->ass_renderer;
382
383 ass_set_frame_size(priv, dim->w, dim->h);
384 ass_set_margins(priv, dim->mt, dim->mb, dim->ml, dim->mr);
385
386 bool set_use_margins = false;
387 int set_sub_pos = 0;
388 float set_line_spacing = 0;
389 float set_font_scale = 1;
390 int set_hinting = 0;
391 bool set_scale_with_window = false;
392 bool set_scale_by_window = true;
393 bool total_override = false;
394 // With forced overrides, apply the --sub-* specific options
395 if (converted || opts->ass_style_override == 3) { // 'force'
396 set_scale_with_window = opts->sub_scale_with_window;
397 set_use_margins = opts->sub_use_margins;
398 set_scale_by_window = opts->sub_scale_by_window;
399 total_override = true;
400 } else {
401 set_scale_with_window = opts->ass_scale_with_window;
402 set_use_margins = opts->ass_use_margins;
403 }
404 if (converted || opts->ass_style_override) {
405 set_sub_pos = 100 - opts->sub_pos;
406 set_line_spacing = opts->ass_line_spacing;
407 set_hinting = opts->ass_hinting;
408 set_font_scale = opts->sub_scale;
409 }
410 if (set_scale_with_window) {
411 int vidh = dim->h - (dim->mt + dim->mb);
412 set_font_scale *= dim->h / (float)MPMAX(vidh, 1);
413 }
414 if (!set_scale_by_window) {
415 double factor = dim->h / 720.0;
416 if (factor != 0.0)
417 set_font_scale /= factor;
418 }
419 ass_set_use_margins(priv, set_use_margins);
420 ass_set_line_position(priv, set_sub_pos);
421 ass_set_shaper(priv, opts->ass_shaper);
422 int set_force_flags = 0;
423 if (total_override)
424 set_force_flags |= ASS_OVERRIDE_BIT_STYLE | ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
425 if (opts->ass_style_override == 4) // 'scale'
426 set_force_flags |= ASS_OVERRIDE_BIT_SELECTIVE_FONT_SCALE;
427 if (converted)
428 set_force_flags |= ASS_OVERRIDE_BIT_ALIGNMENT;
429 #ifdef ASS_JUSTIFY_AUTO
430 if ((converted || opts->ass_style_override) && opts->ass_justify)
431 set_force_flags |= ASS_OVERRIDE_BIT_JUSTIFY;
432 #endif
433 ass_set_selective_style_override_enabled(priv, set_force_flags);
434 ASS_Style style = {0};
435 mp_ass_set_style(&style, 288, opts->sub_style);
436 ass_set_selective_style_override(priv, &style);
437 free(style.FontName);
438 if (converted && track->default_style < track->n_styles) {
439 mp_ass_set_style(track->styles + track->default_style,
440 track->PlayResY, opts->sub_style);
441 }
442 ass_set_font_scale(priv, set_font_scale);
443 ass_set_hinting(priv, set_hinting);
444 ass_set_line_spacing(priv, set_line_spacing);
445 }
446
has_overrides(char * s)447 static bool has_overrides(char *s)
448 {
449 if (!s)
450 return false;
451 return strstr(s, "\\pos") || strstr(s, "\\move") || strstr(s, "\\clip") ||
452 strstr(s, "\\iclip") || strstr(s, "\\org") || strstr(s, "\\p");
453 }
454
455 #define END(ev) ((ev)->Start + (ev)->Duration)
456
find_timestamp(struct sd * sd,double pts)457 static long long find_timestamp(struct sd *sd, double pts)
458 {
459 struct sd_ass_priv *priv = sd->priv;
460 if (pts == MP_NOPTS_VALUE)
461 return 0;
462
463 long long ts = llrint(pts * 1000);
464
465 if (!sd->opts->sub_fix_timing || sd->opts->ass_style_override == 0)
466 return ts;
467
468 // Try to fix small gaps and overlaps.
469 ASS_Track *track = priv->ass_track;
470 int threshold = SUB_GAP_THRESHOLD * 1000;
471 int keep = SUB_GAP_KEEP * 1000;
472
473 // Find the "current" event.
474 ASS_Event *ev[2] = {0};
475 int n_ev = 0;
476 for (int n = 0; n < track->n_events; n++) {
477 ASS_Event *event = &track->events[n];
478 if (ts >= event->Start - threshold && ts <= END(event) + threshold) {
479 if (n_ev >= MP_ARRAY_SIZE(ev))
480 return ts; // multiple overlaps - give up (probably complex subs)
481 ev[n_ev++] = event;
482 }
483 }
484
485 if (n_ev != 2)
486 return ts;
487
488 // Simple/minor heuristic against destroying typesetting.
489 if (ev[0]->Style != ev[1]->Style || has_overrides(ev[0]->Text) ||
490 has_overrides(ev[1]->Text))
491 return ts;
492
493 // Sort by start timestamps.
494 if (ev[0]->Start > ev[1]->Start)
495 MPSWAP(ASS_Event*, ev[0], ev[1]);
496
497 // We want to fix partial overlaps only.
498 if (END(ev[0]) >= END(ev[1]))
499 return ts;
500
501 if (ev[0]->Duration < keep || ev[1]->Duration < keep)
502 return ts;
503
504 // Gap between the events -> move ts to show the end of the first event.
505 if (ts >= END(ev[0]) && ts < ev[1]->Start && END(ev[0]) < ev[1]->Start &&
506 END(ev[0]) + threshold >= ev[1]->Start)
507 return END(ev[0]) - 1;
508
509 // Overlap -> move ts to the (exclusive) end of the first event.
510 // Relies on the fact that the ASS_Renderer has no overlap registered, even
511 // if there is one. This happens to work because we never render the
512 // overlapped state, and libass never resolves a collision.
513 if (ts >= ev[1]->Start && ts <= END(ev[0]) && END(ev[0]) > ev[1]->Start &&
514 END(ev[0]) <= ev[1]->Start + threshold)
515 return END(ev[0]);
516
517 return ts;
518 }
519
520 #undef END
521
get_bitmaps(struct sd * sd,struct mp_osd_res dim,int format,double pts)522 static struct sub_bitmaps *get_bitmaps(struct sd *sd, struct mp_osd_res dim,
523 int format, double pts)
524 {
525 struct sd_ass_priv *ctx = sd->priv;
526 struct mp_subtitle_opts *opts = sd->opts;
527 bool no_ass = !opts->ass_enabled || ctx->on_top ||
528 opts->ass_style_override == 5;
529 bool converted = ctx->is_converted || no_ass;
530 ASS_Track *track = no_ass ? ctx->shadow_track : ctx->ass_track;
531 ASS_Renderer *renderer = ctx->ass_renderer;
532 struct sub_bitmaps *res = &(struct sub_bitmaps){0};
533
534 if (pts == MP_NOPTS_VALUE || !renderer)
535 goto done;
536
537 // Currently no supported text sub formats support a distinction between forced
538 // and unforced lines, so we just assume everything's unforced and discard everything.
539 // If we ever see a format that makes this distinction, we can add support here.
540 if (opts->forced_subs_only_current)
541 goto done;
542
543 double scale = dim.display_par;
544 if (!converted && (!opts->ass_style_override ||
545 opts->ass_vsfilter_aspect_compat))
546 {
547 // Let's use the original video PAR for vsfilter compatibility:
548 double par = ctx->video_params.p_w / (double)ctx->video_params.p_h;
549 if (isnormal(par))
550 scale *= par;
551 }
552 configure_ass(sd, &dim, converted, track);
553 ass_set_pixel_aspect(renderer, scale);
554 if (!converted && (!opts->ass_style_override ||
555 opts->ass_vsfilter_blur_compat))
556 {
557 ass_set_storage_size(renderer, ctx->video_params.w, ctx->video_params.h);
558 } else {
559 ass_set_storage_size(renderer, 0, 0);
560 }
561 long long ts = find_timestamp(sd, pts);
562 if (ctx->duration_unknown && pts != MP_NOPTS_VALUE) {
563 mp_ass_flush_old_events(track, ts);
564 ctx->num_seen_packets = 0;
565 sd->preload_ok = false;
566 }
567
568 if (no_ass)
569 fill_plaintext(sd, pts);
570
571 int changed;
572 ASS_Image *imgs = ass_render_frame(renderer, track, ts, &changed);
573 mp_ass_packer_pack(ctx->packer, &imgs, 1, changed, format, res);
574
575 done:
576 // mangle_colors() modifies the color field, so copy the thing _before_.
577 res = sub_bitmaps_copy(&ctx->copy_cache, res);
578
579 if (!converted && res)
580 mangle_colors(sd, res);
581
582 return res;
583 }
584
585 struct buf {
586 char *start;
587 int size;
588 int len;
589 };
590
append(struct buf * b,char c)591 static void append(struct buf *b, char c)
592 {
593 if (b->len < b->size) {
594 b->start[b->len] = c;
595 b->len++;
596 }
597 }
598
ass_to_plaintext(struct buf * b,const char * in)599 static void ass_to_plaintext(struct buf *b, const char *in)
600 {
601 bool in_tag = false;
602 const char *open_tag_pos = NULL;
603 bool in_drawing = false;
604 while (*in) {
605 if (in_tag) {
606 if (in[0] == '}') {
607 in += 1;
608 in_tag = false;
609 } else if (in[0] == '\\' && in[1] == 'p') {
610 in += 2;
611 // Skip text between \pN and \p0 tags. A \p without a number
612 // is the same as \p0, and leading 0s are also allowed.
613 in_drawing = false;
614 while (in[0] >= '0' && in[0] <= '9') {
615 if (in[0] != '0')
616 in_drawing = true;
617 in += 1;
618 }
619 } else {
620 in += 1;
621 }
622 } else {
623 if (in[0] == '\\' && (in[1] == 'N' || in[1] == 'n')) {
624 in += 2;
625 append(b, '\n');
626 } else if (in[0] == '\\' && in[1] == 'h') {
627 in += 2;
628 append(b, ' ');
629 } else if (in[0] == '{') {
630 open_tag_pos = in;
631 in += 1;
632 in_tag = true;
633 } else {
634 if (!in_drawing)
635 append(b, in[0]);
636 in += 1;
637 }
638 }
639 }
640 // A '{' without a closing '}' is always visible.
641 if (in_tag) {
642 while (*open_tag_pos)
643 append(b, *open_tag_pos++);
644 }
645 }
646
647 // Empty string counts as whitespace. Reads s[len-1] even if there are \0s.
is_whitespace_only(char * s,int len)648 static bool is_whitespace_only(char *s, int len)
649 {
650 for (int n = 0; n < len; n++) {
651 if (s[n] != ' ' && s[n] != '\t')
652 return false;
653 }
654 return true;
655 }
656
get_text_buf(struct sd * sd,double pts,enum sd_text_type type)657 static char *get_text_buf(struct sd *sd, double pts, enum sd_text_type type)
658 {
659 struct sd_ass_priv *ctx = sd->priv;
660 ASS_Track *track = ctx->ass_track;
661
662 if (pts == MP_NOPTS_VALUE)
663 return NULL;
664 long long ipts = find_timestamp(sd, pts);
665
666 struct buf b = {ctx->last_text, sizeof(ctx->last_text) - 1};
667
668 for (int i = 0; i < track->n_events; ++i) {
669 ASS_Event *event = track->events + i;
670 if (ipts >= event->Start && ipts < event->Start + event->Duration) {
671 if (event->Text) {
672 int start = b.len;
673 if (type == SD_TEXT_TYPE_PLAIN) {
674 ass_to_plaintext(&b, event->Text);
675 } else {
676 char *t = event->Text;
677 while (*t)
678 append(&b, *t++);
679 }
680 if (is_whitespace_only(&b.start[start], b.len - start)) {
681 b.len = start;
682 } else {
683 append(&b, '\n');
684 }
685 }
686 }
687 }
688
689 b.start[b.len] = '\0';
690
691 if (b.len > 0 && b.start[b.len - 1] == '\n')
692 b.start[b.len - 1] = '\0';
693
694 return ctx->last_text;
695 }
696
get_text(struct sd * sd,double pts,enum sd_text_type type)697 static char *get_text(struct sd *sd, double pts, enum sd_text_type type)
698 {
699 return talloc_strdup(NULL, get_text_buf(sd, pts, type));
700 }
701
get_times(struct sd * sd,double pts)702 static struct sd_times get_times(struct sd *sd, double pts)
703 {
704 struct sd_ass_priv *ctx = sd->priv;
705 ASS_Track *track = ctx->ass_track;
706 struct sd_times res = { .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE };
707
708 if (pts == MP_NOPTS_VALUE || ctx->duration_unknown)
709 return res;
710
711 long long ipts = find_timestamp(sd, pts);
712
713 for (int i = 0; i < track->n_events; ++i) {
714 ASS_Event *event = track->events + i;
715 if (ipts >= event->Start && ipts < event->Start + event->Duration) {
716 double start = event->Start / 1000.0;
717 double end = event->Duration == UNKNOWN_DURATION ?
718 MP_NOPTS_VALUE : (event->Start + event->Duration) / 1000.0;
719
720 if (res.start == MP_NOPTS_VALUE || res.start > start)
721 res.start = start;
722
723 if (res.end == MP_NOPTS_VALUE || res.end < end)
724 res.end = end;
725 }
726 }
727
728 return res;
729 }
730
fill_plaintext(struct sd * sd,double pts)731 static void fill_plaintext(struct sd *sd, double pts)
732 {
733 struct sd_ass_priv *ctx = sd->priv;
734 ASS_Track *track = ctx->shadow_track;
735
736 ass_flush_events(track);
737
738 char *text = get_text_buf(sd, pts, SD_TEXT_TYPE_PLAIN);
739 if (!text)
740 return;
741
742 bstr dst = {0};
743
744 if (ctx->on_top)
745 bstr_xappend(NULL, &dst, bstr0("{\\a6}"));
746
747 while (*text) {
748 if (*text == '{')
749 bstr_xappend(NULL, &dst, bstr0("\\"));
750 bstr_xappend(NULL, &dst, (bstr){text, 1});
751 // Break ASS escapes with U+2060 WORD JOINER
752 if (*text == '\\')
753 mp_append_utf8_bstr(NULL, &dst, 0x2060);
754 text++;
755 }
756
757 if (!dst.start)
758 return;
759
760 int n = ass_alloc_event(track);
761 ASS_Event *event = track->events + n;
762 event->Start = 0;
763 event->Duration = INT_MAX;
764 event->Style = track->default_style;
765 event->Text = strdup(dst.start);
766
767 talloc_free(dst.start);
768 }
769
reset(struct sd * sd)770 static void reset(struct sd *sd)
771 {
772 struct sd_ass_priv *ctx = sd->priv;
773 if (sd->opts->sub_clear_on_seek || ctx->duration_unknown || ctx->clear_once) {
774 ass_flush_events(ctx->ass_track);
775 ctx->num_seen_packets = 0;
776 sd->preload_ok = false;
777 ctx->clear_once = false;
778 }
779 if (ctx->converter)
780 lavc_conv_reset(ctx->converter);
781 }
782
uninit(struct sd * sd)783 static void uninit(struct sd *sd)
784 {
785 struct sd_ass_priv *ctx = sd->priv;
786
787 filters_destroy(sd);
788 if (ctx->converter)
789 lavc_conv_uninit(ctx->converter);
790 assobjects_destroy(sd);
791 talloc_free(ctx->copy_cache);
792 }
793
control(struct sd * sd,enum sd_ctrl cmd,void * arg)794 static int control(struct sd *sd, enum sd_ctrl cmd, void *arg)
795 {
796 struct sd_ass_priv *ctx = sd->priv;
797 switch (cmd) {
798 case SD_CTRL_SUB_STEP: {
799 double *a = arg;
800 long long ts = llrint(a[0] * 1000.0);
801 long long res = ass_step_sub(ctx->ass_track, ts, a[1]);
802 if (!res)
803 return false;
804 a[0] += res / 1000.0;
805 return true;
806 }
807 case SD_CTRL_SET_VIDEO_PARAMS:
808 ctx->video_params = *(struct mp_image_params *)arg;
809 return CONTROL_OK;
810 case SD_CTRL_SET_TOP:
811 ctx->on_top = *(bool *)arg;
812 return CONTROL_OK;
813 case SD_CTRL_UPDATE_OPTS: {
814 int flags = (uintptr_t)arg;
815 if (flags & UPDATE_SUB_FILT) {
816 filters_destroy(sd);
817 filters_init(sd);
818 ctx->clear_once = true; // allow reloading on seeks
819 }
820 if (flags & UPDATE_SUB_HARD) {
821 assobjects_destroy(sd);
822 assobjects_init(sd);
823 }
824 return CONTROL_OK;
825 }
826 default:
827 return CONTROL_UNKNOWN;
828 }
829 }
830
831 const struct sd_functions sd_ass = {
832 .name = "ass",
833 .accept_packets_in_advance = true,
834 .init = init,
835 .decode = decode,
836 .get_bitmaps = get_bitmaps,
837 .get_text = get_text,
838 .get_times = get_times,
839 .control = control,
840 .reset = reset,
841 .select = enable_output,
842 .uninit = uninit,
843 };
844
845 // Disgusting hack for (xy-)vsfilter color compatibility.
mangle_colors(struct sd * sd,struct sub_bitmaps * parts)846 static void mangle_colors(struct sd *sd, struct sub_bitmaps *parts)
847 {
848 struct mp_subtitle_opts *opts = sd->opts;
849 struct sd_ass_priv *ctx = sd->priv;
850 enum mp_csp csp = 0;
851 enum mp_csp_levels levels = 0;
852 if (opts->ass_vsfilter_color_compat == 0) // "no"
853 return;
854 bool force_601 = opts->ass_vsfilter_color_compat == 3;
855 ASS_Track *track = ctx->ass_track;
856 static const int ass_csp[] = {
857 [YCBCR_BT601_TV] = MP_CSP_BT_601,
858 [YCBCR_BT601_PC] = MP_CSP_BT_601,
859 [YCBCR_BT709_TV] = MP_CSP_BT_709,
860 [YCBCR_BT709_PC] = MP_CSP_BT_709,
861 [YCBCR_SMPTE240M_TV] = MP_CSP_SMPTE_240M,
862 [YCBCR_SMPTE240M_PC] = MP_CSP_SMPTE_240M,
863 };
864 static const int ass_levels[] = {
865 [YCBCR_BT601_TV] = MP_CSP_LEVELS_TV,
866 [YCBCR_BT601_PC] = MP_CSP_LEVELS_PC,
867 [YCBCR_BT709_TV] = MP_CSP_LEVELS_TV,
868 [YCBCR_BT709_PC] = MP_CSP_LEVELS_PC,
869 [YCBCR_SMPTE240M_TV] = MP_CSP_LEVELS_TV,
870 [YCBCR_SMPTE240M_PC] = MP_CSP_LEVELS_PC,
871 };
872 int trackcsp = track->YCbCrMatrix;
873 if (force_601)
874 trackcsp = YCBCR_BT601_TV;
875 // NONE is a bit random, but the intention is: don't modify colors.
876 if (trackcsp == YCBCR_NONE)
877 return;
878 if (trackcsp < sizeof(ass_csp) / sizeof(ass_csp[0]))
879 csp = ass_csp[trackcsp];
880 if (trackcsp < sizeof(ass_levels) / sizeof(ass_levels[0]))
881 levels = ass_levels[trackcsp];
882 if (trackcsp == YCBCR_DEFAULT) {
883 csp = MP_CSP_BT_601;
884 levels = MP_CSP_LEVELS_TV;
885 }
886 // Unknown colorspace (either YCBCR_UNKNOWN, or a valid value unknown to us)
887 if (!csp || !levels)
888 return;
889
890 struct mp_image_params params = ctx->video_params;
891
892 if (force_601) {
893 params.color = (struct mp_colorspace){
894 .space = MP_CSP_BT_709,
895 .levels = MP_CSP_LEVELS_TV,
896 };
897 }
898
899 if (csp == params.color.space && levels == params.color.levels)
900 return;
901
902 bool basic_conv = params.color.space == MP_CSP_BT_709 &&
903 params.color.levels == MP_CSP_LEVELS_TV &&
904 csp == MP_CSP_BT_601 &&
905 levels == MP_CSP_LEVELS_TV;
906
907 // With "basic", only do as much as needed for basic compatibility.
908 if (opts->ass_vsfilter_color_compat == 1 && !basic_conv)
909 return;
910
911 if (params.color.space != ctx->last_params.color.space ||
912 params.color.levels != ctx->last_params.color.levels)
913 {
914 int msgl = basic_conv ? MSGL_V : MSGL_WARN;
915 ctx->last_params = params;
916 MP_MSG(sd, msgl, "mangling colors like vsfilter: "
917 "RGB -> %s %s -> %s %s -> RGB\n",
918 m_opt_choice_str(mp_csp_names, csp),
919 m_opt_choice_str(mp_csp_levels_names, levels),
920 m_opt_choice_str(mp_csp_names, params.color.space),
921 m_opt_choice_str(mp_csp_names, params.color.levels));
922 }
923
924 // Conversion that VSFilter would use
925 struct mp_csp_params vs_params = MP_CSP_PARAMS_DEFAULTS;
926 vs_params.color.space = csp;
927 vs_params.color.levels = levels;
928 struct mp_cmat vs_yuv2rgb, vs_rgb2yuv;
929 mp_get_csp_matrix(&vs_params, &vs_yuv2rgb);
930 mp_invert_cmat(&vs_rgb2yuv, &vs_yuv2rgb);
931
932 // Proper conversion to RGB
933 struct mp_csp_params rgb_params = MP_CSP_PARAMS_DEFAULTS;
934 rgb_params.color = params.color;
935 struct mp_cmat vs2rgb;
936 mp_get_csp_matrix(&rgb_params, &vs2rgb);
937
938 for (int n = 0; n < parts->num_parts; n++) {
939 struct sub_bitmap *sb = &parts->parts[n];
940 uint32_t color = sb->libass.color;
941 int r = (color >> 24u) & 0xff;
942 int g = (color >> 16u) & 0xff;
943 int b = (color >> 8u) & 0xff;
944 int a = 0xff - (color & 0xff);
945 int rgb[3] = {r, g, b}, yuv[3];
946 mp_map_fixp_color(&vs_rgb2yuv, 8, rgb, 8, yuv);
947 mp_map_fixp_color(&vs2rgb, 8, yuv, 8, rgb);
948 sb->libass.color = MP_ASS_RGBA(rgb[0], rgb[1], rgb[2], a);
949 }
950 }
951
sd_ass_fmt_offset(const char * evt_fmt)952 int sd_ass_fmt_offset(const char *evt_fmt)
953 {
954 // "Text" is always last (as it's arbitrary content in buf), e.g. format:
955 // "Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"
956 int n = 0;
957 while (evt_fmt && (evt_fmt = strchr(evt_fmt, ',')))
958 evt_fmt++, n++;
959 return n-1; // buffer is without the format's Start/End, with ReadOrder
960 }
961
sd_ass_pkt_text(struct sd_filter * ft,struct demux_packet * pkt,int offset)962 bstr sd_ass_pkt_text(struct sd_filter *ft, struct demux_packet *pkt, int offset)
963 {
964 // e.g. pkt->buffer ("4" is ReadOrder): "4,0,Default,,0,0,0,,fifth line"
965 bstr txt = {(char *)pkt->buffer, pkt->len}, t0 = txt;
966 while (offset-- > 0) {
967 int n = bstrchr(txt, ',');
968 if (n < 0) { // shouldn't happen
969 MP_WARN(ft, "Malformed event '%.*s'\n", BSTR_P(t0));
970 return (bstr){NULL, 0};
971 }
972 txt = bstr_cut(txt, n+1);
973 }
974 return txt;
975 }
976
sd_ass_to_plaintext(char * out,size_t out_siz,const char * in)977 bstr sd_ass_to_plaintext(char *out, size_t out_siz, const char *in)
978 {
979 struct buf b = {out, out_siz, 0};
980 ass_to_plaintext(&b, in);
981 if (b.len < out_siz)
982 out[b.len] = 0;
983 return (bstr){out, b.len};
984 }
985