Coverage Report

Created: 2026-02-14 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/webvttdec.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2012 Clément Bœsch
3
 *
4
 * This file is part of FFmpeg.
5
 *
6
 * FFmpeg is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * FFmpeg is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with FFmpeg; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
/**
22
 * @file
23
 * WebVTT subtitle decoder
24
 * @see https://www.w3.org/TR/webvtt1/
25
 * @todo need to support extended markups and cue settings
26
 */
27
28
#include "avcodec.h"
29
#include "ass.h"
30
#include "codec_internal.h"
31
#include "libavutil/bprint.h"
32
33
static const struct {
34
    const char *from;
35
    const char *to;
36
} webvtt_tag_replace[] = {
37
    {"{", "\\{{}"}, {"\\", "\\\xe2\x81\xa0"}, // escape to avoid ASS markup conflicts
38
    {"&gt;", ">"}, {"&lt;", "<"},
39
    {"&lrm;", "\xe2\x80\x8e"}, {"&rlm;", "\xe2\x80\x8f"},
40
    {"&amp;", "&"}, {"&nbsp;", "\\h"},
41
};
42
static const struct {
43
    const char from[6];
44
    const char to[6];
45
} webvtt_valid_tags[] = {
46
    {"i", "{\\i1}"}, {"/i", "{\\i0}"},
47
    {"b", "{\\b1}"}, {"/b", "{\\b0}"},
48
    {"u", "{\\u1}"}, {"/u", "{\\u0}"},
49
};
50
51
static int webvtt_event_to_ass(AVBPrint *buf, const char *p)
52
161k
{
53
161k
    int i, again = 0;
54
55
17.7M
    while (*p) {
56
17.6M
        if (*p == '<') {
57
3.11k
            const char *tag_end = strchr(p, '>');
58
3.11k
            ptrdiff_t len;
59
3.11k
            if (!tag_end)
60
2.31k
                break;
61
801
            len = tag_end - p + 1;
62
4.16k
            for (i = 0; i < FF_ARRAY_ELEMS(webvtt_valid_tags); i++) {
63
3.84k
                const char *from = webvtt_valid_tags[i].from;
64
3.84k
                if(!strncmp(p + 1, from, strlen(from))) {
65
474
                    av_bprintf(buf, "%s", webvtt_valid_tags[i].to);
66
474
                    break;
67
474
                }
68
3.84k
            }
69
801
            p += len;
70
801
            again = 1;
71
801
        }
72
73
23.5M
        for (i = 0; i < FF_ARRAY_ELEMS(webvtt_tag_replace); i++) {
74
23.3M
            const char *from = webvtt_tag_replace[i].from;
75
23.3M
            const size_t len = strlen(from);
76
23.3M
            if (!strncmp(p, from, len)) {
77
17.3M
                av_bprintf(buf, "%s", webvtt_tag_replace[i].to);
78
17.3M
                p += len;
79
17.3M
                again = 1;
80
17.3M
                break;
81
17.3M
            }
82
23.3M
        }
83
84
17.6M
        if (again) {
85
17.3M
            again = 0;
86
17.3M
            continue;
87
17.3M
        }
88
207k
        if (p[0] == '\n' && p[1])
89
3.89k
            av_bprintf(buf, "\\N");
90
203k
        else if (*p != '\r')
91
203k
            av_bprint_chars(buf, *p, 1);
92
207k
        p++;
93
207k
    }
94
161k
    return 0;
95
161k
}
96
97
static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
98
                               int *got_sub_ptr, const AVPacket *avpkt)
99
161k
{
100
161k
    int ret = 0;
101
161k
    const char *ptr = avpkt->data;
102
161k
    FFASSDecoderContext *s = avctx->priv_data;
103
161k
    AVBPrint buf;
104
105
161k
    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
106
161k
    if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
107
161k
        ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
108
161k
    av_bprint_finalize(&buf, NULL);
109
161k
    if (ret < 0)
110
0
        return ret;
111
161k
    *got_sub_ptr = sub->num_rects > 0;
112
161k
    return avpkt->size;
113
161k
}
114
115
const FFCodec ff_webvtt_decoder = {
116
    .p.name         = "webvtt",
117
    CODEC_LONG_NAME("WebVTT subtitle"),
118
    .p.type         = AVMEDIA_TYPE_SUBTITLE,
119
    .p.id           = AV_CODEC_ID_WEBVTT,
120
    FF_CODEC_DECODE_SUB_CB(webvtt_decode_frame),
121
    .init           = ff_ass_subtitle_header_default,
122
    .flush          = ff_ass_decoder_flush,
123
    .priv_data_size = sizeof(FFASSDecoderContext),
124
};