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