Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * SSA/ASS spliting functions
  3.  * Copyright (c) 2010  Aurelien Jacobs <aurel@gnuage.org>
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. #include "avcodec.h"
  23. #include "ass_split.h"
  24.  
  25. typedef enum {
  26.     ASS_STR,
  27.     ASS_INT,
  28.     ASS_FLT,
  29.     ASS_COLOR,
  30.     ASS_TIMESTAMP,
  31.     ASS_ALGN,
  32. } ASSFieldType;
  33.  
  34. typedef struct {
  35.     const char *name;
  36.     int type;
  37.     int offset;
  38. } ASSFields;
  39.  
  40. typedef struct {
  41.     const char *section;
  42.     const char *format_header;
  43.     const char *fields_header;
  44.     int         size;
  45.     int         offset;
  46.     int         offset_count;
  47.     ASSFields   fields[10];
  48. } ASSSection;
  49.  
  50. static const ASSSection ass_sections[] = {
  51.     { .section       = "Script Info",
  52.       .offset        = offsetof(ASS, script_info),
  53.       .fields = {{"ScriptType", ASS_STR, offsetof(ASSScriptInfo, script_type)},
  54.                  {"Collisions", ASS_STR, offsetof(ASSScriptInfo, collisions) },
  55.                  {"PlayResX",   ASS_INT, offsetof(ASSScriptInfo, play_res_x) },
  56.                  {"PlayResY",   ASS_INT, offsetof(ASSScriptInfo, play_res_y) },
  57.                  {"Timer",      ASS_FLT, offsetof(ASSScriptInfo, timer)      },
  58.                  {0},
  59.         }
  60.     },
  61.     { .section       = "V4+ Styles",
  62.       .format_header = "Format",
  63.       .fields_header = "Style",
  64.       .size          = sizeof(ASSStyle),
  65.       .offset        = offsetof(ASS, styles),
  66.       .offset_count  = offsetof(ASS, styles_count),
  67.       .fields = {{"Name",         ASS_STR,  offsetof(ASSStyle, name)         },
  68.                  {"Fontname",     ASS_STR,  offsetof(ASSStyle, font_name)    },
  69.                  {"Fontsize",     ASS_INT,  offsetof(ASSStyle, font_size)    },
  70.                  {"PrimaryColour",ASS_COLOR,offsetof(ASSStyle, primary_color)},
  71.                  {"BackColour",   ASS_COLOR,offsetof(ASSStyle, back_color)   },
  72.                  {"Bold",         ASS_INT,  offsetof(ASSStyle, bold)         },
  73.                  {"Italic",       ASS_INT,  offsetof(ASSStyle, italic)       },
  74.                  {"Underline",    ASS_INT,  offsetof(ASSStyle, underline)    },
  75.                  {"Alignment",    ASS_INT,  offsetof(ASSStyle, alignment)    },
  76.                  {0},
  77.         }
  78.     },
  79.     { .section       = "V4 Styles",
  80.       .format_header = "Format",
  81.       .fields_header = "Style",
  82.       .size          = sizeof(ASSStyle),
  83.       .offset        = offsetof(ASS, styles),
  84.       .offset_count  = offsetof(ASS, styles_count),
  85.       .fields = {{"Name",         ASS_STR,  offsetof(ASSStyle, name)         },
  86.                  {"Fontname",     ASS_STR,  offsetof(ASSStyle, font_name)    },
  87.                  {"Fontsize",     ASS_INT,  offsetof(ASSStyle, font_size)    },
  88.                  {"PrimaryColour",ASS_COLOR,offsetof(ASSStyle, primary_color)},
  89.                  {"BackColour",   ASS_COLOR,offsetof(ASSStyle, back_color)   },
  90.                  {"Bold",         ASS_INT,  offsetof(ASSStyle, bold)         },
  91.                  {"Italic",       ASS_INT,  offsetof(ASSStyle, italic)       },
  92.                  {"Alignment",    ASS_ALGN, offsetof(ASSStyle, alignment)    },
  93.                  {0},
  94.         }
  95.     },
  96.     { .section       = "Events",
  97.       .format_header = "Format",
  98.       .fields_header = "Dialogue",
  99.       .size          = sizeof(ASSDialog),
  100.       .offset        = offsetof(ASS, dialogs),
  101.       .offset_count  = offsetof(ASS, dialogs_count),
  102.       .fields = {{"Layer",  ASS_INT,        offsetof(ASSDialog, layer)       },
  103.                  {"Start",  ASS_TIMESTAMP,  offsetof(ASSDialog, start)       },
  104.                  {"End",    ASS_TIMESTAMP,  offsetof(ASSDialog, end)         },
  105.                  {"Style",  ASS_STR,        offsetof(ASSDialog, style)       },
  106.                  {"Text",   ASS_STR,        offsetof(ASSDialog, text)        },
  107.                  {0},
  108.         }
  109.     },
  110. };
  111.  
  112.  
  113. typedef int (*ASSConvertFunc)(void *dest, const char *buf, int len);
  114.  
  115. static int convert_str(void *dest, const char *buf, int len)
  116. {
  117.     char *str = av_malloc(len + 1);
  118.     if (str) {
  119.         memcpy(str, buf, len);
  120.         str[len] = 0;
  121.         if (*(void **)dest)
  122.             av_free(*(void **)dest);
  123.         *(char **)dest = str;
  124.     }
  125.     return !str;
  126. }
  127. static int convert_int(void *dest, const char *buf, int len)
  128. {
  129.     return sscanf(buf, "%d", (int *)dest) == 1;
  130. }
  131. static int convert_flt(void *dest, const char *buf, int len)
  132. {
  133.     return sscanf(buf, "%f", (float *)dest) == 1;
  134. }
  135. static int convert_color(void *dest, const char *buf, int len)
  136. {
  137.     return sscanf(buf, "&H%8x", (int *)dest) == 1 ||
  138.            sscanf(buf, "%d",    (int *)dest) == 1;
  139. }
  140. static int convert_timestamp(void *dest, const char *buf, int len)
  141. {
  142.     int c, h, m, s, cs;
  143.     if ((c = sscanf(buf, "%d:%02d:%02d.%02d", &h, &m, &s, &cs)) == 4)
  144.         *(int *)dest = 360000*h + 6000*m + 100*s + cs;
  145.     return c == 4;
  146. }
  147. static int convert_alignment(void *dest, const char *buf, int len)
  148. {
  149.     int a;
  150.     if (sscanf(buf, "%d", &a) == 1) {
  151.         /* convert V4 Style alignment to V4+ Style */
  152.         *(int *)dest = a + ((a&4) >> 1) - 5*!!(a&8);
  153.         return 1;
  154.     }
  155.     return 0;
  156. }
  157.  
  158. static const ASSConvertFunc convert_func[] = {
  159.     [ASS_STR]       = convert_str,
  160.     [ASS_INT]       = convert_int,
  161.     [ASS_FLT]       = convert_flt,
  162.     [ASS_COLOR]     = convert_color,
  163.     [ASS_TIMESTAMP] = convert_timestamp,
  164.     [ASS_ALGN]      = convert_alignment,
  165. };
  166.  
  167.  
  168. struct ASSSplitContext {
  169.     ASS ass;
  170.     int current_section;
  171.     int field_number[FF_ARRAY_ELEMS(ass_sections)];
  172.     int *field_order[FF_ARRAY_ELEMS(ass_sections)];
  173. };
  174.  
  175.  
  176. static uint8_t *realloc_section_array(ASSSplitContext *ctx)
  177. {
  178.     const ASSSection *section = &ass_sections[ctx->current_section];
  179.     int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
  180.     void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset);
  181.     uint8_t *tmp = av_realloc(*section_ptr, (*count+1)*section->size);
  182.     if (!tmp)
  183.         return NULL;
  184.     *section_ptr = tmp;
  185.     tmp += *count * section->size;
  186.     memset(tmp, 0, section->size);
  187.     (*count)++;
  188.     return tmp;
  189. }
  190.  
  191. static inline int is_eol(char buf)
  192. {
  193.     return buf == '\r' || buf == '\n' || buf == 0;
  194. }
  195.  
  196. static inline const char *skip_space(const char *buf)
  197. {
  198.     while (*buf == ' ')
  199.         buf++;
  200.     return buf;
  201. }
  202.  
  203. static const char *ass_split_section(ASSSplitContext *ctx, const char *buf)
  204. {
  205.     const ASSSection *section = &ass_sections[ctx->current_section];
  206.     int *number = &ctx->field_number[ctx->current_section];
  207.     int *order = ctx->field_order[ctx->current_section];
  208.     int *tmp, i, len;
  209.  
  210.     while (buf && *buf) {
  211.         if (buf[0] == '[') {
  212.             ctx->current_section = -1;
  213.             break;
  214.         }
  215.         if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':')) {
  216.             /* skip comments */
  217.         } else if (section->format_header && !order) {
  218.             len = strlen(section->format_header);
  219.             if (strncmp(buf, section->format_header, len) || buf[len] != ':')
  220.                 return NULL;
  221.             buf += len + 1;
  222.             while (!is_eol(*buf)) {
  223.                 buf = skip_space(buf);
  224.                 len = strcspn(buf, ", \r\n");
  225.                 if (!(tmp = av_realloc(order, (*number + 1) * sizeof(*order))))
  226.                     return NULL;
  227.                 order = tmp;
  228.                 order[*number] = -1;
  229.                 for (i=0; section->fields[i].name; i++)
  230.                     if (!strncmp(buf, section->fields[i].name, len)) {
  231.                         order[*number] = i;
  232.                         break;
  233.                     }
  234.                 (*number)++;
  235.                 buf = skip_space(buf + len + (buf[len] == ','));
  236.             }
  237.             ctx->field_order[ctx->current_section] = order;
  238.         } else if (section->fields_header) {
  239.             len = strlen(section->fields_header);
  240.             if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') {
  241.                 uint8_t *ptr, *struct_ptr = realloc_section_array(ctx);
  242.                 if (!struct_ptr)  return NULL;
  243.                 buf += len + 1;
  244.                 for (i=0; !is_eol(*buf) && i < *number; i++) {
  245.                     int last = i == *number - 1;
  246.                     buf = skip_space(buf);
  247.                     len = strcspn(buf, last ? "\r\n" : ",\r\n");
  248.                     if (order[i] >= 0) {
  249.                         ASSFieldType type = section->fields[order[i]].type;
  250.                         ptr = struct_ptr + section->fields[order[i]].offset;
  251.                         convert_func[type](ptr, buf, len);
  252.                     }
  253.                     buf += len;
  254.                     if (!last && *buf) buf++;
  255.                     buf = skip_space(buf);
  256.                 }
  257.             }
  258.         } else {
  259.             len = strcspn(buf, ":\r\n");
  260.             if (buf[len] == ':') {
  261.                 for (i=0; section->fields[i].name; i++)
  262.                     if (!strncmp(buf, section->fields[i].name, len)) {
  263.                         ASSFieldType type = section->fields[i].type;
  264.                         uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
  265.                         ptr += section->fields[i].offset;
  266.                         buf = skip_space(buf + len + 1);
  267.                         convert_func[type](ptr, buf, strcspn(buf, "\r\n"));
  268.                         break;
  269.                     }
  270.             }
  271.         }
  272.         buf += strcspn(buf, "\n");
  273.         buf += !!*buf;
  274.     }
  275.     return buf;
  276. }
  277.  
  278. static int ass_split(ASSSplitContext *ctx, const char *buf)
  279. {
  280.     char c, section[16];
  281.     int i;
  282.  
  283.     if (ctx->current_section >= 0)
  284.         buf = ass_split_section(ctx, buf);
  285.  
  286.     while (buf && *buf) {
  287.         if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) {
  288.             buf += strcspn(buf, "\n");
  289.             buf += !!*buf;
  290.             for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
  291.                 if (!strcmp(section, ass_sections[i].section)) {
  292.                     ctx->current_section = i;
  293.                     buf = ass_split_section(ctx, buf);
  294.                 }
  295.         } else {
  296.             buf += strcspn(buf, "\n");
  297.             buf += !!*buf;
  298.         }
  299.     }
  300.     return buf ? 0 : AVERROR_INVALIDDATA;
  301. }
  302.  
  303. ASSSplitContext *ff_ass_split(const char *buf)
  304. {
  305.     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
  306.     ctx->current_section = -1;
  307.     if (ass_split(ctx, buf) < 0) {
  308.         ff_ass_split_free(ctx);
  309.         return NULL;
  310.     }
  311.     return ctx;
  312. }
  313.  
  314. static void free_section(ASSSplitContext *ctx, const ASSSection *section)
  315. {
  316.     uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
  317.     int i, j, *count, c = 1;
  318.  
  319.     if (section->format_header) {
  320.         ptr   = *(void **)ptr;
  321.         count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
  322.     } else
  323.         count = &c;
  324.  
  325.     if (ptr)
  326.         for (i=0; i<*count; i++, ptr += section->size)
  327.             for (j=0; section->fields[j].name; j++) {
  328.                 const ASSFields *field = &section->fields[j];
  329.                 if (field->type == ASS_STR)
  330.                     av_freep(ptr + field->offset);
  331.             }
  332.     *count = 0;
  333.  
  334.     if (section->format_header)
  335.         av_freep((uint8_t *)&ctx->ass + section->offset);
  336. }
  337.  
  338. ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf,
  339.                                int cache, int *number)
  340. {
  341.     ASSDialog *dialog = NULL;
  342.     int i, count;
  343.     if (!cache)
  344.         for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
  345.             if (!strcmp(ass_sections[i].section, "Events")) {
  346.                 free_section(ctx, &ass_sections[i]);
  347.                 break;
  348.             }
  349.     count = ctx->ass.dialogs_count;
  350.     if (ass_split(ctx, buf) == 0)
  351.         dialog = ctx->ass.dialogs + count;
  352.     if (number)
  353.         *number = ctx->ass.dialogs_count - count;
  354.     return dialog;
  355. }
  356.  
  357. void ff_ass_split_free(ASSSplitContext *ctx)
  358. {
  359.     if (ctx) {
  360.         int i;
  361.         for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) {
  362.             free_section(ctx, &ass_sections[i]);
  363.             av_freep(&(ctx->field_order[i]));
  364.         }
  365.         av_free(ctx);
  366.     }
  367. }
  368.  
  369.  
  370. int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  371.                                 const char *buf)
  372. {
  373.     const char *text = NULL;
  374.     char new_line[2];
  375.     int text_len = 0;
  376.  
  377.     while (buf && *buf) {
  378.         if (text && callbacks->text &&
  379.             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
  380.              !strncmp(buf, "{\\", 2))) {
  381.             callbacks->text(priv, text, text_len);
  382.             text = NULL;
  383.         }
  384.         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
  385.             if (callbacks->new_line)
  386.                 callbacks->new_line(priv, new_line[0] == 'N');
  387.             buf += 2;
  388.         } else if (!strncmp(buf, "{\\", 2)) {
  389.             buf++;
  390.             while (*buf == '\\') {
  391.                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
  392.                 unsigned int color = 0xFFFFFFFF;
  393.                 int len, size = -1, an = -1, alpha = -1;
  394.                 int x1, y1, x2, y2, t1 = -1, t2 = -1;
  395.                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
  396.                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
  397.                     len += close != -1;
  398.                     if (callbacks->style)
  399.                         callbacks->style(priv, style[0], close);
  400.                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
  401.                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
  402.                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
  403.                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
  404.                     if (callbacks->color)
  405.                         callbacks->color(priv, color, c_num[0] - '0');
  406.                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
  407.                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
  408.                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
  409.                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
  410.                     if (callbacks->alpha)
  411.                         callbacks->alpha(priv, alpha, c_num[0] - '0');
  412.                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
  413.                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
  414.                     if (callbacks->font_name)
  415.                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
  416.                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
  417.                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
  418.                     if (callbacks->font_size)
  419.                         callbacks->font_size(priv, size);
  420.                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
  421.                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
  422.                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
  423.                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
  424.                     if (an != -1 && buf[2] != 'n')
  425.                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
  426.                     if (callbacks->alignment)
  427.                         callbacks->alignment(priv, an);
  428.                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
  429.                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
  430.                     if (callbacks->cancel_overrides)
  431.                         callbacks->cancel_overrides(priv, tmp);
  432.                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
  433.                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
  434.                     if (callbacks->move)
  435.                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
  436.                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
  437.                     if (callbacks->move)
  438.                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
  439.                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
  440.                     if (callbacks->origin)
  441.                         callbacks->origin(priv, x1, y1);
  442.                 } else {
  443.                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
  444.                 }
  445.                 buf += len - 1;
  446.             }
  447.             if (*buf++ != '}')
  448.                 return AVERROR_INVALIDDATA;
  449.         } else {
  450.             if (!text) {
  451.                 text = buf;
  452.                 text_len = 1;
  453.             } else
  454.                 text_len++;
  455.             buf++;
  456.         }
  457.     }
  458.     if (text && callbacks->text)
  459.         callbacks->text(priv, text, text_len);
  460.     if (callbacks->end)
  461.         callbacks->end(priv);
  462.     return 0;
  463. }
  464.  
  465. ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
  466. {
  467.     ASS *ass = &ctx->ass;
  468.     int i;
  469.  
  470.     if (!style || !*style)
  471.         style = "Default";
  472.     for (i=0; i<ass->styles_count; i++)
  473.         if (!strcmp(ass->styles[i].name, style))
  474.             return ass->styles + i;
  475.     return NULL;
  476. }
  477.