blob: 7b2d1750deb9959417cb8620f24b9a8cf2e5bfa2
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 http://dev.w3.org/html5/webvtt/ |
25 | * @todo need to support extended markups and cue settings |
26 | */ |
27 | |
28 | #include "avcodec.h" |
29 | #include "ass.h" |
30 | #include "libavutil/bprint.h" |
31 | |
32 | static const struct { |
33 | const char *from; |
34 | const char *to; |
35 | } webvtt_tag_replace[] = { |
36 | {"<i>", "{\\i1}"}, {"</i>", "{\\i0}"}, |
37 | {"<b>", "{\\b1}"}, {"</b>", "{\\b0}"}, |
38 | {"<u>", "{\\u1}"}, {"</u>", "{\\u0}"}, |
39 | {"{", "\\{"}, {"}", "\\}"}, // escape to avoid ASS markup conflicts |
40 | {">", ">"}, {"<", "<"}, |
41 | {"‎", ""}, {"‏", ""}, // FIXME: properly honor bidi marks |
42 | {"&", "&"}, {" ", "\\h"}, |
43 | }; |
44 | |
45 | static int webvtt_event_to_ass(AVBPrint *buf, const char *p) |
46 | { |
47 | int i, again = 0, skip = 0; |
48 | |
49 | while (*p) { |
50 | |
51 | for (i = 0; i < FF_ARRAY_ELEMS(webvtt_tag_replace); i++) { |
52 | const char *from = webvtt_tag_replace[i].from; |
53 | const size_t len = strlen(from); |
54 | if (!strncmp(p, from, len)) { |
55 | av_bprintf(buf, "%s", webvtt_tag_replace[i].to); |
56 | p += len; |
57 | again = 1; |
58 | break; |
59 | } |
60 | } |
61 | if (!*p) |
62 | break; |
63 | |
64 | if (again) { |
65 | again = 0; |
66 | skip = 0; |
67 | continue; |
68 | } |
69 | if (*p == '<') |
70 | skip = 1; |
71 | else if (*p == '>') |
72 | skip = 0; |
73 | else if (p[0] == '\n' && p[1]) |
74 | av_bprintf(buf, "\\N"); |
75 | else if (!skip && *p != '\r') |
76 | av_bprint_chars(buf, *p, 1); |
77 | p++; |
78 | } |
79 | return 0; |
80 | } |
81 | |
82 | static int webvtt_decode_frame(AVCodecContext *avctx, |
83 | void *data, int *got_sub_ptr, AVPacket *avpkt) |
84 | { |
85 | int ret = 0; |
86 | AVSubtitle *sub = data; |
87 | const char *ptr = avpkt->data; |
88 | FFASSDecoderContext *s = avctx->priv_data; |
89 | AVBPrint buf; |
90 | |
91 | av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); |
92 | if (ptr && avpkt->size > 0 && !webvtt_event_to_ass(&buf, ptr)) |
93 | ret = ff_ass_add_rect(sub, buf.str, s->readorder++, 0, NULL, NULL); |
94 | av_bprint_finalize(&buf, NULL); |
95 | if (ret < 0) |
96 | return ret; |
97 | *got_sub_ptr = sub->num_rects > 0; |
98 | return avpkt->size; |
99 | } |
100 | |
101 | AVCodec ff_webvtt_decoder = { |
102 | .name = "webvtt", |
103 | .long_name = NULL_IF_CONFIG_SMALL("WebVTT subtitle"), |
104 | .type = AVMEDIA_TYPE_SUBTITLE, |
105 | .id = AV_CODEC_ID_WEBVTT, |
106 | .decode = webvtt_decode_frame, |
107 | .init = ff_ass_subtitle_header_default, |
108 | .flush = ff_ass_decoder_flush, |
109 | .priv_data_size = sizeof(FFASSDecoderContext), |
110 | }; |
111 |