414cd47e661b2ac99e2d841dec679b9952a45ccf
[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 <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 typedef struct {
35     const char *abbr;
36     int width, height;
37 } VideoSizeAbbr;
38
39 typedef struct {
40     const char *abbr;
41     AVRational rate;
42 } VideoRateAbbr;
43
44 static const VideoSizeAbbr video_size_abbrs[] = {
45     { "ntsc",      720, 480 },
46     { "pal",       720, 576 },
47     { "qntsc",     352, 240 }, /* VCD compliant NTSC */
48     { "qpal",      352, 288 }, /* VCD compliant PAL */
49     { "sntsc",     640, 480 }, /* square pixel NTSC */
50     { "spal",      768, 576 }, /* square pixel PAL */
51     { "film",      352, 240 },
52     { "ntsc-film", 352, 240 },
53     { "sqcif",     128,  96 },
54     { "qcif",      176, 144 },
55     { "cif",       352, 288 },
56     { "4cif",      704, 576 },
57     { "16cif",    1408,1152 },
58     { "qqvga",     160, 120 },
59     { "qvga",      320, 240 },
60     { "vga",       640, 480 },
61     { "svga",      800, 600 },
62     { "xga",      1024, 768 },
63     { "uxga",     1600,1200 },
64     { "qxga",     2048,1536 },
65     { "sxga",     1280,1024 },
66     { "qsxga",    2560,2048 },
67     { "hsxga",    5120,4096 },
68     { "wvga",      852, 480 },
69     { "wxga",     1366, 768 },
70     { "wsxga",    1600,1024 },
71     { "wuxga",    1920,1200 },
72     { "woxga",    2560,1600 },
73     { "wqsxga",   3200,2048 },
74     { "wquxga",   3840,2400 },
75     { "whsxga",   6400,4096 },
76     { "whuxga",   7680,4800 },
77     { "cga",       320, 200 },
78     { "ega",       640, 350 },
79     { "hd480",     852, 480 },
80     { "hd720",    1280, 720 },
81     { "hd1080",   1920,1080 },
82 };
83
84 static const VideoRateAbbr video_rate_abbrs[]= {
85     { "ntsc",      { 30000, 1001 } },
86     { "pal",       {    25,    1 } },
87     { "qntsc",     { 30000, 1001 } }, /* VCD compliant NTSC */
88     { "qpal",      {    25,    1 } }, /* VCD compliant PAL */
89     { "sntsc",     { 30000, 1001 } }, /* square pixel NTSC */
90     { "spal",      {    25,    1 } }, /* square pixel PAL */
91     { "film",      {    24,    1 } },
92     { "ntsc-film", { 24000, 1001 } },
93 };
94
95 int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
96 {
97     int i;
98     int n = FF_ARRAY_ELEMS(video_size_abbrs);
99     char *p;
100     int width = 0, height = 0;
101
102     for (i = 0; i < n; i++) {
103         if (!strcmp(video_size_abbrs[i].abbr, str)) {
104             width  = video_size_abbrs[i].width;
105             height = video_size_abbrs[i].height;
106             break;
107         }
108     }
109     if (i == n) {
110         width = strtol(str, &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 av_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 (!av_strcasecmp(color_string2, "random") || !av_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         double 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 || alpha < 0) {
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 (!av_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 static const char *small_strptime(const char *p, const char *fmt, struct tm *dt)
403 {
404     int c, val;
405
406     for(;;) {
407         c = *fmt++;
408         if (c == '\0') {
409             return p;
410         } else if (c == '%') {
411             c = *fmt++;
412             switch(c) {
413             case 'H':
414                 val = date_get_num(&p, 0, 23, 2);
415                 if (val == -1)
416                     return NULL;
417                 dt->tm_hour = val;
418                 break;
419             case 'M':
420                 val = date_get_num(&p, 0, 59, 2);
421                 if (val == -1)
422                     return NULL;
423                 dt->tm_min = val;
424                 break;
425             case 'S':
426                 val = date_get_num(&p, 0, 59, 2);
427                 if (val == -1)
428                     return NULL;
429                 dt->tm_sec = val;
430                 break;
431             case 'Y':
432                 val = date_get_num(&p, 0, 9999, 4);
433                 if (val == -1)
434                     return NULL;
435                 dt->tm_year = val - 1900;
436                 break;
437             case 'm':
438                 val = date_get_num(&p, 1, 12, 2);
439                 if (val == -1)
440                     return NULL;
441                 dt->tm_mon = val - 1;
442                 break;
443             case 'd':
444                 val = date_get_num(&p, 1, 31, 2);
445                 if (val == -1)
446                     return NULL;
447                 dt->tm_mday = val;
448                 break;
449             case '%':
450                 goto match;
451             default:
452                 return NULL;
453             }
454         } else {
455         match:
456             if (c != *p)
457                 return NULL;
458             p++;
459         }
460     }
461 }
462
463 time_t av_timegm(struct tm *tm)
464 {
465     time_t t;
466
467     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
468
469     if (m < 3) {
470         m += 12;
471         y--;
472     }
473
474     t = 86400 *
475         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
476
477     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
478
479     return t;
480 }
481
482 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
483 {
484     const char *p;
485     int64_t t;
486     struct tm dt = { 0 };
487     int i;
488     static const char * const date_fmt[] = {
489         "%Y-%m-%d",
490         "%Y%m%d",
491     };
492     static const char * const time_fmt[] = {
493         "%H:%M:%S",
494         "%H%M%S",
495     };
496     const char *q;
497     int is_utc, len;
498     char lastch;
499     int negative = 0;
500
501     time_t now = time(0);
502
503     len = strlen(timestr);
504     if (len > 0)
505         lastch = timestr[len - 1];
506     else
507         lastch = '\0';
508     is_utc = (lastch == 'z' || lastch == 'Z');
509
510     p = timestr;
511     q = NULL;
512     if (!duration) {
513         if (!av_strncasecmp(timestr, "now", len)) {
514             *timeval = (int64_t) now * 1000000;
515             return 0;
516         }
517
518         /* parse the year-month-day part */
519         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
520             q = small_strptime(p, date_fmt[i], &dt);
521             if (q) {
522                 break;
523             }
524         }
525
526         /* if the year-month-day part is missing, then take the
527          * current year-month-day time */
528         if (!q) {
529             if (is_utc) {
530                 dt = *gmtime(&now);
531             } else {
532                 dt = *localtime(&now);
533             }
534             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
535         } else {
536             p = q;
537         }
538
539         if (*p == 'T' || *p == 't' || *p == ' ')
540             p++;
541
542         /* parse the hour-minute-second part */
543         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
544             q = small_strptime(p, time_fmt[i], &dt);
545             if (q) {
546                 break;
547             }
548         }
549     } else {
550         /* parse timestr as a duration */
551         if (p[0] == '-') {
552             negative = 1;
553             ++p;
554         }
555         /* parse timestr as HH:MM:SS */
556         q = small_strptime(p, time_fmt[0], &dt);
557         if (!q) {
558             char *o;
559             /* parse timestr as S+ */
560             dt.tm_sec = strtol(p, &o, 10);
561             if (o == p) {
562                 /* the parsing didn't succeed */
563                 *timeval = INT64_MIN;
564                 return AVERROR(EINVAL);
565             }
566             dt.tm_min = 0;
567             dt.tm_hour = 0;
568             q = o;
569         }
570     }
571
572     /* Now we have all the fields that we can get */
573     if (!q) {
574         *timeval = INT64_MIN;
575         return AVERROR(EINVAL);
576     }
577
578     if (duration) {
579         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
580     } else {
581         dt.tm_isdst = -1;       /* unknown */
582         if (is_utc) {
583             t = av_timegm(&dt);
584         } else {
585             t = mktime(&dt);
586         }
587     }
588
589     t *= 1000000;
590
591     /* parse the .m... part */
592     if (*q == '.') {
593         int val, n;
594         q++;
595         for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
596             if (!av_isdigit(*q))
597                 break;
598             val += n * (*q - '0');
599         }
600         t += val;
601     }
602     *timeval = negative ? -t : t;
603     return 0;
604 }
605
606 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
607 {
608     const char *p;
609     char tag[128], *q;
610
611     p = info;
612     if (*p == '?')
613         p++;
614     for(;;) {
615         q = tag;
616         while (*p != '\0' && *p != '=' && *p != '&') {
617             if ((q - tag) < sizeof(tag) - 1)
618                 *q++ = *p;
619             p++;
620         }
621         *q = '\0';
622         q = arg;
623         if (*p == '=') {
624             p++;
625             while (*p != '&' && *p != '\0') {
626                 if ((q - arg) < arg_size - 1) {
627                     if (*p == '+')
628                         *q++ = ' ';
629                     else
630                         *q++ = *p;
631                 }
632                 p++;
633             }
634         }
635         *q = '\0';
636         if (!strcmp(tag, tag1))
637             return 1;
638         if (*p != '&')
639             break;
640         p++;
641     }
642     return 0;
643 }
644
645 #ifdef TEST
646
647 int main(void)
648 {
649     printf("Testing av_parse_video_rate()\n");
650     {
651         int i;
652         static const char *const rates[] = {
653             "-inf",
654             "inf",
655             "nan",
656             "123/0",
657             "-123 / 0",
658             "",
659             "/",
660             " 123  /  321",
661             "foo/foo",
662             "foo/1",
663             "1/foo",
664             "0/0",
665             "/0",
666             "1/",
667             "1",
668             "0",
669             "-123/123",
670             "-foo",
671             "123.23",
672             ".23",
673             "-.23",
674             "-0.234",
675             "-0.0000001",
676             "  21332.2324   ",
677             " -21332.2324   ",
678         };
679
680         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
681             int ret;
682             AVRational q = { 0, 0 };
683             ret = av_parse_video_rate(&q, rates[i]);
684             printf("'%s' -> %d/%d %s\n",
685                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
686         }
687     }
688
689     printf("\nTesting av_parse_color()\n");
690     {
691         int i;
692         uint8_t rgba[4];
693         static const char *const color_names[] = {
694             "foo",
695             "red",
696             "Red ",
697             "RED",
698             "Violet",
699             "Yellow",
700             "Red",
701             "0x000000",
702             "0x0000000",
703             "0xff000000",
704             "0x3e34ff",
705             "0x3e34ffaa",
706             "0xffXXee",
707             "0xfoobar",
708             "0xffffeeeeeeee",
709             "#ff0000",
710             "#ffXX00",
711             "ff0000",
712             "ffXX00",
713             "red@foo",
714             "random@10",
715             "0xff0000@1.0",
716             "red@",
717             "red@0xfff",
718             "red@0xf",
719             "red@2",
720             "red@0.1",
721             "red@-1",
722             "red@0.5",
723             "red@1.0",
724             "red@256",
725             "red@10foo",
726             "red@-1.0",
727             "red@-0.0",
728         };
729
730         av_log_set_level(AV_LOG_DEBUG);
731
732         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
733             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
734                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
735                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
736         }
737     }
738
739     return 0;
740 }
741
742 #endif /* TEST */