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]

Reply via email to