H264分辨率解码概述
目錄
- 1. 申明
- 2. 目的
- 3. 背景知識
- 3.1 哥倫比亞指數(shù)編碼無符號數(shù)的解碼
- 3.2 哥倫比亞指數(shù)編碼有符號數(shù)的解碼
- 3.3 解析參數(shù)序列集的過程
- 4. 代碼demo
- 5. 專欄知識鏈接
- 6. 寫在最后
1. 申明
本文章屬于原創(chuàng),其中參考的代碼及文章在結(jié)尾處標明,侵刪。
2. 目的
本文是為了解析H264中攜帶的視頻分辨率而寫的一個demo。
3. 背景知識
本文及文中所涉及代碼,均以TS流作為媒介。3.1 哥倫比亞指數(shù)編碼無符號數(shù)的解碼
(1)首先讀取當前位置的bit位,記錄連續(xù)0的個數(shù)N
(2)對于連續(xù)0的個數(shù)為0值為0,對于連續(xù)0的個數(shù)不為0 跳過第一個非0的bit位
(3)再讀取N個數(shù)據(jù),此數(shù)據(jù)以無符號形式解析num
(4)num=num-1+2的N次方
3.2 哥倫比亞指數(shù)編碼有符號數(shù)的解碼
(1)按照3.1的講述先獲取此段數(shù)據(jù)的哥倫比亞指數(shù)編碼的無符號形式
(2)如果數(shù)據(jù)為奇數(shù)num=(num+1)/2;如果是偶數(shù)num=-(num/2)
3.3 解析參數(shù)序列集的過程
描述符一列表示此處數(shù)據(jù)的類型f和u表示的是無符號的數(shù)據(jù)類型,ue表示的是哥倫比亞指數(shù)編碼的無符號數(shù)據(jù),se表示的是哥倫比亞指數(shù)編碼的有符號數(shù)據(jù)。
4. 代碼demo
#define H264_SPS_HEAD_FLAG 0x67/*當滿足0x00 0x00 剔除0x03@param1 buf: 數(shù)據(jù)指針@param2 nstart_bit: 計算因子 */ static inline void kick_out(unsigned char *buf, uint32_t *nstart_bit) {int pos = *nstart_bit / 8;if (buf[pos] == 0x03 && pos >= 2){if (buf[pos -1] == 0x00 && buf[pos - 2] == 0x00)*nstart_bit += 8;} }/*無符號指數(shù)哥倫布熵編碼@param1 buf: 數(shù)據(jù)指針@param2 len: 數(shù)據(jù)長度@param3 nstart_bit: 計算因子 */ static inline uint32_t ue(unsigned char *buf,uint32_t len, uint32_t *nstart_bit) {//計算0bit的個數(shù)kick_out(buf, nstart_bit);uint32_t n_zero_num = 0;while (*nstart_bit < len * 8){if (buf[*nstart_bit / 8] & (0x80 >> (*nstart_bit % 8))){break;}n_zero_num++;++(*nstart_bit);}++(*nstart_bit);//計算結(jié)果uint32_t ret = 0, i = 0;for (; i < n_zero_num; ++i){ret <<= 1;if (buf[*nstart_bit / 8] & (0x80 >> (*nstart_bit % 8))){ret += 1;}++(*nstart_bit);}return (1 << n_zero_num) - 1 + ret; }/*有符號指數(shù)哥倫布熵編碼@param1 buf: 數(shù)據(jù)指針@param2 len: 數(shù)據(jù)長度@param3 nstart_bit: 計算因子 */ static inline int se(unsigned char *buf, uint32_t len, uint32_t *nstart_bit) {int ue_val = ue(buf, len, nstart_bit);//1.調(diào)用庫函數(shù)取不小于本身的最小整數(shù),// double k = ue_val;// int n_value = ceil(k / 2);//ceil函數(shù):ceil函數(shù)的作用是求不小于給定實數(shù)的最小整數(shù)。ceil(2)=ceil(1.2)=cei(1.5)=2.00// if (ue_val % 2 == 0)// n_value = -n_value;//2.取余法int n_value;if (ue_val % 2 == 0)n_value = -(ue_val / 2);elsen_value = (ue_val / 2) + 1;return n_value; }/*根據(jù)bit_count進行解碼@param1 bit_count: 比特位數(shù)@param2 buf: 數(shù)據(jù)指針@param3 nstart_bit: 計算因子 */ static inline uint64_t u(uint32_t bit_count,unsigned char *buf, uint32_t *nstart_bit) {uint32_t ret = 0, i = 0;kick_out(buf, nstart_bit);for (; i < bit_count; ++i){ret <<= 1;if (buf[(*nstart_bit) / 8] & (0x80 >> ((*nstart_bit) % 8))){ret += 1;}++(*nstart_bit);}return ret; }/*H264的NAL起始碼防競爭機制@param1 buf: 臨時buf@param2 buf_size: 臨時buf大小 */ void de_emulation_prevention(unsigned char *buf, uint32_t *buf_size) {uint32_t i = 0, j = 0;unsigned char *tmp_ptr = NULL;uint32_t tmp_buf_size = 0;uint32_t val = 0;tmp_ptr = buf;tmp_buf_size = *buf_size;for (i = 0; i < (tmp_buf_size - 2); ++i){//check for 0x000003val = (tmp_ptr[i] ^ 0x00) + (tmp_ptr[i+1] ^ 0x00) + (tmp_ptr[i+2] ^ 0x03);if (val == 0){//kick out 0x03for (j = i + 2; j < tmp_buf_size - 1; j++)tmp_ptr[j] = tmp_ptr[j + 1];//and so we should devrease bufsize--(*buf_size);}} }/*根據(jù)誤差調(diào)整分辨率@param1 width: 原始寬數(shù)據(jù)的指針@param2 hight: 原始高數(shù)據(jù)的指針@param3 offset_width: 橫向誤差和@param4 offset_hight: 縱向誤差和 */ static inline BOOL adjust_resolution_by_offset(uint32_t *width, uint32_t *hight,uint8_t offset_width, uint8_t offset_hight) {//左右存在對齊,分辨率寬度減去二倍的像素if (offset_width) {if ((offset_width * 2) <= *width)*width -= offset_width * 2;elsereturn FALSE;}//上下存在對齊,分辨率高度減去二倍的像素if (offset_hight) {if ((offset_hight * 2) <= *hight)*hight -= offset_hight * 2;elsereturn FALSE;}return TRUE; }/*查找H265 SPS數(shù)據(jù)起始部分@param1 buf: 原始數(shù)據(jù)buf的二級指針@param2 len: 原始數(shù)據(jù)大小的指針@param3 type: 要查找的視頻類型起始字節(jié):H264: 0x67H265: 0x42 */ static inline BOOL find_sps_first_byte(unsigned char **buf, uint32_t *len, uint8_t type) {unsigned char *pos = (unsigned char *)memchr(*buf, type, *len);if (NULL == pos)return FALSE;*len -= pos - *buf;*buf = pos;return TRUE; }/*解碼H264@param1 pdata: 原始數(shù)據(jù)buf的指針@param2 len: 原始數(shù)據(jù)的大小 */ static inline BOOL decode_h264(unsigned char *pdata, uint32_t len) {uint32_t start_bit = 0;//查找SPS起始頭if (!find_sps_first_byte(&pdata, &len, H264_SPS_HEAD_FLAG))return FALSE;//H264的NAL起始碼防競爭機制,該函數(shù)會改變原始碼流,替換使用kick_out函數(shù)//de_emulation_prevention(pdata, &len);//解碼 forbidden_zero_bitu(1, pdata, &start_bit);//解碼 nal_ref_idcu(2, pdata, &start_bit);//解碼 nal_unit_typeint nal_unit_type = u(5, pdata, &start_bit);if (nal_unit_type == 7){//解碼 profile_idcint profile_idc = u(8, pdata, &start_bit);//解碼 constraint_set0_flagu(1, pdata, &start_bit);//(pdata[1] & 0x80)>>7;//解碼 constraint_set1_flagu(1, pdata, &start_bit);//(pdata[1] & 0x40)>>6;//解碼 constraint_set2_flagu(1, pdata, &start_bit);//(pdata[1] & 0x20)>>5;//解碼 constraint_set3_flagu(1, pdata, &start_bit);//(pdata[1] & 0x10)>>4;//解碼 reserved_zero_4bitsu(4, pdata, &start_bit);//解碼 level_idcu(8, pdata, &start_bit);//解碼 seq_parameter_set_idue(pdata, len, &start_bit);if (profile_idc == 100 || profile_idc == 110 ||profile_idc == 122 || profile_idc == 144 ){//解碼 chroma_format_idcint chroma_format_idc = ue(pdata, len, &start_bit);if ( chroma_format_idc == 3)u(1, pdata, &start_bit);//解碼 residual_colour_transform_flag//解碼 bit_depth_luma_minus8ue(pdata, len, &start_bit);//解碼 bit_depth_chroma_minus8ue(pdata, len, &start_bit);//解碼 qpprime_y_zero_transform_bypass_flagu(1, pdata, &start_bit);//解碼判斷 seq_scaling_matrix_present_flagif (u(1, pdata, &start_bit)){int i = 0;for (; i < 8; ++i)u(1, pdata, &start_bit);//解碼 seq_scaling_list_present_flag}}//解碼 log2_max_frame_num_minus4ue(pdata, len, &start_bit);//解碼 pic_order_cnt_typeint pic_order_cnt_type = ue(pdata, len, &start_bit);if (pic_order_cnt_type == 0) {//解碼 log2_max_pic_order_cnt_lsb_minus4ue(pdata, len, &start_bit);}else if (pic_order_cnt_type == 1){//解碼 delta_pic_order_always_zero_flagu(1, pdata, &start_bit);//解碼 offset_for_non_ref_picse(pdata, len, &start_bit);//解碼 offset_for_top_to_bottom_fieldse(pdata, len, &start_bit);int i = 0;//解碼作為循環(huán)控制條件 num_ref_frames_in_pic_order_cnt_cyclefor (; i < ue(pdata, len, &start_bit); ++i)se(pdata, len, &start_bit);// 解碼 offset_for_ref_frame}//解碼 num_ref_framesue(pdata, len, &start_bit);//解碼 gaps_in_frame_num_value_allowed_flagu(1, pdata, &start_bit);//解碼分辨率寬 pic_width_in_mbs_minus1uint32_t width = ue(pdata, len, &start_bit);//解碼分辨率高 pic_height_in_map_units_minus1uint32_t height = ue(pdata, len, &start_bit);width =(width + 1) * 16;height =(height + 1) * 16;//需要判斷場分片還是幀分片uint64_t frame_mbs_only_flag = u(1, pdata, &start_bit);height *= (2 - frame_mbs_only_flag);//解碼作判斷 frame_mbs_only_flagif(!frame_mbs_only_flag) u(1, pdata, &start_bit);//解碼 mb_adaptive_frame_field_flag//解碼 direct_8x8_inference_flagu(1,pdata,&start_bit); //解碼做判斷 frame_cropping_flagif(u(1, pdata, &start_bit)){//為真則出現(xiàn)了對齊現(xiàn)象,需要在寬或高上減掉對齊的像素//解碼 frame_crop_left_offsetuint32_t frame_crop_left_offset = ue(pdata, len, &start_bit);//解碼 frame_crop_right_offsetuint32_t frame_crop_right_offset = ue(pdata, len, &start_bit);//解碼 frame_crop_top_offsetuint32_t frame_crop_top_offset = ue(pdata, len, &start_bit);//解碼 frame_crop_bottom_offsetuint32_t frame_crop_bottom_offset = ue(pdata, len, &start_bit);//調(diào)整分辨率if (!adjust_resolution_by_offset(&width, &height,frame_crop_left_offset + frame_crop_right_offset,frame_crop_top_offset + frame_crop_bottom_offset)) {;//不夠減}}}return TRUE; }劃重點: 為了解析分辨率,需要一層層進行解碼,所以上面代碼中,只調(diào)用u(),ue()而未獲取返回值的,可以暫時不用關(guān)注,有興趣可以自己了解。需要強調(diào)的是,最后對于補齊的計算,如果不進行補齊的核對,會出現(xiàn)640*368這種分辨率,而實際應(yīng)用中我們的寬高比為16:9, 所以分辨率應(yīng)該為640:360, 那么這多出來的8,其實就是在傳輸過程中,必須以宏塊(16的倍數(shù))來傳輸,為了滿足這個要求,高度向上進行了補齊,所以在計算真實值的時候,要根據(jù)frame_crop_left_offset,frame_crop_right_offset,frame_crop_top_offset,frame_crop_bottom_offset進行相應(yīng)的調(diào)整。
5. 專欄知識鏈接
1. 協(xié)議知識概述
2. H265分辨率解碼概述
3. 以太網(wǎng)Ethernet解碼概述
6. 寫在最后
本文引用了以下文章作者的代碼或思路, 并結(jié)合了實際項目中的代碼整理出的demo,如有問題歡迎指正。https://www.jianshu.com/p/b26963f2f926
https://blog.csdn.net/wishfly/article/details/75127050
https://blog.csdn.net/jifukui/article/details/90698872
官方文檔
https://www.doc88.com/p-3601708718418.html
總結(jié)
以上是生活随笔為你收集整理的H264分辨率解码概述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【赫夫曼树详解】赫夫曼树简介及java代
- 下一篇: 以太网Ethernet解码概述