]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
Merge commit '6234058148d6e1ee765913b678a22385a87bffac'
[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 #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             char *o;
617             /* parse timestr as S+ */
618             dt.tm_sec = strtol(p, &o, 10);
619             if (o == p) /* the parsing didn't succeed */
620                 return AVERROR(EINVAL);
621             dt.tm_min = 0;
622             dt.tm_hour = 0;
623             q = o;
624         }
625     }
626
627     /* Now we have all the fields that we can get */
628     if (!q)
629         return AVERROR(EINVAL);
630
631     /* parse the .m... part */
632     if (*q == '.') {
633         int n;
634         q++;
635         for (n = 100000; n >= 1; n /= 10, q++) {
636             if (!av_isdigit(*q))
637                 break;
638             microseconds += n * (*q - '0');
639         }
640         while (av_isdigit(*q))
641             q++;
642     }
643
644     if (duration) {
645         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
646     } else {
647         int is_utc = *q == 'Z' || *q == 'z';
648         q += is_utc;
649         if (today) { /* fill in today's date */
650             struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
651             dt2.tm_hour = dt.tm_hour;
652             dt2.tm_min  = dt.tm_min;
653             dt2.tm_sec  = dt.tm_sec;
654             dt = dt2;
655         }
656         t = is_utc ? av_timegm(&dt) : mktime(&dt);
657     }
658
659     /* Check that we are at the end of the string */
660     if (*q)
661         return AVERROR(EINVAL);
662
663     t *= 1000000;
664     t += microseconds;
665     *timeval = negative ? -t : t;
666     return 0;
667 }
668
669 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
670 {
671     const char *p;
672     char tag[128], *q;
673
674     p = info;
675     if (*p == '?')
676         p++;
677     for(;;) {
678         q = tag;
679         while (*p != '\0' && *p != '=' && *p != '&') {
680             if ((q - tag) < sizeof(tag) - 1)
681                 *q++ = *p;
682             p++;
683         }
684         *q = '\0';
685         q = arg;
686         if (*p == '=') {
687             p++;
688             while (*p != '&' && *p != '\0') {
689                 if ((q - arg) < arg_size - 1) {
690                     if (*p == '+')
691                         *q++ = ' ';
692                     else
693                         *q++ = *p;
694                 }
695                 p++;
696             }
697         }
698         *q = '\0';
699         if (!strcmp(tag, tag1))
700             return 1;
701         if (*p != '&')
702             break;
703         p++;
704     }
705     return 0;
706 }
707
708 #ifdef TEST
709
710 static uint32_t randomv = MKTAG('L','A','V','U');
711
712 static uint32_t av_get_random_seed_deterministic(void)
713 {
714     return randomv = randomv * 1664525 + 1013904223;
715 }
716
717 int main(void)
718 {
719     printf("Testing av_parse_video_rate()\n");
720     {
721         int i;
722         static const char *const rates[] = {
723             "-inf",
724             "inf",
725             "nan",
726             "123/0",
727             "-123 / 0",
728             "",
729             "/",
730             " 123  /  321",
731             "foo/foo",
732             "foo/1",
733             "1/foo",
734             "0/0",
735             "/0",
736             "1/",
737             "1",
738             "0",
739             "-123/123",
740             "-foo",
741             "123.23",
742             ".23",
743             "-.23",
744             "-0.234",
745             "-0.0000001",
746             "  21332.2324   ",
747             " -21332.2324   ",
748         };
749
750         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
751             int ret;
752             AVRational q = { 0, 0 };
753             ret = av_parse_video_rate(&q, rates[i]);
754             printf("'%s' -> %d/%d %s\n",
755                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
756         }
757     }
758
759     printf("\nTesting av_parse_color()\n");
760     {
761         int i;
762         uint8_t rgba[4];
763         static const char *const color_names[] = {
764             "bikeshed",
765             "RaNdOm",
766             "foo",
767             "red",
768             "Red ",
769             "RED",
770             "Violet",
771             "Yellow",
772             "Red",
773             "0x000000",
774             "0x0000000",
775             "0xff000000",
776             "0x3e34ff",
777             "0x3e34ffaa",
778             "0xffXXee",
779             "0xfoobar",
780             "0xffffeeeeeeee",
781             "#ff0000",
782             "#ffXX00",
783             "ff0000",
784             "ffXX00",
785             "red@foo",
786             "random@10",
787             "0xff0000@1.0",
788             "red@",
789             "red@0xfff",
790             "red@0xf",
791             "red@2",
792             "red@0.1",
793             "red@-1",
794             "red@0.5",
795             "red@1.0",
796             "red@256",
797             "red@10foo",
798             "red@-1.0",
799             "red@-0.0",
800         };
801
802         av_log_set_level(AV_LOG_DEBUG);
803
804         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
805             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
806                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
807                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
808             else
809                 printf("%s -> error\n", color_names[i]);
810         }
811     }
812
813     printf("\nTesting av_small_strptime()\n");
814     {
815         int i;
816         struct tm tm = { 0 };
817         struct fmt_timespec_entry {
818             const char *fmt, *timespec;
819         } fmt_timespec_entries[] = {
820             { "%Y-%m-%d",                    "2012-12-21" },
821             { "%Y - %m - %d",                "2012-12-21" },
822             { "%Y-%m-%d %H:%M:%S",           "2012-12-21 20:12:21" },
823             { "  %Y - %m - %d %H : %M : %S", "   2012 - 12 -  21   20 : 12 : 21" },
824         };
825
826         av_log_set_level(AV_LOG_DEBUG);
827         for (i = 0;  i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
828             char *p;
829             struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
830             printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
831             p = av_small_strptime(e->timespec, e->fmt, &tm);
832             if (p) {
833                 printf("%04d-%02d-%2d %02d:%02d:%02d\n",
834                        1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
835                        tm.tm_hour, tm.tm_min, tm.tm_sec);
836             } else {
837                 printf("error\n");
838             }
839         }
840     }
841
842     printf("\nTesting av_parse_time()\n");
843     {
844         int i;
845         int64_t tv;
846         time_t tvi;
847         struct tm *tm;
848         static char tzstr[] = "TZ=CET-1";
849         static const char * const time_string[] = {
850             "now",
851             "12:35:46",
852             "2000-12-20 0:02:47.5z",
853             "2000-12-20T010247.6",
854         };
855         static const char * const duration_string[] = {
856             "2:34:56.79",
857             "-1:23:45.67",
858             "42.1729",
859             "-1729.42",
860             "12:34",
861         };
862
863         av_log_set_level(AV_LOG_DEBUG);
864         putenv(tzstr);
865         printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
866         for (i = 0;  i < FF_ARRAY_ELEMS(time_string); i++) {
867             printf("%-24s -> ", time_string[i]);
868             if (av_parse_time(&tv, time_string[i], 0)) {
869                 printf("error\n");
870             } else {
871                 tvi = tv / 1000000;
872                 tm = gmtime(&tvi);
873                 printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
874                        tv / 1000000, (int)(tv % 1000000),
875                        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
876                        tm->tm_hour, tm->tm_min, tm->tm_sec);
877             }
878         }
879         for (i = 0;  i < FF_ARRAY_ELEMS(duration_string); i++) {
880             printf("%-24s -> ", duration_string[i]);
881             if (av_parse_time(&tv, duration_string[i], 1)) {
882                 printf("error\n");
883             } else {
884                 printf("%+21"PRIi64"\n", tv);
885             }
886         }
887     }
888
889     return 0;
890 }
891
892 #endif /* TEST */