]> git.sesse.net Git - ffmpeg/blobdiff - libavfilter/parseutils.c
Stefanos port was missing DIRS variable in the Makefile for the newly added x86
[ffmpeg] / libavfilter / parseutils.c
index 85effb06c118a33e3f3c76ca65f760336ab8a04e..669ec9d298e70fce7e865fa29f315cf52b381ec5 100644 (file)
  */
 
 /**
- * @file libavfilter/parseutils.c
+ * @file
  * parsing utils
  */
 
 #include <strings.h>
 #include "libavutil/avutil.h"
+#include "libavutil/avstring.h"
 #include "libavutil/random_seed.h"
 #include "parseutils.h"
 
@@ -34,6 +35,7 @@ char *av_get_token(const char **buf, const char *term)
     char *out = av_malloc(strlen(*buf) + 1);
     char *ret= out, *end= out;
     const char *p = *buf;
+    if (!out) return NULL;
     p += strspn(p, WHITESPACES);
 
     while(*p && !strspn(p, term)) {
@@ -64,7 +66,7 @@ char *av_get_token(const char **buf, const char *term)
 
 typedef struct {
     const char *name;            ///< a string representing the name of the color
-    uint8_t     rgba_color[4];   ///< RGBA values for the color
+    uint8_t     rgb_color[3];    ///< RGB values for the color
 } ColorEntry;
 
 static ColorEntry color_table[] = {
@@ -215,23 +217,31 @@ static int color_table_compare(const void *lhs, const void *rhs)
     return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
 }
 
+#define ALPHA_SEP '@'
+
 int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
 {
-    if (!strcasecmp(color_string, "random") || !strcasecmp(color_string, "bikeshed")) {
-        int rgba = ff_random_get_seed();
+    char *tail, color_string2[128];
+    const ColorEntry *entry;
+    av_strlcpy(color_string2, color_string, sizeof(color_string2));
+    if ((tail = strchr(color_string2, ALPHA_SEP)))
+        *tail++ = 0;
+    rgba_color[3] = 255;
+
+    if (!strcasecmp(color_string2, "random") || !strcasecmp(color_string2, "bikeshed")) {
+        int rgba = av_get_random_seed();
         rgba_color[0] = rgba >> 24;
         rgba_color[1] = rgba >> 16;
         rgba_color[2] = rgba >> 8;
         rgba_color[3] = rgba;
-    } else
-    if (!strncmp(color_string, "0x", 2)) {
+    } else if (!strncmp(color_string2, "0x", 2)) {
         char *tail;
-        int len = strlen(color_string);
-        int rgba = strtol(color_string, &tail, 16);
+        int len = strlen(color_string2);
+        unsigned int rgba = strtoul(color_string2, &tail, 16);
 
         if (*tail || (len != 8 && len != 10)) {
-            av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string);
-            return -1;
+            av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
+            return AVERROR(EINVAL);
         }
         if (len == 10) {
             rgba_color[3] = rgba;
@@ -241,25 +251,146 @@ int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
         rgba_color[1] = rgba >> 8;
         rgba_color[2] = rgba;
     } else {
-        const ColorEntry *entry = bsearch(color_string,
-                                          color_table,
-                                          FF_ARRAY_ELEMS(color_table),
-                                          sizeof(ColorEntry),
-                                          color_table_compare);
+        entry = bsearch(color_string2,
+                        color_table,
+                        FF_ARRAY_ELEMS(color_table),
+                        sizeof(ColorEntry),
+                        color_table_compare);
         if (!entry) {
-            av_log(log_ctx, AV_LOG_DEBUG, "Cannot find color '%s'\n", color_string);
-            return -1;
+            av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
+            return AVERROR(EINVAL);
         }
-        memcpy(rgba_color, entry->rgba_color, 4);
+        memcpy(rgba_color, entry->rgb_color, 3);
+    }
+
+    if (tail) {
+        unsigned long int alpha;
+        const char *alpha_string = tail;
+        if (!strncmp(alpha_string, "0x", 2)) {
+            alpha = strtoul(alpha_string, &tail, 16);
+        } else {
+            alpha = strtoul(alpha_string, &tail, 10);
+            if (*tail) {
+                double d = strtod(alpha_string, &tail);
+                alpha = d * 255;
+            }
+        }
+
+        if (tail == alpha_string || *tail || alpha > 255) {
+            av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
+                   alpha_string, color_string);
+            return AVERROR(EINVAL);
+        }
+        rgba_color[3] = alpha;
     }
 
     return 0;
 }
 
+/**
+ * Store the value in the field in ctx that is named like key.
+ * ctx must be an AVClass context, storing is done using AVOptions.
+ *
+ * @param buf the string to parse, buf will be updated to point at the
+ * separator just after the parsed key/value pair
+ * @param key_val_sep a 0-terminated list of characters used to
+ * separate key from value
+ * @param pairs_sep a 0-terminated list of characters used to separate
+ * two pairs from each other
+ * @return 0 if the key/value pair has been successfully parsed and
+ * set, or a negative value corresponding to an AVERROR code in case
+ * of error:
+ * AVERROR(EINVAL) if the key/value pair cannot be parsed,
+ * the error code issued by av_set_string3() if the key/value pair
+ * cannot be set
+ */
+static int parse_key_value_pair(void *ctx, const char **buf,
+                                const char *key_val_sep, const char *pairs_sep)
+{
+    char *key = av_get_token(buf, key_val_sep);
+    char *val;
+    int ret;
+
+    if (*key && strspn(*buf, key_val_sep)) {
+        (*buf)++;
+        val = av_get_token(buf, pairs_sep);
+    } else {
+        av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
+        av_free(key);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);
+
+    ret = av_set_string3(ctx, key, val, 1, NULL);
+    if (ret == AVERROR(ENOENT))
+        av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);
+
+    av_free(key);
+    av_free(val);
+    return ret;
+}
+
+int av_set_options_string(void *ctx, const char *opts,
+                          const char *key_val_sep, const char *pairs_sep)
+{
+    int ret, count = 0;
+
+    while (*opts) {
+        if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
+            return ret;
+        count++;
+
+        if (*opts)
+            opts++;
+    }
+
+    return count;
+}
+
 #ifdef TEST
 
 #undef printf
 
+typedef struct TestContext
+{
+    const AVClass *class;
+    int num;
+    int toggle;
+    char *string;
+    int flags;
+    AVRational rational;
+} TestContext;
+
+#define OFFSET(x) offsetof(TestContext, x)
+
+#define TEST_FLAG_COOL 01
+#define TEST_FLAG_LAME 02
+#define TEST_FLAG_MU   04
+
+static const AVOption test_options[]= {
+{"num",      "set num",        OFFSET(num),      FF_OPT_TYPE_INT,      0,              0,        100                 },
+{"toggle",   "set toggle",     OFFSET(toggle),   FF_OPT_TYPE_INT,      0,              0,        1                   },
+{"rational", "set rational",   OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0,              0,        10                  },
+{"string",   "set string",     OFFSET(string),   FF_OPT_TYPE_STRING,   0,              CHAR_MIN, CHAR_MAX            },
+{"flags",    "set flags",      OFFSET(flags),    FF_OPT_TYPE_FLAGS,    0,              0,        INT_MAX, 0, "flags" },
+{"cool",     "set cool flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_COOL, INT_MIN,  INT_MAX, 0, "flags" },
+{"lame",     "set lame flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_LAME, INT_MIN,  INT_MAX, 0, "flags" },
+{"mu",       "set mu flag ",   0,                FF_OPT_TYPE_CONST,    TEST_FLAG_MU,   INT_MIN,  INT_MAX, 0, "flags" },
+{NULL},
+};
+
+static const char *test_get_name(void *ctx)
+{
+    return "test";
+}
+
+static const AVClass test_class = {
+    "TestContext",
+    test_get_name,
+    test_options
+};
+
 int main(void)
 {
     int i;
@@ -314,11 +445,27 @@ int main(void)
             "Red",
             "0x000000",
             "0x0000000",
+            "0xff000000",
             "0x3e34ff",
             "0x3e34ffaa",
             "0xffXXee",
             "0xfoobar",
             "0xffffeeeeeeee",
+            "red@foo",
+            "random@10",
+            "0xff0000@1.0",
+            "red@",
+            "red@0xfff",
+            "red@0xf",
+            "red@2",
+            "red@0.1",
+            "red@-1",
+            "red@0.5",
+            "red@1.0",
+            "red@256",
+            "red@10foo",
+            "red@-1.0",
+            "red@-0.0",
         };
 
         av_log_set_level(AV_LOG_DEBUG);
@@ -329,6 +476,45 @@ int main(void)
         }
     }
 
+    printf("\nTesting av_set_options_string()\n");
+    {
+        TestContext test_ctx;
+        const char *options[] = {
+            "",
+            ":",
+            "=",
+            "foo=:",
+            ":=foo",
+            "=foo",
+            "foo=",
+            "foo",
+            "foo=val",
+            "foo==val",
+            "toggle=:",
+            "string=:",
+            "toggle=1 : foo",
+            "toggle=100",
+            "toggle==1",
+            "flags=+mu-lame : num=42: toggle=0",
+            "num=42 : string=blahblah",
+            "rational=0 : rational=1/2 : rational=1/-1",
+            "rational=-1/0",
+        };
+
+        test_ctx.class = &test_class;
+        av_opt_set_defaults2(&test_ctx, 0, 0);
+        test_ctx.string = av_strdup("default");
+
+        av_log_set_level(AV_LOG_DEBUG);
+
+        for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
+            av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
+            if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
+                av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
+            printf("\n");
+        }
+    }
+
     return 0;
 }