Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Marvin Scholz <epirat07-at-gmail.com@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Subject: [FFmpeg-devel] [PATCH 1/2] avformat/tee: refactor option processing
Date: Wed, 25 Jun 2025 00:08:04 +0200
Message-ID: <20250624220805.50371-1-epirat07@gmail.com> (raw)

Instead of the convoluted nested macros, use a convenience
helper function. While this makes the code slightly longer, it
is now much clearer what is happening without running the file
through a preprocessor first.

Additionally do not mess with the internals of the dictionary,
just to save two string copies.
---
 libavformat/tee.c | 98 +++++++++++++++++++++++++++++++----------------
 1 file changed, 66 insertions(+), 32 deletions(-)

diff --git a/libavformat/tee.c b/libavformat/tee.c
index 0150ee9f22..0a6e9d0c82 100644
--- a/libavformat/tee.c
+++ b/libavformat/tee.c
@@ -80,8 +80,9 @@ static const AVClass tee_muxer_class = {
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
-static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave)
+static inline int parse_slave_failure_policy_option(const char *opt, void *ctx)
 {
+    TeeSlave *tee_slave = ctx;
     if (!opt) {
         tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY;
         return 0;
@@ -97,8 +98,9 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t
     return AVERROR(EINVAL);
 }
 
-static int parse_slave_fifo_policy(const char *use_fifo, TeeSlave *tee_slave)
+static int parse_slave_fifo_policy(const char *use_fifo, void *ctx)
 {
+    TeeSlave *tee_slave = ctx;
     /*TODO - change this to use proper function for parsing boolean
      *       options when there is one */
     if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) {
@@ -111,8 +113,9 @@ static int parse_slave_fifo_policy(const char *use_fifo, TeeSlave *tee_slave)
     return 0;
 }
 
-static int parse_slave_fifo_options(const char *fifo_options, TeeSlave *tee_slave)
+static int parse_slave_fifo_options(const char *fifo_options, void *ctx)
 {
+    TeeSlave *tee_slave = ctx;
     return av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0);
 }
 
@@ -152,6 +155,46 @@ static void close_slaves(AVFormatContext *avf)
     av_freep(&tee->slaves);
 }
 
+typedef int (*OptionParserFunc)(const char *value, void *context);
+
+/**
+ * Retrieve and optionally copy or process the given dict key's value
+ *
+ * The dictionary value for \p key is retrieved, if it exists the
+ * value is passed to the \p handler function together with the
+ * \c context, if any. If the handler returns an error, it is returned
+ * to the caller.
+ *
+ * If \c out_value is not NULL, the value is also copied and assigned
+ * to that.
+ *
+ * After processing, the key is removed from the dictionary.
+ */
+static int dict_consume_value(AVDictionary **dict, const char *key, char **out_value,
+    OptionParserFunc handler, void *context)
+{
+    const AVDictionaryEntry *entry = av_dict_get(*dict, key, NULL, 0);
+    if (!entry)
+        return 0;
+
+    if (handler) {
+        int ret = handler(entry->value, context);
+        if (ret < 0)
+            return ret;
+    }
+
+    if (out_value) {
+        char *value_copy = av_strdup(entry->value);
+        if (!value_copy)
+            return AVERROR(ENOMEM);
+
+        *out_value = value_copy;
+    }
+
+    av_dict_set(dict, key, NULL, 0);
+    return 0;
+}
+
 static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
 {
     int ret;
@@ -167,35 +210,26 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
     if ((ret = ff_tee_parse_slave_options(avf, slave, &options, &filename)) < 0)
         return ret;
 
-#define CONSUME_OPTION(option, field, action) do {                      \
-        AVDictionaryEntry *en = av_dict_get(options, option, NULL, 0);  \
-        if (en) {                                                       \
-            field = en->value;                                          \
-            { action }                                                  \
-            av_dict_set(&options, option, NULL, 0);                     \
-        }                                                               \
-    } while (0)
-#define STEAL_OPTION(option, field)                                     \
-    CONSUME_OPTION(option, field,                                       \
-                   en->value = NULL; /* prevent it from being freed */)
-#define PROCESS_OPTION(option, function, on_error) do {                 \
-        const char *value;                                              \
-        CONSUME_OPTION(option, value, if ((ret = function) < 0)         \
-                                          { { on_error } goto end; });  \
-    } while (0)
-
-    STEAL_OPTION("f", format);
-    STEAL_OPTION("select", select);
-    PROCESS_OPTION("onfail",
-                   parse_slave_failure_policy_option(value, tee_slave),
-                   av_log(avf, AV_LOG_ERROR, "Invalid onfail option value, "
-                          "valid options are 'abort' and 'ignore'\n"););
-    PROCESS_OPTION("use_fifo",
-                   parse_slave_fifo_policy(value, tee_slave),
-                   av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n",
-                          av_err2str(ret)););
-    PROCESS_OPTION("fifo_options",
-                   parse_slave_fifo_options(value, tee_slave), ;);
+    if ((ret = dict_consume_value(&options, "f", &format, NULL, NULL)) < 0)
+        goto end;
+    if ((ret = dict_consume_value(&options, "select", &select, NULL, NULL)) < 0)
+        goto end;
+    if ((ret = dict_consume_value(&options, "onfail", NULL,
+            parse_slave_failure_policy_option, tee_slave)) < 0) {
+        av_log(avf, AV_LOG_ERROR, "Invalid onfail option value, "
+            "valid options are 'abort' and 'ignore'\n");
+        goto end;
+    }
+    if ((ret = dict_consume_value(&options, "use_fifo", NULL,
+            parse_slave_fifo_policy, tee_slave)) < 0) {
+        av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n",
+            av_err2str(ret));
+        goto end;
+    }
+    if ((ret = dict_consume_value(&options, "fifo_options", NULL,
+            parse_slave_fifo_options, tee_slave)) < 0)
+        goto end;
+
     entry = NULL;
     while ((entry = av_dict_get(options, "bsfs", entry, AV_DICT_IGNORE_SUFFIX))) {
         /* trim out strlen("bsfs") characters from key */
-- 
2.39.5 (Apple Git-154)

_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".

             reply	other threads:[~2025-06-24 22:08 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-24 22:08 Marvin Scholz [this message]
2025-06-24 22:08 ` [FFmpeg-devel] [PATCH 2/2] avformat/tee: fix multiple bsfs in tee Marvin Scholz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250624220805.50371-1-epirat07@gmail.com \
    --to=epirat07-at-gmail.com@ffmpeg.org \
    --cc=ffmpeg-devel@ffmpeg.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git