Log with level AV_LOG_ERROR if av_parse_color() cannot recognize a
[ffmpeg.git] / libavfilter / parseutils.c
1 /*
2  * copyright (c) 2009 Stefano Sabatini
3  * This file is part of FFmpeg.
4  *
5  * FFmpeg is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * FFmpeg is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with FFmpeg; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19
20 /**
21  * @file libavfilter/parseutils.c
22  * parsing utils
23  */
24
25 #include <strings.h>
26 #include "libavutil/avutil.h"
27 #include "libavutil/random_seed.h"
28 #include "parseutils.h"
29
30 #define WHITESPACES " \n\t"
31
32 char *av_get_token(const char **buf, const char *term)
33 {
34     char *out = av_malloc(strlen(*buf) + 1);
35     char *ret= out, *end= out;
36     const char *p = *buf;
37     p += strspn(p, WHITESPACES);
38
39     while(*p && !strspn(p, term)) {
40         char c = *p++;
41         if(c == '\\' && *p){
42             *out++ = *p++;
43             end= out;
44         }else if(c == '\''){
45             while(*p && *p != '\'')
46                 *out++ = *p++;
47             if(*p){
48                 p++;
49                 end= out;
50             }
51         }else{
52             *out++ = c;
53         }
54     }
55
56     do{
57         *out-- = 0;
58     }while(out >= end && strspn(out, WHITESPACES));
59
60     *buf = p;
61
62     return ret;
63 }
64
65 typedef struct {
66     const char *name;            ///< a string representing the name of the color
67     uint8_t     rgba_color[4];   ///< RGBA values for the color
68 } ColorEntry;
69
70 static ColorEntry color_table[] = {
71     { "AliceBlue",            { 0xF0, 0xF8, 0xFF } },
72     { "AntiqueWhite",         { 0xFA, 0xEB, 0xD7 } },
73     { "Aqua",                 { 0x00, 0xFF, 0xFF } },
74     { "Aquamarine",           { 0x7F, 0xFF, 0xD4 } },
75     { "Azure",                { 0xF0, 0xFF, 0xFF } },
76     { "Beige",                { 0xF5, 0xF5, 0xDC } },
77     { "Bisque",               { 0xFF, 0xE4, 0xC4 } },
78     { "Black",                { 0x00, 0x00, 0x00 } },
79     { "BlanchedAlmond",       { 0xFF, 0xEB, 0xCD } },
80     { "Blue",                 { 0x00, 0x00, 0xFF } },
81     { "BlueViolet",           { 0x8A, 0x2B, 0xE2 } },
82     { "Brown",                { 0xA5, 0x2A, 0x2A } },
83     { "BurlyWood",            { 0xDE, 0xB8, 0x87 } },
84     { "CadetBlue",            { 0x5F, 0x9E, 0xA0 } },
85     { "Chartreuse",           { 0x7F, 0xFF, 0x00 } },
86     { "Chocolate",            { 0xD2, 0x69, 0x1E } },
87     { "Coral",                { 0xFF, 0x7F, 0x50 } },
88     { "CornflowerBlue",       { 0x64, 0x95, 0xED } },
89     { "Cornsilk",             { 0xFF, 0xF8, 0xDC } },
90     { "Crimson",              { 0xDC, 0x14, 0x3C } },
91     { "Cyan",                 { 0x00, 0xFF, 0xFF } },
92     { "DarkBlue",             { 0x00, 0x00, 0x8B } },
93     { "DarkCyan",             { 0x00, 0x8B, 0x8B } },
94     { "DarkGoldenRod",        { 0xB8, 0x86, 0x0B } },
95     { "DarkGray",             { 0xA9, 0xA9, 0xA9 } },
96     { "DarkGreen",            { 0x00, 0x64, 0x00 } },
97     { "DarkKhaki",            { 0xBD, 0xB7, 0x6B } },
98     { "DarkMagenta",          { 0x8B, 0x00, 0x8B } },
99     { "DarkOliveGreen",       { 0x55, 0x6B, 0x2F } },
100     { "Darkorange",           { 0xFF, 0x8C, 0x00 } },
101     { "DarkOrchid",           { 0x99, 0x32, 0xCC } },
102     { "DarkRed",              { 0x8B, 0x00, 0x00 } },
103     { "DarkSalmon",           { 0xE9, 0x96, 0x7A } },
104     { "DarkSeaGreen",         { 0x8F, 0xBC, 0x8F } },
105     { "DarkSlateBlue",        { 0x48, 0x3D, 0x8B } },
106     { "DarkSlateGray",        { 0x2F, 0x4F, 0x4F } },
107     { "DarkTurquoise",        { 0x00, 0xCE, 0xD1 } },
108     { "DarkViolet",           { 0x94, 0x00, 0xD3 } },
109     { "DeepPink",             { 0xFF, 0x14, 0x93 } },
110     { "DeepSkyBlue",          { 0x00, 0xBF, 0xFF } },
111     { "DimGray",              { 0x69, 0x69, 0x69 } },
112     { "DodgerBlue",           { 0x1E, 0x90, 0xFF } },
113     { "FireBrick",            { 0xB2, 0x22, 0x22 } },
114     { "FloralWhite",          { 0xFF, 0xFA, 0xF0 } },
115     { "ForestGreen",          { 0x22, 0x8B, 0x22 } },
116     { "Fuchsia",              { 0xFF, 0x00, 0xFF } },
117     { "Gainsboro",            { 0xDC, 0xDC, 0xDC } },
118     { "GhostWhite",           { 0xF8, 0xF8, 0xFF } },
119     { "Gold",                 { 0xFF, 0xD7, 0x00 } },
120     { "GoldenRod",            { 0xDA, 0xA5, 0x20 } },
121     { "Gray",                 { 0x80, 0x80, 0x80 } },
122     { "Green",                { 0x00, 0x80, 0x00 } },
123     { "GreenYellow",          { 0xAD, 0xFF, 0x2F } },
124     { "HoneyDew",             { 0xF0, 0xFF, 0xF0 } },
125     { "HotPink",              { 0xFF, 0x69, 0xB4 } },
126     { "IndianRed",            { 0xCD, 0x5C, 0x5C } },
127     { "Indigo",               { 0x4B, 0x00, 0x82 } },
128     { "Ivory",                { 0xFF, 0xFF, 0xF0 } },
129     { "Khaki",                { 0xF0, 0xE6, 0x8C } },
130     { "Lavender",             { 0xE6, 0xE6, 0xFA } },
131     { "LavenderBlush",        { 0xFF, 0xF0, 0xF5 } },
132     { "LawnGreen",            { 0x7C, 0xFC, 0x00 } },
133     { "LemonChiffon",         { 0xFF, 0xFA, 0xCD } },
134     { "LightBlue",            { 0xAD, 0xD8, 0xE6 } },
135     { "LightCoral",           { 0xF0, 0x80, 0x80 } },
136     { "LightCyan",            { 0xE0, 0xFF, 0xFF } },
137     { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
138     { "LightGrey",            { 0xD3, 0xD3, 0xD3 } },
139     { "LightGreen",           { 0x90, 0xEE, 0x90 } },
140     { "LightPink",            { 0xFF, 0xB6, 0xC1 } },
141     { "LightSalmon",          { 0xFF, 0xA0, 0x7A } },
142     { "LightSeaGreen",        { 0x20, 0xB2, 0xAA } },
143     { "LightSkyBlue",         { 0x87, 0xCE, 0xFA } },
144     { "LightSlateGray",       { 0x77, 0x88, 0x99 } },
145     { "LightSteelBlue",       { 0xB0, 0xC4, 0xDE } },
146     { "LightYellow",          { 0xFF, 0xFF, 0xE0 } },
147     { "Lime",                 { 0x00, 0xFF, 0x00 } },
148     { "LimeGreen",            { 0x32, 0xCD, 0x32 } },
149     { "Linen",                { 0xFA, 0xF0, 0xE6 } },
150     { "Magenta",              { 0xFF, 0x00, 0xFF } },
151     { "Maroon",               { 0x80, 0x00, 0x00 } },
152     { "MediumAquaMarine",     { 0x66, 0xCD, 0xAA } },
153     { "MediumBlue",           { 0x00, 0x00, 0xCD } },
154     { "MediumOrchid",         { 0xBA, 0x55, 0xD3 } },
155     { "MediumPurple",         { 0x93, 0x70, 0xD8 } },
156     { "MediumSeaGreen",       { 0x3C, 0xB3, 0x71 } },
157     { "MediumSlateBlue",      { 0x7B, 0x68, 0xEE } },
158     { "MediumSpringGreen",    { 0x00, 0xFA, 0x9A } },
159     { "MediumTurquoise",      { 0x48, 0xD1, 0xCC } },
160     { "MediumVioletRed",      { 0xC7, 0x15, 0x85 } },
161     { "MidnightBlue",         { 0x19, 0x19, 0x70 } },
162     { "MintCream",            { 0xF5, 0xFF, 0xFA } },
163     { "MistyRose",            { 0xFF, 0xE4, 0xE1 } },
164     { "Moccasin",             { 0xFF, 0xE4, 0xB5 } },
165     { "NavajoWhite",          { 0xFF, 0xDE, 0xAD } },
166     { "Navy",                 { 0x00, 0x00, 0x80 } },
167     { "OldLace",              { 0xFD, 0xF5, 0xE6 } },
168     { "Olive",                { 0x80, 0x80, 0x00 } },
169     { "OliveDrab",            { 0x6B, 0x8E, 0x23 } },
170     { "Orange",               { 0xFF, 0xA5, 0x00 } },
171     { "OrangeRed",            { 0xFF, 0x45, 0x00 } },
172     { "Orchid",               { 0xDA, 0x70, 0xD6 } },
173     { "PaleGoldenRod",        { 0xEE, 0xE8, 0xAA } },
174     { "PaleGreen",            { 0x98, 0xFB, 0x98 } },
175     { "PaleTurquoise",        { 0xAF, 0xEE, 0xEE } },
176     { "PaleVioletRed",        { 0xD8, 0x70, 0x93 } },
177     { "PapayaWhip",           { 0xFF, 0xEF, 0xD5 } },
178     { "PeachPuff",            { 0xFF, 0xDA, 0xB9 } },
179     { "Peru",                 { 0xCD, 0x85, 0x3F } },
180     { "Pink",                 { 0xFF, 0xC0, 0xCB } },
181     { "Plum",                 { 0xDD, 0xA0, 0xDD } },
182     { "PowderBlue",           { 0xB0, 0xE0, 0xE6 } },
183     { "Purple",               { 0x80, 0x00, 0x80 } },
184     { "Red",                  { 0xFF, 0x00, 0x00 } },
185     { "RosyBrown",            { 0xBC, 0x8F, 0x8F } },
186     { "RoyalBlue",            { 0x41, 0x69, 0xE1 } },
187     { "SaddleBrown",          { 0x8B, 0x45, 0x13 } },
188     { "Salmon",               { 0xFA, 0x80, 0x72 } },
189     { "SandyBrown",           { 0xF4, 0xA4, 0x60 } },
190     { "SeaGreen",             { 0x2E, 0x8B, 0x57 } },
191     { "SeaShell",             { 0xFF, 0xF5, 0xEE } },
192     { "Sienna",               { 0xA0, 0x52, 0x2D } },
193     { "Silver",               { 0xC0, 0xC0, 0xC0 } },
194     { "SkyBlue",              { 0x87, 0xCE, 0xEB } },
195     { "SlateBlue",            { 0x6A, 0x5A, 0xCD } },
196     { "SlateGray",            { 0x70, 0x80, 0x90 } },
197     { "Snow",                 { 0xFF, 0xFA, 0xFA } },
198     { "SpringGreen",          { 0x00, 0xFF, 0x7F } },
199     { "SteelBlue",            { 0x46, 0x82, 0xB4 } },
200     { "Tan",                  { 0xD2, 0xB4, 0x8C } },
201     { "Teal",                 { 0x00, 0x80, 0x80 } },
202     { "Thistle",              { 0xD8, 0xBF, 0xD8 } },
203     { "Tomato",               { 0xFF, 0x63, 0x47 } },
204     { "Turquoise",            { 0x40, 0xE0, 0xD0 } },
205     { "Violet",               { 0xEE, 0x82, 0xEE } },
206     { "Wheat",                { 0xF5, 0xDE, 0xB3 } },
207     { "White",                { 0xFF, 0xFF, 0xFF } },
208     { "WhiteSmoke",           { 0xF5, 0xF5, 0xF5 } },
209     { "Yellow",               { 0xFF, 0xFF, 0x00 } },
210     { "YellowGreen",          { 0x9A, 0xCD, 0x32 } },
211 };
212
213 static int color_table_compare(const void *lhs, const void *rhs)
214 {
215     return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
216 }
217
218 int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
219 {
220     if (!strcasecmp(color_string, "random") || !strcasecmp(color_string, "bikeshed")) {
221         int rgba = ff_random_get_seed();
222         rgba_color[0] = rgba >> 24;
223         rgba_color[1] = rgba >> 16;
224         rgba_color[2] = rgba >> 8;
225         rgba_color[3] = rgba;
226     } else
227     if (!strncmp(color_string, "0x", 2)) {
228         char *tail;
229         int len = strlen(color_string);
230         int rgba = strtol(color_string, &tail, 16);
231
232         if (*tail || (len != 8 && len != 10)) {
233             av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string);
234             return -1;
235         }
236         if (len == 10) {
237             rgba_color[3] = rgba;
238             rgba >>= 8;
239         }
240         rgba_color[0] = rgba >> 16;
241         rgba_color[1] = rgba >> 8;
242         rgba_color[2] = rgba;
243     } else {
244         const ColorEntry *entry = bsearch(color_string,
245                                           color_table,
246                                           FF_ARRAY_ELEMS(color_table),
247                                           sizeof(ColorEntry),
248                                           color_table_compare);
249         if (!entry) {
250             av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string);
251             return -1;
252         }
253         memcpy(rgba_color, entry->rgba_color, 4);
254     }
255
256     return 0;
257 }
258
259 /**
260  * Stores the value in the field in ctx that is named like key.
261  * ctx must be an AVClass context, storing is done using AVOptions.
262  *
263  * @param buf the string to parse, buf will be updated to point at the
264  * separator just after the parsed key/value pair
265  * @param key_val_sep a 0-terminated list of characters used to
266  * separate key from value
267  * @param pairs_sep a 0-terminated list of characters used to separate
268  * two pairs from each other
269  * @return 0 if the key/value pair has been successfully parsed and
270  * set, or a negative value corresponding to an AVERROR code in case
271  * of error:
272  * AVERROR(EINVAL) if the key/value pair cannot be parsed,
273  * the error code issued by av_set_string3() if the key/value pair
274  * cannot be set
275  */
276 static int parse_key_value_pair(void *ctx, const char **buf,
277                                 const char *key_val_sep, const char *pairs_sep)
278 {
279     char *key = av_get_token(buf, key_val_sep);
280     char *val;
281     int ret;
282
283     if (*key && strspn(*buf, key_val_sep)) {
284         (*buf)++;
285         val = av_get_token(buf, pairs_sep);
286     } else {
287         av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
288         av_free(key);
289         return AVERROR(EINVAL);
290     }
291
292     av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);
293
294     ret = av_set_string3(ctx, key, val, 1, NULL);
295
296     av_free(key);
297     av_free(val);
298     return ret;
299 }
300
301 int av_set_options_string(void *ctx, const char *opts,
302                           const char *key_val_sep, const char *pairs_sep)
303 {
304     int ret, count = 0;
305
306     while (*opts) {
307         if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
308             return ret;
309         count++;
310
311         if (*opts)
312             opts++;
313     }
314
315     return count;
316 }
317
318 #ifdef TEST
319
320 #undef printf
321
322 typedef struct TestContext
323 {
324     const AVClass *class;
325     int num;
326     int toggle;
327     char *string;
328     int flags;
329     AVRational rational;
330 } TestContext;
331
332 #define OFFSET(x) offsetof(TestContext, x)
333
334 #define TEST_FLAG_COOL 01
335 #define TEST_FLAG_LAME 02
336 #define TEST_FLAG_MU   04
337
338 static const AVOption test_options[]= {
339 {"num",      "set num",        OFFSET(num),      FF_OPT_TYPE_INT,      0,              0,        100                 },
340 {"toggle",   "set toggle",     OFFSET(toggle),   FF_OPT_TYPE_INT,      0,              0,        1                   },
341 {"rational", "set rational",   OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0,              0,        10                  },
342 {"string",   "set string",     OFFSET(string),   FF_OPT_TYPE_STRING,   0,              CHAR_MIN, CHAR_MAX            },
343 {"flags",    "set flags",      OFFSET(flags),    FF_OPT_TYPE_FLAGS,    0,              0,        INT_MAX, 0, "flags" },
344 {"cool",     "set cool flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_COOL, INT_MIN,  INT_MAX, 0, "flags" },
345 {"lame",     "set lame flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_LAME, INT_MIN,  INT_MAX, 0, "flags" },
346 {"mu",       "set mu flag ",   0,                FF_OPT_TYPE_CONST,    TEST_FLAG_MU,   INT_MIN,  INT_MAX, 0, "flags" },
347 {NULL},
348 };
349
350 static const char *test_get_name(void *ctx)
351 {
352     return "test";
353 }
354
355 static const AVClass test_class = {
356     "TestContext",
357     test_get_name,
358     test_options
359 };
360
361 int main(void)
362 {
363     int i;
364
365     const char *strings[] = {
366         "''",
367         "",
368         ":",
369         "\\",
370         "'",
371         "    ''    :",
372         "    ''  ''  :",
373         "foo   '' :",
374         "'foo'",
375         "foo     ",
376         "foo\\",
377         "foo':  blah:blah",
378         "foo\\:  blah:blah",
379         "foo\'",
380         "'foo :  '  :blahblah",
381         "\\ :blah",
382         "     foo",
383         "      foo       ",
384         "      foo     \\ ",
385         "foo ':blah",
386         " foo   bar    :   blahblah",
387         "\\f\\o\\o",
388         "'foo : \\ \\  '   : blahblah",
389         "'\\fo\\o:': blahblah",
390         "\\'fo\\o\\:':  foo  '  :blahblah"
391     };
392
393     for (i=0; i < FF_ARRAY_ELEMS(strings); i++) {
394         const char *p= strings[i];
395         printf("|%s|", p);
396         printf(" -> |%s|", av_get_token(&p, ":"));
397         printf(" + |%s|\n", p);
398     }
399
400     printf("\nTesting av_parse_color()\n");
401     {
402         uint8_t rgba[4];
403         const char *color_names[] = {
404             "bikeshed",
405             "RaNdOm",
406             "foo",
407             "red",
408             "Red ",
409             "RED",
410             "Violet",
411             "Yellow",
412             "Red",
413             "0x000000",
414             "0x0000000",
415             "0x3e34ff",
416             "0x3e34ffaa",
417             "0xffXXee",
418             "0xfoobar",
419             "0xffffeeeeeeee",
420         };
421
422         av_log_set_level(AV_LOG_DEBUG);
423
424         for (int i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
425             if (av_parse_color(rgba, color_names[i], NULL) >= 0)
426                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
427         }
428     }
429
430     printf("\nTesting av_set_options_string()\n");
431     {
432         TestContext test_ctx;
433         const char *options[] = {
434             "",
435             ":",
436             "=",
437             "foo=:",
438             ":=foo",
439             "=foo",
440             "foo=",
441             "foo",
442             "foo=val",
443             "foo==val",
444             "toggle=:",
445             "string=:",
446             "toggle=1 : foo",
447             "toggle=100",
448             "toggle==1",
449             "flags=+mu-lame : num=42: toggle=0",
450             "num=42 : string=blahblah",
451             "rational=0 : rational=1/2 : rational=1/-1",
452             "rational=-1/0",
453         };
454
455         test_ctx.class = &test_class;
456         av_opt_set_defaults2(&test_ctx, 0, 0);
457         test_ctx.string = av_strdup("default");
458
459         av_log_set_level(AV_LOG_DEBUG);
460
461         for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
462             av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
463             if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
464                 av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
465             printf("\n");
466         }
467     }
468
469     return 0;
470 }
471
472 #endif