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