Coverage Report

Created: 2026-06-13 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/sub/osd_libass.c
Line
Count
Source
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 <math.h>
19
#include <stdio.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <assert.h>
23
24
#include "mpv_talloc.h"
25
#include "misc/bstr.h"
26
#include "common/common.h"
27
#include "common/msg.h"
28
#include "osd.h"
29
#include "osd_state.h"
30
31
static const char osd_font_pfb[] =
32
#include "sub/osd_font.otf.inc"
33
;
34
35
#include "sub/ass_mp.h"
36
#include "sub/packer.h"
37
#include "options/options.h"
38
39
40
536
#define ASS_USE_OSD_FONT "{\\fnmpv-osd-symbols}"
41
42
static void append_ass(struct ass_state *ass, struct mp_osd_res *res,
43
                       ASS_Image **img_list, bool *changed);
44
45
static void create_ass_renderer(struct osd_state *osd, struct ass_state *ass)
46
1.05k
{
47
1.05k
    if (ass->render)
48
670
        return;
49
50
386
    ass->log = mp_log_new(NULL, osd->log, "libass");
51
386
    ass->library = mp_ass_init(osd->global, osd->opts->osd_style, ass->log);
52
386
    ass_add_font(ass->library, "mpv-osd-symbols", (void *)osd_font_pfb,
53
386
                 sizeof(osd_font_pfb) - 1);
54
55
386
    ass->render = ass_renderer_init(ass->library);
56
386
    if (!ass->render)
57
0
        abort();
58
59
386
    mp_ass_configure_fonts(ass->render, osd->opts->osd_style,
60
386
                           osd->global, ass->log);
61
386
    ass_set_pixel_aspect(ass->render, 1.0);
62
386
}
63
64
static void destroy_ass_renderer(struct ass_state *ass)
65
716k
{
66
716k
    if (ass->track)
67
386
        ass_free_track(ass->track);
68
716k
    ass->track = NULL;
69
716k
    if (ass->render)
70
386
        ass_renderer_done(ass->render);
71
716k
    ass->render = NULL;
72
716k
    if (ass->library)
73
386
        ass_library_done(ass->library);
74
716k
    ass->library = NULL;
75
716k
    talloc_free(ass->log);
76
716k
    ass->log = NULL;
77
716k
}
78
79
static void destroy_external(struct osd_external *ext)
80
0
{
81
0
    destroy_ass_renderer(&ext->ass);
82
0
    talloc_free(ext);
83
0
}
84
85
void osd_destroy_backend(struct osd_state *osd)
86
143k
{
87
859k
    for (int n = 0; n < MAX_OSD_PARTS; n++) {
88
716k
        struct osd_object *obj = osd->objs[n];
89
716k
        destroy_ass_renderer(&obj->ass);
90
716k
        for (int i = 0; i < obj->num_externals; i++)
91
0
            destroy_external(obj->externals[i]);
92
716k
        obj->num_externals = 0;
93
716k
    }
94
143k
}
95
96
static void update_playres(struct ass_state *ass, struct mp_osd_res *vo_res)
97
1.32k
{
98
1.32k
    ASS_Track *track = ass->track;
99
1.32k
    int old_res_x = track->PlayResX;
100
1.32k
    int old_res_y = track->PlayResY;
101
102
1.32k
    ass->vo_res = *vo_res;
103
104
1.32k
    double aspect = 1.0 * vo_res->w / MPMAX(vo_res->h, 1);
105
1.32k
    if (vo_res->display_par > 0)
106
587
        aspect = aspect / vo_res->display_par;
107
108
1.32k
    track->PlayResY = ass->res_y ? ass->res_y : MP_ASS_FONT_PLAYRESY;
109
1.32k
    track->PlayResX = ass->res_x ? ass->res_x : track->PlayResY * aspect;
110
111
    // Force libass to clear its internal cache - it doesn't check for
112
    // PlayRes changes itself.
113
1.32k
    if (old_res_x != track->PlayResX || old_res_y != track->PlayResY)
114
386
        ass_set_frame_size(ass->render, 1, 1);
115
1.32k
}
116
117
static void create_ass_track(struct osd_state *osd, struct osd_object *obj,
118
                             struct ass_state *ass)
119
1.05k
{
120
1.05k
    create_ass_renderer(osd, ass);
121
122
1.05k
    ASS_Track *track = ass->track;
123
1.05k
    struct mp_osd_render_opts *opts = osd->opts;
124
1.05k
    if (!track)
125
386
        track = ass->track = ass_new_track(ass->library);
126
127
1.05k
    track->track_type = TRACK_TYPE_ASS;
128
1.05k
    track->Timer = 100.;
129
1.05k
    track->WrapStyle = 1; // end-of-line wrapping instead of smart wrapping
130
1.05k
    track->Kerning = true;
131
1.05k
    track->ScaledBorderAndShadow = true;
132
1.05k
    ass_set_shaper(ass->render, opts->osd_shaper);
133
1.05k
#if LIBASS_VERSION >= 0x01600010
134
1.05k
    ass_track_set_feature(track, ASS_FEATURE_WRAP_UNICODE, 1);
135
1.05k
#endif
136
1.05k
#if LIBASS_VERSION >= 0x01703010
137
1.05k
    ass_configure_prune(track, opts->osd_ass_prune_delay * 1000.0);
138
1.05k
#endif
139
1.05k
    ass_set_cache_limits(ass->render, opts->osd_glyph_limit, opts->osd_bitmap_max_size);
140
1.05k
    update_playres(ass, &obj->vo_res);
141
1.05k
}
142
143
static int find_style(ASS_Track *track, const char *name, int def)
144
1.32k
{
145
2.65k
    for (int n = 0; n < track->n_styles; n++) {
146
2.26k
        if (track->styles[n].Name && strcmp(track->styles[n].Name, name) == 0)
147
941
            return n;
148
2.26k
    }
149
386
    return def;
150
1.32k
}
151
152
// Find a given style, or add it if it's missing.
153
static ASS_Style *get_style(struct ass_state *ass, char *name)
154
1.05k
{
155
1.05k
    ASS_Track *track = ass->track;
156
1.05k
    if (!track)
157
0
        return NULL;
158
159
1.05k
    int sid = find_style(track, name, -1);
160
1.05k
    if (sid >= 0)
161
670
        return &track->styles[sid];
162
163
386
    sid = ass_alloc_style(track);
164
386
    ASS_Style *style = &track->styles[sid];
165
386
    style->Name = strdup(name);
166
    // Set to neutral base direction, as opposed to VSFilter LTR default
167
386
    style->Encoding = -1;
168
386
    return style;
169
1.05k
}
170
171
static ASS_Event *add_osd_ass_event(ASS_Track *track, const char *style,
172
                                    bstr text)
173
271
{
174
271
    int n = ass_alloc_event(track);
175
271
    ASS_Event *event = track->events + n;
176
271
    event->Start = 0;
177
271
    event->Duration = 100;
178
271
    event->Style = find_style(track, style, 0);
179
271
    event->ReadOrder = n;
180
271
    mp_assert(event->Text == NULL);
181
271
    if (text.start) {
182
270
        event->Text = malloc(text.len + 1);
183
270
        MP_HANDLE_OOM(event->Text);
184
270
        memcpy(event->Text, text.start, text.len);
185
270
        event->Text[text.len] = '\0';
186
270
    }
187
271
    return event;
188
271
}
189
190
static void clear_ass(struct ass_state *ass)
191
346
{
192
346
    if (ass->track)
193
12
        ass_flush_events(ass->track);
194
346
}
195
196
void osd_get_function_sym(char *buffer, size_t buffer_size, int osd_function)
197
493
{
198
493
    snprintf(buffer, buffer_size, OSD_CODEPOINTS_ESCAPE "%c", osd_function);
199
493
}
200
201
void osd_mangle_ass(bstr *dst, const char *in, bool replace_newlines)
202
271
{
203
271
    const char *start = in;
204
271
    bool escape_ass = true;
205
220k
    while (*in) {
206
        // As used by osd_get_function_sym().
207
220k
        size_t len = strlen(OSD_CODEPOINTS_ESCAPE);
208
220k
        if (!strncmp(in, OSD_CODEPOINTS_ESCAPE, len) && in[len]) {
209
536
            bstr_xappend(NULL, dst, bstr0(ASS_USE_OSD_FONT));
210
536
            mp_append_utf8_bstr(NULL, dst, OSD_CODEPOINTS + in[len]);
211
536
            bstr_xappend(NULL, dst, bstr0("{\\r}"));
212
536
            in += len + 1;
213
536
            continue;
214
536
        }
215
219k
        len = strlen(OSD_ASS_0);
216
219k
        if (!strncmp(in, OSD_ASS_0, len)) {
217
131
            escape_ass = false;
218
131
            in += len;
219
131
            continue;
220
131
        }
221
219k
        len = strlen(OSD_ASS_1);
222
219k
        if (!strncmp(in, OSD_ASS_1, len)) {
223
11
            escape_ass = true;
224
11
            in += len;
225
11
            continue;
226
11
        }
227
219k
        len = strlen(TERM_MSG_0);
228
219k
        if (!strncmp(in, TERM_MSG_0, len)) {
229
48
            in += len;
230
48
            continue;
231
48
        }
232
219k
        if (escape_ass && *in == '{')
233
1.05k
            bstr_xappend(NULL, dst, bstr0("\\"));
234
        // Replace newlines with \N for escape-ass. This is necessary to apply
235
        // ASS tags past newlines and to preserve consecutive newlines with
236
        // osd-overlay because update_external() adds a ASS event per line.
237
219k
        if (replace_newlines && *in == '\n') {
238
0
            bstr_xappend(NULL, dst, bstr0("\\N"));
239
0
            in += 1;
240
0
            continue;
241
0
        }
242
        // Libass will strip leading whitespace
243
219k
        if (in[0] == ' ' && (in == start || in[-1] == '\n')) {
244
8.28k
            bstr_xappend(NULL, dst, bstr0("\\h"));
245
8.28k
            in += 1;
246
8.28k
            continue;
247
8.28k
        }
248
211k
        bstr_xappend(NULL, dst, (bstr){(char *)in, 1});
249
        // Break ASS escapes with U+2060 WORD JOINER
250
211k
        if (escape_ass && *in == '\\')
251
577
            mp_append_utf8_bstr(NULL, dst, 0x2060);
252
211k
        in++;
253
211k
    }
254
271
}
255
256
static ASS_Event *add_osd_ass_event_escaped(ASS_Track *track, const char *style,
257
                                            const char *text)
258
271
{
259
271
    bstr buf = {0};
260
271
    osd_mangle_ass(&buf, text, false);
261
271
    ASS_Event *e = add_osd_ass_event(track, style, buf);
262
271
    talloc_free(buf.start);
263
271
    return e;
264
271
}
265
266
static ASS_Style *prepare_osd_ass(struct osd_state *osd, struct osd_object *obj)
267
1.05k
{
268
1.05k
    struct mp_osd_render_opts *opts = osd->opts;
269
270
1.05k
    create_ass_track(osd, obj, &obj->ass);
271
272
1.05k
    struct osd_style_opts font = *opts->osd_style;
273
1.05k
    font.font_size *= opts->osd_scale;
274
275
1.05k
    double playresy = obj->ass.track->PlayResY;
276
    // Compensate for libass and mp_ass_set_style scaling the font etc.
277
1.05k
    if (!opts->osd_scale_by_window && obj->vo_res.h)
278
0
        playresy *= 720.0 / obj->vo_res.h;
279
280
1.05k
    ASS_Style *style = get_style(&obj->ass, "OSD");
281
1.05k
    mp_ass_set_style(style, playresy, &font);
282
1.05k
    return style;
283
1.05k
}
284
285
static void update_osd_text(struct osd_state *osd, struct osd_object *obj)
286
346
{
287
288
346
    if (!obj->text[0])
289
75
        return;
290
291
271
    prepare_osd_ass(osd, obj);
292
271
    add_osd_ass_event_escaped(obj->ass.track, "OSD", obj->text);
293
271
}
294
295
void osd_get_text_size(struct osd_state *osd, int *out_screen_h, int *out_font_h)
296
785
{
297
785
    mp_mutex_lock(&osd->lock);
298
785
    struct osd_object *obj = osd->objs[OSDTYPE_OSD];
299
785
    ASS_Style *style = prepare_osd_ass(osd, obj);
300
785
    *out_screen_h = obj->ass.track->PlayResY - style->MarginV;
301
785
    *out_font_h = style->FontSize;
302
785
    mp_mutex_unlock(&osd->lock);
303
785
}
304
305
// align: -1 .. +1
306
// frame: size of the containing area
307
// obj: size of the object that should be positioned inside the area
308
// margin: min. distance from object to frame (as long as -1 <= align <= +1)
309
static float get_align(float align, float frame, float obj, float margin)
310
0
{
311
0
    frame -= margin * 2;
312
0
    return margin + frame / 2 - obj / 2 + (frame - obj) / 2 * align;
313
0
}
314
315
struct ass_draw {
316
    int scale;
317
    char *text;
318
};
319
320
static void ass_draw_start(struct ass_draw *d)
321
0
{
322
0
    d->scale = MPMAX(d->scale, 1);
323
0
    d->text = talloc_asprintf_append(d->text, "{\\p%d}", d->scale);
324
0
}
325
326
static void ass_draw_stop(struct ass_draw *d)
327
0
{
328
0
    d->text = talloc_strdup_append(d->text, "{\\p0}");
329
0
}
330
331
static void ass_draw_c(struct ass_draw *d, float x, float y)
332
0
{
333
0
    int ix = round(x * (1 << (d->scale - 1)));
334
0
    int iy = round(y * (1 << (d->scale - 1)));
335
0
    d->text = talloc_asprintf_append(d->text, " %d %d", ix, iy);
336
0
}
337
338
static void ass_draw_append(struct ass_draw *d, const char *t)
339
0
{
340
0
    d->text = talloc_strdup_append(d->text, t);
341
0
}
342
343
static void ass_draw_move_to(struct ass_draw *d, float x, float y)
344
0
{
345
0
    ass_draw_append(d, " m");
346
0
    ass_draw_c(d, x, y);
347
0
}
348
349
static void ass_draw_line_to(struct ass_draw *d, float x, float y)
350
0
{
351
0
    ass_draw_append(d, " l");
352
0
    ass_draw_c(d, x, y);
353
0
}
354
355
static void ass_draw_rect_ccw(struct ass_draw *d, float x0, float y0,
356
                              float x1, float y1)
357
0
{
358
0
    ass_draw_move_to(d, x0, y0);
359
0
    ass_draw_line_to(d, x0, y1);
360
0
    ass_draw_line_to(d, x1, y1);
361
0
    ass_draw_line_to(d, x1, y0);
362
0
}
363
364
static void ass_draw_rect_cw(struct ass_draw *d, float x0, float y0,
365
                             float x1, float y1)
366
0
{
367
0
    ass_draw_move_to(d, x0, y0);
368
0
    ass_draw_line_to(d, x1, y0);
369
0
    ass_draw_line_to(d, x1, y1);
370
0
    ass_draw_line_to(d, x0, y1);
371
0
}
372
373
static void ass_draw_reset(struct ass_draw *d)
374
0
{
375
0
    talloc_free(d->text);
376
0
    d->text = NULL;
377
0
}
378
379
static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj,
380
                            float *o_x, float *o_y, float *o_w, float *o_h,
381
                            float *o_border)
382
0
{
383
0
    struct mp_osd_render_opts *opts = osd->opts;
384
0
    struct osd_bar_style_opts *bar_opts = opts->osd_bar_style;
385
386
0
    create_ass_track(osd, obj, &obj->ass);
387
0
    ASS_Track *track = obj->ass.track;
388
389
0
    ASS_Style *style = get_style(&obj->ass, "progbar");
390
0
    if (!style) {
391
0
        *o_x = *o_y = *o_w = *o_h = *o_border = 0;
392
0
        return;
393
0
    }
394
395
0
    mp_ass_set_style(style, track->PlayResY, opts->osd_style);
396
397
    // override the default osd opaque-box into plain outline. Otherwise
398
    // the opaque box is not aligned with the bar (even without shadow),
399
    // and each bar ass event gets its own opaque box - breaking the bar.
400
0
    style->BorderStyle = 1; // outline
401
402
0
    *o_w = track->PlayResX * (bar_opts->w / 100.0);
403
0
    *o_h = track->PlayResY * (bar_opts->h / 100.0);
404
405
0
    style->Outline = bar_opts->outline_size;
406
    // Rendering with shadow is broken (because there's more than one shape)
407
0
    style->Shadow = 0;
408
0
    style->Blur = 0;
409
410
0
    style->Alignment = 5;
411
412
0
    *o_border = style->Outline;
413
414
0
    *o_x = get_align(bar_opts->align_x, track->PlayResX, *o_w, *o_border);
415
0
    *o_y = get_align(bar_opts->align_y, track->PlayResY, *o_h, *o_border);
416
0
}
417
418
static void update_progbar(struct osd_state *osd, struct osd_object *obj)
419
346
{
420
346
    if (obj->progbar_state.type < 0)
421
346
        return;
422
423
0
    float px, py, width, height, border;
424
0
    get_osd_bar_box(osd, obj, &px, &py, &width, &height, &border);
425
426
0
    ASS_Track *track = obj->ass.track;
427
428
0
    float sx = px - border * 2 - height / 4; // includes additional spacing
429
0
    float sy = py + height / 2;
430
431
0
    bstr buf = bstr0(talloc_asprintf(NULL, "{\\an6\\pos(%f,%f)}", sx, sy));
432
433
0
    if (obj->progbar_state.type == 0 || obj->progbar_state.type >= 256) {
434
        // no sym
435
0
    } else if (obj->progbar_state.type >= 32) {
436
0
        mp_append_utf8_bstr(NULL, &buf, obj->progbar_state.type);
437
0
    } else {
438
0
        bstr_xappend(NULL, &buf, bstr0(ASS_USE_OSD_FONT));
439
0
        mp_append_utf8_bstr(NULL, &buf, OSD_CODEPOINTS + obj->progbar_state.type);
440
0
        bstr_xappend(NULL, &buf, bstr0("{\\r}"));
441
0
    }
442
443
0
    add_osd_ass_event(track, "progbar", buf);
444
0
    talloc_free(buf.start);
445
446
0
    struct ass_draw *d = &(struct ass_draw) { .scale = 4 };
447
448
0
    if (osd->opts->osd_style->back_color.a && osd->opts->osd_style->border_style != 1) {
449
        // the bar style always ignores the --osd-back-color config - it messes
450
        // up the bar. draw an artificial box at the original back color.
451
0
        struct m_color bc = osd->opts->osd_style->back_color;
452
0
        d->text = talloc_asprintf_append(d->text,
453
0
            "{\\pos(%f,%f)\\bord0\\1a&H%02X\\1c&H%02X%02X%02X&}",
454
0
             px, py, 255 - bc.a, (int)bc.b, (int)bc.g, (int)bc.r);
455
456
0
        ass_draw_start(d);
457
0
        ass_draw_rect_cw(d, -border, -border, width + border, height + border);
458
0
        ass_draw_stop(d);
459
0
        add_osd_ass_event(track, "progbar", bstr0(d->text));
460
0
        ass_draw_reset(d);
461
0
    }
462
463
    // filled area
464
0
    d->text = talloc_asprintf_append(d->text, "{\\bord0\\pos(%f,%f)}", px, py);
465
0
    ass_draw_start(d);
466
0
    float pos = obj->progbar_state.value * width - border / 2;
467
0
    ass_draw_rect_cw(d, 0, 0, pos, height);
468
0
    ass_draw_stop(d);
469
0
    add_osd_ass_event(track, "progbar", bstr0(d->text));
470
0
    ass_draw_reset(d);
471
472
    // position marker
473
0
    d->text = talloc_asprintf_append(d->text, "{\\bord%f\\pos(%f,%f)}",
474
0
                                     border / 2, px, py);
475
0
    ass_draw_start(d);
476
0
    ass_draw_move_to(d, pos + border / 2, 0);
477
0
    ass_draw_line_to(d, pos + border / 2, height);
478
0
    ass_draw_stop(d);
479
0
    add_osd_ass_event(track, "progbar", bstr0(d->text));
480
0
    ass_draw_reset(d);
481
482
0
    d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py);
483
0
    ass_draw_start(d);
484
485
    // the box
486
0
    ass_draw_rect_cw(d, -border, -border, width + border, height + border);
487
488
    // the "hole"
489
0
    ass_draw_rect_ccw(d, 0, 0, width, height);
490
491
0
    struct osd_bar_style_opts *bar_opts = osd->opts->osd_bar_style;
492
    // chapter marks
493
0
    if (bar_opts->marker_style) {
494
0
        for (int n = 0; n < obj->progbar_state.num_stops; n++) {
495
0
            float s = obj->progbar_state.stops[n] * width;
496
0
            float size = MPMAX(border * bar_opts->marker_scale,
497
0
                               bar_opts->marker_min_size);
498
499
0
            if (bar_opts->marker_style == 2 &&
500
0
                s > size / 2 && s < width - size / 2)
501
0
            { // line
502
0
                ass_draw_rect_cw(d, s - size / 2, 0, s + size / 2, height);
503
0
            } else if (s > size && s < width - size) { //triangle
504
0
                ass_draw_move_to(d, s + size, 0);
505
0
                ass_draw_line_to(d, s,        size);
506
0
                ass_draw_line_to(d, s - size, 0);
507
508
0
                ass_draw_move_to(d, s - size, height);
509
0
                ass_draw_line_to(d, s,        height - size);
510
0
                ass_draw_line_to(d, s + size, height);
511
0
            }
512
0
        }
513
0
    }
514
515
0
    ass_draw_stop(d);
516
0
    add_osd_ass_event(track, "progbar", bstr0(d->text));
517
0
    ass_draw_reset(d);
518
0
}
519
520
static void update_osd(struct osd_state *osd, struct osd_object *obj)
521
346
{
522
346
    obj->osd_changed = false;
523
346
    clear_ass(&obj->ass);
524
346
    update_osd_text(osd, obj);
525
346
    update_progbar(osd, obj);
526
346
}
527
528
static void update_external(struct osd_state *osd, struct osd_object *obj,
529
                            struct osd_external *ext)
530
0
{
531
0
    bstr t = bstr0(ext->ov.data);
532
0
    ext->ass.res_x = ext->ov.res_x;
533
0
    ext->ass.res_y = ext->ov.res_y;
534
0
    create_ass_track(osd, obj, &ext->ass);
535
536
0
    clear_ass(&ext->ass);
537
538
0
    int resy = ext->ass.track->PlayResY;
539
0
    mp_ass_set_style(get_style(&ext->ass, "OSD"), resy, osd->opts->osd_style);
540
541
    // Some scripts will reference this style name with \r tags.
542
0
    const struct osd_style_opts *def = osd_style_conf.defaults;
543
0
    mp_ass_set_style(get_style(&ext->ass, "Default"), resy, def);
544
545
0
    while (t.len) {
546
0
        bstr line;
547
0
        bstr_split_tok(t, "\n", &line, &t);
548
0
        if (line.len)
549
0
            add_osd_ass_event(ext->ass.track, "OSD", line);
550
0
    }
551
0
}
552
553
static int cmp_zorder(const void *pa, const void *pb)
554
0
{
555
0
    const struct osd_external *a = *(struct osd_external **)pa;
556
0
    const struct osd_external *b = *(struct osd_external **)pb;
557
0
    return a->ov.z == b->ov.z ? 0 : (a->ov.z > b->ov.z ? 1 : -1);
558
0
}
559
560
void osd_set_external(struct osd_state *osd, struct osd_external_ass *ov)
561
0
{
562
0
    mp_mutex_lock(&osd->lock);
563
0
    struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL];
564
0
    bool zorder_changed = false;
565
0
    int index = -1;
566
567
0
    for (int n = 0; n < obj->num_externals; n++) {
568
0
        struct osd_external *e = obj->externals[n];
569
0
        if (e->ov.id == ov->id && e->ov.owner == ov->owner) {
570
0
            index = n;
571
0
            break;
572
0
        }
573
0
    }
574
575
0
    if (index < 0) {
576
0
        if (!ov->format)
577
0
            goto done;
578
0
        struct osd_external *new = talloc_zero(NULL, struct osd_external);
579
0
        new->ov.owner = ov->owner;
580
0
        new->ov.id = ov->id;
581
0
        MP_TARRAY_APPEND(obj, obj->externals, obj->num_externals, new);
582
0
        index = obj->num_externals - 1;
583
0
        zorder_changed = true;
584
0
    }
585
586
0
    struct osd_external *entry = obj->externals[index];
587
588
0
    if (!ov->format) {
589
0
        if (!entry->ov.hidden) {
590
0
            obj->changed = true;
591
0
            osd->want_redraw_notification = true;
592
0
        }
593
0
        destroy_external(entry);
594
0
        MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, index);
595
0
        goto done;
596
0
    }
597
598
0
    if (!entry->ov.hidden || !ov->hidden) {
599
0
        obj->changed = true;
600
0
        osd->want_redraw_notification = true;
601
0
    }
602
603
0
    entry->ov.format = ov->format;
604
0
    if (!entry->ov.data)
605
0
        entry->ov.data = talloc_strdup(entry, "");
606
0
    entry->ov.data[0] = '\0'; // reuse memory allocation
607
0
    entry->ov.data = talloc_strdup_append(entry->ov.data, ov->data);
608
0
    entry->ov.res_x = ov->res_x;
609
0
    entry->ov.res_y = ov->res_y;
610
0
    zorder_changed |= entry->ov.z != ov->z;
611
0
    entry->ov.z = ov->z;
612
0
    entry->ov.hidden = ov->hidden;
613
614
0
    update_external(osd, obj, entry);
615
616
0
    if (zorder_changed) {
617
0
        qsort(obj->externals, obj->num_externals, sizeof(obj->externals[0]),
618
0
              cmp_zorder);
619
0
    }
620
621
0
    if (ov->out_rc) {
622
0
        struct mp_osd_res vo_res = entry->ass.vo_res;
623
        // Defined fallback if VO has not drawn this yet
624
0
        if (vo_res.w < 1 || vo_res.h < 1) {
625
0
            vo_res = (struct mp_osd_res){
626
0
                .w = entry->ov.res_x,
627
0
                .h = entry->ov.res_y,
628
0
                .display_par = 1,
629
0
            };
630
            // According to osd-overlay command description.
631
0
            if (vo_res.w < 1)
632
0
                vo_res.w = 1280;
633
0
            if (vo_res.h < 1)
634
0
                vo_res.h = 720;
635
0
        }
636
637
0
        ASS_Image *img_list = NULL;
638
0
        append_ass(&entry->ass, &vo_res, &img_list, NULL);
639
640
0
        mp_ass_get_bb(img_list, entry->ass.track, &vo_res, ov->out_rc);
641
0
    }
642
643
0
done:
644
0
    mp_mutex_unlock(&osd->lock);
645
0
}
646
647
void osd_set_external_remove_owner(struct osd_state *osd, void *owner)
648
274k
{
649
274k
    mp_mutex_lock(&osd->lock);
650
274k
    struct osd_object *obj = osd->objs[OSDTYPE_EXTERNAL];
651
274k
    for (int n = obj->num_externals - 1; n >= 0; n--) {
652
0
        struct osd_external *e = obj->externals[n];
653
0
        if (e->ov.owner == owner) {
654
0
            destroy_external(e);
655
0
            MP_TARRAY_REMOVE_AT(obj->externals, obj->num_externals, n);
656
0
            obj->changed = true;
657
0
            osd->want_redraw_notification = true;
658
0
        }
659
0
    }
660
274k
    mp_mutex_unlock(&osd->lock);
661
274k
}
662
663
static void append_ass(struct ass_state *ass, struct mp_osd_res *res,
664
                       ASS_Image **img_list, bool *changed)
665
994
{
666
994
    if (!ass->render || !ass->track) {
667
723
        *img_list = NULL;
668
723
        return;
669
723
    }
670
671
271
    update_playres(ass, res);
672
673
271
    ass_set_frame_size(ass->render, res->w, res->h);
674
271
    ass_set_pixel_aspect(ass->render, res->display_par);
675
676
271
    int ass_changed;
677
271
    *img_list = ass_render_frame(ass->render, ass->track, 0, &ass_changed);
678
679
271
    ass->changed |= ass_changed;
680
681
271
    if (changed) {
682
271
        *changed |= ass->changed;
683
271
        ass->changed = false;
684
271
    }
685
271
}
686
687
struct sub_bitmaps *osd_object_get_bitmaps(struct osd_state *osd,
688
                                           struct osd_object *obj, int format)
689
1.29k
{
690
1.29k
    if (obj->type == OSDTYPE_OSD) {
691
648
        if (obj->osd_changed) {
692
346
            update_osd(osd, obj);
693
346
        } else {
694
302
            mp_require(obj->sub_packer);
695
302
            goto done;
696
302
        }
697
648
    }
698
699
994
    if (!obj->sub_packer)
700
660
        obj->sub_packer = mp_sub_packer_alloc(obj);
701
702
994
    MP_TARRAY_GROW(obj, obj->ass_imgs, obj->num_externals + 1);
703
704
994
    append_ass(&obj->ass, &obj->vo_res, &obj->ass_imgs[0], &obj->changed);
705
994
    for (int n = 0; n < obj->num_externals; n++) {
706
0
        if (obj->externals[n]->ov.hidden) {
707
0
            update_playres(&obj->externals[n]->ass, &obj->vo_res);
708
0
            obj->ass_imgs[n + 1] = NULL;
709
0
        } else {
710
0
            append_ass(&obj->externals[n]->ass, &obj->vo_res,
711
0
                       &obj->ass_imgs[n + 1], &obj->changed);
712
0
        }
713
0
    }
714
715
1.29k
done:;
716
1.29k
    struct sub_bitmaps out_imgs = {0};
717
1.29k
    mp_sub_packer_pack_ass(obj->sub_packer, obj->ass_imgs, obj->num_externals + 1,
718
1.29k
                       obj->changed, false, format, &out_imgs);
719
720
1.29k
    obj->changed = false;
721
722
1.29k
    return sub_bitmaps_copy(&obj->copy_cache, &out_imgs);
723
994
}