]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
doc/avconv: expand documentation for the -s option.
[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 <sys/time.h>
25 #include <time.h>
26
27 #include "avstring.h"
28 #include "avutil.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         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 av_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 (!av_strcasecmp(color_string2, "random") || !av_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 static const char *small_strptime(const char *p, const char *fmt, struct tm *dt)
404 {
405     int c, val;
406
407     for(;;) {
408         c = *fmt++;
409         if (c == '\0') {
410             return p;
411         } else if (c == '%') {
412             c = *fmt++;
413             switch(c) {
414             case 'H':
415                 val = date_get_num(&p, 0, 23, 2);
416                 if (val == -1)
417                     return NULL;
418                 dt->tm_hour = val;
419                 break;
420             case 'M':
421                 val = date_get_num(&p, 0, 59, 2);
422                 if (val == -1)
423                     return NULL;
424                 dt->tm_min = val;
425                 break;
426             case 'S':
427                 val = date_get_num(&p, 0, 59, 2);
428                 if (val == -1)
429                     return NULL;
430                 dt->tm_sec = val;
431                 break;
432             case 'Y':
433                 val = date_get_num(&p, 0, 9999, 4);
434                 if (val == -1)
435                     return NULL;
436                 dt->tm_year = val - 1900;
437                 break;
438             case 'm':
439                 val = date_get_num(&p, 1, 12, 2);
440                 if (val == -1)
441                     return NULL;
442                 dt->tm_mon = val - 1;
443                 break;
444             case 'd':
445                 val = date_get_num(&p, 1, 31, 2);
446                 if (val == -1)
447                     return NULL;
448                 dt->tm_mday = val;
449                 break;
450             case '%':
451                 goto match;
452             default:
453                 return NULL;
454             }
455         } else {
456         match:
457             if (c != *p)
458                 return NULL;
459             p++;
460         }
461     }
462 }
463
464 time_t av_timegm(struct tm *tm)
465 {
466     time_t t;
467
468     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
469
470     if (m < 3) {
471         m += 12;
472         y--;
473     }
474
475     t = 86400 *
476         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
477
478     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
479
480     return t;
481 }
482
483 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
484 {
485     const char *p;
486     int64_t t;
487     struct tm dt = { 0 };
488     int i;
489     static const char * const date_fmt[] = {
490         "%Y-%m-%d",
491         "%Y%m%d",
492     };
493     static const char * const time_fmt[] = {
494         "%H:%M:%S",
495         "%H%M%S",
496     };
497     const char *q;
498     int is_utc, len;
499     char lastch;
500     int negative = 0;
501
502 #undef time
503     time_t now = time(0);
504
505     len = strlen(timestr);
506     if (len > 0)
507         lastch = timestr[len - 1];
508     else
509         lastch = '\0';
510     is_utc = (lastch == 'z' || lastch == 'Z');
511
512     p = timestr;
513     q = NULL;
514     if (!duration) {
515         if (!av_strncasecmp(timestr, "now", len)) {
516             *timeval = (int64_t) now * 1000000;
517             return 0;
518         }
519
520         /* parse the year-month-day part */
521         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
522             q = small_strptime(p, date_fmt[i], &dt);
523             if (q) {
524                 break;
525             }
526         }
527
528         /* if the year-month-day part is missing, then take the
529          * current year-month-day time */
530         if (!q) {
531             if (is_utc) {
532                 dt = *gmtime(&now);
533             } else {
534                 dt = *localtime(&now);
535             }
536             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
537         } else {
538             p = q;
539         }
540
541         if (*p == 'T' || *p == 't' || *p == ' ')
542             p++;
543
544         /* parse the hour-minute-second part */
545         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
546             q = small_strptime(p, time_fmt[i], &dt);
547             if (q) {
548                 break;
549             }
550         }
551     } else {
552         /* parse timestr as a duration */
553         if (p[0] == '-') {
554             negative = 1;
555             ++p;
556         }
557         /* parse timestr as HH:MM:SS */
558         q = small_strptime(p, time_fmt[0], &dt);
559         if (!q) {
560             /* parse timestr as S+ */
561             dt.tm_sec = strtol(p, (char **)&q, 10);
562             if (q == p) {
563                 /* the parsing didn't succeed */
564                 *timeval = INT64_MIN;
565                 return AVERROR(EINVAL);
566             }
567             dt.tm_min = 0;
568             dt.tm_hour = 0;
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 (!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 #undef printf
648
649 int main(void)
650 {
651     printf("Testing av_parse_video_rate()\n");
652     {
653         int i;
654         const char *rates[] = {
655             "-inf",
656             "inf",
657             "nan",
658             "123/0",
659             "-123 / 0",
660             "",
661             "/",
662             " 123  /  321",
663             "foo/foo",
664             "foo/1",
665             "1/foo",
666             "0/0",
667             "/0",
668             "1/",
669             "1",
670             "0",
671             "-123/123",
672             "-foo",
673             "123.23",
674             ".23",
675             "-.23",
676             "-0.234",
677             "-0.0000001",
678             "  21332.2324   ",
679             " -21332.2324   ",
680         };
681
682         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
683             int ret;
684             AVRational q = (AVRational){0, 0};
685             ret = av_parse_video_rate(&q, rates[i]),
686             printf("'%s' -> %d/%d ret:%d\n",
687                    rates[i], q.num, q.den, ret);
688         }
689     }
690
691     printf("\nTesting av_parse_color()\n");
692     {
693         int i;
694         uint8_t rgba[4];
695         const char *color_names[] = {
696             "bikeshed",
697             "RaNdOm",
698             "foo",
699             "red",
700             "Red ",
701             "RED",
702             "Violet",
703             "Yellow",
704             "Red",
705             "0x000000",
706             "0x0000000",
707             "0xff000000",
708             "0x3e34ff",
709             "0x3e34ffaa",
710             "0xffXXee",
711             "0xfoobar",
712             "0xffffeeeeeeee",
713             "#ff0000",
714             "#ffXX00",
715             "ff0000",
716             "ffXX00",
717             "red@foo",
718             "random@10",
719             "0xff0000@1.0",
720             "red@",
721             "red@0xfff",
722             "red@0xf",
723             "red@2",
724             "red@0.1",
725             "red@-1",
726             "red@0.5",
727             "red@1.0",
728             "red@256",
729             "red@10foo",
730             "red@-1.0",
731             "red@-0.0",
732         };
733
734         av_log_set_level(AV_LOG_DEBUG);
735
736         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
737             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
738                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
739         }
740     }
741
742     return 0;
743 }
744
745 #endif /* TEST */