]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
avutil: add yuva422p and yuva444p formats
[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 <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 (!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 #undef time
502     time_t now = time(0);
503
504     len = strlen(timestr);
505     if (len > 0)
506         lastch = timestr[len - 1];
507     else
508         lastch = '\0';
509     is_utc = (lastch == 'z' || lastch == 'Z');
510
511     p = timestr;
512     q = NULL;
513     if (!duration) {
514         if (!av_strncasecmp(timestr, "now", len)) {
515             *timeval = (int64_t) now * 1000000;
516             return 0;
517         }
518
519         /* parse the year-month-day part */
520         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
521             q = small_strptime(p, date_fmt[i], &dt);
522             if (q) {
523                 break;
524             }
525         }
526
527         /* if the year-month-day part is missing, then take the
528          * current year-month-day time */
529         if (!q) {
530             if (is_utc) {
531                 dt = *gmtime(&now);
532             } else {
533                 dt = *localtime(&now);
534             }
535             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
536         } else {
537             p = q;
538         }
539
540         if (*p == 'T' || *p == 't' || *p == ' ')
541             p++;
542
543         /* parse the hour-minute-second part */
544         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
545             q = small_strptime(p, time_fmt[i], &dt);
546             if (q) {
547                 break;
548             }
549         }
550     } else {
551         /* parse timestr as a duration */
552         if (p[0] == '-') {
553             negative = 1;
554             ++p;
555         }
556         /* parse timestr as HH:MM:SS */
557         q = small_strptime(p, time_fmt[0], &dt);
558         if (!q) {
559             /* parse timestr as S+ */
560             dt.tm_sec = strtol(p, (char **)&q, 10);
561             if (q == 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         }
569     }
570
571     /* Now we have all the fields that we can get */
572     if (!q) {
573         *timeval = INT64_MIN;
574         return AVERROR(EINVAL);
575     }
576
577     if (duration) {
578         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
579     } else {
580         dt.tm_isdst = -1;       /* unknown */
581         if (is_utc) {
582             t = av_timegm(&dt);
583         } else {
584             t = mktime(&dt);
585         }
586     }
587
588     t *= 1000000;
589
590     /* parse the .m... part */
591     if (*q == '.') {
592         int val, n;
593         q++;
594         for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
595             if (!isdigit(*q))
596                 break;
597             val += n * (*q - '0');
598         }
599         t += val;
600     }
601     *timeval = negative ? -t : t;
602     return 0;
603 }
604
605 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
606 {
607     const char *p;
608     char tag[128], *q;
609
610     p = info;
611     if (*p == '?')
612         p++;
613     for(;;) {
614         q = tag;
615         while (*p != '\0' && *p != '=' && *p != '&') {
616             if ((q - tag) < sizeof(tag) - 1)
617                 *q++ = *p;
618             p++;
619         }
620         *q = '\0';
621         q = arg;
622         if (*p == '=') {
623             p++;
624             while (*p != '&' && *p != '\0') {
625                 if ((q - arg) < arg_size - 1) {
626                     if (*p == '+')
627                         *q++ = ' ';
628                     else
629                         *q++ = *p;
630                 }
631                 p++;
632             }
633         }
634         *q = '\0';
635         if (!strcmp(tag, tag1))
636             return 1;
637         if (*p != '&')
638             break;
639         p++;
640     }
641     return 0;
642 }
643
644 #ifdef TEST
645
646 #undef printf
647
648 int main(void)
649 {
650     printf("Testing av_parse_video_rate()\n");
651     {
652         int i;
653         static const char *const rates[] = {
654             "-inf",
655             "inf",
656             "nan",
657             "123/0",
658             "-123 / 0",
659             "",
660             "/",
661             " 123  /  321",
662             "foo/foo",
663             "foo/1",
664             "1/foo",
665             "0/0",
666             "/0",
667             "1/",
668             "1",
669             "0",
670             "-123/123",
671             "-foo",
672             "123.23",
673             ".23",
674             "-.23",
675             "-0.234",
676             "-0.0000001",
677             "  21332.2324   ",
678             " -21332.2324   ",
679         };
680
681         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
682             int ret;
683             AVRational q = { 0, 0 };
684             ret = av_parse_video_rate(&q, rates[i]);
685             printf("'%s' -> %d/%d %s\n",
686                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
687         }
688     }
689
690     printf("\nTesting av_parse_color()\n");
691     {
692         int i;
693         uint8_t rgba[4];
694         static const char *const color_names[] = {
695             "foo",
696             "red",
697             "Red ",
698             "RED",
699             "Violet",
700             "Yellow",
701             "Red",
702             "0x000000",
703             "0x0000000",
704             "0xff000000",
705             "0x3e34ff",
706             "0x3e34ffaa",
707             "0xffXXee",
708             "0xfoobar",
709             "0xffffeeeeeeee",
710             "#ff0000",
711             "#ffXX00",
712             "ff0000",
713             "ffXX00",
714             "red@foo",
715             "random@10",
716             "0xff0000@1.0",
717             "red@",
718             "red@0xfff",
719             "red@0xf",
720             "red@2",
721             "red@0.1",
722             "red@-1",
723             "red@0.5",
724             "red@1.0",
725             "red@256",
726             "red@10foo",
727             "red@-1.0",
728             "red@-0.0",
729         };
730
731         av_log_set_level(AV_LOG_DEBUG);
732
733         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
734             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
735                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
736                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
737         }
738     }
739
740     return 0;
741 }
742
743 #endif /* TEST */