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