FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/webvttdec.c
Date: 2024-11-21 09:21:34
Exec Total Coverage
Lines: 78 90 86.7%
Functions: 5 6 83.3%
Branches: 49 70 70.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 demuxer
24 * @see http://dev.w3.org/html5/webvtt/
25 */
26
27 #include "avformat.h"
28 #include "demux.h"
29 #include "internal.h"
30 #include "subtitles.h"
31 #include "libavutil/bprint.h"
32 #include "libavutil/intreadwrite.h"
33 #include "libavutil/opt.h"
34
35 typedef struct {
36 const AVClass *class;
37 FFDemuxSubtitlesQueue q;
38 int kind;
39 } WebVTTContext;
40
41 7186 static int webvtt_probe(const AVProbeData *p)
42 {
43 7186 const uint8_t *ptr = p->buf;
44
45
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7177 times.
7186 if (AV_RB24(ptr) == 0xEFBBBF)
46 9 ptr += 3; /* skip UTF-8 BOM */
47
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7184 times.
7186 if (!strncmp(ptr, "WEBVTT", 6) &&
48
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 (!ptr[6] || strchr("\n\r\t ", ptr[6])))
49 2 return AVPROBE_SCORE_MAX;
50 7184 return 0;
51 }
52
53 84 static int64_t read_ts(const char *s)
54 {
55 int hh, mm, ss, ms;
56
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 69 times.
84 if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600LL + mm*60LL + ss) * 1000LL + ms;
57
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60LL + ss) * 1000LL + ms;
58 return AV_NOPTS_VALUE;
59 }
60
61 3 static int webvtt_read_header(AVFormatContext *s)
62 {
63 3 WebVTTContext *webvtt = s->priv_data;
64 AVBPrint cue;
65 3 int res = 0;
66 3 AVStream *st = avformat_new_stream(s, NULL);
67
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!st)
69 return AVERROR(ENOMEM);
70 3 avpriv_set_pts_info(st, 64, 1, 1000);
71 3 st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE;
72 3 st->codecpar->codec_id = AV_CODEC_ID_WEBVTT;
73 3 st->disposition |= webvtt->kind;
74
75 3 av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED);
76
77 47 for (;;) {
78 int i;
79 int64_t pos;
80 AVPacket *sub;
81 const char *p, *identifier, *settings;
82 size_t identifier_len, settings_len;
83 int64_t ts_start, ts_end;
84
85 50 res = ff_subtitles_read_chunk(s->pb, &cue);
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (res < 0)
87 goto end;
88
89
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 47 times.
50 if (!cue.len)
90 3 break;
91
92 47 p = identifier = cue.str;
93 47 pos = avio_tell(s->pb);
94
95 /* ignore header chunk */
96
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 3 times.
47 if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) ||
97
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 !strncmp(p, "WEBVTT", 6) ||
98
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 !strncmp(p, "STYLE", 5) ||
99
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 !strncmp(p, "REGION", 6) ||
100
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 42 times.
44 !strncmp(p, "NOTE", 4))
101 5 continue;
102
103 /* optional cue identifier (can be a number like in SRT or some kind of
104 * chaptering id) */
105
4/6
✓ Branch 0 taken 493 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 487 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 487 times.
✗ Branch 5 not taken.
493 for (i = 0; p[i] && p[i] != '\n' && p[i] != '\r'; i++) {
106
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 451 times.
487 if (!strncmp(p + i, "-->", 3)) {
107 36 identifier = NULL;
108 36 break;
109 }
110 }
111
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 6 times.
42 if (!identifier)
112 36 identifier_len = 0;
113 else {
114 6 identifier_len = strcspn(p, "\r\n");
115 6 p += identifier_len;
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (*p == '\r')
117 p++;
118
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (*p == '\n')
119 6 p++;
120 }
121
122 /* cue timestamps */
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE)
124 break;
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (!(p = strstr(p, "-->")))
126 break;
127 42 p += 2;
128
3/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
84 do p++; while (*p == ' ' || *p == '\t');
129
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
42 if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE)
130 break;
131
132 /* optional cue settings */
133 42 p += strcspn(p, "\n\r\t ");
134
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 42 times.
58 while (*p == '\t' || *p == ' ')
135 16 p++;
136 42 settings = p;
137 42 settings_len = strcspn(p, "\r\n");
138 42 p += settings_len;
139
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 38 times.
42 if (*p == '\r')
140 4 p++;
141
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 if (*p == '\n')
142 42 p++;
143
144 /* create packet */
145 42 sub = ff_subtitles_queue_insert(&webvtt->q, p, strlen(p), 0);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if (!sub) {
147 res = AVERROR(ENOMEM);
148 goto end;
149 }
150 42 sub->pos = pos;
151 42 sub->pts = ts_start;
152 42 sub->duration = ts_end - ts_start;
153
154 #define SET_SIDE_DATA(name, type) do { \
155 if (name##_len) { \
156 uint8_t *buf = av_packet_new_side_data(sub, type, name##_len); \
157 if (!buf) { \
158 res = AVERROR(ENOMEM); \
159 goto end; \
160 } \
161 memcpy(buf, name, name##_len); \
162 } \
163 } while (0)
164
165
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 36 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
42 SET_SIDE_DATA(identifier, AV_PKT_DATA_WEBVTT_IDENTIFIER);
166
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
42 SET_SIDE_DATA(settings, AV_PKT_DATA_WEBVTT_SETTINGS);
167 }
168
169 3 ff_subtitles_queue_finalize(s, &webvtt->q);
170
171 3 end:
172 3 av_bprint_finalize(&cue, NULL);
173 3 return res;
174 }
175
176 45 static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt)
177 {
178 45 WebVTTContext *webvtt = s->priv_data;
179 45 return ff_subtitles_queue_read_packet(&webvtt->q, pkt);
180 }
181
182 static int webvtt_read_seek(AVFormatContext *s, int stream_index,
183 int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
184 {
185 WebVTTContext *webvtt = s->priv_data;
186 return ff_subtitles_queue_seek(&webvtt->q, s, stream_index,
187 min_ts, ts, max_ts, flags);
188 }
189
190 3 static int webvtt_read_close(AVFormatContext *s)
191 {
192 3 WebVTTContext *webvtt = s->priv_data;
193 3 ff_subtitles_queue_clean(&webvtt->q);
194 3 return 0;
195 }
196
197 #define OFFSET(x) offsetof(WebVTTContext, x)
198 #define KIND_FLAGS AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_DECODING_PARAM
199
200 static const AVOption options[] = {
201 { "kind", "Set kind of WebVTT track", OFFSET(kind), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" },
202 { "subtitles", "WebVTT subtitles kind", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" },
203 { "captions", "WebVTT captions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" },
204 { "descriptions", "WebVTT descriptions kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" },
205 { "metadata", "WebVTT metadata kind", 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, INT_MIN, INT_MAX, KIND_FLAGS, .unit = "webvtt_kind" },
206 { NULL }
207 };
208
209 static const AVClass webvtt_demuxer_class = {
210 .class_name = "WebVTT demuxer",
211 .item_name = av_default_item_name,
212 .option = options,
213 .version = LIBAVUTIL_VERSION_INT,
214 };
215
216 const FFInputFormat ff_webvtt_demuxer = {
217 .p.name = "webvtt",
218 .p.long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"),
219 .p.extensions = "vtt",
220 .p.priv_class = &webvtt_demuxer_class,
221 .priv_data_size = sizeof(WebVTTContext),
222 .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
223 .read_probe = webvtt_probe,
224 .read_header = webvtt_read_header,
225 .read_packet = webvtt_read_packet,
226 .read_seek2 = webvtt_read_seek,
227 .read_close = webvtt_read_close,
228 };
229