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