| 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 = §ion->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 |