Line | Branch | Exec | Source |
---|---|---|---|
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 <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 | 1061 | static int convert_str(void *dest, const char *buf, int len) | |
153 | { | ||
154 | 1061 | char *str = av_malloc(len + 1); | |
155 |
1/2✓ Branch 0 taken 1061 times.
✗ Branch 1 not taken.
|
1061 | if (str) { |
156 | 1061 | memcpy(str, buf, len); | |
157 | 1061 | str[len] = 0; | |
158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1061 times.
|
1061 | if (*(void **)dest) |
159 | ✗ | av_free(*(void **)dest); | |
160 | 1061 | *(char **)dest = str; | |
161 | } | ||
162 | 1061 | return !str; | |
163 | } | ||
164 | 1428 | static int convert_int(void *dest, const char *buf, int len) | |
165 | { | ||
166 | 1428 | return sscanf(buf, "%d", (int *)dest) == 1; | |
167 | } | ||
168 | 55 | static int convert_flt(void *dest, const char *buf, int len) | |
169 | { | ||
170 | 55 | return sscanf(buf, "%f", (float *)dest) == 1; | |
171 | } | ||
172 | 55 | static int convert_color(void *dest, const char *buf, int len) | |
173 | { | ||
174 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 44 times.
|
66 | return sscanf(buf, "&H%8x", (int *)dest) == 1 || |
175 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | sscanf(buf, "%d", (int *)dest) == 1; |
176 | } | ||
177 | ✗ | static int convert_timestamp(void *dest, const char *buf, int len) | |
178 | { | ||
179 | int c, h, m, s, cs; | ||
180 | ✗ | if ((c = sscanf(buf, "%d:%02d:%02d.%02d", &h, &m, &s, &cs)) == 4) | |
181 | ✗ | *(int *)dest = 360000*h + 6000*m + 100*s + cs; | |
182 | ✗ | return c == 4; | |
183 | } | ||
184 | ✗ | static int convert_alignment(void *dest, const char *buf, int len) | |
185 | { | ||
186 | int a; | ||
187 | ✗ | if (sscanf(buf, "%d", &a) == 1) { | |
188 | /* convert V4 Style alignment to V4+ Style */ | ||
189 | ✗ | *(int *)dest = a + ((a&4) >> 1) - 5*!!(a&8); | |
190 | ✗ | return 1; | |
191 | } | ||
192 | ✗ | return 0; | |
193 | } | ||
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 | 11 | static uint8_t *realloc_section_array(ASSSplitContext *ctx) | |
214 | { | ||
215 | 11 | const ASSSection *section = &ass_sections[ctx->current_section]; | |
216 | 11 | int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count); | |
217 | 11 | void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset); | |
218 | 11 | uint8_t *tmp = av_realloc_array(*section_ptr, (*count+1), section->size); | |
219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!tmp) |
220 | ✗ | return NULL; | |
221 | 11 | *section_ptr = tmp; | |
222 | 11 | tmp += *count * section->size; | |
223 | 11 | memset(tmp, 0, section->size); | |
224 | 11 | (*count)++; | |
225 | 11 | return tmp; | |
226 | } | ||
227 | |||
228 | 649 | static inline int is_eol(char buf) | |
229 | { | ||
230 |
4/6✓ Branch 0 taken 649 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 616 times.
✓ Branch 3 taken 33 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 616 times.
|
649 | return buf == '\r' || buf == '\n' || buf == 0; |
231 | } | ||
232 | |||
233 | 3578 | static inline const char *skip_space(const char *buf) | |
234 | { | ||
235 |
2/2✓ Branch 0 taken 407 times.
✓ Branch 1 taken 3578 times.
|
3985 | while (*buf == ' ') |
236 | 407 | buf++; | |
237 | 3578 | return buf; | |
238 | } | ||
239 | |||
240 | ✗ | static int *get_default_field_orders(const ASSSection *section, int *number) | |
241 | { | ||
242 | int i; | ||
243 | ✗ | int *order = av_malloc_array(FF_ARRAY_ELEMS(section->fields), sizeof(*order)); | |
244 | |||
245 | ✗ | if (!order) | |
246 | ✗ | return NULL; | |
247 | ✗ | for (i = 0; section->fields[i].name; i++) | |
248 | ✗ | order[i] = i; | |
249 | ✗ | *number = i; | |
250 | ✗ | while (i < FF_ARRAY_ELEMS(section->fields)) | |
251 | ✗ | order[i++] = -1; | |
252 | ✗ | return order; | |
253 | } | ||
254 | |||
255 | 33 | static const char *ass_split_section(ASSSplitContext *ctx, const char *buf) | |
256 | { | ||
257 | 33 | const ASSSection *section = &ass_sections[ctx->current_section]; | |
258 | 33 | int *number = &ctx->field_number[ctx->current_section]; | |
259 | 33 | int *order = ctx->field_order[ctx->current_section]; | |
260 | int i, len; | ||
261 | |||
262 |
3/4✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 143 times.
✓ Branch 3 taken 11 times.
|
154 | while (buf && *buf) { |
263 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 121 times.
|
143 | if (buf[0] == '[') { |
264 | 22 | ctx->current_section = -1; | |
265 | 22 | break; | |
266 | } | ||
267 |
3/6✓ Branch 0 taken 110 times.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 110 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
121 | if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':')) |
268 | 11 | goto next_line; // skip comments | |
269 | |||
270 | 110 | len = strcspn(buf, ":\r\n"); | |
271 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 22 times.
|
110 | if (buf[len] == ':' && |
272 |
4/4✓ Branch 0 taken 33 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 11 times.
|
88 | (!section->fields_header || strncmp(buf, section->fields_header, len))) { |
273 |
2/2✓ Branch 0 taken 308 times.
✓ Branch 1 taken 77 times.
|
385 | for (i = 0; i < FF_ARRAY_ELEMS(ass_sections); i++) { |
274 |
2/2✓ Branch 0 taken 231 times.
✓ Branch 1 taken 77 times.
|
308 | if (ass_sections[i].fields_header && |
275 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 231 times.
|
231 | !strncmp(buf, ass_sections[i].fields_header, len)) { |
276 | ✗ | ctx->current_section = i; | |
277 | ✗ | section = &ass_sections[ctx->current_section]; | |
278 | ✗ | number = &ctx->field_number[ctx->current_section]; | |
279 | ✗ | order = ctx->field_order[ctx->current_section]; | |
280 | ✗ | break; | |
281 | } | ||
282 | } | ||
283 | } | ||
284 |
4/4✓ Branch 0 taken 44 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 22 times.
|
110 | if (section->format_header && !order) { |
285 | 22 | len = strlen(section->format_header); | |
286 |
2/4✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
|
22 | if (!strncmp(buf, section->format_header, len) && buf[len] == ':') { |
287 | 22 | buf += len + 1; | |
288 |
2/2✓ Branch 1 taken 363 times.
✓ Branch 2 taken 22 times.
|
385 | while (!is_eol(*buf)) { |
289 | 363 | buf = skip_space(buf); | |
290 | 363 | len = strcspn(buf, ", \r\n"); | |
291 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 363 times.
|
363 | if (av_reallocp_array(&order, (*number + 1), sizeof(*order)) != 0) |
292 | ✗ | return NULL; | |
293 | |||
294 | 363 | order[*number] = -1; | |
295 |
1/2✓ Branch 0 taken 3520 times.
✗ Branch 1 not taken.
|
3520 | for (i=0; section->fields[i].name; i++) |
296 |
2/2✓ Branch 0 taken 363 times.
✓ Branch 1 taken 3157 times.
|
3520 | if (!strncmp(buf, section->fields[i].name, len)) { |
297 | 363 | order[*number] = i; | |
298 | 363 | break; | |
299 | } | ||
300 | 363 | (*number)++; | |
301 | 363 | buf = skip_space(buf + len + (buf[len] == ',')); | |
302 | } | ||
303 | 22 | ctx->field_order[ctx->current_section] = order; | |
304 | 22 | goto next_line; | |
305 | } | ||
306 | } | ||
307 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 66 times.
|
88 | if (section->fields_header) { |
308 | 22 | len = strlen(section->fields_header); | |
309 |
3/4✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
22 | if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') { |
310 | 11 | uint8_t *ptr, *struct_ptr = realloc_section_array(ctx); | |
311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!struct_ptr) return NULL; |
312 | |||
313 | /* No format header line found so far, assume default */ | ||
314 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!order) { |
315 | ✗ | order = get_default_field_orders(section, number); | |
316 | ✗ | if (!order) | |
317 | ✗ | return NULL; | |
318 | ✗ | ctx->field_order[ctx->current_section] = order; | |
319 | } | ||
320 | |||
321 | 11 | buf += len + 1; | |
322 |
3/4✓ Branch 1 taken 253 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 253 times.
✗ Branch 4 not taken.
|
264 | for (i=0; !is_eol(*buf) && i < *number; i++) { |
323 | 253 | int last = i == *number - 1; | |
324 | 253 | buf = skip_space(buf); | |
325 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 242 times.
|
253 | len = strcspn(buf, last ? "\r\n" : ",\r\n"); |
326 |
1/2✓ Branch 0 taken 253 times.
✗ Branch 1 not taken.
|
253 | if (order[i] >= 0) { |
327 | 253 | ASSFieldType type = section->fields[order[i]].type; | |
328 | 253 | ptr = struct_ptr + section->fields[order[i]].offset; | |
329 | 253 | convert_func[type](ptr, buf, len); | |
330 | } | ||
331 | 253 | buf += len; | |
332 |
3/4✓ Branch 0 taken 242 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 242 times.
✗ Branch 3 not taken.
|
253 | if (!last && *buf) buf++; |
333 | 253 | buf = skip_space(buf); | |
334 | } | ||
335 | } | ||
336 | } else { | ||
337 | 66 | len = strcspn(buf, ":\r\n"); | |
338 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 55 times.
|
66 | if (buf[len] == ':') { |
339 |
2/2✓ Branch 0 taken 198 times.
✓ Branch 1 taken 22 times.
|
220 | for (i=0; section->fields[i].name; i++) |
340 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 165 times.
|
198 | if (!strncmp(buf, section->fields[i].name, len)) { |
341 | 33 | ASSFieldType type = section->fields[i].type; | |
342 | 33 | uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset; | |
343 | 33 | ptr += section->fields[i].offset; | |
344 | 33 | buf = skip_space(buf + len + 1); | |
345 | 33 | convert_func[type](ptr, buf, strcspn(buf, "\r\n")); | |
346 | 33 | break; | |
347 | } | ||
348 | } | ||
349 | } | ||
350 | 33 | next_line: | |
351 | 121 | buf += strcspn(buf, "\n"); | |
352 | 121 | buf += !!*buf; | |
353 | } | ||
354 | 33 | return buf; | |
355 | } | ||
356 | |||
357 | 11 | static int ass_split(ASSSplitContext *ctx, const char *buf) | |
358 | { | ||
359 | char c, section[16]; | ||
360 | int i; | ||
361 | |||
362 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (ctx->current_section >= 0) |
363 | ✗ | buf = ass_split_section(ctx, buf); | |
364 | |||
365 |
3/4✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 11 times.
|
44 | while (buf && *buf) { |
366 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) { |
367 | 33 | buf += strcspn(buf, "\n"); | |
368 | 33 | buf += !!*buf; | |
369 |
2/2✓ Branch 0 taken 132 times.
✓ Branch 1 taken 33 times.
|
165 | for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) |
370 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 99 times.
|
132 | if (!strcmp(section, ass_sections[i].section)) { |
371 | 33 | ctx->current_section = i; | |
372 | 33 | buf = ass_split_section(ctx, buf); | |
373 | } | ||
374 | } else { | ||
375 | ✗ | buf += strcspn(buf, "\n"); | |
376 | ✗ | buf += !!*buf; | |
377 | } | ||
378 | } | ||
379 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | return buf ? 0 : AVERROR_INVALIDDATA; |
380 | } | ||
381 | |||
382 | 11 | ASSSplitContext *ff_ass_split(const char *buf) | |
383 | { | ||
384 | 11 | ASSSplitContext *ctx = av_mallocz(sizeof(*ctx)); | |
385 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if (!ctx) |
386 | ✗ | return NULL; | |
387 |
2/4✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
|
11 | if (buf && !strncmp(buf, "\xef\xbb\xbf", 3)) // Skip UTF-8 BOM header |
388 | ✗ | buf += 3; | |
389 | 11 | ctx->current_section = -1; | |
390 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
|
11 | if (ass_split(ctx, buf) < 0) { |
391 | ✗ | ff_ass_split_free(ctx); | |
392 | ✗ | return NULL; | |
393 | } | ||
394 | 11 | return ctx; | |
395 | } | ||
396 | |||
397 | 44 | static void free_section(ASSSplitContext *ctx, const ASSSection *section) | |
398 | { | ||
399 | 44 | uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset; | |
400 | 44 | int i, j, *count, c = 1; | |
401 | |||
402 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 11 times.
|
44 | if (section->format_header) { |
403 | 33 | ptr = *(void **)ptr; | |
404 | 33 | count = (int *)((uint8_t *)&ctx->ass + section->offset_count); | |
405 | } else | ||
406 | 11 | count = &c; | |
407 | |||
408 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
|
44 | if (ptr) |
409 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
|
44 | for (i=0; i<*count; i++, ptr += section->size) |
410 |
2/2✓ Branch 0 taken 308 times.
✓ Branch 1 taken 22 times.
|
330 | for (j=0; section->fields[j].name; j++) { |
411 | 308 | const ASSFields *field = §ion->fields[j]; | |
412 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 264 times.
|
308 | if (field->type == ASS_STR) |
413 | 44 | av_freep(ptr + field->offset); | |
414 | } | ||
415 | 44 | *count = 0; | |
416 | |||
417 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 11 times.
|
44 | if (section->format_header) |
418 | 33 | av_freep((uint8_t *)&ctx->ass + section->offset); | |
419 | 44 | } | |
420 | |||
421 | 257 | void ff_ass_free_dialog(ASSDialog **dialogp) | |
422 | { | ||
423 | 257 | ASSDialog *dialog = *dialogp; | |
424 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 257 times.
|
257 | if (!dialog) |
425 | ✗ | return; | |
426 | 257 | av_freep(&dialog->style); | |
427 | 257 | av_freep(&dialog->name); | |
428 | 257 | av_freep(&dialog->effect); | |
429 | 257 | av_freep(&dialog->text); | |
430 | 257 | av_freep(dialogp); | |
431 | } | ||
432 | |||
433 | 257 | ASSDialog *ff_ass_split_dialog(ASSSplitContext *ctx, const char *buf) | |
434 | { | ||
435 | int i; | ||
436 | static const ASSFields fields[] = { | ||
437 | {"ReadOrder", ASS_INT, offsetof(ASSDialog, readorder)}, | ||
438 | {"Layer", ASS_INT, offsetof(ASSDialog, layer) }, | ||
439 | {"Style", ASS_STR, offsetof(ASSDialog, style) }, | ||
440 | {"Name", ASS_STR, offsetof(ASSDialog, name) }, | ||
441 | {"MarginL", ASS_INT, offsetof(ASSDialog, margin_l) }, | ||
442 | {"MarginR", ASS_INT, offsetof(ASSDialog, margin_r) }, | ||
443 | {"MarginV", ASS_INT, offsetof(ASSDialog, margin_v) }, | ||
444 | {"Effect", ASS_STR, offsetof(ASSDialog, effect) }, | ||
445 | {"Text", ASS_STR, offsetof(ASSDialog, text) }, | ||
446 | }; | ||
447 | |||
448 | 257 | ASSDialog *dialog = av_mallocz(sizeof(*dialog)); | |
449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 257 times.
|
257 | if (!dialog) |
450 | ✗ | return NULL; | |
451 | |||
452 |
2/2✓ Branch 0 taken 2313 times.
✓ Branch 1 taken 257 times.
|
2570 | for (i = 0; i < FF_ARRAY_ELEMS(fields); i++) { |
453 | size_t len; | ||
454 | 2313 | const int last = i == FF_ARRAY_ELEMS(fields) - 1; | |
455 | 2313 | const ASSFieldType type = fields[i].type; | |
456 | 2313 | uint8_t *ptr = (uint8_t *)dialog + fields[i].offset; | |
457 | 2313 | buf = skip_space(buf); | |
458 |
2/2✓ Branch 0 taken 257 times.
✓ Branch 1 taken 2056 times.
|
2313 | len = last ? strlen(buf) : strcspn(buf, ","); |
459 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2313 times.
|
2313 | if (len >= INT_MAX) { |
460 | ✗ | ff_ass_free_dialog(&dialog); | |
461 | ✗ | return NULL; | |
462 | } | ||
463 | 2313 | convert_func[type](ptr, buf, len); | |
464 | 2313 | buf += len; | |
465 |
2/2✓ Branch 0 taken 2056 times.
✓ Branch 1 taken 257 times.
|
2313 | if (*buf) buf++; |
466 | } | ||
467 | 257 | return dialog; | |
468 | } | ||
469 | |||
470 | 11 | void ff_ass_split_free(ASSSplitContext *ctx) | |
471 | { | ||
472 |
1/2✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
|
11 | if (ctx) { |
473 | int i; | ||
474 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 11 times.
|
55 | for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) { |
475 | 44 | free_section(ctx, &ass_sections[i]); | |
476 | 44 | av_freep(&(ctx->field_order[i])); | |
477 | } | ||
478 | 11 | av_free(ctx); | |
479 | } | ||
480 | 11 | } | |
481 | |||
482 | |||
483 | 257 | int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, | |
484 | const char *buf) | ||
485 | { | ||
486 | 257 | const char *text = NULL; | |
487 | char new_line[2]; | ||
488 | 257 | int text_len = 0; | |
489 | |||
490 |
3/4✓ Branch 0 taken 19198 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18941 times.
✓ Branch 3 taken 257 times.
|
19198 | while (buf && *buf) { |
491 |
3/4✓ Branch 0 taken 17905 times.
✓ Branch 1 taken 1036 times.
✓ Branch 2 taken 17905 times.
✗ Branch 3 not taken.
|
18941 | if (text && callbacks->text && |
492 |
2/2✓ Branch 0 taken 17662 times.
✓ Branch 1 taken 243 times.
|
17905 | (sscanf(buf, "\\%1[nN]", new_line) == 1 || |
493 |
2/2✓ Branch 0 taken 164 times.
✓ Branch 1 taken 17498 times.
|
17662 | !strncmp(buf, "{\\", 2))) { |
494 | 407 | callbacks->text(priv, text, text_len); | |
495 | 407 | text = NULL; | |
496 | } | ||
497 |
2/2✓ Branch 0 taken 338 times.
✓ Branch 1 taken 18603 times.
|
18941 | if (sscanf(buf, "\\%1[nN]", new_line) == 1) { |
498 |
1/2✓ Branch 0 taken 338 times.
✗ Branch 1 not taken.
|
338 | if (callbacks->new_line) |
499 | 338 | callbacks->new_line(priv, new_line[0] == 'N'); | |
500 | 338 | buf += 2; | |
501 |
2/2✓ Branch 0 taken 478 times.
✓ Branch 1 taken 18125 times.
|
18603 | } else if (!strncmp(buf, "{\\", 2)) { |
502 | 478 | buf++; | |
503 |
2/2✓ Branch 0 taken 478 times.
✓ Branch 1 taken 478 times.
|
956 | while (*buf == '\\') { |
504 | 478 | char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0}; | |
505 | 478 | unsigned int color = 0xFFFFFFFF; | |
506 | 478 | int len, size = -1, an = -1, alpha = -1; | |
507 | 478 | int x1, y1, x2, y2, t1 = -1, t2 = -1; | |
508 |
2/2✓ Branch 0 taken 174 times.
✓ Branch 1 taken 304 times.
|
478 | if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) { |
509 |
3/4✓ Branch 0 taken 90 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 90 times.
✗ Branch 3 not taken.
|
174 | int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1; |
510 | 174 | len += close != -1; | |
511 |
2/2✓ Branch 0 taken 62 times.
✓ Branch 1 taken 112 times.
|
174 | if (callbacks->style) |
512 | 62 | callbacks->style(priv, style[0], close); | |
513 |
2/2✓ Branch 0 taken 269 times.
✓ Branch 1 taken 35 times.
|
304 | } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 || |
514 |
2/2✓ Branch 0 taken 224 times.
✓ Branch 1 taken 45 times.
|
269 | sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 || |
515 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 || |
516 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
|
224 | sscanf(buf, "\\%1[1234]c&H%X&%1[\\}]%n", c_num, &color, sep, &len) > 2) { |
517 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (callbacks->color) |
518 | ✗ | callbacks->color(priv, color, c_num[0] - '0'); | |
519 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 || |
520 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 || |
521 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 || |
522 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
|
224 | sscanf(buf, "\\%1[1234]a&H%2X&%1[\\}]%n", c_num, &alpha, sep, &len) > 2) { |
523 | ✗ | if (callbacks->alpha) | |
524 | ✗ | callbacks->alpha(priv, alpha, c_num[0] - '0'); | |
525 |
2/2✓ Branch 0 taken 219 times.
✓ Branch 1 taken 5 times.
|
224 | } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 || |
526 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 214 times.
|
219 | sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) { |
527 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if (callbacks->font_name) |
528 | ✗ | callbacks->font_name(priv, tmp[0] ? tmp : NULL); | |
529 |
2/2✓ Branch 0 taken 179 times.
✓ Branch 1 taken 35 times.
|
214 | } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 || |
530 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 134 times.
|
179 | sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) { |
531 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 80 times.
|
80 | if (callbacks->font_size) |
532 | ✗ | callbacks->font_size(priv, size); | |
533 |
1/2✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
|
134 | } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 || |
534 |
1/2✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
|
134 | sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 || |
535 |
1/2✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
|
134 | sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 || |
536 |
2/2✓ Branch 0 taken 97 times.
✓ Branch 1 taken 37 times.
|
134 | sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) { |
537 |
2/4✓ Branch 0 taken 97 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 97 times.
|
97 | if (an != -1 && buf[2] != 'n') |
538 | ✗ | an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0); | |
539 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 65 times.
|
97 | if (callbacks->alignment) |
540 | 32 | callbacks->alignment(priv, an); | |
541 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 || |
542 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
|
37 | sscanf(buf, "\\r%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) { |
543 | ✗ | if (callbacks->cancel_overrides) | |
544 | ✗ | callbacks->cancel_overrides(priv, tmp); | |
545 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | } else if (sscanf(buf, "\\move(%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, sep, &len) > 4 || |
546 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 37 times.
|
37 | sscanf(buf, "\\move(%d,%d,%d,%d,%d,%d)%1[\\}]%n", &x1, &y1, &x2, &y2, &t1, &t2, sep, &len) > 6) { |
547 | ✗ | if (callbacks->move) | |
548 | ✗ | callbacks->move(priv, x1, y1, x2, y2, t1, t2); | |
549 |
1/2✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
|
37 | } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { |
550 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 5 times.
|
37 | if (callbacks->move) |
551 | 32 | callbacks->move(priv, x1, y1, x1, y1, -1, -1); | |
552 | ✗ | } else if (sscanf(buf, "\\org(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) { | |
553 | ✗ | if (callbacks->origin) | |
554 | ✗ | callbacks->origin(priv, x1, y1); | |
555 | } else { | ||
556 | ✗ | len = strcspn(buf+1, "\\}") + 2; /* skip unknown code */ | |
557 | } | ||
558 | 478 | buf += len - 1; | |
559 | } | ||
560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 478 times.
|
478 | if (*buf++ != '}') |
561 | ✗ | return AVERROR_INVALIDDATA; | |
562 | } else { | ||
563 |
2/2✓ Branch 0 taken 627 times.
✓ Branch 1 taken 17498 times.
|
18125 | if (!text) { |
564 | 627 | text = buf; | |
565 | 627 | text_len = 1; | |
566 | } else | ||
567 | 17498 | text_len++; | |
568 | 18125 | buf++; | |
569 | } | ||
570 | } | ||
571 |
3/4✓ Branch 0 taken 220 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 220 times.
✗ Branch 3 not taken.
|
257 | if (text && callbacks->text) |
572 | 220 | callbacks->text(priv, text, text_len); | |
573 |
2/2✓ Branch 0 taken 109 times.
✓ Branch 1 taken 148 times.
|
257 | if (callbacks->end) |
574 | 109 | callbacks->end(priv); | |
575 | 257 | return 0; | |
576 | } | ||
577 | |||
578 | 110 | ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style) | |
579 | { | ||
580 | 110 | ASS *ass = &ctx->ass; | |
581 | int i; | ||
582 | |||
583 |
2/4✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 110 times.
|
110 | if (!style || !*style) |
584 | ✗ | style = "Default"; | |
585 |
1/2✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
|
110 | for (i=0; i<ass->styles_count; i++) |
586 |
2/4✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 110 times.
✗ Branch 3 not taken.
|
110 | if (ass->styles[i].name && !strcmp(ass->styles[i].name, style)) |
587 | 110 | return ass->styles + i; | |
588 | ✗ | return NULL; | |
589 | } | ||
590 |