FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/ass_split.c
Date: 2026-09-28 04:46:15
Exec Total Coverage
Lines: 240 297 80.8%
Functions: 16 19 84.2%
Branches: 186 278 66.9%

Line Branch Exec 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 1751 static int convert_str(void *dest, const char *buf, int len)
153 {
154 1751 char *str = av_malloc(len + 1);
155
1/2
✓ Branch 0 taken 1751 times.
✗ Branch 1 not taken.
1751 if (str) {
156 1751 memcpy(str, buf, len);
157 1751 str[len] = 0;
158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1751 times.
1751 if (*(void **)dest)
159 ✗ av_free(*(void **)dest);
160 1751 *(char **)dest = str;
161 }
162 1751 return !str;
163 }
164 2342 static int convert_int(void *dest, const char *buf, int len)
165 {
166 2342 return sscanf(buf, "%d", (int *)dest) == 1;
167 }
168 85 static int convert_flt(void *dest, const char *buf, int len)
169 {
170 85 return sscanf(buf, "%f", (float *)dest) == 1;
171 }
172 85 static int convert_color(void *dest, const char *buf, int len)
173 {
174
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 68 times.
102 return sscanf(buf, "&H%8x", (int *)dest) == 1 ||
175
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 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 18 static uint8_t *realloc_section_array(ASSSplitContext *ctx)
214 {
215 18 const ASSSection *section = &ass_sections[ctx->current_section];
216 18 int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
217 18 void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset);
218 18 uint8_t *tmp = av_realloc_array(*section_ptr, (*count+1), section->size);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (!tmp)
220 ✗ return NULL;
221 18 *section_ptr = tmp;
222 18 tmp += *count * section->size;
223 18 memset(tmp, 0, section->size);
224 18 (*count)++;
225 18 return tmp;
226 }
227
228 1011 static inline int is_eol(char buf)
229 {
230
4/6
✓ Branch 0 taken 1011 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 958 times.
✓ Branch 3 taken 53 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 958 times.
1011 return buf == '\r' || buf == '\n' || buf == 0;
231 }
232
233 5785 static inline const char *skip_space(const char *buf)
234 {
235
2/2
✓ Branch 0 taken 635 times.
✓ Branch 1 taken 5785 times.
6420 while (*buf == ' ')
236 635 buf++;
237 5785 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 54 static const char *ass_split_section(ASSSplitContext *ctx, const char *buf)
256 {
257 54 const ASSSection *section = &ass_sections[ctx->current_section];
258 54 int *number = &ctx->field_number[ctx->current_section];
259 54 int *order = ctx->field_order[ctx->current_section];
260 int i, len;
261
262
3/4
✓ Branch 0 taken 239 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 220 times.
✓ Branch 3 taken 19 times.
239 while (buf && *buf) {
263
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 185 times.
220 if (buf[0] == '[') {
264 35 ctx->current_section = -1;
265 35 break;
266 }
267
3/6
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 170 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
185 if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':'))
268 15 goto next_line; // skip comments
269
270 170 len = strcspn(buf, ":\r\n");
271
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 34 times.
170 if (buf[len] == ':' &&
272
4/4
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 83 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 18 times.
136 (!section->fields_header || strncmp(buf, section->fields_header, len))) {
273
2/2
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 118 times.
590 for (i = 0; i < FF_ARRAY_ELEMS(ass_sections); i++) {
274
2/2
✓ Branch 0 taken 354 times.
✓ Branch 1 taken 118 times.
472 if (ass_sections[i].fields_header &&
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 354 times.
354 !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 70 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 35 times.
170 if (section->format_header && !order) {
285 35 len = strlen(section->format_header);
286
2/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 if (!strncmp(buf, section->format_header, len) && buf[len] == ':') {
287 35 buf += len + 1;
288
2/2
✓ Branch 1 taken 564 times.
✓ Branch 2 taken 35 times.
599 while (!is_eol(*buf)) {
289 564 buf = skip_space(buf);
290 564 len = strcspn(buf, ", \r\n");
291
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 564 times.
564 if (av_reallocp_array(&order, (*number + 1), sizeof(*order)) != 0)
292 ✗ return NULL;
293
294 564 order[*number] = -1;
295
1/2
✓ Branch 0 taken 5446 times.
✗ Branch 1 not taken.
5446 for (i=0; section->fields[i].name; i++)
296
2/2
✓ Branch 0 taken 564 times.
✓ Branch 1 taken 4882 times.
5446 if (!strncmp(buf, section->fields[i].name, len)) {
297 564 order[*number] = i;
298 564 break;
299 }
300 564 (*number)++;
301 564 buf = skip_space(buf + len + (buf[len] == ','));
302 }
303 35 ctx->field_order[ctx->current_section] = order;
304 35 goto next_line;
305 }
306 }
307
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 100 times.
135 if (section->fields_header) {
308 35 len = strlen(section->fields_header);
309
3/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
35 if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') {
310 18 uint8_t *ptr, *struct_ptr = realloc_section_array(ctx);
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 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 18 times.
18 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 18 buf += len + 1;
322
3/4
✓ Branch 1 taken 394 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 394 times.
✗ Branch 4 not taken.
412 for (i=0; !is_eol(*buf) && i < *number; i++) {
323 394 int last = i == *number - 1;
324 394 buf = skip_space(buf);
325
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 376 times.
394 len = strcspn(buf, last ? "\r\n" : ",\r\n");
326
1/2
✓ Branch 0 taken 394 times.
✗ Branch 1 not taken.
394 if (order[i] >= 0) {
327 394 ASSFieldType type = section->fields[order[i]].type;
328 394 ptr = struct_ptr + section->fields[order[i]].offset;
329 394 convert_func[type](ptr, buf, len);
330 }
331 394 buf += len;
332
3/4
✓ Branch 0 taken 376 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 376 times.
✗ Branch 3 not taken.
394 if (!last && *buf) buf++;
333 394 buf = skip_space(buf);
334 }
335 }
336 } else {
337 100 len = strcspn(buf, ":\r\n");
338
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 83 times.
100 if (buf[len] == ':') {
339
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 30 times.
318 for (i=0; section->fields[i].name; i++)
340
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 235 times.
288 if (!strncmp(buf, section->fields[i].name, len)) {
341 53 ASSFieldType type = section->fields[i].type;
342 53 uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
343 53 ptr += section->fields[i].offset;
344 53 buf = skip_space(buf + len + 1);
345 53 convert_func[type](ptr, buf, strcspn(buf, "\r\n"));
346 53 break;
347 }
348 }
349 }
350 47 next_line:
351 185 buf += strcspn(buf, "\n");
352 185 buf += !!*buf;
353 }
354 54 return buf;
355 }
356
357 19 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 19 times.
19 if (ctx->current_section >= 0)
363 ✗ buf = ass_split_section(ctx, buf);
364
365
3/4
✓ Branch 0 taken 73 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 19 times.
73 while (buf && *buf) {
366
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) {
367 54 buf += strcspn(buf, "\n");
368 54 buf += !!*buf;
369
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 54 times.
270 for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++)
370
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 162 times.
216 if (!strcmp(section, ass_sections[i].section)) {
371 54 ctx->current_section = i;
372 54 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 19 times.
✗ Branch 1 not taken.
19 return buf ? 0 : AVERROR_INVALIDDATA;
380 }
381
382 19 av_cold ASSSplitContext *ff_ass_split(const char *buf)
383 {
384 19 ASSSplitContext *ctx = av_mallocz(sizeof(*ctx));
385
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (!ctx)
386 ✗ return NULL;
387
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 19 times.
19 if (buf && !strncmp(buf, "\xef\xbb\xbf", 3)) // Skip UTF-8 BOM header
388 ✗ buf += 3;
389 19 ctx->current_section = -1;
390
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 if (ass_split(ctx, buf) < 0) {
391 ✗ ff_ass_split_free(ctx);
392 ✗ return NULL;
393 }
394 19 return ctx;
395 }
396
397 76 static void free_section(ASSSplitContext *ctx, const ASSSection *section)
398 {
399 76 uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset;
400 76 int i, j, *count, c = 1;
401
402
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 19 times.
76 if (section->format_header) {
403 57 ptr = *(void **)ptr;
404 57 count = (int *)((uint8_t *)&ctx->ass + section->offset_count);
405 } else
406 19 count = &c;
407
408
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 39 times.
76 if (ptr)
409
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 37 times.
74 for (i=0; i<*count; i++, ptr += section->size)
410
2/2
✓ Branch 0 taken 509 times.
✓ Branch 1 taken 37 times.
546 for (j=0; section->fields[j].name; j++) {
411 509 const ASSFields *field = &section->fields[j];
412
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 435 times.
509 if (field->type == ASS_STR)
413 74 av_freep(ptr + field->offset);
414 }
415 76 *count = 0;
416
417
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 19 times.
76 if (section->format_header)
418 57 av_freep((uint8_t *)&ctx->ass + section->offset);
419 76 }
420
421 424 void ff_ass_free_dialog(ASSDialog **dialogp)
422 {
423 424 ASSDialog *dialog = *dialogp;
424
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 424 times.
424 if (!dialog)
425 ✗ return;
426 424 av_freep(&dialog->style);
427 424 av_freep(&dialog->name);
428 424 av_freep(&dialog->effect);
429 424 av_freep(&dialog->text);
430 424 av_freep(dialogp);
431 }
432
433 424 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 424 ASSDialog *dialog = av_mallocz(sizeof(*dialog));
449
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 424 times.
424 if (!dialog)
450 ✗ return NULL;
451
452
2/2
✓ Branch 0 taken 3816 times.
✓ Branch 1 taken 424 times.
4240 for (i = 0; i < FF_ARRAY_ELEMS(fields); i++) {
453 size_t len;
454 3816 const int last = i == FF_ARRAY_ELEMS(fields) - 1;
455 3816 const ASSFieldType type = fields[i].type;
456 3816 uint8_t *ptr = (uint8_t *)dialog + fields[i].offset;
457 3816 buf = skip_space(buf);
458
2/2
✓ Branch 0 taken 424 times.
✓ Branch 1 taken 3392 times.
3816 len = last ? strlen(buf) : strcspn(buf, ",");
459
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3816 times.
3816 if (len >= INT_MAX) {
460 ✗ ff_ass_free_dialog(&dialog);
461 ✗ return NULL;
462 }
463 3816 convert_func[type](ptr, buf, len);
464 3816 buf += len;
465
2/2
✓ Branch 0 taken 3392 times.
✓ Branch 1 taken 424 times.
3816 if (*buf) buf++;
466 }
467 424 return dialog;
468 }
469
470 19 av_cold void ff_ass_split_free(ASSSplitContext *ctx)
471 {
472
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if (ctx) {
473 int i;
474
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 19 times.
95 for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) {
475 76 free_section(ctx, &ass_sections[i]);
476 76 av_freep(&(ctx->field_order[i]));
477 }
478 19 av_free(ctx);
479 }
480 19 }
481
482
483 424 int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv,
484 const char *buf)
485 {
486 424 const char *text = NULL;
487 char new_line[2];
488 424 int text_len = 0;
489
490
3/4
✓ Branch 0 taken 28992 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28568 times.
✓ Branch 3 taken 424 times.
28992 while (buf && *buf) {
491
3/4
✓ Branch 0 taken 27065 times.
✓ Branch 1 taken 1503 times.
✓ Branch 2 taken 27065 times.
✗ Branch 3 not taken.
28568 if (text && callbacks->text &&
492
2/2
✓ Branch 0 taken 26727 times.
✓ Branch 1 taken 338 times.
27065 (sscanf(buf, "\\%1[nN]", new_line) == 1 ||
493
2/2
✓ Branch 0 taken 230 times.
✓ Branch 1 taken 26497 times.
26727 !strncmp(buf, "{\\", 2))) {
494 568 callbacks->text(priv, text, text_len);
495 568 text = NULL;
496 }
497
2/2
✓ Branch 0 taken 471 times.
✓ Branch 1 taken 28097 times.
28568 if (sscanf(buf, "\\%1[nN]", new_line) == 1) {
498
1/2
✓ Branch 0 taken 471 times.
✗ Branch 1 not taken.
471 if (callbacks->new_line)
499 471 callbacks->new_line(priv, new_line[0] == 'N');
500 471 buf += 2;
501
2/2
✓ Branch 0 taken 666 times.
✓ Branch 1 taken 27431 times.
28097 } else if (!strncmp(buf, "{\\", 2)) {
502 666 buf++;
503
2/2
✓ Branch 0 taken 666 times.
✓ Branch 1 taken 666 times.
1332 while (*buf == '\\') {
504 666 char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0};
505 666 unsigned int color = 0xFFFFFFFF, dur = 0;
506 666 int len, size = -1, an = -1, alpha = -1;
507 666 int x1, y1, x2, y2, t1 = -1, t2 = -1;
508
2/2
✓ Branch 0 taken 242 times.
✓ Branch 1 taken 424 times.
666 if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) {
509
3/4
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 118 times.
✓ Branch 2 taken 124 times.
✗ Branch 3 not taken.
242 int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1;
510 242 len += close != -1;
511
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 168 times.
242 if (callbacks->style)
512 74 callbacks->style(priv, style[0], close);
513
2/2
✓ Branch 0 taken 375 times.
✓ Branch 1 taken 49 times.
424 } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 ||
514
2/2
✓ Branch 0 taken 312 times.
✓ Branch 1 taken 63 times.
375 sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 ||
515
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 ||
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 312 times.
312 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 112 times.
112 if (callbacks->color)
518 ✗ callbacks->color(priv, color, c_num[0] - '0');
519
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 ||
520
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 ||
521
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 ||
522
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 312 times.
312 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 305 times.
✓ Branch 1 taken 7 times.
312 } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 ||
526
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 298 times.
305 sscanf(buf, "\\fn%127[^\\}]%1[\\}]%n", tmp, sep, &len) > 1) {
527
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (callbacks->font_name)
528 ✗ callbacks->font_name(priv, tmp[0] ? tmp : NULL);
529
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 49 times.
298 } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 ||
530
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 186 times.
249 sscanf(buf, "\\fs%u%1[\\}]%n", &size, sep, &len) > 1) {
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 112 times.
112 if (callbacks->font_size)
532 ✗ callbacks->font_size(priv, size);
533
1/2
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
186 } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 ||
534
1/2
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
186 sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 ||
535
1/2
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
186 sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 ||
536
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 59 times.
186 sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) {
537
2/4
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 127 times.
127 if (an != -1 && buf[2] != 'n')
538 ✗ an = (an&3) + (an&4 ? 6 : an&8 ? 3 : 0);
539
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 91 times.
127 if (callbacks->alignment)
540 36 callbacks->alignment(priv, an);
541
1/2
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
59 } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 ||
542
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 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 59 times.
✗ Branch 1 not taken.
59 } 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 59 times.
59 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
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 16 times.
59 } else if (sscanf(buf, "\\pos(%d,%d)%1[\\}]%n", &x1, &y1, sep, &len) > 2) {
550
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 7 times.
43 if (callbacks->move)
551 36 callbacks->move(priv, x1, y1, x1, y1, -1, -1);
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 } 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
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 13 times.
16 } else if (sscanf(buf, "\\kf%u%1[\\}]%n", &dur, sep, &len) > 1 ||
556
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 sscanf(buf, "\\ko%u%1[\\}]%n", &dur, sep, &len) > 1 ||
557
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 sscanf(buf, "\\%1[kK]%u%1[\\}]%n", c, &dur, sep, &len) > 2) {
558
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16 if (callbacks->karaoke)
559 16 callbacks->karaoke(priv, dur);
560 } else {
561 ✗ len = strcspn(buf+1, "\\}") + 2; /* skip unknown code */
562 }
563 666 buf += len - 1;
564 }
565
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 666 times.
666 if (*buf++ != '}')
566 ✗ return AVERROR_INVALIDDATA;
567 } else {
568
2/2
✓ Branch 0 taken 934 times.
✓ Branch 1 taken 26497 times.
27431 if (!text) {
569 934 text = buf;
570 934 text_len = 1;
571 } else
572 26497 text_len++;
573 27431 buf++;
574 }
575 }
576
3/4
✓ Branch 0 taken 366 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 366 times.
✗ Branch 3 not taken.
424 if (text && callbacks->text)
577 366 callbacks->text(priv, text, text_len);
578
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 222 times.
424 if (callbacks->end)
579 202 callbacks->end(priv);
580 424 return 0;
581 }
582
583 205 ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style)
584 {
585 205 ASS *ass = &ctx->ass;
586 int i;
587
588
2/4
✓ Branch 0 taken 205 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 205 times.
205 if (!style || !*style)
589 ✗ style = "Default";
590
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 1 times.
205 for (i=0; i<ass->styles_count; i++)
591
2/4
✓ Branch 0 taken 204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 204 times.
✗ Branch 3 not taken.
204 if (ass->styles[i].name && !strcmp(ass->styles[i].name, style))
592 204 return ass->styles + i;
593 1 return NULL;
594 }
595