ass_split.c 23 KB

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