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