[FFmpeg-devel] [PATCH] Add decoding of > 32-bit residuals to FLAC
Martijn van Beurden
mvanb1 at gmail.com
Sun Mar 27 16:39:33 EEST 2022
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
More information about the ffmpeg-devel
mailing list