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