]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavutil / parseutils.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 /**
20  * @file
21  * misc parsing utilities
22  */
23
24 #include <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             case 'J':
458                 val = date_get_num(&p, 0, c == 'H' ? 23 : INT_MAX, 2);
459                 if (val == -1)
460                     return NULL;
461                 dt->tm_hour = val;
462                 break;
463             case 'M':
464                 val = date_get_num(&p, 0, 59, 2);
465                 if (val == -1)
466                     return NULL;
467                 dt->tm_min = val;
468                 break;
469             case 'S':
470                 val = date_get_num(&p, 0, 59, 2);
471                 if (val == -1)
472                     return NULL;
473                 dt->tm_sec = val;
474                 break;
475             case 'Y':
476                 val = date_get_num(&p, 0, 9999, 4);
477                 if (val == -1)
478                     return NULL;
479                 dt->tm_year = val - 1900;
480                 break;
481             case 'm':
482                 val = date_get_num(&p, 1, 12, 2);
483                 if (val == -1)
484                     return NULL;
485                 dt->tm_mon = val - 1;
486                 break;
487             case 'd':
488                 val = date_get_num(&p, 1, 31, 2);
489                 if (val == -1)
490                     return NULL;
491                 dt->tm_mday = val;
492                 break;
493             case '%':
494                 goto match;
495             default:
496                 return NULL;
497             }
498         } else {
499         match:
500             if (c != *p)
501                 return NULL;
502             p++;
503         }
504     }
505 }
506
507 time_t av_timegm(struct tm *tm)
508 {
509     time_t t;
510
511     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
512
513     if (m < 3) {
514         m += 12;
515         y--;
516     }
517
518     t = 86400 *
519         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
520
521     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
522
523     return t;
524 }
525
526 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
527 {
528     const char *p, *q;
529     int64_t t;
530     time_t now;
531     struct tm dt = { 0 };
532     int today = 0, negative = 0, microseconds = 0;
533     int i;
534     static const char * const date_fmt[] = {
535         "%Y-%m-%d",
536         "%Y%m%d",
537     };
538     static const char * const time_fmt[] = {
539         "%H:%M:%S",
540         "%H%M%S",
541     };
542
543     p = timestr;
544     q = NULL;
545     *timeval = INT64_MIN;
546     if (!duration) {
547         now = time(0);
548
549         if (!av_strcasecmp(timestr, "now")) {
550             *timeval = (int64_t) now * 1000000;
551             return 0;
552         }
553
554         /* parse the year-month-day part */
555         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
556             q = av_small_strptime(p, date_fmt[i], &dt);
557             if (q)
558                 break;
559         }
560
561         /* if the year-month-day part is missing, then take the
562          * current year-month-day time */
563         if (!q) {
564             today = 1;
565             q = p;
566         }
567         p = q;
568
569         if (*p == 'T' || *p == 't' || *p == ' ')
570             p++;
571
572         /* parse the hour-minute-second part */
573         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
574             q = av_small_strptime(p, time_fmt[i], &dt);
575             if (q)
576                 break;
577         }
578     } else {
579         /* parse timestr as a duration */
580         if (p[0] == '-') {
581             negative = 1;
582             ++p;
583         }
584         /* parse timestr as HH:MM:SS */
585         q = av_small_strptime(p, "%J:%M:%S", &dt);
586         if (!q) {
587             /* parse timestr as S+ */
588             dt.tm_sec = strtol(p, (void *)&q, 10);
589             if (q == p) /* the parsing didn't succeed */
590                 return AVERROR(EINVAL);
591             dt.tm_min = 0;
592             dt.tm_hour = 0;
593         }
594     }
595
596     /* Now we have all the fields that we can get */
597     if (!q)
598         return AVERROR(EINVAL);
599
600     /* parse the .m... part */
601     if (*q == '.') {
602         int n;
603         q++;
604         for (n = 100000; n >= 1; n /= 10, q++) {
605             if (!isdigit(*q))
606                 break;
607             microseconds += n * (*q - '0');
608         }
609         while (isdigit(*q))
610             q++;
611     }
612
613     if (duration) {
614         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
615     } else {
616         int is_utc = *q == 'Z' || *q == 'z';
617         q += is_utc;
618         if (today) { /* fill in today's date */
619             struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
620             dt2.tm_hour = dt.tm_hour;
621             dt2.tm_min  = dt.tm_min;
622             dt2.tm_sec  = dt.tm_sec;
623             dt = dt2;
624         }
625         t = is_utc ? av_timegm(&dt) : mktime(&dt);
626     }
627
628     /* Check that we are at the end of the string */
629     if (*q)
630         return AVERROR(EINVAL);
631
632     t *= 1000000;
633     t += microseconds;
634     *timeval = negative ? -t : t;
635     return 0;
636 }
637
638 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
639 {
640     const char *p;
641     char tag[128], *q;
642
643     p = info;
644     if (*p == '?')
645         p++;
646     for(;;) {
647         q = tag;
648         while (*p != '\0' && *p != '=' && *p != '&') {
649             if ((q - tag) < sizeof(tag) - 1)
650                 *q++ = *p;
651             p++;
652         }
653         *q = '\0';
654         q = arg;
655         if (*p == '=') {
656             p++;
657             while (*p != '&' && *p != '\0') {
658                 if ((q - arg) < arg_size - 1) {
659                     if (*p == '+')
660                         *q++ = ' ';
661                     else
662                         *q++ = *p;
663                 }
664                 p++;
665             }
666         }
667         *q = '\0';
668         if (!strcmp(tag, tag1))
669             return 1;
670         if (*p != '&')
671             break;
672         p++;
673     }
674     return 0;
675 }
676
677 #ifdef TEST
678
679 static uint32_t random = MKTAG('L','A','V','U');
680
681 static uint32_t av_get_random_seed_deterministic(void)
682 {
683     return random = random * 1664525 + 1013904223;
684 }
685
686 #undef printf
687
688 int main(void)
689 {
690     printf("Testing av_parse_video_rate()\n");
691     {
692         int i;
693         static const char *const rates[] = {
694             "-inf",
695             "inf",
696             "nan",
697             "123/0",
698             "-123 / 0",
699             "",
700             "/",
701             " 123  /  321",
702             "foo/foo",
703             "foo/1",
704             "1/foo",
705             "0/0",
706             "/0",
707             "1/",
708             "1",
709             "0",
710             "-123/123",
711             "-foo",
712             "123.23",
713             ".23",
714             "-.23",
715             "-0.234",
716             "-0.0000001",
717             "  21332.2324   ",
718             " -21332.2324   ",
719         };
720
721         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
722             int ret;
723             AVRational q = { 0, 0 };
724             ret = av_parse_video_rate(&q, rates[i]);
725             printf("'%s' -> %d/%d %s\n",
726                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
727         }
728     }
729
730     printf("\nTesting av_parse_color()\n");
731     {
732         int i;
733         uint8_t rgba[4];
734         static const char *const color_names[] = {
735             "bikeshed",
736             "RaNdOm",
737             "foo",
738             "red",
739             "Red ",
740             "RED",
741             "Violet",
742             "Yellow",
743             "Red",
744             "0x000000",
745             "0x0000000",
746             "0xff000000",
747             "0x3e34ff",
748             "0x3e34ffaa",
749             "0xffXXee",
750             "0xfoobar",
751             "0xffffeeeeeeee",
752             "#ff0000",
753             "#ffXX00",
754             "ff0000",
755             "ffXX00",
756             "red@foo",
757             "random@10",
758             "0xff0000@1.0",
759             "red@",
760             "red@0xfff",
761             "red@0xf",
762             "red@2",
763             "red@0.1",
764             "red@-1",
765             "red@0.5",
766             "red@1.0",
767             "red@256",
768             "red@10foo",
769             "red@-1.0",
770             "red@-0.0",
771         };
772
773         av_log_set_level(AV_LOG_DEBUG);
774
775         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
776             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
777                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
778                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
779             else
780                 printf("%s -> error\n", color_names[i]);
781         }
782     }
783
784     printf("\nTesting av_small_strptime()\n");
785     {
786         int i;
787         struct tm tm = { 0 };
788         struct fmt_timespec_entry {
789             const char *fmt, *timespec;
790         } fmt_timespec_entries[] = {
791             { "%Y-%m-%d",                    "2012-12-21" },
792             { "%Y - %m - %d",                "2012-12-21" },
793             { "%Y-%m-%d %H:%M:%S",           "2012-12-21 20:12:21" },
794             { "  %Y - %m - %d %H : %M : %S", "   2012 - 12 -  21   20 : 12 : 21" },
795         };
796
797         av_log_set_level(AV_LOG_DEBUG);
798         for (i = 0;  i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
799             char *p;
800             struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
801             printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
802             p = av_small_strptime(e->timespec, e->fmt, &tm);
803             if (p) {
804                 printf("%04d-%02d-%2d %02d:%02d:%02d\n",
805                        1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
806                        tm.tm_hour, tm.tm_min, tm.tm_sec);
807             } else {
808                 printf("error\n");
809             }
810         }
811     }
812
813     printf("\nTesting av_parse_time()\n");
814     {
815         int i;
816         int64_t tv;
817         time_t tvi;
818         struct tm *tm;
819         static char tzstr[] = "TZ=CET-1";
820         const char *time_string[] = {
821             "now",
822             "12:35:46",
823             "2000-12-20 0:02:47.5z",
824             "2000-12-20T010247.6",
825         };
826         const char *duration_string[] = {
827             "2:34:56.79",
828             "-1:23:45.67",
829             "42.1729",
830             "-1729.42",
831             "12:34",
832         };
833
834         av_log_set_level(AV_LOG_DEBUG);
835         putenv(tzstr);
836         printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
837         for (i = 0;  i < FF_ARRAY_ELEMS(time_string); i++) {
838             printf("%-24s -> ", time_string[i]);
839             if (av_parse_time(&tv, time_string[i], 0)) {
840                 printf("error\n");
841             } else {
842                 tvi = tv / 1000000;
843                 tm = gmtime(&tvi);
844                 printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
845                        tv / 1000000, (int)(tv % 1000000),
846                        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
847                        tm->tm_hour, tm->tm_min, tm->tm_sec);
848             }
849         }
850         for (i = 0;  i < FF_ARRAY_ELEMS(duration_string); i++) {
851             printf("%-24s -> ", duration_string[i]);
852             if (av_parse_time(&tv, duration_string[i], 1)) {
853                 printf("error\n");
854             } else {
855                 printf("%+21"PRIi64"\n", tv);
856             }
857         }
858     }
859
860     return 0;
861 }
862
863 #endif /* TEST */