PR #21147 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21147 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21147.patch
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. Therefore, this PR introduces a new function that attempts to reconfigure an opened encoder with a dictionary of options. Future work will include adding support for this feature to the CLI. >From c800acd1f94e5254e99192dc23c12a0676993dcf Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Sun, 16 Feb 2025 21:17:53 -0300 Subject: [PATCH 1/6] avutil/opt: allow passing a fake object to av_opt_set() The result will be a dry run of the process, where no value is set but all the checks are made. Useful for, as the AV_OPT_SEARCH_FAKE_OBJ flag states, run the relevant process without having to allocate the corresponding object. Signed-off-by: James Almer <[email protected]> --- libavutil/opt.c | 77 ++++++++++++++++++++++++++++++++++++++++++------- libavutil/opt.h | 11 +++---- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/libavutil/opt.c b/libavutil/opt.c index fc5834e168..c312bbdd32 100644 --- a/libavutil/opt.c +++ b/libavutil/opt.c @@ -171,7 +171,7 @@ static int opt_set_init(void *obj, const char *name, int search_flags, void *tgt; o = av_opt_find2(obj, name, NULL, 0, search_flags, &tgt); - if (!o || !tgt) + if (!o || (!tgt && !(search_flags & AV_OPT_SEARCH_FAKE_OBJ))) return AVERROR_OPTION_NOT_FOUND; if (o->flags & AV_OPT_FLAG_READONLY) @@ -226,7 +226,7 @@ static int opt_set_init(void *obj, const char *name, int search_flags, if (ptgt) *ptgt = tgt; if (pdst) - *pdst = ((uint8_t *)tgt) + o->offset; + *pdst = tgt ? ((uint8_t *)tgt) + o->offset : NULL; return 0; } @@ -301,6 +301,9 @@ static int write_number(void *obj, const AVOption *o, void *dst, double num, int } } + if (!dst) + return 0; + switch (type) { case AV_OPT_TYPE_PIXEL_FMT: *(enum AVPixelFormat *)dst = llrint(num / den) * intnum; @@ -367,12 +370,15 @@ static int hexchar2int(char c) { static int set_string_binary(void *obj, const AVOption *o, const char *val, uint8_t **dst) { - int *lendst = (int *)(dst + 1); + int *lendst; uint8_t *bin, *ptr; int len; + if (dst) { + lendst = (int *)(dst + 1); av_freep(dst); *lendst = 0; + } if (!val || !(len = strlen(val))) return 0; @@ -393,14 +399,19 @@ static int set_string_binary(void *obj, const AVOption *o, const char *val, uint } *ptr++ = (a << 4) | b; } + if (dst) { *dst = bin; *lendst = len; + } else + av_free(bin); return 0; } static int set_string(void *obj, const AVOption *o, const char *val, uint8_t **dst) { + if (!dst) + return 0; av_freep(dst); if (!val) return 0; @@ -495,7 +506,7 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con } } } - if (type == AV_OPT_TYPE_FLAGS) { + if (type == AV_OPT_TYPE_FLAGS && dst) { intnum = *(unsigned int*)dst; if (cmd == '+') d = intnum | (int64_t)d; @@ -513,24 +524,32 @@ static int set_string_number(void *obj, void *target_obj, const AVOption *o, con static int set_string_image_size(void *obj, const AVOption *o, const char *val, int *dst) { + int tmp[2]; int ret; if (!val || !strcmp(val, "none")) { + if (dst) { dst[0] = dst[1] = 0; + } return 0; } - ret = av_parse_video_size(dst, dst + 1, val); + ret = av_parse_video_size(&tmp[0], &tmp[1], val); if (ret < 0) av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as image size\n", o->name, val); + if (dst) + memcpy(dst, tmp, sizeof(tmp)); return ret; } static int set_string_video_rate(void *obj, const AVOption *o, const char *val, AVRational *dst) { - int ret = av_parse_video_rate(dst, val); + AVRational tmp; + int ret = av_parse_video_rate(&tmp, val); if (ret < 0) av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as video rate\n", o->name, val); + if (dst) + *dst = tmp; return ret; } @@ -541,9 +560,12 @@ static int set_string_color(void *obj, const AVOption *o, const char *val, uint8 if (!val) { return 0; } else { - ret = av_parse_color(dst, val, -1, obj); + uint8_t tmp[4]; + ret = av_parse_color(tmp, val, -1, obj); if (ret < 0) av_log(obj, AV_LOG_ERROR, "Unable to parse \"%s\" option value \"%s\" as color\n", o->name, val); + if (dst) + memcpy(dst, tmp, sizeof(tmp)); return ret; } return 0; @@ -579,6 +601,7 @@ static int set_string_bool(void *obj, const AVOption *o, const char *val, int *d if (n < o->min || n > o->max) goto fail; + if (dst) *dst = n; return 0; @@ -623,6 +646,7 @@ static int set_string_fmt(void *obj, const AVOption *o, const char *val, uint8_t return AVERROR(ERANGE); } + if (dst) *(int *)dst = fmt; return 0; } @@ -661,6 +685,9 @@ static int set_string_dict(void *obj, const AVOption *o, const char *val, uint8_ } } + if (!dst) + return 0; + av_dict_free((AVDictionary **)dst); *dst = (uint8_t *)options; @@ -670,11 +697,21 @@ static int set_string_dict(void *obj, const AVOption *o, const char *val, uint8_ static int set_string_channel_layout(void *obj, const AVOption *o, const char *val, void *dst) { + AVChannelLayout tmp; AVChannelLayout *channel_layout = dst; + int ret; + if (dst) av_channel_layout_uninit(channel_layout); + else + channel_layout = &tmp; if (!val) return 0; - return av_channel_layout_from_string(channel_layout, val); + ret = av_channel_layout_from_string(channel_layout, val); + if (ret < 0) + return ret; + if (!dst) + av_channel_layout_uninit(channel_layout); + return 0; } static int opt_set_elem(void *obj, void *target_obj, const AVOption *o, @@ -733,6 +770,7 @@ static int opt_set_elem(void *obj, void *target_obj, const AVOption *o, usecs / 1000000.0, o->name, o->min / 1000000.0, o->max / 1000000.0); return AVERROR(ERANGE); } + if (dst) *(int64_t *)dst = usecs; return 0; } @@ -812,6 +850,7 @@ static int opt_set_array(void *obj, void *target_obj, const AVOption *o, } av_freep(&str); + if (dst) opt_free_array(o, dst, opt_array_pcount(dst)); if (arr && nb_elems < arr->size_min) { @@ -822,8 +861,10 @@ static int opt_set_array(void *obj, void *target_obj, const AVOption *o, goto fail; } + if (dst) { *((void **)dst) = elems; *opt_array_pcount(dst) = nb_elems; + } return 0; fail: @@ -873,8 +914,10 @@ static int set_number(void *obj, const char *name, double num, int den, int64_t ret = opt_set_init(obj, name, search_flags, require_type, NULL, &o, &dst); if (ret < 0) return ret; + if (dst) + ret = write_number(obj, o, dst, num, den, intnum); - return write_number(obj, o, dst, num, den, intnum); + return ret; } int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags) @@ -904,6 +947,7 @@ int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int if (ret < 0) return ret; + if (dst) { ptr = len ? av_malloc(len) : NULL; if (len && !ptr) return AVERROR(ENOMEM); @@ -915,6 +959,7 @@ int av_opt_set_bin(void *obj, const char *name, const uint8_t *val, int len, int *lendst = len; if (len) memcpy(ptr, val, len); + } return 0; } @@ -936,8 +981,10 @@ int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_ return AVERROR(EINVAL); } + if (dst) { dst[0] = w; dst[1] = h; + } return 0; } @@ -967,6 +1014,7 @@ static int set_format(void *obj, const char *name, int fmt, int search_flags, fmt, name, desc, min, max); return AVERROR(ERANGE); } + if (dst) *dst = fmt; return 0; } @@ -992,9 +1040,13 @@ int av_opt_set_dict_val(void *obj, const char *name, const AVDictionary *val, if (ret < 0) return ret; + if (dst) { av_dict_free(dst); - return av_dict_copy(dst, val, 0); + ret = av_dict_copy(dst, val, 0); + } + + return ret; } int av_opt_set_chlayout(void *obj, const char *name, @@ -1009,7 +1061,10 @@ int av_opt_set_chlayout(void *obj, const char *name, if (ret < 0) return ret; - return av_channel_layout_copy(dst, channel_layout); + if (dst) + ret = av_channel_layout_copy(dst, channel_layout); + + return ret; } static void format_duration(char *buf, size_t size, int64_t d) diff --git a/libavutil/opt.h b/libavutil/opt.h index ab24fae777..d90f2363cd 100644 --- a/libavutil/opt.h +++ b/libavutil/opt.h @@ -605,10 +605,10 @@ const AVClass *av_opt_child_class_iterate(const AVClass *parent, void **iter); #define AV_OPT_SEARCH_CHILDREN (1 << 0) /**< Search in possible children of the given object first. */ /** - * The obj passed to av_opt_find() is fake -- only a double pointer to AVClass - * instead of a required pointer to a struct containing AVClass. This is - * useful for searching for options without needing to allocate the corresponding - * object. + * The obj passed to av_opt_find() or av_opt_set() is fake -- only a double pointer + * to AVClass instead of a required pointer to a struct containing AVClass. This is + * useful for searching for options or verifying they can be applied without needing + * to allocate the corresponding object. */ #define AV_OPT_SEARCH_FAKE_OBJ (1 << 1) @@ -842,7 +842,8 @@ int av_opt_copy(void *dest, const void *src); * @{ * Those functions set the field of obj with the given name to value. * - * @param[in] obj A struct whose first element is a pointer to an AVClass. + * @param[in] obj A struct whose first element is a pointer to an AVClass. Alternatively + * a double pointer to an AVClass, if AV_OPT_SEARCH_FAKE_OBJ search flag is set. * @param[in] name the name of the field to set * @param[in] val The value to set. In case of av_opt_set() if the field is not * of a string type, then the given string is parsed. -- 2.49.1 >From eb93c7e367bb48b1f00360765ac9cb9f573fedf1 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 11 Feb 2025 12:57:40 -0300 Subject: [PATCH 2/6] avcodec/encode: add a function to gracefully reconfigure an encoder 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 <[email protected]> --- 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 1a8f77af82..c8ad6c401a 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -2232,6 +2232,25 @@ int avcodec_parameters_to_context(AVCodecContext *codec, */ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); +/** + * 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 f509e5d94e..13b5d9c8f5 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 eea982e56c..674aa2bdbb 100644 --- a/libavcodec/codec_internal.h +++ b/libavcodec/codec_internal.h @@ -96,9 +96,11 @@ typedef struct FFCodecDefault { const char *key; const char *value; + int flags; } FFCodecDefault; struct AVCodecContext; +struct AVDictionary; struct AVSubtitle; struct AVPacket; enum AVCodecConfig; @@ -254,6 +256,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 407bd8920f..6a277a5f0a 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -25,6 +25,7 @@ #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" +#include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/samplefmt.h" @@ -542,6 +543,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 82bd3c9431..81e596984a 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.49.1 >From 4a8f7f0e5ee33c756ebc21c7541bfb6f724875ba Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 11 Feb 2025 21:49:41 -0300 Subject: [PATCH 3/6] avcodec/libx264: refactor encoder configuration functions Will be useful in the next commit. Signed-off-by: James Almer <[email protected]> --- libavcodec/libx264.c | 90 ++++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index b688d4f321..017550dd7c 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -192,11 +192,59 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, return 1; } -static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) +static void reconfig_encoder(AVCodecContext *ctx) +{ + X264Context *x4 = ctx->priv_data; + + if (x4->avcintra_class >= 0) + return; + + if (x4->params.vui.i_sar_height*ctx->sample_aspect_ratio.num != ctx->sample_aspect_ratio.den * x4->params.vui.i_sar_width) { + x4->params.vui.i_sar_height = ctx->sample_aspect_ratio.den; + x4->params.vui.i_sar_width = ctx->sample_aspect_ratio.num; + x264_encoder_reconfig(x4->enc, &x4->params); + } + + if (x4->params.rc.i_vbv_buffer_size != ctx->rc_buffer_size / 1000 || + x4->params.rc.i_vbv_max_bitrate != ctx->rc_max_rate / 1000) { + x4->params.rc.i_vbv_buffer_size = ctx->rc_buffer_size / 1000; + x4->params.rc.i_vbv_max_bitrate = ctx->rc_max_rate / 1000; + x264_encoder_reconfig(x4->enc, &x4->params); + } + + if (x4->params.rc.i_rc_method == X264_RC_ABR && + x4->params.rc.i_bitrate != ctx->bit_rate / 1000) { + x4->params.rc.i_bitrate = ctx->bit_rate / 1000; + x264_encoder_reconfig(x4->enc, &x4->params); + } + + if (x4->crf >= 0 && + x4->params.rc.i_rc_method == X264_RC_CRF && + x4->params.rc.f_rf_constant != x4->crf) { + x4->params.rc.f_rf_constant = x4->crf; + x264_encoder_reconfig(x4->enc, &x4->params); + } + + if (x4->params.rc.i_rc_method == X264_RC_CQP && + x4->cqp >= 0 && + x4->params.rc.i_qp_constant != x4->cqp) { + x4->params.rc.i_qp_constant = x4->cqp; + x264_encoder_reconfig(x4->enc, &x4->params); + } + + if (x4->crf_max >= 0 && + x4->params.rc.f_rf_constant_max != x4->crf_max) { + x4->params.rc.f_rf_constant_max = x4->crf_max; + x264_encoder_reconfig(x4->enc, &x4->params); + } +} + +static void reconfig_encoder_from_frame(AVCodecContext *ctx, const AVFrame *frame) { X264Context *x4 = ctx->priv_data; AVFrameSideData *side_data; + reconfig_encoder(ctx); if (x4->avcintra_class < 0) { if (x4->params.b_interlaced && x4->params.b_tff != !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) { @@ -204,44 +252,6 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) x4->params.b_tff = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); x264_encoder_reconfig(x4->enc, &x4->params); } - if (x4->params.vui.i_sar_height*ctx->sample_aspect_ratio.num != ctx->sample_aspect_ratio.den * x4->params.vui.i_sar_width) { - x4->params.vui.i_sar_height = ctx->sample_aspect_ratio.den; - x4->params.vui.i_sar_width = ctx->sample_aspect_ratio.num; - x264_encoder_reconfig(x4->enc, &x4->params); - } - - if (x4->params.rc.i_vbv_buffer_size != ctx->rc_buffer_size / 1000 || - x4->params.rc.i_vbv_max_bitrate != ctx->rc_max_rate / 1000) { - x4->params.rc.i_vbv_buffer_size = ctx->rc_buffer_size / 1000; - x4->params.rc.i_vbv_max_bitrate = ctx->rc_max_rate / 1000; - x264_encoder_reconfig(x4->enc, &x4->params); - } - - if (x4->params.rc.i_rc_method == X264_RC_ABR && - x4->params.rc.i_bitrate != ctx->bit_rate / 1000) { - x4->params.rc.i_bitrate = ctx->bit_rate / 1000; - x264_encoder_reconfig(x4->enc, &x4->params); - } - - if (x4->crf >= 0 && - x4->params.rc.i_rc_method == X264_RC_CRF && - x4->params.rc.f_rf_constant != x4->crf) { - x4->params.rc.f_rf_constant = x4->crf; - x264_encoder_reconfig(x4->enc, &x4->params); - } - - if (x4->params.rc.i_rc_method == X264_RC_CQP && - x4->cqp >= 0 && - x4->params.rc.i_qp_constant != x4->cqp) { - x4->params.rc.i_qp_constant = x4->cqp; - x264_encoder_reconfig(x4->enc, &x4->params); - } - - if (x4->crf_max >= 0 && - x4->params.rc.f_rf_constant_max != x4->crf_max) { - x4->params.rc.f_rf_constant_max = x4->crf_max; - x264_encoder_reconfig(x4->enc, &x4->params); - } } side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_STEREO3D); @@ -526,7 +536,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, pic->i_type = X264_TYPE_AUTO; break; } - reconfig_encoder(ctx, frame); + reconfig_encoder_from_frame(ctx, frame); if (x4->a53_cc) { void *sei_data; -- 2.49.1 >From b48e163aa4bceb010d4992a0ce08455790fe9c9c Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 11 Feb 2025 16:11:47 -0300 Subject: [PATCH 4/6] avcodec/libx264: add support for encoder reconfiguration Signed-off-by: James Almer <[email protected]> --- libavcodec/libx264.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index 017550dd7c..bb2c4d4eb2 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -767,6 +767,19 @@ static void X264_flush(AVCodecContext *avctx) x4->sei_size = -x4->sei_size; } +static av_cold int X264_reconf(AVCodecContext *avctx, AVDictionary **dict) +{ + int ret; + + ret = ff_encode_reconf_parse_dict(avctx, dict); + if (ret < 0) + return ret; + + reconfig_encoder(avctx); + + return 0; +} + static av_cold int X264_close(AVCodecContext *avctx) { X264Context *x4 = avctx->priv_data; @@ -1502,6 +1515,7 @@ static const enum AVPixelFormat pix_fmts_8bit_rgb[] = { #define OFFSET(x) offsetof(X264Context, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +#define VER VE | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption options[] = { { "preset", "Set the encoding preset (cf. x264 --fullhelp)", OFFSET(preset), AV_OPT_TYPE_STRING, { .str = "medium" }, 0, 0, VE}, { "tune", "Tune the encoding params (cf. x264 --fullhelp)", OFFSET(tune), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE}, @@ -1512,9 +1526,9 @@ static const AVOption options[] = { {"wpredp", "Weighted prediction for P-frames", OFFSET(wpredp), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE}, {"a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE}, {"x264opts", "x264 options", OFFSET(x264opts), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE}, - { "crf", "Select the quality for constant quality mode", OFFSET(crf), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE }, - { "crf_max", "In CRF mode, prevents VBV from lowering quality beyond this point.",OFFSET(crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE }, - { "qp", "Constant quantization parameter rate control method",OFFSET(cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, + { "crf", "Select the quality for constant quality mode", OFFSET(crf), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VER }, + { "crf_max", "In CRF mode, prevents VBV from lowering quality beyond this point.",OFFSET(crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VER }, + { "qp", "Constant quantization parameter rate control method",OFFSET(cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VER }, { "aq-mode", "AQ method", OFFSET(aq_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "aq_mode"}, { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_NONE}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, { "variance", "Variance AQ (complexity mask)", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_VARIANCE}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, @@ -1584,7 +1598,10 @@ static const AVOption options[] = { }; static const FFCodecDefault x264_defaults[] = { - { "b", "0" }, + { "sar", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "b", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "bufsize", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "maxrate", "0", AV_OPT_FLAG_RUNTIME_PARAM }, { "bf", "-1" }, { "flags2", "0" }, { "g", "-1" }, @@ -1626,6 +1643,7 @@ const FFCodec ff_libx264_encoder = { AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | AV_CODEC_CAP_ENCODER_FLUSH | + AV_CODEC_CAP_RECONF | AV_CODEC_CAP_ENCODER_RECON_FRAME, .p.priv_class = &x264_class, .p.wrapper_name = "libx264", @@ -1633,6 +1651,7 @@ const FFCodec ff_libx264_encoder = { .init = X264_init, FF_CODEC_ENCODE_CB(X264_frame), .flush = X264_flush, + .reconf = X264_reconf, .close = X264_close, .defaults = x264_defaults, CODEC_PIXFMTS_ARRAY(pix_fmts_all), -- 2.49.1 >From fd712354ecf50c64b77a1d9f760204b2d701db20 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 11 Feb 2025 19:13:50 -0300 Subject: [PATCH 5/6] avcodec/libaomenc: refactor encoder configuration functions Will be useful in the next commit. Signed-off-by: James Almer <[email protected]> --- libavcodec/libaomenc.c | 225 +++++++++++++++++++++++------------------ 1 file changed, 124 insertions(+), 101 deletions(-) diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c index be319d0f3c..79f13c2186 100644 --- a/libavcodec/libaomenc.c +++ b/libavcodec/libaomenc.c @@ -74,7 +74,10 @@ typedef struct AOMEncoderContext { AVBSFContext *bsf; DOVIContext dovi; struct aom_codec_ctx encoder; + struct aom_codec_enc_cfg enccfg; struct aom_image rawimg; + aom_codec_flags_t flags; + aom_img_fmt_t img_fmt; struct aom_fixed_buf twopass_stats; unsigned twopass_stats_size; struct FrameListData *coded_frame_list; @@ -722,7 +725,6 @@ static int choose_tiling(AVCodecContext *avctx, return 0; } - static const struct { int aom_enum; unsigned offset; @@ -762,28 +764,22 @@ static const struct { { AV1E_SET_ENABLE_SMOOTH_INTERINTRA, OFFSET(enable_smooth_interintra) }, }; -static av_cold int aom_init(AVCodecContext *avctx, - const struct aom_codec_iface *iface) +static av_cold int aom_config(AVCodecContext *avctx, + const struct aom_codec_iface *iface) { AOMContext *ctx = avctx->priv_data; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); - struct aom_codec_enc_cfg enccfg = { 0 }; - aom_codec_flags_t flags = - (avctx->flags & AV_CODEC_FLAG_PSNR) ? AOM_CODEC_USE_PSNR : 0; int res; - aom_img_fmt_t img_fmt; aom_codec_caps_t codec_caps = aom_codec_get_caps(iface); - av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str()); - av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config()); + ctx->flags = (avctx->flags & AV_CODEC_FLAG_PSNR) ? AOM_CODEC_USE_PSNR : 0; - if ((res = aom_codec_enc_config_default(iface, &enccfg, ctx->usage)) != AOM_CODEC_OK) { + if ((res = aom_codec_enc_config_default(iface, &ctx->enccfg, ctx->usage)) != AOM_CODEC_OK) { av_log(avctx, AV_LOG_ERROR, "Failed to get config: %s\n", aom_codec_err_to_string(res)); return AVERROR(EINVAL); } - if (set_pix_fmt(avctx, codec_caps, &enccfg, &flags, &img_fmt)) + if (set_pix_fmt(avctx, codec_caps, &ctx->enccfg, &ctx->flags, &ctx->img_fmt)) return AVERROR(EINVAL); if(!avctx->bit_rate) @@ -792,39 +788,30 @@ static av_cold int aom_init(AVCodecContext *avctx, return AVERROR(EINVAL); } - dump_enc_cfg(avctx, &enccfg, AV_LOG_DEBUG); - - enccfg.g_w = avctx->width; - enccfg.g_h = avctx->height; - enccfg.g_timebase.num = avctx->time_base.num; - enccfg.g_timebase.den = avctx->time_base.den; - enccfg.g_threads = + ctx->enccfg.g_w = avctx->width; + ctx->enccfg.g_h = avctx->height; + ctx->enccfg.g_timebase.num = avctx->time_base.num; + ctx->enccfg.g_timebase.den = avctx->time_base.den; + ctx->enccfg.g_threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 64); if (ctx->lag_in_frames >= 0) - enccfg.g_lag_in_frames = ctx->lag_in_frames; - - if (avctx->flags & AV_CODEC_FLAG_PASS1) - enccfg.g_pass = AOM_RC_FIRST_PASS; - else if (avctx->flags & AV_CODEC_FLAG_PASS2) - enccfg.g_pass = AOM_RC_LAST_PASS; - else - enccfg.g_pass = AOM_RC_ONE_PASS; + ctx->enccfg.g_lag_in_frames = ctx->lag_in_frames; if (avctx->rc_min_rate == avctx->rc_max_rate && avctx->rc_min_rate == avctx->bit_rate && avctx->bit_rate) { - enccfg.rc_end_usage = AOM_CBR; + ctx->enccfg.rc_end_usage = AOM_CBR; } else if (ctx->crf >= 0) { - enccfg.rc_end_usage = AOM_CQ; + ctx->enccfg.rc_end_usage = AOM_CQ; if (!avctx->bit_rate) - enccfg.rc_end_usage = AOM_Q; + ctx->enccfg.rc_end_usage = AOM_Q; } if (avctx->bit_rate) { - enccfg.rc_target_bitrate = av_rescale_rnd(avctx->bit_rate, 1, 1000, + ctx->enccfg.rc_target_bitrate = av_rescale_rnd(avctx->bit_rate, 1, 1000, AV_ROUND_NEAR_INF); - } else if (enccfg.rc_end_usage != AOM_Q) { - enccfg.rc_end_usage = AOM_Q; + } else if (ctx->enccfg.rc_end_usage != AOM_Q) { + ctx->enccfg.rc_end_usage = AOM_Q; ctx->crf = 32; av_log(avctx, AV_LOG_WARNING, "Neither bitrate nor constrained quality specified, using default CRF of %d\n", @@ -832,95 +819,65 @@ static av_cold int aom_init(AVCodecContext *avctx, } if (avctx->qmin >= 0) - enccfg.rc_min_quantizer = avctx->qmin; + ctx->enccfg.rc_min_quantizer = avctx->qmin; if (avctx->qmax >= 0) { - enccfg.rc_max_quantizer = avctx->qmax; + ctx->enccfg.rc_max_quantizer = avctx->qmax; } else if (!ctx->crf) { - enccfg.rc_max_quantizer = 0; + ctx->enccfg.rc_max_quantizer = 0; } - if (enccfg.rc_end_usage == AOM_CQ || enccfg.rc_end_usage == AOM_Q) { - if (ctx->crf < enccfg.rc_min_quantizer || ctx->crf > enccfg.rc_max_quantizer) { + if (ctx->enccfg.rc_end_usage == AOM_CQ || ctx->enccfg.rc_end_usage == AOM_Q) { + if (ctx->crf < ctx->enccfg.rc_min_quantizer || ctx->crf > ctx->enccfg.rc_max_quantizer) { av_log(avctx, AV_LOG_ERROR, "CQ level %d must be between minimum and maximum quantizer value (%d-%d)\n", - ctx->crf, enccfg.rc_min_quantizer, enccfg.rc_max_quantizer); + ctx->crf, ctx->enccfg.rc_min_quantizer, ctx->enccfg.rc_max_quantizer); return AVERROR(EINVAL); } } - enccfg.rc_dropframe_thresh = ctx->drop_threshold; + ctx->enccfg.rc_dropframe_thresh = ctx->drop_threshold; // 0-100 (0 => CBR, 100 => VBR) - enccfg.rc_2pass_vbr_bias_pct = round(avctx->qcompress * 100); + ctx->enccfg.rc_2pass_vbr_bias_pct = round(avctx->qcompress * 100); if (ctx->minsection_pct >= 0) - enccfg.rc_2pass_vbr_minsection_pct = ctx->minsection_pct; + ctx->enccfg.rc_2pass_vbr_minsection_pct = ctx->minsection_pct; else if (avctx->bit_rate) - enccfg.rc_2pass_vbr_minsection_pct = + ctx->enccfg.rc_2pass_vbr_minsection_pct = avctx->rc_min_rate * 100LL / avctx->bit_rate; if (ctx->maxsection_pct >= 0) - enccfg.rc_2pass_vbr_maxsection_pct = ctx->maxsection_pct; + ctx->enccfg.rc_2pass_vbr_maxsection_pct = ctx->maxsection_pct; else if (avctx->rc_max_rate) - enccfg.rc_2pass_vbr_maxsection_pct = + ctx->enccfg.rc_2pass_vbr_maxsection_pct = avctx->rc_max_rate * 100LL / avctx->bit_rate; if (avctx->rc_buffer_size) - enccfg.rc_buf_sz = + ctx->enccfg.rc_buf_sz = avctx->rc_buffer_size * 1000LL / avctx->bit_rate; if (avctx->rc_initial_buffer_occupancy) - enccfg.rc_buf_initial_sz = + ctx->enccfg.rc_buf_initial_sz = avctx->rc_initial_buffer_occupancy * 1000LL / avctx->bit_rate; - enccfg.rc_buf_optimal_sz = enccfg.rc_buf_sz * 5 / 6; + ctx->enccfg.rc_buf_optimal_sz = ctx->enccfg.rc_buf_sz * 5 / 6; if (ctx->rc_undershoot_pct >= 0) - enccfg.rc_undershoot_pct = ctx->rc_undershoot_pct; + ctx->enccfg.rc_undershoot_pct = ctx->rc_undershoot_pct; if (ctx->rc_overshoot_pct >= 0) - enccfg.rc_overshoot_pct = ctx->rc_overshoot_pct; + ctx->enccfg.rc_overshoot_pct = ctx->rc_overshoot_pct; // _enc_init() will balk if kf_min_dist differs from max w/AOM_KF_AUTO if (avctx->keyint_min >= 0 && avctx->keyint_min == avctx->gop_size) - enccfg.kf_min_dist = avctx->keyint_min; + ctx->enccfg.kf_min_dist = avctx->keyint_min; if (avctx->gop_size >= 0) - enccfg.kf_max_dist = avctx->gop_size; - - if (enccfg.g_pass == AOM_RC_FIRST_PASS) - enccfg.g_lag_in_frames = 0; - else if (enccfg.g_pass == AOM_RC_LAST_PASS) { - int decode_size, ret; - - if (!avctx->stats_in) { - av_log(avctx, AV_LOG_ERROR, "No stats file for second pass\n"); - return AVERROR_INVALIDDATA; - } - - ctx->twopass_stats.sz = strlen(avctx->stats_in) * 3 / 4; - ret = av_reallocp(&ctx->twopass_stats.buf, ctx->twopass_stats.sz); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "Stat buffer alloc (%zu bytes) failed\n", - ctx->twopass_stats.sz); - ctx->twopass_stats.sz = 0; - return ret; - } - decode_size = av_base64_decode(ctx->twopass_stats.buf, avctx->stats_in, - ctx->twopass_stats.sz); - if (decode_size < 0) { - av_log(avctx, AV_LOG_ERROR, "Stat buffer decode failed\n"); - return AVERROR_INVALIDDATA; - } - - ctx->twopass_stats.sz = decode_size; - enccfg.rc_twopass_stats_in = ctx->twopass_stats; - } + ctx->enccfg.kf_max_dist = avctx->gop_size; /* 0-3: For non-zero values the encoder increasingly optimizes for reduced * complexity playback on low powered devices at the expense of encode * quality. */ if (avctx->profile != AV_PROFILE_UNKNOWN) - enccfg.g_profile = avctx->profile; + ctx->enccfg.g_profile = avctx->profile; - enccfg.g_error_resilient = ctx->error_resilient; + ctx->enccfg.g_error_resilient = ctx->error_resilient; - res = choose_tiling(avctx, &enccfg); + res = choose_tiling(avctx, &ctx->enccfg); if (res < 0) return res; @@ -928,22 +885,22 @@ static av_cold int aom_init(AVCodecContext *avctx, // Set the maximum number of frames to 1. This will let libaom set // still_picture and reduced_still_picture_header to 1 in the Sequence // Header as required by AVIF still images. - enccfg.g_limit = 1; + ctx->enccfg.g_limit = 1; // Reduce memory usage for still images. - enccfg.g_lag_in_frames = 0; + ctx->enccfg.g_lag_in_frames = 0; // All frames will be key frames. - enccfg.kf_max_dist = 0; - enccfg.kf_mode = AOM_KF_DISABLED; + ctx->enccfg.kf_max_dist = 0; + ctx->enccfg.kf_mode = AOM_KF_DISABLED; } - /* Construct Encoder Context */ - res = aom_codec_enc_init(&ctx->encoder, iface, &enccfg, flags); - if (res != AOM_CODEC_OK) { - dump_enc_cfg(avctx, &enccfg, AV_LOG_WARNING); - log_encoder_error(avctx, "Failed to initialize encoder"); - return AVERROR(EINVAL); - } - dump_enc_cfg(avctx, &enccfg, AV_LOG_DEBUG); + return 0; +} + +static av_cold int aom_codecctl(AVCodecContext *avctx) +{ + AOMContext *ctx = avctx->priv_data; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); + aom_codec_caps_t codec_caps = aom_codec_get_caps(ctx->encoder.iface); // codec control failures are currently treated only as warnings av_log(avctx, AV_LOG_DEBUG, "aom_codec_control\n"); @@ -1010,11 +967,77 @@ static av_cold int aom_init(AVCodecContext *avctx, #endif // provide dummy value to initialize wrapper, values will be updated each _encode() - aom_img_wrap(&ctx->rawimg, img_fmt, avctx->width, avctx->height, 1, + aom_img_wrap(&ctx->rawimg, ctx->img_fmt, avctx->width, avctx->height, 1, (unsigned char*)1); if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH) - ctx->rawimg.bit_depth = enccfg.g_bit_depth; + ctx->rawimg.bit_depth = ctx->enccfg.g_bit_depth; + + return 0; +} + +static av_cold int aom_init(AVCodecContext *avctx, + const struct aom_codec_iface *iface) +{ + AOMContext *ctx = avctx->priv_data; + int res; + + av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str()); + av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config()); + + res = aom_config(avctx, iface); + if (res < 0) + return res; + + if (avctx->flags & AV_CODEC_FLAG_PASS1) + ctx->enccfg.g_pass = AOM_RC_FIRST_PASS; + else if (avctx->flags & AV_CODEC_FLAG_PASS2) + ctx->enccfg.g_pass = AOM_RC_LAST_PASS; + else + ctx->enccfg.g_pass = AOM_RC_ONE_PASS; + + if (ctx->enccfg.g_pass == AOM_RC_FIRST_PASS) + ctx->enccfg.g_lag_in_frames = 0; + else if (ctx->enccfg.g_pass == AOM_RC_LAST_PASS) { + int decode_size, ret; + + if (!avctx->stats_in) { + av_log(avctx, AV_LOG_ERROR, "No stats file for second pass\n"); + return AVERROR_INVALIDDATA; + } + + ctx->twopass_stats.sz = strlen(avctx->stats_in) * 3 / 4; + ret = av_reallocp(&ctx->twopass_stats.buf, ctx->twopass_stats.sz); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "Stat buffer alloc (%zu bytes) failed\n", + ctx->twopass_stats.sz); + ctx->twopass_stats.sz = 0; + return ret; + } + decode_size = av_base64_decode(ctx->twopass_stats.buf, avctx->stats_in, + ctx->twopass_stats.sz); + if (decode_size < 0) { + av_log(avctx, AV_LOG_ERROR, "Stat buffer decode failed\n"); + return AVERROR_INVALIDDATA; + } + + ctx->twopass_stats.sz = decode_size; + ctx->enccfg.rc_twopass_stats_in = ctx->twopass_stats; + } + + /* Construct Encoder Context */ + res = aom_codec_enc_init(&ctx->encoder, iface, &ctx->enccfg, ctx->flags); + if (res != AOM_CODEC_OK) { + dump_enc_cfg(avctx, &ctx->enccfg, AV_LOG_WARNING); + log_encoder_error(avctx, "Failed to initialize encoder"); + return AVERROR(EINVAL); + } + dump_enc_cfg(avctx, &ctx->enccfg, AV_LOG_DEBUG); + + res = aom_codecctl(avctx); + if (res < 0) + return res; ctx->dovi.logctx = avctx; if ((res = ff_dovi_configure(&ctx->dovi, avctx)) < 0) @@ -1046,8 +1069,8 @@ static av_cold int aom_init(AVCodecContext *avctx, if (!cpb_props) return AVERROR(ENOMEM); - if (enccfg.rc_end_usage == AOM_CBR || - enccfg.g_pass != AOM_RC_ONE_PASS) { + if (ctx->enccfg.rc_end_usage == AOM_CBR || + ctx->enccfg.g_pass != AOM_RC_ONE_PASS) { cpb_props->max_bitrate = avctx->rc_max_rate; cpb_props->min_bitrate = avctx->rc_min_rate; cpb_props->avg_bitrate = avctx->bit_rate; -- 2.49.1 >From 8da718fbe95e5e7592e951aa39cdebff861ff6ed Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Tue, 11 Feb 2025 19:14:15 -0300 Subject: [PATCH 6/6] avcodec/libaomenc: add support for encoder reconfiguration Signed-off-by: James Almer <[email protected]> --- libavcodec/libaomenc.c | 47 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c index 79f13c2186..4d5708eae7 100644 --- a/libavcodec/libaomenc.c +++ b/libavcodec/libaomenc.c @@ -1513,9 +1513,40 @@ static av_cold int av1_init(AVCodecContext *avctx) return aom_init(avctx, aom_codec_av1_cx()); } +static av_cold int av1_reconf(AVCodecContext *avctx, AVDictionary **dict) +{ + AOMContext *ctx = avctx->priv_data; + int loglevel; + int res; + + res = ff_encode_reconf_parse_dict(avctx, dict); + if (res < 0) + return res; + + res = aom_config(avctx, ctx->encoder.iface); + if (res < 0) + return res; + + res = aom_codec_enc_config_set(&ctx->encoder, &ctx->enccfg); + loglevel = res != AOM_CODEC_OK ? AV_LOG_WARNING : AV_LOG_DEBUG; + av_log(avctx, loglevel, "Reconfigure options:\n"); + dump_enc_cfg(avctx, &ctx->enccfg, loglevel); + if (res != AOM_CODEC_OK) { + log_encoder_error(avctx, "Failed to reconfigure encoder"); + return AVERROR(EINVAL); + } + + res = aom_codecctl(avctx); + if (res < 0) + return res; + + return 0; +} + #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +#define VER VE | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption options[] = { - { "cpu-used", "Quality/Speed ratio modifier", OFFSET(cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 8, VE}, + { "cpu-used", "Quality/Speed ratio modifier", OFFSET(cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 8, VER}, { "auto-alt-ref", "Enable use of alternate reference " "frames (2-pass only)", OFFSET(auto_alt_ref), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE}, { "lag-in-frames", "Number of frames to look ahead at for " @@ -1529,7 +1560,7 @@ static const AVOption options[] = { { "cyclic", "Cyclic Refresh Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, .unit = "aq_mode"}, { "error-resilience", "Error resilience configuration", OFFSET(error_resilient), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "er"}, { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = AOM_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, .unit = "er"}, - { "crf", "Select the quality for constant quality mode", offsetof(AOMContext, crf), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, VE }, + { "crf", "Select the quality for constant quality mode", offsetof(AOMContext, crf), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, VER }, { "static-thresh", "A change threshold on blocks below which they will be skipped by the encoder", OFFSET(static_thresh), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "drop-threshold", "Frame drop threshold", offsetof(AOMContext, drop_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, VE }, { "denoise-noise-level", "Amount of noise to be removed", OFFSET(denoise_noise_level), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, @@ -1593,9 +1624,13 @@ static const AVOption options[] = { }; static const FFCodecDefault defaults[] = { - { "b", "0" }, - { "qmin", "-1" }, - { "qmax", "-1" }, + { "b", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "bufsize", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "maxrate", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "minrate", "0", AV_OPT_FLAG_RUNTIME_PARAM }, + { "qmin", "-1", AV_OPT_FLAG_RUNTIME_PARAM }, + { "qmax", "-1", AV_OPT_FLAG_RUNTIME_PARAM }, + { "sar", "0", AV_OPT_FLAG_RUNTIME_PARAM }, { "g", "-1" }, { "keyint_min", "-1" }, { NULL }, @@ -1615,6 +1650,7 @@ FFCodec ff_libaom_av1_encoder = { .p.id = AV_CODEC_ID_AV1, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_ENCODER_RECON_FRAME | + AV_CODEC_CAP_RECONF | AV_CODEC_CAP_OTHER_THREADS, .color_ranges = AVCOL_RANGE_MPEG | AVCOL_RANGE_JPEG, .p.profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), @@ -1622,6 +1658,7 @@ FFCodec ff_libaom_av1_encoder = { .p.wrapper_name = "libaom", .priv_data_size = sizeof(AOMContext), .init = av1_init, + .reconf = av1_reconf, FF_CODEC_ENCODE_CB(aom_encode), .close = aom_free, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
