diff mbox series

[FFmpeg-devel,4/5] avcodec/rangecoder: Implement reading and writing upto 8 bits raw

Message ID 20241016132639.1958007-4-michael@niedermayer.cc
State New
Headers show
Series [FFmpeg-devel,1/5] avcodec/rangecoder: only perform renorm check/loop for callers that need it | expand

Commit Message

Michael Niedermayer Oct. 16, 2024, 1:26 p.m. UTC
This is much faster than doing 1 bit and updating a state

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
---
 libavcodec/rangecoder.h       | 53 +++++++++++++++++++++++++++++++++++
 libavcodec/tests/rangecoder.c | 13 +++++++++
 2 files changed, 66 insertions(+)
diff mbox series

Patch

diff --git a/libavcodec/rangecoder.h b/libavcodec/rangecoder.h
index da13c453edd..4cfdd2fdbe9 100644
--- a/libavcodec/rangecoder.h
+++ b/libavcodec/rangecoder.h
@@ -82,6 +82,28 @@  static inline void renorm_encoder(RangeCoder *c)
         c->range <<= 8;
 }
 
+static inline void renorm_encoder_raw(RangeCoder *c, int extra)
+{
+    if (c->outstanding_byte < 0) {
+        c->outstanding_byte = c->low >> 8 + extra;
+    } else if (c->low <= 0xFF00 << extra) {
+        *c->bytestream++ = c->outstanding_byte;
+        for (; c->outstanding_count; c->outstanding_count--)
+            *c->bytestream++ = 0xFF;
+        c->outstanding_byte = c->low >> 8 + extra;
+    } else if (c->low >= 0x10000 << extra) {
+        *c->bytestream++ = c->outstanding_byte + 1;
+        for (; c->outstanding_count; c->outstanding_count--)
+            *c->bytestream++ = 0x00;
+        c->outstanding_byte = (c->low >> 8 + extra) & 0xFF;
+    } else {
+        c->outstanding_count++;
+    }
+
+    c->low     = (c->low & ((1 << 8 + extra) - 1)) << 8 - extra;
+    c->range <<= 8 - extra;
+}
+
 static inline int get_rac_count(RangeCoder *c)
 {
     int x = c->bytestream - c->bytestream_start + c->outstanding_count;
@@ -110,6 +132,21 @@  static inline void put_rac(RangeCoder *c, uint8_t *const state, int bit)
         renorm_encoder(c);
 }
 
+static inline void put_rac_raw(RangeCoder *c, int bits, int len)
+{
+    int r = c->range >> len;
+    av_assert2(len <= 8);
+    av_assert2(bits >> len == 0);
+
+    if (r < 0x100) {
+        c->low = (c->low << len) + c->range * bits;
+        renorm_encoder_raw(c, len);
+    } else {
+        c->low += r * bits;
+        c->range = r;
+    }
+}
+
 static inline void refill(RangeCoder *c)
 {
         c->range <<= 8;
@@ -141,4 +178,20 @@  static inline int get_rac(RangeCoder *c, uint8_t *const state)
     }
 }
 
+static inline int get_rac_raw(RangeCoder *c, int len)
+{
+    int bits;
+    int r = c->range >> len;
+    av_assert2(len <= 8);
+
+    if (r < 0x100) {
+        refill(c);
+        r = c->range >> len;
+    }
+    bits = c->low / r;
+    c->low -= r * bits;
+    c->range = r;
+    return bits;
+}
+
 #endif /* AVCODEC_RANGECODER_H */
diff --git a/libavcodec/tests/rangecoder.c b/libavcodec/tests/rangecoder.c
index fd858535a5b..7634953585d 100644
--- a/libavcodec/tests/rangecoder.c
+++ b/libavcodec/tests/rangecoder.c
@@ -76,6 +76,11 @@  int main(void)
             for (i = 0; i < SIZE; i++)
                 put_rac(&c, state, r[i] & 1);
 
+            for (i = 0; i < SIZE; i++) {
+                int len = r[i++] % 7 + 1;
+                put_rac_raw(&c, r[i]&((1<<len) - 1), len);
+            }
+
             actual_length = ff_rac_terminate(&c, version);
 
             ff_init_range_decoder(&c, b, version ? SIZE : actual_length);
@@ -87,6 +92,14 @@  int main(void)
                     av_log(NULL, AV_LOG_ERROR, "rac failure at %d pass %d version %d\n", i, p, version);
                     return 1;
                 }
+            for (i = 0; i < SIZE; i++) {
+                int len = r[i++] % 7 + 1;
+                int mask = (1<<len) - 1;
+                if ((r[i] & mask) != get_rac_raw(&c, len)) {
+                    av_log(NULL, AV_LOG_ERROR, "rac raw failure at %d pass %d version %d\n", i, p, version);
+                    return 1;
+                }
+            }
 
             if (rac_check_termination(&c, version) < 0) {
                 av_log(NULL, AV_LOG_ERROR, "rac failure at termination pass %d version %d\n", p, version);