PR #23692 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23692
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23692.patch

Reduces code duplication and unifies behavior across encoders.


>From 4793ad88c8097608c2dee0555e0e943a1bf80a3c Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:28:51 -0300
Subject: [PATCH 1/9] avcodec/libfdk-aacenc: don't propagate a skip samples
 value higher than the frame size

This puts the burden of keeping track of remaining samples on the user.
It's best if we just report the exact amount of discardable samples
within the packet's duration.

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/libfdk-aacenc.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c
index 6c28633fba..168b9d775d 100644
--- a/libavcodec/libfdk-aacenc.c
+++ b/libavcodec/libfdk-aacenc.c
@@ -55,7 +55,7 @@ typedef struct AACContext {
     int prog_ref;
     int metadata_mode;
     AACENC_MetaData metaDataSetup;
-    int delay_sent;
+    int delay;
     int frame_length;
 
     AudioFrameQueue afq;
@@ -421,6 +421,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
     }
 
     avctx->frame_size = info.frameLength;
+    s->delay =
 #if FDKENC_VER_AT_LEAST(4, 0) // 4.0.0
     avctx->initial_padding = info.nDelay;
 #else
@@ -536,14 +537,16 @@ static int aac_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
         av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n");
         return AVERROR(EINVAL);
     }
-    if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) 
{
+
+    if (s->delay > 0 || discard_padding > 0) {
         uint8_t *side_data =
             av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
         if (!side_data)
             return AVERROR(ENOMEM);
-        if (!s->delay_sent) {
-            AV_WL32(side_data, avctx->initial_padding);
-            s->delay_sent = 1;
+        if (s->delay) {
+            AV_WL32(side_data, FFMIN(s->delay, 
ff_samples_from_time_base(avctx, avpkt->duration)));
+            s->delay -= ff_samples_from_time_base(avctx, avpkt->duration);
+            s->delay = FFMAX(s->delay, 0);
         }
         AV_WL32(side_data + 4, discard_padding);
     }
-- 
2.52.0


>From 8b5025510b9a67c37107e6955a519e988a60e5aa Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:32:16 -0300
Subject: [PATCH 2/9] avcodec/encode: add a helper function to export skip
 samples side data

Reduces code duplication.

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/encode.c        | 31 +++++++++++++++++++++++++++++++
 libavcodec/encode.h        |  2 ++
 libavcodec/libfdk-aacenc.c | 24 ++++--------------------
 3 files changed, 37 insertions(+), 20 deletions(-)

diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index 56b2f4362b..e2c234c81a 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -229,6 +229,37 @@ int ff_encode_get_frame(AVCodecContext *avctx, AVFrame 
*frame)
     return 0;
 }
 
+int ff_encode_set_skip_samples(AVCodecContext *avctx, AVPacket *avpkt, int 
frame_size, int *pdelay)
+{
+    int64_t duration = ff_samples_from_time_base(avctx, avpkt->duration);
+    int delay = *pdelay, discard_padding;
+
+    av_assert1(avctx->frame_size);
+
+    discard_padding = frame_size - duration;
+    // Check if subtraction resulted in an overflow
+    if ((discard_padding < frame_size) != (avpkt->duration > 0)) {
+        av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (delay || discard_padding > 0) {
+        uint8_t *side_data =
+            av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
+        if (!side_data)
+            return AVERROR(ENOMEM);
+        if (delay) {
+            AV_WL32(side_data, FFMIN(delay, duration));
+            delay -= duration;
+            delay = FFMAX(delay, 0);
+        }
+        AV_WL32(side_data + 4, discard_padding);
+        *pdelay = delay;
+    }
+
+    return 0;
+}
+
 static int encode_set_packet_props(AVCodecContext *avctx, AVPacket *avpkt, 
const AVFrame *frame)
 {
     AVCodecInternal *avci = avctx->internal;
diff --git a/libavcodec/encode.h b/libavcodec/encode.h
index b66b387030..d7452f77b2 100644
--- a/libavcodec/encode.h
+++ b/libavcodec/encode.h
@@ -120,4 +120,6 @@ static av_always_inline int64_t 
ff_samples_from_time_base(const AVCodecContext *
 #define FF_MATRIX_TYPE_CHROMA_INTRA (1U << 2)
 int ff_check_codec_matrices(AVCodecContext *avctx, unsigned types, uint16_t 
min, uint16_t max);
 
+int ff_encode_set_skip_samples(AVCodecContext *avctx, AVPacket *avpkt, int 
frame_size, int *pdelay);
+
 #endif /* AVCODEC_ENCODE_H */
diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c
index 168b9d775d..d7c342bf18 100644
--- a/libavcodec/libfdk-aacenc.c
+++ b/libavcodec/libfdk-aacenc.c
@@ -468,7 +468,7 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
     int out_buffer_identifier = OUT_BITSTREAM_DATA;
     int out_buffer_size, out_buffer_element_size;
     void *out_ptr;
-    int ret, discard_padding;
+    int ret;
     uint8_t dummy_buf[1];
     AACENC_ERROR err;
 
@@ -531,25 +531,9 @@ static int aac_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
     ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
                        &avpkt->duration);
 
-    discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, 
avpkt->duration);
-    // Check if subtraction resulted in an overflow
-    if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) {
-        av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n");
-        return AVERROR(EINVAL);
-    }
-
-    if (s->delay > 0 || discard_padding > 0) {
-        uint8_t *side_data =
-            av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
-        if (!side_data)
-            return AVERROR(ENOMEM);
-        if (s->delay) {
-            AV_WL32(side_data, FFMIN(s->delay, 
ff_samples_from_time_base(avctx, avpkt->duration)));
-            s->delay -= ff_samples_from_time_base(avctx, avpkt->duration);
-            s->delay = FFMAX(s->delay, 0);
-        }
-        AV_WL32(side_data + 4, discard_padding);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, avctx->frame_size, 
&s->delay);
+    if (ret < 0)
+        return ret;
 
     avpkt->size     = out_args.numOutBytes;
     avpkt->flags   |= AV_PKT_FLAG_KEY;
-- 
2.52.0


>From 61f9f2edb90abc981b797186cbd1199f23c6f7a9 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:33:03 -0300
Subject: [PATCH 3/9] avcodec/aacenc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/aacenc.c | 12 ++++--------
 libavcodec/aacenc.h |  2 ++
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c
index 88d5ff55f7..45f5d5b8e0 100644
--- a/libavcodec/aacenc.c
+++ b/libavcodec/aacenc.c
@@ -1415,14 +1415,9 @@ static int aac_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
     ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
                        &avpkt->duration);
 
-    int discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, 
avpkt->duration);
-    if (discard_padding > 0) {
-        uint8_t *side_data =
-            av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
-        if (!side_data)
-            return AVERROR(ENOMEM);
-        AV_WL32(side_data + 4, discard_padding);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, avctx->frame_size, 
&s->delay);
+    if (ret < 0)
+        return ret;
 
     avpkt->flags |= AV_PKT_FLAG_KEY;
 
@@ -1506,6 +1501,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx)
     /* Constants */
     s->last_frame_pb_count = 0;
     avctx->frame_size = 1024;
+    s->delay =
     avctx->initial_padding = 1024;
     s->lambda = avctx->global_quality > 0 ? avctx->global_quality : 120;
 
diff --git a/libavcodec/aacenc.h b/libavcodec/aacenc.h
index 67069cbdf1..63e732704b 100644
--- a/libavcodec/aacenc.h
+++ b/libavcodec/aacenc.h
@@ -240,6 +240,8 @@ typedef struct AACEncContext {
     enum RawDataBlockType cur_type;              ///< channel group type 
cur_channel belongs to
 
     AudioFrameQueue afq;
+    int delay;
+
     DECLARE_ALIGNED(32, int,   qcoefs)[96];      ///< quantized coefficients
     DECLARE_ALIGNED(32, float, scoefs)[1024];    ///< scaled coefficients
 
-- 
2.52.0


>From 1f3f230f8722d2aac4bab191729efccb4f07947b Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:33:11 -0300
Subject: [PATCH 4/9] avcodec/ac3enc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/ac3enc.c                        | 13 ++++---------
 libavcodec/ac3enc.h                        |  1 +
 tests/ref/fate/ac3-fixed-encode-2          |  2 +-
 tests/ref/fate/ac3-fixed-encode-3          |  2 +-
 tests/ref/fate/copy-shortest1              |  2 +-
 tests/ref/fate/copy-shortest2              |  2 +-
 tests/ref/fate/ffmpeg-filter_complex_audio |  2 +-
 tests/ref/fate/shortest                    |  2 +-
 8 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c
index 69744989ba..b31427c807 100644
--- a/libavcodec/ac3enc.c
+++ b/libavcodec/ac3enc.c
@@ -1982,7 +1982,6 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
                         const AVFrame *frame, int *got_packet_ptr)
 {
     AC3EncodeContext *const s = avctx->priv_data;
-    int discard_padding;
     int ret;
 
     /* add current frame to queue */
@@ -2028,14 +2027,9 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
     ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
                        &avpkt->duration);
 
-    discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, 
avpkt->duration);
-    if (discard_padding > 0) {
-        uint8_t *side_data =
-            av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10);
-        if (!side_data)
-            return AVERROR(ENOMEM);
-        AV_WL32(side_data + 4, discard_padding);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, avctx->frame_size, 
&s->delay);
+    if (ret < 0)
+        return ret;
 
     *got_packet_ptr = 1;
     return 0;
@@ -2509,6 +2503,7 @@ av_cold int ff_ac3_encode_init(AVCodecContext *avctx)
         return ret;
 
     avctx->frame_size = AC3_BLOCK_SIZE * s->num_blocks;
+    s->delay =
     avctx->initial_padding = AC3_BLOCK_SIZE;
 
     s->bitstream_mode = avctx->audio_service_type;
diff --git a/libavcodec/ac3enc.h b/libavcodec/ac3enc.h
index 3e92af17f0..8e64fd654e 100644
--- a/libavcodec/ac3enc.h
+++ b/libavcodec/ac3enc.h
@@ -255,6 +255,7 @@ typedef struct AC3EncodeContext {
     int ref_bap_set;                                         ///< indicates if 
ref_bap pointers have been set
 
     AudioFrameQueue afq;
+    int delay;
 
     /** fixed vs. float function pointers */
     void (*encode_frame)(struct AC3EncodeContext *s, const AVFrame *frame);
diff --git a/tests/ref/fate/ac3-fixed-encode-2 
b/tests/ref/fate/ac3-fixed-encode-2
index 468441cd7e..c01184806b 100644
--- a/tests/ref/fate/ac3-fixed-encode-2
+++ b/tests/ref/fate/ac3-fixed-encode-2
@@ -3,7 +3,7 @@
 #codec_id 0: ac3
 #sample_rate 0: 44100
 #channel_layout_name 0: 5.1(side)
-0,       -256,       -256,     1536,     1114, 0x32fd276c
+0,       -256,       -256,     1536,     1114, 0x32fd276c, S=1, Skip Samples,  
     10, 0x00090001
 0,       1280,       1280,     1536,     1116, 0x1ac63ba7
 0,       2816,       2816,     1536,     1114, 0xdde82dbc
 0,       4352,       4352,     1536,     1114, 0x39313179
diff --git a/tests/ref/fate/ac3-fixed-encode-3 
b/tests/ref/fate/ac3-fixed-encode-3
index 041bb8c636..7171043753 100644
--- a/tests/ref/fate/ac3-fixed-encode-3
+++ b/tests/ref/fate/ac3-fixed-encode-3
@@ -3,7 +3,7 @@
 #codec_id 0: ac3
 #sample_rate 0: 44100
 #channel_layout_name 0: 5.1
-0,       -256,       -256,     1536,     1114, 0x89963331
+0,       -256,       -256,     1536,     1114, 0x89963331, S=1, Skip Samples,  
     10, 0x00090001
 0,       1280,       1280,     1536,     1116, 0x9eda26ff
 0,       2816,       2816,     1536,     1114, 0x0734390b
 0,       4352,       4352,     1536,     1114, 0xa6e33728
diff --git a/tests/ref/fate/copy-shortest1 b/tests/ref/fate/copy-shortest1
index 83a73d5263..d8ba4a59f9 100644
--- a/tests/ref/fate/copy-shortest1
+++ b/tests/ref/fate/copy-shortest1
@@ -13,7 +13,7 @@
 #sample_rate 1: 44100
 #channel_layout_name 1: mono
 #stream#, dts,        pts, duration,     size, hash
-1,       -256,       -256,     1536,      416, 180f042a77b9500f9a002cafd2f670a2
+1,       -256,       -256,     1536,      416, 
180f042a77b9500f9a002cafd2f670a2, S=1,       10, 
4bce5775f7be5f45922731da9a33b4f3
 0,          0,          0,     2048,     8719, bbea2a7487d61d39a0b2f2fe62a4df4a
 1,       1280,       1280,     1536,      418, 77effcb2892958193be38a788328616b
 0,       2048,       2048,     2048,      975, 94f30e410595452ee981d96224516504
diff --git a/tests/ref/fate/copy-shortest2 b/tests/ref/fate/copy-shortest2
index 83a73d5263..d8ba4a59f9 100644
--- a/tests/ref/fate/copy-shortest2
+++ b/tests/ref/fate/copy-shortest2
@@ -13,7 +13,7 @@
 #sample_rate 1: 44100
 #channel_layout_name 1: mono
 #stream#, dts,        pts, duration,     size, hash
-1,       -256,       -256,     1536,      416, 180f042a77b9500f9a002cafd2f670a2
+1,       -256,       -256,     1536,      416, 
180f042a77b9500f9a002cafd2f670a2, S=1,       10, 
4bce5775f7be5f45922731da9a33b4f3
 0,          0,          0,     2048,     8719, bbea2a7487d61d39a0b2f2fe62a4df4a
 1,       1280,       1280,     1536,      418, 77effcb2892958193be38a788328616b
 0,       2048,       2048,     2048,      975, 94f30e410595452ee981d96224516504
diff --git a/tests/ref/fate/ffmpeg-filter_complex_audio 
b/tests/ref/fate/ffmpeg-filter_complex_audio
index 5b42ab1b8a..c178e0bd94 100644
--- a/tests/ref/fate/ffmpeg-filter_complex_audio
+++ b/tests/ref/fate/ffmpeg-filter_complex_audio
@@ -3,7 +3,7 @@
 #codec_id 0: ac3
 #sample_rate 0: 44100
 #channel_layout_name 0: mono
-0,       -256,       -256,     1536,      416, 0x3001fb2d
+0,       -256,       -256,     1536,      416, 0x3001fb2d, S=1, Skip Samples,  
     10, 0x00090001
 0,       1280,       1280,     1536,      418, 0xba72fc16
 0,       2816,       2816,     1536,      418, 0xba72fc16
 0,       4352,       4352,      259,      418, 0xba72fc16, S=1, Skip Samples,  
     10, 0x06020101
diff --git a/tests/ref/fate/shortest b/tests/ref/fate/shortest
index 68d1038b71..8343db4872 100644
--- a/tests/ref/fate/shortest
+++ b/tests/ref/fate/shortest
@@ -8,7 +8,7 @@
 #codec_id 1: ac3
 #sample_rate 1: 44100
 #channel_layout_name 1: mono
-1,       -256,       -256,     1536,      416, 0xcedecce4
+1,       -256,       -256,     1536,      416, 0xcedecce4, S=1, Skip Samples,  
     10, 0x00090001
 0,          0,          0,        1,    27867, 0x1426a0d6, S=1, Quality stats, 
       8, 0x050000a1
 1,       1280,       1280,     1536,      418, 0x4ebabf82
 0,          1,          1,        1,     9806, 0xbebc2826, F=0x0, S=1, Quality 
stats,        8, 0x050400a2
-- 
2.52.0


>From 047421ce213f0b01bd599a8a39b5bb4b71c0aab0 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:33:29 -0300
Subject: [PATCH 5/9] avcodec/libmp3lame: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/libmp3lame.c | 26 ++++++--------------------
 1 file changed, 6 insertions(+), 20 deletions(-)

diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c
index 339505ccfe..a0bd43d231 100644
--- a/libavcodec/libmp3lame.c
+++ b/libavcodec/libmp3lame.c
@@ -52,7 +52,7 @@ typedef struct LAMEContext {
     int reservoir;
     int joint_stereo;
     int abr;
-    int delay_sent;
+    int delay;
     float *samples_flt[2];
     AudioFrameQueue afq;
     AVFloatDSPContext *fdsp;
@@ -153,6 +153,7 @@ static av_cold int mp3lame_encode_init(AVCodecContext 
*avctx)
     }
 
     /* get encoder delay */
+    s->delay =
     avctx->initial_padding = lame_get_encoder_delay(s->gfp) + 528 + 1;
     ff_af_queue_init(avctx, &s->afq);
 
@@ -201,7 +202,7 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
 {
     LAMEContext *s = avctx->priv_data;
     MPADecodeHeader hdr;
-    int len, ret, ch, discard_padding;
+    int len, ret, ch;
     int lame_result;
     uint32_t h;
 
@@ -285,24 +286,9 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
         ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts,
                            &avpkt->duration);
 
-        discard_padding = avctx->frame_size - ff_samples_from_time_base(avctx, 
avpkt->duration);
-        // Check if subtraction resulted in an overflow
-        if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) {
-            av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n");
-            return AVERROR(EINVAL);
-        }
-        if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding 
> 0) {
-            uint8_t* side_data = av_packet_new_side_data(avpkt,
-                                                         
AV_PKT_DATA_SKIP_SAMPLES,
-                                                         10);
-            if (!side_data)
-                return AVERROR(ENOMEM);
-            if (!s->delay_sent) {
-                AV_WL32(side_data, avctx->initial_padding);
-                s->delay_sent = 1;
-            }
-            AV_WL32(side_data + 4, discard_padding);
-        }
+        ret = ff_encode_set_skip_samples(avctx, avpkt, avctx->frame_size, 
&s->delay);
+        if (ret < 0)
+            return ret;
 
         *got_packet_ptr = 1;
     }
-- 
2.52.0


>From a0fe17a4be28fe034413879aa4e3f1fcbd9fc95e Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:33:47 -0300
Subject: [PATCH 6/9] avcodec/libopusenc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/libopusenc.c | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
index 394911ff36..f05d365aeb 100644
--- a/libavcodec/libopusenc.c
+++ b/libavcodec/libopusenc.c
@@ -47,6 +47,7 @@ typedef struct LibopusEncOpts {
 #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
     int apply_phase_inv;
 #endif
+    int delay;
 } LibopusEncOpts;
 
 typedef struct LibopusEncContext {
@@ -56,6 +57,7 @@ typedef struct LibopusEncContext {
     uint8_t *samples;
     LibopusEncOpts opts;
     AudioFrameQueue afq;
+    int delay;
     const uint8_t *encoder_channel_map;
 } LibopusEncContext;
 
@@ -434,6 +436,8 @@ static av_cold int libopus_encode_init(AVCodecContext 
*avctx)
                "Unable to get number of lookahead samples: %s\n",
                opus_strerror(ret));
 
+    opus->delay = avctx->initial_padding;
+
     libopus_write_header(avctx, opus->stream_count, coupled_stream_count,
                          mapping_family, libopus_channel_mapping);
 
@@ -471,7 +475,6 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
     const int sample_size      = channels * bytes_per_sample;
     const uint8_t *audio;
     int ret;
-    int discard_padding;
 
     if (frame) {
         ret = ff_af_queue_add(&opus->afq, frame);
@@ -520,18 +523,9 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket 
*avpkt,
     ff_af_queue_remove(&opus->afq, opus->opts.packet_size,
                        &avpkt->pts, &avpkt->duration);
 
-    discard_padding = opus->opts.packet_size - 
ff_samples_from_time_base(avctx, avpkt->duration);
-    // Check if subtraction resulted in an overflow
-    if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0))
-        return AVERROR(EINVAL);
-    if (discard_padding > 0) {
-        uint8_t* side_data = av_packet_new_side_data(avpkt,
-                                                     AV_PKT_DATA_SKIP_SAMPLES,
-                                                     10);
-        if (!side_data)
-            return AVERROR(ENOMEM);
-        AV_WL32(side_data + 4, discard_padding);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, opus->opts.packet_size, 
&opus->delay);
+    if (ret < 0)
+        return ret;
 
     *got_packet_ptr = 1;
 
-- 
2.52.0


>From 54ae2b3df2880a8950cee04a316a40d46f5c59c6 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:34:00 -0300
Subject: [PATCH 7/9] avcodec/libvorbisenc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/libvorbisenc.c | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/libavcodec/libvorbisenc.c b/libavcodec/libvorbisenc.c
index 352a5e1297..8f282bfe0f 100644
--- a/libavcodec/libvorbisenc.c
+++ b/libavcodec/libvorbisenc.c
@@ -56,6 +56,7 @@ typedef struct LibvorbisEncContext {
     double iblock;                      /**< impulse block bias option      */
     AVVorbisParseContext vp;            /**< parse context to get durations */
     AudioFrameQueue afq;                /**< frame queue for timestamps     */
+    int delay;                          /**< initial padding                */
 } LibvorbisEncContext;
 
 static const AVOption options[] = {
@@ -225,6 +226,7 @@ static av_cold int 
libvorbis_get_priming_samples(vorbis_info *vi, AVCodecContext
             ret = vorbis_error_to_averror(ret);
             goto error;
         }
+        s->delay =
         avctx->initial_padding = av_vorbis_parse_frame(&s->vp, op.packet, 
op.bytes);
     }
 
@@ -415,19 +417,11 @@ static int libvorbis_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
 
     duration = av_vorbis_parse_frame(&s->vp, avpkt->data, avpkt->size);
     if (duration > 0) {
-        int discard_padding;
-
         ff_af_queue_remove(&s->afq, duration, &avpkt->pts, &avpkt->duration);
 
-        discard_padding = duration - ff_samples_from_time_base(avctx, 
avpkt->duration);
-        if (discard_padding > 0) {
-            uint8_t *side_data = av_packet_new_side_data(avpkt,
-                                                         
AV_PKT_DATA_SKIP_SAMPLES,
-                                                         10);
-            if (!side_data)
-                return AVERROR(ENOMEM);
-            AV_WL32(side_data + 4, discard_padding);
-        }
+        ret = ff_encode_set_skip_samples(avctx, avpkt, duration, &s->delay);
+        if (ret < 0)
+            return ret;
     }
 
     *got_packet_ptr = 1;
-- 
2.52.0


>From 54611eea24a249c858a628d42869d06fab14e168 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:34:13 -0300
Subject: [PATCH 8/9] avcodec/opus/enc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/opus/enc.c           | 14 ++++++--------
 tests/ref/fate/opus-enc-silence |  2 +-
 2 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/libavcodec/opus/enc.c b/libavcodec/opus/enc.c
index 74531f9142..4736c301d7 100644
--- a/libavcodec/opus/enc.c
+++ b/libavcodec/opus/enc.c
@@ -55,6 +55,7 @@ typedef struct OpusEncContext {
     OpusPacketInfo packet;
 
     int channels;
+    int delay;
 
     CeltFrame *frame;
     OpusRangeCoder *rc;
@@ -560,7 +561,7 @@ static int opus_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
                              const AVFrame *frame, int *got_packet_ptr)
 {
     OpusEncContext *s = avctx->priv_data;
-    int ret, frame_size, discard_padding, alloc_size = 0;
+    int ret, frame_size, alloc_size = 0;
 
     if (frame) { /* Add new frame to queue */
         if ((ret = ff_af_queue_add(&s->afq, frame)) < 0)
@@ -613,13 +614,9 @@ static int opus_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
     /* Remove samples from queue and skip if needed */
     ff_af_queue_remove(&s->afq, s->packet.frames*frame_size, &avpkt->pts, 
&avpkt->duration);
 
-    discard_padding = s->packet.frames*frame_size - 
ff_samples_from_time_base(avctx, avpkt->duration);
-    if (discard_padding > 0) {
-        uint8_t *side = av_packet_new_side_data(avpkt, 
AV_PKT_DATA_SKIP_SAMPLES, 10);
-        if (!side)
-            return AVERROR(ENOMEM);
-        AV_WL32(&side[4], discard_padding);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, 
s->packet.frames*frame_size, &s->delay);
+    if (ret < 0)
+        return ret;
 
     *got_packet_ptr = 1;
 
@@ -655,6 +652,7 @@ static av_cold int opus_encode_init(AVCodecContext *avctx)
     int max_delay_samples = (s->options.max_delay_ms * s->avctx->sample_rate) 
/ 1000;
     avctx->frame_size = 
OPUS_BLOCK_SIZE(FFMIN(OPUS_SAMPLES_TO_BLOCK_SIZE(max_delay_samples), 
CELT_BLOCK_960));
     /* Initial padding will change if SILK is ever supported */
+    s->delay =
     avctx->initial_padding = 120;
 
     if (!avctx->bit_rate) {
diff --git a/tests/ref/fate/opus-enc-silence b/tests/ref/fate/opus-enc-silence
index dd8e02e02d..8d3e71e1ca 100644
--- a/tests/ref/fate/opus-enc-silence
+++ b/tests/ref/fate/opus-enc-silence
@@ -4,5 +4,5 @@
 #codec_id 0: opus
 #sample_rate 0: 48000
 #channel_layout_name 0: stereo
-0,       -120,       -120,      960,        1, 0x00fc00fc
+0,       -120,       -120,      960,        1, 0x00fc00fc, S=1, Skip Samples,  
     10, 0x04b00078
 0,        840,        840,      168,        1, 0x00fc00fc, S=1, Skip Samples,  
     10, 0x009f001b
-- 
2.52.0


>From 3a98d676b05c2c8f8d8c29fd2b59c66419b1f509 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Fri, 3 Jul 2026 13:34:26 -0300
Subject: [PATCH 9/9] avcodec/vorbisenc: use ff_encode_set_skip_samples()

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/vorbisenc.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/libavcodec/vorbisenc.c b/libavcodec/vorbisenc.c
index ab30dd49ed..07c0ec1eab 100644
--- a/libavcodec/vorbisenc.c
+++ b/libavcodec/vorbisenc.c
@@ -120,6 +120,8 @@ typedef struct vorbis_enc_context {
     float quality;
 
     AudioFrameQueue afq;
+    int delay;
+
     struct FFBufQueue bufqueue;
 
     int ncodebooks;
@@ -1198,12 +1200,9 @@ static int vorbis_encode_frame(AVCodecContext *avctx, 
AVPacket *avpkt,
 
     ff_af_queue_remove(&venc->afq, frame_size, &avpkt->pts, &avpkt->duration);
 
-    if (frame_size > avpkt->duration) {
-        uint8_t *side = av_packet_new_side_data(avpkt, 
AV_PKT_DATA_SKIP_SAMPLES, 10);
-        if (!side)
-            return AVERROR(ENOMEM);
-        AV_WL32(&side[4], frame_size - avpkt->duration);
-    }
+    ret = ff_encode_set_skip_samples(avctx, avpkt, frame_size, &venc->delay);
+    if (ret < 0)
+        return ret;
 
     *got_packet_ptr = 1;
     return 0;
@@ -1296,6 +1295,7 @@ static av_cold int vorbis_encode_init(AVCodecContext 
*avctx)
     avctx->extradata_size = ret;
 
     avctx->frame_size = 64;
+    venc->delay =
     avctx->initial_padding = 1 << (venc->log2_blocksize[1] - 1);
 
     ff_af_queue_init(avctx, &venc->afq);
-- 
2.52.0

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

Reply via email to