PR #23651 opened by James Almer (jamrial) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23651 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23651.patch
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 -` >From 378faf5bac08d2f856996e8d7a320e2bdaf0ddc7 Mon Sep 17 00:00:00 2001 From: James Almer <[email protected]> Date: Mon, 29 Jun 2026 19:24:39 -0300 Subject: [PATCH] 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 | 34 ++++++++++++++++++++++++++----- libavcodec/ac3enc.h | 6 +++++- libavcodec/ac3enc_fixed.c | 3 ++- libavcodec/ac3enc_float.c | 3 ++- libavcodec/ac3enc_template.c | 29 ++++++++++++++++++++++---- libavcodec/eac3enc.c | 3 ++- tests/fate/ac3.mak | 18 +++++++--------- tests/ref/fate/ac3-fixed-encode-2 | 1 + tests/ref/fate/autorotate | 2 +- tests/ref/fate/shortest | 1 + tests/ref/fate/unknown_layout-ac3 | 2 +- tests/ref/lavf/rm | 2 +- 12 files changed, 77 insertions(+), 27 deletions(-) diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c index 5a1a3ab63a..9172348712 100644 --- a/libavcodec/ac3enc.c +++ b/libavcodec/ac3enc.c @@ -1982,8 +1982,18 @@ 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) { + if ((ret = ff_af_queue_add(&s->afq, frame)) < 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 +2003,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 +2024,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; @@ -2155,8 +2174,10 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx) { AC3EncodeContext *s = avctx->priv_data; - for (int ch = 0; ch < s->channels; ch++) + for (int ch = 0; ch < s->channels; ch++) { av_freep(&s->planar_samples[ch]); + av_freep(&s->input_samples[ch]); + } av_freep(&s->bap_buffer); av_freep(&s->bap1_buffer); av_freep(&s->mdct_coef_buffer); @@ -2416,7 +2437,8 @@ static av_cold int allocate_buffers(AC3EncodeContext *s) for (int ch = 0; ch < s->channels; ch++) { s->planar_samples[ch] = av_mallocz(AC3_BLOCK_SIZE * sampletype_size); - if (!s->planar_samples[ch]) + s->input_samples[ch] = av_mallocz(AC3_BLOCK_SIZE * sampletype_size * s->num_blocks); + if (!s->planar_samples[ch] || !s->input_samples[ch]) return AVERROR(ENOMEM); } @@ -2516,6 +2538,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..6d662c74da 100644 --- a/libavcodec/ac3enc_template.c +++ b/libavcodec/ac3enc_template.c @@ -49,9 +49,10 @@ * This applies the KBD window and normalizes the input to reduce precision * loss due to fixed-point calculations. */ -static void apply_mdct(AC3EncodeContext *s, uint8_t * const *samples) +static void apply_mdct(AC3EncodeContext *s) { av_assert1(s->num_blocks > 0); + uint8_t * const *samples = s->input_samples; for (int ch = 0; ch < s->channels; ch++) { const SampleType *input_samples0 = (const SampleType*)s->planar_samples[ch]; @@ -346,10 +347,30 @@ static void compute_rematrixing_strategy(AC3EncodeContext *s) } } - -static void encode_frame(AC3EncodeContext *s, uint8_t * const *samples) +/* + * Copy input samples. + */ +static void copy_input_samples(AC3EncodeContext *s, const AVFrame *frame) { - apply_mdct(s, samples); + int ch; + int end = frame ? frame->nb_samples : 0; + + for (ch = 0; ch < s->channels; ch++) { + /* copy new samples and zero any remaining samples */ + if (frame) { + memcpy(s->input_samples[ch], + frame->extended_data[ch], + frame->nb_samples * sizeof(SampleType)); + } + memset(&s->input_samples[ch][end], 0, + (s->avctx->frame_size - end) * sizeof(SampleType)); + } +} + +static void encode_frame(AC3EncodeContext *s, const AVFrame *frame) +{ + copy_input_samples(s, frame); + apply_mdct(s); s->cpl_on = s->cpl_enabled; ff_ac3_compute_coupling_strategy(s); 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..c0b10c68fb 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-encode: CMP_TARGET = 404.53 -fate-ac3-encode: SIZE_TOLERANCE = 488 +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 = 410.25 -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-encode: CMP_TARGET = 516.94 -fate-eac3-encode: SIZE_TOLERANCE = 488 +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 = 520.69 fate-ac3-encode fate-eac3-encode: CMP = stddev fate-ac3-encode fate-eac3-encode: REF = $(SAMPLES)/audio-reference/luckynight_2ch_44kHz_s16.wav @@ -89,7 +85,7 @@ fate-ac3-fixed-encode: tests/data/asynth-44100-2.wav fate-ac3-fixed-encode: SRC = $(TARGET_PATH)/tests/data/asynth-44100-2.wav fate-ac3-fixed-encode: CMD = md5 -i $(SRC) -c ac3_fixed -ab 128k -f ac3 -flags +bitexact -af aresample fate-ac3-fixed-encode: CMP = oneline -fate-ac3-fixed-encode: REF = e9d78bca187b4bbafc4512bcea8efd3e +fate-ac3-fixed-encode: REF = 024170ce0147cbb9d6eda34b30b05935 # This tests that the LFE does not get lost when converting the input 7.1 # to a channel layout supported by the encoder. 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/autorotate b/tests/ref/fate/autorotate index 1166bb63fc..34870592c8 100644 --- a/tests/ref/fate/autorotate +++ b/tests/ref/fate/autorotate @@ -1,4 +1,4 @@ -bb03affe94dfbe39c8cad8eb69dc367c *tests/data/fate/autorotate.mov +54cea6414b534ed2ee8c37c00988dabd *tests/data/fate/autorotate.mov 197366 tests/data/fate/autorotate.mov #extradata 0: 34, 0x9d7d073f #tb 0: 1/15360 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 diff --git a/tests/ref/fate/unknown_layout-ac3 b/tests/ref/fate/unknown_layout-ac3 index a694c52899..071541f2db 100644 --- a/tests/ref/fate/unknown_layout-ac3 +++ b/tests/ref/fate/unknown_layout-ac3 @@ -1 +1 @@ -ff7e25844b3cb6abb571ef7e226cbafa +15c6d7256fa03f0fdbf6e0e2376448c0 diff --git a/tests/ref/lavf/rm b/tests/ref/lavf/rm index 62251380cf..cec7ec0725 100644 --- a/tests/ref/lavf/rm +++ b/tests/ref/lavf/rm @@ -1,2 +1,2 @@ -a7b0ac6e5131bbf662a07ccc82ab8618 *tests/data/lavf/lavf.rm +4ef2bdea6c0fbf4f71726e5a9e359415 *tests/data/lavf/lavf.rm 346424 tests/data/lavf/lavf.rm -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
