[PR] avcodec/alac: don't require a clean end tag to stop decoding (PR #23948)
flux via ffmpeg-devel <[email protected]> Wed, 29 Jul 2026 14:18:47 -0000
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <178533472822.51.12361070911860821878@29965ddac10e> |
PR #23948 opened by flux URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23948 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23948.patch # Summary of changes alac_decode_frame()'s element loop stopped only on TYPE_END or running out of bits, with no check for "all declared channels decoded". Some escape/verbatim frames pad to the packet's byte boundary (matching maxFrameBytes) without an end tag. The loop then reads the zero padding as a new element, and the channel-count guard rejects it with "invalid element channel count". Apple's reference decoder breaks out of the same loop once channelIndex >= numChannels: https://github.com/macosforge/alac/blob/c38887c5c5e64a4b31108733bd79ca9b2496d987/codec/ALACDecoder.cpp#L588-L593 Stop the loop once ch reaches alac->channels, and only require an end tag / check leftover bits when the loop actually reached one. New fate sample file attached to this PR: ```fate-samples alac/alac-noendtag.m4a ``` Current build of ffmpeg will fail to decode this file, but afconvert (reference ALAC decoder) and patched ffmpeg build will succeed. >From cb0ec887c3b2597178bf7931b2a5bd0589b1ee06 Mon Sep 17 00:00:00 2001 From: Thitat Auareesuksakul <[email protected]> Date: Wed, 29 Jul 2026 22:50:18 +0900 Subject: [PATCH] avcodec/alac: don't require a clean end tag to stop decoding alac_decode_frame()'s element loop stopped only on TYPE_END or running out of bits, with no check for "all declared channels decoded". Some escape/verbatim frames pad to the packet's byte boundary (matching maxFrameBytes) without an end tag. The loop then reads the zero padding as a new element, and the channel-count guard rejects it with "invalid element channel count". Apple's reference decoder breaks out of the same loop once channelIndex >= numChannels: https://github.com/macosforge/alac/blob/c38887c5c5e64a4b31108733bd79ca9b2496d987/codec/ALACDecoder.cpp#L588-L593 Stop the loop once ch reaches alac->channels, and only require an end tag / check leftover bits when the loop actually reached one. Signed-off-by: Thitat Auareesuksakul <[email protected]> --- libavcodec/alac.c | 9 ++++++--- tests/fate/lossless-audio.mak | 3 +++ tests/ref/fate/lossless-alac-noendtag | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 tests/ref/fate/lossless-alac-noendtag diff --git a/libavcodec/alac.c b/libavcodec/alac.c index f91288e97c..241c43e073 100644 --- a/libavcodec/alac.c +++ b/libavcodec/alac.c @@ -432,7 +432,10 @@ static int alac_decode_frame(AVCodecContext *avctx, AVFrame *frame, got_end = 0; alac->nb_samples = 0; ch = 0; - while (get_bits_left(&alac->gb) >= 3) { + /* stop once all channels are decoded instead of requiring a clean end tag. + * The reference decoder does the same (channelIndex >= numChannels in its element loop), + * since bits after the last audio element may just be padding to the frame's max coded size */ + while (ch < alac->channels && get_bits_left(&alac->gb) >= 3) { element = get_bits(&alac->gb, 3); if (element == TYPE_END) { got_end = 1; @@ -458,12 +461,12 @@ static int alac_decode_frame(AVCodecContext *avctx, AVFrame *frame, ch += channels; } - if (!got_end) { + if (!got_end && ch < alac->channels) { av_log(avctx, AV_LOG_ERROR, "no end tag found. incomplete packet.\n"); return AVERROR_INVALIDDATA; } - if (avpkt->size * 8 - get_bits_count(&alac->gb) > 8) { + if (got_end && avpkt->size * 8 - get_bits_count(&alac->gb) > 8) { av_log(avctx, AV_LOG_ERROR, "Error : %d bits left\n", avpkt->size * 8 - get_bits_count(&alac->gb)); } diff --git a/tests/fate/lossless-audio.mak b/tests/fate/lossless-audio.mak index 8be62b5244..a1d0aa6f08 100644 --- a/tests/fate/lossless-audio.mak +++ b/tests/fate/lossless-audio.mak @@ -1,6 +1,9 @@ FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, MOV, ALAC, ARESAMPLE_FILTER) += fate-lossless-alac fate-lossless-alac: CMD = md5 -i $(TARGET_SAMPLES)/lossless-audio/inside.m4a -f s16le -af aresample +FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, MOV, ALAC, ARESAMPLE_FILTER) += fate-lossless-alac-noendtag +fate-lossless-alac-noendtag: CMD = md5 -i $(TARGET_SAMPLES)/alac/alac-noendtag.m4a -f s16le -af aresample + FATE_SAMPLES_LOSSLESS_AUDIO-$(call DEMDEC, MLP, MLP) += fate-lossless-meridianaudio fate-lossless-meridianaudio: CMD = md5 -i $(TARGET_SAMPLES)/lossless-audio/luckynight-partial.mlp -f s16le diff --git a/tests/ref/fate/lossless-alac-noendtag b/tests/ref/fate/lossless-alac-noendtag new file mode 100644 index 0000000000..a41cba4217 --- /dev/null +++ b/tests/ref/fate/lossless-alac-noendtag @@ -0,0 +1 @@ +989862aa6617be4c1bc1c5df4de81683 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]