09eebcf56f25ea09d3def39231785f29e839042c
[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 <strings.h>
25 #include "parseutils.h"
26 #include "libavutil/avutil.h"
27 #include "libavutil/eval.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/random_seed.h"
30
31 typedef struct {
32     const char *abbr;
33     int width, height;
34 } VideoSizeAbbr;
35
36 typedef struct {
37     const char *abbr;
38     AVRational rate;
39 } VideoRateAbbr;
40
41 static const VideoSizeAbbr video_size_abbrs[] = {
42     { "ntsc",      720, 480 },
43     { "pal",       720, 576 },
44     { "qntsc",     352, 240 }, /* VCD compliant NTSC */
45     { "qpal",      352, 288 }, /* VCD compliant PAL */
46     { "sntsc",     640, 480 }, /* square pixel NTSC */
47     { "spal",      768, 576 }, /* square pixel PAL */
48     { "film",      352, 240 },
49     { "ntsc-film", 352, 240 },
50     { "sqcif",     128,  96 },
51     { "qcif",      176, 144 },
52     { "cif",       352, 288 },
53     { "4cif",      704, 576 },
54     { "16cif",    1408,1152 },
55     { "qqvga",     160, 120 },
56     { "qvga",      320, 240 },
57     { "vga",       640, 480 },
58     { "svga",      800, 600 },
59     { "xga",      1024, 768 },
60     { "uxga",     1600,1200 },
61     { "qxga",     2048,1536 },
62     { "sxga",     1280,1024 },
63     { "qsxga",    2560,2048 },
64     { "hsxga",    5120,4096 },
65     { "wvga",      852, 480 },
66     { "wxga",     1366, 768 },
67     { "wsxga",    1600,1024 },
68     { "wuxga",    1920,1200 },
69     { "woxga",    2560,1600 },
70     { "wqsxga",   3200,2048 },
71     { "wquxga",   3840,2400 },
72     { "whsxga",   6400,4096 },
73     { "whuxga",   7680,4800 },
74     { "cga",       320, 200 },
75     { "ega",       640, 350 },
76     { "hd480",     852, 480 },
77     { "hd720",    1280, 720 },
78     { "hd1080",   1920,1080 },
79 };
80
81 static const VideoRateAbbr video_rate_abbrs[]= {
82     { "ntsc",      { 30000, 1001 } },
83     { "pal",       {    25,    1 } },
84     { "qntsc",     { 30000, 1001 } }, /* VCD compliant NTSC */
85     { "qpal",      {    25,    1 } }, /* VCD compliant PAL */
86     { "sntsc",     { 30000, 1001 } }, /* square pixel NTSC */
87     { "spal",      {    25,    1 } }, /* square pixel PAL */
88     { "film",      {    24,    1 } },
89     { "ntsc-film", { 24000, 1001 } },
90 };
91
92 int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
93 {
94     int i;
95     int n = FF_ARRAY_ELEMS(video_size_abbrs);
96     char *p;
97     int width = 0, height = 0;
98
99     for (i = 0; i < n; i++) {
100         if (!strcmp(video_size_abbrs[i].abbr, str)) {
101             width  = video_size_abbrs[i].width;
102             height = video_size_abbrs[i].height;
103             break;
104         }
105     }
106     if (i == n) {
107         p = str;
108         width = strtol(p, &p, 10);
109         if (*p)
110             p++;
111         height = strtol(p, &p, 10);
112     }
113     if (width <= 0 || height <= 0)
114         return AVERROR(EINVAL);
115     *width_ptr  = width;
116     *height_ptr = height;
117     return 0;
118 }
119
120 int av_parse_video_rate(AVRational *rate, const char *arg)
121 {
122     int i, ret;
123     int n = FF_ARRAY_ELEMS(video_rate_abbrs);
124     double res;
125
126     /* First, we check our abbreviation table */
127     for (i = 0; i < n; ++i)
128         if (!strcmp(video_rate_abbrs[i].abbr, arg)) {
129             *rate = video_rate_abbrs[i].rate;
130             return 0;
131         }
132
133     /* Then, we try to parse it as fraction */
134     if ((ret = av_expr_parse_and_eval(&res, arg, NULL, NULL, NULL, NULL, NULL, NULL,
135                                       NULL, 0, NULL)) < 0)
136         return ret;
137     *rate = av_d2q(res, 1001000);
138     if (rate->num <= 0 || rate->den <= 0)
139         return AVERROR(EINVAL);
140     return 0;
141 }
142
143 typedef struct {
144     const char *name;            ///< a string representing the name of the color
145     uint8_t     rgb_color[3];    ///< RGB values for the color
146 } ColorEntry;
147
148 static ColorEntry color_table[] = {
149     { "AliceBlue",            { 0xF0, 0xF8, 0xFF } },
150     { "AntiqueWhite",         { 0xFA, 0xEB, 0xD7 } },
151     { "Aqua",                 { 0x00, 0xFF, 0xFF } },
152     { "Aquamarine",           { 0x7F, 0xFF, 0xD4 } },
153     { "Azure",                { 0xF0, 0xFF, 0xFF } },
154     { "Beige",                { 0xF5, 0xF5, 0xDC } },
155     { "Bisque",               { 0xFF, 0xE4, 0xC4 } },
156     { "Black",                { 0x00, 0x00, 0x00 } },
157     { "BlanchedAlmond",       { 0xFF, 0xEB, 0xCD } },
158     { "Blue",                 { 0x00, 0x00, 0xFF } },
159     { "BlueViolet",           { 0x8A, 0x2B, 0xE2 } },
160     { "Brown",                { 0xA5, 0x2A, 0x2A } },
161     { "BurlyWood",            { 0xDE, 0xB8, 0x87 } },
162     { "CadetBlue",            { 0x5F, 0x9E, 0xA0 } },
163     { "Chartreuse",           { 0x7F, 0xFF, 0x00 } },
164     { "Chocolate",            { 0xD2, 0x69, 0x1E } },
165     { "Coral",                { 0xFF, 0x7F, 0x50 } },
166     { "CornflowerBlue",       { 0x64, 0x95, 0xED } },
167     { "Cornsilk",             { 0xFF, 0xF8, 0xDC } },
168     { "Crimson",              { 0xDC, 0x14, 0x3C } },
169     { "Cyan",                 { 0x00, 0xFF, 0xFF } },
170     { "DarkBlue",             { 0x00, 0x00, 0x8B } },
171     { "DarkCyan",             { 0x00, 0x8B, 0x8B } },
172     { "DarkGoldenRod",        { 0xB8, 0x86, 0x0B } },
173     { "DarkGray",             { 0xA9, 0xA9, 0xA9 } },
174     { "DarkGreen",            { 0x00, 0x64, 0x00 } },
175     { "DarkKhaki",            { 0xBD, 0xB7, 0x6B } },
176     { "DarkMagenta",          { 0x8B, 0x00, 0x8B } },
177     { "DarkOliveGreen",       { 0x55, 0x6B, 0x2F } },
178     { "Darkorange",           { 0xFF, 0x8C, 0x00 } },
179     { "DarkOrchid",           { 0x99, 0x32, 0xCC } },
180     { "DarkRed",              { 0x8B, 0x00, 0x00 } },
181     { "DarkSalmon",           { 0xE9, 0x96, 0x7A } },
182     { "DarkSeaGreen",         { 0x8F, 0xBC, 0x8F } },
183     { "DarkSlateBlue",        { 0x48, 0x3D, 0x8B } },
184     { "DarkSlateGray",        { 0x2F, 0x4F, 0x4F } },
185     { "DarkTurquoise",        { 0x00, 0xCE, 0xD1 } },
186     { "DarkViolet",           { 0x94, 0x00, 0xD3 } },
187     { "DeepPink",             { 0xFF, 0x14, 0x93 } },
188     { "DeepSkyBlue",          { 0x00, 0xBF, 0xFF } },
189     { "DimGray",              { 0x69, 0x69, 0x69 } },
190     { "DodgerBlue",           { 0x1E, 0x90, 0xFF } },
191     { "FireBrick",            { 0xB2, 0x22, 0x22 } },
192     { "FloralWhite",          { 0xFF, 0xFA, 0xF0 } },
193     { "ForestGreen",          { 0x22, 0x8B, 0x22 } },
194     { "Fuchsia",              { 0xFF, 0x00, 0xFF } },
195     { "Gainsboro",            { 0xDC, 0xDC, 0xDC } },
196     { "GhostWhite",           { 0xF8, 0xF8, 0xFF } },
197     { "Gold",                 { 0xFF, 0xD7, 0x00 } },
198     { "GoldenRod",            { 0xDA, 0xA5, 0x20 } },
199     { "Gray",                 { 0x80, 0x80, 0x80 } },
200     { "Green",                { 0x00, 0x80, 0x00 } },
201     { "GreenYellow",          { 0xAD, 0xFF, 0x2F } },
202     { "HoneyDew",             { 0xF0, 0xFF, 0xF0 } },
203     { "HotPink",              { 0xFF, 0x69, 0xB4 } },
204     { "IndianRed",            { 0xCD, 0x5C, 0x5C } },
205     { "Indigo",               { 0x4B, 0x00, 0x82 } },
206     { "Ivory",                { 0xFF, 0xFF, 0xF0 } },
207     { "Khaki",                { 0xF0, 0xE6, 0x8C } },
208     { "Lavender",             { 0xE6, 0xE6, 0xFA } },
209     { "LavenderBlush",        { 0xFF, 0xF0, 0xF5 } },
210     { "LawnGreen",            { 0x7C, 0xFC, 0x00 } },
211     { "LemonChiffon",         { 0xFF, 0xFA, 0xCD } },
212     { "LightBlue",            { 0xAD, 0xD8, 0xE6 } },
213     { "LightCoral",           { 0xF0, 0x80, 0x80 } },
214     { "LightCyan",            { 0xE0, 0xFF, 0xFF } },
215     { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
216     { "LightGrey",            { 0xD3, 0xD3, 0xD3 } },
217     { "LightGreen",           { 0x90, 0xEE, 0x90 } },
218     { "LightPink",            { 0xFF, 0xB6, 0xC1 } },
219     { "LightSalmon",          { 0xFF, 0xA0, 0x7A } },
220     { "LightSeaGreen",        { 0x20, 0xB2, 0xAA } },
221     { "LightSkyBlue",         { 0x87, 0xCE, 0xFA } },
222     { "LightSlateGray",       { 0x77, 0x88, 0x99 } },
223     { "LightSteelBlue",       { 0xB0, 0xC4, 0xDE } },
224     { "LightYellow",          { 0xFF, 0xFF, 0xE0 } },
225     { "Lime",                 { 0x00, 0xFF, 0x00 } },
226     { "LimeGreen",            { 0x32, 0xCD, 0x32 } },
227     { "Linen",                { 0xFA, 0xF0, 0xE6 } },
228     { "Magenta",              { 0xFF, 0x00, 0xFF } },
229     { "Maroon",               { 0x80, 0x00, 0x00 } },
230     { "MediumAquaMarine",     { 0x66, 0xCD, 0xAA } },
231     { "MediumBlue",           { 0x00, 0x00, 0xCD } },
232     { "MediumOrchid",         { 0xBA, 0x55, 0xD3 } },
233     { "MediumPurple",         { 0x93, 0x70, 0xD8 } },
234     { "MediumSeaGreen",       { 0x3C, 0xB3, 0x71 } },
235     { "MediumSlateBlue",      { 0x7B, 0x68, 0xEE } },
236     { "MediumSpringGreen",    { 0x00, 0xFA, 0x9A } },
237     { "MediumTurquoise",      { 0x48, 0xD1, 0xCC } },
238     { "MediumVioletRed",      { 0xC7, 0x15, 0x85 } },
239     { "MidnightBlue",         { 0x19, 0x19, 0x70 } },
240     { "MintCream",            { 0xF5, 0xFF, 0xFA } },
241     { "MistyRose",            { 0xFF, 0xE4, 0xE1 } },
242     { "Moccasin",             { 0xFF, 0xE4, 0xB5 } },
243     { "NavajoWhite",          { 0xFF, 0xDE, 0xAD } },
244     { "Navy",                 { 0x00, 0x00, 0x80 } },
245     { "OldLace",              { 0xFD, 0xF5, 0xE6 } },
246     { "Olive",                { 0x80, 0x80, 0x00 } },
247     { "OliveDrab",            { 0x6B, 0x8E, 0x23 } },
248     { "Orange",               { 0xFF, 0xA5, 0x00 } },
249     { "OrangeRed",            { 0xFF, 0x45, 0x00 } },
250     { "Orchid",               { 0xDA, 0x70, 0xD6 } },
251     { "PaleGoldenRod",        { 0xEE, 0xE8, 0xAA } },
252     { "PaleGreen",            { 0x98, 0xFB, 0x98 } },
253     { "PaleTurquoise",        { 0xAF, 0xEE, 0xEE } },
254     { "PaleVioletRed",        { 0xD8, 0x70, 0x93 } },
255     { "PapayaWhip",           { 0xFF, 0xEF, 0xD5 } },
256     { "PeachPuff",            { 0xFF, 0xDA, 0xB9 } },
257     { "Peru",                 { 0xCD, 0x85, 0x3F } },
258     { "Pink",                 { 0xFF, 0xC0, 0xCB } },
259     { "Plum",                 { 0xDD, 0xA0, 0xDD } },
260     { "PowderBlue",           { 0xB0, 0xE0, 0xE6 } },
261     { "Purple",               { 0x80, 0x00, 0x80 } },
262     { "Red",                  { 0xFF, 0x00, 0x00 } },
263     { "RosyBrown",            { 0xBC, 0x8F, 0x8F } },
264     { "RoyalBlue",            { 0x41, 0x69, 0xE1 } },
265     { "SaddleBrown",          { 0x8B, 0x45, 0x13 } },
266     { "Salmon",               { 0xFA, 0x80, 0x72 } },
267     { "SandyBrown",           { 0xF4, 0xA4, 0x60 } },
268     { "SeaGreen",             { 0x2E, 0x8B, 0x57 } },
269     { "SeaShell",             { 0xFF, 0xF5, 0xEE } },
270     { "Sienna",               { 0xA0, 0x52, 0x2D } },
271     { "Silver",               { 0xC0, 0xC0, 0xC0 } },
272     { "SkyBlue",              { 0x87, 0xCE, 0xEB } },
273     { "SlateBlue",            { 0x6A, 0x5A, 0xCD } },
274     { "SlateGray",            { 0x70, 0x80, 0x90 } },
275     { "Snow",                 { 0xFF, 0xFA, 0xFA } },
276     { "SpringGreen",          { 0x00, 0xFF, 0x7F } },
277     { "SteelBlue",            { 0x46, 0x82, 0xB4 } },
278     { "Tan",                  { 0xD2, 0xB4, 0x8C } },
279     { "Teal",                 { 0x00, 0x80, 0x80 } },
280     { "Thistle",              { 0xD8, 0xBF, 0xD8 } },
281     { "Tomato",               { 0xFF, 0x63, 0x47 } },
282     { "Turquoise",            { 0x40, 0xE0, 0xD0 } },
283     { "Violet",               { 0xEE, 0x82, 0xEE } },
284     { "Wheat",                { 0xF5, 0xDE, 0xB3 } },
285     { "White",                { 0xFF, 0xFF, 0xFF } },
286     { "WhiteSmoke",           { 0xF5, 0xF5, 0xF5 } },
287     { "Yellow",               { 0xFF, 0xFF, 0x00 } },
288     { "YellowGreen",          { 0x9A, 0xCD, 0x32 } },
289 };
290
291 static int color_table_compare(const void *lhs, const void *rhs)
292 {
293     return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
294 }
295
296 #define ALPHA_SEP '@'
297
298 int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen,
299                    void *log_ctx)
300 {
301     char *tail, color_string2[128];
302     const ColorEntry *entry;
303     int len, hex_offset = 0;
304
305     if (color_string[0] == '#') {
306         hex_offset = 1;
307     } else if (!strncmp(color_string, "0x", 2))
308         hex_offset = 2;
309
310     if (slen < 0)
311         slen = strlen(color_string);
312     av_strlcpy(color_string2, color_string + hex_offset,
313                FFMIN(slen-hex_offset+1, sizeof(color_string2)));
314     if ((tail = strchr(color_string2, ALPHA_SEP)))
315         *tail++ = 0;
316     len = strlen(color_string2);
317     rgba_color[3] = 255;
318
319     if (!strcasecmp(color_string2, "random") || !strcasecmp(color_string2, "bikeshed")) {
320         int rgba = av_get_random_seed();
321         rgba_color[0] = rgba >> 24;
322         rgba_color[1] = rgba >> 16;
323         rgba_color[2] = rgba >> 8;
324         rgba_color[3] = rgba;
325     } else if (hex_offset ||
326                strspn(color_string2, "0123456789ABCDEFabcdef") == len) {
327         char *tail;
328         unsigned int rgba = strtoul(color_string2, &tail, 16);
329
330         if (*tail || (len != 6 && len != 8)) {
331             av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
332             return AVERROR(EINVAL);
333         }
334         if (len == 8) {
335             rgba_color[3] = rgba;
336             rgba >>= 8;
337         }
338         rgba_color[0] = rgba >> 16;
339         rgba_color[1] = rgba >> 8;
340         rgba_color[2] = rgba;
341     } else {
342         entry = bsearch(color_string2,
343                         color_table,
344                         FF_ARRAY_ELEMS(color_table),
345                         sizeof(ColorEntry),
346                         color_table_compare);
347         if (!entry) {
348             av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
349             return AVERROR(EINVAL);
350         }
351         memcpy(rgba_color, entry->rgb_color, 3);
352     }
353
354     if (tail) {
355         unsigned long int alpha;
356         const char *alpha_string = tail;
357         if (!strncmp(alpha_string, "0x", 2)) {
358             alpha = strtoul(alpha_string, &tail, 16);
359         } else {
360             alpha = 255 * strtod(alpha_string, &tail);
361         }
362
363         if (tail == alpha_string || *tail || alpha > 255) {
364             av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
365                    alpha_string, color_string);
366             return AVERROR(EINVAL);
367         }
368         rgba_color[3] = alpha;
369     }
370
371     return 0;
372 }
373
374 #ifdef TEST
375
376 #undef printf
377
378 int main(void)
379 {
380     printf("Testing av_parse_video_rate()\n");
381     {
382         int i;
383         const char *rates[] = {
384             "-inf",
385             "inf",
386             "nan",
387             "123/0",
388             "-123 / 0",
389             "",
390             "/",
391             " 123  /  321",
392             "foo/foo",
393             "foo/1",
394             "1/foo",
395             "0/0",
396             "/0",
397             "1/",
398             "1",
399             "0",
400             "-123/123",
401             "-foo",
402             "123.23",
403             ".23",
404             "-.23",
405             "-0.234",
406             "-0.0000001",
407             "  21332.2324   ",
408             " -21332.2324   ",
409         };
410
411         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
412             int ret;
413             AVRational q = (AVRational){0, 0};
414             ret = av_parse_video_rate(&q, rates[i]),
415             printf("'%s' -> %d/%d ret:%d\n",
416                    rates[i], q.num, q.den, ret);
417         }
418     }
419
420     printf("\nTesting av_parse_color()\n");
421     {
422         int i;
423         uint8_t rgba[4];
424         const char *color_names[] = {
425             "bikeshed",
426             "RaNdOm",
427             "foo",
428             "red",
429             "Red ",
430             "RED",
431             "Violet",
432             "Yellow",
433             "Red",
434             "0x000000",
435             "0x0000000",
436             "0xff000000",
437             "0x3e34ff",
438             "0x3e34ffaa",
439             "0xffXXee",
440             "0xfoobar",
441             "0xffffeeeeeeee",
442             "#ff0000",
443             "#ffXX00",
444             "ff0000",
445             "ffXX00",
446             "red@foo",
447             "random@10",
448             "0xff0000@1.0",
449             "red@",
450             "red@0xfff",
451             "red@0xf",
452             "red@2",
453             "red@0.1",
454             "red@-1",
455             "red@0.5",
456             "red@1.0",
457             "red@256",
458             "red@10foo",
459             "red@-1.0",
460             "red@-0.0",
461         };
462
463         av_log_set_level(AV_LOG_DEBUG);
464
465         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
466             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
467                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
468         }
469     }
470
471     return 0;
472 }
473
474 #endif /* TEST */