Coverage Report

Created: 2026-06-13 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mpv/sub/lavc_conv.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 <assert.h>
20
21
#include <libavcodec/avcodec.h>
22
#include <libavutil/intreadwrite.h>
23
#include <libavutil/common.h>
24
#include <libavutil/opt.h>
25
26
#include "mpv_talloc.h"
27
#include "common/msg.h"
28
#include "common/av_common.h"
29
#include "demux/stheader.h"
30
#include "misc/bstr.h"
31
#include "sd.h"
32
33
struct lavc_conv {
34
    struct mp_log *log;
35
    struct mp_subtitle_opts *opts;
36
    bool styled;
37
    AVCodecContext *avctx;
38
    AVPacket *avpkt;
39
    AVPacket *avpkt_vtt;
40
    const char *codec;
41
    char *extradata;
42
    AVSubtitle cur;
43
    char **cur_list;
44
};
45
46
static const char *get_lavc_format(const char *format)
47
1.10k
{
48
    // For the hack involving parse_webvtt().
49
1.10k
    if (format && strcmp(format, "webvtt-webm") == 0)
50
0
        format = "webvtt";
51
    // Most text subtitles are srt/html style anyway.
52
1.10k
    if (format && strcmp(format, "text") == 0)
53
0
        format = "subrip";
54
1.10k
    return format;
55
1.10k
}
56
57
struct lavc_conv *lavc_conv_create(struct sd *sd)
58
1.10k
{
59
1.10k
    struct lavc_conv *priv = talloc_zero(NULL, struct lavc_conv);
60
1.10k
    priv->log = sd->log;
61
1.10k
    priv->opts = sd->opts;
62
1.10k
    priv->cur_list = talloc_array(priv, char*, 0);
63
1.10k
    priv->codec = sd->codec->codec;
64
1.10k
    AVCodecContext *avctx = NULL;
65
1.10k
    AVDictionary *opts = NULL;
66
1.10k
    const char *fmt = get_lavc_format(priv->codec);
67
1.10k
    const AVCodec *codec = avcodec_find_decoder(mp_codec_to_av_codec_id(fmt));
68
1.10k
    if (!codec)
69
71
        goto error;
70
1.03k
    avctx = avcodec_alloc_context3(codec);
71
1.03k
    if (!avctx)
72
0
        goto error;
73
1.03k
    if (mp_set_avctx_codec_headers(avctx, sd->codec) < 0)
74
0
        goto error;
75
76
1.03k
    MP_VERBOSE(sd, "Using subtitle decoder %s\n", codec->name);
77
78
1.03k
    priv->avpkt = av_packet_alloc();
79
1.03k
    priv->avpkt_vtt = av_packet_alloc();
80
1.03k
    if (!priv->avpkt || !priv->avpkt_vtt)
81
0
        goto error;
82
83
1.03k
    switch (codec->id) {
84
0
    case AV_CODEC_ID_DVB_TELETEXT:
85
0
        av_dict_set_int(&opts, "txt_format", 2, 0);
86
0
        break;
87
0
    case AV_CODEC_ID_ARIB_CAPTION:
88
0
        av_dict_set_int(&opts, "sub_type", SUBTITLE_ASS, 0);
89
0
        break;
90
1.03k
    }
91
92
1.03k
    av_dict_set(&opts, "sub_text_format", "ass", 0);
93
1.03k
    av_dict_set(&opts, "flags2", "+ass_ro_flush_noop", 0);
94
1.03k
    if (strcmp(priv->codec, "eia_608") == 0)
95
36
        av_dict_set(&opts, "real_time", "1", 0);
96
1.03k
    if (avcodec_open2(avctx, codec, &opts) < 0)
97
5
        goto error;
98
1.02k
    av_dict_free(&opts);
99
    // Documented as "set by libavcodec", but there is no other way
100
1.02k
    avctx->time_base = (AVRational) {1, 1000};
101
1.02k
    avctx->pkt_timebase = avctx->time_base;
102
1.02k
    avctx->sub_charenc_mode = FF_SUB_CHARENC_MODE_IGNORE;
103
1.02k
    priv->avctx = avctx;
104
1.02k
    priv->extradata = talloc_strndup(priv, avctx->subtitle_header,
105
1.02k
                                     avctx->subtitle_header_size);
106
1.02k
    mp_codec_info_from_av(avctx, sd->codec);
107
    // Keep original codec name, which get_lavc_format() may have transformed
108
1.02k
    sd->codec->codec = priv->codec;
109
1.02k
    return priv;
110
111
76
 error:
112
76
    MP_FATAL(priv, "Could not open libavcodec subtitle converter\n");
113
76
    av_dict_free(&opts);
114
76
    avcodec_free_context(&avctx);
115
76
    mp_free_av_packet(&priv->avpkt);
116
76
    mp_free_av_packet(&priv->avpkt_vtt);
117
76
    talloc_free(priv);
118
76
    return NULL;
119
1.03k
}
120
121
char *lavc_conv_get_extradata(struct lavc_conv *priv)
122
1.02k
{
123
1.02k
    return priv->extradata;
124
1.02k
}
125
126
// FFmpeg WebVTT packets are pre-parsed in some way. The FFmpeg Matroska
127
// demuxer does this on its own. In order to free our demuxer_mkv.c from
128
// codec-specific crud, we do this here.
129
// Copied from libavformat/matroskadec.c (FFmpeg 818ebe9 / 2013-08-19)
130
// License: LGPL v2.1 or later
131
// Author header: The FFmpeg Project
132
// Modified in some ways.
133
static int parse_webvtt(AVPacket *in, AVPacket *pkt)
134
0
{
135
0
    uint8_t *id, *settings, *text, *buf;
136
0
    int id_len, settings_len, text_len;
137
0
    uint8_t *p, *q;
138
0
    int err;
139
140
0
    uint8_t *data = in->data;
141
0
    int data_len = in->size;
142
143
0
    if (data_len <= 0)
144
0
        return AVERROR_INVALIDDATA;
145
146
0
    p = data;
147
0
    q = data + data_len;
148
149
0
    id = p;
150
0
    id_len = -1;
151
0
    while (p < q) {
152
0
        if (*p == '\r' || *p == '\n') {
153
0
            id_len = p - id;
154
0
            if (*p == '\r')
155
0
                p++;
156
0
            break;
157
0
        }
158
0
        p++;
159
0
    }
160
161
0
    if (p >= q || *p != '\n')
162
0
        return AVERROR_INVALIDDATA;
163
0
    p++;
164
165
0
    settings = p;
166
0
    settings_len = -1;
167
0
    while (p < q) {
168
0
        if (*p == '\r' || *p == '\n') {
169
0
            settings_len = p - settings;
170
0
            if (*p == '\r')
171
0
                p++;
172
0
            break;
173
0
        }
174
0
        p++;
175
0
    }
176
177
0
    if (p >= q || *p != '\n')
178
0
        return AVERROR_INVALIDDATA;
179
0
    p++;
180
181
0
    text = p;
182
0
    text_len = q - p;
183
0
    while (text_len > 0) {
184
0
        const int len = text_len - 1;
185
0
        const uint8_t c = p[len];
186
0
        if (c != '\r' && c != '\n')
187
0
            break;
188
0
        text_len = len;
189
0
    }
190
191
0
    if (text_len <= 0)
192
0
        return AVERROR_INVALIDDATA;
193
194
0
    err = av_new_packet(pkt, text_len);
195
0
    if (err < 0)
196
0
        return AVERROR(err);
197
198
0
    memcpy(pkt->data, text, text_len);
199
200
0
    if (id_len > 0) {
201
0
        buf = av_packet_new_side_data(pkt,
202
0
                                      AV_PKT_DATA_WEBVTT_IDENTIFIER,
203
0
                                      id_len);
204
0
        if (buf == NULL) {
205
0
            av_packet_unref(pkt);
206
0
            return AVERROR(ENOMEM);
207
0
        }
208
0
        memcpy(buf, id, id_len);
209
0
    }
210
211
0
    if (settings_len > 0) {
212
0
        buf = av_packet_new_side_data(pkt,
213
0
                                      AV_PKT_DATA_WEBVTT_SETTINGS,
214
0
                                      settings_len);
215
0
        if (buf == NULL) {
216
0
            av_packet_unref(pkt);
217
0
            return AVERROR(ENOMEM);
218
0
        }
219
0
        memcpy(buf, settings, settings_len);
220
0
    }
221
222
0
    pkt->pts = in->pts;
223
0
    pkt->duration = in->duration;
224
0
    return 0;
225
0
}
226
227
// Return a NULL-terminated list of ASS event lines and have
228
// the AVSubtitle display PTS and duration set to input
229
// double variables.
230
char **lavc_conv_decode(struct lavc_conv *priv, struct demux_packet *packet,
231
                        double *sub_pts, double *sub_duration)
232
1.41k
{
233
1.41k
    AVCodecContext *avctx = priv->avctx;
234
1.41k
    AVPacket *curr_pkt = priv->avpkt;
235
1.41k
    int ret, got_sub;
236
1.41k
    int num_cur = 0;
237
238
1.41k
    avsubtitle_free(&priv->cur);
239
240
1.41k
    mp_set_av_packet(priv->avpkt, packet, &avctx->time_base);
241
1.41k
    if (priv->avpkt->pts < 0)
242
15
        priv->avpkt->pts = 0;
243
244
1.41k
    if (strcmp(priv->codec, "webvtt-webm") == 0) {
245
0
        if (parse_webvtt(priv->avpkt, priv->avpkt_vtt) < 0) {
246
0
            MP_ERR(priv, "Error parsing subtitle\n");
247
0
            goto done;
248
0
        }
249
0
        curr_pkt = priv->avpkt_vtt;
250
0
    }
251
252
1.41k
    priv->styled = avctx->codec_id == AV_CODEC_ID_DVB_TELETEXT;
253
254
1.41k
    if (avctx->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
255
0
        if (!priv->opts->teletext_page) {
256
0
            av_opt_set(avctx, "txt_page", "subtitle", AV_OPT_SEARCH_CHILDREN);
257
0
            priv->styled = false;
258
0
        } else if (priv->opts->teletext_page == -1) {
259
0
            av_opt_set(avctx, "txt_page", "*", AV_OPT_SEARCH_CHILDREN);
260
0
        } else {
261
0
            char page[4];
262
0
            snprintf(page, sizeof(page), "%d", priv->opts->teletext_page);
263
0
            av_opt_set(avctx, "txt_page", page, AV_OPT_SEARCH_CHILDREN);
264
0
        }
265
0
    }
266
267
1.41k
    ret = avcodec_decode_subtitle2(avctx, &priv->cur, &got_sub, curr_pkt);
268
1.41k
    if (ret < 0) {
269
0
        MP_ERR(priv, "Error decoding subtitle\n");
270
1.41k
    } else if (got_sub) {
271
1.41k
        *sub_pts = packet->pts + mp_pts_from_av(priv->cur.start_display_time,
272
1.41k
                                               &avctx->time_base);
273
1.41k
        *sub_duration = priv->cur.end_display_time == UINT32_MAX ?
274
1.41k
                        UINT32_MAX :
275
1.41k
                        mp_pts_from_av(priv->cur.end_display_time -
276
1.41k
                                       priv->cur.start_display_time,
277
1.41k
                                       &avctx->time_base);
278
279
2.83k
        for (int i = 0; i < priv->cur.num_rects; i++) {
280
1.41k
            if (priv->cur.rects[i]->w > 0 && priv->cur.rects[i]->h > 0)
281
1.41k
                MP_WARN(priv, "Ignoring bitmap subtitle.\n");
282
1.41k
            char *ass_line = priv->cur.rects[i]->ass;
283
1.41k
            if (!ass_line)
284
0
                continue;
285
1.41k
            MP_TARRAY_APPEND(priv, priv->cur_list, num_cur, ass_line);
286
1.41k
        }
287
1.41k
    }
288
289
1.41k
done:
290
1.41k
    av_packet_unref(priv->avpkt_vtt);
291
1.41k
    MP_TARRAY_APPEND(priv, priv->cur_list, num_cur, NULL);
292
1.41k
    return priv->cur_list;
293
1.41k
}
294
295
bool lavc_conv_is_styled(struct lavc_conv *priv)
296
0
{
297
0
    return priv->styled;
298
0
}
299
300
void lavc_conv_reset(struct lavc_conv *priv)
301
2.05k
{
302
2.05k
    avcodec_flush_buffers(priv->avctx);
303
2.05k
}
304
305
void lavc_conv_uninit(struct lavc_conv *priv)
306
1.02k
{
307
1.02k
    avsubtitle_free(&priv->cur);
308
1.02k
    avcodec_free_context(&priv->avctx);
309
1.02k
    mp_free_av_packet(&priv->avpkt);
310
1.02k
    mp_free_av_packet(&priv->avpkt_vtt);
311
1.02k
    talloc_free(priv);
312
1.02k
}