[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