| 1 | /* |
| 2 | * This file is part of FFmpeg. |
| 3 | * |
| 4 | * FFmpeg is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Lesser General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2.1 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * FFmpeg is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Lesser General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Lesser General Public |
| 15 | * License along with FFmpeg; if not, write to the Free Software |
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | |
| 19 | /** |
| 20 | * @file |
| 21 | * misc parsing utilities |
| 22 | */ |
| 23 | |
| 24 | #include <time.h> |
| 25 | |
| 26 | #include "avstring.h" |
| 27 | #include "avutil.h" |
| 28 | #include "common.h" |
| 29 | #include "eval.h" |
| 30 | #include "log.h" |
| 31 | #include "random_seed.h" |
| 32 | #include "time_internal.h" |
| 33 | #include "parseutils.h" |
| 34 | |
| 35 | #ifdef TEST |
| 36 | |
| 37 | #define av_get_random_seed av_get_random_seed_deterministic |
| 38 | static uint32_t av_get_random_seed_deterministic(void); |
| 39 | |
| 40 | #define time(t) 1331972053 |
| 41 | |
| 42 | #endif |
| 43 | |
| 44 | int av_parse_ratio(AVRational *q, const char *str, int max, |
| 45 | int log_offset, void *log_ctx) |
| 46 | { |
| 47 | char c; |
| 48 | int ret; |
| 49 | |
| 50 | if (sscanf(str, "%d:%d%c", &q->num, &q->den, &c) != 2) { |
| 51 | double d; |
| 52 | ret = av_expr_parse_and_eval(&d, str, NULL, NULL, |
| 53 | NULL, NULL, NULL, NULL, |
| 54 | NULL, log_offset, log_ctx); |
| 55 | if (ret < 0) |
| 56 | return ret; |
| 57 | *q = av_d2q(d, max); |
| 58 | } else { |
| 59 | av_reduce(&q->num, &q->den, q->num, q->den, max); |
| 60 | } |
| 61 | |
| 62 | return 0; |
| 63 | } |
| 64 | |
| 65 | typedef struct { |
| 66 | const char *abbr; |
| 67 | int width, height; |
| 68 | } VideoSizeAbbr; |
| 69 | |
| 70 | typedef struct { |
| 71 | const char *abbr; |
| 72 | AVRational rate; |
| 73 | } VideoRateAbbr; |
| 74 | |
| 75 | static const VideoSizeAbbr video_size_abbrs[] = { |
| 76 | { "ntsc", 720, 480 }, |
| 77 | { "pal", 720, 576 }, |
| 78 | { "qntsc", 352, 240 }, /* VCD compliant NTSC */ |
| 79 | { "qpal", 352, 288 }, /* VCD compliant PAL */ |
| 80 | { "sntsc", 640, 480 }, /* square pixel NTSC */ |
| 81 | { "spal", 768, 576 }, /* square pixel PAL */ |
| 82 | { "film", 352, 240 }, |
| 83 | { "ntsc-film", 352, 240 }, |
| 84 | { "sqcif", 128, 96 }, |
| 85 | { "qcif", 176, 144 }, |
| 86 | { "cif", 352, 288 }, |
| 87 | { "4cif", 704, 576 }, |
| 88 | { "16cif", 1408,1152 }, |
| 89 | { "qqvga", 160, 120 }, |
| 90 | { "qvga", 320, 240 }, |
| 91 | { "vga", 640, 480 }, |
| 92 | { "svga", 800, 600 }, |
| 93 | { "xga", 1024, 768 }, |
| 94 | { "uxga", 1600,1200 }, |
| 95 | { "qxga", 2048,1536 }, |
| 96 | { "sxga", 1280,1024 }, |
| 97 | { "qsxga", 2560,2048 }, |
| 98 | { "hsxga", 5120,4096 }, |
| 99 | { "wvga", 852, 480 }, |
| 100 | { "wxga", 1366, 768 }, |
| 101 | { "wsxga", 1600,1024 }, |
| 102 | { "wuxga", 1920,1200 }, |
| 103 | { "woxga", 2560,1600 }, |
| 104 | { "wqsxga", 3200,2048 }, |
| 105 | { "wquxga", 3840,2400 }, |
| 106 | { "whsxga", 6400,4096 }, |
| 107 | { "whuxga", 7680,4800 }, |
| 108 | { "cga", 320, 200 }, |
| 109 | { "ega", 640, 350 }, |
| 110 | { "hd480", 852, 480 }, |
| 111 | { "hd720", 1280, 720 }, |
| 112 | { "hd1080", 1920,1080 }, |
| 113 | { "2k", 2048,1080 }, /* Digital Cinema System Specification */ |
| 114 | { "2kflat", 1998,1080 }, |
| 115 | { "2kscope", 2048, 858 }, |
| 116 | { "4k", 4096,2160 }, /* Digital Cinema System Specification */ |
| 117 | { "4kflat", 3996,2160 }, |
| 118 | { "4kscope", 4096,1716 }, |
| 119 | { "nhd", 640,360 }, |
| 120 | { "hqvga", 240,160 }, |
| 121 | { "wqvga", 400,240 }, |
| 122 | { "fwqvga", 432,240 }, |
| 123 | { "hvga", 480,320 }, |
| 124 | { "qhd", 960,540 }, |
| 125 | }; |
| 126 | |
| 127 | static const VideoRateAbbr video_rate_abbrs[]= { |
| 128 | { "ntsc", { 30000, 1001 } }, |
| 129 | { "pal", { 25, 1 } }, |
| 130 | { "qntsc", { 30000, 1001 } }, /* VCD compliant NTSC */ |
| 131 | { "qpal", { 25, 1 } }, /* VCD compliant PAL */ |
| 132 | { "sntsc", { 30000, 1001 } }, /* square pixel NTSC */ |
| 133 | { "spal", { 25, 1 } }, /* square pixel PAL */ |
| 134 | { "film", { 24, 1 } }, |
| 135 | { "ntsc-film", { 24000, 1001 } }, |
| 136 | }; |
| 137 | |
| 138 | int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str) |
| 139 | { |
| 140 | int i; |
| 141 | int n = FF_ARRAY_ELEMS(video_size_abbrs); |
| 142 | const char *p; |
| 143 | int width = 0, height = 0; |
| 144 | |
| 145 | for (i = 0; i < n; i++) { |
| 146 | if (!strcmp(video_size_abbrs[i].abbr, str)) { |
| 147 | width = video_size_abbrs[i].width; |
| 148 | height = video_size_abbrs[i].height; |
| 149 | break; |
| 150 | } |
| 151 | } |
| 152 | if (i == n) { |
| 153 | width = strtol(str, (void*)&p, 10); |
| 154 | if (*p) |
| 155 | p++; |
| 156 | height = strtol(p, (void*)&p, 10); |
| 157 | |
| 158 | /* trailing extraneous data detected, like in 123x345foobar */ |
| 159 | if (*p) |
| 160 | return AVERROR(EINVAL); |
| 161 | } |
| 162 | if (width <= 0 || height <= 0) |
| 163 | return AVERROR(EINVAL); |
| 164 | *width_ptr = width; |
| 165 | *height_ptr = height; |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | int av_parse_video_rate(AVRational *rate, const char *arg) |
| 170 | { |
| 171 | int i, ret; |
| 172 | int n = FF_ARRAY_ELEMS(video_rate_abbrs); |
| 173 | |
| 174 | /* First, we check our abbreviation table */ |
| 175 | for (i = 0; i < n; ++i) |
| 176 | if (!strcmp(video_rate_abbrs[i].abbr, arg)) { |
| 177 | *rate = video_rate_abbrs[i].rate; |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | /* Then, we try to parse it as fraction */ |
| 182 | if ((ret = av_parse_ratio_quiet(rate, arg, 1001000)) < 0) |
| 183 | return ret; |
| 184 | if (rate->num <= 0 || rate->den <= 0) |
| 185 | return AVERROR(EINVAL); |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | typedef struct { |
| 190 | const char *name; ///< a string representing the name of the color |
| 191 | uint8_t rgb_color[3]; ///< RGB values for the color |
| 192 | } ColorEntry; |
| 193 | |
| 194 | static const ColorEntry color_table[] = { |
| 195 | { "AliceBlue", { 0xF0, 0xF8, 0xFF } }, |
| 196 | { "AntiqueWhite", { 0xFA, 0xEB, 0xD7 } }, |
| 197 | { "Aqua", { 0x00, 0xFF, 0xFF } }, |
| 198 | { "Aquamarine", { 0x7F, 0xFF, 0xD4 } }, |
| 199 | { "Azure", { 0xF0, 0xFF, 0xFF } }, |
| 200 | { "Beige", { 0xF5, 0xF5, 0xDC } }, |
| 201 | { "Bisque", { 0xFF, 0xE4, 0xC4 } }, |
| 202 | { "Black", { 0x00, 0x00, 0x00 } }, |
| 203 | { "BlanchedAlmond", { 0xFF, 0xEB, 0xCD } }, |
| 204 | { "Blue", { 0x00, 0x00, 0xFF } }, |
| 205 | { "BlueViolet", { 0x8A, 0x2B, 0xE2 } }, |
| 206 | { "Brown", { 0xA5, 0x2A, 0x2A } }, |
| 207 | { "BurlyWood", { 0xDE, 0xB8, 0x87 } }, |
| 208 | { "CadetBlue", { 0x5F, 0x9E, 0xA0 } }, |
| 209 | { "Chartreuse", { 0x7F, 0xFF, 0x00 } }, |
| 210 | { "Chocolate", { 0xD2, 0x69, 0x1E } }, |
| 211 | { "Coral", { 0xFF, 0x7F, 0x50 } }, |
| 212 | { "CornflowerBlue", { 0x64, 0x95, 0xED } }, |
| 213 | { "Cornsilk", { 0xFF, 0xF8, 0xDC } }, |
| 214 | { "Crimson", { 0xDC, 0x14, 0x3C } }, |
| 215 | { "Cyan", { 0x00, 0xFF, 0xFF } }, |
| 216 | { "DarkBlue", { 0x00, 0x00, 0x8B } }, |
| 217 | { "DarkCyan", { 0x00, 0x8B, 0x8B } }, |
| 218 | { "DarkGoldenRod", { 0xB8, 0x86, 0x0B } }, |
| 219 | { "DarkGray", { 0xA9, 0xA9, 0xA9 } }, |
| 220 | { "DarkGreen", { 0x00, 0x64, 0x00 } }, |
| 221 | { "DarkKhaki", { 0xBD, 0xB7, 0x6B } }, |
| 222 | { "DarkMagenta", { 0x8B, 0x00, 0x8B } }, |
| 223 | { "DarkOliveGreen", { 0x55, 0x6B, 0x2F } }, |
| 224 | { "Darkorange", { 0xFF, 0x8C, 0x00 } }, |
| 225 | { "DarkOrchid", { 0x99, 0x32, 0xCC } }, |
| 226 | { "DarkRed", { 0x8B, 0x00, 0x00 } }, |
| 227 | { "DarkSalmon", { 0xE9, 0x96, 0x7A } }, |
| 228 | { "DarkSeaGreen", { 0x8F, 0xBC, 0x8F } }, |
| 229 | { "DarkSlateBlue", { 0x48, 0x3D, 0x8B } }, |
| 230 | { "DarkSlateGray", { 0x2F, 0x4F, 0x4F } }, |
| 231 | { "DarkTurquoise", { 0x00, 0xCE, 0xD1 } }, |
| 232 | { "DarkViolet", { 0x94, 0x00, 0xD3 } }, |
| 233 | { "DeepPink", { 0xFF, 0x14, 0x93 } }, |
| 234 | { "DeepSkyBlue", { 0x00, 0xBF, 0xFF } }, |
| 235 | { "DimGray", { 0x69, 0x69, 0x69 } }, |
| 236 | { "DodgerBlue", { 0x1E, 0x90, 0xFF } }, |
| 237 | { "FireBrick", { 0xB2, 0x22, 0x22 } }, |
| 238 | { "FloralWhite", { 0xFF, 0xFA, 0xF0 } }, |
| 239 | { "ForestGreen", { 0x22, 0x8B, 0x22 } }, |
| 240 | { "Fuchsia", { 0xFF, 0x00, 0xFF } }, |
| 241 | { "Gainsboro", { 0xDC, 0xDC, 0xDC } }, |
| 242 | { "GhostWhite", { 0xF8, 0xF8, 0xFF } }, |
| 243 | { "Gold", { 0xFF, 0xD7, 0x00 } }, |
| 244 | { "GoldenRod", { 0xDA, 0xA5, 0x20 } }, |
| 245 | { "Gray", { 0x80, 0x80, 0x80 } }, |
| 246 | { "Green", { 0x00, 0x80, 0x00 } }, |
| 247 | { "GreenYellow", { 0xAD, 0xFF, 0x2F } }, |
| 248 | { "HoneyDew", { 0xF0, 0xFF, 0xF0 } }, |
| 249 | { "HotPink", { 0xFF, 0x69, 0xB4 } }, |
| 250 | { "IndianRed", { 0xCD, 0x5C, 0x5C } }, |
| 251 | { "Indigo", { 0x4B, 0x00, 0x82 } }, |
| 252 | { "Ivory", { 0xFF, 0xFF, 0xF0 } }, |
| 253 | { "Khaki", { 0xF0, 0xE6, 0x8C } }, |
| 254 | { "Lavender", { 0xE6, 0xE6, 0xFA } }, |
| 255 | { "LavenderBlush", { 0xFF, 0xF0, 0xF5 } }, |
| 256 | { "LawnGreen", { 0x7C, 0xFC, 0x00 } }, |
| 257 | { "LemonChiffon", { 0xFF, 0xFA, 0xCD } }, |
| 258 | { "LightBlue", { 0xAD, 0xD8, 0xE6 } }, |
| 259 | { "LightCoral", { 0xF0, 0x80, 0x80 } }, |
| 260 | { "LightCyan", { 0xE0, 0xFF, 0xFF } }, |
| 261 | { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } }, |
| 262 | { "LightGreen", { 0x90, 0xEE, 0x90 } }, |
| 263 | { "LightGrey", { 0xD3, 0xD3, 0xD3 } }, |
| 264 | { "LightPink", { 0xFF, 0xB6, 0xC1 } }, |
| 265 | { "LightSalmon", { 0xFF, 0xA0, 0x7A } }, |
| 266 | { "LightSeaGreen", { 0x20, 0xB2, 0xAA } }, |
| 267 | { "LightSkyBlue", { 0x87, 0xCE, 0xFA } }, |
| 268 | { "LightSlateGray", { 0x77, 0x88, 0x99 } }, |
| 269 | { "LightSteelBlue", { 0xB0, 0xC4, 0xDE } }, |
| 270 | { "LightYellow", { 0xFF, 0xFF, 0xE0 } }, |
| 271 | { "Lime", { 0x00, 0xFF, 0x00 } }, |
| 272 | { "LimeGreen", { 0x32, 0xCD, 0x32 } }, |
| 273 | { "Linen", { 0xFA, 0xF0, 0xE6 } }, |
| 274 | { "Magenta", { 0xFF, 0x00, 0xFF } }, |
| 275 | { "Maroon", { 0x80, 0x00, 0x00 } }, |
| 276 | { "MediumAquaMarine", { 0x66, 0xCD, 0xAA } }, |
| 277 | { "MediumBlue", { 0x00, 0x00, 0xCD } }, |
| 278 | { "MediumOrchid", { 0xBA, 0x55, 0xD3 } }, |
| 279 | { "MediumPurple", { 0x93, 0x70, 0xD8 } }, |
| 280 | { "MediumSeaGreen", { 0x3C, 0xB3, 0x71 } }, |
| 281 | { "MediumSlateBlue", { 0x7B, 0x68, 0xEE } }, |
| 282 | { "MediumSpringGreen", { 0x00, 0xFA, 0x9A } }, |
| 283 | { "MediumTurquoise", { 0x48, 0xD1, 0xCC } }, |
| 284 | { "MediumVioletRed", { 0xC7, 0x15, 0x85 } }, |
| 285 | { "MidnightBlue", { 0x19, 0x19, 0x70 } }, |
| 286 | { "MintCream", { 0xF5, 0xFF, 0xFA } }, |
| 287 | { "MistyRose", { 0xFF, 0xE4, 0xE1 } }, |
| 288 | { "Moccasin", { 0xFF, 0xE4, 0xB5 } }, |
| 289 | { "NavajoWhite", { 0xFF, 0xDE, 0xAD } }, |
| 290 | { "Navy", { 0x00, 0x00, 0x80 } }, |
| 291 | { "OldLace", { 0xFD, 0xF5, 0xE6 } }, |
| 292 | { "Olive", { 0x80, 0x80, 0x00 } }, |
| 293 | { "OliveDrab", { 0x6B, 0x8E, 0x23 } }, |
| 294 | { "Orange", { 0xFF, 0xA5, 0x00 } }, |
| 295 | { "OrangeRed", { 0xFF, 0x45, 0x00 } }, |
| 296 | { "Orchid", { 0xDA, 0x70, 0xD6 } }, |
| 297 | { "PaleGoldenRod", { 0xEE, 0xE8, 0xAA } }, |
| 298 | { "PaleGreen", { 0x98, 0xFB, 0x98 } }, |
| 299 | { "PaleTurquoise", { 0xAF, 0xEE, 0xEE } }, |
| 300 | { "PaleVioletRed", { 0xD8, 0x70, 0x93 } }, |
| 301 | { "PapayaWhip", { 0xFF, 0xEF, 0xD5 } }, |
| 302 | { "PeachPuff", { 0xFF, 0xDA, 0xB9 } }, |
| 303 | { "Peru", { 0xCD, 0x85, 0x3F } }, |
| 304 | { "Pink", { 0xFF, 0xC0, 0xCB } }, |
| 305 | { "Plum", { 0xDD, 0xA0, 0xDD } }, |
| 306 | { "PowderBlue", { 0xB0, 0xE0, 0xE6 } }, |
| 307 | { "Purple", { 0x80, 0x00, 0x80 } }, |
| 308 | { "Red", { 0xFF, 0x00, 0x00 } }, |
| 309 | { "RosyBrown", { 0xBC, 0x8F, 0x8F } }, |
| 310 | { "RoyalBlue", { 0x41, 0x69, 0xE1 } }, |
| 311 | { "SaddleBrown", { 0x8B, 0x45, 0x13 } }, |
| 312 | { "Salmon", { 0xFA, 0x80, 0x72 } }, |
| 313 | { "SandyBrown", { 0xF4, 0xA4, 0x60 } }, |
| 314 | { "SeaGreen", { 0x2E, 0x8B, 0x57 } }, |
| 315 | { "SeaShell", { 0xFF, 0xF5, 0xEE } }, |
| 316 | { "Sienna", { 0xA0, 0x52, 0x2D } }, |
| 317 | { "Silver", { 0xC0, 0xC0, 0xC0 } }, |
| 318 | { "SkyBlue", { 0x87, 0xCE, 0xEB } }, |
| 319 | { "SlateBlue", { 0x6A, 0x5A, 0xCD } }, |
| 320 | { "SlateGray", { 0x70, 0x80, 0x90 } }, |
| 321 | { "Snow", { 0xFF, 0xFA, 0xFA } }, |
| 322 | { "SpringGreen", { 0x00, 0xFF, 0x7F } }, |
| 323 | { "SteelBlue", { 0x46, 0x82, 0xB4 } }, |
| 324 | { "Tan", { 0xD2, 0xB4, 0x8C } }, |
| 325 | { "Teal", { 0x00, 0x80, 0x80 } }, |
| 326 | { "Thistle", { 0xD8, 0xBF, 0xD8 } }, |
| 327 | { "Tomato", { 0xFF, 0x63, 0x47 } }, |
| 328 | { "Turquoise", { 0x40, 0xE0, 0xD0 } }, |
| 329 | { "Violet", { 0xEE, 0x82, 0xEE } }, |
| 330 | { "Wheat", { 0xF5, 0xDE, 0xB3 } }, |
| 331 | { "White", { 0xFF, 0xFF, 0xFF } }, |
| 332 | { "WhiteSmoke", { 0xF5, 0xF5, 0xF5 } }, |
| 333 | { "Yellow", { 0xFF, 0xFF, 0x00 } }, |
| 334 | { "YellowGreen", { 0x9A, 0xCD, 0x32 } }, |
| 335 | }; |
| 336 | |
| 337 | static int color_table_compare(const void *lhs, const void *rhs) |
| 338 | { |
| 339 | return av_strcasecmp(lhs, ((const ColorEntry *)rhs)->name); |
| 340 | } |
| 341 | |
| 342 | #define ALPHA_SEP '@' |
| 343 | |
| 344 | int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen, |
| 345 | void *log_ctx) |
| 346 | { |
| 347 | char *tail, color_string2[128]; |
| 348 | const ColorEntry *entry; |
| 349 | int len, hex_offset = 0; |
| 350 | |
| 351 | if (color_string[0] == '#') { |
| 352 | hex_offset = 1; |
| 353 | } else if (!strncmp(color_string, "0x", 2)) |
| 354 | hex_offset = 2; |
| 355 | |
| 356 | if (slen < 0) |
| 357 | slen = strlen(color_string); |
| 358 | av_strlcpy(color_string2, color_string + hex_offset, |
| 359 | FFMIN(slen-hex_offset+1, sizeof(color_string2))); |
| 360 | if ((tail = strchr(color_string2, ALPHA_SEP))) |
| 361 | *tail++ = 0; |
| 362 | len = strlen(color_string2); |
| 363 | rgba_color[3] = 255; |
| 364 | |
| 365 | if (!av_strcasecmp(color_string2, "random") || !av_strcasecmp(color_string2, "bikeshed")) { |
| 366 | int rgba = av_get_random_seed(); |
| 367 | rgba_color[0] = rgba >> 24; |
| 368 | rgba_color[1] = rgba >> 16; |
| 369 | rgba_color[2] = rgba >> 8; |
| 370 | rgba_color[3] = rgba; |
| 371 | } else if (hex_offset || |
| 372 | strspn(color_string2, "0123456789ABCDEFabcdef") == len) { |
| 373 | char *tail; |
| 374 | unsigned int rgba = strtoul(color_string2, &tail, 16); |
| 375 | |
| 376 | if (*tail || (len != 6 && len != 8)) { |
| 377 | av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2); |
| 378 | return AVERROR(EINVAL); |
| 379 | } |
| 380 | if (len == 8) { |
| 381 | rgba_color[3] = rgba; |
| 382 | rgba >>= 8; |
| 383 | } |
| 384 | rgba_color[0] = rgba >> 16; |
| 385 | rgba_color[1] = rgba >> 8; |
| 386 | rgba_color[2] = rgba; |
| 387 | } else { |
| 388 | entry = bsearch(color_string2, |
| 389 | color_table, |
| 390 | FF_ARRAY_ELEMS(color_table), |
| 391 | sizeof(ColorEntry), |
| 392 | color_table_compare); |
| 393 | if (!entry) { |
| 394 | av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2); |
| 395 | return AVERROR(EINVAL); |
| 396 | } |
| 397 | memcpy(rgba_color, entry->rgb_color, 3); |
| 398 | } |
| 399 | |
| 400 | if (tail) { |
| 401 | double alpha; |
| 402 | const char *alpha_string = tail; |
| 403 | if (!strncmp(alpha_string, "0x", 2)) { |
| 404 | alpha = strtoul(alpha_string, &tail, 16); |
| 405 | } else { |
| 406 | double norm_alpha = strtod(alpha_string, &tail); |
| 407 | if (norm_alpha < 0.0 || norm_alpha > 1.0) |
| 408 | alpha = 256; |
| 409 | else |
| 410 | alpha = 255 * norm_alpha; |
| 411 | } |
| 412 | |
| 413 | if (tail == alpha_string || *tail || alpha > 255 || alpha < 0) { |
| 414 | av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n", |
| 415 | alpha_string, color_string); |
| 416 | return AVERROR(EINVAL); |
| 417 | } |
| 418 | rgba_color[3] = alpha; |
| 419 | } |
| 420 | |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | const char *av_get_known_color_name(int color_idx, const uint8_t **rgbp) |
| 425 | { |
| 426 | const ColorEntry *color; |
| 427 | |
| 428 | if ((unsigned)color_idx >= FF_ARRAY_ELEMS(color_table)) |
| 429 | return NULL; |
| 430 | |
| 431 | color = &color_table[color_idx]; |
| 432 | if (rgbp) |
| 433 | *rgbp = color->rgb_color; |
| 434 | |
| 435 | return color->name; |
| 436 | } |
| 437 | |
| 438 | /* get a positive number between n_min and n_max, for a maximum length |
| 439 | of len_max. Return -1 if error. */ |
| 440 | static int date_get_num(const char **pp, |
| 441 | int n_min, int n_max, int len_max) |
| 442 | { |
| 443 | int i, val, c; |
| 444 | const char *p; |
| 445 | |
| 446 | p = *pp; |
| 447 | val = 0; |
| 448 | for(i = 0; i < len_max; i++) { |
| 449 | c = *p; |
| 450 | if (!av_isdigit(c)) |
| 451 | break; |
| 452 | val = (val * 10) + c - '0'; |
| 453 | p++; |
| 454 | } |
| 455 | /* no number read ? */ |
| 456 | if (p == *pp) |
| 457 | return -1; |
| 458 | if (val < n_min || val > n_max) |
| 459 | return -1; |
| 460 | *pp = p; |
| 461 | return val; |
| 462 | } |
| 463 | |
| 464 | char *av_small_strptime(const char *p, const char *fmt, struct tm *dt) |
| 465 | { |
| 466 | int c, val; |
| 467 | |
| 468 | for(;;) { |
| 469 | /* consume time string until a non whitespace char is found */ |
| 470 | while (av_isspace(*fmt)) { |
| 471 | while (av_isspace(*p)) |
| 472 | p++; |
| 473 | fmt++; |
| 474 | } |
| 475 | c = *fmt++; |
| 476 | if (c == '\0') { |
| 477 | return (char *)p; |
| 478 | } else if (c == '%') { |
| 479 | c = *fmt++; |
| 480 | switch(c) { |
| 481 | case 'H': |
| 482 | case 'J': |
| 483 | val = date_get_num(&p, 0, c == 'H' ? 23 : INT_MAX, 2); |
| 484 | if (val == -1) |
| 485 | return NULL; |
| 486 | dt->tm_hour = val; |
| 487 | break; |
| 488 | case 'M': |
| 489 | val = date_get_num(&p, 0, 59, 2); |
| 490 | if (val == -1) |
| 491 | return NULL; |
| 492 | dt->tm_min = val; |
| 493 | break; |
| 494 | case 'S': |
| 495 | val = date_get_num(&p, 0, 59, 2); |
| 496 | if (val == -1) |
| 497 | return NULL; |
| 498 | dt->tm_sec = val; |
| 499 | break; |
| 500 | case 'Y': |
| 501 | val = date_get_num(&p, 0, 9999, 4); |
| 502 | if (val == -1) |
| 503 | return NULL; |
| 504 | dt->tm_year = val - 1900; |
| 505 | break; |
| 506 | case 'm': |
| 507 | val = date_get_num(&p, 1, 12, 2); |
| 508 | if (val == -1) |
| 509 | return NULL; |
| 510 | dt->tm_mon = val - 1; |
| 511 | break; |
| 512 | case 'd': |
| 513 | val = date_get_num(&p, 1, 31, 2); |
| 514 | if (val == -1) |
| 515 | return NULL; |
| 516 | dt->tm_mday = val; |
| 517 | break; |
| 518 | case '%': |
| 519 | goto match; |
| 520 | default: |
| 521 | return NULL; |
| 522 | } |
| 523 | } else { |
| 524 | match: |
| 525 | if (c != *p) |
| 526 | return NULL; |
| 527 | p++; |
| 528 | } |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | time_t av_timegm(struct tm *tm) |
| 533 | { |
| 534 | time_t t; |
| 535 | |
| 536 | int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday; |
| 537 | |
| 538 | if (m < 3) { |
| 539 | m += 12; |
| 540 | y--; |
| 541 | } |
| 542 | |
| 543 | t = 86400LL * |
| 544 | (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469); |
| 545 | |
| 546 | t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec; |
| 547 | |
| 548 | return t; |
| 549 | } |
| 550 | |
| 551 | int av_parse_time(int64_t *timeval, const char *timestr, int duration) |
| 552 | { |
| 553 | const char *p, *q; |
| 554 | int64_t t; |
| 555 | time_t now; |
| 556 | struct tm dt = { 0 }, tmbuf; |
| 557 | int today = 0, negative = 0, microseconds = 0; |
| 558 | int i; |
| 559 | static const char * const date_fmt[] = { |
| 560 | "%Y-%m-%d", |
| 561 | "%Y%m%d", |
| 562 | }; |
| 563 | static const char * const time_fmt[] = { |
| 564 | "%H:%M:%S", |
| 565 | "%H%M%S", |
| 566 | }; |
| 567 | |
| 568 | p = timestr; |
| 569 | q = NULL; |
| 570 | *timeval = INT64_MIN; |
| 571 | if (!duration) { |
| 572 | now = time(0); |
| 573 | |
| 574 | if (!av_strcasecmp(timestr, "now")) { |
| 575 | *timeval = (int64_t) now * 1000000; |
| 576 | return 0; |
| 577 | } |
| 578 | |
| 579 | /* parse the year-month-day part */ |
| 580 | for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) { |
| 581 | q = av_small_strptime(p, date_fmt[i], &dt); |
| 582 | if (q) |
| 583 | break; |
| 584 | } |
| 585 | |
| 586 | /* if the year-month-day part is missing, then take the |
| 587 | * current year-month-day time */ |
| 588 | if (!q) { |
| 589 | today = 1; |
| 590 | q = p; |
| 591 | } |
| 592 | p = q; |
| 593 | |
| 594 | if (*p == 'T' || *p == 't' || *p == ' ') |
| 595 | p++; |
| 596 | |
| 597 | /* parse the hour-minute-second part */ |
| 598 | for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) { |
| 599 | q = av_small_strptime(p, time_fmt[i], &dt); |
| 600 | if (q) |
| 601 | break; |
| 602 | } |
| 603 | } else { |
| 604 | /* parse timestr as a duration */ |
| 605 | if (p[0] == '-') { |
| 606 | negative = 1; |
| 607 | ++p; |
| 608 | } |
| 609 | /* parse timestr as HH:MM:SS */ |
| 610 | q = av_small_strptime(p, "%J:%M:%S", &dt); |
| 611 | if (!q) { |
| 612 | /* parse timestr as MM:SS */ |
| 613 | q = av_small_strptime(p, "%M:%S", &dt); |
| 614 | dt.tm_hour = 0; |
| 615 | } |
| 616 | if (!q) { |
| 617 | char *o; |
| 618 | /* parse timestr as S+ */ |
| 619 | dt.tm_sec = strtol(p, &o, 10); |
| 620 | if (o == p) /* the parsing didn't succeed */ |
| 621 | return AVERROR(EINVAL); |
| 622 | dt.tm_min = 0; |
| 623 | dt.tm_hour = 0; |
| 624 | q = o; |
| 625 | } |
| 626 | } |
| 627 | |
| 628 | /* Now we have all the fields that we can get */ |
| 629 | if (!q) |
| 630 | return AVERROR(EINVAL); |
| 631 | |
| 632 | /* parse the .m... part */ |
| 633 | if (*q == '.') { |
| 634 | int n; |
| 635 | q++; |
| 636 | for (n = 100000; n >= 1; n /= 10, q++) { |
| 637 | if (!av_isdigit(*q)) |
| 638 | break; |
| 639 | microseconds += n * (*q - '0'); |
| 640 | } |
| 641 | while (av_isdigit(*q)) |
| 642 | q++; |
| 643 | } |
| 644 | |
| 645 | if (duration) { |
| 646 | t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec; |
| 647 | } else { |
| 648 | int is_utc = *q == 'Z' || *q == 'z'; |
| 649 | q += is_utc; |
| 650 | if (today) { /* fill in today's date */ |
| 651 | struct tm dt2 = is_utc ? *gmtime_r(&now, &tmbuf) : *localtime_r(&now, &tmbuf); |
| 652 | dt2.tm_hour = dt.tm_hour; |
| 653 | dt2.tm_min = dt.tm_min; |
| 654 | dt2.tm_sec = dt.tm_sec; |
| 655 | dt = dt2; |
| 656 | } |
| 657 | t = is_utc ? av_timegm(&dt) : mktime(&dt); |
| 658 | } |
| 659 | |
| 660 | /* Check that we are at the end of the string */ |
| 661 | if (*q) |
| 662 | return AVERROR(EINVAL); |
| 663 | |
| 664 | t *= 1000000; |
| 665 | t += microseconds; |
| 666 | *timeval = negative ? -t : t; |
| 667 | return 0; |
| 668 | } |
| 669 | |
| 670 | int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info) |
| 671 | { |
| 672 | const char *p; |
| 673 | char tag[128], *q; |
| 674 | |
| 675 | p = info; |
| 676 | if (*p == '?') |
| 677 | p++; |
| 678 | for(;;) { |
| 679 | q = tag; |
| 680 | while (*p != '\0' && *p != '=' && *p != '&') { |
| 681 | if ((q - tag) < sizeof(tag) - 1) |
| 682 | *q++ = *p; |
| 683 | p++; |
| 684 | } |
| 685 | *q = '\0'; |
| 686 | q = arg; |
| 687 | if (*p == '=') { |
| 688 | p++; |
| 689 | while (*p != '&' && *p != '\0') { |
| 690 | if ((q - arg) < arg_size - 1) { |
| 691 | if (*p == '+') |
| 692 | *q++ = ' '; |
| 693 | else |
| 694 | *q++ = *p; |
| 695 | } |
| 696 | p++; |
| 697 | } |
| 698 | } |
| 699 | *q = '\0'; |
| 700 | if (!strcmp(tag, tag1)) |
| 701 | return 1; |
| 702 | if (*p != '&') |
| 703 | break; |
| 704 | p++; |
| 705 | } |
| 706 | return 0; |
| 707 | } |
| 708 | |
| 709 | #ifdef TEST |
| 710 | |
| 711 | static uint32_t randomv = MKTAG('L','A','V','U'); |
| 712 | |
| 713 | static uint32_t av_get_random_seed_deterministic(void) |
| 714 | { |
| 715 | return randomv = randomv * 1664525 + 1013904223; |
| 716 | } |
| 717 | |
| 718 | int main(void) |
| 719 | { |
| 720 | printf("Testing av_parse_video_rate()\n"); |
| 721 | { |
| 722 | int i; |
| 723 | static const char *const rates[] = { |
| 724 | "-inf", |
| 725 | "inf", |
| 726 | "nan", |
| 727 | "123/0", |
| 728 | "-123 / 0", |
| 729 | "", |
| 730 | "/", |
| 731 | " 123 / 321", |
| 732 | "foo/foo", |
| 733 | "foo/1", |
| 734 | "1/foo", |
| 735 | "0/0", |
| 736 | "/0", |
| 737 | "1/", |
| 738 | "1", |
| 739 | "0", |
| 740 | "-123/123", |
| 741 | "-foo", |
| 742 | "123.23", |
| 743 | ".23", |
| 744 | "-.23", |
| 745 | "-0.234", |
| 746 | "-0.0000001", |
| 747 | " 21332.2324 ", |
| 748 | " -21332.2324 ", |
| 749 | }; |
| 750 | |
| 751 | for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) { |
| 752 | int ret; |
| 753 | AVRational q = { 0, 0 }; |
| 754 | ret = av_parse_video_rate(&q, rates[i]); |
| 755 | printf("'%s' -> %d/%d %s\n", |
| 756 | rates[i], q.num, q.den, ret ? "ERROR" : "OK"); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | printf("\nTesting av_parse_color()\n"); |
| 761 | { |
| 762 | int i; |
| 763 | uint8_t rgba[4]; |
| 764 | static const char *const color_names[] = { |
| 765 | "bikeshed", |
| 766 | "RaNdOm", |
| 767 | "foo", |
| 768 | "red", |
| 769 | "Red ", |
| 770 | "RED", |
| 771 | "Violet", |
| 772 | "Yellow", |
| 773 | "Red", |
| 774 | "0x000000", |
| 775 | "0x0000000", |
| 776 | "0xff000000", |
| 777 | "0x3e34ff", |
| 778 | "0x3e34ffaa", |
| 779 | "0xffXXee", |
| 780 | "0xfoobar", |
| 781 | "0xffffeeeeeeee", |
| 782 | "#ff0000", |
| 783 | "#ffXX00", |
| 784 | "ff0000", |
| 785 | "ffXX00", |
| 786 | "red@foo", |
| 787 | "random@10", |
| 788 | "0xff0000@1.0", |
| 789 | "red@", |
| 790 | "red@0xfff", |
| 791 | "red@0xf", |
| 792 | "red@2", |
| 793 | "red@0.1", |
| 794 | "red@-1", |
| 795 | "red@0.5", |
| 796 | "red@1.0", |
| 797 | "red@256", |
| 798 | "red@10foo", |
| 799 | "red@-1.0", |
| 800 | "red@-0.0", |
| 801 | }; |
| 802 | |
| 803 | av_log_set_level(AV_LOG_DEBUG); |
| 804 | |
| 805 | for (i = 0; i < FF_ARRAY_ELEMS(color_names); i++) { |
| 806 | if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0) |
| 807 | printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", |
| 808 | color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]); |
| 809 | else |
| 810 | printf("%s -> error\n", color_names[i]); |
| 811 | } |
| 812 | } |
| 813 | |
| 814 | printf("\nTesting av_small_strptime()\n"); |
| 815 | { |
| 816 | int i; |
| 817 | struct tm tm = { 0 }; |
| 818 | struct fmt_timespec_entry { |
| 819 | const char *fmt, *timespec; |
| 820 | } fmt_timespec_entries[] = { |
| 821 | { "%Y-%m-%d", "2012-12-21" }, |
| 822 | { "%Y - %m - %d", "2012-12-21" }, |
| 823 | { "%Y-%m-%d %H:%M:%S", "2012-12-21 20:12:21" }, |
| 824 | { " %Y - %m - %d %H : %M : %S", " 2012 - 12 - 21 20 : 12 : 21" }, |
| 825 | }; |
| 826 | |
| 827 | av_log_set_level(AV_LOG_DEBUG); |
| 828 | for (i = 0; i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) { |
| 829 | char *p; |
| 830 | struct fmt_timespec_entry *e = &fmt_timespec_entries[i]; |
| 831 | printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec); |
| 832 | p = av_small_strptime(e->timespec, e->fmt, &tm); |
| 833 | if (p) { |
| 834 | printf("%04d-%02d-%2d %02d:%02d:%02d\n", |
| 835 | 1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, |
| 836 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
| 837 | } else { |
| 838 | printf("error\n"); |
| 839 | } |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | printf("\nTesting av_parse_time()\n"); |
| 844 | { |
| 845 | int i; |
| 846 | int64_t tv; |
| 847 | time_t tvi; |
| 848 | struct tm *tm; |
| 849 | static char tzstr[] = "TZ=CET-1"; |
| 850 | static const char * const time_string[] = { |
| 851 | "now", |
| 852 | "12:35:46", |
| 853 | "2000-12-20 0:02:47.5z", |
| 854 | "2000-12-20T010247.6", |
| 855 | }; |
| 856 | static const char * const duration_string[] = { |
| 857 | "2:34:56.79", |
| 858 | "-1:23:45.67", |
| 859 | "42.1729", |
| 860 | "-1729.42", |
| 861 | "12:34", |
| 862 | }; |
| 863 | |
| 864 | av_log_set_level(AV_LOG_DEBUG); |
| 865 | putenv(tzstr); |
| 866 | printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n"); |
| 867 | for (i = 0; i < FF_ARRAY_ELEMS(time_string); i++) { |
| 868 | printf("%-24s -> ", time_string[i]); |
| 869 | if (av_parse_time(&tv, time_string[i], 0)) { |
| 870 | printf("error\n"); |
| 871 | } else { |
| 872 | tvi = tv / 1000000; |
| 873 | tm = gmtime(&tvi); |
| 874 | printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n", |
| 875 | tv / 1000000, (int)(tv % 1000000), |
| 876 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, |
| 877 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
| 878 | } |
| 879 | } |
| 880 | for (i = 0; i < FF_ARRAY_ELEMS(duration_string); i++) { |
| 881 | printf("%-24s -> ", duration_string[i]); |
| 882 | if (av_parse_time(&tv, duration_string[i], 1)) { |
| 883 | printf("error\n"); |
| 884 | } else { |
| 885 | printf("%+21"PRIi64"\n", tv); |
| 886 | } |
| 887 | } |
| 888 | } |
| 889 | |
| 890 | return 0; |
| 891 | } |
| 892 | |
| 893 | #endif /* TEST */ |