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