FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/webvttdec.c
Date: 2025-08-19 23:55:23
Exec Total Coverage
Lines: 42 44 95.5%
Functions: 2 2 100.0%
Branches: 24 30 80.0%

Line Branch Exec 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 27 static int webvtt_event_to_ass(AVBPrint *buf, const char *p)
52 {
53 27 int i, again = 0;
54
55
2/2
✓ Branch 0 taken 1094 times.
✓ Branch 1 taken 27 times.
1121 while (*p) {
56
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 1045 times.
1094 if (*p == '<') {
57 49 const char *tag_end = strchr(p, '>');
58 ptrdiff_t len;
59
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 49 times.
49 if (!tag_end)
60 break;
61 49 len = tag_end - p + 1;
62
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 41 times.
303 for (i = 0; i < FF_ARRAY_ELEMS(webvtt_valid_tags); i++) {
63 262 const char *from = webvtt_valid_tags[i].from;
64
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 254 times.
262 if(!strncmp(p + 1, from, strlen(from))) {
65 8 av_bprintf(buf, "%s", webvtt_valid_tags[i].to);
66 8 break;
67 }
68 }
69 49 p += len;
70 49 again = 1;
71 }
72
73
2/2
✓ Branch 0 taken 8729 times.
✓ Branch 1 taken 1084 times.
9813 for (i = 0; i < FF_ARRAY_ELEMS(webvtt_tag_replace); i++) {
74 8729 const char *from = webvtt_tag_replace[i].from;
75 8729 const size_t len = strlen(from);
76
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8719 times.
8729 if (!strncmp(p, from, len)) {
77 10 av_bprintf(buf, "%s", webvtt_tag_replace[i].to);
78 10 p += len;
79 10 again = 1;
80 10 break;
81 }
82 }
83
84
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 1035 times.
1094 if (again) {
85 59 again = 0;
86 59 continue;
87 }
88
3/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1026 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
1035 if (p[0] == '\n' && p[1])
89 9 av_bprintf(buf, "\\N");
90
2/2
✓ Branch 0 taken 1025 times.
✓ Branch 1 taken 1 times.
1026 else if (*p != '\r')
91 1025 av_bprint_chars(buf, *p, 1);
92 1035 p++;
93 }
94 27 return 0;
95 }
96
97 27 static int webvtt_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
98 int *got_sub_ptr, const AVPacket *avpkt)
99 {
100 27 int ret = 0;
101 27 const char *ptr = avpkt->data;
102 27 FFASSDecoderContext *s = avctx->priv_data;
103 AVBPrint buf;
104
105 27 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
106
3/6
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 27 times.
✗ Branch 6 not taken.
27 if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr))
107 27 ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL);
108 27 av_bprint_finalize(&buf, NULL);
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if (ret < 0)
110 return ret;
111 27 *got_sub_ptr = sub->num_rects > 0;
112 27 return avpkt->size;
113 }
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 };
125