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