PR #25248 opened by rnissl
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25248
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25248.patch

Follow-up to #25156.

The hex `sidecar` value does not fit a real Windows command line. One APP 
record is a few hundred hex characters, and a ride selects thousands of frames. 
See the note on #25156.

`av_opt_set_from_string()` now follows the filter rule: a name that starts with 
`/` is a path. Binary options receive the raw bytes, so 
`jpegapp=/sidecar=app.bin` passes the length-prefixed sidecar. 
`jpegapp=sidecar=<hex>` is unchanged. A colon in the path is escaped with a 
backslash.



>From 3e22a3878f4c05d3a7ffd1c0f8883e368da4a908 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= <[email protected]>
Date: Tue, 20 Oct 2026 09:49:02 +0200
Subject: [PATCH] avutil/opt: load option values from a file

A name that starts with '/' is a path, as with filter options. Binary
options receive the raw bytes, so jpegapp=/sidecar=app.bin passes a
length-prefixed sidecar that does not fit on the command line as hex.
---
 doc/bitstream_filters.texi |  8 +++++-
 libavcodec/bsf/jpegapp.c   |  2 +-
 libavutil/opt.c            | 50 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index cc1cd453a73..ed031e37ef8 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -635,7 +635,12 @@ the two bytes immediately following SOI, that APP0 segment 
is dropped.
 APP0 segments preceded by fill bytes or another segment are retained.
 An APP1-only payload keeps the encoder APP0: SOI, APP1, APP0, DQT, ...
 
-The filter requires @option{sidecar}. Extra unused sidecar bytes are
+The filter requires @option{sidecar}. In an option string, a name with a
+leading slash loads the value from a file, as filter options do. A binary
+option receives those bytes unchanged, so a length-prefixed sidecar is
+@code{jpegapp=/sidecar=app.bin}. A colon in the path is escaped with a
+backslash. The hex form remains available for a short sidecar.
+Extra unused sidecar bytes are
 ignored; a packet without a matching record is an error.
 Records follow packets in arrival order. Flushing the filter does not
 rewind the sidecar, so a reused context needs a sidecar positioned for
@@ -646,6 +651,7 @@ matching the remaining packets. Flushing does not clear 
this error.
 
 @example
 ffmpeg -i in.mp4 -c:v mjpeg -bsf:v jpegapp=sidecar=00000004DEADBEEF 
frame_%d.jpg
+ffmpeg -i in.mp4 -c:v mjpeg -bsf:v jpegapp=/sidecar=app.bin frame_%d.jpg
 @end example
 
 @section mjpeg2jpeg
diff --git a/libavcodec/bsf/jpegapp.c b/libavcodec/bsf/jpegapp.c
index 8024b77d469..1b3c57172ba 100644
--- a/libavcodec/bsf/jpegapp.c
+++ b/libavcodec/bsf/jpegapp.c
@@ -217,7 +217,7 @@ fail:
 #define OFFSET(x) offsetof(JPEGAppContext, x)
 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM)
 static const AVOption jpegapp_options[] = {
-    { "sidecar", "sidecar of u32be-length + APP-segment records", 
OFFSET(sidecar),
+    { "sidecar", "sidecar of u32be-length + APP-segment records, or 
/sidecar=path", OFFSET(sidecar),
       AV_OPT_TYPE_BINARY, .flags = FLAGS },
     { NULL },
 };
diff --git a/libavutil/opt.c b/libavutil/opt.c
index 0f69e04b3b5..4740e5eac00 100644
--- a/libavutil/opt.c
+++ b/libavutil/opt.c
@@ -31,6 +31,7 @@
 #include "channel_layout.h"
 #include "dict.h"
 #include "eval.h"
+#include "file.h"
 #include "log.h"
 #include "mem.h"
 #include "parseutils.h"
@@ -2015,7 +2016,54 @@ int av_opt_set_from_string(void *ctx, const char *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 (key && key[0] == '/' && key[1]) {
+            const char *name = key + 1;
+            const AVOption *o = av_opt_find(ctx, name, NULL, 0, 0);
+            uint8_t *data = NULL;
+            size_t size = 0;
+
+            /* Same rule as filter options: a leading '/' loads the value
+             * from a file. Binary options receive the raw bytes. */
+            if (!o) {
+                av_log(ctx, AV_LOG_ERROR, "Option '%s' not found\n", name);
+                av_free(value);
+                av_free(parsed_key);
+                return AVERROR_OPTION_NOT_FOUND;
+            }
+            ret = av_file_map(value, &data, &size, 0, ctx);
+            if (ret < 0) {
+                av_free(value);
+                av_free(parsed_key);
+                return ret;
+            }
+            if (size > INT_MAX) {
+                av_file_unmap(data, size);
+                av_free(value);
+                av_free(parsed_key);
+                return AVERROR(EINVAL);
+            }
+            if (o->type == AV_OPT_TYPE_BINARY) {
+                ret = av_opt_set_bin(ctx, name, data ? data : (const uint8_t 
*)"",
+                                     (int)size, 0);
+            } else {
+                char *text = av_malloc(size + 1);
+
+                if (!text) {
+                    av_file_unmap(data, size);
+                    av_free(value);
+                    av_free(parsed_key);
+                    return AVERROR(ENOMEM);
+                }
+                if (size && data)
+                    memcpy(text, data, size);
+                text[size] = 0;
+                ret = av_opt_set(ctx, name, text, 0);
+                av_free(text);
+            }
+            av_file_unmap(data, size);
+        } else
+            ret = av_opt_set(ctx, key, value, 0);
+        if (ret < 0) {
             if (ret == AVERROR_OPTION_NOT_FOUND)
                 av_log(ctx, AV_LOG_ERROR, "Option '%s' not found\n", key);
             av_free(value);
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to