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