日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

H264分辨率解码概述

發布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 H264分辨率解码概述 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

    • 1. 申明
    • 2. 目的
    • 3. 背景知識
      • 3.1 哥倫比亞指數編碼無符號數的解碼
      • 3.2 哥倫比亞指數編碼有符號數的解碼
      • 3.3 解析參數序列集的過程
    • 4. 代碼demo
    • 5. 專欄知識鏈接
    • 6. 寫在最后

1. 申明

本文章屬于原創,其中參考的代碼及文章在結尾處標明,侵刪。

2. 目的

本文是為了解析H264中攜帶的視頻分辨率而寫的一個demo。

3. 背景知識

本文及文中所涉及代碼,均以TS流作為媒介。

3.1 哥倫比亞指數編碼無符號數的解碼

(1)首先讀取當前位置的bit位,記錄連續0的個數N

(2)對于連續0的個數為0值為0,對于連續0的個數不為0 跳過第一個非0的bit位

(3)再讀取N個數據,此數據以無符號形式解析num

(4)num=num-1+2的N次方

3.2 哥倫比亞指數編碼有符號數的解碼

(1)按照3.1的講述先獲取此段數據的哥倫比亞指數編碼的無符號形式

(2)如果數據為奇數num=(num+1)/2;如果是偶數num=-(num/2)

3.3 解析參數序列集的過程




描述符一列表示此處數據的類型f和u表示的是無符號的數據類型,ue表示的是哥倫比亞指數編碼的無符號數據,se表示的是哥倫比亞指數編碼的有符號數據。

4. 代碼demo

#define H264_SPS_HEAD_FLAG 0x67/*當滿足0x00 0x00 剔除0x03@param1 buf: 數據指針@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;} }/*無符號指數哥倫布熵編碼@param1 buf: 數據指針@param2 len: 數據長度@param3 nstart_bit: 計算因子 */ static inline uint32_t ue(unsigned char *buf,uint32_t len, uint32_t *nstart_bit) {//計算0bit的個數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);//計算結果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; }/*有符號指數哥倫布熵編碼@param1 buf: 數據指針@param2 len: 數據長度@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.調用庫函數取不小于本身的最小整數,// double k = ue_val;// int n_value = ceil(k / 2);//ceil函數:ceil函數的作用是求不小于給定實數的最小整數。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; }/*根據bit_count進行解碼@param1 bit_count: 比特位數@param2 buf: 數據指針@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);}} }/*根據誤差調整分辨率@param1 width: 原始寬數據的指針@param2 hight: 原始高數據的指針@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數據起始部分@param1 buf: 原始數據buf的二級指針@param2 len: 原始數據大小的指針@param3 type: 要查找的視頻類型起始字節: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: 原始數據buf的指針@param2 len: 原始數據的大小 */ 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起始碼防競爭機制,該函數會改變原始碼流,替換使用kick_out函數//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;//解碼作為循環控制條件 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)){//為真則出現了對齊現象,需要在寬或高上減掉對齊的像素//解碼 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);//調整分辨率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; }

劃重點: 為了解析分辨率,需要一層層進行解碼,所以上面代碼中,只調用u(),ue()而未獲取返回值的,可以暫時不用關注,有興趣可以自己了解。需要強調的是,最后對于補齊的計算,如果不進行補齊的核對,會出現640*368這種分辨率,而實際應用中我們的寬高比為16:9, 所以分辨率應該為640:360, 那么這多出來的8,其實就是在傳輸過程中,必須以宏塊(16的倍數)來傳輸,為了滿足這個要求,高度向上進行了補齊,所以在計算真實值的時候,要根據frame_crop_left_offset,frame_crop_right_offset,frame_crop_top_offset,frame_crop_bottom_offset進行相應的調整。

5. 專欄知識鏈接

1. 協議知識概述
2. H265分辨率解碼概述
3. 以太網Ethernet解碼概述

6. 寫在最后

本文引用了以下文章作者的代碼或思路, 并結合了實際項目中的代碼整理出的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

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的H264分辨率解码概述的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。