* [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set()
@ 2025-02-18 13:08 James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder James Almer
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
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 <jamrial@gmail.com>
---
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 44b507dbd4..196e3660e1 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 option value \"%s\" as image size\n", 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 option value \"%s\" as video rate\n", 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 option value \"%s\" as color\n", 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 d313679263..227e677355 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.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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder
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
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 03/11] avcodec/libx264: refactor encoder configuration functions James Almer
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 03/11] avcodec/libx264: refactor encoder configuration functions
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 ` [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder James Almer
@ 2025-02-18 13:08 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 04/11] avcodec/libx264: add support for encoder reconfiguration James Almer
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Will be useful in the next commit.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/libx264.c | 88 ++++++++++++++++++++++++--------------------
1 file changed, 49 insertions(+), 39 deletions(-)
diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
index 409f45fc7d..f5517ad080 100644
--- a/libavcodec/libx264.c
+++ b/libavcodec/libx264.c
@@ -194,54 +194,64 @@ 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;
- AVFrameSideData *side_data;
+ if (x4->avcintra_class >= 0)
+ return;
- if (x4->avcintra_class < 0) {
- if (x4->params.b_interlaced && x4->params.b_tff != !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) {
+ 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);
+ }
- 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_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->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->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->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);
+ }
+}
- 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;
+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)) {
+
+ x4->params.b_tff = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST);
x264_encoder_reconfig(x4->enc, &x4->params);
}
}
@@ -528,7 +538,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.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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 04/11] avcodec/libx264: add support for encoder reconfiguration
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 ` [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 03/11] avcodec/libx264: refactor encoder configuration functions James Almer
@ 2025-02-18 13:08 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 05/11] avcodec/libaomenc: refactor encoder configuration functions James Almer
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/libx264.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
index f5517ad080..d2adfa7bc5 100644
--- a/libavcodec/libx264.c
+++ b/libavcodec/libx264.c
@@ -769,6 +769,32 @@ static void X264_flush(AVCodecContext *avctx)
x4->sei_size = -x4->sei_size;
}
+static av_cold int X264_reconf(AVCodecContext *avctx, AVDictionary **dict)
+{
+ static const AVOption global_opts[] = {
+ { "aspect", "sample aspect ratio", offsetof(AVCodecContext, sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
+ { "sar", "sample aspect ratio", offsetof(AVCodecContext, sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
+ { "bufsize", "set ratecontrol buffer size (in bits)", offsetof(AVCodecContext, rc_buffer_size), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
+ { "b", "set bitrate (in bits/s)", offsetof(AVCodecContext, bit_rate), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
+ { NULL },
+ };
+ static const AVOption private_opts[] = {
+ { "crf", "Select the quality for constant quality mode", offsetof(X264Context, crf), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+ { "crf_max", "In CRF mode, prevents VBV from lowering quality beyond this point.", offsetof(X264Context, crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+ { "qp", "Constant quantization parameter rate control method", offsetof(X264Context, cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+ };
+ int ret;
+
+ ret = ff_encode_reconf_parse_dict(avctx, global_opts, private_opts, dict);
+ if (ret < 0)
+ return ret;
+
+ reconfig_encoder(avctx);
+
+ return 0;
+}
+
static av_cold int X264_close(AVCodecContext *avctx)
{
X264Context *x4 = avctx->priv_data;
@@ -1634,6 +1660,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",
@@ -1641,6 +1668,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,
.p.pix_fmts = pix_fmts_all,
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 05/11] avcodec/libaomenc: refactor encoder configuration functions
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (2 preceding siblings ...)
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 04/11] avcodec/libx264: add support for encoder reconfiguration James Almer
@ 2025-02-18 13:08 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 06/11] avcodec/libaomenc: add support for encoder reconfiguration James Almer
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Will be useful in the next commit.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/libaomenc.c | 238 +++++++++++++++++++++++------------------
1 file changed, 131 insertions(+), 107 deletions(-)
diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 0f7571ee7a..38bdff6a38 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -73,7 +73,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;
@@ -342,9 +345,6 @@ static av_cold int codecctl_intp(AVCodecContext *avctx,
int width = -30;
int res;
- snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]);
- av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, *ptr);
-
res = aom_codec_control(&ctx->encoder, id, ptr);
if (res != AOM_CODEC_OK) {
snprintf(buf, sizeof(buf), "Failed to set %s codec control",
@@ -353,6 +353,9 @@ static av_cold int codecctl_intp(AVCodecContext *avctx,
return AVERROR(EINVAL);
}
+ snprintf(buf, sizeof(buf), "%s:", ctlidstr[id]);
+ av_log(avctx, AV_LOG_DEBUG, " %*s%d\n", width, buf, *ptr);
+
return 0;
}
#endif
@@ -673,29 +676,22 @@ static int choose_tiling(AVCodecContext *avctx,
return 0;
}
-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;
- AVCPBProperties *cpb_props;
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)
@@ -704,39 +700,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",
@@ -744,95 +731,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 (%"SIZE_SPECIFIER" 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;
@@ -840,22 +797,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");
@@ -983,20 +940,87 @@ 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;
- cpb_props = ff_encode_add_cpb_side_data(avctx);
- if (!cpb_props)
- return AVERROR(ENOMEM);
+ return 0;
+}
+
+static av_cold int aom_init(AVCodecContext *avctx,
+ const struct aom_codec_iface *iface)
+{
+ AOMContext *ctx = avctx->priv_data;
+ AVCPBProperties *cpb_props;
+ 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 (%"SIZE_SPECIFIER" 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)
return res;
+ cpb_props = ff_encode_add_cpb_side_data(avctx);
+ if (!cpb_props)
+ return AVERROR(ENOMEM);
+
if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
const AVBitStreamFilter *filter = av_bsf_get_by_name("extract_extradata");
int ret;
@@ -1019,8 +1043,8 @@ static av_cold int aom_init(AVCodecContext *avctx,
return ret;
}
- 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.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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 06/11] avcodec/libaomenc: add support for encoder reconfiguration
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (3 preceding siblings ...)
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 05/11] avcodec/libaomenc: refactor encoder configuration functions James Almer
@ 2025-02-18 13:08 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 07/11] fftools/ffmpeg_filter: add AVOptions to OutputFilter James Almer
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/libaomenc.c | 47 +++++++++++++++++++++++++++++++++++++-----
libavcodec/libx264.c | 27 ++++++++----------------
2 files changed, 51 insertions(+), 23 deletions(-)
diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c
index 38bdff6a38..d040898b0e 100644
--- a/libavcodec/libaomenc.c
+++ b/libavcodec/libaomenc.c
@@ -1488,10 +1488,41 @@ 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 OFFSET(x) offsetof(AOMContext, 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[] = {
- { "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 "
@@ -1505,7 +1536,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},
@@ -1569,9 +1600,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 },
@@ -1591,6 +1626,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),
@@ -1598,6 +1634,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 |
diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c
index d2adfa7bc5..d9cf7f5428 100644
--- a/libavcodec/libx264.c
+++ b/libavcodec/libx264.c
@@ -771,22 +771,9 @@ static void X264_flush(AVCodecContext *avctx)
static av_cold int X264_reconf(AVCodecContext *avctx, AVDictionary **dict)
{
- static const AVOption global_opts[] = {
- { "aspect", "sample aspect ratio", offsetof(AVCodecContext, sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
- { "sar", "sample aspect ratio", offsetof(AVCodecContext, sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
- { "bufsize", "set ratecontrol buffer size (in bits)", offsetof(AVCodecContext, rc_buffer_size), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
- { "b", "set bitrate (in bits/s)", offsetof(AVCodecContext, bit_rate), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM},
- { NULL },
- };
- static const AVOption private_opts[] = {
- { "crf", "Select the quality for constant quality mode", offsetof(X264Context, crf), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
- { "crf_max", "In CRF mode, prevents VBV from lowering quality beyond this point.", offsetof(X264Context, crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
- { "qp", "Constant quantization parameter rate control method", offsetof(X264Context, cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
- { NULL },
- };
int ret;
- ret = ff_encode_reconf_parse_dict(avctx, global_opts, private_opts, dict);
+ ret = ff_encode_reconf_parse_dict(avctx, dict);
if (ret < 0)
return ret;
@@ -1536,6 +1523,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},
@@ -1546,9 +1534,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" },
@@ -1618,7 +1606,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" },
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 07/11] fftools/ffmpeg_filter: add AVOptions to OutputFilter
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (4 preceding siblings ...)
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 06/11] avcodec/libaomenc: add support for encoder reconfiguration James Almer
@ 2025-02-18 13:08 ` 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
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Preparation work for the following commits.
Signed-off-by: James Almer <jamrial@gmail.com>
---
fftools/ffmpeg_filter.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 800e2a3f06..09086ca67c 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -640,10 +640,21 @@ static const char *ofilter_item_name(void *obj)
return ofp->log_name;
}
+static const AVOption ofilter_options[] = {
+ {"video_size", "set video size", offsetof(OutputFilterPriv, width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, INT_MAX },
+ {"pixel_format", "set pixel format", offsetof(OutputFilterPriv, format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, -1, INT_MAX },
+ {"colorspace", "color space", offsetof(OutputFilterPriv, color_space), AV_OPT_TYPE_INT, {.i64 = AVCOL_SPC_UNSPECIFIED }, 0, INT_MAX },
+ {"color_range", "color range", offsetof(OutputFilterPriv, color_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, INT_MAX },
+ {"ar", "set audio sampling rate (in Hz)", offsetof(OutputFilterPriv, sample_rate), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX },
+ {"ch_layout", NULL, offsetof(OutputFilterPriv, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL }, 0, 0 },
+ { NULL },
+};
+
static const AVClass ofilter_class = {
.class_name = "OutputFilter",
.version = LIBAVUTIL_VERSION_INT,
.item_name = ofilter_item_name,
+ .option = ofilter_options,
.parent_log_context_offset = offsetof(OutputFilterPriv, log_parent),
.category = AV_CLASS_CATEGORY_FILTER,
};
@@ -662,9 +673,7 @@ static OutputFilter *ofilter_alloc(FilterGraph *fg, enum AVMediaType type)
ofp->log_parent = fg;
ofilter->graph = fg;
ofilter->type = type;
- ofp->format = -1;
- ofp->color_space = AVCOL_SPC_UNSPECIFIED;
- ofp->color_range = AVCOL_RANGE_UNSPECIFIED;
+ av_opt_set_defaults(ofp);
ofp->index = fg->nb_outputs - 1;
snprintf(ofp->log_name, sizeof(ofp->log_name), "%co%d",
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 08/11] fftools/ffmpeg_enc: split off parts of enc_open() into a separate function
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (5 preceding siblings ...)
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 07/11] fftools/ffmpeg_filter: add AVOptions to OutputFilter James Almer
@ 2025-02-18 13:08 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 09/11] fftools/ffmpeg_enc: split off encoder flushing in encoder_thread() " James Almer
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Preparation work for the following commits.
Signed-off-by: James Almer <jamrial@gmail.com>
---
fftools/ffmpeg_enc.c | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index a46af4dce1..8e68831ee8 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -181,7 +181,7 @@ static int hw_device_setup_for_encode(Encoder *e, AVCodecContext *enc_ctx,
return 0;
}
-int enc_open(void *opaque, const AVFrame *frame)
+static int enc_reopen(void *opaque, const AVFrame *frame)
{
OutputStream *ost = opaque;
InputStream *ist = ost->ist;
@@ -190,14 +190,9 @@ int enc_open(void *opaque, const AVFrame *frame)
AVCodecContext *enc_ctx = e->enc_ctx;
Decoder *dec = NULL;
const AVCodec *enc = enc_ctx->codec;
- OutputFile *of = ost->file;
FrameData *fd;
- int frame_samples = 0;
int ret;
- if (ep->opened)
- return 0;
-
// frame is always non-NULL for audio and video
av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO && enc->type != AVMEDIA_TYPE_AUDIO));
@@ -343,14 +338,34 @@ int enc_open(void *opaque, const AVFrame *frame)
ep->opened = 1;
- if (enc_ctx->frame_size)
- frame_samples = enc_ctx->frame_size;
-
if (enc_ctx->bit_rate && enc_ctx->bit_rate < 1000 &&
enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */)
av_log(e, AV_LOG_WARNING, "The bitrate parameter is set too low."
" It takes bits/s as argument, not kbits/s\n");
+ return 0;
+}
+
+int enc_open(void *opaque, const AVFrame *frame)
+{
+ OutputStream *ost = opaque;
+ Encoder *e = ost->enc;
+ EncoderPriv *ep = ep_from_enc(e);
+ AVCodecContext *enc_ctx = e->enc_ctx;
+ OutputFile *of = ost->file;
+ int frame_samples = 0;
+ int ret;
+
+ if (ep->opened)
+ return 0;
+
+ ret = enc_reopen(opaque, frame);
+ if (ret < 0)
+ return ret;
+
+ if (enc_ctx->frame_size)
+ frame_samples = enc_ctx->frame_size;
+
ret = of_stream_init(of, ost, enc_ctx);
if (ret < 0)
return ret;
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 09/11] fftools/ffmpeg_enc: split off encoder flushing in encoder_thread() into a separate function
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (6 preceding siblings ...)
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 ` 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
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Preparation work for the following commits.
Signed-off-by: James Almer <jamrial@gmail.com>
---
fftools/ffmpeg_enc.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index 8e68831ee8..acb0702385 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -862,6 +862,19 @@ fail:
return AVERROR(ENOMEM);
}
+static int flush_encoder(OutputStream *ost, EncoderThread *et)
+{
+ Encoder *e = ost->enc;
+ int ret;
+
+ ret = frame_encode(ost, NULL, et->pkt);
+ if (ret < 0 && ret != AVERROR_EOF)
+ av_log(e, AV_LOG_ERROR, "Error flushing encoder: %s\n",
+ av_err2str(ret));
+
+ return ret;
+}
+
int encoder_thread(void *arg)
{
OutputStream *ost = arg;
@@ -927,12 +940,8 @@ int encoder_thread(void *arg)
}
// flush the encoder
- if (ret == 0 || ret == AVERROR_EOF) {
- ret = frame_encode(ost, NULL, et.pkt);
- if (ret < 0 && ret != AVERROR_EOF)
- av_log(e, AV_LOG_ERROR, "Error flushing encoder: %s\n",
- av_err2str(ret));
- }
+ if (ret == 0 || ret == AVERROR_EOF)
+ ret = flush_encoder(ost, &et);
// EOF is normal thread termination
if (ret == AVERROR_EOF)
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 10/11] fftools/ffmpeg_enc: store a few more AVCodecContext fields in Encoder
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (7 preceding siblings ...)
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 ` James Almer
2025-02-18 13:08 ` [FFmpeg-devel] [PATCH 11/11] fftools/ffmpeg: support reinitializing the encoder James Almer
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
Preparation work for the following commits.
Signed-off-by: James Almer <jamrial@gmail.com>
---
fftools/ffmpeg.h | 4 ++++
fftools/ffmpeg_enc.c | 5 +++++
fftools/ffmpeg_mux_init.c | 8 ++++----
3 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 6cc0da05a0..af76d81a10 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -567,6 +567,10 @@ typedef struct Encoder {
AVCodecContext *enc_ctx;
+ uint32_t codec_tag;
+ int flags;
+ int global_quality;
+
// number of frames/samples sent to the encoder
uint64_t frames_encoded;
uint64_t samples_encoded;
diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index acb0702385..32b41ea51c 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -218,6 +218,11 @@ static int enc_reopen(void *opaque, const AVFrame *frame)
if (ist)
dec = ist->decoder;
+ if (ost->enc->codec_tag)
+ enc_ctx->codec_tag = e->codec_tag;
+ enc_ctx->flags |= e->flags;
+ enc_ctx->global_quality = e->global_quality;
+
// the timebase is chosen by filtering code
if (ost->type == AVMEDIA_TYPE_AUDIO || ost->type == AVMEDIA_TYPE_VIDEO) {
enc_ctx->time_base = frame->time_base;
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 4a973e5286..3eff514f7b 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -1490,13 +1490,13 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
ost->st->codecpar->codec_tag = tag;
ms->par_in->codec_tag = tag;
if (ost->enc)
- ost->enc->enc_ctx->codec_tag = tag;
+ ost->enc->codec_tag = tag;
}
opt_match_per_stream_dbl(ost, &o->qscale, oc, st, &qscale);
if (ost->enc && qscale >= 0) {
- ost->enc->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE;
- ost->enc->enc_ctx->global_quality = FF_QP2LAMBDA * qscale;
+ ost->enc->flags |= AV_CODEC_FLAG_QSCALE;
+ ost->enc->global_quality = FF_QP2LAMBDA * qscale;
}
if (ms->sch_idx >= 0) {
@@ -1519,7 +1519,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
oc, st, &ost->fix_sub_duration_heartbeat);
if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc)
- ost->enc->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
+ ost->enc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
opt_match_per_stream_int(ost, &o->copy_initial_nonkeyframes,
oc, st, &ms->copy_initial_nonkeyframes);
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH 11/11] fftools/ffmpeg: support reinitializing the encoder
2025-02-18 13:08 [FFmpeg-devel] [PATCH 01/11] avutil/opt: allow passing a fake object to av_opt_set() James Almer
` (8 preceding siblings ...)
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 ` James Almer
9 siblings, 0 replies; 11+ messages in thread
From: James Almer @ 2025-02-18 13:08 UTC (permalink / raw)
To: ffmpeg-devel
With this, an encoding process can change certain values that until now were
settable during init only.
A graceful reconfiguration will be attempted first, which depends on the
capabilities of the underlying encoder. If that fails, a full encoder restart
is performed instead.
The reconfiguration parameters are passed as a list of target timestamp and
key=value pairs to be applied at that point during encoding. Future
improvements may also include targetting encoded frame number, and extending
the command feature to also support passing encoder runtime arguments and not
just filter runtime change arguments.
Example:
ffmpeg -i INPUT -reinit_opts:v:0 "pts=100000|crf=18,pts=200000|video_size=1280x720:crf=20" -crf 30 -c:v:0 libx264 OUTPUT
Signed-off-by: James Almer <jamrial@gmail.com>
---
doc/ffmpeg.texi | 5 ++
fftools/ffmpeg.c | 4 ++
fftools/ffmpeg.h | 10 +++
fftools/ffmpeg_enc.c | 141 +++++++++++++++++++++++++++++++++++++-
fftools/ffmpeg_filter.c | 129 +++++++++++++++++++++++++++++++++-
fftools/ffmpeg_mux_init.c | 16 ++++-
fftools/ffmpeg_opt.c | 5 ++
7 files changed, 303 insertions(+), 7 deletions(-)
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index da6549f043..0e98838b44 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -1209,6 +1209,11 @@ ffmpeg -i input.iamf -c:a copy -stream_group map=0=0:st=0:st=1:st=2:st=3 -stream
-streamid 0:0 -streamid 1:1 -streamid 2:2 -streamid 3:3 output.mp4
@end example
+@item -reinit_opts[:@var{stream_specifier}] pts=@var{pts}|@var{key}=@var{value}[:@var{key}=@var{value},pts=@var{pts}...] (@emph{output,per-stream})
+
+Creates a "," separated group of options to reconfigure an encoder at the
+specified output @var{pts} in AV_TIME_BASE units.
+
@item -target @var{type} (@emph{output})
Specify target file type (@code{vcd}, @code{svcd}, @code{dvd}, @code{dv},
@code{dv50}). @var{type} may be prefixed with @code{pal-}, @code{ntsc-} or
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index dc321fb4a2..8a19aeebf6 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -394,6 +394,7 @@ static void frame_data_free(void *opaque, uint8_t *data)
FrameData *fd = (FrameData *)data;
avcodec_parameters_free(&fd->par_enc);
+ av_dict_free(&fd->reinit_opts);
av_free(data);
}
@@ -422,6 +423,7 @@ static int frame_data_ensure(AVBufferRef **dst, int writable)
memcpy(fd, fd_src, sizeof(*fd));
fd->par_enc = NULL;
+ fd->reinit_opts = NULL;
if (fd_src->par_enc) {
int ret = 0;
@@ -430,6 +432,8 @@ static int frame_data_ensure(AVBufferRef **dst, int writable)
ret = fd->par_enc ?
avcodec_parameters_copy(fd->par_enc, fd_src->par_enc) :
AVERROR(ENOMEM);
+ if (!ret && fd_src->reinit_opts)
+ ret = av_dict_copy(&fd->reinit_opts, fd_src->reinit_opts, 0);
if (ret < 0) {
av_buffer_unref(dst);
av_buffer_unref(&src);
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index af76d81a10..061898dd5f 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -248,6 +248,7 @@ typedef struct OptionsContext {
SpecifierOptList enc_time_bases;
SpecifierOptList autoscale;
SpecifierOptList bits_per_raw_sample;
+ SpecifierOptList enc_reinit_opts;
SpecifierOptList enc_stats_pre;
SpecifierOptList enc_stats_post;
SpecifierOptList mux_stats;
@@ -341,6 +342,8 @@ typedef struct OutputFilterOptions {
const enum AVColorSpace *color_spaces;
const enum AVColorRange *color_ranges;
+ const char *reinit_opts;
+
// for simple filtergraphs only, view specifier passed
// along to the decoder
const ViewSpecifier *vs;
@@ -567,6 +570,11 @@ typedef struct Encoder {
AVCodecContext *enc_ctx;
+ // initial encoder options
+ AVDictionary *encoder_opts;
+ // pts|key=value list of options to reinitialize encoder
+ char *reinit_opts;
+
uint32_t codec_tag;
int flags;
int global_quality;
@@ -676,6 +684,8 @@ typedef struct FrameData {
int64_t wallclock[LATENCY_PROBE_NB];
AVCodecParameters *par_enc;
+
+ AVDictionary *reinit_opts;
} FrameData;
extern InputFile **input_files;
diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index 32b41ea51c..c657238072 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -31,6 +31,7 @@
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
+#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/rational.h"
#include "libavutil/time.h"
@@ -49,6 +50,7 @@ typedef struct EncoderPriv {
// number of packets received from the encoder
uint64_t packets_encoded;
+ int got_first_packet;
int opened;
int attach_par;
@@ -78,6 +80,7 @@ void enc_free(Encoder **penc)
if (enc->enc_ctx)
av_freep(&enc->enc_ctx->stats_in);
avcodec_free_context(&enc->enc_ctx);
+ av_dict_free(&enc->encoder_opts);
av_freep(penc);
}
@@ -96,6 +99,29 @@ static const AVClass enc_class = {
.item_name = enc_item_name,
};
+static int enc_realloc(Encoder *enc, const AVCodec *codec)
+{
+ EncoderPriv *ep = ep_from_enc(enc);
+ char *stats_in = NULL;
+
+ if (enc->enc_ctx)
+ stats_in = enc->enc_ctx->stats_in;
+ avcodec_free_context(&enc->enc_ctx);
+
+ ep->opened = 0;
+ ep->got_first_packet = 0;
+
+ enc->enc_ctx = avcodec_alloc_context3(codec);
+ if (!enc->enc_ctx) {
+ av_freep(&stats_in);
+ return AVERROR(ENOMEM);
+ }
+
+ enc->enc_ctx->stats_in = stats_in;
+
+ return 0;
+}
+
int enc_alloc(Encoder **penc, const AVCodec *codec,
Scheduler *sch, unsigned sch_idx, void *log_parent)
{
@@ -181,7 +207,26 @@ static int hw_device_setup_for_encode(Encoder *e, AVCodecContext *enc_ctx,
return 0;
}
-static int enc_reopen(void *opaque, const AVFrame *frame)
+static int apply_enc_options(Encoder *e, AVDictionary **opts)
+{
+ AVCodecContext *enc_ctx = e->enc_ctx;
+
+ int ret = av_opt_set_dict2(enc_ctx, opts, AV_OPT_SEARCH_CHILDREN);
+ if (ret < 0) {
+ av_log(e, AV_LOG_ERROR, "Error applying encoder options: %s\n",
+ av_err2str(ret));
+ return ret;
+ }
+
+ ret = check_avoptions(*opts);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int enc_reopen(void *opaque, const AVFrame *frame,
+ AVDictionary **extra_encoder_opts)
{
OutputStream *ost = opaque;
InputStream *ist = ost->ist;
@@ -190,9 +235,32 @@ static int enc_reopen(void *opaque, const AVFrame *frame)
AVCodecContext *enc_ctx = e->enc_ctx;
Decoder *dec = NULL;
const AVCodec *enc = enc_ctx->codec;
+ AVDictionary *encoder_opts = NULL;
FrameData *fd;
+ int threads_manual;
int ret;
+ ret = av_dict_copy(&encoder_opts, ost->enc->encoder_opts, 0);
+ if (ret < 0)
+ return ret;
+
+ threads_manual = !!av_dict_get(encoder_opts, "threads", NULL, 0);
+ ret = apply_enc_options(e, &encoder_opts);
+ av_dict_free(&encoder_opts);
+ if (ret < 0)
+ return ret;
+
+ if (extra_encoder_opts) {
+ threads_manual |= !!av_dict_get(*extra_encoder_opts, "threads", NULL, 0);
+ ret = apply_enc_options(e, extra_encoder_opts);
+ if (ret < 0)
+ return ret;
+ }
+
+ // default to automatic thread count
+ if (!threads_manual)
+ enc_ctx->thread_count = 0;
+
// frame is always non-NULL for audio and video
av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO && enc->type != AVMEDIA_TYPE_AUDIO));
@@ -364,7 +432,7 @@ int enc_open(void *opaque, const AVFrame *frame)
if (ep->opened)
return 0;
- ret = enc_reopen(opaque, frame);
+ ret = enc_reopen(opaque, frame, NULL);
if (ret < 0)
return ret;
@@ -696,9 +764,19 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame,
return AVERROR(ENOMEM);
fd->wallclock[LATENCY_PROBE_ENC_POST] = av_gettime_relative();
+ // attach extradata to first packet if the encoder was reinitialized
+ if (!ep->got_first_packet && ep->packets_encoded && enc->extradata_size) {
+ uint8_t *extradata = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+ enc->extradata_size);
+ if (!extradata)
+ return AVERROR(ENOMEM);
+ memcpy(extradata, enc->extradata, enc->extradata_size);
+ ep->got_first_packet = 1;
+ }
// attach stream parameters to first packet if requested
avcodec_parameters_free(&fd->par_enc);
- if (ep->attach_par && !ep->packets_encoded) {
+ if (!ep->packets_encoded) {
+ if (ep->attach_par) {
fd->par_enc = avcodec_parameters_alloc();
if (!fd->par_enc)
return AVERROR(ENOMEM);
@@ -706,6 +784,8 @@ static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame,
ret = avcodec_parameters_from_context(fd->par_enc, enc);
if (ret < 0)
return ret;
+ }
+ ep->got_first_packet = 1;
}
pkt->flags |= AV_PKT_FLAG_TRUSTED;
@@ -880,12 +960,57 @@ static int flush_encoder(OutputStream *ost, EncoderThread *et)
return ret;
}
+static int reinit_encoder(OutputStream *ost, EncoderThread *et)
+{
+ Encoder *e = ost->enc;
+ AVDictionary *copy = NULL;
+ const FrameData *fd = frame_data_c(et->frame);
+ int ret = AVERROR_BUG;
+
+ // Lets try a graceful reconfiguration first
+ if (e->enc_ctx->codec->capabilities & AV_CODEC_CAP_RECONF) {
+ ret = av_dict_copy(©, fd->reinit_opts, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_encode_reconfigure(e->enc_ctx, ©);
+ av_dict_free(©);
+ if (!ret)
+ return 0;
+
+ av_log(e, AV_LOG_INFO, "Could not reconfigure the encoder."
+ " Trying to restart it instead\n");
+ }
+ ret = av_dict_copy(©, fd->reinit_opts, 0);
+ if (ret < 0)
+ return ret;
+
+ // Go ahead and do a full restart of the encoder
+ ret = flush_encoder(ost, et);
+ if (ret < 0 && ret != AVERROR_EOF)
+ goto end;
+
+ ret = enc_realloc(e, e->enc_ctx->codec);
+ if (ret < 0)
+ goto end;
+ av_log(e, AV_LOG_DEBUG, "Restarting encoder\n");
+ ret = enc_reopen(ost, et->frame, ©);
+ if (ret < 0)
+ goto end;
+
+ ret = 0;
+end:
+ av_dict_free(©);
+ return ret;
+}
+
int encoder_thread(void *arg)
{
OutputStream *ost = arg;
Encoder *e = ost->enc;
EncoderPriv *ep = ep_from_enc(e);
EncoderThread et;
+ const FrameData *fd;
int ret = 0, input_status = 0;
int name_set = 0;
@@ -929,6 +1054,16 @@ int encoder_thread(void *arg)
name_set = 1;
}
+ fd = frame_data_c(et.frame);
+ if (fd && fd->reinit_opts) {
+ ret = reinit_encoder(ost, &et);
+ if (ret < 0) {
+ av_log(e, AV_LOG_ERROR, "Error reconfiguring or restarting encoder: %s\n",
+ av_err2str(ret));
+ goto finish;
+ }
+ }
+
ret = frame_encode(ost, et.frame, et.pkt);
av_packet_unref(et.pkt);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 09086ca67c..23e6da8902 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -192,6 +192,11 @@ typedef struct FPSConvContext {
int framerate_clip;
} FPSConvContext;
+typedef struct ReinitOpts {
+ int64_t pts;
+ AVDictionary *dict;
+} ReinitOpts;
+
typedef struct OutputFilterPriv {
OutputFilter ofilter;
@@ -243,6 +248,9 @@ typedef struct OutputFilterPriv {
int64_t next_pts;
FPSConvContext fps;
+ AVFifo *reinit_opts_fifo;
+ ReinitOpts reinit_opts;
+
unsigned flags;
} OutputFilterPriv;
@@ -674,6 +682,7 @@ static OutputFilter *ofilter_alloc(FilterGraph *fg, enum AVMediaType type)
ofilter->graph = fg;
ofilter->type = type;
av_opt_set_defaults(ofp);
+ ofp->reinit_opts = (ReinitOpts){ .pts = AV_NOPTS_VALUE };
ofp->index = fg->nb_outputs - 1;
snprintf(ofp->log_name, sizeof(ofp->log_name), "%co%d",
@@ -806,6 +815,75 @@ static int set_channel_layout(OutputFilterPriv *f, const AVChannelLayout *layout
return 0;
}
+static int parse_reinit_opts(AVFifo **pout, const char *opts, void *logctx)
+{
+ AVFifo *out;
+ int ret = AVERROR_BUG;
+ char *ptr, *str, *substr = NULL;
+ const char *token;
+
+ str = av_strdup(opts);
+ if (!str)
+ return AVERROR(ENOMEM);
+
+ out = av_fifo_alloc2(1, sizeof(ReinitOpts), AV_FIFO_FLAG_AUTO_GROW);
+ if (!out) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+
+ token = av_strtok(str, ",", &ptr);
+ while (token) {
+ ReinitOpts o = { 0 };
+ const char *subtoken;
+ char *subptr, *endptr;
+
+ substr = av_strdup(token);
+ if (!substr) {
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ subtoken = av_strtok(substr, "|", &subptr);
+ if (subtoken && subptr) {
+ if (!av_strstart(subtoken, "pts=", &subtoken)) {
+ av_log(logctx, AV_LOG_ERROR, "Invalid reinit identifier\n");
+ ret = AVERROR(ENOMEM);
+ goto end;
+ }
+ o.pts = strtoll(subtoken, &endptr, 0);
+ if (*endptr || o.pts < 0) {
+ ret = AVERROR(EINVAL);
+ goto end;
+ }
+ ret = av_dict_parse_string(&o.dict, subptr, "=", ":", 0);
+ if (ret < 0) {
+ av_log(logctx, AV_LOG_ERROR, "Error parsing encoder options\n");
+ goto end;
+ }
+ ret = av_fifo_write(out, &o, 1);
+ if (ret < 0) {
+ av_dict_free(&o.dict);
+ goto end;
+ }
+ } else {
+ ret = AVERROR(EINVAL);
+ goto end;
+ }
+ av_freep(&substr);
+ if (ptr)
+ ptr += strspn(ptr, " \n\t\r");
+ token = av_strtok(NULL, ",", &ptr);
+ }
+
+ ret = 0;
+end:
+ *pout = out;
+ av_free(substr);
+ av_free(str);
+
+ return ret;
+}
+
int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc,
const OutputFilterOptions *opts)
{
@@ -832,6 +910,12 @@ int ofilter_bind_enc(OutputFilter *ofilter, unsigned sched_idx_enc,
if (!ofp->name)
return AVERROR(EINVAL);
+ if (opts->reinit_opts) {
+ ret = parse_reinit_opts(&ofp->reinit_opts_fifo, opts->reinit_opts, ofilter);
+ if (ret < 0)
+ return ret;
+ }
+
ret = av_dict_copy(&ofp->sws_opts, opts->sws_opts, 0);
if (ret < 0)
return ret;
@@ -1045,6 +1129,12 @@ void fg_free(FilterGraph **pfg)
av_freep(&ofilter->name);
av_freep(&ofilter->apad);
av_freep(&ofp->name);
+ av_dict_free(&ofp->reinit_opts.dict);
+ if (ofp->reinit_opts_fifo) {
+ while (av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1) >= 0)
+ av_dict_free(&ofp->reinit_opts.dict);
+ av_fifo_freep2(&ofp->reinit_opts_fifo);
+ }
av_channel_layout_uninit(&ofp->ch_layout);
av_frame_side_data_free(&ofp->side_data, &ofp->nb_side_data);
av_freep(&fg->outputs[j]);
@@ -2864,7 +2954,7 @@ static const char *unknown_if_null(const char *str)
}
static int send_frame(FilterGraph *fg, FilterGraphThread *fgt,
- InputFilter *ifilter, AVFrame *frame)
+ InputFilter *ifilter, AVFrame *frame, int force_reinit)
{
InputFilterPriv *ifp = ifp_from_ifilter(ifilter);
FrameData *fd;
@@ -2917,7 +3007,7 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt,
}
/* (re)init the graph if possible, otherwise buffer the frame and return */
- if (need_reinit || !fgt->graph) {
+ if (need_reinit || force_reinit || !fgt->graph) {
AVFrame *tmp = av_frame_alloc();
if (!tmp)
@@ -2961,6 +3051,8 @@ static int send_frame(FilterGraph *fg, FilterGraphThread *fgt,
av_bprintf(&reason, "downmix medatata changed, ");
if (need_reinit & HWACCEL_CHANGED)
av_bprintf(&reason, "hwaccel changed, ");
+ if (force_reinit)
+ av_bprintf(&reason, "reinitialization arguments were provided, ");
if (reason.len > 1)
reason.str[reason.len - 2] = '\0'; // remove last comma
av_log(fg, AV_LOG_INFO, "Reconfiguring filter graph%s%s\n", reason.len ? " because " : "", reason.str);
@@ -3125,7 +3217,38 @@ static int filter_thread(void *arg)
ret = sub2video_frame(ifilter, (fgt.frame->buf[0] || hb_frame) ? fgt.frame : NULL,
!fgt.graph);
} else if (fgt.frame->buf[0]) {
- ret = send_frame(fg, &fgt, ifilter, fgt.frame);
+ int reinit = 0;
+ for (unsigned i = 0; i < fg->nb_outputs; i++) {
+ OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]);
+
+ if (ofp->reinit_opts.pts == AV_NOPTS_VALUE && ofp->reinit_opts_fifo &&
+ av_fifo_can_read(ofp->reinit_opts_fifo)) {
+ av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1);
+ }
+ if (ofp->reinit_opts.pts != AV_NOPTS_VALUE &&
+ ofp->reinit_opts.pts == av_rescale_q(fgt.frame->pts, fgt.frame->time_base, AV_TIME_BASE_Q)) {
+ FrameData *fd = frame_data(fgt.frame);
+ if (!fd) {
+ ret = AVERROR(ENOMEM);
+ goto finish;
+ }
+
+ av_dict_free(&fd->reinit_opts);
+ ret = av_dict_copy(&fd->reinit_opts, ofp->reinit_opts.dict, 0);
+ if (ret < 0)
+ goto finish;
+
+ av_opt_set_dict(ofp, &ofp->reinit_opts.dict);
+ av_dict_free(&ofp->reinit_opts.dict);
+
+ if (av_fifo_can_read(ofp->reinit_opts_fifo))
+ av_fifo_read(ofp->reinit_opts_fifo, &ofp->reinit_opts, 1);
+ else
+ ofp->reinit_opts = (ReinitOpts){ .pts = AV_NOPTS_VALUE };
+ reinit = 1;
+ }
+ }
+ ret = send_frame(fg, &fgt, ifilter, fgt.frame, reinit);
} else {
av_assert1(o == FRAME_OPAQUE_EOF);
ret = send_eof(&fgt, ifilter, fgt.frame->pts, fgt.frame->time_base);
diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c
index 3eff514f7b..bd0fe90c06 100644
--- a/fftools/ffmpeg_mux_init.c
+++ b/fftools/ffmpeg_mux_init.c
@@ -931,6 +931,7 @@ ost_bind_filter(const Muxer *mux, MuxStream *ms, OutputFilter *ofilter,
0 : mux->of.start_time,
.vs = vs,
.nb_threads = -1,
+ .reinit_opts = ost->enc->reinit_opts,
.flags = OFILTER_FLAG_DISABLE_CONVERT * !!keep_pix_fmt |
OFILTER_FLAG_AUTOSCALE * !!autoscale |
@@ -1301,7 +1302,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
AVIOContext *s = NULL;
char *buf = NULL, *arg = NULL;
const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL;
- const char *enc_time_base = NULL, *preset = NULL;
+ const char *enc_time_base = NULL, *enc_reinit_opts = NULL, *preset = NULL;
ret = filter_codec_opts(o->g->codec_opts, enc->id,
oc, st, enc, &encoder_opts,
@@ -1341,6 +1342,16 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
goto fail;
}
+ opt_match_per_stream_str(ost, &o->enc_reinit_opts, oc, st, &enc_reinit_opts);
+ if (enc_reinit_opts &&
+ (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) {
+ ost->enc->reinit_opts = av_strdup(enc_reinit_opts);
+ if (!ost->enc->reinit_opts) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+
opt_match_per_stream_str(ost, &o->enc_stats_pre, oc, st, &enc_stats_pre);
if (enc_stats_pre &&
(type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) {
@@ -1411,6 +1422,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type,
threads_manual = !!av_dict_get(encoder_opts, "threads", NULL, 0);
+ ret = av_dict_copy(&ost->enc->encoder_opts, encoder_opts, 0);
+ if (ret < 0)
+ goto fail;
ret = av_opt_set_dict2(ost->enc->enc_ctx, &encoder_opts, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
av_log(ost, AV_LOG_ERROR, "Error applying encoder options: %s\n",
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 3c0c682594..76aabb7581 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -1788,6 +1788,11 @@ const OptionDef options[] = {
{ .off = OFFSET(mux_stats_fmt) },
"format of the stats written with -stats_mux_pre" },
+ { "reinit_opts", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT,
+ { .off = OFFSET(enc_reinit_opts) },
+ "List of encoder options to use to reinitialize the encoder at given timestamps",
+ "pts1|video_size=size:g=12,pts2|video_size=size..." },
+
/* video options */
{ "vframes", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_CANON,
{ .func_arg = opt_video_frames },
--
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".
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-02-18 13:10 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [FFmpeg-devel] [PATCH 02/11] avcodec/encode: add a function to gracefully reconfigure an encoder James Almer
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
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