Imported Debian version 2.4.3~trusty1
[deb_ffmpeg.git] / ffmpeg / libavcodec / microdvddec.c
CommitLineData
2ba45a60
DM
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 * MicroDVD subtitle decoder
24 *
25 * Based on the specifications found here:
26 * https://trac.videolan.org/vlc/ticket/1825#comment:6
27 */
28
29#include "libavutil/avstring.h"
30#include "libavutil/parseutils.h"
31#include "libavutil/bprint.h"
32#include "avcodec.h"
33#include "ass.h"
34
35static int indexof(const char *s, int c)
36{
37 char *f = strchr(s, c);
38 return f ? (f - s) : -1;
39}
40
41struct microdvd_tag {
42 char key;
43 int persistent;
44 uint32_t data1;
45 uint32_t data2;
46 char *data_string;
47 int data_string_len;
48};
49
50#define MICRODVD_PERSISTENT_OFF 0
51#define MICRODVD_PERSISTENT_ON 1
52#define MICRODVD_PERSISTENT_OPENED 2
53
54// Color, Font, Size, cHarset, stYle, Position, cOordinate
55#define MICRODVD_TAGS "cfshyYpo"
56
57static void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
58{
59 int tag_index = indexof(MICRODVD_TAGS, tag.key);
60
61 if (tag_index < 0)
62 return;
63 memcpy(&tags[tag_index], &tag, sizeof(tag));
64}
65
66// italic, bold, underline, strike-through
67#define MICRODVD_STYLES "ibus"
68
69static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
70{
71 while (*s == '{') {
72 char *start = s;
73 char tag_char = *(s + 1);
74 struct microdvd_tag tag = {0};
75
76 if (!tag_char || *(s + 2) != ':')
77 break;
78 s += 3;
79
80 switch (tag_char) {
81
82 /* Style */
83 case 'Y':
84 tag.persistent = MICRODVD_PERSISTENT_ON;
85 case 'y':
86 while (*s && *s != '}') {
87 int style_index = indexof(MICRODVD_STYLES, *s);
88
89 if (style_index >= 0)
90 tag.data1 |= (1 << style_index);
91 s++;
92 }
93 if (*s != '}')
94 break;
95 /* We must distinguish persistent and non-persistent styles
96 * to handle this kind of style tags: {y:ib}{Y:us} */
97 tag.key = tag_char;
98 break;
99
100 /* Color */
101 case 'C':
102 tag.persistent = MICRODVD_PERSISTENT_ON;
103 case 'c':
104 if (*s == '$')
105 s++;
106 tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
107 if (*s != '}')
108 break;
109 tag.key = 'c';
110 break;
111
112 /* Font name */
113 case 'F':
114 tag.persistent = MICRODVD_PERSISTENT_ON;
115 case 'f': {
116 int len = indexof(s, '}');
117 if (len < 0)
118 break;
119 tag.data_string = s;
120 tag.data_string_len = len;
121 s += len;
122 tag.key = 'f';
123 break;
124 }
125
126 /* Font size */
127 case 'S':
128 tag.persistent = MICRODVD_PERSISTENT_ON;
129 case 's':
130 tag.data1 = strtol(s, &s, 10);
131 if (*s != '}')
132 break;
133 tag.key = 's';
134 break;
135
136 /* Charset */
137 case 'H': {
138 //TODO: not yet handled, just parsed.
139 int len = indexof(s, '}');
140 if (len < 0)
141 break;
142 tag.data_string = s;
143 tag.data_string_len = len;
144 s += len;
145 tag.key = 'h';
146 break;
147 }
148
149 /* Position */
150 case 'P':
151 tag.persistent = MICRODVD_PERSISTENT_ON;
152 tag.data1 = (*s++ == '1');
153 if (*s != '}')
154 break;
155 tag.key = 'p';
156 break;
157
158 /* Coordinates */
159 case 'o':
160 tag.persistent = MICRODVD_PERSISTENT_ON;
161 tag.data1 = strtol(s, &s, 10);
162 if (*s != ',')
163 break;
164 s++;
165 tag.data2 = strtol(s, &s, 10);
166 if (*s != '}')
167 break;
168 tag.key = 'o';
169 break;
170
171 default: /* Unknown tag, we consider it's text */
172 break;
173 }
174
175 if (tag.key == 0)
176 return start;
177
178 microdvd_set_tag(tags, tag);
179 s++;
180 }
181 return s;
182}
183
184static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
185{
186 int i, sidx;
187 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
188 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
189 continue;
190 switch (tags[i].key) {
191 case 'Y':
192 case 'y':
193 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
194 if (tags[i].data1 & (1 << sidx))
195 av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
196 break;
197
198 case 'c':
199 av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
200 break;
201
202 case 'f':
203 av_bprintf(new_line, "{\\fn%.*s}",
204 tags[i].data_string_len, tags[i].data_string);
205 break;
206
207 case 's':
208 av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
209 break;
210
211 case 'p':
212 if (tags[i].data1 == 0)
213 av_bprintf(new_line, "{\\an8}");
214 break;
215
216 case 'o':
217 av_bprintf(new_line, "{\\pos(%d,%d)}",
218 tags[i].data1, tags[i].data2);
219 break;
220 }
221 if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
222 tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
223 }
224}
225
226static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
227 struct microdvd_tag *tags)
228{
229 int i, sidx;
230
231 for (i = sizeof(MICRODVD_TAGS) - 2; i >= 0; i--) {
232 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
233 continue;
234 switch (tags[i].key) {
235
236 case 'y':
237 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
238 if (tags[i].data1 & (1 << sidx))
239 av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
240 break;
241
242 case 'c':
243 av_bprintf(new_line, "{\\c}");
244 break;
245
246 case 'f':
247 av_bprintf(new_line, "{\\fn}");
248 break;
249
250 case 's':
251 av_bprintf(new_line, "{\\fs}");
252 break;
253 }
254 tags[i].key = 0;
255 }
256}
257
258static int microdvd_decode_frame(AVCodecContext *avctx,
259 void *data, int *got_sub_ptr, AVPacket *avpkt)
260{
261 AVSubtitle *sub = data;
262 AVBPrint new_line;
263 char c;
264 char *decoded_sub;
265 char *line = avpkt->data;
266 char *end = avpkt->data + avpkt->size;
267 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
268
269 if (avpkt->size <= 0)
270 return avpkt->size;
271
272 /* To be removed later */
273 if (sscanf(line, "{%*d}{%*[0123456789]}%c", &c) == 1 &&
274 line[avpkt->size - 1] == '\n') {
275 av_log(avctx, AV_LOG_ERROR, "AVPacket is not clean (contains timing "
276 "information and a trailing line break). You need to upgrade "
277 "your libavformat or sanitize your packet.\n");
278 return AVERROR_INVALIDDATA;
279 }
280
281 av_bprint_init(&new_line, 0, 2048);
282
283 // subtitle content
284 while (line < end && *line) {
285
286 // parse MicroDVD tags, and open them in ASS
287 line = microdvd_load_tags(tags, line);
288 microdvd_open_tags(&new_line, tags);
289
290 // simple copy until EOL or forced carriage return
291 while (line < end && *line && *line != '|') {
292 av_bprint_chars(&new_line, *line, 1);
293 line++;
294 }
295
296 // line split
297 if (line < end && *line == '|') {
298 microdvd_close_no_persistent_tags(&new_line, tags);
299 av_bprintf(&new_line, "\\N");
300 line++;
301 }
302 }
303 if (new_line.len) {
304 av_bprintf(&new_line, "\r\n");
305
306 av_bprint_finalize(&new_line, &decoded_sub);
307 if (*decoded_sub) {
308 int64_t start = avpkt->pts;
309 int64_t duration = avpkt->duration;
310 int ts_start = av_rescale_q(start, avctx->time_base, (AVRational){1,100});
311 int ts_duration = duration != -1 ?
312 av_rescale_q(duration, avctx->time_base, (AVRational){1,100}) : -1;
313 ff_ass_add_rect(sub, decoded_sub, ts_start, ts_duration, 0);
314 }
315 av_free(decoded_sub);
316 }
317
318 *got_sub_ptr = sub->num_rects > 0;
319 return avpkt->size;
320}
321
322static int microdvd_init(AVCodecContext *avctx)
323{
324 int i, sidx;
325 AVBPrint font_buf;
326 int font_size = ASS_DEFAULT_FONT_SIZE;
327 int color = ASS_DEFAULT_COLOR;
328 int bold = ASS_DEFAULT_BOLD;
329 int italic = ASS_DEFAULT_ITALIC;
330 int underline = ASS_DEFAULT_UNDERLINE;
331 int alignment = ASS_DEFAULT_ALIGNMENT;
332 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
333
334 av_bprint_init(&font_buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
335 av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT);
336
337 if (avctx->extradata) {
338 microdvd_load_tags(tags, avctx->extradata);
339 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
340 switch (av_tolower(tags[i].key)) {
341 case 'y':
342 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) {
343 if (tags[i].data1 & (1 << sidx)) {
344 switch (MICRODVD_STYLES[sidx]) {
345 case 'i': italic = 1; break;
346 case 'b': bold = 1; break;
347 case 'u': underline = 1; break;
348 }
349 }
350 }
351 break;
352
353 case 'c': color = tags[i].data1; break;
354 case 's': font_size = tags[i].data1; break;
355 case 'p': alignment = 8; break;
356
357 case 'f':
358 av_bprint_clear(&font_buf);
359 av_bprintf(&font_buf, "%.*s",
360 tags[i].data_string_len, tags[i].data_string);
361 break;
362 }
363 }
364 }
365 return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
366 ASS_DEFAULT_BACK_COLOR, bold, italic,
367 underline, alignment);
368}
369
370AVCodec ff_microdvd_decoder = {
371 .name = "microdvd",
372 .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
373 .type = AVMEDIA_TYPE_SUBTITLE,
374 .id = AV_CODEC_ID_MICRODVD,
375 .init = microdvd_init,
376 .decode = microdvd_decode_frame,
377};