| 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 | 1713 | static int convert_str(void *dest, const char *buf, int len) | |
| 153 | { | ||
| 154 | 1713 | char *str = av_malloc(len + 1); | |
| 155 |
1/2✓ Branch 0 taken 1713 times.
✗ Branch 1 not taken.
|
1713 | if (str) { |
| 156 | 1713 | memcpy(str, buf, len); | |
| 157 | 1713 | str[len] = 0; | |
| 158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1713 times.
|
1713 | if (*(void **)dest) |
| 159 | ✗ | av_free(*(void **)dest); | |
| 160 | 1713 | *(char **)dest = str; | |
| 161 | } | ||
| 162 | 1713 | return !str; | |
| 163 | } | ||
| 164 | 2280 | static int convert_int(void *dest, const char *buf, int len) | |
| 165 | { | ||
| 166 | 2280 | return sscanf(buf, "%d", (int *)dest) == 1; | |
| 167 | } | ||
| 168 | 75 | static int convert_flt(void *dest, const char *buf, int len) | |
| 169 | { | ||
| 170 | 75 | return sscanf(buf, "%f", (float *)dest) == 1; | |
| 171 | } | ||
| 172 | 75 | static int convert_color(void *dest, const char *buf, int len) | |
| 173 | { | ||
| 174 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 60 times.
|
90 | return sscanf(buf, "&H%8x", (int *)dest) == 1 || |
| 175 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | 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 | 15 | static uint8_t *realloc_section_array(ASSSplitContext *ctx) | |
| 214 | { | ||
| 215 | 15 | const ASSSection *section = &ass_sections[ctx->current_section]; | |
| 216 | 15 | int *count = (int *)((uint8_t *)&ctx->ass + section->offset_count); | |
| 217 | 15 | void **section_ptr = (void **)((uint8_t *)&ctx->ass + section->offset); | |
| 218 | 15 | uint8_t *tmp = av_realloc_array(*section_ptr, (*count+1), section->size); | |
| 219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (!tmp) |
| 220 | ✗ | return NULL; | |
| 221 | 15 | *section_ptr = tmp; | |
| 222 | 15 | tmp += *count * section->size; | |
| 223 | 15 | memset(tmp, 0, section->size); | |
| 224 | 15 | (*count)++; | |
| 225 | 15 | return tmp; | |
| 226 | } | ||
| 227 | |||
| 228 | 885 | static inline int is_eol(char buf) | |
| 229 | { | ||
| 230 |
4/6✓ Branch 0 taken 885 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 840 times.
✓ Branch 3 taken 45 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 840 times.
|
885 | return buf == '\r' || buf == '\n' || buf == 0; |
| 231 | } | ||
| 232 | |||
| 233 | 5478 | static inline const char *skip_space(const char *buf) | |
| 234 | { | ||
| 235 |
2/2✓ Branch 0 taken 555 times.
✓ Branch 1 taken 5478 times.
|
6033 | while (*buf == ' ') |
| 236 | 555 | buf++; | |
| 237 | 5478 | 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 | 45 | static const char *ass_split_section(ASSSplitContext *ctx, const char *buf) | |
| 256 | { | ||
| 257 | 45 | const ASSSection *section = &ass_sections[ctx->current_section]; | |
| 258 | 45 | int *number = &ctx->field_number[ctx->current_section]; | |
| 259 | 45 | int *order = ctx->field_order[ctx->current_section]; | |
| 260 | int i, len; | ||
| 261 | |||
| 262 |
3/4✓ Branch 0 taken 210 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 195 times.
✓ Branch 3 taken 15 times.
|
210 | while (buf && *buf) { |
| 263 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 165 times.
|
195 | if (buf[0] == '[') { |
| 264 | 30 | ctx->current_section = -1; | |
| 265 | 30 | break; | |
| 266 | } | ||
| 267 |
3/6✓ Branch 0 taken 150 times.
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 150 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
165 | if (buf[0] == ';' || (buf[0] == '!' && buf[1] == ':')) |
| 268 | 15 | goto next_line; // skip comments | |
| 269 | |||
| 270 | 150 | len = strcspn(buf, ":\r\n"); | |
| 271 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 30 times.
|
150 | if (buf[len] == ':' && |
| 272 |
4/4✓ Branch 0 taken 45 times.
✓ Branch 1 taken 75 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 15 times.
|
120 | (!section->fields_header || strncmp(buf, section->fields_header, len))) { |
| 273 |
2/2✓ Branch 0 taken 420 times.
✓ Branch 1 taken 105 times.
|
525 | for (i = 0; i < FF_ARRAY_ELEMS(ass_sections); i++) { |
| 274 |
2/2✓ Branch 0 taken 315 times.
✓ Branch 1 taken 105 times.
|
420 | if (ass_sections[i].fields_header && |
| 275 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
|
315 | !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 60 times.
✓ Branch 1 taken 90 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 30 times.
|
150 | if (section->format_header && !order) { |
| 285 | 30 | len = strlen(section->format_header); | |
| 286 |
2/4✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
|
30 | if (!strncmp(buf, section->format_header, len) && buf[len] == ':') { |
| 287 | 30 | buf += len + 1; | |
| 288 |
2/2✓ Branch 1 taken 495 times.
✓ Branch 2 taken 30 times.
|
525 | while (!is_eol(*buf)) { |
| 289 | 495 | buf = skip_space(buf); | |
| 290 | 495 | len = strcspn(buf, ", \r\n"); | |
| 291 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 495 times.
|
495 | if (av_reallocp_array(&order, (*number + 1), sizeof(*order)) != 0) |
| 292 | ✗ | return NULL; | |
| 293 | |||
| 294 | 495 | order[*number] = -1; | |
| 295 |
1/2✓ Branch 0 taken 4800 times.
✗ Branch 1 not taken.
|
4800 | for (i=0; section->fields[i].name; i++) |
| 296 |
2/2✓ Branch 0 taken 495 times.
✓ Branch 1 taken 4305 times.
|
4800 | if (!strncmp(buf, section->fields[i].name, len)) { |
| 297 | 495 | order[*number] = i; | |
| 298 | 495 | break; | |
| 299 | } | ||
| 300 | 495 | (*number)++; | |
| 301 | 495 | buf = skip_space(buf + len + (buf[len] == ',')); | |
| 302 | } | ||
| 303 | 30 | ctx->field_order[ctx->current_section] = order; | |
| 304 | 30 | goto next_line; | |
| 305 | } | ||
| 306 | } | ||
| 307 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 90 times.
|
120 | if (section->fields_header) { |
| 308 | 30 | len = strlen(section->fields_header); | |
| 309 |
3/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
30 | if (!strncmp(buf, section->fields_header, len) && buf[len] == ':') { |
| 310 | 15 | uint8_t *ptr, *struct_ptr = realloc_section_array(ctx); | |
| 311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | 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 15 times.
|
15 | 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 | 15 | buf += len + 1; | |
| 322 |
3/4✓ Branch 1 taken 345 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 345 times.
✗ Branch 4 not taken.
|
360 | for (i=0; !is_eol(*buf) && i < *number; i++) { |
| 323 | 345 | int last = i == *number - 1; | |
| 324 | 345 | buf = skip_space(buf); | |
| 325 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 330 times.
|
345 | len = strcspn(buf, last ? "\r\n" : ",\r\n"); |
| 326 |
1/2✓ Branch 0 taken 345 times.
✗ Branch 1 not taken.
|
345 | if (order[i] >= 0) { |
| 327 | 345 | ASSFieldType type = section->fields[order[i]].type; | |
| 328 | 345 | ptr = struct_ptr + section->fields[order[i]].offset; | |
| 329 | 345 | convert_func[type](ptr, buf, len); | |
| 330 | } | ||
| 331 | 345 | buf += len; | |
| 332 |
3/4✓ Branch 0 taken 330 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 330 times.
✗ Branch 3 not taken.
|
345 | if (!last && *buf) buf++; |
| 333 | 345 | buf = skip_space(buf); | |
| 334 | } | ||
| 335 | } | ||
| 336 | } else { | ||
| 337 | 90 | len = strcspn(buf, ":\r\n"); | |
| 338 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 75 times.
|
90 | if (buf[len] == ':') { |
| 339 |
2/2✓ Branch 0 taken 270 times.
✓ Branch 1 taken 30 times.
|
300 | for (i=0; section->fields[i].name; i++) |
| 340 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 225 times.
|
270 | if (!strncmp(buf, section->fields[i].name, len)) { |
| 341 | 45 | ASSFieldType type = section->fields[i].type; | |
| 342 | 45 | uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset; | |
| 343 | 45 | ptr += section->fields[i].offset; | |
| 344 | 45 | buf = skip_space(buf + len + 1); | |
| 345 | 45 | convert_func[type](ptr, buf, strcspn(buf, "\r\n")); | |
| 346 | 45 | break; | |
| 347 | } | ||
| 348 | } | ||
| 349 | } | ||
| 350 | 45 | next_line: | |
| 351 | 165 | buf += strcspn(buf, "\n"); | |
| 352 | 165 | buf += !!*buf; | |
| 353 | } | ||
| 354 | 45 | return buf; | |
| 355 | } | ||
| 356 | |||
| 357 | 15 | 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 15 times.
|
15 | if (ctx->current_section >= 0) |
| 363 | ✗ | buf = ass_split_section(ctx, buf); | |
| 364 | |||
| 365 |
3/4✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
✓ Branch 3 taken 15 times.
|
60 | while (buf && *buf) { |
| 366 |
1/2✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
|
45 | if (sscanf(buf, "[%15[0-9A-Za-z+ ]]%c", section, &c) == 2) { |
| 367 | 45 | buf += strcspn(buf, "\n"); | |
| 368 | 45 | buf += !!*buf; | |
| 369 |
2/2✓ Branch 0 taken 180 times.
✓ Branch 1 taken 45 times.
|
225 | for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) |
| 370 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 135 times.
|
180 | if (!strcmp(section, ass_sections[i].section)) { |
| 371 | 45 | ctx->current_section = i; | |
| 372 | 45 | 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 15 times.
✗ Branch 1 not taken.
|
15 | return buf ? 0 : AVERROR_INVALIDDATA; |
| 380 | } | ||
| 381 | |||
| 382 | 15 | av_cold ASSSplitContext *ff_ass_split(const char *buf) | |
| 383 | { | ||
| 384 | 15 | ASSSplitContext *ctx = av_mallocz(sizeof(*ctx)); | |
| 385 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if (!ctx) |
| 386 | ✗ | return NULL; | |
| 387 |
2/4✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
|
15 | if (buf && !strncmp(buf, "\xef\xbb\xbf", 3)) // Skip UTF-8 BOM header |
| 388 | ✗ | buf += 3; | |
| 389 | 15 | ctx->current_section = -1; | |
| 390 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
|
15 | if (ass_split(ctx, buf) < 0) { |
| 391 | ✗ | ff_ass_split_free(ctx); | |
| 392 | ✗ | return NULL; | |
| 393 | } | ||
| 394 | 15 | return ctx; | |
| 395 | } | ||
| 396 | |||
| 397 | 60 | static void free_section(ASSSplitContext *ctx, const ASSSection *section) | |
| 398 | { | ||
| 399 | 60 | uint8_t *ptr = (uint8_t *)&ctx->ass + section->offset; | |
| 400 | 60 | int i, j, *count, c = 1; | |
| 401 | |||
| 402 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 15 times.
|
60 | if (section->format_header) { |
| 403 | 45 | ptr = *(void **)ptr; | |
| 404 | 45 | count = (int *)((uint8_t *)&ctx->ass + section->offset_count); | |
| 405 | } else | ||
| 406 | 15 | count = &c; | |
| 407 | |||
| 408 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
60 | if (ptr) |
| 409 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 30 times.
|
60 | for (i=0; i<*count; i++, ptr += section->size) |
| 410 |
2/2✓ Branch 0 taken 420 times.
✓ Branch 1 taken 30 times.
|
450 | for (j=0; section->fields[j].name; j++) { |
| 411 | 420 | const ASSFields *field = §ion->fields[j]; | |
| 412 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 360 times.
|
420 | if (field->type == ASS_STR) |
| 413 | 60 | av_freep(ptr + field->offset); | |
| 414 | } | ||
| 415 | 60 | *count = 0; | |
| 416 | |||
| 417 |
2/2✓ Branch 0 taken 45 times.
✓ Branch 1 taken 15 times.
|
60 | if (section->format_header) |
| 418 | 45 | av_freep((uint8_t *)&ctx->ass + section->offset); | |
| 419 | 60 | } | |
| 420 | |||
| 421 | 417 | void ff_ass_free_dialog(ASSDialog **dialogp) | |
| 422 | { | ||
| 423 | 417 | ASSDialog *dialog = *dialogp; | |
| 424 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 417 times.
|
417 | if (!dialog) |
| 425 | ✗ | return; | |
| 426 | 417 | av_freep(&dialog->style); | |
| 427 | 417 | av_freep(&dialog->name); | |
| 428 | 417 | av_freep(&dialog->effect); | |
| 429 | 417 | av_freep(&dialog->text); | |
| 430 | 417 | av_freep(dialogp); | |
| 431 | } | ||
| 432 | |||
| 433 | 417 | 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 | 417 | ASSDialog *dialog = av_mallocz(sizeof(*dialog)); | |
| 449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 417 times.
|
417 | if (!dialog) |
| 450 | ✗ | return NULL; | |
| 451 | |||
| 452 |
2/2✓ Branch 0 taken 3753 times.
✓ Branch 1 taken 417 times.
|
4170 | for (i = 0; i < FF_ARRAY_ELEMS(fields); i++) { |
| 453 | size_t len; | ||
| 454 | 3753 | const int last = i == FF_ARRAY_ELEMS(fields) - 1; | |
| 455 | 3753 | const ASSFieldType type = fields[i].type; | |
| 456 | 3753 | uint8_t *ptr = (uint8_t *)dialog + fields[i].offset; | |
| 457 | 3753 | buf = skip_space(buf); | |
| 458 |
2/2✓ Branch 0 taken 417 times.
✓ Branch 1 taken 3336 times.
|
3753 | len = last ? strlen(buf) : strcspn(buf, ","); |
| 459 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3753 times.
|
3753 | if (len >= INT_MAX) { |
| 460 | ✗ | ff_ass_free_dialog(&dialog); | |
| 461 | ✗ | return NULL; | |
| 462 | } | ||
| 463 | 3753 | convert_func[type](ptr, buf, len); | |
| 464 | 3753 | buf += len; | |
| 465 |
2/2✓ Branch 0 taken 3336 times.
✓ Branch 1 taken 417 times.
|
3753 | if (*buf) buf++; |
| 466 | } | ||
| 467 | 417 | return dialog; | |
| 468 | } | ||
| 469 | |||
| 470 | 15 | av_cold void ff_ass_split_free(ASSSplitContext *ctx) | |
| 471 | { | ||
| 472 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if (ctx) { |
| 473 | int i; | ||
| 474 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 15 times.
|
75 | for (i=0; i<FF_ARRAY_ELEMS(ass_sections); i++) { |
| 475 | 60 | free_section(ctx, &ass_sections[i]); | |
| 476 | 60 | av_freep(&(ctx->field_order[i])); | |
| 477 | } | ||
| 478 | 15 | av_free(ctx); | |
| 479 | } | ||
| 480 | 15 | } | |
| 481 | |||
| 482 | |||
| 483 | 417 | int ff_ass_split_override_codes(const ASSCodesCallbacks *callbacks, void *priv, | |
| 484 | const char *buf) | ||
| 485 | { | ||
| 486 | 417 | const char *text = NULL; | |
| 487 | char new_line[2]; | ||
| 488 | 417 | int text_len = 0; | |
| 489 | |||
| 490 |
3/4✓ Branch 0 taken 28760 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28343 times.
✓ Branch 3 taken 417 times.
|
28760 | while (buf && *buf) { |
| 491 |
3/4✓ Branch 0 taken 26883 times.
✓ Branch 1 taken 1460 times.
✓ Branch 2 taken 26883 times.
✗ Branch 3 not taken.
|
28343 | if (text && callbacks->text && |
| 492 |
2/2✓ Branch 0 taken 26548 times.
✓ Branch 1 taken 335 times.
|
26883 | (sscanf(buf, "\\%1[nN]", new_line) == 1 || |
| 493 |
2/2✓ Branch 0 taken 216 times.
✓ Branch 1 taken 26332 times.
|
26548 | !strncmp(buf, "{\\", 2))) { |
| 494 | 551 | callbacks->text(priv, text, text_len); | |
| 495 | 551 | text = NULL; | |
| 496 | } | ||
| 497 |
2/2✓ Branch 0 taken 468 times.
✓ Branch 1 taken 27875 times.
|
28343 | if (sscanf(buf, "\\%1[nN]", new_line) == 1) { |
| 498 |
1/2✓ Branch 0 taken 468 times.
✗ Branch 1 not taken.
|
468 | if (callbacks->new_line) |
| 499 | 468 | callbacks->new_line(priv, new_line[0] == 'N'); | |
| 500 | 468 | buf += 2; | |
| 501 |
2/2✓ Branch 0 taken 630 times.
✓ Branch 1 taken 27245 times.
|
27875 | } else if (!strncmp(buf, "{\\", 2)) { |
| 502 | 630 | buf++; | |
| 503 |
2/2✓ Branch 0 taken 630 times.
✓ Branch 1 taken 630 times.
|
1260 | while (*buf == '\\') { |
| 504 | 630 | char style[2], c[2], sep[2], c_num[2] = "0", tmp[128] = {0}; | |
| 505 | 630 | unsigned int color = 0xFFFFFFFF; | |
| 506 | 630 | int len, size = -1, an = -1, alpha = -1; | |
| 507 | 630 | int x1, y1, x2, y2, t1 = -1, t2 = -1; | |
| 508 |
2/2✓ Branch 0 taken 230 times.
✓ Branch 1 taken 400 times.
|
630 | if (sscanf(buf, "\\%1[bisu]%1[01\\}]%n", style, c, &len) > 1) { |
| 509 |
3/4✓ Branch 0 taken 118 times.
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 118 times.
✗ Branch 3 not taken.
|
230 | int close = c[0] == '0' ? 1 : c[0] == '1' ? 0 : -1; |
| 510 | 230 | len += close != -1; | |
| 511 |
2/2✓ Branch 0 taken 62 times.
✓ Branch 1 taken 168 times.
|
230 | if (callbacks->style) |
| 512 | 62 | callbacks->style(priv, style[0], close); | |
| 513 |
2/2✓ Branch 0 taken 351 times.
✓ Branch 1 taken 49 times.
|
400 | } else if (sscanf(buf, "\\c%1[\\}]%n", sep, &len) > 0 || |
| 514 |
2/2✓ Branch 0 taken 288 times.
✓ Branch 1 taken 63 times.
|
351 | sscanf(buf, "\\c&H%X&%1[\\}]%n", &color, sep, &len) > 1 || |
| 515 |
1/2✓ Branch 0 taken 288 times.
✗ Branch 1 not taken.
|
288 | sscanf(buf, "\\%1[1234]c%1[\\}]%n", c_num, sep, &len) > 1 || |
| 516 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 288 times.
|
288 | 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 288 times.
✗ Branch 1 not taken.
|
288 | } else if (sscanf(buf, "\\alpha%1[\\}]%n", sep, &len) > 0 || |
| 520 |
1/2✓ Branch 0 taken 288 times.
✗ Branch 1 not taken.
|
288 | sscanf(buf, "\\alpha&H%2X&%1[\\}]%n", &alpha, sep, &len) > 1 || |
| 521 |
1/2✓ Branch 0 taken 288 times.
✗ Branch 1 not taken.
|
288 | sscanf(buf, "\\%1[1234]a%1[\\}]%n", c_num, sep, &len) > 1 || |
| 522 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 288 times.
|
288 | 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 281 times.
✓ Branch 1 taken 7 times.
|
288 | } else if (sscanf(buf, "\\fn%1[\\}]%n", sep, &len) > 0 || |
| 526 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 274 times.
|
281 | 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 225 times.
✓ Branch 1 taken 49 times.
|
274 | } else if (sscanf(buf, "\\fs%1[\\}]%n", sep, &len) > 0 || |
| 530 |
2/2✓ Branch 0 taken 63 times.
✓ Branch 1 taken 162 times.
|
225 | 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 162 times.
✗ Branch 1 not taken.
|
162 | } else if (sscanf(buf, "\\a%1[\\}]%n", sep, &len) > 0 || |
| 534 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | sscanf(buf, "\\a%2u%1[\\}]%n", &an, sep, &len) > 1 || |
| 535 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | sscanf(buf, "\\an%1[\\}]%n", sep, &len) > 0 || |
| 536 |
2/2✓ Branch 0 taken 123 times.
✓ Branch 1 taken 39 times.
|
162 | sscanf(buf, "\\an%1u%1[\\}]%n", &an, sep, &len) > 1) { |
| 537 |
2/4✓ Branch 0 taken 123 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
|
123 | 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 91 times.
|
123 | if (callbacks->alignment) |
| 540 | 32 | callbacks->alignment(priv, an); | |
| 541 |
1/2✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
|
39 | } else if (sscanf(buf, "\\r%1[\\}]%n", sep, &len) > 0 || |
| 542 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39 | 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 39 times.
✗ Branch 1 not taken.
|
39 | } 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 39 times.
|
39 | 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 39 times.
✗ Branch 1 not taken.
|
39 | } 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 7 times.
|
39 | 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 | 630 | buf += len - 1; | |
| 559 | } | ||
| 560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 630 times.
|
630 | if (*buf++ != '}') |
| 561 | ✗ | return AVERROR_INVALIDDATA; | |
| 562 | } else { | ||
| 563 |
2/2✓ Branch 0 taken 913 times.
✓ Branch 1 taken 26332 times.
|
27245 | if (!text) { |
| 564 | 913 | text = buf; | |
| 565 | 913 | text_len = 1; | |
| 566 | } else | ||
| 567 | 26332 | text_len++; | |
| 568 | 27245 | buf++; | |
| 569 | } | ||
| 570 | } | ||
| 571 |
3/4✓ Branch 0 taken 362 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 362 times.
✗ Branch 3 not taken.
|
417 | if (text && callbacks->text) |
| 572 | 362 | callbacks->text(priv, text, text_len); | |
| 573 |
2/2✓ Branch 0 taken 195 times.
✓ Branch 1 taken 222 times.
|
417 | if (callbacks->end) |
| 574 | 195 | callbacks->end(priv); | |
| 575 | 417 | return 0; | |
| 576 | } | ||
| 577 | |||
| 578 | 196 | ASSStyle *ff_ass_style_get(ASSSplitContext *ctx, const char *style) | |
| 579 | { | ||
| 580 | 196 | ASS *ass = &ctx->ass; | |
| 581 | int i; | ||
| 582 | |||
| 583 |
2/4✓ Branch 0 taken 196 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 196 times.
|
196 | if (!style || !*style) |
| 584 | ✗ | style = "Default"; | |
| 585 |
1/2✓ Branch 0 taken 196 times.
✗ Branch 1 not taken.
|
196 | for (i=0; i<ass->styles_count; i++) |
| 586 |
2/4✓ Branch 0 taken 196 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 196 times.
✗ Branch 3 not taken.
|
196 | if (ass->styles[i].name && !strcmp(ass->styles[i].name, style)) |
| 587 | 196 | return ass->styles + i; | |
| 588 | ✗ | return NULL; | |
| 589 | } | ||
| 590 |