From: Martijn van Beurden <mvanb1@gmail.com> To: ffmpeg-devel@ffmpeg.org Cc: Martijn van Beurden <mvanb1@gmail.com> Subject: [FFmpeg-devel] [PATCH] Add decoding of > 32-bit residuals to FLAC Date: Sun, 27 Mar 2022 15:39:33 +0200 Message-ID: <20220327133933.43039-1-mvanb1@gmail.com> (raw) The size of residuals in a FLAC file coding for 24-bit PCM can exceed the range of a 32-bit signed integer. One pathological example with residuals exceeding [-2^33,2^33) can be found here: http://www.audiograaf.nl/misc_stuff/Extreme%20residual%20LPC%20order%2014.flac The theorectical maximum bit depth for a residual in a FLAC file is 32 + 1 + 15 + 5 - 0 = 53 bit (max bit depth + extra bit for side channel + max lpc coeff precision + log2(max_order) - min lpc shift) This patch adds detection of the possibilty of such residuals occuring and an alternate data path wide enough to handle them --- libavcodec/flacdec.c | 107 ++++++++++++++++++++++++++++++++++++++----- libavcodec/golomb.h | 56 ++++++++++++++++++++++ 2 files changed, 152 insertions(+), 11 deletions(-) diff --git a/libavcodec/flacdec.c b/libavcodec/flacdec.c index dd6026f9de..3be1b63411 100644 --- a/libavcodec/flacdec.c +++ b/libavcodec/flacdec.c @@ -64,6 +64,8 @@ typedef struct FLACContext { uint8_t *decoded_buffer; unsigned int decoded_buffer_size; int buggy_lpc; ///< use workaround for old lavc encoded files + int64_t *residual64; ///< to keep residuals exceeding int32_t + unsigned int residual64_size; FLACDSPContext dsp; } FLACContext; @@ -149,6 +151,10 @@ static int allocate_buffers(FLACContext *s) if (!s->decoded_buffer) return AVERROR(ENOMEM); + av_fast_malloc(&s->residual64, &s->residual64_size, 8*s->flac_stream_info.max_blocksize); + if (!s->residual64) + return AVERROR(ENOMEM); + ret = av_samples_fill_arrays((uint8_t **)s->decoded, NULL, s->decoded_buffer, s->flac_stream_info.channels, @@ -279,6 +285,66 @@ static int decode_residuals(FLACContext *s, int32_t *decoded, int pred_order) return 0; } +static int decode_residuals64(FLACContext *s, int64_t *decoded, int pred_order) +{ + GetBitContext gb = s->gb; + int i, tmp, partition, method_type, rice_order; + int rice_bits, rice_esc; + int samples; + + method_type = get_bits(&gb, 2); + rice_order = get_bits(&gb, 4); + + samples = s->blocksize >> rice_order; + rice_bits = 4 + method_type; + rice_esc = (1 << rice_bits) - 1; + + decoded += pred_order; + i = pred_order; + + if (method_type > 1) { + av_log(s->avctx, AV_LOG_ERROR, "illegal residual coding method %d\n", + method_type); + return AVERROR_INVALIDDATA; + } + + if (samples << rice_order != s->blocksize) { + av_log(s->avctx, AV_LOG_ERROR, "invalid rice order: %i blocksize %i\n", + rice_order, s->blocksize); + return AVERROR_INVALIDDATA; + } + + if (pred_order > samples) { + av_log(s->avctx, AV_LOG_ERROR, "invalid predictor order: %i > %i\n", + pred_order, samples); + return AVERROR_INVALIDDATA; + } + + for (partition = 0; partition < (1 << rice_order); partition++) { + tmp = get_bits(&gb, rice_bits); + if (tmp == rice_esc) { + tmp = get_bits(&gb, 5); + for (; i < samples; i++) + *decoded++ = get_sbits_long(&gb, tmp); + } else { + for (; i < samples; i++) { + int64_t v = get_sr_golomb64_flac(&gb, tmp, 1); + if (v == INT64_MAX) { + av_log(s->avctx, AV_LOG_ERROR, "invalid residual\n"); + return AVERROR_INVALIDDATA; + } + + *decoded++ = v; + } + } + i = 0; + } + + s->gb = gb; + + return 0; +} + static int decode_subframe_fixed(FLACContext *s, int32_t *decoded, int pred_order, int bps) { @@ -358,6 +424,21 @@ static void lpc_analyze_remodulate(SUINT32 *decoded, const int coeffs[32], } } +static void lpc_residual64(int32_t *decoded, const int64_t *residual, + const int coeffs[32], int pred_order, + int qlevel, int len) +{ + int i, j; + + for (i = pred_order; i < len; i++, decoded++) { + int64_t sum = 0; + for (j = 0; j < pred_order; j++) + sum += (int64_t)coeffs[j] * decoded[j]; + decoded[j] = residual[i] + (sum >> qlevel); + } + +} + static int decode_subframe_lpc(FLACContext *s, int32_t *decoded, int pred_order, int bps) { @@ -386,19 +467,23 @@ static int decode_subframe_lpc(FLACContext *s, int32_t *decoded, int pred_order, coeffs[pred_order - i - 1] = get_sbits(&s->gb, coeff_prec); } - if ((ret = decode_residuals(s, decoded, pred_order)) < 0) - return ret; - - if ( ( s->buggy_lpc && s->flac_stream_info.bps <= 16) - || ( !s->buggy_lpc && bps <= 16 - && bps + coeff_prec + av_log2(pred_order) <= 32)) { - s->dsp.lpc16(decoded, coeffs, pred_order, qlevel, s->blocksize); + if (bps + coeff_prec + av_log2(pred_order) - qlevel <= 32) { + if ((ret = decode_residuals(s, decoded, pred_order)) < 0) + return ret; + if ( ( s->buggy_lpc && s->flac_stream_info.bps <= 16) + || ( !s->buggy_lpc && bps <= 16 + && bps + coeff_prec + av_log2(pred_order) <= 32)) { + s->dsp.lpc16(decoded, coeffs, pred_order, qlevel, s->blocksize); + } else { + s->dsp.lpc32(decoded, coeffs, pred_order, qlevel, s->blocksize); + if (s->flac_stream_info.bps <= 16) + lpc_analyze_remodulate(decoded, coeffs, pred_order, qlevel, s->blocksize, bps); + } } else { - s->dsp.lpc32(decoded, coeffs, pred_order, qlevel, s->blocksize); - if (s->flac_stream_info.bps <= 16) - lpc_analyze_remodulate(decoded, coeffs, pred_order, qlevel, s->blocksize, bps); + if ((ret = decode_residuals64(s, s->residual64, pred_order)) < 0) + return ret; + lpc_residual64(decoded, s->residual64, coeffs, pred_order, qlevel, s->blocksize); } - return 0; } diff --git a/libavcodec/golomb.h b/libavcodec/golomb.h index 164c2583b6..5ebcdda059 100644 --- a/libavcodec/golomb.h +++ b/libavcodec/golomb.h @@ -543,6 +543,62 @@ static inline int get_sr_golomb_flac(GetBitContext *gb, int k, int limit, return (v >> 1) ^ -(v & 1); } +static inline int64_t get_sr_golomb64_flac(GetBitContext *gb, int k, + int esc_len) +{ + uint64_t buf; + int log; + + OPEN_READER(re, gb); + UPDATE_CACHE(re, gb); + buf = GET_CACHE(re, gb); + + log = av_log2(buf); + + av_assert2(k <= 31); + + if (log - k >= 64 - MIN_CACHE_BITS + (MIN_CACHE_BITS == 64)) { + buf >>= log - k; + buf += (62U - log) << k; + LAST_SKIP_BITS(re, gb, 64 + k - log); + CLOSE_READER(re, gb); + } else { + int64_t i; + for (i = 0; SHOW_UBITS(re, gb, MIN_CACHE_BITS) == 0; i += MIN_CACHE_BITS) { + if (gb->size_in_bits <= re_index) { + CLOSE_READER(re, gb); + return INT64_MAX; + } + LAST_SKIP_BITS(re, gb, MIN_CACHE_BITS); + UPDATE_CACHE(re, gb); + } + for (; SHOW_UBITS(re, gb, 1) == 0; i++) { + SKIP_BITS(re, gb, 1); + } + LAST_SKIP_BITS(re, gb, 1); + UPDATE_CACHE(re, gb); + + if (k) { + if (k > MIN_CACHE_BITS - 1) { + buf = SHOW_UBITS(re, gb, 16) << (k-16); + LAST_SKIP_BITS(re, gb, 16); + UPDATE_CACHE(re, gb); + buf |= SHOW_UBITS(re, gb, k-16); + LAST_SKIP_BITS(re, gb, k-16); + } else { + buf = SHOW_UBITS(re, gb, k); + LAST_SKIP_BITS(re, gb, k); + } + } else { + buf = 0; + } + + buf += (i << k); + CLOSE_READER(re, gb); + } + return (buf >> 1) ^ -(buf & 1); +} + /** * read unsigned golomb rice code (shorten). */ -- 2.30.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next reply other threads:[~2022-03-27 13:40 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-03-27 13:39 Martijn van Beurden [this message] 2022-03-30 10:42 ` Andreas Rheinhardt 2022-03-30 17:54 ` Martijn van Beurden
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20220327133933.43039-1-mvanb1@gmail.com \ --to=mvanb1@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git