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"
35 static int indexof(const char *s
, int c
)
37 char *f
= strchr(s
, c
);
38 return f
? (f
- s
) : -1;
50 #define MICRODVD_PERSISTENT_OFF 0
51 #define MICRODVD_PERSISTENT_ON 1
52 #define MICRODVD_PERSISTENT_OPENED 2
54 // Color, Font, Size, cHarset, stYle, Position, cOordinate
55 #define MICRODVD_TAGS "cfshyYpo"
57 static void microdvd_set_tag(struct microdvd_tag
*tags
, struct microdvd_tag tag
)
59 int tag_index
= indexof(MICRODVD_TAGS
, tag
.key
);
63 memcpy(&tags
[tag_index
], &tag
, sizeof(tag
));
66 // italic, bold, underline, strike-through
67 #define MICRODVD_STYLES "ibus"
69 /* some samples have lines that start with a / indicating non persistent italic
71 static char *check_for_italic_slash_marker(struct microdvd_tag
*tags
, char *s
)
74 struct microdvd_tag tag
= tags
[indexof(MICRODVD_TAGS
, 'y')];
76 tag
.data1
|= 1 << 0 /* 'i' position in MICRODVD_STYLES */;
77 microdvd_set_tag(tags
, tag
);
83 static char *microdvd_load_tags(struct microdvd_tag
*tags
, char *s
)
85 s
= check_for_italic_slash_marker(tags
, s
);
89 char tag_char
= *(s
+ 1);
90 struct microdvd_tag tag
= {0};
92 if (!tag_char
|| *(s
+ 2) != ':')
100 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
102 while (*s
&& *s
!= '}') {
103 int style_index
= indexof(MICRODVD_STYLES
, *s
);
105 if (style_index
>= 0)
106 tag
.data1
|= (1 << style_index
);
111 /* We must distinguish persistent and non-persistent styles
112 * to handle this kind of style tags: {y:ib}{Y:us} */
118 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
120 while (*s
== '$' || *s
== '#')
122 tag
.data1
= strtol(s
, &s
, 16) & 0x00ffffff;
130 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
132 int len
= indexof(s
, '}');
136 tag
.data_string_len
= len
;
144 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
146 tag
.data1
= strtol(s
, &s
, 10);
154 //TODO: not yet handled, just parsed.
155 int len
= indexof(s
, '}');
159 tag
.data_string_len
= len
;
167 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
168 tag
.data1
= (*s
++ == '1');
176 tag
.persistent
= MICRODVD_PERSISTENT_ON
;
177 tag
.data1
= strtol(s
, &s
, 10);
181 tag
.data2
= strtol(s
, &s
, 10);
187 default: /* Unknown tag, we consider it's text */
194 microdvd_set_tag(tags
, tag
);
197 return check_for_italic_slash_marker(tags
, s
);
200 static void microdvd_open_tags(AVBPrint
*new_line
, struct microdvd_tag
*tags
)
203 for (i
= 0; i
< sizeof(MICRODVD_TAGS
) - 1; i
++) {
204 if (tags
[i
].persistent
== MICRODVD_PERSISTENT_OPENED
)
206 switch (tags
[i
].key
) {
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
]);
215 av_bprintf(new_line
, "{\\c&H%06X&}", tags
[i
].data1
);
219 av_bprintf(new_line
, "{\\fn%.*s}",
220 tags
[i
].data_string_len
, tags
[i
].data_string
);
224 av_bprintf(new_line
, "{\\fs%d}", tags
[i
].data1
);
228 if (tags
[i
].data1
== 0)
229 av_bprintf(new_line
, "{\\an8}");
233 av_bprintf(new_line
, "{\\pos(%d,%d)}",
234 tags
[i
].data1
, tags
[i
].data2
);
237 if (tags
[i
].persistent
== MICRODVD_PERSISTENT_ON
)
238 tags
[i
].persistent
= MICRODVD_PERSISTENT_OPENED
;
242 static void microdvd_close_no_persistent_tags(AVBPrint
*new_line
,
243 struct microdvd_tag
*tags
)
247 for (i
= sizeof(MICRODVD_TAGS
) - 2; i
>= 0; i
--) {
248 if (tags
[i
].persistent
!= MICRODVD_PERSISTENT_OFF
)
250 switch (tags
[i
].key
) {
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
]);
259 av_bprintf(new_line
, "{\\c}");
263 av_bprintf(new_line
, "{\\fn}");
267 av_bprintf(new_line
, "{\\fs}");
274 static int microdvd_decode_frame(AVCodecContext
*avctx
,
275 void *data
, int *got_sub_ptr
, AVPacket
*avpkt
)
277 AVSubtitle
*sub
= data
;
279 char *line
= avpkt
->data
;
280 char *end
= avpkt
->data
+ avpkt
->size
;
281 struct microdvd_tag tags
[sizeof(MICRODVD_TAGS
) - 1] = {{0}};
283 if (avpkt
->size
<= 0)
286 av_bprint_init(&new_line
, 0, 2048);
289 while (line
< end
&& *line
) {
291 // parse MicroDVD tags, and open them in ASS
292 line
= microdvd_load_tags(tags
, line
);
293 microdvd_open_tags(&new_line
, tags
);
295 // simple copy until EOL or forced carriage return
296 while (line
< end
&& *line
&& *line
!= '|') {
297 av_bprint_chars(&new_line
, *line
, 1);
302 if (line
< end
&& *line
== '|') {
303 microdvd_close_no_persistent_tags(&new_line
, tags
);
304 av_bprintf(&new_line
, "\\N");
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;
316 ret
= ff_ass_add_rect_bprint(sub
, &new_line
, ts_start
, ts_duration
);
317 av_bprint_finalize(&new_line
, NULL
);
322 *got_sub_ptr
= sub
->num_rects
> 0;
326 static int microdvd_init(AVCodecContext
*avctx
)
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}};
338 av_bprint_init(&font_buf
, 0, AV_BPRINT_SIZE_AUTOMATIC
);
339 av_bprintf(&font_buf
, "%s", ASS_DEFAULT_FONT
);
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
)) {
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;
357 case 'c': color
= tags
[i
].data1
; break;
358 case 's': font_size
= tags
[i
].data1
; break;
359 case 'p': alignment
= 8; break;
362 av_bprint_clear(&font_buf
);
363 av_bprintf(&font_buf
, "%.*s",
364 tags
[i
].data_string_len
, tags
[i
].data_string
);
369 return ff_ass_subtitle_header(avctx
, font_buf
.str
, font_size
, color
,
370 ASS_DEFAULT_BACK_COLOR
, bold
, italic
,
371 underline
, alignment
);
374 AVCodec ff_microdvd_decoder
= {
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
,