The introduction

For more articles in this series, visit the Do-it-yourself H.264 decoder

In the previous section, we elaborated the theoretical knowledge of the four kinds of exponential Columbus entropy coding ue(V), signed exponential Columbus entropy coding SE (V), mapped exponential Columbus entropy coding Me (V) and truncated exponential Columbus entropy coding Te (V). In this section, we will look at how to use code to decode exponential Columbian encoded data.

This section mainly introduces the decoding of unsigned index Columbus entropy coding and signed index Columbus entropy coding. For mapping index Columbus entropy coding and truncation index Columbus entropy coding, because some pre-knowledge has not been covered, this section will not be implemented for the moment, and it will be implemented later when encountered.

BitStream

As we mentioned earlier, operations in H.264 streams are in bits, so this level of granularity cannot be achieved with ordinary Pointers.

We now need a stream object that operates in bits, which we will call a BitStream.

Interface design

The structure of the class

*/ BitStream(unsigned char * buf, int size); BitStream(unsigned char * buf, int size); ~BitStream(); Private: // Pointer to the buffer position unsigned char * start = nullptr; // The length of the buffer (Byte) int size = 0; Unsigned char * p = nullptr; Int bits_left = 8; };Copy the code

An illustration of each property

Function design

  • Interface to read 1 bit from Bitstream

    Before we write to read the exponential Columbus-encoded data from the data stream, we need to wrap a basic function: read 1 bit of data first.

    int ReadU1();
    Copy the code

    We define a member function of ReadU1. This function reads a bit from bitstram, converts the bit to an int, and moves the pointer back one bit. If bits_left is reduced to 0, then unsigned char * p is moved back one byte. A bit can only be a 0 or a 1, but we’ll use an int for convenience. If you think it’s a waste of space, you can fix it yourself.

    This function is implemented as follows:

    int ReadU1()
    {
        int r = 0;
        bits_left--;
        r = ((*(p)) >> bits_left) & 0x01;
        if (bits_left == 0) {
            p++;
            bits_left = 8;
        }
        return r;
    }
    Copy the code
  • Interface for reading n bits from Bitstream

    Next, we write a function that is an extension of ReadU1 that reads n bits of data from Bitstream.

    int ReadU(int n)
    {
        int r = 0;
        int i;
        for (i = 0; i < n; i++) {
            r |= ( ReadU1() << ( n - i - 1 ) );
        }
        return r;
    }
    Copy the code
  • Read an unsigned exponential Columbus entropy encoded data from Bitstream

    This probably doesn’t need too much explanation. We have covered this in the previous summary.

    int ReadUE()
    {
        int r = 0;
        int i = 0;
        while((ReadU1() == 0) && (i < 32)){
            i++;
        }
        r = ReadU(i);
        r += (1 << i) - 1;
        return r;
    }
    Copy the code
  • Read a signed exponential Columbus entropy encoded data from Bitstream

    When we read the signed exponential Columbus entropy code, we actually read it unsigned, and then read it and then parse the sign.

    int ReadSE()
    {
        int r = ReadUE();
        if (r & 0x01) {
            r = (r+1)/2;
        }
        else {
            r = -(r/2);
        }
        return r;
    }
    Copy the code

The complete code

BitStream.hpp

#ifndef EYERLIB_BITSTREAM_HPP
#define EYERLIB_BITSTREAM_HPP

class BitStream {
public:
    BitStream(unsigned char * buf, int size);
    ~BitStream();

    int ReadU1();
    int ReadU(int n);

    int ReadUE();
    int ReadSE();

private:
    unsigned char * start = nullptr;
    int size = 0;
    unsigned char * p = nullptr;
    int bits_left;
};


#endif //EYERLIB_BITSTREAM_HPP
Copy the code

BitStream.cpp

#include "BitStream.hpp"


BitStream::BitStream(unsigned char * _buf, int _size)
{
    start = _buf;
    p = _buf;
    size = _size;
    bits_left = 8;
}

BitStream::~BitStream()
{

}

int BitStream::ReadU1()
{
    int r = 0;
    bits_left--;
    r = ((*(p)) >> bits_left) & 0x01;
    if (bits_left == 0) {
        p++;
        bits_left = 8;
    }
    return r;
}

int BitStream::ReadU(int n)
{
    int r = 0;
    int i;
    for (i = 0; i < n; i++) {
        r |= ( ReadU1() << ( n - i - 1 ) );
    }
    return r;
}

int BitStream::ReadUE()
{
    int r = 0;
    int i = 0;
    while((ReadU1() == 0) && (i < 32)){
        i++;
    }
    r = ReadU(i);
    r += (1 << i) - 1;
    return r;
}

int BitStream::ReadSE()
{
    int r = ReadUE();
    if (r & 0x01) {
        r = (r+1)/2;
    }
    else {
        r = -(r/2);
    }
    return r;
}
Copy the code