2 * Copyright (c) 2012 Clément Bœsch
4 * This file is part of FFmpeg.
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.
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.
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
23 * MicroDVD subtitle decoder
25 * Based on the specifications found here:
26 * https://trac.videolan.org/vlc/ticket/1825#comment:6
29 #include "libavutil/avstring.h"
30 #include "libavutil/parseutils.h"
31 #include "libavutil/bprint.h"
34 #include "codec_internal.h"
36 static int indexof(const char *s
, int c
)
38 char *f
= strchr(s
, c
);
39 return f
? (f
- s
) : -1;
51 #define MICRODVD_PERSISTENT_OFF 0
52 #define MICRODVD_PERSISTENT_ON 1
53 #define MICRODVD_PERSISTENT_OPENED 2
55 // Color, Font, Size, cHarset, stYle, Position, cOordinate
56 #define MICRODVD_TAGS "cfshyYpo"
58 static void microdvd_set_tag(struct microdvd_tag
*tags
, struct microdvd_tag tag
)
60 int tag_index
= indexof(MICRODVD_TAGS
, tag
.key
);
64 memcpy(&tags
[tag_index
], &tag
, sizeof(tag
));
67 // italic, bold, underline, strike-through
68 #define MICRODVD_STYLES "ibus"
70 /* some samples have lines that start with a / indicating non persistent italic
72 static char *check_for_italic_slash_marker(struct microdvd_tag
*tags
, char *s
)
75 struct microdvd_tag tag
= tags
[indexof(MICRODVD_TAGS
, 'y')];
77 tag
.data1
|= 1 << 0 /* 'i' position in MICRODVD_STYLES */;
78 microdvd_set_tag(tags
, tag
);
84 static char *microdvd_load_tags(struct microdvd_tag
*tags
, char *s
)
86 s
= check_for_italic_slash_marker(tags
, s
);
90 char tag_char
= *(s
+ 1);
91 struct microdvd_tag tag
= {0};
93 if (!tag_char
|| *(s
+ 2) != ':')
101 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
103 while (*s
&& *s
!= '}' && s
- start
< 256) {
104 int style_index
= indexof(MICRODVD_STYLES
, *s
);
106 if (style_index
>= 0)
107 tag
.data1
|= (1 << style_index
);
112 /* We must distinguish persistent and non-persistent styles
113 * to handle this kind of style tags: {y:ib}{Y:us} */
119 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
121 while (*s
== '$' || *s
== '#')
123 tag
.data1
= strtol(s
, &s
, 16) & 0x00ffffff;
131 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
133 int len
= indexof(s
, '}');
137 tag
.data_string_len
= len
;
145 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
147 tag
.data1
= strtol(s
, &s
, 10);
155 //TODO: not yet handled, just parsed.
156 int len
= indexof(s
, '}');
160 tag
.data_string_len
= len
;
170 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
171 tag
.data1
= (*s
++ == '1');
179 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
180 tag
.data1
= strtol(s
, &s
, 10);
184 tag
.data2
= strtol(s
, &s
, 10);
190 default: /* Unknown tag, we consider it's text */
197 microdvd_set_tag(tags
, tag
);
200 return check_for_italic_slash_marker(tags
, s
);
203 static void microdvd_open_tags(AVBPrint
*new_line
, struct microdvd_tag
*tags
)
206 for (i
= 0; i
< sizeof(MICRODVD_TAGS
) - 1; i
++) {
207 if (tags
[i
].persistent
== MICRODVD_PERSISTENT_OPENED
)
209 switch (tags
[i
].key
) {
212 for (sidx
= 0; sidx
< sizeof(MICRODVD_STYLES
) - 1; sidx
++)
213 if (tags
[i
].data1
& (1 << sidx
))
214 av_bprintf(new_line
, "{\\%c1}", MICRODVD_STYLES
[sidx
]);
218 av_bprintf(new_line
, "{\\c&H%06"PRIX32
"&}", tags
[i
].data1
);
222 av_bprintf(new_line
, "{\\fn%.*s}",
223 tags
[i
].data_string_len
, tags
[i
].data_string
);
227 av_bprintf(new_line
, "{\\fs%"PRId32
"}", tags
[i
].data1
);
231 if (tags
[i
].data1
== 0)
232 av_bprintf(new_line
, "{\\an8}");
236 av_bprintf(new_line
, "{\\pos(%"PRId32
",%"PRId32
")}",
237 tags
[i
].data1
, tags
[i
].data2
);
240 if (tags
[i
].persistent
== MICRODVD_PERSISTENT_ON
)
241 tags
[i
].persistent
= MICRODVD_PERSISTENT_OPENED
;
245 static void microdvd_close_no_persistent_tags(AVBPrint
*new_line
,
246 struct microdvd_tag
*tags
)
250 for (i
= sizeof(MICRODVD_TAGS
) - 2; i
>= 0; i
--) {
251 if (tags
[i
].persistent
!= MICRODVD_PERSISTENT_OFF
)
253 switch (tags
[i
].key
) {
256 for (sidx
= sizeof(MICRODVD_STYLES
) - 2; sidx
>= 0; sidx
--)
257 if (tags
[i
].data1
& (1 << sidx
))
258 av_bprintf(new_line
, "{\\%c0}", MICRODVD_STYLES
[sidx
]);
262 av_bprintf(new_line
, "{\\c}");
266 av_bprintf(new_line
, "{\\fn}");
270 av_bprintf(new_line
, "{\\fs}");
277 static int microdvd_decode_frame(AVCodecContext
*avctx
, AVSubtitle
*sub
,
278 int *got_sub_ptr
, const AVPacket
*avpkt
)
281 char *line
= avpkt
->data
;
282 char *end
= avpkt
->data
+ avpkt
->size
;
283 FFASSDecoderContext
*s
= avctx
->priv_data
;
284 struct microdvd_tag tags
[sizeof(MICRODVD_TAGS
) - 1] = {{0}};
286 if (avpkt
->size
<= 0)
289 av_bprint_init(&new_line
, 0, 2048);
292 while (line
< end
&& *line
) {
294 // parse MicroDVD tags, and open them in ASS
295 line
= microdvd_load_tags(tags
, line
);
296 microdvd_open_tags(&new_line
, tags
);
298 // simple copy until EOL or forced carriage return
299 while (line
< end
&& *line
&& *line
!= '|') {
300 av_bprint_chars(&new_line
, *line
, 1);
305 if (line
< end
&& *line
== '|') {
306 microdvd_close_no_persistent_tags(&new_line
, tags
);
307 av_bprintf(&new_line
, "\\N");
312 int ret
= ff_ass_add_rect(sub
, new_line
.str
, s
->readorder
++, 0, NULL
, NULL
);
313 av_bprint_finalize(&new_line
, NULL
);
318 *got_sub_ptr
= sub
->num_rects
> 0;
322 static int microdvd_init(AVCodecContext
*avctx
)
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}};
334 av_bprint_init(&font_buf
, 0, AV_BPRINT_SIZE_AUTOMATIC
);
335 av_bprintf(&font_buf
, "%s", ASS_DEFAULT_FONT
);
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
)) {
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;
353 case 'c': color
= tags
[i
].data1
; break;
354 case 's': font_size
= tags
[i
].data1
; break;
355 case 'p': alignment
= 8; break;
358 av_bprint_clear(&font_buf
);
359 av_bprintf(&font_buf
, "%.*s",
360 tags
[i
].data_string_len
, tags
[i
].data_string
);
365 return ff_ass_subtitle_header(avctx
, font_buf
.str
, font_size
, color
,
366 ASS_DEFAULT_BACK_COLOR
, bold
, italic
,
367 underline
, ASS_DEFAULT_BORDERSTYLE
,
371 const FFCodec ff_microdvd_decoder
= {
372 .p
.name
= "microdvd",
373 CODEC_LONG_NAME("MicroDVD subtitle"),
374 .p
.type
= AVMEDIA_TYPE_SUBTITLE
,
375 .p
.id
= AV_CODEC_ID_MICRODVD
,
376 .init
= microdvd_init
,
377 FF_CODEC_DECODE_SUB_CB(microdvd_decode_frame
),
378 .flush
= ff_ass_decoder_flush
,
379 .priv_data_size
= sizeof(FFASSDecoderContext
),