[FFmpeg-devel] [PATCH] opt: implement av_opt_set_from_string().

Nicolas George nicolas.george at normalesup.org
Sun Sep 9 15:13:23 CEST 2012


It is similar to av_set_options_string() but accepts a list
of options that can be in shorthand: if the key is omitted
on the first fields, the keys from the shorthand list are
assumed, in order.

Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 libavutil/opt.c |  116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libavutil/opt.h |   26 +++++++++++++
 2 files changed, 142 insertions(+)


Reading the key directly instead of using the tokenizer makes the code,
IMHO, much simpler and the error reporting clearer. It does not limit the
user, as the key names are short and made of alphanumeric characters.

A small dilemma: if shorthand = { "a", "b", NULL }, how should ":42" be
parsed? As "a=:b=42" (setting both a and b) or as "b=42" (leaving a to its
default)? The problem is that since the value is parsed using the tokenizer,
we can not distinguish ":42" from "'':42".


diff --git a/libavutil/opt.c b/libavutil/opt.c
index 0957281..b800daa 100644
--- a/libavutil/opt.c
+++ b/libavutil/opt.c
@@ -790,6 +790,91 @@ int av_set_options_string(void *ctx, const char *opts,
     return count;
 }
 
+#define WHITESPACES " \n\t"
+
+static int is_key_char(char c)
+{
+    return (unsigned)((c | 32) - 'a') < 26 ||
+           (unsigned)(c - '0') < 10 ||
+           c == '-' || c == '_' || c == '/' || c == '.';
+}
+
+/**
+ * Read a key from a string. The key consists of is_key_char characters and
+ * is terminated by a character from the delim string; spaces are ignored.
+ * The key buffer must be 4 bytes larger than the longest acceptable key.
+ * If the key is too long, an ellipsis will be written at the end.
+ * @return  0 for success (even with ellipsis), <0 for failure
+ */
+static int get_key(const char **ropts, const char *delim, char *key, unsigned key_size)
+{
+    unsigned pos = 0;
+    const char *opts = *ropts;
+
+    opts += strspn(opts, WHITESPACES);
+    while (is_key_char(*opts)) {
+        key[pos++] = *opts;
+        if (pos == key_size)
+            pos--;
+        (opts)++;
+    }
+    opts += strspn(opts, WHITESPACES);
+    if (!*opts || !strchr(delim, *opts))
+        return AVERROR(EINVAL);
+    opts++;
+    key[pos++] = 0;
+    if (pos == key_size)
+        key[pos - 4] = key[pos - 3] = key[pos - 2] = '.';
+    *ropts = opts;
+    return 0;
+}
+
+int av_opt_set_from_string(void *ctx, const char *opts,
+                           const char *const *shorthand,
+                           const char *key_val_sep, const char *pairs_sep)
+{
+    int ret, count = 0;
+    const char *dummy_shorthand = NULL;
+    char key_buf[68], *value;
+    const char *key;
+
+    if (!opts)
+        return 0;
+    if (!shorthand)
+        shorthand = &dummy_shorthand;
+
+    while (*opts) {
+        if ((ret = get_key(&opts, key_val_sep, key_buf, sizeof(key_buf))) < 0) {
+            if (*shorthand) {
+                key = *(shorthand++);
+            } else {
+                av_log(ctx, AV_LOG_ERROR, "No option name near '%s'\n", opts);
+                return AVERROR(EINVAL);
+            }
+        } else {
+            key = key_buf;
+            while (*shorthand) /* discard all remaining shorthand */
+                shorthand++;
+        }
+
+        if (!(value = av_get_token(&opts, pairs_sep)))
+            return AVERROR(ENOMEM);
+        if (*opts && strchr(pairs_sep, *opts))
+            opts++;
+
+        av_log(ctx, AV_LOG_DEBUG, "Setting '%s' to value '%s'\n", key, value);
+        if ((ret = av_opt_set(ctx, key, value, 0)) < 0) {
+            if (ret == AVERROR_OPTION_NOT_FOUND)
+                av_log(ctx, AV_LOG_ERROR, "Option '%s' not found.\n", key);
+            return ret;
+        }
+
+        av_free(value);
+        count++;
+    }
+    return count;
+}
+
 void av_opt_free(void *obj)
 {
     const AVOption *o = NULL;
@@ -986,6 +1071,37 @@ int main(void)
         av_freep(&test_ctx.string);
     }
 
+    printf("\nTesting av_opt_set_from_string()\n");
+    {
+        TestContext test_ctx = { 0 };
+        const char *options[] = {
+            "",
+            "5",
+            "5:hello",
+            "5:hello:size=pal",
+            "5:size=pal:hello",
+            ":",
+            "=",
+            " 5 : hello : size = pal ",
+            "a_very_long_option_name_that_will_need_to_be_ellipsized_around_here=42"
+        };
+        const char *shorthand[] = { "num", "string", NULL };
+
+        test_ctx.class = &test_class;
+        av_opt_set_defaults(&test_ctx);
+        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_opt_set_from_string(&test_ctx, options[i], shorthand, "=", ":") < 0)
+                av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
+            printf("\n");
+        }
+        av_freep(&test_ctx.string);
+    }
+
     return 0;
 }
 
diff --git a/libavutil/opt.h b/libavutil/opt.h
index 285d854..5c9b35e 100644
--- a/libavutil/opt.h
+++ b/libavutil/opt.h
@@ -394,6 +394,32 @@ int av_set_options_string(void *ctx, const char *opts,
                           const char *key_val_sep, const char *pairs_sep);
 
 /**
+ * Parse the key=value pairs list in opts. For each key=value pair found,
+ * set the value of the corresponding option in ctx.
+ *
+ * @param ctx          the AVClass object to set options
+ * @param opts         the options string, key-value pairs separated by a
+ *                     delimiter
+ * @param shorthand    a NULL-terminated array of options names for shorthand
+ *                     notation: if the first field in opts has no key part,
+ *                     the key is taken from the first element of shorthand;
+ *                     then again for the second, etc., until either opts is
+ *                     finished, shorthand is finished or a named option is
+ *                     found; after that, all options must be named
+ * @param key_val_sep  a 0-terminated list of characters used to separate
+ *                     key from value, for example '='
+ * @param pairs_sep    a 0-terminated list of characters used to separate
+ *                     two pairs from each other, for example ':' or ','
+ * @return  the number of successfully set key=value pairs, or a negative
+ *          value corresponding to an AVERROR code in case of error:
+ *          AVERROR(EINVAL) if opts cannot be parsed,
+ *          the error code issued by av_set_string3() if a key/value pair
+ *          cannot be set
+ */
+int av_opt_set_from_string(void *ctx, const char *opts,
+                           const char *const *shorthand,
+                           const char *key_val_sep, const char *pairs_sep);
+/**
  * Free all string and binary options in obj.
  */
 void av_opt_free(void *obj);
-- 
1.7.10.4



More information about the ffmpeg-devel mailing list