OSDN Git Service

avcodec/snow: Initialize spatial_decomposition_count to a valid value
[android-x86/external-ffmpeg.git] / libavcodec / microdvddec.c
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
35 static int indexof(const char *s, int c)
36 {
37     char *f = strchr(s, c);
38     return f ? (f - s) : -1;
39 }
40
41 struct 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
57 static 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
69 /* some samples have lines that start with a / indicating non persistent italic
70  * marker */
71 static 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
83 static char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
84 {
85     s = check_for_italic_slash_marker(tags, s);
86
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':
120             while (*s == '$' || *s == '#')
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     }
197     return check_for_italic_slash_marker(tags, s);
198 }
199
200 static 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
242 static 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
274 static int microdvd_decode_frame(AVCodecContext *avctx,
275                                  void *data, int *got_sub_ptr, AVPacket *avpkt)
276 {
277     AVSubtitle *sub = data;
278     AVBPrint new_line;
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
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) {
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;
320     }
321
322     *got_sub_ptr = sub->num_rects > 0;
323     return avpkt->size;
324 }
325
326 static 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
374 AVCodec 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 };