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