You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
6.8KB

  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. * @file
  22. * JACOsub subtitle decoder
  23. * @see http://unicorn.us.com/jacosub/jscripts.html
  24. */
  25. #include <time.h>
  26. #include "ass.h"
  27. #include "jacosub.h"
  28. #include "libavutil/avstring.h"
  29. #include "libavutil/bprint.h"
  30. #include "libavutil/time_internal.h"
  31. #undef time
  32. static int insert_text(AVBPrint *dst, const char *in, const char *arg)
  33. {
  34. av_bprintf(dst, "%s", arg);
  35. return 0;
  36. }
  37. static int insert_datetime(AVBPrint *dst, const char *in, const char *arg)
  38. {
  39. char buf[16] = {0};
  40. time_t now = time(0);
  41. struct tm ltime;
  42. localtime_r(&now, &ltime);
  43. if (strftime(buf, sizeof(buf), arg, &ltime))
  44. av_bprintf(dst, "%s", buf);
  45. return 0;
  46. }
  47. static int insert_color(AVBPrint *dst, const char *in, const char *arg)
  48. {
  49. return 1; // skip id
  50. }
  51. static int insert_font(AVBPrint *dst, const char *in, const char *arg)
  52. {
  53. return 1; // skip id
  54. }
  55. static const struct {
  56. const char *from;
  57. const char *arg;
  58. int (*func)(AVBPrint *dst, const char *in, const char *arg);
  59. } ass_codes_map[] = {
  60. {"\\~", "~", insert_text}, // tilde doesn't need escaping
  61. {"~", "{\\h}", insert_text}, // hard space
  62. {"\\n", "\\N", insert_text}, // newline
  63. {"\\D", "%d %b %Y", insert_datetime}, // current date
  64. {"\\T", "%H:%M", insert_datetime}, // current time
  65. {"\\N", "{\\r}", insert_text}, // reset to default style
  66. {"\\I", "{\\i1}", insert_text}, // italic on
  67. {"\\i", "{\\i0}", insert_text}, // italic off
  68. {"\\B", "{\\b1}", insert_text}, // bold on
  69. {"\\b", "{\\b0}", insert_text}, // bold off
  70. {"\\U", "{\\u1}", insert_text}, // underline on
  71. {"\\u", "{\\u0}", insert_text}, // underline off
  72. {"\\C", "", insert_color}, // TODO: color
  73. {"\\F", "", insert_font}, // TODO: font
  74. };
  75. enum {
  76. ALIGN_VB = 1<<0, // vertical bottom, default
  77. ALIGN_VM = 1<<1, // vertical middle
  78. ALIGN_VT = 1<<2, // vertical top
  79. ALIGN_JC = 1<<3, // justify center, default
  80. ALIGN_JL = 1<<4, // justify left
  81. ALIGN_JR = 1<<5, // justify right
  82. };
  83. static void jacosub_to_ass(AVCodecContext *avctx, AVBPrint *dst, const char *src)
  84. {
  85. int i, valign = 0, halign = 0;
  86. char c = av_toupper(*src);
  87. char directives[128] = {0};
  88. /* extract the optional directives */
  89. if ((c >= 'A' && c <= 'Z') || c == '[') {
  90. char *p = directives;
  91. char *pend = directives + sizeof(directives) - 1;
  92. do *p++ = av_toupper(*src++);
  93. while (*src && !jss_whitespace(*src) && p < pend);
  94. *p = 0;
  95. src = jss_skip_whitespace(src);
  96. }
  97. /* handle directives (TODO: handle more of them, and more reliably) */
  98. if (strstr(directives, "VB")) valign = ALIGN_VB;
  99. else if (strstr(directives, "VM")) valign = ALIGN_VM;
  100. else if (strstr(directives, "VT")) valign = ALIGN_VT;
  101. if (strstr(directives, "JC")) halign = ALIGN_JC;
  102. else if (strstr(directives, "JL")) halign = ALIGN_JL;
  103. else if (strstr(directives, "JR")) halign = ALIGN_JR;
  104. if (valign || halign) {
  105. if (!valign) valign = ALIGN_VB;
  106. if (!halign) halign = ALIGN_JC;
  107. switch (valign | halign) {
  108. case ALIGN_VB | ALIGN_JL: av_bprintf(dst, "{\\an1}"); break; // bottom left
  109. case ALIGN_VB | ALIGN_JC: av_bprintf(dst, "{\\an2}"); break; // bottom center
  110. case ALIGN_VB | ALIGN_JR: av_bprintf(dst, "{\\an3}"); break; // bottom right
  111. case ALIGN_VM | ALIGN_JL: av_bprintf(dst, "{\\an4}"); break; // middle left
  112. case ALIGN_VM | ALIGN_JC: av_bprintf(dst, "{\\an5}"); break; // middle center
  113. case ALIGN_VM | ALIGN_JR: av_bprintf(dst, "{\\an6}"); break; // middle right
  114. case ALIGN_VT | ALIGN_JL: av_bprintf(dst, "{\\an7}"); break; // top left
  115. case ALIGN_VT | ALIGN_JC: av_bprintf(dst, "{\\an8}"); break; // top center
  116. case ALIGN_VT | ALIGN_JR: av_bprintf(dst, "{\\an9}"); break; // top right
  117. }
  118. }
  119. /* process timed line */
  120. while (*src && *src != '\n') {
  121. /* text continue on the next line */
  122. if (src[0] == '\\' && src[1] == '\n') {
  123. src += 2;
  124. while (jss_whitespace(*src))
  125. src++;
  126. continue;
  127. }
  128. /* special character codes */
  129. for (i = 0; i < FF_ARRAY_ELEMS(ass_codes_map); i++) {
  130. const char *from = ass_codes_map[i].from;
  131. const char *arg = ass_codes_map[i].arg;
  132. size_t codemap_len = strlen(from);
  133. if (!strncmp(src, from, codemap_len)) {
  134. src += codemap_len;
  135. src += ass_codes_map[i].func(dst, src, arg);
  136. break;
  137. }
  138. }
  139. /* simple char copy */
  140. if (i == FF_ARRAY_ELEMS(ass_codes_map))
  141. av_bprintf(dst, "%c", *src++);
  142. }
  143. }
  144. static int jacosub_decode_frame(AVCodecContext *avctx,
  145. void *data, int *got_sub_ptr, AVPacket *avpkt)
  146. {
  147. int ret;
  148. AVSubtitle *sub = data;
  149. const char *ptr = avpkt->data;
  150. FFASSDecoderContext *s = avctx->priv_data;
  151. if (avpkt->size <= 0)
  152. goto end;
  153. if (*ptr) {
  154. AVBPrint buffer;
  155. // skip timers
  156. ptr = jss_skip_whitespace(ptr);
  157. ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
  158. ptr = strchr(ptr, ' '); if (!ptr) goto end; ptr++;
  159. av_bprint_init(&buffer, JSS_MAX_LINESIZE, JSS_MAX_LINESIZE);
  160. jacosub_to_ass(avctx, &buffer, ptr);
  161. ret = ff_ass_add_rect(sub, buffer.str, s->readorder++, 0, NULL, NULL);
  162. av_bprint_finalize(&buffer, NULL);
  163. if (ret < 0)
  164. return ret;
  165. }
  166. end:
  167. *got_sub_ptr = sub->num_rects > 0;
  168. return avpkt->size;
  169. }
  170. AVCodec ff_jacosub_decoder = {
  171. .name = "jacosub",
  172. .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle"),
  173. .type = AVMEDIA_TYPE_SUBTITLE,
  174. .id = AV_CODEC_ID_JACOSUB,
  175. .init = ff_ass_subtitle_header_default,
  176. .decode = jacosub_decode_frame,
  177. .flush = ff_ass_decoder_flush,
  178. .priv_data_size = sizeof(FFASSDecoderContext),
  179. };