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