... | ... |
@@ -898,6 +898,7 @@ performance on systems without hardware floating point support). |
898 | 898 |
@item SubRip (SRT) @tab X @tab X @tab X @tab X |
899 | 899 |
@item SubViewer @tab @tab X @tab @tab X |
900 | 900 |
@item 3GPP Timed Text @tab @tab @tab X @tab X |
901 |
+@item WebVTT @tab @tab X @tab @tab X |
|
901 | 902 |
@item XSUB @tab @tab @tab X @tab X |
902 | 903 |
@end multitable |
903 | 904 |
|
... | ... |
@@ -463,6 +463,7 @@ OBJS-$(CONFIG_VP6_DECODER) += vp6.o vp56.o vp56data.o vp56dsp.o \ |
463 | 463 |
OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8dsp.o vp56rac.o |
464 | 464 |
OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o |
465 | 465 |
OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o |
466 |
+OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o |
|
466 | 467 |
OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o |
467 | 468 |
OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o |
468 | 469 |
OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o wma.o wma_common.o aactab.o |
... | ... |
@@ -422,6 +422,7 @@ void avcodec_register_all(void) |
422 | 422 |
REGISTER_ENCDEC (SRT, srt); |
423 | 423 |
REGISTER_ENCDEC (SUBRIP, subrip); |
424 | 424 |
REGISTER_DECODER (SUBVIEWER, subviewer); |
425 |
+ REGISTER_DECODER (WEBVTT, webvtt); |
|
425 | 426 |
REGISTER_ENCDEC (XSUB, xsub); |
426 | 427 |
|
427 | 428 |
/* external libraries */ |
... | ... |
@@ -448,6 +448,7 @@ enum AVCodecID { |
448 | 448 |
AV_CODEC_ID_REALTEXT = MKBETAG('R','T','X','T'), |
449 | 449 |
AV_CODEC_ID_SUBVIEWER = MKBETAG('S','u','b','V'), |
450 | 450 |
AV_CODEC_ID_SUBRIP = MKBETAG('S','R','i','p'), |
451 |
+ AV_CODEC_ID_WEBVTT = MKBETAG('W','V','T','T'), |
|
451 | 452 |
|
452 | 453 |
/* other specific kind of codecs (generally used for attachments) */ |
453 | 454 |
AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. |
... | ... |
@@ -2343,6 +2343,12 @@ static const AVCodecDescriptor codec_descriptors[] = { |
2343 | 2343 |
.long_name = NULL_IF_CONFIG_SMALL("SubViewer subtitle"), |
2344 | 2344 |
}, |
2345 | 2345 |
{ |
2346 |
+ .id = AV_CODEC_ID_WEBVTT, |
|
2347 |
+ .type = AVMEDIA_TYPE_SUBTITLE, |
|
2348 |
+ .name = "webvtt", |
|
2349 |
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), |
|
2350 |
+ }, |
|
2351 |
+ { |
|
2346 | 2352 |
.id = AV_CODEC_ID_BINTEXT, |
2347 | 2353 |
.type = AVMEDIA_TYPE_VIDEO, |
2348 | 2354 |
.name = "bintext", |
34 | 34 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,100 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2012 Clément Bœsch |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+/** |
|
21 |
+ * @file |
|
22 |
+ * WebVTT subtitle decoder |
|
23 |
+ * @see http://dev.w3.org/html5/webvtt/ |
|
24 |
+ * @todo need to support extended markups and cue settings |
|
25 |
+ */ |
|
26 |
+ |
|
27 |
+#include "avcodec.h" |
|
28 |
+#include "ass.h" |
|
29 |
+#include "libavutil/bprint.h" |
|
30 |
+ |
|
31 |
+static const struct { |
|
32 |
+ const char *from; |
|
33 |
+ const char *to; |
|
34 |
+} webvtt_tag_replace[] = { |
|
35 |
+ {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"}, |
|
36 |
+ {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"}, |
|
37 |
+ {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"}, |
|
38 |
+ {"{", "\\{"}, {"}", "\\}"}, // escape to avoid ASS markup conflicts |
|
39 |
+}; |
|
40 |
+ |
|
41 |
+static int webvtt_event_to_ass(AVBPrint *buf, const char *p) |
|
42 |
+{ |
|
43 |
+ int i, skip = 0; |
|
44 |
+ |
|
45 |
+ while (*p) { |
|
46 |
+ |
|
47 |
+ for (i = 0; i < FF_ARRAY_ELEMS(webvtt_tag_replace); i++) { |
|
48 |
+ const char *from = webvtt_tag_replace[i].from; |
|
49 |
+ const size_t len = strlen(from); |
|
50 |
+ if (!strncmp(p, from, len)) { |
|
51 |
+ av_bprintf(buf, "%s", webvtt_tag_replace[i].to); |
|
52 |
+ p += len; |
|
53 |
+ break; |
|
54 |
+ } |
|
55 |
+ } |
|
56 |
+ if (!*p) |
|
57 |
+ break; |
|
58 |
+ |
|
59 |
+ if (*p == '<') |
|
60 |
+ skip = 1; |
|
61 |
+ else if (*p == '>') |
|
62 |
+ skip = 0; |
|
63 |
+ else if (p[0] == '\n' && p[1]) |
|
64 |
+ av_bprintf(buf, "\\N"); |
|
65 |
+ else if (!skip && *p != '\r') |
|
66 |
+ av_bprint_chars(buf, *p, 1); |
|
67 |
+ p++; |
|
68 |
+ } |
|
69 |
+ av_bprintf(buf, "\r\n"); |
|
70 |
+ return 0; |
|
71 |
+} |
|
72 |
+ |
|
73 |
+static int webvtt_decode_frame(AVCodecContext *avctx, |
|
74 |
+ void *data, int *got_sub_ptr, AVPacket *avpkt) |
|
75 |
+{ |
|
76 |
+ AVSubtitle *sub = data; |
|
77 |
+ const char *ptr = avpkt->data; |
|
78 |
+ AVBPrint buf; |
|
79 |
+ |
|
80 |
+ av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); |
|
81 |
+ if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) { |
|
82 |
+ int ts_start = av_rescale_q(avpkt->pts, avctx->time_base, (AVRational){1,100}); |
|
83 |
+ int ts_duration = avpkt->duration != -1 ? |
|
84 |
+ av_rescale_q(avpkt->duration, avctx->time_base, (AVRational){1,100}) : -1; |
|
85 |
+ ff_ass_add_rect(sub, buf.str, ts_start, ts_duration, 0); |
|
86 |
+ } |
|
87 |
+ *got_sub_ptr = sub->num_rects > 0; |
|
88 |
+ av_bprint_finalize(&buf, NULL); |
|
89 |
+ return avpkt->size; |
|
90 |
+} |
|
91 |
+ |
|
92 |
+AVCodec ff_webvtt_decoder = { |
|
93 |
+ .name = "webvtt", |
|
94 |
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), |
|
95 |
+ .type = AVMEDIA_TYPE_SUBTITLE, |
|
96 |
+ .id = AV_CODEC_ID_WEBVTT, |
|
97 |
+ .decode = webvtt_decode_frame, |
|
98 |
+ .init = ff_ass_subtitle_header_default, |
|
99 |
+}; |
... | ... |
@@ -355,6 +355,7 @@ OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o |
355 | 355 |
OBJS-$(CONFIG_WEBM_MUXER) += matroskaenc.o matroska.o \ |
356 | 356 |
isom.o avc.o \ |
357 | 357 |
flacenc_header.o avlanguage.o |
358 |
+OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o |
|
358 | 359 |
OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o |
359 | 360 |
OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood_vqa.o |
360 | 361 |
OBJS-$(CONFIG_WTV_DEMUXER) += wtvdec.o wtv.o asfdec.o asf.o asfcrypt.o \ |
... | ... |
@@ -252,6 +252,7 @@ void av_register_all(void) |
252 | 252 |
REGISTER_MUXDEMUX (WAV, wav); |
253 | 253 |
REGISTER_DEMUXER (WC3, wc3); |
254 | 254 |
REGISTER_MUXER (WEBM, webm); |
255 |
+ REGISTER_DEMUXER (WEBVTT, webvtt); |
|
255 | 256 |
REGISTER_DEMUXER (WSAUD, wsaud); |
256 | 257 |
REGISTER_DEMUXER (WSVQA, wsvqa); |
257 | 258 |
REGISTER_MUXDEMUX (WTV, wtv); |
... | ... |
@@ -30,8 +30,8 @@ |
30 | 30 |
#include "libavutil/avutil.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFORMAT_VERSION_MAJOR 54 |
33 |
-#define LIBAVFORMAT_VERSION_MINOR 27 |
|
34 |
-#define LIBAVFORMAT_VERSION_MICRO 101 |
|
33 |
+#define LIBAVFORMAT_VERSION_MINOR 28 |
|
34 |
+#define LIBAVFORMAT_VERSION_MICRO 100 |
|
35 | 35 |
|
36 | 36 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
37 | 37 |
LIBAVFORMAT_VERSION_MINOR, \ |
38 | 38 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,188 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2012 Clément Bœsch |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+/** |
|
21 |
+ * @file |
|
22 |
+ * WebVTT subtitle demuxer |
|
23 |
+ * @see http://dev.w3.org/html5/webvtt/ |
|
24 |
+ */ |
|
25 |
+ |
|
26 |
+#include "avformat.h" |
|
27 |
+#include "internal.h" |
|
28 |
+#include "subtitles.h" |
|
29 |
+#include "libavutil/bprint.h" |
|
30 |
+#include "libavutil/intreadwrite.h" |
|
31 |
+ |
|
32 |
+typedef struct { |
|
33 |
+ FFDemuxSubtitlesQueue q; |
|
34 |
+} WebVTTContext; |
|
35 |
+ |
|
36 |
+static int webvtt_probe(AVProbeData *p) |
|
37 |
+{ |
|
38 |
+ const uint8_t *ptr = p->buf; |
|
39 |
+ |
|
40 |
+ if (AV_RB24(ptr) == 0xEFBBBF) |
|
41 |
+ ptr += 3; /* skip UTF-8 BOM */ |
|
42 |
+ if (!strncmp(ptr, "WEBVTT", 6) && |
|
43 |
+ (!ptr[6] || strchr("\n\r\t ", ptr[6]))) |
|
44 |
+ return AVPROBE_SCORE_MAX; |
|
45 |
+ return 0; |
|
46 |
+} |
|
47 |
+ |
|
48 |
+static int64_t read_ts(const char *s) |
|
49 |
+{ |
|
50 |
+ int hh, mm, ss, ms; |
|
51 |
+ if (sscanf(s, "%u:%u:%u.%u", &hh, &mm, &ss, &ms) == 4) return (hh*3600 + mm*60 + ss) * 1000 + ms; |
|
52 |
+ if (sscanf(s, "%u:%u.%u", &mm, &ss, &ms) == 3) return ( mm*60 + ss) * 1000 + ms; |
|
53 |
+ return AV_NOPTS_VALUE; |
|
54 |
+} |
|
55 |
+ |
|
56 |
+static int64_t extract_cue(AVBPrint *buf, AVIOContext *pb) |
|
57 |
+{ |
|
58 |
+ int prev_chr_is_eol = 0; |
|
59 |
+ int64_t pos = avio_tell(pb); |
|
60 |
+ |
|
61 |
+ av_bprint_clear(buf); |
|
62 |
+ for (;;) { |
|
63 |
+ char c = avio_r8(pb); |
|
64 |
+ if (!c) |
|
65 |
+ break; |
|
66 |
+ if (c == '\r' || c == '\n') { |
|
67 |
+ if (prev_chr_is_eol) |
|
68 |
+ break; |
|
69 |
+ prev_chr_is_eol = (c == '\n'); |
|
70 |
+ } else |
|
71 |
+ prev_chr_is_eol = 0; |
|
72 |
+ if (c != '\r') |
|
73 |
+ av_bprint_chars(buf, c, 1); |
|
74 |
+ } |
|
75 |
+ av_bprint_chars(buf, '\0', 1); |
|
76 |
+ return pos; |
|
77 |
+} |
|
78 |
+ |
|
79 |
+static int webvtt_read_header(AVFormatContext *s) |
|
80 |
+{ |
|
81 |
+ WebVTTContext *webvtt = s->priv_data; |
|
82 |
+ AVBPrint header, cue; |
|
83 |
+ int res = 0; |
|
84 |
+ AVStream *st = avformat_new_stream(s, NULL); |
|
85 |
+ |
|
86 |
+ if (!st) |
|
87 |
+ return AVERROR(ENOMEM); |
|
88 |
+ avpriv_set_pts_info(st, 64, 1, 1000); |
|
89 |
+ st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; |
|
90 |
+ st->codec->codec_id = AV_CODEC_ID_WEBVTT; |
|
91 |
+ |
|
92 |
+ av_bprint_init(&header, 0, AV_BPRINT_SIZE_UNLIMITED); |
|
93 |
+ av_bprint_init(&cue, 0, AV_BPRINT_SIZE_UNLIMITED); |
|
94 |
+ |
|
95 |
+ for (;;) { |
|
96 |
+ int i, len; |
|
97 |
+ int64_t pos = extract_cue(&cue, s->pb); |
|
98 |
+ AVPacket *sub; |
|
99 |
+ const char *p = cue.str; |
|
100 |
+ const char *identifier = p; |
|
101 |
+ //const char *settings = NULL; |
|
102 |
+ int64_t ts_start, ts_end; |
|
103 |
+ |
|
104 |
+ if (!*p) // EOF |
|
105 |
+ break; |
|
106 |
+ |
|
107 |
+ /* ignore header chunk */ |
|
108 |
+ if (!strncmp(p, "\xEF\xBB\xBFWEBVTT", 9) || |
|
109 |
+ !strncmp(p, "WEBVTT", 6)) |
|
110 |
+ continue; |
|
111 |
+ |
|
112 |
+ /* optional cue identifier (can be a number like in SRT or some kind of |
|
113 |
+ * chaptering id), silently skip it */ |
|
114 |
+ for (i = 0; p[i] && p[i] != '\n'; i++) { |
|
115 |
+ if (!strncmp(p + i, "-->", 3)) { |
|
116 |
+ identifier = NULL; |
|
117 |
+ break; |
|
118 |
+ } |
|
119 |
+ } |
|
120 |
+ if (identifier) |
|
121 |
+ p += strcspn(p, "\n"); |
|
122 |
+ |
|
123 |
+ /* cue timestamps */ |
|
124 |
+ if ((ts_start = read_ts(p)) == AV_NOPTS_VALUE) |
|
125 |
+ break; |
|
126 |
+ if (!(p = strstr(p, "-->"))) |
|
127 |
+ break; |
|
128 |
+ p += 3; |
|
129 |
+ do p++; while (*p == ' ' || *p == '\t'); |
|
130 |
+ if ((ts_end = read_ts(p)) == AV_NOPTS_VALUE) |
|
131 |
+ break; |
|
132 |
+ |
|
133 |
+ /* optional cue settings, TODO: store in side_data */ |
|
134 |
+ p += strcspn(p, "\n\t "); |
|
135 |
+ while (*p == '\t' || *p == ' ') |
|
136 |
+ p++; |
|
137 |
+ if (*p != '\n') { |
|
138 |
+ //settings = p; |
|
139 |
+ p += strcspn(p, "\n"); |
|
140 |
+ } |
|
141 |
+ if (*p == '\n') |
|
142 |
+ p++; |
|
143 |
+ |
|
144 |
+ /* create packet */ |
|
145 |
+ len = cue.str + cue.len - p - 1; |
|
146 |
+ sub = ff_subtitles_queue_insert(&webvtt->q, p, len, 0); |
|
147 |
+ if (!sub) { |
|
148 |
+ res = AVERROR(ENOMEM); |
|
149 |
+ goto end; |
|
150 |
+ } |
|
151 |
+ sub->pos = pos; |
|
152 |
+ sub->pts = ts_start; |
|
153 |
+ sub->duration = ts_end - ts_start; |
|
154 |
+ } |
|
155 |
+ |
|
156 |
+ ff_subtitles_queue_finalize(&webvtt->q); |
|
157 |
+ |
|
158 |
+end: |
|
159 |
+ av_bprint_finalize(&cue, NULL); |
|
160 |
+ av_bprint_finalize(&header, NULL); |
|
161 |
+ return res; |
|
162 |
+} |
|
163 |
+ |
|
164 |
+static int webvtt_read_packet(AVFormatContext *s, AVPacket *pkt) |
|
165 |
+{ |
|
166 |
+ WebVTTContext *webvtt = s->priv_data; |
|
167 |
+ return ff_subtitles_queue_read_packet(&webvtt->q, pkt); |
|
168 |
+} |
|
169 |
+ |
|
170 |
+static int webvtt_read_close(AVFormatContext *s) |
|
171 |
+{ |
|
172 |
+ WebVTTContext *webvtt = s->priv_data; |
|
173 |
+ ff_subtitles_queue_clean(&webvtt->q); |
|
174 |
+ return 0; |
|
175 |
+} |
|
176 |
+ |
|
177 |
+AVInputFormat ff_webvtt_demuxer = { |
|
178 |
+ .name = "webvtt", |
|
179 |
+ .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), |
|
180 |
+ .priv_data_size = sizeof(WebVTTContext), |
|
181 |
+ .read_probe = webvtt_probe, |
|
182 |
+ .read_header = webvtt_read_header, |
|
183 |
+ .read_packet = webvtt_read_packet, |
|
184 |
+ .read_close = webvtt_read_close, |
|
185 |
+ .flags = AVFMT_GENERIC_INDEX, |
|
186 |
+ .extensions = "vtt", |
|
187 |
+}; |
... | ... |
@@ -25,5 +25,8 @@ fate-sub-subripenc: CMD = md5 -i $(SAMPLES)/sub/MovText_capability_tester.mp4 -s |
25 | 25 |
FATE_SUBTITLES += fate-sub-subviewer |
26 | 26 |
fate-sub-subviewer: CMD = md5 -i $(SAMPLES)/sub/SubViewer_capability_tester.sub -f ass |
27 | 27 |
|
28 |
+FATE_SUBTITLES += fate-sub-webvtt |
|
29 |
+fate-sub-webvtt: CMD = md5 -i $(SAMPLES)/sub/WebVTT_capability_tester.vtt -f ass |
|
30 |
+ |
|
28 | 31 |
FATE_SAMPLES_FFMPEG += $(FATE_SUBTITLES) |
29 | 32 |
fate-subtitles: $(FATE_SUBTITLES) |