Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
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(&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, &copy, AV_OPT_SEARCH_FAKE_OBJ);
+        if (ret < 0)
+            goto end;
+    }
+    ret = av_opt_set_dict2((void *)&avctx->av_class, &copy, 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(&copy);
+
+    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".

  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