H.264筆記

H.264標準寫得比較繁復,所以考慮在瀏覽完Whitepaper之后就開始研讀X264代碼。X264代碼風格還是比較清晰簡潔的。

根據對標準得理解,Picture Order Count在Slice解碼的一開始就被提及:

I0 B1 B2 P3 B4 B5 P6
I0 P3 B1 B2 P6 B4 B5

于是I0的POC是0,P3的POC是3,B1是1……

為了支持H264復雜的幀存機制,X264以專門的一個模塊frame.c進行處理。

common/frame.c中包括一組幀緩沖操作函數。包括對幀進行FILO和FIFO存取,空閑幀隊列的相應操作等。

以下逐個函數分析encoder.c中編碼一幀的函數x264_encoder_encode中有關frame的調用:

x264_reference_update
這個函數里最主要的工作的是將上一個參考幀放入參考幀隊列,并從空閑幀隊列中取出一幀作為當前的參考工作幀(即解碼操作的目的幀),即h->fdec。

x264_t結構體維護著CODEC的諸多重要信息,其中成員frames是一個指示和控制幀編碼過程的結構。其中current是已經準備就緒可以編碼的幀,其類型已經確定;next是尚未確定類型的幀;unused用于回收不使用的frame結構體以備今后再次使用。frames結構體中i_input指示當前輸入的幀的(播放順序)序號。i_delay設置為由B幀個數(線程個數)確定的幀緩沖延遲,在多線程情況下為i_delay = i_bframe + i_threads - 1。而判斷B幀緩沖填充是否足夠則通過條件判斷:h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads。
x264_encoder_encode每次會以參數送入一幀待編碼的幀pic_in,函數首先會從空閑隊列中取出一幀用于承載該新幀,而它的i_frame被設定為播放順序計數,如:fenc->i_frame = h->frames.i_input++。
x264_encoder_encode在根據上述判據確定B幀緩沖充滿的情況下才進行后續編碼工作。


當當前隊列(current隊列)可用幀為0時,需要對next隊列中的幀進行判決,需要進行如下過程:
1. 調用x264_slicetype_decide
這個函數確定當前條帶(幀)的類型
其中首先調用x264_ratecontrol_slice_type,依據碼率控制逐個求出next列表中所有幀的類型(雖然在當前并不全部用到,見后)。
隨后統計審查并調整next列表,保證IDR幀滿足有關最大關鍵幀間隔的要求的正常出現:即針對frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max作判斷。審查按順序針對所有被判定為B系或AUTO類型的幀進行(這些幀在審核過程中被確認為B幀),直到遇到第一個不是這樣的幀。
如果某個幀被指定為IDR,則一個GOP在它之前結束。

2. 而后,即將next列表中已經判定的一系列幀(先后是一些B幀和一個非B幀)轉移到current列表中。在這個過程中:
原始序列(播放順序)B0, B1, B2, P,轉移后的順序為P, B0, B1, B2。在使用bframe_pyramid模式時,中間的B幀要前置,即上述順序變為:P, B1, B0, B2。

此時,就可以從current隊列中取出一幀,進行編碼,現在記這幀叫h->fenc。
首先做幾項和幀有關的設置工作:
1. 如果f_enc是IDR,則將最近IDR序號標記h->frames.i_last_idr設置為i_frame。
2. 根據f_enc的類型確定NAL和SLICE類型相關參數。
3. 設置POC為2 * (h->fenc->i_frame - h->frames.i_last_idr)。并使得h->fdec和h->fenc的主要幀參數一致。

隨后進行以下一些過程:
x264_reference_build_list
在這個函數中,我們將遇到參考幀列表h->frames.reference和H.264很有特色的雙列表(h->fref0、h->fref1)。前者中放置了所有可用于參考的參考幀。
首先將所有reference列表中的幀按照POC和h->fenc的POC的大小關系不同復制到雙列表中,其中h->fref0放置POC較小的那些。然后對雙列表進行排序,使得h->fref0中的幀POC按從大到小排列,h->fref1按從小到大排列,于是這兩個列表中靠前的幀的POC比較接近當前幀h->fenc。在這里,一個特殊的情況是,對于P幀(只需要用到列表h->fref0做參考),其排序依據是i_frame_num而不是POC,因此需要對這種情況的列表h->fref0置位需重排序標記。最后對雙列表的長度做限制。

x264_ratecontrol_start
碼率控制初始化,在以后專門的話題中論述。
在初始化后,從碼率控制體中獲得全局QP值:i_global_qp = x264_ratecontrol_qp( h )。

如果當前是B條帶(幀),則調用x264_macroblock_bipred_init
該函數設置一些參考幀關系矩陣,關系矩陣以fref0的索引為行坐標,以fref1的索引為列坐標。主要包括:dist_scale_factor,bipred_weight,矩陣值主要由相應幀的POC決定。后續討論。

x264_slice_init
內部調用x264_slice_header_init,它對Header進行配置,含義以后討論。

bs_init
初始化碼流工作上下文。

隨后將幀類型表現到碼流中。接著寫SEI,SPS和PPS。

然后就是最關鍵的寫條帶(即主要的編碼工作)開始的地方:
x264_slices_write
這里通過x264_stack_align調用x264_slice_write,為了將棧做4字節對齊,以提高運行效率。

x264_slice_write函數是編碼一幀的關鍵函數,將在下一章中論述。

宏塊結構

這里主要有一下幾個過程:
1. 初始化h->stat.frame,即全部清零。
2. 寫條帶頭:x264_slice_header_write,即把剛才x264_slice_header_init設置的一些參數寫入。
3. 如果是CABAC編碼,則初始化CABAC。有關CABAC在后續相關章節討論。
4. 遍歷一幀中的所有宏塊,這是編碼的主要部分:
for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; )
其中sh.i_first_mb和sh.i_last_mb在x264_slice_header_init中賦值,分別是0和宏塊行列數乘積。
5. 最后輸出碼流尾部。
其間穿插一些必要的初始化和配置。

本章著重分析編碼的主要部分,包括一下過程:
需要注意的是,這里討論的宏塊是固定尺寸的,即Y分量計為16x16。(而運動宏塊的尺寸則是多變的)

1. 首先根據宏塊的序號得出宏塊的坐標。然后統計當前宏塊的碼流位置:
int mb_spos = bs_pos(&h->out.bs) + x264_cabac_pos(&h->cabac);

2. 然后出現一句:
if( i_mb_x == 0 )
??? x264_fdec_filter_row( h, i_mb_y );
這是對行進行去塊過程。詳細情況專門論述。

3. 隨后是x264_macroblock_cache_load,這里主要進行當前宏塊和相關信息的提取。這個函數比較大,也在專題中論述。

4. 接下去進行解碼分析操作
x264_macroblock_analyse

5. 以及解碼實體:
x264_macroblock_encode

6. 而后處理碼流輸出,也分CABAC和CAVLC兩種情況。

7. 接著出現x264_macroblock_cache_save,和x264_macroblock_cache_load對應,保存處理完的宏塊。

8. 更新宏塊統計


下面,首先看編碼主體x264_macroblock_encode的結構。
該函數按不同宏塊類型情況進行不同的處理。包括如下類型:
P_SKIP
B_SKIP
I_16x16 - 一次完成一個塊h->mb.pic.p_fdec[0]的編碼。
?? 用函數指針h->predict_16x16[i_mode]進行幀內預測
I_8x8?? - 分4次完成。h->mb.pic.p_fdec[0][8 * (i&1) + 8 * (i>>1) * FDEC_STRIDE]指定了當前位置。即:
?? [0][1]
?? [2][3]
?? 用函數指針完成4個宏塊的幀內預測
I_4x4 - 分16次完成。h->mb.pic.p_fdec[0][4 * block_idx_x[i] + 4 * block_idx_y[i] * FDEC_STRIDE]指定當前位置。
?? 根據block_idx_x和block_idx_y的定義,其順序是:
?? [0][1][4][5]
?? [2][3][6][7]
?? [8][9][c][d]
?? [a][b][e][f]
?? 用函數指針完成4個宏塊的幀內預測
Inter - 首先進行運動補償。見后續討論。
????????? 然后分多種情形:
?? 1. 無損壓縮情況:h->mb.b_lossless == TRUE(必須采用4x4DCT)
????? 根據上述I_4x4中描述的順序遍歷16個4x4塊。
????? 這里調用的函數是zigzag_sub_4x4_field,完成殘差。(無損情況直接發送殘差)
?? 2. 指定采用8x8DCT情況:h->mb.b_transform_8x8 == TRUE
????? 首先是作殘差并DCT的函數:sub16x16_dct8。
????? 然后對4個8x8的DCT系數塊進行處理:包括可能的去噪x264_denoise_dct,量化(標量/矢量)。有關量化也在專題中論述。量化完成后進行系數掃描(對角平衡的普通掃描)。
?? 3. 一般情況
?????
接著處理色差分量,如果是Intra的則進行預測。
最后寫Coded Block Pattern。

幀內預測

環路內濾波

H.264環路內濾波顧名思義在編碼側開啟后解碼部分必須跟隨開啟,因此是該視頻編碼方案的不可分割的組成部分。

??? 以下整理了Baseline情形下環路濾波的四種情形:

??? All cases that may exist for Baseline
??? Bs = 4: either is intra, MB edge
??? Bs = 3: either is intra, block edge
??? Bs = 2: both are inter, either is coded
??? Bs = 1: both are inter neither is coded, different ref pictures, either MV component is no smaller than 4
??? Bs = 0: o.w.

?

??? 以T264為例:
??? 環路濾波入口函數以光柵掃描順序對所有宏塊實施deblock_mb。

??? deblock_mb完成一個宏塊的濾波,它包含以下步驟:

??? 1. 水平相鄰宏塊濾波,即對與mb_xy-1宏塊的邊界濾波,以及內部3條縱向塊邊界濾波;

??? 2. 垂直相鄰宏塊濾波,即對與mb_xy-mb_stride宏塊的邊界濾波,以及內部3條橫向塊邊界濾波;

?

??? 每條邊界長度均為16個像素,等分成4份,每個對應一個最小塊大小的邊界,以此為單元進行濾波。
??? 對每條邊界調用get_strength,獲得這4個部分的Bs值。

本章討論的代碼主要位于common/predict.c中。
x264_macroblock_cache_load函數在每個宏塊解碼之前初始化某些狀態,在x264_slice_write函數的宏塊處理循環中被調用。
i_mb_xy:?? 當前宏塊的索引
i_mb_4x4:? 當前宏塊中第一個4x4塊的索引
i_mb_8x8:? 當前宏塊中第一個8x8塊的索引
i_top_y:?? 上方宏塊的y索引
i_top_xy:? 上方宏塊的索引
i_top_4x4: 當前宏塊中第一個4x4塊上方的4x4塊的索引
i_top_8x8: 當前宏塊中第一個8x8塊上方的8x8塊的索引

這里首先初始化和當前宏塊毗鄰的已解碼塊。
這里用到x264_scan8,它指示如下掃描結構:

?? 0 1 2 3 4 5 6 7
?0
?1?? 0 1?? 0 1 4 5
?2?? 2 3?? 2 3 6 7
?3???????? 8 9 C D
?4?? 0 1?? A B E F
?5?? 2 3

?在空缺部分恰好可以填入相應宏塊的毗鄰塊。

以下逐個各種分析預測情況:

對于16x16的塊:
predict_16x16
共7種預測模式:H, V, DC, P, LEFT, TOP, 128
predict_16x16_h
用左側相鄰16x16塊的右側像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_16x16_p
右上左下均值預測覆蓋(注意均值生成)
predict_16x16_dc
用左側邊沿和上方邊沿的均值作為DC預測值進行單一覆蓋
predict_16x16_dc_left
只用左側邊沿進行DC單一覆蓋
predict_16x16_dc_top
只用上方邊沿進行DC單一覆蓋
predict_16x16_dc_128
用128進行單一覆蓋

對于8x8的色差塊:
predict_8x8c_dc_128
用128進行單一覆蓋
predict_8x8c_dc_left
上下兩個4行分別以對應左邊沿4個像素均值單一覆蓋
predict_8x8c_dc_top
左右兩個4列分別以對應上邊沿4個像素均值單一覆蓋
predict_8x8c_dc
?? s0 s1
s2 b0 b1
s3 b2 b3
s?是邊沿4像素組
4個4x4塊分別用s0,s2均值,s1均值,s3均值和s1,s3均值單一覆蓋
predict_8x8c_h
用左側相鄰8x8塊的右邊沿像素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行像素沿豎直方向覆蓋
predict_8x8c_p
右上左下均值預測覆蓋(注意均值生成)

對于4x4的塊:(這是H264文檔經常拿來作demo的)
predict_4x4_dc_128
用128進行單一覆蓋
predict_4x4_dc_left
用左邊沿4個像素均值單一覆蓋
predict_4x4_dc_top
用上邊沿4個像素均值單一覆蓋
predict_4x4_dc
用左側和上方邊沿共8個像素均值單一覆蓋
predict_4x4_h
用左側相鄰4x4塊的右邊沿像素沿水平方向覆蓋
predict_4x4_v
用上方相鄰4x4塊的底行像素沿豎直方向覆蓋
predict_4x4_ddl
右上至左下預測覆蓋(注意均值生成)
predict_4x4_ddr
左上至右下預測覆蓋(注意均值生成)
predict_4x4_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_4x4_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hu
左下到右上(偏右)預測覆蓋(注意均值生成)

對于8x8亮度塊:
edge中存放
predict_8x8_dc_128
用128進行單一覆蓋
predict_8x8_dc_left
用左側塊邊沿8個像素均值單一覆蓋
predict_8x8_dc_top
用上方塊邊沿8個像素均值單一覆蓋
predict_8x8_dc
用左側塊和上方塊邊沿共16個像素均值單一覆蓋
predict_8x8_h
用左側邊沿預測覆蓋
predict_8x8_v
用上方邊沿預測覆蓋
predict_8x8_ddl
右上到左下預測覆蓋(注意均值生成)
predict_8x8_ddr
左上到右下預測覆蓋(注意均值生成)
predict_8x8_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_8x8_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hu
左下到右上(偏右)預測覆蓋(注意均值生成)

以上“注意均值生成”指預測值一般由起點決定,起點由反向延長線確定,起點2倍權重和兩側點構成均值。
例如45度均值為以起始點對應45度位置的點作為中心(2倍權重),其兩側點作為補充形成的均值。

H.264標準規定,首先由macroblock層的mb_type導出intra塊的預測類型macroblock prediction mode,其中包含子塊尺度信息,有4x4,8x8和16x16三種。4x4和8x8按照標準由變換尺寸區分,4x4是最常用的模式。
在確定預測類型之后,對于含16個子塊的4x4模式,每個子塊的預測模式(即上述討論)由標準規定的判決算法給出:
<...>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/453756.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/453756.shtml
英文地址,請注明出處:http://en.pswp.cn/news/453756.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

進制轉換中dbho是什么意思_什么是網段?二進制十進制如何互相轉換?看完這篇,你就全明白了...

之前的文章講了ip&#xff0c;子網掩碼&#xff0c;網關的關系&#xff0c;今天著重講一下網段。我們用傻瓜交換機通訊時&#xff0c;一個網段的設備才能互相通訊&#xff0c;怎么能判斷兩個ip是同一個網段呢&#xff1f;今天就簡單的說一下。(這篇文章用語音聽可以起到催眠作用…

【網絡流24題】星際轉移問題(最大流)

【網絡流24題】星際轉移問題&#xff08;最大流&#xff09; 題面 Cogs 題解 因為天數是未知的&#xff0c;所以我們要想辦法處理天數 可以選擇二分或者依次累加天數 因為數據范圍較小&#xff0c;使用二分可能反而復雜度會增高 所以使用不斷累加天數 那么&#xff0c;把所有的…

使用 gunicorn 部署flask項目

1、WSGI協議 Web框架致力于如何生成HTML代碼&#xff0c;而Web服務器用于處理和響應HTTP請求。Web框架和Web服務器之間的通信&#xff0c;需要一套雙方都遵守的接口協議。WSGI協議就是用來統一這兩者的接口的。 2、WSGI容器 常用的WSGI容器有Gunicorn和uWSGI&#xff0c;但G…

軟件需求與問題解決

&#xff08;一&#xff09; 小滿當上項目經理后不久&#xff0c;參與了一個大項目。當時市場簽下來的時候&#xff0c;公司里面是歡天喜地的。項目做了一年多。到了交付的時候&#xff0c;用戶卻很不滿意&#xff0c;當初說好的東西&#xff0c;好多都變了卦。用戶是上帝&…

flex 換主軸后子元素占滿_Chrome72 嵌套 flex 布局修改,你的網站可能會發生布局錯亂...

起源2019 年 1 月 29 日&#xff0c;Chrome72 正式版(72.0.3626.81)發布&#xff0c;本次發布帶來了一個改變&#xff0c;且沒有在更新日志中提及&#xff0c;該改變導致某些網站發生了布局錯亂。該改變主要針對的是嵌套的flex布局&#xff0c;下面我們一起看下是怎么回事。問題…

使用 Django + Wusgi + Nginx 部署 Django

如何在生產上部署Django? Django的部署可以有很多方式&#xff0c;采用 nginxuwsgi 的方式是其中比較常見的一種方式。 uwsgi介紹 uWSGI是一個Web服務器&#xff0c;它實現了WSGI協議、uwsgi、http等協議。Nginx中HttpUwsgiModule的作用是與uWSGI服務器進行交換。 WSGI / …

網絡學習網址

網絡之路博客 http://ccieh3c.com/ 轉載于:https://www.cnblogs.com/changha0/p/8179801.html

路由到另外一個頁面_Nextjs使用解讀一(項目搭建與路由系統)

文章說明&#xff1a;1. 之前想搭建個人博客&#xff0c;由于學習的是react技術棧&#xff0c;所以就到處搜羅資料學了nextjs&#xff0c;配合koa就把博客搭起來了。該系列文章基于我的學習筆記&#xff0c;重新整理了一遍&#xff0c;如果有錯誤之處&#xff0c;還請指正。2. …

微信獲取token -1000

最終翻看微信開發api找到需要去配置IP白名單。只需要配置訪問來源IP即可。 轉載于:https://www.cnblogs.com/yangjinqiang/p/8184663.html

產品技術和管理

為啥純粹為消費者傳遞體驗的活動可以價格不菲&#xff0c;幾為暴利&#xff1f;——談客戶體驗作為客戶價值提升之源 不論產品還是服務&#xff0c;如果能夠為消費者傳遞有益的體驗&#xff0c;其價值就可以在一般的產品服務之上得以體現&#xff1b;附加了體驗的產品&#xff…

Linux 修改系統編碼

linux服務器的字符集設置可能影響到網站頁面出現 “&#xff1f;&#xff1f;&#xff1f;” 等問號亂碼&#xff0c;還有可能導致文件中的漢字部分出現亂碼。有兩個原因 服務器沒有安裝 zh_CN.UTF-8 字符集&#xff0c;導致不支持中文&#xff01;服務器雖然裝了 zh_CN.UTF-8…

jquery ztree 設置勾選_047 JAVA-jQuery

jQuery操作元素屬性的值表單:<body><input type"button" name"" id"but1" value"測試獲得屬性值" /><hr />賬號&#xff1a;<input type"text" name"sxtzh" id"zhanghao" value&q…

Opencv undefined reference to `cv::imread() Ubuntu編譯

Ubuntu下編譯一個C文件&#xff0c;C源程序中使用了opencv&#xff0c;opencv的安裝沒有問題&#xff0c;但是在編譯的過程中出現如下錯誤&#xff1a; undefined reference to cv::imread(std::string const&, int)undefined reference to cv::noArray()undefined referen…

深度學習目標檢測之 YOLO v1

這是繼 RCNN&#xff0c;fast-RCNN 和 faster-RCNN 之后&#xff0c;rbg&#xff08;RossGirshick&#xff09;針對DL目標檢測速度問題提出的另外一種框架。YOLO V1 增強版本GPU中能跑45fps&#xff0c;簡化版本155fps。 論文名&#xff1a; 《You Only Look Once&#xff1a;…

編程珠璣番外篇

1.Plan 9 的八卦 在 Windows 下喜歡用 FTP 的同學抱怨 Linux 下面沒有如 LeapFTP 那樣的方便的工具. 在蘋果下面用慣了 Cyberduck 的同學可能也會抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻煩的事情. 其實一點都不麻煩, 因為在 LINUX 系統上壓根就不需要用 FTP. 為什么呢? 因…

BT下載原理分析

版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 BitTorrent協議。 BT全名為BitTorrent,是一個p2p軟件,你在下載download的同時&#xff0c;也在為其他用戶提供上傳upload&#xff0c;因為大家是“互相幫助”&#xff0c;所以不會隨著用戶數的增加而…

表格列求和_excel表格制作,Excel表格的基本操作,包含制作一個表格10方面的知識...

創建表格&#xff0c;插入與刪除一行一列或多行多行&#xff0c;一次移動一行一列或多行多列&#xff0c;拆分與合并單元格&#xff0c;單元格內換行&#xff0c;表格求和與求平均值是Excel表格的基本操作&#xff1b;除此之外&#xff0c;Excel表格的基本操作還包括調整行高列…

深度學習之 FPN (Feature Pyramid Networks)

論文題目&#xff1a;Feature Pyramid Networks for Object Detection論文鏈接&#xff1a;https://arxiv.org/abs/1612.03144論文代碼&#xff1a;Caffe版本 https://github.com/unsky/FPN 《Feature Pyramid Networks for Object Detection》這篇論文主要解決的問題是目標檢…

ISLR—第二章 Statistical Learning

Statistical Learning Y 和X的關系why estimate f 用來預測 預測的時候可以將f^當成一個black box來用&#xff0c;目的主要是預測對應x時候的y而不關系它們之間的關系。用來推斷 推斷的時候&#xff0c;f^不能是一個black box&#xff0c;因為我們想知道predictor和response之…

提高編程思想

虛函數和抽象函數有什么區別 虛函數是有代碼的并明確允許子類去覆蓋&#xff0c;但子類也可不覆蓋,就是說可以直接用&#xff0c;不用重寫 抽象函數是沒有代碼&#xff0c;子類繼承后一定要重寫 ****************************************************************** 在一…