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]
