This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit b5bfb7716c4914efdccbc3b507a77d676616be51
Author:     James Almer <[email protected]>
AuthorDate: Mon Jun 29 19:24:39 2026 -0300
Commit:     James Almer <[email protected]>
CommitDate: Thu Jul 2 17:53:20 2026 -0300

    avcodec/ac3enc: drain the buffered samples at the end of encoding
    
    When encoding a stream with an amount of samples multiple of a block, the 
last
    the last 256 samples would be lost as the encoders were not marked as
    AV_CODEC_CAP_DELAY.
    
    This can be easily reproduced with:
    
    ffmpeg -f lavfi -i sine -ac 2 -af atrim=start_sample=0:end_sample=4608 -c:a 
eac3 -f framecrc -
    
    Signed-off-by: James Almer <[email protected]>
---
 libavcodec/ac3enc.c               | 35 ++++++++++++++++++++++++++++++++---
 libavcodec/ac3enc.h               |  6 +++++-
 libavcodec/ac3enc_fixed.c         |  3 ++-
 libavcodec/ac3enc_float.c         |  3 ++-
 libavcodec/ac3enc_template.c      | 24 +++++++++++++++++++++++-
 libavcodec/eac3enc.c              |  3 ++-
 tests/fate/ac3.mak                | 12 ++++--------
 tests/ref/fate/ac3-fixed-encode-2 |  1 +
 tests/ref/fate/shortest           |  1 +
 9 files changed, 72 insertions(+), 16 deletions(-)

diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c
index 5a1a3ab63a..69744989ba 100644
--- a/libavcodec/ac3enc.c
+++ b/libavcodec/ac3enc.c
@@ -1982,8 +1982,19 @@ 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 */
+    if (frame) {
+        ret = ff_af_queue_add(&s->afq, frame);
+        if (ret < 0)
+            return ret;
+    } else {
+        if (!s->afq.remaining_samples || (!s->afq.frame_alloc && 
!s->afq.frame_count))
+            return 0;
+    }
+
     if (s->options.allow_per_frame_metadata) {
         ret = ac3_validate_metadata(s);
         if (ret)
@@ -1993,7 +2004,7 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
     if (s->bit_alloc.sr_code == 1 || s->eac3)
         ac3_adjust_frame_size(s);
 
-    s->encode_frame(s, frame->extended_data);
+    s->encode_frame(s, frame);
 
     ac3_apply_rematrixing(s);
 
@@ -2014,8 +2025,17 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket 
*avpkt,
         return ret;
     ac3_output_frame(s, avpkt->data);
 
-    if (frame->pts != AV_NOPTS_VALUE)
-        avpkt->pts = frame->pts - ff_samples_to_time_base(avctx, 
avctx->initial_padding);
+    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);
+    }
 
     *got_packet_ptr = 1;
     return 0;
@@ -2157,6 +2177,7 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx)
 
     for (int ch = 0; ch < s->channels; ch++)
         av_freep(&s->planar_samples[ch]);
+    av_freep(&s->input_samples[0]);
     av_freep(&s->bap_buffer);
     av_freep(&s->bap1_buffer);
     av_freep(&s->mdct_coef_buffer);
@@ -2170,6 +2191,7 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx)
     av_freep(&s->cpl_coord_buffer);
     av_freep(&s->fdsp);
 
+    ff_af_queue_close(&s->afq);
     av_tx_uninit(&s->tx);
 
     return 0;
@@ -2419,6 +2441,11 @@ static av_cold int allocate_buffers(AC3EncodeContext *s)
         if (!s->planar_samples[ch])
             return AVERROR(ENOMEM);
     }
+    int ret = av_samples_alloc(s->input_samples, NULL, s->channels,
+                               AC3_BLOCK_SIZE * s->num_blocks,
+                               s->avctx->sample_fmt, 0);
+    if (ret < 0)
+        return ret;
 
     if (!FF_ALLOC_TYPED_ARRAY(s->bap_buffer,         total_coefs)          ||
         !FF_ALLOC_TYPED_ARRAY(s->bap1_buffer,        total_coefs)          ||
@@ -2516,6 +2543,8 @@ av_cold int ff_ac3_encode_init(AVCodecContext *avctx)
 
     dprint_options(s);
 
+    ff_af_queue_init(avctx, &s->afq);
+
     ff_thread_once(&init_static_once, exponent_init);
 
     return 0;
diff --git a/libavcodec/ac3enc.h b/libavcodec/ac3enc.h
index 5e98ad188b..3e92af17f0 100644
--- a/libavcodec/ac3enc.h
+++ b/libavcodec/ac3enc.h
@@ -37,6 +37,7 @@
 #include "ac3.h"
 #include "ac3defs.h"
 #include "ac3dsp.h"
+#include "audio_frame_queue.h"
 #include "avcodec.h"
 #include "codec_internal.h"
 #include "mathops.h"
@@ -233,6 +234,7 @@ typedef struct AC3EncodeContext {
     int exponent_bits;                      ///< number of bits used for 
exponents
 
     uint8_t *planar_samples[AC3_MAX_CHANNELS - 1];
+    uint8_t *input_samples[AC3_MAX_CHANNELS - 1];
     uint8_t *bap_buffer;
     uint8_t *bap1_buffer;
     CoefType *mdct_coef_buffer;
@@ -252,8 +254,10 @@ typedef struct AC3EncodeContext {
     uint8_t *ref_bap     [AC3_MAX_CHANNELS][AC3_MAX_BLOCKS]; ///< bit 
allocation pointers (bap)
     int ref_bap_set;                                         ///< indicates if 
ref_bap pointers have been set
 
+    AudioFrameQueue afq;
+
     /** fixed vs. float function pointers */
-    void (*encode_frame)(struct AC3EncodeContext *s, uint8_t * const *samples);
+    void (*encode_frame)(struct AC3EncodeContext *s, const AVFrame *frame);
 
     /* AC-3 vs. E-AC-3 function pointers */
     void (*output_frame_header)(struct AC3EncodeContext *s, struct 
PutBitContext *pb);
diff --git a/libavcodec/ac3enc_fixed.c b/libavcodec/ac3enc_fixed.c
index 42530b0ea1..876bb6bbc6 100644
--- a/libavcodec/ac3enc_fixed.c
+++ b/libavcodec/ac3enc_fixed.c
@@ -114,7 +114,8 @@ const FFCodec ff_ac3_fixed_encoder = {
     CODEC_LONG_NAME("ATSC A/52A (AC-3)"),
     .p.type          = AVMEDIA_TYPE_AUDIO,
     .p.id            = AV_CODEC_ID_AC3,
-    .p.capabilities  = AV_CODEC_CAP_DR1 | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+    .p.capabilities  = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
+                       AV_CODEC_CAP_SMALL_LAST_FRAME | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     .priv_data_size  = sizeof(AC3EncodeContext),
     .init            = ac3_fixed_encode_init,
     FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
diff --git a/libavcodec/ac3enc_float.c b/libavcodec/ac3enc_float.c
index 0ae7ddd7eb..974c38ac25 100644
--- a/libavcodec/ac3enc_float.c
+++ b/libavcodec/ac3enc_float.c
@@ -116,7 +116,8 @@ const FFCodec ff_ac3_encoder = {
     CODEC_LONG_NAME("ATSC A/52A (AC-3)"),
     .p.type          = AVMEDIA_TYPE_AUDIO,
     .p.id            = AV_CODEC_ID_AC3,
-    .p.capabilities  = AV_CODEC_CAP_DR1 | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+    .p.capabilities  = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
+                       AV_CODEC_CAP_SMALL_LAST_FRAME | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     .priv_data_size  = sizeof(AC3EncodeContext),
     .init            = ff_ac3_float_encode_init,
     FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
diff --git a/libavcodec/ac3enc_template.c b/libavcodec/ac3enc_template.c
index 049666fdca..5331b45cb9 100644
--- a/libavcodec/ac3enc_template.c
+++ b/libavcodec/ac3enc_template.c
@@ -346,9 +346,31 @@ static void compute_rematrixing_strategy(AC3EncodeContext 
*s)
     }
 }
 
+static void copy_input_samples(AC3EncodeContext *s, const AVFrame *frame)
+{
+    int end = frame ? frame->nb_samples : 0;
+
+    /* copy new samples and zero any remaining samples */
+    if (frame) {
+        av_samples_copy(s->input_samples, frame->extended_data, 0, 0,
+                        frame->nb_samples, s->channels,
+                        s->avctx->sample_fmt);
+    }
+    av_samples_set_silence(s->input_samples, end,
+                           s->avctx->frame_size - end,
+                           s->channels, s->avctx->sample_fmt);
+}
 
-static void encode_frame(AC3EncodeContext *s, uint8_t * const *samples)
+static void encode_frame(AC3EncodeContext *s, const AVFrame *frame)
 {
+    uint8_t **samples;
+
+    if (!frame || frame->nb_samples < s->avctx->frame_size) {
+        copy_input_samples(s, frame);
+        samples = s->input_samples;
+    } else
+        samples = frame->extended_data;
+
     apply_mdct(s, samples);
 
     s->cpl_on = s->cpl_enabled;
diff --git a/libavcodec/eac3enc.c b/libavcodec/eac3enc.c
index 10b1ab337c..7a678b518c 100644
--- a/libavcodec/eac3enc.c
+++ b/libavcodec/eac3enc.c
@@ -270,7 +270,8 @@ const FFCodec ff_eac3_encoder = {
     CODEC_LONG_NAME("ATSC A/52 E-AC-3"),
     .p.type          = AVMEDIA_TYPE_AUDIO,
     .p.id            = AV_CODEC_ID_EAC3,
-    .p.capabilities  = AV_CODEC_CAP_DR1 | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+    .p.capabilities  = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
+                       AV_CODEC_CAP_SMALL_LAST_FRAME | 
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
     .priv_data_size  = sizeof(AC3EncodeContext),
     .init            = eac3_encode_init,
     FF_CODEC_ENCODE_CB(ff_ac3_encode_frame),
diff --git a/tests/fate/ac3.mak b/tests/fate/ac3.mak
index 9f655c3ff4..362671ed91 100644
--- a/tests/fate/ac3.mak
+++ b/tests/fate/ac3.mak
@@ -68,18 +68,14 @@ $(FATE_AC3) $(FATE_EAC3): CMP = oneoff
 FATE_AC3-$(call  PCM, AC3,  AC3 AC3_FIXED, PCM_S16LE_MUXER ARESAMPLE_FILTER)  
+= $(FATE_AC3)
 FATE_EAC3-$(call PCM, EAC3, EAC3,          PCM_S16LE_MUXER ARESAMPLE_FILTER) 
+= $(FATE_EAC3)
 
-FATE_AC3-$(call ENCDEC, AC3, AC3, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER 
PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-ac3-encode
-fate-ac3-encode: CMD = enc_dec_pcm ac3 wav s16le $(subst 
$(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a ac3 -b:a 128k
-fate-ac3-encode: CMP_SHIFT = -1024
+FATE_AC3-$(call ENCDEC, AC3, MP4 MOV, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER 
PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-ac3-encode
+fate-ac3-encode: CMD = enc_dec_pcm mp4 wav s16le $(subst 
$(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a ac3 -b:a 128k
 fate-ac3-encode: CMP_TARGET = 404.53
-fate-ac3-encode: SIZE_TOLERANCE = 488
 
 
-FATE_EAC3-$(call ENCDEC, EAC3, EAC3, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER 
PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-eac3-encode
-fate-eac3-encode: CMD = enc_dec_pcm eac3 wav s16le $(subst 
$(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a eac3 -b:a 128k
-fate-eac3-encode: CMP_SHIFT = -1024
+FATE_EAC3-$(call ENCDEC, EAC3, MP4 MOV, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER 
PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-eac3-encode
+fate-eac3-encode: CMD = enc_dec_pcm mp4 wav s16le $(subst 
$(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a eac3 -b:a 128k
 fate-eac3-encode: CMP_TARGET = 516.94
-fate-eac3-encode: SIZE_TOLERANCE = 488
 
 fate-ac3-encode fate-eac3-encode: CMP = stddev
 fate-ac3-encode fate-eac3-encode: REF = 
$(SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav
diff --git a/tests/ref/fate/ac3-fixed-encode-2 
b/tests/ref/fate/ac3-fixed-encode-2
index 8e945b6637..468441cd7e 100644
--- a/tests/ref/fate/ac3-fixed-encode-2
+++ b/tests/ref/fate/ac3-fixed-encode-2
@@ -11,3 +11,4 @@
 0,       7424,       7424,     1536,     1114, 0xfbcc27ad
 0,       8960,       8960,     1536,     1114, 0xe7ed3321
 0,      10496,      10496,     1536,     1114, 0xa1823473
+0,      12032,      12032,      256,     1116, 0x7cc628e3, S=1, Skip Samples,  
     10, 0x00190005
diff --git a/tests/ref/fate/shortest b/tests/ref/fate/shortest
index 3592b3f1f0..68d1038b71 100644
--- a/tests/ref/fate/shortest
+++ b/tests/ref/fate/shortest
@@ -115,3 +115,4 @@
 0,         48,         48,        1,    28143, 0x1df268c5, S=1, Quality stats, 
       8, 0x050000a1
 1,      85760,      85760,     1536,      418, 0xae06ca91
 0,         49,         49,        1,    10073, 0xedb9f031, F=0x0, S=1, Quality 
stats,        8, 0x050400a2
+1,      87296,      87296,      256,      418, 0x0a0bdddc, S=1, Skip Samples,  
     10, 0x00190005

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

Reply via email to