Subversion Repositories Kolibri OS

Rev

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[24];
  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.                  {"SecondaryColour", ASS_COLOR, offsetof(ASSStyle, secondary_color)},
  72.                  {"OutlineColour",   ASS_COLOR, offsetof(ASSStyle, outline_color)  },
  73.                  {"BackColour",      ASS_COLOR, offsetof(ASSStyle, back_color)     },
  74.                  {"Bold",            ASS_INT,   offsetof(ASSStyle, bold)           },
  75.                  {"Italic",          ASS_INT,   offsetof(ASSStyle, italic)         },
  76.                  {"Underline",       ASS_INT,   offsetof(ASSStyle, underline)      },
  77.                  {"StrikeOut",       ASS_INT,   offsetof(ASSStyle, strikeout)      },
  78.                  {"ScaleX",          ASS_FLT,   offsetof(ASSStyle, scalex)         },
  79.                  {"ScaleY",          ASS_FLT,   offsetof(ASSStyle, scaley)         },
  80.                  {"Spacing",         ASS_FLT,   offsetof(ASSStyle, spacing)        },
  81.                  {"Angle",           ASS_FLT,   offsetof(ASSStyle, angle)          },
  82.                  {"BorderStyle",     ASS_INT,   offsetof(ASSStyle, border_style)   },
  83.                  {"Outline",         ASS_FLT,   offsetof(ASSStyle, outline)        },
  84.                  {"Shadow",          ASS_FLT,   offsetof(ASSStyle, shadow)         },
  85.                  {"Alignment",       ASS_INT,   offsetof(ASSStyle, alignment)      },
  86.                  {"MarginL",         ASS_INT,   offsetof(ASSStyle, margin_l)       },
  87.                  {"MarginR",         ASS_INT,   offsetof(ASSStyle, margin_r)       },
  88.                  {"MarginV",         ASS_INT,   offsetof(ASSStyle, margin_v)       },
  89.                  {"Encoding",        ASS_INT,   offsetof(ASSStyle, encoding)       },
  90.                  {0},
  91.         }
  92.     },
  93.     { .section       = "V4 Styles",
  94.       .format_header = "Format",
  95.       .fields_header = "Style",
  96.       .size          = sizeof(ASSStyle),
  97.       .offset        = offsetof(ASS, styles),
  98.       .offset_count  = offsetof(ASS, styles_count),
  99.       .fields = {{"Name",            ASS_STR,   offsetof(ASSStyle, name)           },
  100.                  {"Fontname",        ASS_STR,   offsetof(ASSStyle, font_name)      },
  101.                  {"Fontsize",        ASS_INT,   offsetof(ASSStyle, font_size)      },
  102.                  {"PrimaryColour",   ASS_COLOR, offsetof(ASSStyle, primary_color)  },
  103.                  {"SecondaryColour", ASS_COLOR, offsetof(ASSStyle, secondary_color)},
  104.                  {"TertiaryColour",  ASS_COLOR, offsetof(ASSStyle, outline_color)  },
  105.                  {"BackColour",      ASS_COLOR, offsetof(ASSStyle, back_color)     },
  106.                  {"Bold",            ASS_INT,   offsetof(ASSStyle, bold)           },
  107.                  {"Italic",          ASS_INT,   offsetof(ASSStyle, italic)         },
  108.                  {"BorderStyle",     ASS_INT,   offsetof(ASSStyle, border_style)   },
  109.                  {"Outline",         ASS_FLT,   offsetof(ASSStyle, outline)        },
  110.                  {"Shadow",          ASS_FLT,   offsetof(ASSStyle, shadow)         },
  111.                  {"Alignment",       ASS_ALGN,  offsetof(ASSStyle, alignment)      },
  112.                  {"MarginL",         ASS_INT,   offsetof(ASSStyle, margin_l)       },
  113.                  {"MarginR",         ASS_INT,   offsetof(ASSStyle, margin_r)       },
  114.                  {"MarginV",         ASS_INT,   offsetof(ASSStyle, margin_v)       },
  115.                  {"AlphaLevel",      ASS_INT,   offsetof(ASSStyle, alpha_level)    },
  116.                  {"Encoding",        ASS_INT,   offsetof(ASSStyle, encoding)       },
  117.                  {0},
  118.         }
  119.     },
  120.     { .section       = "Events",
  121.       .format_header = "Format",
  122.       .fields_header = "Dialogue",
  123.       .size          = sizeof(ASSDialog),
  124.       .offset        = offsetof(ASS, dialogs),
  125.       .offset_count  = offsetof(ASS, dialogs_count),
  126.       .fields = {{"Layer",   ASS_INT,        offsetof(ASSDialog, layer)   },
  127.                  {"Start",   ASS_TIMESTAMP,  offsetof(ASSDialog, start)   },
  128.                  {"End",     ASS_TIMESTAMP,  offsetof(ASSDialog, end)     },
  129.                  {"Style",   ASS_STR,        offsetof(ASSDialog, style)   },
  130.                  {"Name",    ASS_STR,        offsetof(ASSDialog, name)    },
  131.                  {"MarginL", ASS_INT,        offsetof(ASSDialog, margin_l)},
  132.                  {"MarginR", ASS_INT,        offsetof(ASSDialog, margin_r)},
  133.                  {"MarginV", ASS_INT,        offsetof(ASSDialog, margin_v)},
  134.                  {"Effect",  ASS_STR,        offsetof(ASSDialog, effect)  },
  135.                  {"Text",    ASS_STR,        offsetof(ASSDialog, text)    },
  136.                  {0},
  137.         }
  138.     },
  139. };
  140.  
  141.  
  142. typedef int (*ASSConvertFunc)(void *dest, const char *buf, int len);
  143.  
  144. static int convert_str(void *dest, const char *buf, int len)
  145. {
  146.     char *str = av_malloc(len + 1);
  147.     if (str) {
  148.         memcpy(str, buf, len);
  149.         str[len] = 0;
  150.         if (*(void **)dest)
  151.             av_free(*(void **)dest);
  152.         *(char **)dest = str;
  153.     }
  154.     return !str;
  155. }
  156. static int convert_int(void *dest, const char *buf, int len)
  157. {
  158.     return sscanf(buf, "%d", (int *)dest) == 1;
  159. }
  160. static int convert_flt(void *dest, const char *buf, int len)
  161. {
  162.     return sscanf(buf, "%f", (float *)dest) == 1;
  163. }
  164. static int convert_color(void *dest, const char *buf, int len)
  165. {
  166.     return sscanf(buf, "&H%8x", (int *)dest) == 1 ||
  167.            sscanf(buf, "%d",    (int *)dest) == 1;
  168. }
  169. static int convert_timestamp(void *dest, const char *buf, int len)
  170. {
  171.     int c, h, m, s, cs;
  172.     if ((c = sscanf(buf, "%d:%02d:%02d.%02d", &h, &m, &s, &cs)) == 4)
  173.         *(int *)dest = 360000*h + 6000*m + 100*s + cs;
  174.     return c == 4;
  175. }
  176. static int convert_alignment(void *dest, const char *buf, int len)
  177. {
  178.     int a;
  179.     if (sscanf(buf, "%d", &a) == 1) {
  180.         /* convert V4 Style alignment to V4+ Style */
  181.         *(int *)dest = a + ((a&4) >> 1) - 5*!!(a&8);
  182.         return 1;
  183.     }
  184.     return 0;
  185. }
  186.  
  187. static const ASSConvertFunc convert_func[] = {
  188.     [ASS_STR]       = convert_str,
  189.     [ASS_INT]       = convert_int,
  190.     [ASS_FLT]       = convert_flt,
  191.     [ASS_COLOR]     = convert_color,
  192.     [ASS_TIMESTAMP] = convert_timestamp,
  193.     [ASS_ALGN]      = convert_alignment,
  194. };
  195.  
  196.  
  197. struct ASSSplitContext {
  198.     ASS ass;
  199.     int current_section;
  200.     int field_number[FF_ARRAY_ELEMS(ass_sections)];
  201.     int *field_order[FF_ARRAY_ELEMS(ass_sections)];
  202. };
  203.  
  204.  
  205. static uint8_t *realloc_section_array(ASSSplitContext *ctx)
  206. {
  207.     const ASSSection *section = &ass_sections[ctx->current_section];
  208.     int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
  209.     void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset);
  210.     uint8_t *tmp = av_realloc_array(*section_ptr, (*count+1), section->size);
  211.     if (!tmp)
  212.         return NULL;
  213.     *section_ptr = tmp;
  214.     tmp += *count * section->size;
  215.     memset(tmp, 0, section->size);
  216.     (*count)++;
  217.     return tmp;
  218. }
  219.  
  220. static inline int is_eol(char buf)
  221. {
  222.     return buf == '\r' || buf == '\n' || buf == 0;
  223. }
  224.  
  225. static inline const char *skip_space(const char *buf)
  226. {
  227.     while (*buf == ' ')
  228.         buf++;
  229.     return buf;
  230. }
  231.  
  232. static int *get_default_field_orders(const ASSSection *section)
  233. {
  234.     int i;
  235.     int *order = av_malloc_array(FF_ARRAY_ELEMS(section->fields), sizeof(*order));
  236.  
  237.     if (!order)
  238.         return NULL;
  239.     for (i = 0; section->fields[i].name; i++)
  240.         order[i] = i;
  241.     while (i < FF_ARRAY_ELEMS(section->fields))
  242.         order[i] = -1;
  243.     return order;
  244. }
  245.  
  246. static const char *ass_split_section(ASSSplitContext *ctx, const char *buf)
  247. {
  248.     const ASSSection *section = &ass_sections[ctx->current_section];
  249.     int *number = &ctx->field_number[ctx->current_section];
  250.     int *order = ctx->field_order[ctx->current_section];
  251.     int *tmp, i, len;
  252.  
  253.     while (buf && *buf) {
  254.         if (buf[0] == '[') {
  255.             ctx->current_section = -1;
  256.             break;
  257.         }
  258.         if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':')) {
  259.             /* skip comments */
  260.         } else if (section->format_header && !order) {
  261.             len = strlen(section->format_header);
  262.             if (strncmp(buf, section->format_header, len) || buf[len] != ':')
  263.                 goto next_line;
  264.             buf += len + 1;
  265.             while (!is_eol(*buf)) {
  266.                 buf = skip_space(buf);
  267.                 len = strcspn(buf, ", \r\n");
  268.                 if (!(tmp = av_realloc_array(order, (*number + 1), sizeof(*order))))
  269.                     return NULL;
  270.                 order = tmp;
  271.                 order[*number] = -1;
  272.                 for (i=0; section->fields[i].name; i++)
  273.                     if (!strncmp(buf, section->fields[i].name, len)) {
  274.                         order[*number] = i;
  275.                         break;
  276.                     }
  277.                 (*number)++;
  278.                 buf = skip_space(buf + len + (buf[len] == ','));
  279.             }
  280.             ctx->field_order[ctx->current_section] = order;
  281.         } else if (section->fields_header) {
  282.             len = strlen(section->fields_header);
  283.             if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') {
  284.                 uint8_t *ptr, *struct_ptr = realloc_section_array(ctx);
  285.                 if (!struct_ptr)  return NULL;
  286.  
  287.                 /* No format header line found so far, assume default */
  288.                 if (!order) {
  289.                     order = get_default_field_orders(section);
  290.                     if (!order)
  291.                         return NULL;
  292.                     ctx->field_order[ctx->current_section] = order;
  293.                 }
  294.  
  295.                 buf += len + 1;
  296.                 for (i=0; !is_eol(*buf) && i < *number; i++) {
  297.                     int last = i == *number - 1;
  298.                     buf = skip_space(buf);
  299.                     len = strcspn(buf, last ? "\r\n" : ",\r\n");
  300.                     if (order[i] >= 0) {
  301.                         ASSFieldType type = section->fields[order[i]].type;
  302.                         ptr = struct_ptr + section->fields[order[i]].offset;
  303.                         convert_func[type](ptr, buf, len);
  304.                     }
  305.                     buf += len;
  306.                     if (!last && *buf) buf++;
  307.                     buf = skip_space(buf);
  308.                 }
  309.             }
  310.         } else {
  311.             len = strcspn(buf, ":\r\n");
  312.             if (buf[len] == ':') {
  313.                 for (i=0; section->fields[i].name; i++)
  314.                     if (!strncmp(buf, section->fields[i].name, len)) {
  315.                         ASSFieldType type = section->fields[i].type;
  316.                         uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
  317.                         ptr += section->fields[i].offset;
  318.                         buf = skip_space(buf + len + 1);
  319.                         convert_func[type](ptr, buf, strcspn(buf, "\r\n"));
  320.                         break;
  321.                     }
  322.             }
  323.         }
  324. next_line:
  325.         buf += strcspn(buf, "\n");
  326.         buf += !!*buf;
  327.     }
  328.     return buf;
  329. }
  330.  
  331. static int ass_split(ASSSplitContext *ctx, const char *buf)
  332. {
  333.     char c, section[16];
  334.     int i;
  335.  
  336.     if (ctx->current_section >= 0)
  337.         buf = ass_split_section(ctx, buf);
  338.  
  339.     while (buf && *buf) {
  340.         if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) {
  341.             buf += strcspn(buf, "\n");
  342.             buf += !!*buf;
  343.             for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
  344.                 if (!strcmp(section, ass_sections[i].section)) {
  345.                     ctx->current_section = i;
  346.                     buf = ass_split_section(ctx, buf);
  347.                 }
  348.         } else {
  349.             buf += strcspn(buf, "\n");
  350.             buf += !!*buf;
  351.         }
  352.     }
  353.     return buf ? 0 : AVERROR_INVALIDDATA;
  354. }
  355.  
  356. ASSSplitContext *ff_ass_split(const char *buf)
  357. {
  358.     ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
  359.     if (!ctx)
  360.         return NULL;
  361.     ctx->current_section = -1;
  362.     if (ass_split(ctx, buf) < 0) {
  363.         ff_ass_split_free(ctx);
  364.         return NULL;
  365.     }
  366.     return ctx;
  367. }
  368.  
  369. static void free_section(ASSSplitContext *ctx, const ASSSection *section)
  370. {
  371.     uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
  372.     int i, j, *count, c = 1;
  373.  
  374.     if (section->format_header) {
  375.         ptr   = *(void **)ptr;
  376.         count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
  377.     } else
  378.         count = &c;
  379.  
  380.     if (ptr)
  381.         for (i=0; i<*count; i++, ptr += section->size)
  382.             for (j=0; section->fields[j].name; j++) {
  383.                 const ASSFields *field = &section->fields[j];
  384.                 if (field->type == ASS_STR)
  385.                     av_freep(ptr + field->offset);
  386.             }
  387.     *count = 0;
  388.  
  389.     if (section->format_header)
  390.         av_freep((uint8_t *)&ctx->ass + section->offset);
  391. }
  392.  
  393. ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf,
  394.                                int cache, int *number)
  395. {
  396.     ASSDialog *dialog = NULL;
  397.     int i, count;
  398.     if (!cache)
  399.         for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
  400.             if (!strcmp(ass_sections[i].section, "Events")) {
  401.                 free_section(ctx, &ass_sections[i]);
  402.                 break;
  403.             }
  404.     count = ctx->ass.dialogs_count;
  405.     if (ass_split(ctx, buf) == 0)
  406.         dialog = ctx->ass.dialogs + count;
  407.     if (number)
  408.         *number = ctx->ass.dialogs_count - count;
  409.     return dialog;
  410. }
  411.  
  412. void ff_ass_split_free(ASSSplitContext *ctx)
  413. {
  414.     if (ctx) {
  415.         int i;
  416.         for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) {
  417.             free_section(ctx, &ass_sections[i]);
  418.             av_freep(&(ctx->field_order[i]));
  419.         }
  420.         av_free(ctx);
  421.     }
  422. }
  423.  
  424.  
  425. int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
  426.                                 const char *buf)
  427. {
  428.     const char *text = NULL;
  429.     char new_line[2];
  430.     int text_len = 0;
  431.  
  432.     while (buf && *buf) {
  433.         if (text && callbacks->text &&
  434.             (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
  435.              !strncmp(buf, "{\\", 2))) {
  436.             callbacks->text(priv, text, text_len);
  437.             text = NULL;
  438.         }
  439.         if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
  440.             if (callbacks->new_line)
  441.                 callbacks->new_line(priv, new_line[0] == 'N');
  442.             buf += 2;
  443.         } else if (!strncmp(buf, "{\\", 2)) {
  444.             buf++;
  445.             while (*buf == '\\') {
  446.                 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
  447.                 unsigned int color = 0xFFFFFFFF;
  448.                 int len, size = -1, an = -1, alpha = -1;
  449.                 int x1, y1, x2, y2, t1 = -1, t2 = -1;
  450.                 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
  451.                     int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
  452.                     len += close != -1;
  453.                     if (callbacks->style)
  454.                         callbacks->style(priv, style[0], close);
  455.                 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
  456.                            sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
  457.                            sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
  458.                            sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) {
  459.                     if (callbacks->color)
  460.                         callbacks->color(priv, color, c_num[0] - '0');
  461.                 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
  462.                            sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
  463.                            sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
  464.                            sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) {
  465.                     if (callbacks->alpha)
  466.                         callbacks->alpha(priv, alpha, c_num[0] - '0');
  467.                 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
  468.                            sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
  469.                     if (callbacks->font_name)
  470.                         callbacks->font_name(priv, tmp[0] ? tmp : NULL);
  471.                 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
  472.                            sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
  473.                     if (callbacks->font_size)
  474.                         callbacks->font_size(priv, size);
  475.                 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
  476.                            sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
  477.                            sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
  478.                            sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
  479.                     if (an != -1 && buf[2] != 'n')
  480.                         an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
  481.                     if (callbacks->alignment)
  482.                         callbacks->alignment(priv, an);
  483.                 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
  484.                            sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
  485.                     if (callbacks->cancel_overrides)
  486.                         callbacks->cancel_overrides(priv, tmp);
  487.                 } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 ||
  488.                            sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) {
  489.                     if (callbacks->move)
  490.                         callbacks->move(priv, x1, y1, x2, y2, t1, t2);
  491.                 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
  492.                     if (callbacks->move)
  493.                         callbacks->move(priv, x1, y1, x1, y1, -1, -1);
  494.                 } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
  495.                     if (callbacks->origin)
  496.                         callbacks->origin(priv, x1, y1);
  497.                 } else {
  498.                     len = strcspn(buf+1, "\\}") + 2;  /* skip unknown code */
  499.                 }
  500.                 buf += len - 1;
  501.             }
  502.             if (*buf++ != '}')
  503.                 return AVERROR_INVALIDDATA;
  504.         } else {
  505.             if (!text) {
  506.                 text = buf;
  507.                 text_len = 1;
  508.             } else
  509.                 text_len++;
  510.             buf++;
  511.         }
  512.     }
  513.     if (text && callbacks->text)
  514.         callbacks->text(priv, text, text_len);
  515.     if (callbacks->end)
  516.         callbacks->end(priv);
  517.     return 0;
  518. }
  519.  
  520. ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
  521. {
  522.     ASS *ass = &ctx->ass;
  523.     int i;
  524.  
  525.     if (!style || !*style)
  526.         style = "Default";
  527.     for (i=0; i<ass->styles_count; i++)
  528.         if (!strcmp(ass->styles[i].name, style))
  529.             return ass->styles + i;
  530.     return NULL;
  531. }
  532.