Imported Debian version 2.5.0~trusty1.1
[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
f6fa7814
DM
69/* some samples have lines that start with a / indicating non persistent italic
70 * marker */
71static char *check_for_italic_slash_marker(struct microdvd_tag *tags, char *s)
72{
73 if (*s == '/') {
74 struct microdvd_tag tag = tags[indexof(MICRODVD_TAGS, 'y')];
75 tag.key = 'y';
76 tag.data1 |= 1 << 0 /* 'i' position in MICRODVD_STYLES */;
77 microdvd_set_tag(tags, tag);
78 s++;
79 }
80 return s;
81}
82
2ba45a60
DM
83static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
84{
f6fa7814
DM
85 s = check_for_italic_slash_marker(tags, s);
86
2ba45a60
DM
87 while (*s == '{') {
88 char *start = s;
89 char tag_char = *(s + 1);
90 struct microdvd_tag tag = {0};
91
92 if (!tag_char || *(s + 2) != ':')
93 break;
94 s += 3;
95
96 switch (tag_char) {
97
98 /* Style */
99 case 'Y':
100 tag.persistent = MICRODVD_PERSISTENT_ON;
101 case 'y':
102 while (*s && *s != '}') {
103 int style_index = indexof(MICRODVD_STYLES, *s);
104
105 if (style_index >= 0)
106 tag.data1 |= (1 << style_index);
107 s++;
108 }
109 if (*s != '}')
110 break;
111 /* We must distinguish persistent and non-persistent styles
112 * to handle this kind of style tags: {y:ib}{Y:us} */
113 tag.key = tag_char;
114 break;
115
116 /* Color */
117 case 'C':
118 tag.persistent = MICRODVD_PERSISTENT_ON;
119 case 'c':
f6fa7814 120 while (*s == '$' || *s == '#')
2ba45a60
DM
121 s++;
122 tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
123 if (*s != '}')
124 break;
125 tag.key = 'c';
126 break;
127
128 /* Font name */
129 case 'F':
130 tag.persistent = MICRODVD_PERSISTENT_ON;
131 case 'f': {
132 int len = indexof(s, '}');
133 if (len < 0)
134 break;
135 tag.data_string = s;
136 tag.data_string_len = len;
137 s += len;
138 tag.key = 'f';
139 break;
140 }
141
142 /* Font size */
143 case 'S':
144 tag.persistent = MICRODVD_PERSISTENT_ON;
145 case 's':
146 tag.data1 = strtol(s, &s, 10);
147 if (*s != '}')
148 break;
149 tag.key = 's';
150 break;
151
152 /* Charset */
153 case 'H': {
154 //TODO: not yet handled, just parsed.
155 int len = indexof(s, '}');
156 if (len < 0)
157 break;
158 tag.data_string = s;
159 tag.data_string_len = len;
160 s += len;
161 tag.key = 'h';
162 break;
163 }
164
165 /* Position */
166 case 'P':
167 tag.persistent = MICRODVD_PERSISTENT_ON;
168 tag.data1 = (*s++ == '1');
169 if (*s != '}')
170 break;
171 tag.key = 'p';
172 break;
173
174 /* Coordinates */
175 case 'o':
176 tag.persistent = MICRODVD_PERSISTENT_ON;
177 tag.data1 = strtol(s, &s, 10);
178 if (*s != ',')
179 break;
180 s++;
181 tag.data2 = strtol(s, &s, 10);
182 if (*s != '}')
183 break;
184 tag.key = 'o';
185 break;
186
187 default: /* Unknown tag, we consider it's text */
188 break;
189 }
190
191 if (tag.key == 0)
192 return start;
193
194 microdvd_set_tag(tags, tag);
195 s++;
196 }
f6fa7814 197 return check_for_italic_slash_marker(tags, s);
2ba45a60
DM
198}
199
200static void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
201{
202 int i, sidx;
203 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
204 if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
205 continue;
206 switch (tags[i].key) {
207 case 'Y':
208 case 'y':
209 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
210 if (tags[i].data1 & (1 << sidx))
211 av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
212 break;
213
214 case 'c':
215 av_bprintf(new_line, "{\\c&H%06X&}", tags[i].data1);
216 break;
217
218 case 'f':
219 av_bprintf(new_line, "{\\fn%.*s}",
220 tags[i].data_string_len, tags[i].data_string);
221 break;
222
223 case 's':
224 av_bprintf(new_line, "{\\fs%d}", tags[i].data1);
225 break;
226
227 case 'p':
228 if (tags[i].data1 == 0)
229 av_bprintf(new_line, "{\\an8}");
230 break;
231
232 case 'o':
233 av_bprintf(new_line, "{\\pos(%d,%d)}",
234 tags[i].data1, tags[i].data2);
235 break;
236 }
237 if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
238 tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
239 }
240}
241
242static void microdvd_close_no_persistent_tags(AVBPrint *new_line,
243 struct microdvd_tag *tags)
244{
245 int i, sidx;
246
247 for (i = sizeof(MICRODVD_TAGS) - 2; i >= 0; i--) {
248 if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
249 continue;
250 switch (tags[i].key) {
251
252 case 'y':
253 for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
254 if (tags[i].data1 & (1 << sidx))
255 av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
256 break;
257
258 case 'c':
259 av_bprintf(new_line, "{\\c}");
260 break;
261
262 case 'f':
263 av_bprintf(new_line, "{\\fn}");
264 break;
265
266 case 's':
267 av_bprintf(new_line, "{\\fs}");
268 break;
269 }
270 tags[i].key = 0;
271 }
272}
273
274static int microdvd_decode_frame(AVCodecContext *avctx,
275 void *data, int *got_sub_ptr, AVPacket *avpkt)
276{
277 AVSubtitle *sub = data;
278 AVBPrint new_line;
2ba45a60
DM
279 char *line = avpkt->data;
280 char *end = avpkt->data + avpkt->size;
281 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
282
283 if (avpkt->size <= 0)
284 return avpkt->size;
285
2ba45a60
DM
286 av_bprint_init(&new_line, 0, 2048);
287
288 // subtitle content
289 while (line < end && *line) {
290
291 // parse MicroDVD tags, and open them in ASS
292 line = microdvd_load_tags(tags, line);
293 microdvd_open_tags(&new_line, tags);
294
295 // simple copy until EOL or forced carriage return
296 while (line < end && *line && *line != '|') {
297 av_bprint_chars(&new_line, *line, 1);
298 line++;
299 }
300
301 // line split
302 if (line < end && *line == '|') {
303 microdvd_close_no_persistent_tags(&new_line, tags);
304 av_bprintf(&new_line, "\\N");
305 line++;
306 }
307 }
308 if (new_line.len) {
f6fa7814
DM
309 int ret;
310 int64_t start = avpkt->pts;
311 int64_t duration = avpkt->duration;
312 int ts_start = av_rescale_q(start, avctx->time_base, (AVRational){1,100});
313 int ts_duration = duration != -1 ?
314 av_rescale_q(duration, avctx->time_base, (AVRational){1,100}) : -1;
315
316 ret = ff_ass_add_rect_bprint(sub, &new_line, ts_start, ts_duration);
317 av_bprint_finalize(&new_line, NULL);
318 if (ret < 0)
319 return ret;
2ba45a60
DM
320 }
321
322 *got_sub_ptr = sub->num_rects > 0;
323 return avpkt->size;
324}
325
326static int microdvd_init(AVCodecContext *avctx)
327{
328 int i, sidx;
329 AVBPrint font_buf;
330 int font_size = ASS_DEFAULT_FONT_SIZE;
331 int color = ASS_DEFAULT_COLOR;
332 int bold = ASS_DEFAULT_BOLD;
333 int italic = ASS_DEFAULT_ITALIC;
334 int underline = ASS_DEFAULT_UNDERLINE;
335 int alignment = ASS_DEFAULT_ALIGNMENT;
336 struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
337
338 av_bprint_init(&font_buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
339 av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT);
340
341 if (avctx->extradata) {
342 microdvd_load_tags(tags, avctx->extradata);
343 for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
344 switch (av_tolower(tags[i].key)) {
345 case 'y':
346 for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) {
347 if (tags[i].data1 & (1 << sidx)) {
348 switch (MICRODVD_STYLES[sidx]) {
349 case 'i': italic = 1; break;
350 case 'b': bold = 1; break;
351 case 'u': underline = 1; break;
352 }
353 }
354 }
355 break;
356
357 case 'c': color = tags[i].data1; break;
358 case 's': font_size = tags[i].data1; break;
359 case 'p': alignment = 8; break;
360
361 case 'f':
362 av_bprint_clear(&font_buf);
363 av_bprintf(&font_buf, "%.*s",
364 tags[i].data_string_len, tags[i].data_string);
365 break;
366 }
367 }
368 }
369 return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
370 ASS_DEFAULT_BACK_COLOR, bold, italic,
371 underline, alignment);
372}
373
374AVCodec ff_microdvd_decoder = {
375 .name = "microdvd",
376 .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
377 .type = AVMEDIA_TYPE_SUBTITLE,
378 .id = AV_CODEC_ID_MICRODVD,
379 .init = microdvd_init,
380 .decode = microdvd_decode_frame,
381};