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