本章将讲述的是如何使用代码来实现无符号指数哥伦布编码和有符号指数哥伦布编码,然后再进一步实现手动填充sps和pps。
由于较于网络上比较多的讲述了如何从解码哥伦布编码的数据,而比较缺少如何进行编码的说明,本文将讲解如何使用代码来实现指数哥伦布熵编码的实现。
当然最后也补充解码的代码以供参考。
对于h264的sps和pps中主要有如下四种指数哥伦布熵编码:
(1)无符号指数哥伦布熵编码 ue(v)
(2)有符号指数哥伦布熵编码 se(v)
(3)映射指数哥伦布熵编码 me(v)
(4)截断指数哥伦布熵编码 te(v)
由于sps和pps仅用到前面两种熵编码,所以本文将讲述前面两种熵编码。
C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
(1)编码规则
a)先将需要编码的数字加1操作,例如需要进行编码的是a1 = 6,此时需要进行加1操作后得到数值a2 = 7。
b)此时将a2转换为二进制数为"0111",然后获得a2的最小有效位数为n = 3,即有效值为"111"。
d)最后需要在"111"前面补重(n-1)= 2个"0",即可得到"00111"作为a1的无符号指数哥伦布熵编码了。
(2)解码规则
解码规则刚好与上面所述的编码规则相反。还是以最终解码对为6的无符号指数哥伦布熵编码进行解码为例进行说明。
a)需要对a1 = 00111(二进制)进行解码,首先获取到前面有n=2个'0',此时说明数据的长度为len=n+1=3,即a2="111"(二进制)=7
b)此时将a2进行减一操作得到:a3=a2-1=6,即最终得到解码后的值为a3=6。
(1)由于对于流媒体的数据处理特别是对H.264数据进行处理时,很多时候都是需要对Bit(位)进行处理的,而我们日常使用的C++很多时候都是用的比较方便的是字节,此时如果还是使用字节为单位来处理流媒体数据时,往往是很吃力的,因此我们需要建立一种位操作的思维来对流媒体进行处理。
(2)定义一个写数据的位流操作的类代码实现:
#ifndef WRITELIB_BITSTREAM_HPP#define WRITELIB_BITSTREAM_HPP class WriteBitStream { public: WriteBitStream(unsigned char* buf, int size); ~WriteBitStream(); int WriteU1(int data);//写入一位数据 int WriteU(int n, int data);//写入n位数据 //计算出2进制最小位数 int CountBitNum(int data); int WriteUE(int data); int WriteSE(int data); private: unsigned char * start = nullptr; int size = 0; unsigned char * p = nullptr; int bits_left;};#endif
(3)写入一位数据的操作
C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
/*函数名称:WriteU1*函数功能:往p中继续写入一个位的数据*传入参数:int data:输入需要写入的数值, 理论是写入data的最低位的值进去*函数返回值:成功返回0,失败返回-1*函数作者:吴豪乐*日期:2021年1月26日*/int WriteBitStream::WriteU1(int data){ int w = 0; bits_left--; //printf("~(data << bits_left)):%x
", (~(data << bits_left))); *p = ( (data << bits_left) | (*p) ); if (bits_left == 0) { p++; bits_left = 8; } return 0;}
(4)写入n位数据的操作
/*函数名称:WriteU*函数功能:往p中继续写入多少位数据*传入参数:intn :需要写入的位数, int data:输入需要写入的数值,理论是写入data的最低的n位的值进去*函数返回值:成功返回0,失败返回-1*函数作者:吴豪乐*日期:2021年1月26日*/int WriteBitStream:: WriteU(int n, int data){ int r = 0; int i; int data_tmp = data; for (i = 0; i < n; i++) { WriteU1((data_tmp >> (n - i - 1)) & 0x01); //printf("(data_tmp >> (n - i - 1)) & 0x01:%d, i:%d
", (data_tmp >> (n - i - 1)) & 0x01, i); } return 0; }
(5)计算传入数据最小位数
/*函数名称:CountBitNum*函数功能:计算出2进制最小位数*传入参数:int data:输入需要计算位数的数值*函数返回值:成功返回0,失败返回-1*函数作者:吴豪乐*日期:2021年1月26日*/int WriteBitStream::CountBitNum(int data){ int i = 0; while (1) { if ( ((data >> (32 - i - 1)) & 0x01) > 0 ) { break; } else { i++; } } printf("there has %d bit
", 32 - i); return (32-i);}
(6)无符号指数哥伦布编码代码实现
/*函数名称:WriteUE*函数功能:无符号的哥伦布编码*传入参数:int data:输入需要进行编码的数值*函数返回值:成功返回0,失败返回-1*函数作者:吴豪乐*日期:2021年1月26日*/int WriteBitStream::WriteUE(int data){ int len = CountBitNum(data + 1); WriteU(len-1, 0); WriteU(len, (data+1)); return 0;}
(7)有符号指数哥伦布编码代码实现
/*函数名称:WriteSE*函数功能:有符号的哥伦布编码*传入参数:int data:输入需要进行编码的数值,可为正数或者负数*函数返回值:成功返回0,失败返回-1*函数作者:吴豪乐*日期:2021年1月26日*/int WriteBitStream::WriteSE(int data){ int flag = 0; int data_tmp = 0; if (data < 0) { flag = 0x01; data_tmp = -data; } else { flag = 0x00; data_tmp = data; } //printf("data:%d
", data); data_tmp = ((data_tmp) << 1) | flag; data_tmp -= 1;//因为WriteSE不需要进行加一操作 WriteUE(data_tmp); //WriteU(1, flag); return 0;}
C++音视频开发学习资料:点击领取→音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
(1)位操作的查询数据类
#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
(2)读取一位数据
int BitStream::ReadU1(){ int r = 0; bits_left--; r = ((*(p)) >> bits_left) & 0x01; if (bits_left == 0) { p++; bits_left = 8; } //printf("bits_left:%d
", bits_left); return r;}
(3)读取n位数据
int BitStream::ReadU(int n){ int r = 0; int i; for (i = 0; i < n; i++) { r |= (ReadU1() << (n - i - 1)); } return r;}
(4)无符号哥伦布解码
int BitStream::ReadUE(){ int r = 0; int i = 0; while ((ReadU1() == 0) && (i < 32)) { i++; } //printf("i:%d
", i); r = ReadU(i); r += (1 << i) -1; return r;}
(5)有符号哥伦布解码
int BitStream::ReadSE(){ int r = ReadUE(); //printf("ReadUE:%02x
", r);#if 0 if (r & 0x01) { r = (r + 1) / 2; } else { r = -(r / 2); }#endif #if 1 if (r & 0x01) { r = -(r/2); } else { r = (r - 1) / 2; }#endif return r;}
到此已经展示完毕了指数哥伦布熵编码和解码的代码实现过程了
留言与评论(共有 0 条评论) “” |