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