PR #23842 opened by charlymp
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23842
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23842.patch

Raw and fixed rate video encoders allocate a fresh buffer for every packet, 
which for UHD+ means tens of megabytes malloced and freed per frame.
glibc mitigates this for smaller buffers, as its adaptive mmap threshold lets 
allocations up to 32 MiB be recycled from the heap, but beyond that limit every 
packet costs an mmap/munmap pair plus page faults, and the behavior depends on 
the allocator and its tunables (MALLOC_MMAP_THRESHOLD_, musl, the Windows heap 
whose recycling stops around 1 MiB). 
Recycling large buffers through a general purpose heap also contributes to 
memory fragmentation in long running processes.

This series introduces an internal cap letting encoders declare a constant 
packet size, so the default get_encode_buffer() can serve them from an 
AVBufferPool, mirroring the existing decoder frame pool.
This makes the per packet allocation cost deterministic regardless of the 
allocator and keeps large packet buffers out of the heap, limiting 
fragmentation.
A user supplied get_encode_buffer callback keeps priority, and frame threaded 
encoders work unchanged (one pool per worker).


>From af1b4cbf232bd40a16e0497f15d41d9dc11bef28 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Sat, 18 Jul 2026 17:36:08 +0200
Subject: [PATCH 1/3] avcodec/encode: add FF_CODEC_CAP_FIXED_PACKET_SIZE

Encoders producing packets of constant size for given stream parameters
can set this internal capability to make the default get_encode_buffer()
serve packet buffers from an AVBufferPool instead of allocating a new
buffer for every packet. This makes the allocation cost per packet
deterministic and avoids repeated large allocations for raw video
encoders, where every packet can weigh tens of megabytes. The pool is
recreated when the requested size changes, and a user supplied
get_encode_buffer callback keeps priority since it bypasses the default
allocator entirely.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 libavcodec/avcodec.c        |  1 +
 libavcodec/codec_internal.h |  6 ++++++
 libavcodec/encode.c         | 26 ++++++++++++++++++++++----
 libavcodec/internal.h       |  7 +++++++
 4 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c
index 5320dfc06c..16f95fa4a2 100644
--- a/libavcodec/avcodec.c
+++ b/libavcodec/avcodec.c
@@ -467,6 +467,7 @@ av_cold void ff_codec_close(AVCodecContext *avctx)
         av_frame_free(&avci->recon_frame);
 
         av_refstruct_unref(&avci->pool);
+        av_buffer_pool_uninit(&avci->packet_pool);
         av_refstruct_pool_uninit(&avci->progress_frame_pool);
         if (av_codec_is_decoder(avctx->codec))
             ff_decode_internal_uninit(avctx);
diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h
index 5cb65a06e2..f1e1033cbf 100644
--- a/libavcodec/codec_internal.h
+++ b/libavcodec/codec_internal.h
@@ -88,6 +88,12 @@
  * encoders do.
  */
 #define FF_CODEC_CAP_EOF_FLUSH              (1 << 10)
+/**
+ * The encoder requests output packets of a constant size for given stream
+ * parameters. The default get_encode_buffer() serves them from a buffer pool
+ * instead of allocating a new buffer for every packet.
+ */
+#define FF_CODEC_CAP_FIXED_PACKET_SIZE      (1 << 11)
 
 /**
  * FFCodec.codec_tags termination value
diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index 56b2f4362b..61ff2c6b54 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -92,10 +92,28 @@ int avcodec_default_get_encode_buffer(AVCodecContext 
*avctx, AVPacket *avpkt, in
         return AVERROR(EINVAL);
     }
 
-    ret = av_buffer_realloc(&avpkt->buf, avpkt->size + 
AV_INPUT_BUFFER_PADDING_SIZE);
-    if (ret < 0) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet of size %d\n", 
avpkt->size);
-        return ret;
+    if (ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_FIXED_PACKET_SIZE) 
{
+        AVCodecInternal *avci = avctx->internal;
+        int size = avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE;
+
+        if (avci->packet_pool_size != size) {
+            av_buffer_pool_uninit(&avci->packet_pool);
+            avci->packet_pool = av_buffer_pool_init(size, NULL);
+            if (!avci->packet_pool) {
+                avci->packet_pool_size = 0;
+                return AVERROR(ENOMEM);
+            }
+            avci->packet_pool_size = size;
+        }
+        avpkt->buf = av_buffer_pool_get(avci->packet_pool);
+        if (!avpkt->buf)
+            return AVERROR(ENOMEM);
+    } else {
+        ret = av_buffer_realloc(&avpkt->buf, avpkt->size + 
AV_INPUT_BUFFER_PADDING_SIZE);
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet of size 
%d\n", avpkt->size);
+            return ret;
+        }
     }
     avpkt->data = avpkt->buf->data;
 
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 137fd52745..fc61b9514e 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -68,6 +68,13 @@ typedef struct AVCodecInternal {
 
     struct FramePool *pool;
 
+    /**
+     * Packet buffer pool for encoders with FF_CODEC_CAP_FIXED_PACKET_SIZE,
+     * used by the default get_encode_buffer() callback.
+     */
+    AVBufferPool *packet_pool;
+    int packet_pool_size;
+
     struct AVRefStructPool *progress_frame_pool;
 
     void *thread_ctx;
-- 
2.52.0


>From e939bcddb82ec4c739a47b1c37dde9c4d341c507 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Sat, 18 Jul 2026 17:49:53 +0200
Subject: [PATCH 2/3] avcodec: set FF_CODEC_CAP_FIXED_PACKET_SIZE on constant
 packet size encoders

These encoders request a packet size that only depends on the stream
parameters, so the default get_encode_buffer() can serve every packet
from the buffer pool instead of allocating a fresh buffer each time.
Covers rawvideo, v210, r210, r10k, avrp, avui, yuv4, y41p, bitpacked,
bmp, pam, pbm, pgm, pgmyuv, ppm, pfm, phm, xwd, fits, dvvideo, dnxhd
and libsvtjpegxs, whose rate control produces a fixed codestream
budget per frame.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 libavcodec/avuienc.c         | 1 +
 libavcodec/bitpacked_enc.c   | 1 +
 libavcodec/bmpenc.c          | 1 +
 libavcodec/dnxhdenc.c        | 3 ++-
 libavcodec/dvenc.c           | 1 +
 libavcodec/fitsenc.c         | 1 +
 libavcodec/libsvtjpegxsenc.c | 3 ++-
 libavcodec/pamenc.c          | 1 +
 libavcodec/pnmenc.c          | 6 ++++++
 libavcodec/r210enc.c         | 3 +++
 libavcodec/rawenc.c          | 1 +
 libavcodec/v210enc.c         | 1 +
 libavcodec/xwdenc.c          | 1 +
 libavcodec/y41penc.c         | 1 +
 libavcodec/yuv4enc.c         | 1 +
 15 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/libavcodec/avuienc.c b/libavcodec/avuienc.c
index 0dea87f104..04a55e046d 100644
--- a/libavcodec/avuienc.c
+++ b/libavcodec/avuienc.c
@@ -103,4 +103,5 @@ const FFCodec ff_avui_encoder = {
     .color_ranges   = AVCOL_RANGE_MPEG,
     .init         = avui_encode_init,
     FF_CODEC_ENCODE_CB(avui_encode_frame),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/bitpacked_enc.c b/libavcodec/bitpacked_enc.c
index cde3cf83ee..2b52f84f8f 100644
--- a/libavcodec/bitpacked_enc.c
+++ b/libavcodec/bitpacked_enc.c
@@ -115,4 +115,5 @@ const FFCodec ff_bitpacked_encoder = {
     .init           = encode_init,
     FF_CODEC_ENCODE_CB(encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_YUV422P10),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/bmpenc.c b/libavcodec/bmpenc.c
index 15d6778f24..77931e370f 100644
--- a/libavcodec/bmpenc.c
+++ b/libavcodec/bmpenc.c
@@ -169,4 +169,5 @@ const FFCodec ff_bmp_encoder = {
                   AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, AV_PIX_FMT_RGB4_BYTE,
                   AV_PIX_FMT_BGR4_BYTE, AV_PIX_FMT_GRAY8, AV_PIX_FMT_PAL8,
                   AV_PIX_FMT_MONOBLACK),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/dnxhdenc.c b/libavcodec/dnxhdenc.c
index 844731f6c4..ff2a487a89 100644
--- a/libavcodec/dnxhdenc.c
+++ b/libavcodec/dnxhdenc.c
@@ -1358,7 +1358,8 @@ const FFCodec ff_dnxhd_encoder = {
     .p.priv_class   = &dnxhd_class,
     .defaults       = dnxhd_defaults,
     .p.profiles     = NULL_IF_CONFIG_SMALL(ff_dnxhd_profiles),
-    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
+    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP |
+                      FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 
 void ff_dnxhdenc_init(DNXHDEncContext *ctx)
diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c
index f25c3ddfee..fe88310086 100644
--- a/libavcodec/dvenc.c
+++ b/libavcodec/dvenc.c
@@ -1257,4 +1257,5 @@ const FFCodec ff_dvvideo_encoder = {
     CODEC_PIXFMTS(AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P),
     .color_ranges   = AVCOL_RANGE_MPEG,
     .p.priv_class   = &dvvideo_encode_class,
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/fitsenc.c b/libavcodec/fitsenc.c
index 2a31514068..79fda5839c 100644
--- a/libavcodec/fitsenc.c
+++ b/libavcodec/fitsenc.c
@@ -119,4 +119,5 @@ const FFCodec ff_fits_encoder = {
     FF_CODEC_ENCODE_CB(fits_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_GBRAP16BE, AV_PIX_FMT_GBRP16BE, AV_PIX_FMT_GBRP,
                   AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY8),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/libsvtjpegxsenc.c b/libavcodec/libsvtjpegxsenc.c
index d8dbc93075..ef014f8dfc 100644
--- a/libavcodec/libsvtjpegxsenc.c
+++ b/libavcodec/libsvtjpegxsenc.c
@@ -296,7 +296,8 @@ const FFCodec ff_libsvtjpegxs_encoder = {
     FF_CODEC_ENCODE_CB(svt_jpegxs_enc_encode),
     .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1,
     .caps_internal  = FF_CODEC_CAP_NOT_INIT_THREADSAFE |
-                      FF_CODEC_CAP_AUTO_THREADS,
+                      FF_CODEC_CAP_AUTO_THREADS |
+                      FF_CODEC_CAP_FIXED_PACKET_SIZE,
     CODEC_PIXFMTS_ARRAY(pix_fmts),
     .p.wrapper_name = "libsvtjpegxs",
     .p.priv_class = &svtjpegxs_enc_class,
diff --git a/libavcodec/pamenc.c b/libavcodec/pamenc.c
index e0c6eda235..8a137def10 100644
--- a/libavcodec/pamenc.c
+++ b/libavcodec/pamenc.c
@@ -140,4 +140,5 @@ const FFCodec ff_pam_encoder = {
                   AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY8A,
                   AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_YA16BE,
                   AV_PIX_FMT_MONOBLACK),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/pnmenc.c b/libavcodec/pnmenc.c
index 8f67fe0a3b..7d945ed941 100644
--- a/libavcodec/pnmenc.c
+++ b/libavcodec/pnmenc.c
@@ -233,6 +233,7 @@ const FFCodec ff_pgm_encoder = {
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16BE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 
@@ -245,6 +246,7 @@ const FFCodec ff_pgmyuv_encoder = {
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P16BE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 
@@ -257,6 +259,7 @@ const FFCodec ff_ppm_encoder = {
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48BE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 
@@ -269,6 +272,7 @@ const FFCodec ff_pbm_encoder = {
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_MONOWHITE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 
@@ -282,6 +286,7 @@ const FFCodec ff_pfm_encoder = {
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_GBRPF32LE, AV_PIX_FMT_GRAYF32LE,
                   AV_PIX_FMT_GBRPF32BE, AV_PIX_FMT_GRAYF32BE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 
@@ -305,5 +310,6 @@ const FFCodec ff_phm_encoder = {
     .init           = phm_enc_init,
     FF_CODEC_ENCODE_CB(pnm_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GRAYF32),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
diff --git a/libavcodec/r210enc.c b/libavcodec/r210enc.c
index b1ade5cde6..d54044a7f9 100644
--- a/libavcodec/r210enc.c
+++ b/libavcodec/r210enc.c
@@ -100,6 +100,7 @@ const FFCodec ff_r210_encoder = {
     .init           = encode_init,
     FF_CODEC_ENCODE_CB(encode_frame),
     CODEC_PIXFMTS_ARRAY(pix_fmt),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 #if CONFIG_R10K_ENCODER
@@ -112,6 +113,7 @@ const FFCodec ff_r10k_encoder = {
     .init           = encode_init,
     FF_CODEC_ENCODE_CB(encode_frame),
     CODEC_PIXFMTS_ARRAY(pix_fmt),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
 #if CONFIG_AVRP_ENCODER
@@ -124,5 +126,6 @@ const FFCodec ff_avrp_encoder = {
     .init           = encode_init,
     FF_CODEC_ENCODE_CB(encode_frame),
     CODEC_PIXFMTS_ARRAY(pix_fmt),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
 #endif
diff --git a/libavcodec/rawenc.c b/libavcodec/rawenc.c
index 8c577006d9..1626da7c89 100644
--- a/libavcodec/rawenc.c
+++ b/libavcodec/rawenc.c
@@ -90,4 +90,5 @@ const FFCodec ff_rawvideo_encoder = {
                       AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     .init           = raw_encode_init,
     FF_CODEC_ENCODE_CB(raw_encode),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/v210enc.c b/libavcodec/v210enc.c
index 2194e2128c..b5a6fccd8d 100644
--- a/libavcodec/v210enc.c
+++ b/libavcodec/v210enc.c
@@ -118,4 +118,5 @@ const FFCodec ff_v210_encoder = {
     .init           = encode_init,
     FF_CODEC_ENCODE_CB(encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/xwdenc.c b/libavcodec/xwdenc.c
index 1d8b7d9002..4873e1e697 100644
--- a/libavcodec/xwdenc.c
+++ b/libavcodec/xwdenc.c
@@ -227,4 +227,5 @@ const FFCodec ff_xwd_encoder = {
                   AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8,
                   AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
                   AV_PIX_FMT_PAL8, AV_PIX_FMT_GRAY8, AV_PIX_FMT_MONOWHITE),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/y41penc.c b/libavcodec/y41penc.c
index b0044b35ef..de77032162 100644
--- a/libavcodec/y41penc.c
+++ b/libavcodec/y41penc.c
@@ -86,4 +86,5 @@ const FFCodec ff_y41p_encoder = {
     .init         = y41p_encode_init,
     FF_CODEC_ENCODE_CB(y41p_encode_frame),
     CODEC_PIXFMTS(AV_PIX_FMT_YUV411P),
+    .caps_internal = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
diff --git a/libavcodec/yuv4enc.c b/libavcodec/yuv4enc.c
index 093038142f..af2b0f08a4 100644
--- a/libavcodec/yuv4enc.c
+++ b/libavcodec/yuv4enc.c
@@ -78,4 +78,5 @@ const FFCodec ff_yuv4_encoder = {
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     CODEC_PIXFMTS(AV_PIX_FMT_YUV420P),
     FF_CODEC_ENCODE_CB(yuv4_encode_frame),
+    .caps_internal  = FF_CODEC_CAP_FIXED_PACKET_SIZE,
 };
-- 
2.52.0


>From 4cb6efbf8b0087a904b63a5f6b7d8ecfe9d54956 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Sat, 18 Jul 2026 17:36:18 +0200
Subject: [PATCH 3/3] tests/api: add api-encode-pool test

Covers the packet buffer pool used by the default get_encode_buffer()
for encoders with FF_CODEC_CAP_FIXED_PACKET_SIZE: constant packet size,
buffer reuse once a packet is unreferenced, no aliasing between packets
still in use, and pool recreation when the requested size changes. The
test uses the rawvideo encoder and only builds when it is enabled.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 tests/api/Makefile               |   1 +
 tests/api/api-encode-pool-test.c | 153 +++++++++++++++++++++++++++++++
 tests/fate/api.mak               |   5 +
 3 files changed, 159 insertions(+)
 create mode 100644 tests/api/api-encode-pool-test.c

diff --git a/tests/api/Makefile b/tests/api/Makefile
index 987db1b785..ebc1b11aec 100644
--- a/tests/api/Makefile
+++ b/tests/api/Makefile
@@ -1,3 +1,4 @@
+APITESTPROGS-$(CONFIG_RAWVIDEO_ENCODER) += api-encode-pool
 APITESTPROGS-$(call ENCDEC, FLAC, FLAC) += api-flac
 APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264
 APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264-slice
diff --git a/tests/api/api-encode-pool-test.c b/tests/api/api-encode-pool-test.c
new file mode 100644
index 0000000000..5c617d488b
--- /dev/null
+++ b/tests/api/api-encode-pool-test.c
@@ -0,0 +1,153 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Test the packet buffer pool used by the default get_encode_buffer() for
+ * encoders with FF_CODEC_CAP_FIXED_PACKET_SIZE: buffers must be reused once
+ * the packet is unreferenced and must not alias packets still in use.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libavcodec/avcodec.h"
+#include "libavutil/frame.h"
+#include "libavutil/mem.h"
+
+static int encode_frame(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, 
int fill)
+{
+    int ret;
+
+    ret = av_frame_make_writable(frame);
+    if (ret < 0)
+        return ret;
+
+    memset(frame->data[0], fill, frame->linesize[0] * frame->height);
+    frame->pts = fill;
+
+    ret = avcodec_send_frame(ctx, frame);
+    if (ret < 0)
+        return ret;
+
+    return avcodec_receive_packet(ctx, pkt);
+}
+
+int main(void)
+{
+    const AVCodec *codec = avcodec_find_encoder_by_name("rawvideo");
+    AVCodecContext *ctx = NULL;
+    AVFrame *frame = NULL;
+    AVPacket *pkt = NULL, *pkt2 = NULL;
+    uint8_t *snapshot = NULL;
+    const uint8_t *prev_data = NULL;
+    int first_size = -1;
+    int ret = 1;
+
+    if (!codec) {
+        fprintf(stderr, "rawvideo encoder not found\n");
+        return 1;
+    }
+
+    ctx   = avcodec_alloc_context3(codec);
+    frame = av_frame_alloc();
+    pkt   = av_packet_alloc();
+    pkt2  = av_packet_alloc();
+    if (!ctx || !frame || !pkt || !pkt2)
+        goto end;
+
+    ctx->width        = 128;
+    ctx->height       = 64;
+    ctx->pix_fmt      = AV_PIX_FMT_GRAY8;
+    ctx->time_base    = (AVRational){ 1, 25 };
+    ctx->thread_count = 1;
+
+    if (avcodec_open2(ctx, codec, NULL) < 0)
+        goto end;
+
+    frame->format = ctx->pix_fmt;
+    frame->width  = ctx->width;
+    frame->height = ctx->height;
+    if (av_frame_get_buffer(frame, 0) < 0)
+        goto end;
+
+    for (int i = 0; i < 64; i++) {
+        if (encode_frame(ctx, frame, pkt, i) < 0)
+            goto end;
+        if (first_size < 0)
+            first_size = pkt->size;
+        if (pkt->size != first_size) {
+            fprintf(stderr, "packet size changed: %d != %d\n", pkt->size, 
first_size);
+            goto end;
+        }
+        if (i && pkt->buf->data != prev_data) {
+            fprintf(stderr, "packet buffer not reused after unref\n");
+            goto end;
+        }
+        prev_data = pkt->buf->data;
+        av_packet_unref(pkt);
+    }
+
+    if (encode_frame(ctx, frame, pkt, 1) < 0)
+        goto end;
+    snapshot = av_memdup(pkt->data, pkt->size);
+    if (!snapshot)
+        goto end;
+    if (encode_frame(ctx, frame, pkt2, 2) < 0)
+        goto end;
+    if (pkt->buf->data == pkt2->buf->data) {
+        fprintf(stderr, "referenced packets share the same buffer\n");
+        goto end;
+    }
+    if (memcmp(pkt->data, snapshot, pkt->size)) {
+        fprintf(stderr, "referenced packet was overwritten\n");
+        goto end;
+    }
+    av_packet_unref(pkt);
+    av_packet_unref(pkt2);
+
+    /* direct calls, to exercise pool recreation on size change */
+    for (int i = 0; i < 4; i++) {
+        static const int sizes[] = { 1024, 1024, 4096, 1024 };
+
+        pkt->size = sizes[i];
+        if (avcodec_default_get_encode_buffer(ctx, pkt, 0) < 0)
+            goto end;
+        if (pkt->buf->size != sizes[i] + AV_INPUT_BUFFER_PADDING_SIZE) {
+            fprintf(stderr, "unexpected pool buffer size %zu for request %d\n",
+                    pkt->buf->size, sizes[i]);
+            goto end;
+        }
+        if (i && sizes[i] == sizes[i - 1] && pkt->buf->data != prev_data) {
+            fprintf(stderr, "pool buffer not reused for same-size request\n");
+            goto end;
+        }
+        prev_data = pkt->buf->data;
+        av_packet_unref(pkt);
+    }
+
+    ret = 0;
+end:
+    av_free(snapshot);
+    av_packet_free(&pkt);
+    av_packet_free(&pkt2);
+    av_frame_free(&frame);
+    avcodec_free_context(&ctx);
+    return ret;
+}
diff --git a/tests/fate/api.mak b/tests/fate/api.mak
index ba9789d9f7..202dd1bede 100644
--- a/tests/fate/api.mak
+++ b/tests/fate/api.mak
@@ -1,3 +1,8 @@
+FATE_API_LIBAVCODEC-$(CONFIG_RAWVIDEO_ENCODER) += fate-api-encode-pool
+fate-api-encode-pool: $(APITESTSDIR)/api-encode-pool-test$(EXESUF)
+fate-api-encode-pool: CMD = run $(APITESTSDIR)/api-encode-pool-test$(EXESUF)
+fate-api-encode-pool: CMP = null
+
 FATE_API_LIBAVCODEC-$(call ENCDEC, FLAC, FLAC) += fate-api-flac
 fate-api-flac: $(APITESTSDIR)/api-flac-test$(EXESUF)
 fate-api-flac: CMD = run $(APITESTSDIR)/api-flac-test$(EXESUF)
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to