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