lavu/parseutils: add digital cinema frame sizes
[ffmpeg.git] / 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 (!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 (isspace(*fmt)) {
450             while (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 = 86400 *
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 S+ */
592             dt.tm_sec = strtol(p, (void *)&q, 10);
593             if (q == p) /* the parsing didn't succeed */
594                 return AVERROR(EINVAL);
595             dt.tm_min = 0;
596             dt.tm_hour = 0;
597         }
598     }
599
600     /* Now we have all the fields that we can get */
601     if (!q)
602         return AVERROR(EINVAL);
603
604     /* parse the .m... part */
605     if (*q == '.') {
606         int n;
607         q++;
608         for (n = 100000; n >= 1; n /= 10, q++) {
609             if (!isdigit(*q))
610                 break;
611             microseconds += n * (*q - '0');
612         }
613         while (isdigit(*q))
614             q++;
615     }
616
617     if (duration) {
618         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
619     } else {
620         int is_utc = *q == 'Z' || *q == 'z';
621         q += is_utc;
622         if (today) { /* fill in today's date */
623             struct tm dt2 = is_utc ? *gmtime(&now) : *localtime(&now);
624             dt2.tm_hour = dt.tm_hour;
625             dt2.tm_min  = dt.tm_min;
626             dt2.tm_sec  = dt.tm_sec;
627             dt = dt2;
628         }
629         t = is_utc ? av_timegm(&dt) : mktime(&dt);
630     }
631
632     /* Check that we are at the end of the string */
633     if (*q)
634         return AVERROR(EINVAL);
635
636     t *= 1000000;
637     t += microseconds;
638     *timeval = negative ? -t : t;
639     return 0;
640 }
641
642 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
643 {
644     const char *p;
645     char tag[128], *q;
646
647     p = info;
648     if (*p == '?')
649         p++;
650     for(;;) {
651         q = tag;
652         while (*p != '\0' && *p != '=' && *p != '&') {
653             if ((q - tag) < sizeof(tag) - 1)
654                 *q++ = *p;
655             p++;
656         }
657         *q = '\0';
658         q = arg;
659         if (*p == '=') {
660             p++;
661             while (*p != '&' && *p != '\0') {
662                 if ((q - arg) < arg_size - 1) {
663                     if (*p == '+')
664                         *q++ = ' ';
665                     else
666                         *q++ = *p;
667                 }
668                 p++;
669             }
670         }
671         *q = '\0';
672         if (!strcmp(tag, tag1))
673             return 1;
674         if (*p != '&')
675             break;
676         p++;
677     }
678     return 0;
679 }
680
681 #ifdef TEST
682
683 static uint32_t randomv = MKTAG('L','A','V','U');
684
685 static uint32_t av_get_random_seed_deterministic(void)
686 {
687     return randomv = randomv * 1664525 + 1013904223;
688 }
689
690 int main(void)
691 {
692     printf("Testing av_parse_video_rate()\n");
693     {
694         int i;
695         static const char *const rates[] = {
696             "-inf",
697             "inf",
698             "nan",
699             "123/0",
700             "-123 / 0",
701             "",
702             "/",
703             " 123  /  321",
704             "foo/foo",
705             "foo/1",
706             "1/foo",
707             "0/0",
708             "/0",
709             "1/",
710             "1",
711             "0",
712             "-123/123",
713             "-foo",
714             "123.23",
715             ".23",
716             "-.23",
717             "-0.234",
718             "-0.0000001",
719             "  21332.2324   ",
720             " -21332.2324   ",
721         };
722
723         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
724             int ret;
725             AVRational q = { 0, 0 };
726             ret = av_parse_video_rate(&q, rates[i]);
727             printf("'%s' -> %d/%d %s\n",
728                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
729         }
730     }
731
732     printf("\nTesting av_parse_color()\n");
733     {
734         int i;
735         uint8_t rgba[4];
736         static const char *const color_names[] = {
737             "bikeshed",
738             "RaNdOm",
739             "foo",
740             "red",
741             "Red ",
742             "RED",
743             "Violet",
744             "Yellow",
745             "Red",
746             "0x000000",
747             "0x0000000",
748             "0xff000000",
749             "0x3e34ff",
750             "0x3e34ffaa",
751             "0xffXXee",
752             "0xfoobar",
753             "0xffffeeeeeeee",
754             "#ff0000",
755             "#ffXX00",
756             "ff0000",
757             "ffXX00",
758             "red@foo",
759             "random@10",
760             "0xff0000@1.0",
761             "red@",
762             "red@0xfff",
763             "red@0xf",
764             "red@2",
765             "red@0.1",
766             "red@-1",
767             "red@0.5",
768             "red@1.0",
769             "red@256",
770             "red@10foo",
771             "red@-1.0",
772             "red@-0.0",
773         };
774
775         av_log_set_level(AV_LOG_DEBUG);
776
777         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
778             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
779                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
780                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
781             else
782                 printf("%s -> error\n", color_names[i]);
783         }
784     }
785
786     printf("\nTesting av_small_strptime()\n");
787     {
788         int i;
789         struct tm tm = { 0 };
790         struct fmt_timespec_entry {
791             const char *fmt, *timespec;
792         } fmt_timespec_entries[] = {
793             { "%Y-%m-%d",                    "2012-12-21" },
794             { "%Y - %m - %d",                "2012-12-21" },
795             { "%Y-%m-%d %H:%M:%S",           "2012-12-21 20:12:21" },
796             { "  %Y - %m - %d %H : %M : %S", "   2012 - 12 -  21   20 : 12 : 21" },
797         };
798
799         av_log_set_level(AV_LOG_DEBUG);
800         for (i = 0;  i < FF_ARRAY_ELEMS(fmt_timespec_entries); i++) {
801             char *p;
802             struct fmt_timespec_entry *e = &fmt_timespec_entries[i];
803             printf("fmt:'%s' spec:'%s' -> ", e->fmt, e->timespec);
804             p = av_small_strptime(e->timespec, e->fmt, &tm);
805             if (p) {
806                 printf("%04d-%02d-%2d %02d:%02d:%02d\n",
807                        1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
808                        tm.tm_hour, tm.tm_min, tm.tm_sec);
809             } else {
810                 printf("error\n");
811             }
812         }
813     }
814
815     printf("\nTesting av_parse_time()\n");
816     {
817         int i;
818         int64_t tv;
819         time_t tvi;
820         struct tm *tm;
821         static char tzstr[] = "TZ=CET-1";
822         const char *time_string[] = {
823             "now",
824             "12:35:46",
825             "2000-12-20 0:02:47.5z",
826             "2000-12-20T010247.6",
827         };
828         const char *duration_string[] = {
829             "2:34:56.79",
830             "-1:23:45.67",
831             "42.1729",
832             "-1729.42",
833             "12:34",
834         };
835
836         av_log_set_level(AV_LOG_DEBUG);
837         putenv(tzstr);
838         printf("(now is 2012-03-17 09:14:13 +0100, local time is UTC+1)\n");
839         for (i = 0;  i < FF_ARRAY_ELEMS(time_string); i++) {
840             printf("%-24s -> ", time_string[i]);
841             if (av_parse_time(&tv, time_string[i], 0)) {
842                 printf("error\n");
843             } else {
844                 tvi = tv / 1000000;
845                 tm = gmtime(&tvi);
846                 printf("%14"PRIi64".%06d = %04d-%02d-%02dT%02d:%02d:%02dZ\n",
847                        tv / 1000000, (int)(tv % 1000000),
848                        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
849                        tm->tm_hour, tm->tm_min, tm->tm_sec);
850             }
851         }
852         for (i = 0;  i < FF_ARRAY_ELEMS(duration_string); i++) {
853             printf("%-24s -> ", duration_string[i]);
854             if (av_parse_time(&tv, duration_string[i], 1)) {
855                 printf("error\n");
856             } else {
857                 printf("%+21"PRIi64"\n", tv);
858             }
859         }
860     }
861
862     return 0;
863 }
864
865 #endif /* TEST */