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