PR #22732 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22732 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22732.patch
Should help reduce the amount of allocations where av_frame_alloc() is called repeatedly, as implemented in libavfilter in this same PR. >From 7122dc2331877813f193aec65d26f644166cc988 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 6 Apr 2026 14:53:03 -0300 Subject: [PATCH 1/4] avutil/avframe: use reftruct API to allocate frames This is needed for the next commit. Signed-off-by: James Almer <[email protected]> --- libavutil/frame.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libavutil/frame.c b/libavutil/frame.c index be30eb09d2..960f969678 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -49,9 +49,15 @@ static void get_frame_defaults(AVFrame *frame) frame->flags = 0; } +static void frame_unref(AVRefStructOpaque opaque, void *obj) +{ + av_frame_unref(obj); +} + AVFrame *av_frame_alloc(void) { - AVFrame *frame = av_malloc(sizeof(*frame)); + AVFrame *frame = av_refstruct_alloc_ext(sizeof(*frame), AV_REFSTRUCT_FLAG_NO_ZEROING, + NULL, frame_unref); if (!frame) return NULL; @@ -66,8 +72,7 @@ void av_frame_free(AVFrame **frame) if (!frame || !*frame) return; - av_frame_unref(*frame); - av_freep(frame); + av_refstruct_unref(frame); } #define ALIGN (HAVE_SIMD_ALIGN_64 ? 64 : 32) -- 2.52.0 >From 25a1a9051a365dd62d0fd2687358853a2221582b Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 6 Apr 2026 15:25:47 -0300 Subject: [PATCH 2/4] avutil/frame: add an API to allocate a pool of AVFrame Should help reduce the amount of allocations where av_frame_alloc() is called repeatedly. Signed-off-by: James Almer <[email protected]> --- libavutil/frame.c | 24 ++++++++++++++++++++++++ libavutil/frame.h | 9 +++++++++ 2 files changed, 33 insertions(+) diff --git a/libavutil/frame.c b/libavutil/frame.c index 960f969678..2ced8e7ff3 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -732,6 +732,30 @@ void av_frame_remove_side_data(AVFrame *frame, enum AVFrameSideDataType type) av_frame_side_data_remove(&frame->side_data, &frame->nb_side_data, type); } +static av_cold int frame_pool_init_cb(AVRefStructOpaque opaque, void *obj) +{ + AVFrame *frame = obj; + + get_frame_defaults(frame); + + return 0; +} + +static void frame_pool_reset_cb(AVRefStructOpaque unused, void *obj) +{ + AVFrame *frame = obj; + + av_frame_unref(frame); +} + +AVRefStructPool *av_frame_pool_alloc(void) +{ + return av_refstruct_pool_alloc_ext(sizeof(AVFrame), AV_REFSTRUCT_POOL_FLAG_NO_ZEROING, NULL, + frame_pool_init_cb, + frame_pool_reset_cb, + NULL, NULL); +} + static int calc_cropping_offsets(size_t offsets[4], const AVFrame *frame, const AVPixFmtDescriptor *desc) { diff --git a/libavutil/frame.h b/libavutil/frame.h index 771c9ce453..40d684a37e 100644 --- a/libavutil/frame.h +++ b/libavutil/frame.h @@ -33,6 +33,7 @@ #include "channel_layout.h" #include "dict.h" #include "rational.h" +#include "refstruct.h" #include "samplefmt.h" #include "pixfmt.h" #include "version.h" @@ -1169,6 +1170,14 @@ void av_frame_side_data_remove(AVFrameSideData ***sd, int *nb_sd, void av_frame_side_data_remove_by_props(AVFrameSideData ***sd, int *nb_sd, int props); +/** + * Allocate an AVRefStructPool of AVFrames. + * + * AVFrames must be fetched with @ref av_refstruct_pool_get() and behave + * as if they were freshly allocated by av_frame_alloc(). + */ +AVRefStructPool *av_frame_pool_alloc(void); + /** * @} */ -- 2.52.0 >From 7d5710e742afeb075edbb908cb32aebab5ae0ed2 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 6 Apr 2026 15:26:48 -0300 Subject: [PATCH 3/4] avfilter/framepool: use av_frame_pool_alloc() to allocate AVFrame Signed-off-by: James Almer <[email protected]> --- libavfilter/framepool.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libavfilter/framepool.c b/libavfilter/framepool.c index 0259415620..89e31930a7 100644 --- a/libavfilter/framepool.c +++ b/libavfilter/framepool.c @@ -47,6 +47,7 @@ struct FFFramePool { int linesize[4]; AVBufferPool *pools[4]; + AVRefStructPool *frame_pool; }; av_cold FFFramePool *ff_frame_pool_video_init(AVBufferRef* (*alloc)(size_t size), @@ -94,6 +95,10 @@ av_cold FFFramePool *ff_frame_pool_video_init(AVBufferRef* (*alloc)(size_t size) goto fail; } + pool->frame_pool = av_frame_pool_alloc(); + if (!pool->frame_pool) + goto fail; + for (i = 0; i < 4 && sizes[i]; i++) { if (sizes[i] > SIZE_MAX - align) goto fail; @@ -137,6 +142,11 @@ av_cold FFFramePool *ff_frame_pool_audio_init(AVBufferRef* (*alloc)(size_t size) if (pool->linesize[0] > SIZE_MAX - align) goto fail; + + pool->frame_pool = av_frame_pool_alloc(); + if (!pool->frame_pool) + goto fail; + pool->pools[0] = av_buffer_pool_init(pool->linesize[0] + align, NULL); if (!pool->pools[0]) goto fail; @@ -192,7 +202,7 @@ AVFrame *ff_frame_pool_get(FFFramePool *pool) AVFrame *frame; const AVPixFmtDescriptor *desc; - frame = av_frame_alloc(); + frame = av_refstruct_pool_get(pool->frame_pool); if (!frame) { return NULL; } @@ -283,6 +293,8 @@ av_cold void ff_frame_pool_uninit(FFFramePool **pool) if (!*pool) return; + av_refstruct_pool_uninit(&(*pool)->frame_pool); + for (i = 0; i < 4; i++) { av_buffer_pool_uninit(&(*pool)->pools[i]); } -- 2.52.0 >From 8c7bf56038ef4a6d00ed049136591ba61c43e23a Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 6 Apr 2026 16:08:00 -0300 Subject: [PATCH 4/4] avfilter/framepool: don't unnecessarily uninitialize the AVRefStructPool of frames AVFrame allocations can be reused even if the pool needs to be reinitialized because of frame prop changes. Signed-off-by: James Almer <[email protected]> --- libavfilter/audio.c | 5 ++--- libavfilter/framepool.c | 45 ++++++++++++++++++++++++++--------------- libavfilter/framepool.h | 18 +++++++++++------ libavfilter/video.c | 5 ++--- 4 files changed, 45 insertions(+), 28 deletions(-) diff --git a/libavfilter/audio.c b/libavfilter/audio.c index 7d87e9e9a0..d0ed92e75c 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -51,7 +51,7 @@ AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples) int align = av_cpu_max_align(); if (!li->frame_pool) { - li->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, + li->frame_pool = ff_frame_pool_audio_init(li->frame_pool, av_buffer_allocz, channels, nb_samples, link->format, align); if (!li->frame_pool) return NULL; @@ -70,8 +70,7 @@ AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples) if (pool_channels != channels || pool_nb_samples < nb_samples || pool_format != link->format || pool_align != align) { - ff_frame_pool_uninit(&li->frame_pool); - li->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, + li->frame_pool = ff_frame_pool_audio_init(li->frame_pool, av_buffer_allocz, channels, nb_samples, link->format, align); if (!li->frame_pool) return NULL; diff --git a/libavfilter/framepool.c b/libavfilter/framepool.c index 89e31930a7..c9b19516f6 100644 --- a/libavfilter/framepool.c +++ b/libavfilter/framepool.c @@ -47,22 +47,35 @@ struct FFFramePool { int linesize[4]; AVBufferPool *pools[4]; - AVRefStructPool *frame_pool; + AVRefStructPool *frame_pool; //< Must be the last field! }; -av_cold FFFramePool *ff_frame_pool_video_init(AVBufferRef* (*alloc)(size_t size), +static av_cold void frame_pool_reset(FFFramePool *pool) +{ + for (int i = 0; i < 4; i++) { + av_buffer_pool_uninit(&pool->pools[i]); + } + + memset(pool, 0, offsetof(FFFramePool, frame_pool)); +} + +av_cold FFFramePool *ff_frame_pool_video_init(FFFramePool *pool, + AVBufferRef* (*alloc)(size_t size), int width, int height, enum AVPixelFormat format, int align) { int i, ret; - FFFramePool *pool; ptrdiff_t linesizes[4]; size_t sizes[4]; - pool = av_mallocz(sizeof(FFFramePool)); - if (!pool) - return NULL; + if (pool) + frame_pool_reset(pool); + else { + pool = av_mallocz(sizeof(FFFramePool)); + if (!pool) + return NULL; + } pool->type = AVMEDIA_TYPE_VIDEO; pool->width = width; @@ -114,17 +127,21 @@ fail: return NULL; } -av_cold FFFramePool *ff_frame_pool_audio_init(AVBufferRef* (*alloc)(size_t size), +av_cold FFFramePool *ff_frame_pool_audio_init(FFFramePool *pool, + AVBufferRef* (*alloc)(size_t size), int channels, int nb_samples, enum AVSampleFormat format, int align) { int ret, planar; - FFFramePool *pool; - pool = av_mallocz(sizeof(FFFramePool)); - if (!pool) - return NULL; + if (pool) + frame_pool_reset(pool); + else { + pool = av_mallocz(sizeof(FFFramePool)); + if (!pool) + return NULL; + } planar = av_sample_fmt_is_planar(format); @@ -288,16 +305,12 @@ fail: av_cold void ff_frame_pool_uninit(FFFramePool **pool) { - int i; - if (!*pool) return; av_refstruct_pool_uninit(&(*pool)->frame_pool); - for (i = 0; i < 4; i++) { - av_buffer_pool_uninit(&(*pool)->pools[i]); - } + frame_pool_reset(*pool); av_freep(pool); } diff --git a/libavfilter/framepool.h b/libavfilter/framepool.h index 2bef1fe0b6..31e4c56bc6 100644 --- a/libavfilter/framepool.h +++ b/libavfilter/framepool.h @@ -33,8 +33,10 @@ typedef struct FFFramePool FFFramePool; /** - * Allocate and initialize a video frame pool. + * Initialize and if needed allocate a video frame pool. * + * @param pool an already allocated pool, or NULL to allocate a new one. If not + * NULL, the pool will be reset on success and freed on error. * @param alloc a function that will be used to allocate new frame buffers when * the pool is empty. May be NULL, then the default allocator will be used * (av_buffer_alloc()). @@ -42,17 +44,20 @@ typedef struct FFFramePool FFFramePool; * @param height height of each frame in this pool * @param format format of each frame in this pool * @param align buffers alignment of each frame in this pool - * @return newly created video frame pool on success, NULL on error. + * @return a video frame pool on success, NULL on error. */ -FFFramePool *ff_frame_pool_video_init(AVBufferRef* (*alloc)(size_t size), +FFFramePool *ff_frame_pool_video_init(FFFramePool *pool, + AVBufferRef* (*alloc)(size_t size), int width, int height, enum AVPixelFormat format, int align); /** - * Allocate and initialize an audio frame pool. + * Initialize and if needed allocate an audio frame pool. * + * @param pool an already allocated pool, or NULL to allocate a new one. If not + * NULL, the pool will be reset on success and freed on error. * @param alloc a function that will be used to allocate new frame buffers when * the pool is empty. May be NULL, then the default allocator will be used * (av_buffer_alloc()). @@ -60,9 +65,10 @@ FFFramePool *ff_frame_pool_video_init(AVBufferRef* (*alloc)(size_t size), * @param nb_samples number of samples of each frame in this pool * @param format format of each frame in this pool * @param align buffers alignment of each frame in this pool - * @return newly created audio frame pool on success, NULL on error. + * @return a audio frame pool on success, NULL on error. */ -FFFramePool *ff_frame_pool_audio_init(AVBufferRef* (*alloc)(size_t size), +FFFramePool *ff_frame_pool_audio_init(FFFramePool *pool, + AVBufferRef* (*alloc)(size_t size), int channels, int samples, enum AVSampleFormat format, diff --git a/libavfilter/video.c b/libavfilter/video.c index 3f23a12c07..6916c8de84 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -71,7 +71,7 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int alig } if (!li->frame_pool) { - li->frame_pool = ff_frame_pool_video_init(CONFIG_MEMORY_POISONING + li->frame_pool = ff_frame_pool_video_init(NULL, CONFIG_MEMORY_POISONING ? NULL : av_buffer_allocz, w, h, link->format, align); @@ -88,8 +88,7 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int alig FFALIGN(pool_height, pool_align) != FFALIGN(h, align) || pool_format != link->format || pool_align != align) { - ff_frame_pool_uninit(&li->frame_pool); - li->frame_pool = ff_frame_pool_video_init(CONFIG_MEMORY_POISONING + li->frame_pool = ff_frame_pool_video_init(li->frame_pool, CONFIG_MEMORY_POISONING ? NULL : av_buffer_allocz, w, h, link->format, align); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
