diff options
Diffstat (limited to 'libavcodec/jacosubdec.c')
-rw-r--r-- | libavcodec/jacosubdec.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/libavcodec/jacosubdec.c b/libavcodec/jacosubdec.c new file mode 100644 index 0000000000..0c97eb86c0 --- /dev/null +++ b/libavcodec/jacosubdec.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2012 Clément Bœsch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * JACOsub subtitle decoder + * @see http://unicorn.us.com/jacosub/jscripts.html + */ + +#include <time.h> +#include "ass.h" +#include "jacosub.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/time_internal.h" + +#undef time + +static int insert_text(AVBPrint *dst, const char *in, const char *arg) +{ + av_bprintf(dst, "%s", arg); + return 0; +} + +static int insert_datetime(AVBPrint *dst, const char *in, const char *arg) +{ + char buf[16] = {0}; + time_t now = time(0); + struct tm ltime; + + localtime_r(&now, <ime); + if (strftime(buf, sizeof(buf), arg, <ime)) + av_bprintf(dst, "%s", buf); + return 0; +} + +static int insert_color(AVBPrint *dst, const char *in, const char *arg) +{ + return 1; // skip id +} + +static int insert_font(AVBPrint *dst, const char *in, const char *arg) +{ + return 1; // skip id +} + +static const struct { + const char *from; + const char *arg; + int (*func)(AVBPrint *dst, const char *in, const char *arg); +} ass_codes_map[] = { + {"\\~", "~", insert_text}, // tilde doesn't need escaping + {"~", "{\\h}", insert_text}, // hard space + {"\\n", "\\N", insert_text}, // newline + {"\\D", "%d %b %Y", insert_datetime}, // current date + {"\\T", "%H:%M", insert_datetime}, // current time + {"\\N", "{\\r}", insert_text}, // reset to default style + {"\\I", "{\\i1}", insert_text}, // italic on + {"\\i", "{\\i0}", insert_text}, // italic off + {"\\B", "{\\b1}", insert_text}, // bold on + {"\\b", "{\\b0}", insert_text}, // bold off + {"\\U", "{\\u1}", insert_text}, // underline on + {"\\u", "{\\u0}", insert_text}, // underline off + {"\\C", "", insert_color}, // TODO: color + {"\\F", "", insert_font}, // TODO: font +}; + +enum { + ALIGN_VB = 1<<0, // vertical bottom, default + ALIGN_VM = 1<<1, // vertical middle + ALIGN_VT = 1<<2, // vertical top + ALIGN_JC = 1<<3, // justify center, default + ALIGN_JL = 1<<4, // justify left + ALIGN_JR = 1<<5, // justify right +}; + +static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src) +{ + int i, valign = 0, halign = 0; + char c = av_toupper(*src); + char directives[128] = {0}; + + /* extract the optional directives */ + if ((c >= 'A' && c <= 'Z') || c == '[') { + char *p = directives; + char *pend = directives + sizeof(directives) - 1; + + do *p++ = av_toupper(*src++); + while (*src && !jss_whitespace(*src) && p < pend); + *p = 0; + src = jss_skip_whitespace(src); + } + + /* handle directives (TODO: handle more of them, and more reliably) */ + if (strstr(directives, "VB")) valign = ALIGN_VB; + else if (strstr(directives, "VM")) valign = ALIGN_VM; + else if (strstr(directives, "VT")) valign = ALIGN_VT; + if (strstr(directives, "JC")) halign = ALIGN_JC; + else if (strstr(directives, "JL")) halign = ALIGN_JL; + else if (strstr(directives, "JR")) halign = ALIGN_JR; + if (valign || halign) { + if (!valign) valign = ALIGN_VB; + if (!halign) halign = ALIGN_JC; + switch (valign | halign) { + case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left + case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center + case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right + case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left + case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center + case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right + case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left + case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center + case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right + } + } + + /* process timed line */ + while (*src && *src != '\n') { + + /* text continue on the next line */ + if (src[0] == '\\' && src[1] == '\n') { + src += 2; + while (jss_whitespace(*src)) + src++; + continue; + } + + /* special character codes */ + for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) { + const char *from = ass_codes_map[i].from; + const char *arg = ass_codes_map[i].arg; + size_t codemap_len = strlen(from); + + if (!strncmp(src, from, codemap_len)) { + src += codemap_len; + src += ass_codes_map[i].func(dst, src, arg); + break; + } + } + + /* simple char copy */ + if (i == FF_ARRAY_ELEMS(ass_codes_map)) + av_bprintf(dst, "%c", *src++); + } +} + +static int jacosub_decode_frame(AVCodecContext *avctx, + void *data, int *got_sub_ptr, AVPacket *avpkt) +{ + int ret; + AVSubtitle *sub = data; + const char *ptr = avpkt->data; + + if (avpkt->size <= 0) + goto end; + + if (*ptr) { + AVBPrint buffer; + + // skip timers + ptr = jss_skip_whitespace(ptr); + ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++; + ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++; + + av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE); + jacosub_to_ass(avctx, &buffer, ptr); + ret = ff_ass_add_rect_bprint(sub, &buffer, avpkt->pts, avpkt->duration); + av_bprint_finalize(&buffer, NULL); + if (ret < 0) + return ret; + } + +end: + *got_sub_ptr = sub->num_rects > 0; + return avpkt->size; +} + +AVCodec ff_jacosub_decoder = { + .name = "jacosub", + .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle"), + .type = AVMEDIA_TYPE_SUBTITLE, + .id = AV_CODEC_ID_JACOSUB, + .init = ff_ass_subtitle_header_default, + .decode = jacosub_decode_frame, +}; |