]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavutil / parseutils.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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     const 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, (void*)&p, 10);
112         if (*p)
113             p++;
114         height = strtol(p, (void*)&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 const 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 /**
404  * Parse the input string p according to the format string fmt and
405  * store its results in the structure dt.
406  * This implementation supports only a subset of the formats supported
407  * by the standard strptime().
408  *
409  * @return a pointer to the first character not processed in this
410  * function call, or NULL in case the function fails to match all of
411  * the fmt string and therefore an error occurred
412  */
413 static const char *small_strptime(const char *p, const char *fmt, struct tm *dt)
414 {
415     int c, val;
416
417     for(;;) {
418         c = *fmt++;
419         if (c == '\0') {
420             return p;
421         } else if (c == '%') {
422             c = *fmt++;
423             switch(c) {
424             case 'H':
425                 val = date_get_num(&p, 0, 23, 2);
426                 if (val == -1)
427                     return NULL;
428                 dt->tm_hour = val;
429                 break;
430             case 'M':
431                 val = date_get_num(&p, 0, 59, 2);
432                 if (val == -1)
433                     return NULL;
434                 dt->tm_min = val;
435                 break;
436             case 'S':
437                 val = date_get_num(&p, 0, 59, 2);
438                 if (val == -1)
439                     return NULL;
440                 dt->tm_sec = val;
441                 break;
442             case 'Y':
443                 val = date_get_num(&p, 0, 9999, 4);
444                 if (val == -1)
445                     return NULL;
446                 dt->tm_year = val - 1900;
447                 break;
448             case 'm':
449                 val = date_get_num(&p, 1, 12, 2);
450                 if (val == -1)
451                     return NULL;
452                 dt->tm_mon = val - 1;
453                 break;
454             case 'd':
455                 val = date_get_num(&p, 1, 31, 2);
456                 if (val == -1)
457                     return NULL;
458                 dt->tm_mday = val;
459                 break;
460             case '%':
461                 goto match;
462             default:
463                 return NULL;
464             }
465         } else {
466         match:
467             if (c != *p)
468                 return NULL;
469             p++;
470         }
471     }
472 }
473
474 time_t av_timegm(struct tm *tm)
475 {
476     time_t t;
477
478     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
479
480     if (m < 3) {
481         m += 12;
482         y--;
483     }
484
485     t = 86400 *
486         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
487
488     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
489
490     return t;
491 }
492
493 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
494 {
495     const char *p;
496     int64_t t;
497     struct tm dt;
498     int i;
499     static const char * const date_fmt[] = {
500         "%Y-%m-%d",
501         "%Y%m%d",
502     };
503     static const char * const time_fmt[] = {
504         "%H:%M:%S",
505         "%H%M%S",
506     };
507     const char *q;
508     int is_utc, len;
509     char lastch;
510     int negative = 0;
511
512 #undef time
513     time_t now = time(0);
514
515     len = strlen(timestr);
516     if (len > 0)
517         lastch = timestr[len - 1];
518     else
519         lastch = '\0';
520     is_utc = (lastch == 'z' || lastch == 'Z');
521
522     memset(&dt, 0, sizeof(dt));
523
524     p = timestr;
525     q = NULL;
526     if (!duration) {
527         if (!av_strncasecmp(timestr, "now", len)) {
528             *timeval = (int64_t) now * 1000000;
529             return 0;
530         }
531
532         /* parse the year-month-day part */
533         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
534             q = small_strptime(p, date_fmt[i], &dt);
535             if (q) {
536                 break;
537             }
538         }
539
540         /* if the year-month-day part is missing, then take the
541          * current year-month-day time */
542         if (!q) {
543             if (is_utc) {
544                 dt = *gmtime(&now);
545             } else {
546                 dt = *localtime(&now);
547             }
548             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
549         } else {
550             p = q;
551         }
552
553         if (*p == 'T' || *p == 't' || *p == ' ')
554             p++;
555
556         /* parse the hour-minute-second part */
557         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
558             q = small_strptime(p, time_fmt[i], &dt);
559             if (q) {
560                 break;
561             }
562         }
563     } else {
564         /* parse timestr as a duration */
565         if (p[0] == '-') {
566             negative = 1;
567             ++p;
568         }
569         /* parse timestr as HH:MM:SS */
570         q = small_strptime(p, time_fmt[0], &dt);
571         if (!q) {
572             /* parse timestr as S+ */
573             dt.tm_sec = strtol(p, (void *)&q, 10);
574             if (q == p) {
575                 /* the parsing didn't succeed */
576                 *timeval = INT64_MIN;
577                 return AVERROR(EINVAL);
578             }
579             dt.tm_min = 0;
580             dt.tm_hour = 0;
581         }
582     }
583
584     /* Now we have all the fields that we can get */
585     if (!q) {
586         *timeval = INT64_MIN;
587         return AVERROR(EINVAL);
588     }
589
590     if (duration) {
591         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
592     } else {
593         dt.tm_isdst = -1;       /* unknown */
594         if (is_utc) {
595             t = av_timegm(&dt);
596         } else {
597             t = mktime(&dt);
598         }
599     }
600
601     t *= 1000000;
602
603     /* parse the .m... part */
604     if (*q == '.') {
605         int val, n;
606         q++;
607         for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
608             if (!isdigit(*q))
609                 break;
610             val += n * (*q - '0');
611         }
612         t += val;
613     }
614     *timeval = negative ? -t : t;
615     return 0;
616 }
617
618 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
619 {
620     const char *p;
621     char tag[128], *q;
622
623     p = info;
624     if (*p == '?')
625         p++;
626     for(;;) {
627         q = tag;
628         while (*p != '\0' && *p != '=' && *p != '&') {
629             if ((q - tag) < sizeof(tag) - 1)
630                 *q++ = *p;
631             p++;
632         }
633         *q = '\0';
634         q = arg;
635         if (*p == '=') {
636             p++;
637             while (*p != '&' && *p != '\0') {
638                 if ((q - arg) < arg_size - 1) {
639                     if (*p == '+')
640                         *q++ = ' ';
641                     else
642                         *q++ = *p;
643                 }
644                 p++;
645             }
646         }
647         *q = '\0';
648         if (!strcmp(tag, tag1))
649             return 1;
650         if (*p != '&')
651             break;
652         p++;
653     }
654     return 0;
655 }
656
657 #ifdef TEST
658
659 #undef printf
660
661 int main(void)
662 {
663     printf("Testing av_parse_video_rate()\n");
664     {
665         int i;
666         const char *rates[] = {
667             "-inf",
668             "inf",
669             "nan",
670             "123/0",
671             "-123 / 0",
672             "",
673             "/",
674             " 123  /  321",
675             "foo/foo",
676             "foo/1",
677             "1/foo",
678             "0/0",
679             "/0",
680             "1/",
681             "1",
682             "0",
683             "-123/123",
684             "-foo",
685             "123.23",
686             ".23",
687             "-.23",
688             "-0.234",
689             "-0.0000001",
690             "  21332.2324   ",
691             " -21332.2324   ",
692         };
693
694         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
695             int ret;
696             AVRational q = (AVRational){0, 0};
697             ret = av_parse_video_rate(&q, rates[i]),
698             printf("'%s' -> %d/%d ret:%d\n",
699                    rates[i], q.num, q.den, ret);
700         }
701     }
702
703     printf("\nTesting av_parse_color()\n");
704     {
705         int i;
706         uint8_t rgba[4];
707         const char *color_names[] = {
708             "bikeshed",
709             "RaNdOm",
710             "foo",
711             "red",
712             "Red ",
713             "RED",
714             "Violet",
715             "Yellow",
716             "Red",
717             "0x000000",
718             "0x0000000",
719             "0xff000000",
720             "0x3e34ff",
721             "0x3e34ffaa",
722             "0xffXXee",
723             "0xfoobar",
724             "0xffffeeeeeeee",
725             "#ff0000",
726             "#ffXX00",
727             "ff0000",
728             "ffXX00",
729             "red@foo",
730             "random@10",
731             "0xff0000@1.0",
732             "red@",
733             "red@0xfff",
734             "red@0xf",
735             "red@2",
736             "red@0.1",
737             "red@-1",
738             "red@0.5",
739             "red@1.0",
740             "red@256",
741             "red@10foo",
742             "red@-1.0",
743             "red@-0.0",
744         };
745
746         av_log_set_level(AV_LOG_DEBUG);
747
748         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
749             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
750                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
751         }
752     }
753
754     return 0;
755 }
756
757 #endif /* TEST */