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