]> git.sesse.net Git - ffmpeg/blob - libavutil/parseutils.c
timecode: move timecode muxer options to metadata.
[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 <sys/time.h>
25 #include <time.h>
26
27 #include "avstring.h"
28 #include "avutil.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     { "LightGrey",            { 0xD3, 0xD3, 0xD3 } },
254     { "LightGreen",           { 0x90, 0xEE, 0x90 } },
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 /**
442  * Parse the input string p according to the format string fmt and
443  * store its results in the structure dt.
444  * This implementation supports only a subset of the formats supported
445  * by the standard strptime().
446  *
447  * @return a pointer to the first character not processed in this
448  * function call, or NULL in case the function fails to match all of
449  * the fmt string and therefore an error occurred
450  */
451 static const char *small_strptime(const char *p, const char *fmt, struct tm *dt)
452 {
453     int c, val;
454
455     for(;;) {
456         c = *fmt++;
457         if (c == '\0') {
458             return p;
459         } else if (c == '%') {
460             c = *fmt++;
461             switch(c) {
462             case 'H':
463                 val = date_get_num(&p, 0, 23, 2);
464                 if (val == -1)
465                     return NULL;
466                 dt->tm_hour = val;
467                 break;
468             case 'M':
469                 val = date_get_num(&p, 0, 59, 2);
470                 if (val == -1)
471                     return NULL;
472                 dt->tm_min = val;
473                 break;
474             case 'S':
475                 val = date_get_num(&p, 0, 59, 2);
476                 if (val == -1)
477                     return NULL;
478                 dt->tm_sec = val;
479                 break;
480             case 'Y':
481                 val = date_get_num(&p, 0, 9999, 4);
482                 if (val == -1)
483                     return NULL;
484                 dt->tm_year = val - 1900;
485                 break;
486             case 'm':
487                 val = date_get_num(&p, 1, 12, 2);
488                 if (val == -1)
489                     return NULL;
490                 dt->tm_mon = val - 1;
491                 break;
492             case 'd':
493                 val = date_get_num(&p, 1, 31, 2);
494                 if (val == -1)
495                     return NULL;
496                 dt->tm_mday = val;
497                 break;
498             case '%':
499                 goto match;
500             default:
501                 return NULL;
502             }
503         } else {
504         match:
505             if (c != *p)
506                 return NULL;
507             p++;
508         }
509     }
510 }
511
512 time_t av_timegm(struct tm *tm)
513 {
514     time_t t;
515
516     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
517
518     if (m < 3) {
519         m += 12;
520         y--;
521     }
522
523     t = 86400 *
524         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
525
526     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
527
528     return t;
529 }
530
531 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
532 {
533     const char *p, *q;
534     int64_t t;
535     time_t now;
536     struct tm dt = { 0 };
537     int today = 0, negative = 0, microseconds = 0;
538     int i;
539     static const char * const date_fmt[] = {
540         "%Y-%m-%d",
541         "%Y%m%d",
542     };
543     static const char * const time_fmt[] = {
544         "%H:%M:%S",
545         "%H%M%S",
546     };
547
548     p = timestr;
549     q = NULL;
550     *timeval = INT64_MIN;
551     if (!duration) {
552         now = time(0);
553
554         if (!av_strcasecmp(timestr, "now")) {
555             *timeval = (int64_t) now * 1000000;
556             return 0;
557         }
558
559         /* parse the year-month-day part */
560         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
561             q = small_strptime(p, date_fmt[i], &dt);
562             if (q)
563                 break;
564         }
565
566         /* if the year-month-day part is missing, then take the
567          * current year-month-day time */
568         if (!q) {
569             today = 1;
570             q = p;
571         }
572         p = q;
573
574         if (*p == 'T' || *p == 't' || *p == ' ')
575             p++;
576
577         /* parse the hour-minute-second part */
578         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
579             q = small_strptime(p, time_fmt[i], &dt);
580             if (q)
581                 break;
582         }
583     } else {
584         /* parse timestr as a duration */
585         if (p[0] == '-') {
586             negative = 1;
587             ++p;
588         }
589         /* parse timestr as HH:MM:SS */
590         q = small_strptime(p, time_fmt[0], &dt);
591         if (!q) {
592             /* parse timestr as S+ */
593             dt.tm_sec = strtol(p, (void *)&q, 10);
594             if (q == p) /* the parsing didn't succeed */
595                 return AVERROR(EINVAL);
596             dt.tm_min = 0;
597             dt.tm_hour = 0;
598         }
599     }
600
601     /* Now we have all the fields that we can get */
602     if (!q)
603         return AVERROR(EINVAL);
604
605     /* parse the .m... part */
606     if (*q == '.') {
607         int n;
608         q++;
609         for (n = 100000; n >= 1; n /= 10, q++) {
610             if (!isdigit(*q))
611                 break;
612             microseconds += n * (*q - '0');
613         }
614     }
615
616     if (duration) {
617         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
618     } else {
619         int is_utc = *q == 'Z' || *q == 'z';
620         q += is_utc;
621         if (today) { /* fill in today's date */
622             struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
623             dt2.tm_hour = dt.tm_hour;
624             dt2.tm_min  = dt.tm_min;
625             dt2.tm_sec  = dt.tm_sec;
626             dt = dt2;
627         }
628         t = is_utc ? av_timegm(&dt) : mktime(&dt);
629     }
630
631     /* Check that we are at the end of the string */
632     if (*q)
633         return AVERROR(EINVAL);
634
635     t *= 1000000;
636     t += microseconds;
637     *timeval = negative ? -t : t;
638     return 0;
639 }
640
641 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
642 {
643     const char *p;
644     char tag[128], *q;
645
646     p = info;
647     if (*p == '?')
648         p++;
649     for(;;) {
650         q = tag;
651         while (*p != '\0' && *p != '=' && *p != '&') {
652             if ((q - tag) < sizeof(tag) - 1)
653                 *q++ = *p;
654             p++;
655         }
656         *q = '\0';
657         q = arg;
658         if (*p == '=') {
659             p++;
660             while (*p != '&' && *p != '\0') {
661                 if ((q - arg) < arg_size - 1) {
662                     if (*p == '+')
663                         *q++ = ' ';
664                     else
665                         *q++ = *p;
666                 }
667                 p++;
668             }
669         }
670         *q = '\0';
671         if (!strcmp(tag, tag1))
672             return 1;
673         if (*p != '&')
674             break;
675         p++;
676     }
677     return 0;
678 }
679
680 #ifdef TEST
681
682 static uint32_t random = MKTAG('L','A','V','U');
683
684 static uint32_t av_get_random_seed_deterministic(void)
685 {
686     return random = random * 1664525 + 1013904223;
687 }
688
689 #undef printf
690
691 int main(void)
692 {
693     printf("Testing av_parse_video_rate()\n");
694     {
695         int i;
696         const char *rates[] = {
697             "-inf",
698             "inf",
699             "nan",
700             "123/0",
701             "-123 / 0",
702             "",
703             "/",
704             " 123  /  321",
705             "foo/foo",
706             "foo/1",
707             "1/foo",
708             "0/0",
709             "/0",
710             "1/",
711             "1",
712             "0",
713             "-123/123",
714             "-foo",
715             "123.23",
716             ".23",
717             "-.23",
718             "-0.234",
719             "-0.0000001",
720             "  21332.2324   ",
721             " -21332.2324   ",
722         };
723
724         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
725             int ret;
726             char err[1024];
727             AVRational q = (AVRational){0, 0};
728             ret = av_parse_video_rate(&q, rates[i]);
729             av_strerror(ret, err, sizeof(err));
730             printf("'%s' -> %d/%d ret:%s\n",
731                    rates[i], q.num, q.den, err);
732         }
733     }
734
735     printf("\nTesting av_parse_color()\n");
736     {
737         int i;
738         uint8_t rgba[4];
739         const char *color_names[] = {
740             "bikeshed",
741             "RaNdOm",
742             "foo",
743             "red",
744             "Red ",
745             "RED",
746             "Violet",
747             "Yellow",
748             "Red",
749             "0x000000",
750             "0x0000000",
751             "0xff000000",
752             "0x3e34ff",
753             "0x3e34ffaa",
754             "0xffXXee",
755             "0xfoobar",
756             "0xffffeeeeeeee",
757             "#ff0000",
758             "#ffXX00",
759             "ff0000",
760             "ffXX00",
761             "red@foo",
762             "random@10",
763             "0xff0000@1.0",
764             "red@",
765             "red@0xfff",
766             "red@0xf",
767             "red@2",
768             "red@0.1",
769             "red@-1",
770             "red@0.5",
771             "red@1.0",
772             "red@256",
773             "red@10foo",
774             "red@-1.0",
775             "red@-0.0",
776         };
777
778         av_log_set_level(AV_LOG_DEBUG);
779
780         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
781             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
782                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
783             else
784                 printf("%s -> error\n", color_names[i]);
785         }
786     }
787
788     printf("\nTesting av_parse_time()\n");
789     {
790         int i;
791         int64_t tv;
792         time_t tvi;
793         struct tm *tm;
794         static char tzstr[] = "TZ=CET-1";
795         const char *time_string[] = {
796             "now",
797             "12:35:46",
798             "2000-12-20 0:02:47.5z",
799             "2000-12-20T010247.6",
800         };
801         const char *duration_string[] = {
802             "2:34:56.79",
803             "-1:23:45.67",
804             "42.1729",
805             "-1729.42",
806             "12:34",
807         };
808
809         av_log_set_level(AV_LOG_DEBUG);
810         putenv(tzstr);
811         printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
812         for (i = 0;  i < FF_ARRAY_ELEMS(time_string); i++) {
813             printf("%-24s -> ", time_string[i]);
814             if (av_parse_time(&tv, time_string[i], 0)) {
815                 printf("error\n");
816             } else {
817                 tvi = tv / 1000000;
818                 tm = gmtime(&tvi);
819                 printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
820                        tv / 1000000, (int)(tv % 1000000),
821                        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
822                        tm->tm_hour, tm->tm_min, tm->tm_sec);
823             }
824         }
825         for (i = 0;  i < FF_ARRAY_ELEMS(duration_string); i++) {
826             printf("%-24s -> ", duration_string[i]);
827             if (av_parse_time(&tv, duration_string[i], 1)) {
828                 printf("error\n");
829             } else {
830                 printf("%+21"PRIi64"\n", tv);
831             }
832         }
833     }
834
835     return 0;
836 }
837
838 #endif /* TEST */