Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol entropy decode
@ 2025-05-11 10:09 Mark Thompson
  2025-05-11 10:09 ` [FFmpeg-devel] [PATCH v2 2/2] lavc: Add unit test for APV " Mark Thompson
  2025-05-13 19:19 ` [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol " Mark Thompson
  0 siblings, 2 replies; 3+ messages in thread
From: Mark Thompson @ 2025-05-11 10:09 UTC (permalink / raw)
  To: ffmpeg-devel

---
v2:
* Bring error handling to the same level as before.
* Split first run/level step to avoid first-AC handling inside the loop.
* More commentary.
* Other minor improvements.

Thanks,

- Mark

 libavcodec/apv_decode.c  |   5 +-
 libavcodec/apv_decode.h  |  59 ++--
 libavcodec/apv_entropy.c | 612 +++++++++++++++++++++++++++++++--------
 3 files changed, 541 insertions(+), 135 deletions(-)

diff --git a/libavcodec/apv_decode.c b/libavcodec/apv_decode.c
index e15c125b58..eb47298e2e 100644
--- a/libavcodec/apv_decode.c
+++ b/libavcodec/apv_decode.c
@@ -160,6 +160,7 @@ static int apv_decode_block(AVCodecContext *avctx,
     int err;
 
     LOCAL_ALIGNED_32(int16_t, coeff, [64]);
+    memset(coeff, 0, 64 * sizeof(int16_t));
 
     err = ff_apv_entropy_decode_block(coeff, gbc, entropy_state);
     if (err < 0)
@@ -216,8 +217,8 @@ static int apv_decode_tile_component(AVCodecContext *avctx, void *data,
         .log_ctx           = avctx,
         .decode_lut        = &decode_lut,
         .prev_dc           = 0,
-        .prev_dc_diff      = 20,
-        .prev_1st_ac_level = 0,
+        .prev_k_dc         = 5,
+        .prev_k_level      = 0,
     };
 
     int err;
diff --git a/libavcodec/apv_decode.h b/libavcodec/apv_decode.h
index 34c6176ea0..5671d89552 100644
--- a/libavcodec/apv_decode.h
+++ b/libavcodec/apv_decode.h
@@ -33,14 +33,39 @@
 #define APV_VLC_LUT_BITS 9
 #define APV_VLC_LUT_SIZE (1 << APV_VLC_LUT_BITS)
 
-typedef struct APVVLCLUTEntry {
+typedef struct APVSingleVLCLUTEntry {
     uint16_t result;  // Return value if not reading more.
     uint8_t  consume; // Number of bits to consume.
     uint8_t  more;    // Whether to read additional bits.
-} APVVLCLUTEntry;
+} APVSingleVLCLUTEntry;
+
+typedef struct APVMultiVLCLUTEntry {
+    // Number of symbols this bit stream resolves to.
+    uint8_t count;
+    // k_run after decoding all symbols.
+    uint8_t k_run     : 2;
+    // k_level after decoding the first level symbol.
+    uint8_t k_level_0 : 3;
+    // k_level after decoding all symbols.
+    uint8_t k_level_1 : 3;
+    // Run output values.
+    uint8_t run[2];
+    // Level output values.
+    int16_t level[2];
+    // Bit index of the end of each code.
+    uint8_t offset[4];
+} APVMultiVLCLUTEntry;
 
 typedef struct APVVLCLUT {
-    APVVLCLUTEntry lut[6][APV_VLC_LUT_SIZE];
+    // Single-symbol LUT for VLCs.
+    // Applies to all coefficients, but used only for DC coefficients
+    // in the decoder.
+    APVSingleVLCLUTEntry single_lut[6][APV_VLC_LUT_SIZE];
+    // Multi-symbol LUT for run/level combinations, decoding up to four
+    // symbols per step.  Comes in two versions, which to use depends on
+    // whether the next symbol is a run or a level.
+    APVMultiVLCLUTEntry run_first_lut[3][5][APV_VLC_LUT_SIZE];
+    APVMultiVLCLUTEntry level_first_lut[3][5][APV_VLC_LUT_SIZE];
 } APVVLCLUT;
 
 typedef struct APVEntropyState {
@@ -48,33 +73,29 @@ typedef struct APVEntropyState {
 
     const APVVLCLUT *decode_lut;
 
+    // Previous DC level value.
     int16_t prev_dc;
-    int16_t prev_dc_diff;
-    int16_t prev_1st_ac_level;
+    // k parameter implied by the previous DC level value.
+    uint8_t prev_k_dc;
+    // k parameter implied by the previous first AC level value.
+    uint8_t prev_k_level;
 } APVEntropyState;
 
 
 /**
- * Build the decoder VLC look-up table.
+ * Build the decoder VLC look-up tables.
  */
 void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut);
 
 /**
  * Entropy decode a single 8x8 block to coefficients.
  *
- * Outputs in block order (dezigzag already applied).
+ * Outputs nonzero coefficients only to the block row-major order
+ * (dezigzag is applied within the function).  The output block
+ * must have been filled with zeroes before calling this function.
  */
-int ff_apv_entropy_decode_block(int16_t *coeff,
-                                GetBitContext *gbc,
-                                APVEntropyState *state);
-
-/**
- * Read a single APV VLC code.
- *
- * This entrypoint is exposed for testing.
- */
-unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param,
-                             const APVVLCLUT *lut);
-
+int ff_apv_entropy_decode_block(int16_t *restrict coeff,
+                                GetBitContext *restrict gbc,
+                                APVEntropyState *restrict state);
 
 #endif /* AVCODEC_APV_DECODE_H */
diff --git a/libavcodec/apv_entropy.c b/libavcodec/apv_entropy.c
index a5648c09b4..49d5505b6b 100644
--- a/libavcodec/apv_entropy.c
+++ b/libavcodec/apv_entropy.c
@@ -19,15 +19,55 @@
 #include "apv.h"
 #include "apv_decode.h"
 
+#include "put_bits.h"
+
+
+av_always_inline
+static unsigned int apv_read_vlc(GetBitContext *restrict gbc, int k_param,
+                                 const APVVLCLUT *restrict lut)
+{
+    unsigned int next_bits;
+    const APVSingleVLCLUTEntry *ent;
+
+    next_bits = show_bits(gbc, APV_VLC_LUT_BITS);
+    ent = &lut->single_lut[k_param][next_bits];
+
+    if (ent->more) {
+        unsigned int leading_zeroes;
+
+        skip_bits(gbc, ent->consume);
+
+        next_bits = show_bits(gbc, 16);
+        leading_zeroes = 15 - av_log2(next_bits);
+
+        if (leading_zeroes == 0) {
+            // This can't happen mid-stream because the lookup would
+            // have resolved a leading one into a shorter code, but it
+            // can happen if we are hitting the end of the buffer.
+            // Return an invalid code to propagate as an error.
+            return APV_MAX_TRANS_COEFF + 1;
+        }
+
+        skip_bits(gbc, leading_zeroes + 1);
+
+        return (2 << k_param) +
+            ((1 << leading_zeroes) - 1) * (1 << k_param) +
+            get_bits(gbc, leading_zeroes + k_param);
+    } else {
+        skip_bits(gbc, ent->consume);
+        return ent->result;
+    }
+}
 
 void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut)
 {
     const int code_len = APV_VLC_LUT_BITS;
     const int lut_size = APV_VLC_LUT_SIZE;
 
+    // Build the single-symbol VLC table.
     for (int k = 0; k <= 5; k++) {
         for (unsigned int code = 0; code < lut_size; code++) {
-            APVVLCLUTEntry *ent = &decode_lut->lut[k][code];
+            APVSingleVLCLUTEntry   *ent = &decode_lut->single_lut[k][code];
             unsigned int first_bit      = code & (1 << code_len - 1);
             unsigned int remaining_bits = code ^ first_bit;
 
@@ -64,153 +104,497 @@ void ff_apv_entropy_build_decode_lut(APVVLCLUT *decode_lut)
             }
         }
     }
-}
-
-av_always_inline
-static unsigned int apv_read_vlc(GetBitContext *gbc, int k_param,
-                                 const APVVLCLUT *lut)
-{
-    unsigned int next_bits;
-    const APVVLCLUTEntry *ent;
-
-    next_bits = show_bits(gbc, APV_VLC_LUT_BITS);
-    ent = &lut->lut[k_param][next_bits];
-
-    if (ent->more) {
-        unsigned int leading_zeroes;
 
-        skip_bits(gbc, ent->consume);
-
-        next_bits = show_bits(gbc, 16);
-        leading_zeroes = 15 - av_log2(next_bits);
-
-        if (leading_zeroes == 0) {
-            // This can't happen mid-stream because the lookup would
-            // have resolved a leading one into a shorter code, but it
-            // can happen if we are hitting the end of the buffer.
-            // Return an invalid code to propagate as an error.
-            return APV_MAX_TRANS_COEFF + 1;
+    // Build the multi-symbol VLC table.
+    for (int start_run = 0; start_run <= 2; start_run++) {
+        for (int start_level = 0; start_level <= 4; start_level++) {
+            for (unsigned int code = 0; code < lut_size; code++) {
+                APVMultiVLCLUTEntry *ent;
+                int k_run, k_level;
+                GetBitContext gbc;
+                PutBitContext pbc;
+                uint8_t buffer[16];
+                uint8_t run_first_buffer[16];
+                uint8_t level_first_buffer[16];
+
+                memset(buffer, 0, sizeof(buffer));
+                init_put_bits(&pbc, buffer, sizeof(buffer));
+                put_bits(&pbc, APV_VLC_LUT_BITS, code);
+                flush_put_bits(&pbc);
+
+                memcpy(run_first_buffer,   buffer, sizeof(buffer));
+                memcpy(level_first_buffer, buffer, sizeof(buffer));
+
+                k_run   = start_run;
+                k_level = start_level;
+
+                ent = &decode_lut->run_first_lut[k_run][k_level][code];
+                memset(ent, 0, sizeof(*ent));
+                init_get_bits8(&gbc, run_first_buffer, sizeof(run_first_buffer));
+
+                ent->count = 0;
+                for (int i = 0; i <= 1; i++) {
+                    int value, sign, pos;
+
+                    value = apv_read_vlc(&gbc, k_run, decode_lut);
+                    pos = get_bits_count(&gbc);
+                    if (pos > APV_VLC_LUT_BITS)
+                        break;
+                    ent->run[i] = value;
+                    ent->offset[ent->count] = pos;
+                    ++ent->count;
+                    k_run = FFMIN(value >> 2, 2);
+
+                    value = apv_read_vlc(&gbc, k_level, decode_lut);
+                    sign = get_bits1(&gbc);
+                    pos = get_bits_count(&gbc);
+                    if (pos > APV_VLC_LUT_BITS)
+                        break;
+                    ++value;
+                    ent->level[i] = sign ? -value : value;
+                    ent->offset[ent->count] = pos;
+                    ++ent->count;
+                    k_level = FFMIN(value >> 2, 4);
+                    if (i == 0)
+                        ent->k_level_0 = k_level;
+                }
+                if (ent->count > 0 && ent->count < 4)
+                    ent->offset[3] = ent->offset[ent->count - 1];
+                ent->k_run     = k_run;
+                ent->k_level_1 = k_level;
+
+                k_run   = start_run;
+                k_level = start_level;
+
+                ent = &decode_lut->level_first_lut[k_run][k_level][code];
+                memset(ent, 0, sizeof(*ent));
+                init_get_bits8(&gbc, level_first_buffer, sizeof(level_first_buffer));
+
+                ent->count = 0;
+                for (int i = 0; i <= 1; i++) {
+                    int value, sign, pos;
+
+                    value = apv_read_vlc(&gbc, k_level, decode_lut);
+                    sign = get_bits1(&gbc);
+                    pos = get_bits_count(&gbc);
+                    if (pos > APV_VLC_LUT_BITS)
+                        break;
+                    ++value;
+                    ent->level[i] = sign ? -value : value;
+                    ent->offset[ent->count] = pos;
+                    ++ent->count;
+                    k_level = FFMIN(value >> 2, 4);
+                    if (i == 0)
+                        ent->k_level_0 = k_level;
+
+                    value = apv_read_vlc(&gbc, k_run, decode_lut);
+                    pos = get_bits_count(&gbc);
+                    if (pos > APV_VLC_LUT_BITS)
+                        break;
+                    ent->run[i] = value;
+                    ent->offset[ent->count] = pos;
+                    ++ent->count;
+                    k_run = FFMIN(value >> 2, 2);
+                }
+                if (ent->count > 0 && ent->count < 4)
+                    ent->offset[3] = ent->offset[ent->count - 1];
+                ent->k_run     = k_run;
+                ent->k_level_1 = k_level;
+            }
         }
-
-        skip_bits(gbc, leading_zeroes + 1);
-
-        return (2 << k_param) +
-            ((1 << leading_zeroes) - 1) * (1 << k_param) +
-            get_bits(gbc, leading_zeroes + k_param);
-    } else {
-        skip_bits(gbc, ent->consume);
-        return ent->result;
     }
 }
 
-unsigned int ff_apv_read_vlc(GetBitContext *gbc, int k_param,
-                             const APVVLCLUT *lut)
-{
-    return apv_read_vlc(gbc, k_param, lut);
-}
-
-int ff_apv_entropy_decode_block(int16_t *coeff,
-                                GetBitContext *gbc,
-                                APVEntropyState *state)
+int ff_apv_entropy_decode_block(int16_t *restrict coeff,
+                                GetBitContext *restrict gbc,
+                                APVEntropyState *restrict state)
 {
     const APVVLCLUT *lut = state->decode_lut;
-    int k_param;
-
-    // DC coefficient.
+    int scan_pos;
+    int k_dc = state->prev_k_dc;
+    int k_run, k_level;
+    uint32_t next_bits, lut_bits;
+    const APVMultiVLCLUTEntry *ent;
+
+    // DC coefficient is likely to be large and cannot be usefully
+    // combined with other read steps, so extract it separately.
     {
-        int abs_dc_coeff_diff;
-        int sign_dc_coeff_diff;
-        int dc_coeff;
-
-        k_param = av_clip(state->prev_dc_diff >> 1, 0, 5);
-        abs_dc_coeff_diff = apv_read_vlc(gbc, k_param, lut);
-
-        if (abs_dc_coeff_diff > 0)
-            sign_dc_coeff_diff = get_bits1(gbc);
-        else
-            sign_dc_coeff_diff = 0;
+        int dc_coeff, abs_diff, sign;
+
+        abs_diff = apv_read_vlc(gbc, k_dc, lut);
+
+        if (abs_diff) {
+            sign = get_bits1(gbc);
+            if (sign)
+                dc_coeff = state->prev_dc - abs_diff;
+            else
+                dc_coeff = state->prev_dc + abs_diff;
+        } else {
+            dc_coeff = state->prev_dc;
+        }
 
-        if (sign_dc_coeff_diff)
-            dc_coeff = state->prev_dc - abs_dc_coeff_diff;
-        else
-            dc_coeff = state->prev_dc + abs_dc_coeff_diff;
 
         if (dc_coeff < APV_MIN_TRANS_COEFF ||
             dc_coeff > APV_MAX_TRANS_COEFF) {
             av_log(state->log_ctx, AV_LOG_ERROR,
-                   "Out-of-range DC coefficient value: %d "
-                   "(from prev_dc %d abs_dc_coeff_diff %d sign_dc_coeff_diff %d)\n",
-                   dc_coeff, state->prev_dc, abs_dc_coeff_diff, sign_dc_coeff_diff);
+                   "Out-of-range DC coefficient value: %d.\n",
+                   dc_coeff);
             return AVERROR_INVALIDDATA;
         }
 
         coeff[0] = dc_coeff;
 
-        state->prev_dc      = dc_coeff;
-        state->prev_dc_diff = abs_dc_coeff_diff;
+        state->prev_dc   = dc_coeff;
+        state->prev_k_dc = FFMIN(abs_diff >> 1, 5);
     }
 
-    // AC coefficients.
-    {
-        int scan_pos   = 1;
-        int first_ac   = 1;
-        int prev_level = state->prev_1st_ac_level;
-        int prev_run   = 0;
+    // Repeatedly read 18 bits, look up the first half of them in either
+    // the run-first or the level-first table.  If the next code is too
+    // long the 18 bits will allow resolving a run code (up to 63)
+    // without reading any more bits, and will allow the exact length
+    // of a level code to be determined.  (Note that reusing the
+    // single-symbol LUT is never useful here as the multisymbol lookup
+    // has already determined that the code is too long.)
 
-        do {
-            int coeff_zero_run;
+    // Run a single iteration of the run-first LUT to start, then a
+    // single iteration of the level-first LUT if that only read a
+    // single code.  This avoids dealing with the first-AC logic inside
+    // the normal code lookup sequence.
 
-            k_param = av_clip(prev_run >> 2, 0, 2);
-            coeff_zero_run = apv_read_vlc(gbc, k_param, lut);
+    k_level = state->prev_k_level;
+    {
+        next_bits = show_bits(gbc, 18);
+        lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);
+
+        ent = &lut->run_first_lut[0][k_level][lut_bits];
+
+        if (ent->count == 0) {
+            // One long code.
+            uint32_t bits, low_bits;
+            unsigned int leading_zeroes, low_bit_count, low_bit_shift;
+            int run;
+
+            // Remove the prefix bits.
+            bits = next_bits & 0xffff;
+            // Determine code length.
+            leading_zeroes = 15 - av_log2(bits);
+            // Extract the low bits.
+            low_bit_count = leading_zeroes;
+            low_bit_shift = 16 - (1 + 2 * leading_zeroes);
+            low_bits = (bits >> low_bit_shift) & ((1 << low_bit_count) - 1);
+            // Construct run code.
+            run = 2 + ((1 << leading_zeroes) - 1) + low_bits;
+            // Skip over the bits just used.
+            skip_bits(gbc, 2 + leading_zeroes + 1 + low_bit_count);
+
+            scan_pos = run + 1;
+            if (scan_pos >= 64)
+                goto end_of_block;
+            k_run = FFMIN(run >> 2, 2);
+            goto first_level;
+        } else {
+            // One or more short codes starting with a run; if there is
+            // a level code then the length needs to be saved for the
+            // next block.
+
+            scan_pos = ent->run[0] + 1;
+            if (scan_pos >= 64) {
+                skip_bits(gbc, ent->offset[0]);
+                goto end_of_block;
+            }
+            if (ent->count > 1) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
+                ++scan_pos;
+                state->prev_k_level = ent->k_level_0;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[1]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 2) {
+                scan_pos += ent->run[1];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[2]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 3) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
+                ++scan_pos;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[3]);
+                    goto end_of_block;
+                }
+            }
+            skip_bits(gbc, ent->offset[3]);
+            k_run   = ent->k_run;
+            k_level = ent->k_level_1;
+            if (ent->count == 1)
+                goto first_level;
+            else if (ent->count & 1)
+                goto next_is_level;
+            else
+                goto next_is_run;
+        }
+    }
 
-            if (coeff_zero_run > APV_BLK_COEFFS - scan_pos) {
+    first_level: {
+        next_bits = show_bits(gbc, 18);
+        lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);
+
+        ent = &lut->level_first_lut[k_run][k_level][lut_bits];
+
+        if (ent->count == 0) {
+            // One long code.
+            uint32_t bits;
+            unsigned int leading_zeroes;
+            int level, abs_level, sign;
+
+            // Remove the prefix bits.
+            bits = next_bits & 0xffff;
+            // Determine code length.
+            leading_zeroes = 15 - av_log2(bits);
+            // Skip the prefix and length bits.
+            skip_bits(gbc, 2 + leading_zeroes + 1);
+            // Read the rest of the code and construct the level.
+            // Include the + 1 offset for nonzero value here.
+            abs_level = (2 << k_level) +
+                ((1 << leading_zeroes) - 1) * (1 << k_level) +
+                get_bits(gbc, leading_zeroes + k_level) + 1;
+
+            sign = get_bits(gbc, 1);
+            if (sign)
+                level = -abs_level;
+            else
+                level = abs_level;
+
+            // Check range (not checked in any other case, only a long
+            // code can be out of range).
+            if (level < APV_MIN_TRANS_COEFF ||
+                level > APV_MAX_TRANS_COEFF) {
                 av_log(state->log_ctx, AV_LOG_ERROR,
-                       "Out-of-range zero-run value: %d (at scan pos %d)\n",
-                       coeff_zero_run, scan_pos);
+                       "Out-of-range AC coefficient value at %d: %d.\n",
+                       scan_pos, level);
                 return AVERROR_INVALIDDATA;
             }
-
-            for (int i = 0; i < coeff_zero_run; i++) {
-                coeff[ff_zigzag_direct[scan_pos]] = 0;
+            coeff[ff_zigzag_direct[scan_pos]] = level;
+            ++scan_pos;
+            k_level = FFMIN(abs_level >> 2, 4);
+            state->prev_k_level = k_level;
+            if (scan_pos >= 64)
+                goto end_of_block;
+            goto next_is_run;
+
+        } else {
+            // One or more short codes.
+
+            coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
+            ++scan_pos;
+            state->prev_k_level = ent->k_level_0;
+            if (scan_pos >= 64) {
+                skip_bits(gbc, ent->offset[0]);
+                goto end_of_block;
+            }
+            if (ent->count > 1) {
+                scan_pos += ent->run[0];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[1]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 2) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
                 ++scan_pos;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[2]);
+                    goto end_of_block;
+                }
             }
-            prev_run = coeff_zero_run;
-
-            if (scan_pos < APV_BLK_COEFFS) {
-                int abs_ac_coeff_minus1;
-                int sign_ac_coeff;
-                int level;
-
-                k_param = av_clip(prev_level >> 2, 0, 4);
-                abs_ac_coeff_minus1 = apv_read_vlc(gbc, k_param, lut);
-                sign_ac_coeff = get_bits(gbc, 1);
-
-                if (sign_ac_coeff)
-                    level = -abs_ac_coeff_minus1 - 1;
-                else
-                    level = abs_ac_coeff_minus1 + 1;
-
-                if (level < APV_MIN_TRANS_COEFF ||
-                    level > APV_MAX_TRANS_COEFF) {
-                    av_log(state->log_ctx, AV_LOG_ERROR,
-                           "Out-of-range AC coefficient value: %d "
-                           "(from prev_level %d abs_ac_coeff_minus1 %d sign_ac_coeff %d)\n",
-                           level, prev_level, abs_ac_coeff_minus1, sign_ac_coeff);
+            if (ent->count > 3) {
+                scan_pos += ent->run[1];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[3]);
+                    goto end_of_block;
                 }
+            }
+            skip_bits(gbc, ent->offset[3]);
+            k_run   = ent->k_run;
+            k_level = ent->k_level_1;
+            if (ent->count & 1)
+                goto next_is_run;
+            else
+                goto next_is_level;
+        }
+    }
 
-                coeff[ff_zigzag_direct[scan_pos]] = level;
-
-                prev_level = abs_ac_coeff_minus1 + 1;
-                if (first_ac) {
-                    state->prev_1st_ac_level = prev_level;
-                    first_ac = 0;
+    next_is_run: {
+        next_bits = show_bits(gbc, 18);
+        lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);
+
+        ent = &lut->run_first_lut[k_run][k_level][lut_bits];
+
+        if (ent->count == 0) {
+            // One long code.
+            uint32_t bits, low_bits;
+            unsigned int leading_zeroes, low_bit_count, low_bit_shift;
+            int run;
+
+            // Remove the prefix bits.
+            bits = next_bits & 0xffff;
+            // Determine code length.
+            leading_zeroes = 15 - av_log2(bits);
+            // Extract the low bits.
+            low_bit_count = leading_zeroes + k_run;
+            low_bit_shift = 16 - (1 + 2 * leading_zeroes + k_run);
+            low_bits = (bits >> low_bit_shift) & ((1 << low_bit_count) - 1);
+            // Construct run code.
+            run = (2 << k_run) +
+                ((1 << leading_zeroes) - 1) * (1 << k_run) +
+                low_bits;
+            // Skip over the bits just used.
+            skip_bits(gbc, 2 + leading_zeroes + 1 + low_bit_count);
+
+            scan_pos += run;
+            if (scan_pos >= 64)
+                goto end_of_block;
+            k_run = FFMIN(run >> 2, 2);
+            goto next_is_level;
+
+        } else {
+            // One or more short codes.
+
+            scan_pos += ent->run[0];
+            if (scan_pos >= 64) {
+                skip_bits(gbc, ent->offset[0]);
+                goto end_of_block;
+            }
+            if (ent->count > 1) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
+                ++scan_pos;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[1]);
+                    goto end_of_block;
                 }
-
+            }
+            if (ent->count > 2) {
+                scan_pos += ent->run[1];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[2]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 3) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
                 ++scan_pos;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[3]);
+                    goto end_of_block;
+                }
             }
+            skip_bits(gbc, ent->offset[3]);
+            k_run   = ent->k_run;
+            k_level = ent->k_level_1;
+            if (ent->count & 1)
+                goto next_is_level;
+            else
+                goto next_is_run;
+        }
+    }
 
-        } while (scan_pos < APV_BLK_COEFFS);
+    next_is_level: {
+        next_bits = show_bits(gbc, 18);
+        lut_bits = next_bits >> (18 - APV_VLC_LUT_BITS);
+
+        ent = &lut->level_first_lut[k_run][k_level][lut_bits];
+
+        if (ent->count == 0) {
+            // One long code.
+            uint32_t bits;
+            unsigned int leading_zeroes;
+            int level, abs_level, sign;
+
+            // Remove the prefix bits.
+            bits = next_bits & 0xffff;
+            // Determine code length.
+            leading_zeroes = 15 - av_log2(bits);
+            // Skip the prefix and length bits.
+            skip_bits(gbc, 2 + leading_zeroes + 1);
+            // Read the rest of the code and construct the level.
+            // Include the + 1 offset for nonzero value here.
+            abs_level = (2 << k_level) +
+                ((1 << leading_zeroes) - 1) * (1 << k_level) +
+                get_bits(gbc, leading_zeroes + k_level) + 1;
+
+            sign = get_bits(gbc, 1);
+            if (sign)
+                level = -abs_level;
+            else
+                level = abs_level;
+
+            // Check range (not checked in any other case, only a long
+            // code can be out of range).
+            if (level < APV_MIN_TRANS_COEFF ||
+                level > APV_MAX_TRANS_COEFF) {
+                av_log(state->log_ctx, AV_LOG_ERROR,
+                       "Out-of-range AC coefficient value at %d: %d.\n",
+                       scan_pos, level);
+                return AVERROR_INVALIDDATA;
+            }
+            coeff[ff_zigzag_direct[scan_pos]] = level;
+            ++scan_pos;
+            k_level = FFMIN(abs_level >> 2, 4);
+            if (scan_pos >= 64)
+                goto end_of_block;
+            goto next_is_run;
+
+        } else {
+            // One or more short codes.
+
+            coeff[ff_zigzag_direct[scan_pos]] = ent->level[0];
+            ++scan_pos;
+            if (scan_pos >= 64) {
+                skip_bits(gbc, ent->offset[0]);
+                goto end_of_block;
+            }
+            if (ent->count > 1) {
+                scan_pos += ent->run[0];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[1]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 2) {
+                coeff[ff_zigzag_direct[scan_pos]] = ent->level[1];
+                ++scan_pos;
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[2]);
+                    goto end_of_block;
+                }
+            }
+            if (ent->count > 3) {
+                scan_pos += ent->run[1];
+                if (scan_pos >= 64) {
+                    skip_bits(gbc, ent->offset[3]);
+                    goto end_of_block;
+                }
+            }
+            skip_bits(gbc, ent->offset[3]);
+            k_run   = ent->k_run;
+            k_level = ent->k_level_1;
+            if (ent->count & 1)
+                goto next_is_run;
+            else
+                goto next_is_level;
+        }
     }
 
-    return 0;
+    end_of_block: {
+        if (scan_pos > 64) {
+            av_log(state->log_ctx, AV_LOG_ERROR,
+                   "Block decode reached invalid scan position %d.\n",
+                   scan_pos);
+            return AVERROR_INVALIDDATA;
+        }
+        return 0;
+    }
 }
-- 
2.47.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".

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [FFmpeg-devel] [PATCH v2 2/2] lavc: Add unit test for APV entropy decode
  2025-05-11 10:09 [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol entropy decode Mark Thompson
@ 2025-05-11 10:09 ` Mark Thompson
  2025-05-13 19:19 ` [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol " Mark Thompson
  1 sibling, 0 replies; 3+ messages in thread
From: Mark Thompson @ 2025-05-11 10:09 UTC (permalink / raw)
  To: ffmpeg-devel

---
 libavcodec/Makefile       |   1 +
 libavcodec/tests/apv.c    | 449 ++++++++++++++++++++++++++++++++++++++
 tests/fate/libavcodec.mak |   5 +
 3 files changed, 455 insertions(+)
 create mode 100644 libavcodec/tests/apv.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index cae8f3a9f1..77734dff24 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1329,6 +1329,7 @@ TESTPROGS = avcodec                                                     \
             jpeg2000dwt                                                 \
             mathops                                                    \
 
+TESTPROGS-$(CONFIG_APV_DECODER)           += apv
 TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER)     += av1_levels
 TESTPROGS-$(CONFIG_CABAC)                 += cabac
 TESTPROGS-$(CONFIG_GOLOMB)                += golomb
diff --git a/libavcodec/tests/apv.c b/libavcodec/tests/apv.c
new file mode 100644
index 0000000000..018674afe7
--- /dev/null
+++ b/libavcodec/tests/apv.c
@@ -0,0 +1,449 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/lfg.h"
+#include "libavutil/random_seed.h"
+
+#include "libavcodec/apv_decode.h"
+#include "libavcodec/apv_dsp.h"
+#include "libavcodec/put_bits.h"
+
+
+// Whole file included here to get internal symbols.
+#include "libavcodec/apv_entropy.c"
+
+
+// As defined in 7.1.4, for testing.
+// Adds a check to limit loop after reading 16 zero bits to avoid
+// getting stuck reading a stream of zeroes forever (this matches
+// the behaviour of the faster version).
+
+static unsigned int apv_read_vlc_spec(GetBitContext *gbc, int k_param)
+{
+    unsigned int symbol_value = 0;
+    int parse_exp_golomb = 1;
+    int k = k_param;
+    int stop_loop = 0;
+
+    if(get_bits1(gbc) == 1) {
+        parse_exp_golomb = 0;
+    } else {
+        if (get_bits1(gbc) == 0) {
+            symbol_value += (1 << k);
+            parse_exp_golomb = 0;
+        } else {
+            symbol_value += (2 << k);
+            parse_exp_golomb = 1;
+        }
+    }
+    if (parse_exp_golomb) {
+        int read_limit = 0;
+        do {
+            if (get_bits1(gbc) == 1) {
+                stop_loop = 1;
+            } else {
+                if (++read_limit == 16)
+                    break;
+                symbol_value += (1 << k);
+                k++;
+            }
+        } while (!stop_loop);
+    }
+    if (k > 0)
+        symbol_value += get_bits(gbc, k);
+
+    return symbol_value;
+}
+
+// As defined in 7.2.4, for testing.
+
+static void apv_write_vlc_spec(PutBitContext *pbc,
+                               unsigned int symbol_val, int k_param)
+{
+    int prefix_vlc_table[3][2] = {{1, 0}, {0, 0}, {0, 1}};
+
+    unsigned int symbol_value = symbol_val;
+    int val_prefix_vlc = av_clip(symbol_val >> k_param, 0, 2);
+    int bit_count = 0;
+    int k = k_param;
+
+    while (symbol_value >= (1 << k)) {
+        symbol_value -= (1 << k);
+        if (bit_count < 2)
+            put_bits(pbc, 1, prefix_vlc_table[val_prefix_vlc][bit_count]);
+        else
+            put_bits(pbc, 1, 0);
+        if (bit_count >= 2)
+            ++k;
+        ++bit_count;
+    }
+
+    if(bit_count < 2)
+        put_bits(pbc, 1, prefix_vlc_table[val_prefix_vlc][bit_count]);
+    else
+        put_bits(pbc, 1, 1);
+
+    if(k > 0)
+        put_bits(pbc, k, symbol_value);
+}
+
+// Old version of ff_apv_entropy_decode_block, for test comparison.
+
+static int apv_entropy_decode_block(int16_t *restrict coeff,
+                                    GetBitContext *restrict gbc,
+                                    APVEntropyState *restrict state)
+{
+    const APVVLCLUT *lut = state->decode_lut;
+
+    // DC coefficient.
+    {
+        int abs_dc_coeff_diff;
+        int sign_dc_coeff_diff;
+        int dc_coeff;
+
+        abs_dc_coeff_diff = apv_read_vlc(gbc, state->prev_k_dc, lut);
+
+        if (abs_dc_coeff_diff > 0)
+            sign_dc_coeff_diff = get_bits1(gbc);
+        else
+            sign_dc_coeff_diff = 0;
+
+        if (sign_dc_coeff_diff)
+            dc_coeff = state->prev_dc - abs_dc_coeff_diff;
+        else
+            dc_coeff = state->prev_dc + abs_dc_coeff_diff;
+
+        if (dc_coeff < APV_MIN_TRANS_COEFF ||
+            dc_coeff > APV_MAX_TRANS_COEFF) {
+            av_log(state->log_ctx, AV_LOG_ERROR,
+                   "Out-of-range DC coefficient value: %d "
+                   "(from prev_dc %d abs_dc_coeff_diff %d sign_dc_coeff_diff %d)\n",
+                   dc_coeff, state->prev_dc, abs_dc_coeff_diff, sign_dc_coeff_diff);
+            return AVERROR_INVALIDDATA;
+        }
+
+        coeff[0] = dc_coeff;
+
+        state->prev_dc   = dc_coeff;
+        state->prev_k_dc = FFMIN(abs_dc_coeff_diff >> 1, 5);
+    }
+
+    // AC coefficients.
+    {
+        int scan_pos = 1;
+        int first_ac = 1;
+        int k_run    = 0;
+        int k_level  = state->prev_k_level;
+
+        do {
+            int coeff_zero_run;
+
+            coeff_zero_run = apv_read_vlc(gbc, k_run, lut);
+
+            if (coeff_zero_run > APV_BLK_COEFFS - scan_pos) {
+                av_log(state->log_ctx, AV_LOG_ERROR,
+                       "Out-of-range zero-run value: %d (at scan pos %d)\n",
+                       coeff_zero_run, scan_pos);
+                return AVERROR_INVALIDDATA;
+            }
+
+            for (int i = 0; i < coeff_zero_run; i++) {
+                coeff[ff_zigzag_direct[scan_pos]] = 0;
+                ++scan_pos;
+            }
+            k_run = FFMIN(coeff_zero_run >> 2, 2);
+
+            if (scan_pos < APV_BLK_COEFFS) {
+                int abs_ac_coeff_minus1;
+                int sign_ac_coeff;
+                int abs_level, level;
+
+                abs_ac_coeff_minus1 = apv_read_vlc(gbc, k_level, lut);
+                sign_ac_coeff = get_bits(gbc, 1);
+
+                abs_level = abs_ac_coeff_minus1 + 1;
+                if (sign_ac_coeff)
+                    level = -abs_level;
+                else
+                    level = abs_level;
+
+                if (level < APV_MIN_TRANS_COEFF ||
+                    level > APV_MAX_TRANS_COEFF) {
+                    av_log(state->log_ctx, AV_LOG_ERROR,
+                           "Out-of-range AC coefficient value: %d "
+                           "(from k_param %d abs_ac_coeff_minus1 %d sign_ac_coeff %d)\n",
+                           level, k_level, abs_ac_coeff_minus1, sign_ac_coeff);
+                }
+
+                coeff[ff_zigzag_direct[scan_pos]] = level;
+
+                k_level = FFMIN(abs_level >> 2, 4);
+                if (first_ac) {
+                    state->prev_k_level = k_level;
+                    first_ac = 0;
+                }
+
+                ++scan_pos;
+            }
+
+        } while (scan_pos < APV_BLK_COEFFS);
+    }
+
+    return 0;
+}
+
+static void binary(char *buf, uint32_t value, int bits)
+{
+    for (int i = 0; i < bits; i++)
+        buf[i] = (value >> (bits - i - 1) & 1) ? '1' : '0';
+    buf[bits] = '\0';
+}
+
+static int test_apv_read_vlc(void)
+{
+    APVVLCLUT lut;
+    int err = 0;
+
+    ff_apv_entropy_build_decode_lut(&lut);
+
+    // Generate all possible 20 bit sequences (padded with zeroes), then
+    // verify that spec and improved parsing functions get the same result
+    // and consume the same number of bits for each possible k_param.
+
+    for (int k = 0; k <= 5; k++) {
+        for (uint32_t b = 0; b < (1 << 20); b++) {
+            uint8_t buf[8] = {
+                b >> 12,
+                b >> 4,
+                b << 4,
+                0, 0, 0, 0, 0
+            };
+
+            GetBitContext gbc_test, gbc_spec;
+            unsigned int  res_test, res_spec;
+            int           con_test, con_spec;
+
+            init_get_bits8(&gbc_test, buf, 8);
+            init_get_bits8(&gbc_spec, buf, 8);
+
+            res_test = apv_read_vlc     (&gbc_test, k, &lut);
+            res_spec = apv_read_vlc_spec(&gbc_spec, k);
+
+            con_test = get_bits_count(&gbc_test);
+            con_spec = get_bits_count(&gbc_spec);
+
+            if (res_test != res_spec ||
+                con_test != con_spec) {
+                char str[21];
+                binary(str, b, 20);
+                av_log(NULL, AV_LOG_ERROR,
+                       "Mismatch reading %s (%d) with k=%d:\n", str, b, k);
+                av_log(NULL, AV_LOG_ERROR,
+                       "Test function result %d consumed %d bits.\n",
+                       res_test, con_test);
+                av_log(NULL, AV_LOG_ERROR,
+                       "Spec function result %d consumed %d bits.\n",
+                       res_spec, con_spec);
+                ++err;
+                if (err > 10)
+                    return err;
+            }
+        }
+    }
+
+    return err;
+}
+
+static int random_coeff(AVLFG *lfg)
+{
+    // Geometric distribution of code lengths (1-14 bits),
+    // uniform distribution within codes of the length,
+    // equal probability of either sign.
+    int length = (av_lfg_get(lfg) / (UINT_MAX / 14 + 1));
+    int random = av_lfg_get(lfg);
+    int value = (1 << length) + (random & (1 << length) - 1);
+    if (random & (1 << length))
+        return value;
+    else
+        return -value;
+}
+
+static int random_run(AVLFG *lfg)
+{
+    // Expoenential distrbution of run lengths.
+    unsigned int random = av_lfg_get(lfg);
+    for (int len = 0;; len++) {
+        if (random & (1 << len))
+            return len;
+    }
+    // You rolled zero on a 2^32 sided die; well done!
+    return 64;
+}
+
+static int test_apv_entropy_decode_block(void)
+{
+    // Generate random entropy blocks, code them, then ensure they
+    // decode to the same block with both implementations.
+
+    APVVLCLUT decode_lut;
+    AVLFG lfg;
+    unsigned int seed = av_get_random_seed();
+    av_lfg_init(&lfg, seed);
+
+    av_log(NULL, AV_LOG_INFO, "seed = %u\n", seed);
+
+    ff_apv_entropy_build_decode_lut(&decode_lut);
+
+    for (int t = 0; t < 100; t++) {
+        APVEntropyState state, save_state;
+        int16_t block[64];
+        int16_t block_test1[64];
+        int16_t block_test2[64];
+        uint8_t buffer[1024];
+        PutBitContext pbc;
+        GetBitContext gbc;
+        int bits_written;
+        int pos, run, coeff, level, err;
+        int k_dc, k_run, k_level;
+
+        memset(block,  0, sizeof(block));
+        memset(buffer, 0, sizeof(buffer));
+        init_put_bits(&pbc, buffer, sizeof(buffer));
+
+        // Randomly-constructed state.
+        memset(&state, 0, sizeof(state));
+        state.decode_lut   = &decode_lut;
+        state.prev_dc      = random_coeff(&lfg);
+        state.prev_k_dc    = av_lfg_get(&lfg) % 5;
+        state.prev_k_level = av_lfg_get(&lfg) % 4;
+        save_state = state;
+
+        k_dc    = state.prev_k_dc;
+        k_run   = 0;
+        k_level = state.prev_k_level;
+
+        coeff = random_coeff(&lfg) / 2;
+        block[ff_zigzag_direct[0]] = state.prev_dc + coeff;
+        apv_write_vlc_spec(&pbc, FFABS(coeff), k_dc);
+        if (coeff != 0)
+            put_bits(&pbc, 1, coeff < 0);
+
+        pos = 1;
+        while (pos < 64) {
+            run = random_run(&lfg);
+            if (pos + run > 64)
+                run = 64 - pos;
+            apv_write_vlc_spec(&pbc, run, k_run);
+            k_run = av_clip(run >> 2, 0, 2);
+            pos += run;
+            if (pos < 64) {
+                coeff = random_coeff(&lfg);
+                level = FFABS(coeff) - 1;
+                block[ff_zigzag_direct[pos]] = coeff;
+                apv_write_vlc_spec(&pbc, level, k_level);
+                put_bits(&pbc, 1, coeff < 0);
+                k_level = av_clip((level + 1) >> 2, 0, 4);
+                ++pos;
+            }
+        }
+        bits_written = put_bits_count(&pbc);
+        flush_put_bits(&pbc);
+
+        // Fill output block with a distinctive error value.
+        for (int i = 0; i < 64; i++)
+            block_test1[i] = -9999;
+        init_get_bits8(&gbc, buffer, sizeof(buffer));
+
+        err = apv_entropy_decode_block(block_test1, &gbc, &state);
+        if (err < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Entropy decode returned error.\n");
+            return 1;
+        } else {
+            int bits_read = get_bits_count(&gbc);
+            if (bits_written != bits_read) {
+                av_log(NULL, AV_LOG_ERROR, "Wrote %d bits but read %d.\n",
+                       bits_written, bits_read);
+                return 1;
+            } else {
+                err = 0;
+                for (int i = 0; i < 64; i++) {
+                    if (block[i] != block_test1[i])
+                        ++err;
+                }
+                if (err > 0) {
+                    av_log(NULL, AV_LOG_ERROR, "%d mismatches in output block.\n", err);
+                    return err;
+                }
+            }
+        }
+
+        init_get_bits8(&gbc, buffer, sizeof(buffer));
+        memset(block_test2, 0, 64 * sizeof(int16_t));
+
+        err = ff_apv_entropy_decode_block(block_test2, &gbc, &save_state);
+        if (err < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Entropy decode returned error.\n");
+            return 1;
+        } else {
+            int bits_read = get_bits_count(&gbc);
+            if (bits_written != bits_read) {
+                av_log(NULL, AV_LOG_ERROR, "Wrote %d bits but read %d.\n",
+                       bits_written, bits_read);
+                return 1;
+            } else {
+                err = 0;
+                for (int i = 0; i < 64; i++) {
+                    if (block[i] != block_test2[i])
+                        ++err;
+                }
+                if (err > 0) {
+                    av_log(NULL, AV_LOG_ERROR, "%d mismatches in output block.\n", err);
+                    return err;
+                }
+            }
+        }
+
+        if (state.prev_dc      != save_state.prev_dc   ||
+            state.prev_k_dc    != save_state.prev_k_dc ||
+            state.prev_k_level != save_state.prev_k_level) {
+            av_log(NULL, AV_LOG_ERROR, "Entropy state mismatch.\n");
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+int main(void)
+{
+    int err;
+
+    err = test_apv_read_vlc();
+    if (err) {
+        av_log(NULL, AV_LOG_ERROR, "Read VLC test failed.\n");
+        return err;
+    }
+
+    err = test_apv_entropy_decode_block();
+    if (err) {
+        av_log(NULL, AV_LOG_ERROR, "Entropy decode block test failed.\n");
+        return err;
+    }
+
+    return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index ef6e6ec40e..b69ad53f7c 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -3,6 +3,11 @@ fate-av1-levels: libavcodec/tests/av1_levels$(EXESUF)
 fate-av1-levels: CMD = run libavcodec/tests/av1_levels$(EXESUF)
 fate-av1-levels: REF = /dev/null
 
+FATE_LIBAVCODEC-$(CONFIG_APV_DECODER) += fate-apv-entropy
+fate-apv-entropy: libavcodec/tests/apv$(EXESUF)
+fate-apv-entropy: CMD = run libavcodec/tests/apv$(EXESUF)
+fate-apv-entropy: REF = /dev/null
+
 FATE_LIBAVCODEC-yes += fate-avpacket
 fate-avpacket: libavcodec/tests/avpacket$(EXESUF)
 fate-avpacket: CMD = run libavcodec/tests/avpacket$(EXESUF)
-- 
2.47.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".

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol entropy decode
  2025-05-11 10:09 [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol entropy decode Mark Thompson
  2025-05-11 10:09 ` [FFmpeg-devel] [PATCH v2 2/2] lavc: Add unit test for APV " Mark Thompson
@ 2025-05-13 19:19 ` Mark Thompson
  1 sibling, 0 replies; 3+ messages in thread
From: Mark Thompson @ 2025-05-13 19:19 UTC (permalink / raw)
  To: ffmpeg-devel

On 11/05/2025 11:09, Mark Thompson wrote:
> ---
> v2:
> * Bring error handling to the same level as before.
> * Split first run/level step to avoid first-AC handling inside the loop.
> * More commentary.
> * Other minor improvements.
> 
> Thanks,

No further changes here, so applied.

This also seems to fix <https://trac.ffmpeg.org/ticket/11587> (output is now identical to the reference decoder), though I have not assessed exactly what was wrong with the previous version.

Thanks,

- Mark

_______________________________________________
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".

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-05-13 19:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-11 10:09 [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol entropy decode Mark Thompson
2025-05-11 10:09 ` [FFmpeg-devel] [PATCH v2 2/2] lavc: Add unit test for APV " Mark Thompson
2025-05-13 19:19 ` [FFmpeg-devel] [PATCH v2 1/2] apv_decode: Multisymbol " Mark Thompson

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