自己動手寫 H.264 解碼器---指數(shù)哥倫布熵編碼代碼實(shí)現(xiàn)

引言

更多系列文章請?jiān)L問自己動手寫 H.264 解碼器

上一小節(jié)左刽,我們闡述了無符號指數(shù)哥倫布熵編碼 ue(v)呜象,有符號指數(shù)哥倫布熵編碼 se(v)怜奖,映射指數(shù)哥倫布熵編碼 me(v)偎蘸,截?cái)嘀笖?shù)哥倫布熵編碼 te(v)四種指數(shù)哥倫布熵編碼的理論知識帚称。本小節(jié)嘹叫,就來看看怎么用代碼來解碼指數(shù)哥倫布編碼的數(shù)據(jù)嫩絮。

本小節(jié)主要介紹無符號指數(shù)哥倫布熵編碼和有符號指數(shù)哥倫布熵編碼的解碼丛肢,對于映射指數(shù)哥倫布熵編碼和截?cái)嘀笖?shù)哥倫布熵編碼,因?yàn)樯婕暗揭恍┣爸弥R還沒有講到剿干,本節(jié)先不做實(shí)現(xiàn)蜂怎,后續(xù)遇到的時(shí)候會實(shí)現(xiàn)。

BitStream

我們之前就說過怨愤,在 H.264 碼流中的操作單位是 bit派敷,所以,用普通的指針是無法達(dá)到這樣的操作顆粒度撰洗。

我們現(xiàn)在需要一個(gè)以 bit 為操作單位的流對象篮愉,我們把他叫做 BitStream。

1566f2152fff2b58ac3d34d9822e46ea.png

接口設(shè)計(jì)

類的結(jié)構(gòu)

class BitStream {
public:
    /**
      * 構(gòu)造函數(shù)可以傳入一個(gè) buffer差导,這里可以直接把 nalu 的 buffer 傳入
      */
    BitStream(unsigned char * buf, int size);
    ~BitStream();

private:
    // 指向 buffer 開始的位置
    unsigned char * start = nullptr;

    // buffer 的長度(單位 Byte)
    int size = 0;

    // 當(dāng)前讀取到了哪個(gè)字節(jié)
    unsigned char * p = nullptr;

    // 當(dāng)前讀取到了字節(jié)中的第幾位
    int bits_left = 8;
};

各個(gè)屬性的說明圖

2786f5f8c9a2cca4643ba61005a821f9.png

函數(shù)的設(shè)計(jì)

  • 從 Bitstream 中讀取 1 bit 的接口

    在編寫從數(shù)據(jù)流里讀取指數(shù)哥倫布編碼的數(shù)據(jù)之前试躏,我們先來完成一個(gè)最基本的函數(shù)的封裝:先讀取 1 bit 的數(shù)據(jù)。

    int ReadU1();
    

    我們先定義一個(gè) ReadU1 的成員函數(shù)设褐,這個(gè)函數(shù)的作用是從 bitstram 讀取一個(gè) bit 的數(shù)據(jù)颠蕴,并把這一個(gè) bit 的數(shù)據(jù)轉(zhuǎn)換成一個(gè) int 類型泣刹,然后把數(shù)據(jù)指針向后移動一個(gè) bit(通過 bits_left 減 1 實(shí)現(xiàn),如果 bits_left 被減到了 0犀被,那么 unsigned char * p 將向后移動一個(gè)字節(jié))椅您。雖然 1 bit 的數(shù)據(jù)只會有 0 或者 1 兩種情況,但是我們?yōu)榱朔奖氵€是直接用一個(gè) int 來裝寡键。如果你覺得有些浪費(fèi)空間掀泳,可以自己修改一下。

    這個(gè)函數(shù)的實(shí)現(xiàn)如下:

    int ReadU1()
    {
        int r = 0;
        bits_left--;
        r = ((*(p)) >> bits_left) & 0x01;
        if (bits_left == 0) {
            p++;
            bits_left = 8;
        }
        return r;
    }
    
  • 從 Bitstream 中讀取 n bit 的接口

    接下來西轩,我們再寫一個(gè)函數(shù)员舵,這個(gè)函數(shù)是 ReadU1 的擴(kuò)增,我們可以一下從 Bitstream 中讀取 n bit 的數(shù)據(jù)藕畔。

    int ReadU(int n)
    {
        int r = 0;
        int i;
        for (i = 0; i < n; i++) {
            r |= ( ReadU1() << ( n - i - 1 ) );
        }
        return r;
    }
    
  • 從 Bitstream 中讀取一個(gè)無符號指數(shù)哥倫布熵編碼的數(shù)據(jù)

    這個(gè)應(yīng)該是不需要做過多的解釋了马僻。我們在前一小結(jié)已經(jīng)專門講解了。

    int ReadUE()
    {
        int r = 0;
        int i = 0;
        while((ReadU1() == 0) && (i < 32)){
            i++;
        }
        r = ReadU(i);
        r += (1 << i) - 1;
        return r;
    }
    
  • 從 Bitstream 中讀取一個(gè)有符號指數(shù)哥倫布熵編碼的數(shù)據(jù)

    我們在讀取有符號的指數(shù)哥倫布熵編碼的時(shí)候注服,實(shí)際上是先按照無符號的方式去讀韭邓,然后讀出來之后再解析符號。

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

完整代碼

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

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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末溶弟,一起剝皮案震驚了整個(gè)濱河市仍秤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌可很,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凰浮,死亡現(xiàn)場離奇詭異我抠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)袜茧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門菜拓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笛厦,你說我怎么就攤上這事纳鼎。” “怎么了裳凸?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵贱鄙,是天一觀的道長。 經(jīng)常有香客問我姨谷,道長逗宁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任梦湘,我火速辦了婚禮瞎颗,結(jié)果婚禮上件甥,老公的妹妹穿的比我還像新娘。我一直安慰自己哼拔,他們只是感情好引有,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著倦逐,像睡著了一般譬正。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上僻孝,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天导帝,我揣著相機(jī)與錄音,去河邊找鬼穿铆。 笑死您单,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荞雏。 我是一名探鬼主播虐秦,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凤优!你這毒婦竟也來了悦陋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤筑辨,失蹤者是張志新(化名)和其女友劉穎俺驶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棍辕,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暮现,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了楚昭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栖袋。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抚太,靈堂內(nèi)的尸體忽然破棺而出塘幅,到底是詐尸還是另有隱情,我是刑警寧澤尿贫,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布电媳,位于F島的核電站,受9級特大地震影響帅霜,放射性物質(zhì)發(fā)生泄漏匆背。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一身冀、第九天 我趴在偏房一處隱蔽的房頂上張望钝尸。 院中可真熱鬧括享,春花似錦、人聲如沸珍促。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猪叙。三九已至娇斩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間穴翩,已是汗流浹背犬第。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芒帕,地道東北人歉嗓。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像背蟆,于是被迫代替她去往敵國和親鉴分。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容