From 499474a76dcbea441dbb57730aa57c42fd9c9817 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
Date: Wed, 14 Feb 2024 15:49:43 +0200
Subject: [PATCH] Revert "rtpvp8pay: Use GstBitReader instead of dboolhuff
 implementation from libvpx"

This reverts commit b730e7a1b28ce4bcea90fbff7c5293fa2c0a76b2.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3300

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6116>
---
 .../gst/rtp/dboolhuff.LICENSE                 |  29 ++++
 .../gst-plugins-good/gst/rtp/dboolhuff.c      |  74 +++++++++
 .../gst-plugins-good/gst/rtp/dboolhuff.h      | 155 ++++++++++++++++++
 .../gst-plugins-good/gst/rtp/gstrtpvp8pay.c   |  76 +++------
 .../gst-plugins-good/gst/rtp/meson.build      |   1 +
 5 files changed, 284 insertions(+), 51 deletions(-)
 create mode 100644 subprojects/gst-plugins-good/gst/rtp/dboolhuff.LICENSE
 create mode 100644 subprojects/gst-plugins-good/gst/rtp/dboolhuff.c
 create mode 100644 subprojects/gst-plugins-good/gst/rtp/dboolhuff.h

diff --git a/subprojects/gst-plugins-good/gst/rtp/dboolhuff.LICENSE b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.LICENSE
new file mode 100644
index 0000000000..83e4e6f6d7
--- /dev/null
+++ b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2010, Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+  * Neither the name of Google nor the names of its contributors may
+    be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/subprojects/gst-plugins-good/gst/rtp/dboolhuff.c b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.c
new file mode 100644
index 0000000000..118bd0b978
--- /dev/null
+++ b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.c
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the dboolhuff.LICENSE file in this directory.
+ *  See the libvpx original distribution for more information,
+ *  including patent information, and author information.
+ */
+
+
+#include "dboolhuff.h"
+
+#ifdef _MSC_VER
+__declspec (align (16))
+     const unsigned char vp8_norm[256] = {
+#else
+const unsigned char vp8_norm[256] __attribute__((aligned (16))) = {
+#endif
+  0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int
+vp8dx_start_decode (BOOL_DECODER * br,
+    const unsigned char *source, unsigned int source_sz)
+{
+  br->user_buffer_end = source + source_sz;
+  br->user_buffer = source;
+  br->value = 0;
+  br->count = -8;
+  br->range = 255;
+
+  if (source_sz && !source)
+    return 1;
+
+  /* Populate the buffer */
+  vp8dx_bool_decoder_fill (br);
+
+  return 0;
+}
+
+
+void
+vp8dx_bool_decoder_fill (BOOL_DECODER * br)
+{
+  const unsigned char *bufptr;
+  const unsigned char *bufend;
+  VP8_BD_VALUE value;
+  int count;
+  bufend = br->user_buffer_end;
+  bufptr = br->user_buffer;
+  value = br->value;
+  count = br->count;
+
+  VP8DX_BOOL_DECODER_FILL (count, value, bufptr, bufend);
+
+  br->user_buffer = bufptr;
+  br->value = value;
+  br->count = count;
+}
diff --git a/subprojects/gst-plugins-good/gst/rtp/dboolhuff.h b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.h
new file mode 100644
index 0000000000..e0a45a2236
--- /dev/null
+++ b/subprojects/gst-plugins-good/gst/rtp/dboolhuff.h
@@ -0,0 +1,155 @@
+/*
+ *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the dboolhuff.LICENSE file in this directory.
+ *  See the libvpx original distribution for more information,
+ *  including patent information, and author information.
+ */
+
+
+#ifndef DBOOLHUFF_H
+#define DBOOLHUFF_H
+#include <stddef.h>
+#include <limits.h>
+#include <glib.h>
+
+typedef size_t VP8_BD_VALUE;
+
+# define VP8_BD_VALUE_SIZE ((int)sizeof(VP8_BD_VALUE)*CHAR_BIT)
+/*This is meant to be a large, positive constant that can still be efficiently
+   loaded as an immediate (on platforms like ARM, for example).
+  Even relatively modest values like 100 would work fine.*/
+# define VP8_LOTS_OF_BITS (0x40000000)
+
+typedef struct
+{
+    const unsigned char *user_buffer_end;
+    const unsigned char *user_buffer;
+    VP8_BD_VALUE         value;
+    int                  count;
+    unsigned int         range;
+} BOOL_DECODER;
+
+#ifdef _MSC_VER
+__declspec(align(16)) extern const unsigned char vp8_norm[256];
+#else
+extern const unsigned char vp8_norm[256] __attribute__((aligned(16)));
+#endif
+
+int vp8dx_start_decode(BOOL_DECODER *br,
+                       const unsigned char *source,
+                       unsigned int source_sz);
+
+void vp8dx_bool_decoder_fill(BOOL_DECODER *br);
+
+/*The refill loop is used in several places, so define it in a macro to make
+   sure they're all consistent.
+  An inline function would be cleaner, but has a significant penalty, because
+   multiple BOOL_DECODER fields must be modified, and the compiler is not smart
+   enough to eliminate the stores to those fields and the subsequent reloads
+   from them when inlining the function.*/
+#define VP8DX_BOOL_DECODER_FILL(_count,_value,_bufptr,_bufend) \
+    do \
+    { \
+        int shift = VP8_BD_VALUE_SIZE - 8 - ((_count) + 8); \
+        int loop_end, x; \
+        size_t bits_left = ((_bufend)-(_bufptr))*CHAR_BIT; \
+        \
+        x = shift + CHAR_BIT - bits_left; \
+        loop_end = 0; \
+        if(x >= 0) \
+        { \
+            (_count) += VP8_LOTS_OF_BITS; \
+            loop_end = x; \
+            if(!bits_left) break; \
+        } \
+        while(shift >= loop_end) \
+        { \
+            (_count) += CHAR_BIT; \
+            (_value) |= (VP8_BD_VALUE)*(_bufptr)++ << shift; \
+            shift -= CHAR_BIT; \
+        } \
+    } \
+    while(0) \
+
+
+static int vp8dx_decode_bool(BOOL_DECODER *br, int probability) {
+    unsigned int bit = 0;
+    VP8_BD_VALUE value;
+    unsigned int split;
+    VP8_BD_VALUE bigsplit;
+    int count;
+    unsigned int range;
+
+    split = 1 + (((br->range - 1) * probability) >> 8);
+
+    if(br->count < 0)
+        vp8dx_bool_decoder_fill(br);
+
+    value = br->value;
+    count = br->count;
+
+    bigsplit = (VP8_BD_VALUE)split << (VP8_BD_VALUE_SIZE - 8);
+
+    range = split;
+
+    if (value >= bigsplit)
+    {
+        range = br->range - split;
+        value = value - bigsplit;
+        bit = 1;
+    }
+
+    {
+        register unsigned int shift = vp8_norm[range];
+        range <<= shift;
+        value <<= shift;
+        count -= shift;
+    }
+    br->value = value;
+    br->count = count;
+    br->range = range;
+
+    return bit;
+}
+
+static G_GNUC_UNUSED int vp8_decode_value(BOOL_DECODER *br, int bits)
+{
+    int z = 0;
+    int bit;
+
+    for (bit = bits - 1; bit >= 0; bit--)
+    {
+        z |= (vp8dx_decode_bool(br, 0x80) << bit);
+    }
+
+    return z;
+}
+
+static G_GNUC_UNUSED int vp8dx_bool_error(BOOL_DECODER *br)
+{
+    /* Check if we have reached the end of the buffer.
+     *
+     * Variable 'count' stores the number of bits in the 'value' buffer, minus
+     * 8. The top byte is part of the algorithm, and the remainder is buffered
+     * to be shifted into it. So if count == 8, the top 16 bits of 'value' are
+     * occupied, 8 for the algorithm and 8 in the buffer.
+     *
+     * When reading a byte from the user's buffer, count is filled with 8 and
+     * one byte is filled into the value buffer. When we reach the end of the
+     * data, count is additionally filled with VP8_LOTS_OF_BITS. So when
+     * count == VP8_LOTS_OF_BITS - 1, the user's data has been exhausted.
+     */
+    if ((br->count > VP8_BD_VALUE_SIZE) && (br->count < VP8_LOTS_OF_BITS))
+    {
+       /* We have tried to decode bits after the end of
+        * stream was encountered.
+        */
+        return 1;
+    }
+
+    /* No error. */
+    return 0;
+}
+#endif
diff --git a/subprojects/gst-plugins-good/gst/rtp/gstrtpvp8pay.c b/subprojects/gst-plugins-good/gst/rtp/gstrtpvp8pay.c
index 8664b1d548..af055a148a 100644
--- a/subprojects/gst-plugins-good/gst/rtp/gstrtpvp8pay.c
+++ b/subprojects/gst-plugins-good/gst/rtp/gstrtpvp8pay.c
@@ -32,6 +32,7 @@
 #include <gst/rtp/gstrtpbuffer.h>
 #include <gst/video/video.h>
 #include "gstrtpelements.h"
+#include "dboolhuff.h"
 #include "gstrtpvp8pay.h"
 #include "gstrtputils.h"
 
@@ -282,6 +283,7 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer,
   guint8 tmp8 = 0;
   guint8 partitions;
   guint offset;
+  BOOL_DECODER bc;
   guint8 *pdata;
 
   if (G_UNLIKELY (buffer_size < 3))
@@ -330,57 +332,35 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer,
   }
 
   offset = keyframe ? 10 : 3;
-
-  /* Everything below is actually using some form of arithmetic coding, but as
-   * all the bits we're interested in are encoded with 50:50 probability
-   * they're written out as they are and we can just continue using the bit
-   * reader here */
+  vp8dx_start_decode (&bc, data + offset, size - offset);
 
   if (keyframe) {
     /* color space (1 bit) and clamping type (1 bit) */
-    if (!gst_bit_reader_skip (&reader, 2))
-      goto error;
+    vp8dx_decode_bool (&bc, 0x80);
+    vp8dx_decode_bool (&bc, 0x80);
   }
 
   /* segmentation_enabled */
-  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-    goto error;
-
-  if (tmp8) {
-    guint8 update_mb_segmentation_map;
-    guint8 update_segment_feature_data;
-
-    if (!gst_bit_reader_get_bits_uint8 (&reader, &update_mb_segmentation_map,
-            1))
-      goto error;
-
-    if (!gst_bit_reader_get_bits_uint8 (&reader, &update_segment_feature_data,
-            1))
-      goto error;
+  if (vp8dx_decode_bool (&bc, 0x80)) {
+    guint8 update_mb_segmentation_map = vp8dx_decode_bool (&bc, 0x80);
+    guint8 update_segment_feature_data = vp8dx_decode_bool (&bc, 0x80);
 
     if (update_segment_feature_data) {
       /* skip segment feature mode */
-      if (!gst_bit_reader_skip (&reader, 1))
-        goto error;
+      vp8dx_decode_bool (&bc, 0x80);
 
       /* quantizer update */
       for (i = 0; i < 4; i++) {
         /* skip flagged quantizer value (7 bits) and sign (1 bit) */
-        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-          goto error;
-        if (tmp8)
-          if (!gst_bit_reader_skip (&reader, 8))
-            goto error;
+        if (vp8dx_decode_bool (&bc, 0x80))
+          vp8_decode_value (&bc, 8);
       }
 
       /* loop filter update */
       for (i = 0; i < 4; i++) {
         /* skip flagged lf update value (6 bits) and sign (1 bit) */
-        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-          goto error;
-        if (tmp8)
-          if (!gst_bit_reader_skip (&reader, 7))
-            goto error;
+        if (vp8dx_decode_bool (&bc, 0x80))
+          vp8_decode_value (&bc, 7);
       }
     }
 
@@ -388,44 +368,38 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer,
       /* segment prob update */
       for (i = 0; i < 3; i++) {
         /* skip flagged segment prob */
-        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-          goto error;
-        if (!gst_bit_reader_skip (&reader, 8))
-          goto error;
+        if (vp8dx_decode_bool (&bc, 0x80))
+          vp8_decode_value (&bc, 8);
       }
     }
   }
 
   /* skip filter type (1 bit), loop filter level (6 bits) and
    * sharpness level (3 bits) */
-  if (!gst_bit_reader_skip (&reader, 1 + 6 + 3))
-    goto error;
+  vp8_decode_value (&bc, 1);
+  vp8_decode_value (&bc, 6);
+  vp8_decode_value (&bc, 3);
 
   /* loop_filter_adj_enabled */
-  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-    goto error;
-  if (tmp8) {
+  if (vp8dx_decode_bool (&bc, 0x80)) {
 
     /* delta update */
-    if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-      goto error;
-    if (tmp8) {
+    if (vp8dx_decode_bool (&bc, 0x80)) {
 
       for (i = 0; i < 8; i++) {
         /* 8 updates, 1 bit indicate whether there is one and if follow by a
          * 7 bit update */
-        if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 1))
-          goto error;
-        if (tmp8)
-          if (!gst_bit_reader_skip (&reader, 7))
-            goto error;
+        if (vp8dx_decode_bool (&bc, 0x80))
+          vp8_decode_value (&bc, 7);
       }
     }
   }
 
-  if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 2))
+  if (vp8dx_bool_error (&bc))
     goto error;
 
+  tmp8 = vp8_decode_value (&bc, 2);
+
   partitions = 1 << tmp8;
 
   /* Check if things are still sensible */
diff --git a/subprojects/gst-plugins-good/gst/rtp/meson.build b/subprojects/gst-plugins-good/gst/rtp/meson.build
index ff62067675..000b91d7ef 100644
--- a/subprojects/gst-plugins-good/gst/rtp/meson.build
+++ b/subprojects/gst-plugins-good/gst/rtp/meson.build
@@ -1,4 +1,5 @@
 rtp_sources = [
+  'dboolhuff.c',
   'fnv1hash.c',
   'gstbuffermemory.c',
   'gstrtpelement.c',