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