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