Coverage Report

Created: 2026-05-16 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/sub/dec_sub.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 <stdlib.h>
19
#include <stdbool.h>
20
#include <string.h>
21
#include <math.h>
22
#include <assert.h>
23
#include <limits.h>
24
25
#include "demux/demux.h"
26
#include "demux/packet_pool.h"
27
#include "sd.h"
28
#include "dec_sub.h"
29
#include "options/m_config.h"
30
#include "options/options.h"
31
#include "common/global.h"
32
#include "common/msg.h"
33
#include "common/recorder.h"
34
#include "misc/dispatch.h"
35
#include "osdep/threads.h"
36
37
extern const struct sd_functions sd_ass;
38
extern const struct sd_functions sd_lavc;
39
#if HAVE_SUBRANDR
40
extern const struct sd_functions sd_sbr;
41
#endif
42
43
static const struct sd_functions *const sd_list[] = {
44
    &sd_lavc,
45
#if HAVE_SUBRANDR
46
    &sd_sbr,
47
#endif
48
    &sd_ass,
49
    NULL
50
};
51
52
struct dec_sub {
53
    mp_mutex lock;
54
55
    struct mp_log *log;
56
    struct mpv_global *global;
57
    struct demux_packet_pool *packet_pool;
58
    struct mp_subtitle_opts *opts;
59
    struct mp_subtitle_shared_opts *shared_opts;
60
    struct m_config_cache *opts_cache;
61
    struct m_config_cache *shared_opts_cache;
62
63
    struct mp_recorder_sink *recorder_sink;
64
65
    struct attachment_list *attachments;
66
67
    struct sh_stream *sh;
68
    int play_dir;
69
    int order;
70
    double last_pkt_pts;
71
    bool preload_attempted;
72
    double video_fps;
73
    double sub_speed;
74
    bool sub_visible;
75
76
    struct mp_codec_params *codec;
77
    double start, end;
78
    char *lang;
79
80
    double last_vo_pts;
81
    struct sd *sd;
82
83
    struct demux_packet *new_segment;
84
    struct demux_packet **cached_pkts;
85
    int cached_pkt_pos;
86
    int num_cached_pkts;
87
};
88
89
static void update_subtitle_speed(struct dec_sub *sub)
90
3.45k
{
91
3.45k
    struct mp_subtitle_opts *opts = sub->opts;
92
3.45k
    sub->sub_speed = 1.0;
93
94
3.45k
    if (sub->video_fps > 0 && sub->codec->frame_based > 0) {
95
0
        MP_VERBOSE(sub, "Frame based format, dummy FPS: %f, video FPS: %f\n",
96
0
                   sub->codec->frame_based, sub->video_fps);
97
0
        sub->sub_speed *= sub->codec->frame_based / sub->video_fps;
98
0
    }
99
100
3.45k
    if (opts->sub_fps && sub->video_fps)
101
0
        sub->sub_speed *= opts->sub_fps / sub->video_fps;
102
103
3.45k
    sub->sub_speed *= opts->sub_speed;
104
3.45k
}
105
106
// Return the subtitle PTS used for a given video PTS.
107
static double pts_to_subtitle(struct dec_sub *sub, double pts)
108
27.5k
{
109
27.5k
    struct mp_subtitle_shared_opts *opts = sub->shared_opts;
110
27.5k
    float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order];
111
112
27.5k
    if (pts != MP_NOPTS_VALUE)
113
23.7k
        pts = (pts * sub->play_dir - delay) / sub->sub_speed;
114
115
27.5k
    return pts;
116
27.5k
}
117
118
static double pts_from_subtitle(struct dec_sub *sub, double pts)
119
0
{
120
0
    struct mp_subtitle_shared_opts *opts = sub->shared_opts;
121
0
    float delay = sub->order < 0 ? 0.0f : opts->sub_delay[sub->order];
122
123
0
    if (pts != MP_NOPTS_VALUE)
124
0
        pts = (pts * sub->sub_speed + delay) * sub->play_dir;
125
126
0
    return pts;
127
0
}
128
129
static void wakeup_demux(void *ctx)
130
0
{
131
0
    struct mp_dispatch_queue *q = ctx;
132
0
    mp_dispatch_interrupt(q);
133
0
}
134
135
static void destroy_cached_pkts(struct dec_sub *sub)
136
3.45k
{
137
3.45k
    int index = 0;
138
5.00k
    while (index < sub->num_cached_pkts) {
139
1.55k
        demux_packet_pool_push(sub->packet_pool, sub->cached_pkts[index]);
140
1.55k
        sub->cached_pkts[index] = NULL;
141
1.55k
        ++index;
142
1.55k
    }
143
3.45k
    sub->cached_pkt_pos = 0;
144
3.45k
    sub->num_cached_pkts = 0;
145
3.45k
}
146
147
void sub_destroy(struct dec_sub *sub)
148
1.79k
{
149
1.79k
    if (!sub)
150
0
        return;
151
1.79k
    demux_set_stream_wakeup_cb(sub->sh, NULL, NULL);
152
1.79k
    if (sub->sd) {
153
1.72k
        sub_reset(sub);
154
1.72k
        sub->sd->driver->uninit(sub->sd);
155
1.72k
    }
156
1.79k
    talloc_free(sub->sd);
157
1.79k
    mp_mutex_destroy(&sub->lock);
158
1.79k
    talloc_free(sub);
159
1.79k
}
160
161
static struct sd *init_decoder(struct dec_sub *sub)
162
1.79k
{
163
3.43k
    for (int n = 0; sd_list[n]; n++) {
164
3.36k
        const struct sd_functions *driver = sd_list[n];
165
3.36k
        struct sd *sd = talloc(NULL, struct sd);
166
3.36k
        *sd = (struct sd){
167
3.36k
            .global = sub->global,
168
3.36k
            .log = mp_log_new(sd, sub->log, driver->name),
169
3.36k
            .opts = sub->opts,
170
3.36k
            .shared_opts = sub->shared_opts,
171
3.36k
            .driver = driver,
172
3.36k
            .order = sub->order,
173
3.36k
            .attachments = sub->attachments,
174
3.36k
            .codec = sub->codec,
175
3.36k
            .lang = sub->lang,
176
3.36k
            .preload_ok = true,
177
3.36k
        };
178
179
3.36k
        if (sd->driver->init(sd) >= 0)
180
1.72k
            return sd;
181
182
1.64k
        talloc_free(sd);
183
1.64k
    }
184
185
70
    MP_ERR(sub, "Could not find subtitle decoder for format '%s'.\n",
186
70
           sub->codec->codec);
187
70
    return NULL;
188
1.79k
}
189
190
// Thread-safety of the returned object: all functions are thread-safe,
191
// except sub_get_bitmaps() and sub_get_text(). Decoder backends (sd_*)
192
// do not need to acquire locks.
193
// Ownership of attachments goes to the callee, and is released with
194
// talloc_free() (even on failure).
195
struct dec_sub *sub_create(struct mpv_global *global, struct track *track,
196
                           struct attachment_list *attachments, int order)
197
1.79k
{
198
1.79k
    mp_assert(track->stream && track->stream->type == STREAM_SUB);
199
200
1.79k
    struct dec_sub *sub = talloc(NULL, struct dec_sub);
201
1.79k
    *sub = (struct dec_sub){
202
1.79k
        .log = mp_log_new(sub, global->log, "sub"),
203
1.79k
        .global = global,
204
1.79k
        .packet_pool = demux_packet_pool_get(global),
205
1.79k
        .opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_sub_opts),
206
1.79k
        .shared_opts_cache = m_config_cache_alloc(sub, global, &mp_subtitle_shared_sub_opts),
207
1.79k
        .sh = track->stream,
208
1.79k
        .codec = track->stream->codec,
209
1.79k
        .lang = track->lang,
210
1.79k
        .attachments = talloc_steal(sub, attachments),
211
1.79k
        .play_dir = 1,
212
1.79k
        .order = order,
213
1.79k
        .last_pkt_pts = MP_NOPTS_VALUE,
214
1.79k
        .last_vo_pts = MP_NOPTS_VALUE,
215
1.79k
        .start = MP_NOPTS_VALUE,
216
1.79k
        .end = MP_NOPTS_VALUE,
217
1.79k
    };
218
1.79k
    sub->opts = sub->opts_cache->opts;
219
1.79k
    sub->shared_opts = sub->shared_opts_cache->opts;
220
1.79k
    mp_mutex_init(&sub->lock);
221
222
1.79k
    sub->sd = init_decoder(sub);
223
1.79k
    if (sub->sd) {
224
1.72k
        update_subtitle_speed(sub);
225
1.72k
        return sub;
226
1.72k
    }
227
228
70
    sub_destroy(sub);
229
70
    return NULL;
230
1.79k
}
231
232
// Called locked.
233
static void update_segment(struct dec_sub *sub)
234
460
{
235
460
    if (sub->new_segment && sub->last_vo_pts != MP_NOPTS_VALUE &&
236
0
        sub->last_vo_pts >= sub->new_segment->start)
237
0
    {
238
0
        MP_VERBOSE(sub, "Switch segment: %f at %f\n", sub->new_segment->start,
239
0
                   sub->last_vo_pts);
240
241
0
        sub->codec = sub->new_segment->codec;
242
0
        sub->start = sub->new_segment->start;
243
0
        sub->end = sub->new_segment->end;
244
0
        struct sd *new = init_decoder(sub);
245
0
        if (new) {
246
0
            sub->sd->driver->uninit(sub->sd);
247
0
            talloc_free(sub->sd);
248
0
            sub->sd = new;
249
0
            update_subtitle_speed(sub);
250
0
        } else {
251
            // We'll just keep the current decoder, and feed it possibly
252
            // invalid data (not our fault if it crashes or something).
253
0
            MP_ERR(sub, "Can't change to new codec.\n");
254
0
        }
255
0
        sub->sd->driver->decode(sub->sd, sub->new_segment);
256
0
        talloc_free(sub->new_segment);
257
0
        sub->new_segment = NULL;
258
0
    }
259
460
}
260
261
bool sub_can_preload(struct dec_sub *sub)
262
0
{
263
0
    bool r;
264
0
    mp_mutex_lock(&sub->lock);
265
0
    r = sub->sd->driver->accept_packets_in_advance && !sub->preload_attempted;
266
0
    mp_mutex_unlock(&sub->lock);
267
0
    return r;
268
0
}
269
270
void sub_preload(struct dec_sub *sub)
271
0
{
272
0
    mp_mutex_lock(&sub->lock);
273
274
0
    struct mp_dispatch_queue *demux_waiter = mp_dispatch_create(NULL);
275
0
    demux_set_stream_wakeup_cb(sub->sh, wakeup_demux, demux_waiter);
276
277
0
    sub->preload_attempted = true;
278
279
0
    for (;;) {
280
0
        struct demux_packet *pkt = NULL;
281
0
        int r = demux_read_packet_async(sub->sh, &pkt);
282
0
        if (r == 0) {
283
0
            mp_dispatch_queue_process(demux_waiter, INFINITY);
284
0
            continue;
285
0
        }
286
0
        if (!pkt)
287
0
            break;
288
0
        sub->sd->driver->decode(sub->sd, pkt);
289
0
        MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt);
290
0
    }
291
292
0
    demux_set_stream_wakeup_cb(sub->sh, NULL, NULL);
293
0
    talloc_free(demux_waiter);
294
295
0
    mp_mutex_unlock(&sub->lock);
296
0
}
297
298
static bool is_new_segment(struct dec_sub *sub, struct demux_packet *p)
299
1.55k
{
300
1.55k
    return p->segmented &&
301
0
        (p->start != sub->start || p->end != sub->end || p->codec != sub->codec);
302
1.55k
}
303
304
static bool is_packet_visible(struct demux_packet *p, double video_pts)
305
16.4k
{
306
16.4k
    return p && p->pts <= video_pts && (video_pts <= p->pts + p->sub_duration ||
307
405
           p->sub_duration < 0);
308
16.4k
}
309
310
static bool update_pkt_cache(struct dec_sub *sub, double video_pts)
311
16.4k
{
312
16.4k
    if (!sub->cached_pkts[sub->cached_pkt_pos])
313
0
        return false;
314
315
16.4k
    struct demux_packet *pkt = sub->cached_pkts[sub->cached_pkt_pos];
316
16.4k
    struct demux_packet *next_pkt = sub->cached_pkt_pos + 1 < sub->num_cached_pkts ?
317
16.4k
                                    sub->cached_pkts[sub->cached_pkt_pos + 1] : NULL;
318
16.4k
    if (!pkt)
319
0
        return false;
320
321
16.4k
    double pts = video_pts + sub->shared_opts->sub_delay[sub->order];
322
16.4k
    double next_pts = next_pkt ? next_pkt->pts : INT_MAX;
323
16.4k
    double end_pts = pkt->sub_duration >= 0 ? pkt->pts + pkt->sub_duration : INT_MAX;
324
325
16.4k
    if (next_pts < pts || end_pts < pts) {
326
600
        if (sub->cached_pkt_pos + 1 < sub->num_cached_pkts) {
327
423
            TA_FREEP(&sub->cached_pkts[sub->cached_pkt_pos]);
328
423
            pkt = NULL;
329
423
            sub->cached_pkt_pos++;
330
423
        }
331
600
        if (next_pts < pts)
332
419
            return true;
333
600
    }
334
335
16.0k
    if (pkt && pkt->animated == 1)
336
102
        return true;
337
338
15.9k
    return false;
339
16.0k
}
340
341
// Read packets from the demuxer stream passed to sub_create(). Signals if
342
// enough packets were read and if the subtitle state updated in anyway. If
343
// packets_read is false, the player should wait until the demuxer signals new
344
// packets and retry.
345
void sub_read_packets(struct dec_sub *sub, double video_pts, bool force,
346
                      bool *packets_read, bool *sub_updated)
347
27.0k
{
348
27.0k
    *packets_read = true;
349
27.0k
    mp_mutex_lock(&sub->lock);
350
27.0k
    video_pts = pts_to_subtitle(sub, video_pts);
351
28.6k
    while (1) {
352
28.6k
        bool read_more = true;
353
28.6k
        if (sub->sd->driver->accepts_packet)
354
4.36k
            read_more = sub->sd->driver->accepts_packet(sub->sd, video_pts);
355
356
28.6k
        if (!read_more)
357
0
            break;
358
359
28.6k
        if (sub->new_segment && sub->new_segment->start < video_pts) {
360
0
            sub->last_vo_pts = video_pts;
361
0
            update_segment(sub);
362
0
        }
363
364
28.6k
        if (sub->new_segment)
365
0
            break;
366
367
        // (Use this mechanism only if sub_delay matters to avoid corner cases.)
368
28.6k
        float delay = sub->order < 0 ? 0.0f : sub->shared_opts->sub_delay[sub->order];
369
28.6k
        double min_pts = delay < 0 || force ? video_pts : MP_NOPTS_VALUE;
370
371
28.6k
        struct demux_packet *pkt;
372
28.6k
        int st = demux_read_packet_async_until(sub->sh, min_pts, &pkt);
373
        // Note: "wait" (st==0) happens with non-interleaved streams only, and
374
        // then we should stop the playloop until a new enough packet has been
375
        // seen (or the subtitle decoder's queue is full). This usually does not
376
        // happen for interleaved subtitle streams, which never return "wait"
377
        // when reading, unless min_pts is set.
378
28.6k
        if (st <= 0) {
379
27.0k
            *packets_read = st < 0 || (sub->last_pkt_pts != MP_NOPTS_VALUE &&
380
0
                                       sub->last_pkt_pts > video_pts);
381
27.0k
            break;
382
27.0k
        }
383
384
1.55k
        if (sub->recorder_sink)
385
0
            mp_recorder_feed_packet(sub->recorder_sink, pkt);
386
387
1.55k
        sub->last_pkt_pts = pkt->pts;
388
1.55k
        MP_TARRAY_APPEND(sub, sub->cached_pkts, sub->num_cached_pkts, pkt);
389
390
1.55k
        if (is_new_segment(sub, pkt)) {
391
0
            sub->new_segment = demux_copy_packet(sub->packet_pool, pkt);
392
            // Note that this can be delayed to a much later point in time.
393
0
            update_segment(sub);
394
0
            break;
395
0
        }
396
397
1.55k
        if (!(sub->preload_attempted && sub->sd->preload_ok))
398
1.55k
            sub->sd->driver->decode(sub->sd, pkt);
399
1.55k
    }
400
27.0k
    if (sub->cached_pkts && sub->num_cached_pkts) {
401
16.4k
        bool visible = is_packet_visible(sub->cached_pkts[sub->cached_pkt_pos], video_pts);
402
16.4k
        *sub_updated = update_pkt_cache(sub, video_pts) || sub->sub_visible != visible;
403
16.4k
        sub->sub_visible = visible;
404
16.4k
    }
405
27.0k
    mp_mutex_unlock(&sub->lock);
406
27.0k
}
407
408
// Redecode all cached packets if needed.
409
// Used with UPDATE_SUB_HARD and UPDATE_SUB_FILT.
410
void sub_redecode_cached_packets(struct dec_sub *sub)
411
554
{
412
554
    mp_mutex_lock(&sub->lock);
413
554
    int index = sub->cached_pkt_pos;
414
558
    while (index < sub->num_cached_pkts) {
415
4
        sub->sd->driver->decode(sub->sd, sub->cached_pkts[index]);
416
4
        ++index;
417
4
    }
418
554
    mp_mutex_unlock(&sub->lock);
419
554
}
420
421
// Unref sub_bitmaps.rc to free the result. May return NULL.
422
struct sub_bitmaps *sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim,
423
                                    int format, double pts)
424
0
{
425
0
    mp_mutex_lock(&sub->lock);
426
427
0
    pts = pts_to_subtitle(sub, pts);
428
429
0
    sub->last_vo_pts = pts;
430
0
    update_segment(sub);
431
432
0
    struct sub_bitmaps *res = NULL;
433
434
0
    if (!(sub->end != MP_NOPTS_VALUE && pts >= sub->end) &&
435
0
        sub->sd->driver->get_bitmaps)
436
0
        res = sub->sd->driver->get_bitmaps(sub->sd, dim, format, pts);
437
438
0
    mp_mutex_unlock(&sub->lock);
439
0
    return res;
440
0
}
441
442
// The returned string is talloc'ed.
443
char *sub_get_text(struct dec_sub *sub, double pts, enum sd_text_type type)
444
460
{
445
460
    mp_mutex_lock(&sub->lock);
446
460
    char *text = NULL;
447
448
460
    pts = pts_to_subtitle(sub, pts);
449
450
460
    sub->last_vo_pts = pts;
451
460
    update_segment(sub);
452
453
460
    if (sub->sd->driver->get_text)
454
193
        text = sub->sd->driver->get_text(sub->sd, pts, type);
455
460
    mp_mutex_unlock(&sub->lock);
456
460
    return text;
457
460
}
458
459
char *sub_ass_get_extradata(struct dec_sub *sub)
460
0
{
461
0
    char *data = NULL;
462
0
    mp_mutex_lock(&sub->lock);
463
0
    if (strcmp(sub->sd->codec->codec, "ass") != 0)
464
0
        goto done;
465
0
    char *extradata = sub->sd->codec->extradata;
466
0
    int extradata_size = sub->sd->codec->extradata_size;
467
0
    data = talloc_strndup(NULL, extradata, extradata_size);
468
0
done:
469
0
    mp_mutex_unlock(&sub->lock);
470
0
    return data;
471
0
}
472
473
struct sd_times sub_get_times(struct dec_sub *sub, double pts)
474
0
{
475
0
    mp_mutex_lock(&sub->lock);
476
0
    struct sd_times res = { .start = MP_NOPTS_VALUE, .end = MP_NOPTS_VALUE };
477
478
0
    pts = pts_to_subtitle(sub, pts);
479
480
0
    sub->last_vo_pts = pts;
481
0
    update_segment(sub);
482
483
0
    if (sub->sd->driver->get_times)
484
0
        res = sub->sd->driver->get_times(sub->sd, pts);
485
486
0
    mp_mutex_unlock(&sub->lock);
487
0
    return res;
488
0
}
489
490
void sub_reset(struct dec_sub *sub)
491
3.45k
{
492
3.45k
    mp_mutex_lock(&sub->lock);
493
3.45k
    if (sub->sd->driver->reset)
494
3.45k
        sub->sd->driver->reset(sub->sd);
495
3.45k
    sub->last_pkt_pts = MP_NOPTS_VALUE;
496
3.45k
    sub->last_vo_pts = MP_NOPTS_VALUE;
497
3.45k
    destroy_cached_pkts(sub);
498
3.45k
    demux_packet_pool_push(sub->packet_pool, sub->new_segment);
499
3.45k
    sub->new_segment = NULL;
500
3.45k
    mp_mutex_unlock(&sub->lock);
501
3.45k
}
502
503
void sub_select(struct dec_sub *sub, bool selected)
504
3.45k
{
505
3.45k
    mp_mutex_lock(&sub->lock);
506
3.45k
    if (sub->sd->driver->select)
507
3.00k
        sub->sd->driver->select(sub->sd, selected);
508
3.45k
    mp_mutex_unlock(&sub->lock);
509
3.45k
}
510
511
int sub_control(struct dec_sub *sub, enum sd_ctrl cmd, void *arg)
512
53.0k
{
513
53.0k
    int r = CONTROL_UNKNOWN;
514
53.0k
    mp_mutex_lock(&sub->lock);
515
53.0k
    bool propagate = false;
516
53.0k
    switch (cmd) {
517
1.72k
    case SD_CTRL_SET_VIDEO_DEF_FPS:
518
1.72k
        sub->video_fps = *(double *)arg;
519
1.72k
        update_subtitle_speed(sub);
520
1.72k
        break;
521
0
    case SD_CTRL_SUB_STEP: {
522
0
        double *a = arg;
523
0
        double arg2[2] = {a[0], a[1]};
524
0
        arg2[0] = pts_to_subtitle(sub, arg2[0]);
525
0
        if (sub->sd->driver->control)
526
0
            r = sub->sd->driver->control(sub->sd, cmd, arg2);
527
0
        if (r == CONTROL_OK)
528
0
            a[0] = pts_from_subtitle(sub, arg2[0]);
529
0
        break;
530
0
    }
531
0
    case SD_CTRL_UPDATE_OPTS: {
532
0
        uint64_t flags = *(uint64_t *)arg;
533
0
        if (m_config_cache_update(sub->opts_cache))
534
0
            update_subtitle_speed(sub);
535
0
        m_config_cache_update(sub->shared_opts_cache);
536
0
        propagate = true;
537
0
        if (flags & UPDATE_SUB_HARD) {
538
            // forget about the previous preload because
539
            // UPDATE_SUB_HARD will cause a sub reinit
540
            // that clears all preloaded sub packets
541
0
            sub->preload_attempted = false;
542
0
        }
543
0
        break;
544
0
    }
545
51.3k
    default:
546
51.3k
        propagate = true;
547
53.0k
    }
548
53.0k
    if (propagate && sub->sd->driver->control)
549
51.3k
        r = sub->sd->driver->control(sub->sd, cmd, arg);
550
53.0k
    mp_mutex_unlock(&sub->lock);
551
53.0k
    return r;
552
53.0k
}
553
554
void sub_set_recorder_sink(struct dec_sub *sub, struct mp_recorder_sink *sink)
555
0
{
556
0
    mp_mutex_lock(&sub->lock);
557
0
    sub->recorder_sink = sink;
558
0
    mp_mutex_unlock(&sub->lock);
559
0
}
560
561
void sub_set_play_dir(struct dec_sub *sub, int dir)
562
1.72k
{
563
1.72k
    mp_mutex_lock(&sub->lock);
564
1.72k
    sub->play_dir = dir;
565
1.72k
    mp_mutex_unlock(&sub->lock);
566
1.72k
}
567
568
bool sub_is_primary_visible(struct dec_sub *sub)
569
0
{
570
0
    mp_mutex_lock(&sub->lock);
571
0
    bool ret = sub->shared_opts->sub_visibility[0];
572
0
    mp_mutex_unlock(&sub->lock);
573
0
    return ret;
574
0
}
575
576
bool sub_is_secondary_visible(struct dec_sub *sub)
577
0
{
578
0
    mp_mutex_lock(&sub->lock);
579
0
    bool ret = sub->shared_opts->sub_visibility[1];
580
0
    mp_mutex_unlock(&sub->lock);
581
0
    return ret;
582
0
}
583
584
static int sub_line_cmp(const void *a, const void *b)
585
0
{
586
0
    const struct sub_line *la = a, *lb = b;
587
0
    if (la->start < lb->start) return -1;
588
0
    if (la->start > lb->start) return  1;
589
0
    return 0;
590
0
}
591
592
static void dedup_sub_lines(struct sub_lines *lines)
593
0
{
594
0
    int window_start = 0;
595
0
    int window_end = 1;
596
0
    int current_shift = 0;
597
598
0
    while (window_end < lines->num_entries) {
599
0
        struct sub_line next = lines->entries[window_end + current_shift];
600
0
        for (int i = window_start; i < window_end; ++i) {
601
0
            if (lines->entries[i].end < next.start) {
602
0
                struct sub_line tmp = lines->entries[window_start];
603
0
                lines->entries[window_start++] = lines->entries[i];
604
0
                lines->entries[i] = tmp;
605
0
                continue;
606
0
            }
607
608
0
            if (!strcmp(lines->entries[i].text, next.text)) {
609
0
                lines->entries[i].end = MPMAX(next.end, lines->entries[i].end);
610
0
                TA_FREEP(&next.text);
611
0
                ++current_shift, --lines->num_entries;
612
0
                goto skip;
613
0
            }
614
0
        }
615
616
0
        lines->entries[window_end++] = next;
617
618
0
    skip:;
619
0
    }
620
0
}
621
622
struct sub_lines *sub_get_lines(struct dec_sub *sub)
623
0
{
624
0
    mp_mutex_lock(&sub->lock);
625
0
    struct sub_lines *res = NULL;
626
0
    if (sub->sd->driver->get_lines) {
627
0
        res = sub->sd->driver->get_lines(sub->sd);
628
0
        qsort(res->entries, res->num_entries, sizeof(res->entries[0]),
629
0
              sub_line_cmp);
630
0
        dedup_sub_lines(res);
631
        // dedup may sometimes reorder lines to keep its window smaller
632
0
        qsort(res->entries, res->num_entries, sizeof(res->entries[0]),
633
0
              sub_line_cmp);
634
0
    }
635
0
    mp_mutex_unlock(&sub->lock);
636
0
    return res;
637
0
}