From: James Almer <jamrial@gmail.com> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder Date: Tue, 18 Feb 2025 10:08:04 -0300 Message-ID: <20250218130813.74-2-jamrial@gmail.com> (raw) In-Reply-To: <20250218130813.74-1-jamrial@gmail.com> In some cases there are compression and resource usage benefits from changing certain encoder values at runtime over of closing, freeing, allocating and initializing a whole new encoder. The most obvious case is rate control, quality settings, and such. As such, this commit introduces a new function that attempts to reconfigure an opened encoder with a dictionary of options. Private options that can be changed are flagged with the runtime AVOption flag. Global options that can be changed, knowing that each encoder may support some but not others and as such can't be flagged in the global AVOptions struct, are signaled the same way through a new field in the FFCodecDefault struct. An encoder may provide a callback function that will be called for the reconfiguration process. Simple encoders that can work with only the AVOptions being reset and don't require any extra steps may just let the default function be called instead. Signed-off-by: James Almer <jamrial@gmail.com> --- libavcodec/avcodec.h | 19 +++++++ libavcodec/codec.h | 6 +++ libavcodec/codec_internal.h | 8 +++ libavcodec/encode.c | 98 +++++++++++++++++++++++++++++++++++++ libavcodec/encode.h | 4 ++ 5 files changed, 135 insertions(+) diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 403f02d841..7c25671161 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2417,6 +2417,25 @@ attribute_deprecated int avcodec_close(AVCodecContext *avctx); #endif +/** + * Try to reconfigure the encoder with the provided dictionary. May only be used + * if a codec with AV_CODEC_CAP_RECONF has been opened. + * + * Not all options can be changed, and it depends on the encoder. If any of the + * options can't be applied (Either because the option can't be changed, because + * invalid values for them were passed, or other errors), the encoder remains + * untouched and can continue as normal. Unapplied options will remain in *dict, + * and owned by the caller. + * + * @retval 0 success + * @retval AVERROR_OPTION_NOT_FOUND an entry with an invalid key was passed. + * @retval AVERROR(EINVAL) an entry with an invalid value or an invalid + * argument was passed. + * @retval AVERROR(ENOSYS) unsupported encoder. + * @retval "another negative error code" other errors. + */ +int avcodec_encode_reconfigure(AVCodecContext *avctx, AVDictionary **dict); + /** * Free all allocated data in the given subtitle struct. * diff --git a/libavcodec/codec.h b/libavcodec/codec.h index f7541ffc42..9dd4a9f018 100644 --- a/libavcodec/codec.h +++ b/libavcodec/codec.h @@ -50,6 +50,12 @@ * avcodec_default_get_buffer2 or avcodec_default_get_encode_buffer. */ #define AV_CODEC_CAP_DR1 (1 << 1) + +/** + * Encoder can be reconfigured by passing new initialization parameters. + */ +#define AV_CODEC_CAP_RECONF (1 << 2) + /** * Encoder or decoder requires flushing with NULL input at the end in order to * give the complete and correct output. diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h index 5b2db74590..08739651eb 100644 --- a/libavcodec/codec_internal.h +++ b/libavcodec/codec_internal.h @@ -97,9 +97,11 @@ typedef struct FFCodecDefault { const char *key; const char *value; + int flags; } FFCodecDefault; struct AVCodecContext; +struct AVDictionary; struct AVSubtitle; struct AVPacket; @@ -243,6 +245,12 @@ typedef struct FFCodec { */ void (*flush)(struct AVCodecContext *); + /** + * Reconfigure the encoder + * Called by avcodec_encode_reconfigure() + */ + int (*reconf)(struct AVCodecContext *avctx, struct AVDictionary **dict); + /** * Decoding only, a comma-separated list of bitstream filters to apply to * packets before decoding. diff --git a/libavcodec/encode.c b/libavcodec/encode.c index cd10dcf3cd..fcc87662f1 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -26,6 +26,7 @@ #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/mem.h" +#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/samplefmt.h" @@ -559,6 +560,103 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket * return 0; } +av_cold int ff_encode_reconf_parse_dict(AVCodecContext *avctx, AVDictionary **dict) +{ + const AVCodec *codec = avctx->codec; + const FFCodec *codec2 = ffcodec(codec); + const AVDictionaryEntry *t = NULL; + AVDictionary *copy = NULL; + int ret; + + av_assert0(av_codec_is_encoder(codec) && (codec->capabilities & AV_CODEC_CAP_RECONF)); + + ret = av_dict_copy(©, *dict, 0); + if (ret < 0) + goto end; + + // Remove the dictionary entries that would be applied to the private codec context + if (codec->priv_class) { + while ((t = av_dict_iterate(*dict, t))) { + if (av_opt_find(avctx->priv_data, t->key, NULL, + AV_OPT_FLAG_RUNTIME_PARAM, 0)) + av_dict_set(dict, t->key, NULL, 0); + } + } + + // Ditto for global options + if (codec2->defaults) { + const FFCodecDefault *d = codec2->defaults; + while (d->key) { + if (d->flags & AV_OPT_FLAG_RUNTIME_PARAM) + av_dict_set(dict, d->key, NULL, 0); + d++; + } + } + + // If any entry remains, then the requested option/s don't exist or are not settable. + if (av_dict_count(*dict)) { + ret = AVERROR_OPTION_NOT_FOUND; + goto end; + } + + ret = av_dict_copy(dict, copy, 0); + if (ret < 0) + goto end; + + // Do a dry run of applying the options, to ensure the encoder is unchanged in case + // one of them has an invalid value. + // This is done twice, once for avctx and once for the AVCodec, because using the + // search children flag in combination with the fake obj flag will iterate through + // the options from all compiled in codecs if you pass the avctx class. + if (codec->priv_class) { + ret = av_opt_set_dict2((void *)&codec->priv_class, ©, AV_OPT_SEARCH_FAKE_OBJ); + if (ret < 0) + goto end; + } + ret = av_opt_set_dict2((void *)&avctx->av_class, ©, AV_OPT_SEARCH_FAKE_OBJ); + if (ret < 0) + goto end; + + // The dictionary should be empty. + av_assert0(!av_dict_count(copy)); + + ret = av_opt_set_dict2(avctx, dict, AV_OPT_SEARCH_CHILDREN); + if (ret < 0) + goto end; + + // The dictionary should be empty. + av_assert0(!av_dict_count(*dict)); + + ret = 0; +end: + av_dict_free(©); + + return ret; +} + +av_cold int avcodec_encode_reconfigure(AVCodecContext *avctx, AVDictionary **dict) +{ + const FFCodec *codec = ffcodec(avctx->codec); + int ret = AVERROR_BUG; + + if (!avcodec_is_open(avctx) || !av_codec_is_encoder(avctx->codec)) + return AVERROR(EINVAL); + + if (!(avctx->codec->capabilities & AV_CODEC_CAP_RECONF)) { + av_log(avctx, AV_LOG_ERROR, "This encoder does not support reconfiguration\n"); + return AVERROR(ENOSYS); + } + + if (codec->reconf) + ret = codec->reconf(avctx, dict); + else + ret = ff_encode_reconf_parse_dict(avctx, dict); + if (ret < 0) + return ret; + + return 0; +} + static int encode_preinit_video(AVCodecContext *avctx) { const AVCodec *c = avctx->codec; diff --git a/libavcodec/encode.h b/libavcodec/encode.h index 656da135d2..76d22fd31a 100644 --- a/libavcodec/encode.h +++ b/libavcodec/encode.h @@ -21,6 +21,8 @@ #ifndef AVCODEC_ENCODE_H #define AVCODEC_ENCODE_H +#include "libavutil/opt.h" +#include "libavutil/dict.h" #include "libavutil/frame.h" #include "avcodec.h" @@ -79,6 +81,8 @@ int ff_encode_reordered_opaque(AVCodecContext *avctx, int ff_encode_encode_cb(AVCodecContext *avctx, AVPacket *avpkt, AVFrame *frame, int *got_packet); +int ff_encode_reconf_parse_dict(AVCodecContext *avctx, AVDictionary **dict); + /** * Add a CPB properties side data to an encoding context. */ -- 2.48.1 _______________________________________________ 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".
next prev parent reply other threads:[~2025-02-18 13:08 UTC|newest] Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top 2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer 2025-02-18 13:08 ` James Almer [this message] 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 03/11] avcodec/libx264: refactor encoder configuration functions James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 04/11] avcodec/libx264: add support for encoder reconfiguration James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 05/11] avcodec/libaomenc: refactor encoder configuration functions James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 06/11] avcodec/libaomenc: add support for encoder reconfiguration James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 07/11] fftools/ffmpeg_filter: add AVOptions to OutputFilter James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 08/11] fftools/ffmpeg_enc: split off parts of enc_open() into a separate function James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 09/11] fftools/ffmpeg_enc: split off encoder flushing in encoder_thread() " James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 10/11] fftools/ffmpeg_enc: store a few more AVCodecContext fields in Encoder James Almer 2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 11/11] fftools/ffmpeg: support reinitializing the encoder James Almer
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=20250218130813.74-2-jamrial@gmail.com \ --to=jamrial@gmail.com \ --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