From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id F06D14D1D9 for ; Tue, 18 Feb 2025 13:08:54 +0000 (UTC) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 35F1E68C147; Tue, 18 Feb 2025 15:08:41 +0200 (EET) Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 2D80368C0DD for ; Tue, 18 Feb 2025 15:08:34 +0200 (EET) Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-220c92c857aso85382645ad.0 for ; Tue, 18 Feb 2025 05:08:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1739884112; x=1740488912; darn=ffmpeg.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=NsHabo4N0UDPznSi/SheGAFiOKWBKPa1HXa+1sSdB88=; b=ajNdWuMjw4zTjeJoPg9uCqrdB5wNxaof7wiqdvdkkmtJSapG77X1Awun7SDajgTNmK eE89TQqtvKQ04/yy7FCA3wbuGIqZa0xgPPMJnQc5ZljT+cFMTiPT1T5t6TNYuEvk9Wda 0kboZO/DRRqiMh1hl2B4cOSiDdZ63N2lYtwFWJiNUIhISVR920qMRqqfebFQIkgEE56K PyXb9AiC5VBidsP4qqoTdPJ9Qs6EPJaA/YHUemka+Q9tpqDuDYpv+2Xsup2rS6A23hPs IxLy6HHnMPbU6wOgV44DuNavnvWWnvlboVE8bPh1kjlHJkzpNJ7R6SE9/Sxt0HmLqaEj lMPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1739884112; x=1740488912; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NsHabo4N0UDPznSi/SheGAFiOKWBKPa1HXa+1sSdB88=; b=ZBlONLQx3t3kkfz+7uBPpGjhqUrusNYguZTBjxQFZUi2aobOmVHSL4alYiuRs0nefC l/tKa85SLrx4WSoRMZmF3K/bwQvADEWNkO1x6WSFSaOYvyleM7yMGKhDso24j9ouRWjl SgU6JEzqSqBFEkwqAkCGbVWiqj3lHgHezYeVxThqhahc5ZVjFxZJstIkVLBQe/g1+XxL Lpe8jbFV3df3kWxajCiojxD3GpDWvGqjVv14X4+u7wKfapvpmJMEK4v/9CBQVqS9dHxQ HHW6sRrYadsVlgf13NW500EuiijDn5iXVPP/OM7pvAIAbpXd+lf9sGzCla2ulGIqVHR0 UDAw== X-Gm-Message-State: AOJu0YzXTrQJZsI6P2N7DGnpAd/zph4M8DRVL1BxSsCsskZ/3lurgxCC YBJA2I8DM7rwkQVjPb+s2UeSgii+w6jUp2Ghs5gXAXqDWJmE4MTd/dL6lnTH X-Gm-Gg: ASbGnctYbfwxEY9hmv7atodLaDS1PVPx7kbUaUOtiTSWMc8C6vjjEsxiSKZS1mYE8Mo HpqUgOLJV3yyj2zXCg7bUBaqsFyitqjA2DmvrcP7hAgkDGdxSSsdGnEXDOoY/vM1mkmPgGnb/iR 8Z8GJkWq2zqsEQ1nTT7lUnYsUW3//mvKN4uSNW1f+SDwxOS0Eu0kbUI9HF8WAo1owftDDxRsEfK jCwoC1txI/aVwqcTvMrHFRQCaWGqYtPbFLOff+ki0nE7OAB86uyEI7me94KjZMSi0WrzdYNlM9d YbbnhkTGpTTK6ExkATQvFXBFD7x38Q== X-Google-Smtp-Source: AGHT+IGz73T00ob9NE8xu9gWKIoyo/o/qsl5W20NVIi6OrTrnhBc/qsBf8Cnmh2ka9eMgrmdxH6p3A== X-Received: by 2002:a17:902:da87:b0:21f:6dcf:fd2b with SMTP id d9443c01a7336-220d33a5209mr355212875ad.1.1739884111512; Tue, 18 Feb 2025 05:08:31 -0800 (PST) Received: from localhost.localdomain ([2800:2121:b040:c:a0a7:974:71c7:ca89]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-220d556d66fsm89178635ad.180.2025.02.18.05.08.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Feb 2025 05:08:31 -0800 (PST) From: James Almer To: ffmpeg-devel@ffmpeg.org Date: Tue, 18 Feb 2025 10:08:04 -0300 Message-ID: <20250218130813.74-2-jamrial@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250218130813.74-1-jamrial@gmail.com> References: <20250218130813.74-1-jamrial@gmail.com> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Archived-At: List-Archive: List-Post: 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 --- 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".