1. 程式人生 > >轉:如何閱讀X264程式碼

轉:如何閱讀X264程式碼

https://www.cnblogs.com/xkfz007/articles/2616057.html

 

最近我也開始看 X264 的程式碼了,於是想到把我讀程式碼的過程記錄下來,因為總有很多新手問如何讀程式碼,我這個帖子就是專為這些人寫的。至於會讀程式碼的人就完全沒有必要看了。下面當然是以 X264 為例了。JM 以及其他程式碼的學習方法和技巧都是完全一樣的。我所用的版本是在帖子 在VS2008下編譯最新版的x264連線錯誤 裡上傳的版本。最新版本的程式碼基本結構應該變化不大。

首先肯定是要把 X264 編譯通過了,這個我就不多說了,論壇帖子 VS2008下最新X264(svn 2009.9)編譯不過的解決辦法(附編譯通過+修改記憶體洩露版本)

裡講得很清楚。編譯通過之後第一步就是設定編碼參 數,一開始儘量從最基本的學起,因此採用最簡單的選項,我的引數是:-q 28 -o test.264 f:/sequences/qcif/foreman_qcif.yuv 176x144 --no-asm,在哪裡設定呢?看下面的截圖(在第一個圖中選擇 properties 後就會出現第二個圖了)。至於各個引數的意義嘛,看一看 X264 的幫助資訊(x264.c 裡面的 Help 函式),這裡說說我用的這些引數的意思。-q 28 指定採用固定 QP = 28 進行編碼,-o test.264 指定輸出的碼流檔名為 test.264,f:/sequences/qcif/foreman_qcif.yuv 這個當然是指定原始待編碼影象序列了,176x144 指定待編碼影象的寬高,--no-asm 指定不採用匯編優化,全 C 程式碼才方便我們跟蹤噻。對了,差點忘記了,要除錯執行還要設定一下下面第二個截圖的紅色框內的內容。

      
              
==================================================================================



好了,配置完了就要開始除錯運行了哦。在 x264.c 檔案中找到 main 函式,在 x264_param_default 函式所在這一行上下斷點(即把游標移到這一行,然後按 F9),然後除錯執行編碼器(即按 F5),程式就會停在斷點處(如下面截圖)。下面開始單步除錯了,除了我提到的東西,其他都不需要關心。



我以前多次說過,讀程式碼首先要讀框架(即粗讀),任務是大致搞清楚各個函式在幹什麼。那麼 x264_param_default 是我們第一個遇到的函式,它的功能是什麼呢?顧名思義,猜測肯定是進行一些引數的設定了,因為 param 就是英文單詞 parammeter 啊,按 F11 進到函式裡瀏覽一下,果然是對一些變數賦值的操作,那麼多變數啥意思?現在不必管那麼多,因為你現在的任務只是搞清楚各個函式的功能,不要跑題了。 x264_param_default 裡的第一個函式是 memset,這個是系統函 數,你要問啥功能?去查 MSDN;第二個函式是 x264_cpu_detect,啥功能?再顧名思義,因為 X264 針對不同的平臺做了優化,這個肯定是用來檢測你所使用的平臺的,我就不進去看了。好了,直接點 debug 工具欄上的 step out(見下面第一個截圖)返回 main 函式。你在你的 VC 裡沒有看到 debug 工具欄?那好吧,在 VC 工具欄的空白地方點滑鼠右鍵,選中 debug(見下面第二個截圖)。

                             

==================================================================================



下面按 F10,程式執行到 Parse 函式,黃色的箭頭也指向它了(見下面截圖),它是啥功能?上面不是有英文註釋麼?——/* Parse command line */,意思就是說編碼器就是在這個函式裡讀取我們上面設定的引數的。有興趣進去看一下,看看就行,趕緊出來,不要又被陷在裡面了,我就 pass 了。繼續 F10 執行到 Encode,顧名思義,這個就是編碼主函數了,因為後面再也沒有函數了,所以編碼一定是在這個函式裡面完成的(不要問我它前面的 signal 是什麼功能,我一開始說過了,我沒提到的不需要關心,你問了我也不知道它是幹什麼的)。



好了,按 F11 進入 Encode 函式。一進去就有一些賦值語句,你要問那些變數是啥意思?——你跑題了!Encode 裡第一個函式 p_get_frame_total 啥功能,顧名思義,我猜測是計算待編碼影象序列一共有多少幀,要驗證猜測的自己按 F11 跟進去看程式碼,我就 pass 了。繼續 F10,到了函式 x264_encoder_open,它都做了些什麼?F11 進去看看:memset,memcpy 是系統函式,F10 執行到 x264_validate_parameters,它在幹什麼?先想想 validate 是啥意思?然後 F11 進去,然後 F10 瀏覽一下好像還是在做一些引數的賦值和引數檢查,瀏覽過程中對遇到的每個函式顧名思義一下其功能,自己考慮一下要不要跟到裡面去。當我 F10 到 x264_sps_init 的時候,它的函式名讓我有興趣想知道它的具體功能——因為 SPS 跟編碼直接關係。顧名思義SPS 初始化,F11 進去之後會發現是在對 SPS 結構體裡的變數進行賦值。這些變數大部分在 200503 版標準 7.4.2.1 小節都能找到相應的解釋。好了 step out 返回 x264_validate_parameters,繼續 F10,好像其他沒什麼函式是我有興趣的了,直接step out  返回 x264_encoder_open(或者在 x264_validate_parameters 裡一直按 F10,最後也會返回到 x264_encoder_open)

好了,繼續 F10(注意:x264_cqm_parse_file 這個函式是停不住的,它的功能顧名思義是解析量化矩陣配置檔案,我們的編碼選項裡沒有采用量化矩陣,因此它是不會被執行到的,你說我咋顧名思義知道它是跟 量化矩陣相關的,因為 cqm 就是 custom quant matrix,你再要問我咋知道 cqm 是 custom quant matrix,我猜的,真是猜的!),到 x264_reduce_fraction 函式,這回我顧名思義不出來它在幹什麼了,那就 F11 進去,看見一小段計算,也不知道在幹什麼。但是再看看 x264_reduce_fraction 的輸入引數,i_fps_num,顧名思義是幀率,此時把滑鼠移動到這個變數上顯示它的值是 25,見下面截圖(要想知道它在哪裡被賦值的,回頭去找噻,還記得我們在 main 裡遇到的第一個函式是 x264_param_default 麼,當時顧名思義它的功能是什麼?如果你回去找了,你會發現裡面有個語句是:param->i_fps_num = 25;那到底是不是它呢?很簡單,把這裡 25 改成其他值,例如 27,再重新除錯執行到 x264_reduce_fraction,看 h->param.i_fps_num 是否等於 27 就知道了。)



中間來了個插曲打亂了敘述的邏輯節奏。剛才我們是在考慮 x264_reduce_fraction 的功能。F11 進去沒什麼收穫,但從輸入引數上我們可以猜測到它應該是在做跟幀率相關的計算。好了,就先了解這麼多吧。繼續 F10,到了 x264_sps_init,咦,剛才我們不是遇到過一個 x264_sps_init 了麼?為什麼這裡又有一個呢?讀者自己試著去猜測一下原因。
繼續 F10,到了 x264_pps_init,顧名思義,猜測是對 PPS 的引數設定,其中大部分變數在 200503 版標準 7.4.2.2 小節都能找到相應的解釋,有興趣的進去瀏覽一下。繼續 F10,到 x264_validate_levels,顧名思義是檢查一些變數的值是否合理。
繼續 F10,執行到 x264_cqm_init,顧名思義量化矩陣初始化(記心好的朋友一定記得前面有個跟 cqm 相關的函式,那個是解析量化矩陣檔案,功能不一樣哈)。
繼續 F10,到 x264_rdo_init,顧名思義進行 RDO 的一些初始化操作,F11 進去之後根據裡面的函式顧名思義猜測是在進行 CABAC 表的初始化。
繼續 F10,到 x264_predict_16x16_init,顧名思義是進行與 16*16 預測相關的一些初始化,F11 進去對裡面用到的變數顧名思義發現是在設定一些幀內 16*16 預測函式的函式指標。
繼續 F10,到 x264_predict_8x8c_init,顧名思義是與色度預測相關的初始化操作,F11 進去對裡面用到的變數顧名思義猜測是在設定一些幀內色度預測函式的函式指標。
繼續 F10,到 x264_predict_8x8_init,顧名思義是與 8*8 預測相關的初始化操作,F11 進去對裡面用到的變數顧名思義猜測是在設定一些幀內 8*8 預測函式的函式指標。
繼續 F10,到 x264_predict_4x4_init,顧名思義是與 4*4 預測相關的初始化操作,F11 進去對裡面用到的變數顧名思義猜測是在設定一些幀內 4*4 預測函式的函式指標。
繼續 F10,到 x264_init_vlc_tables,顧名思義是與 CAVLC 相關的初始化操作,F11 進去瀏覽下程式碼會知道是在進行 VLC 表初始化。同時可以從這裡猜測到 h->param.b_cabac 這個變數的含義表示是否採用 CABAC 編碼,因為該變數為 0 才進行 CAVLC 表初始化。
繼續 F10,到 x264_pixel_init,顧名思義是畫素初始化,不懂具體啥意思,一頭霧水,那就 F11 進去看看,根據裡面大量出現 SAD/SATD 猜測是在設定運動估計時候計算代價用的一些函式指標,因為計算代價會用到 SAD/SATD。
繼續 F10,到 x264_dct_init,顧名思義是 DCT 相關的初始化,F11 進去對裡面用到的變數顧名思義猜測是在設定 DCT 變換函式的函式指標。
繼續 F10,到 x264_zigzag_init,顧名思義是 zigzag 掃描相關的初始化,F11 進去對裡面用到的變數顧名思義猜測是在設定 zigzag 掃描函式的函式指標。
繼續 F10,到 x264_mc_init,顧名思義是運動補償(MC)相關的初始化。
繼續 F10,到 x264_quant_init,顧名思義是量化相關的初始化,F11 進去會發現裡面是在設定量化相關的函式的函式指標。
繼續 F10,到 x264_deblock_init,顧名思義是去塊濾波相關的初始化,F11 進去會發現裡面是在設定去塊濾波相關的函式的函式指標。
繼續 F10,到 x264_dct_init_weights,顧名思義我也沒猜出是啥意思,F11 進去也沒獲得進一步資訊,那就先把裡面用到的變數在腦袋裡拍個快照。等以後用到這些變數的時候再根據上下文來理解。
繼續 F10,到 mbcmp_init,顧名思義我也沒猜出是啥意思,F11 進去看見有個巨集定義 X264_ME_TESA,猜測是跟運動估計相關的操作。
繼續 F10,到 x264_frame_pop_unused,F11 進去,讀一下里面的程式碼就知道是取得一個 x264_frame_t 結構體,猜測這個結構體是用於存放當前編碼幀的資訊的。
繼續 F10,到 h->thread[ i ]->out.p_bitstream = x264_malloc( h->out.i_bitstream );,根據 p_bitstream 和 x264_malloc 顧名思義是開闢碼流儲存空間的。
繼續 F10,到 x264_macroblock_cache_init,F11 進去瀏覽下程式碼,猜測是開闢巨集塊資訊存放空間,以及進行一些巨集塊資訊初始化設定的。
繼續 F10,到 x264_ratecontrol_new,顧名思義是位元速率控制相關的函式。

好了,x264_encoder_open 函式就這麼瀏覽完了,各個函式的大概功能也知道了。返回到 Encode 函式,F10 繼續,到了 p_set_outfile_param,F11 進去發現是個空函式。F10 繼續,到了 x264_picture_alloc,結合註釋並顧名思義是分配解碼過 程中影象要用到的儲存空間的。F10 繼續,到 x264_mdate,從函式名不知道什麼意思,F11 進去就知道了,是獲取當前時間的。繼續 F10 就到了 for 迴圈,從 for 迴圈的程式碼以及註釋可以知道這個 for 迴圈的功能就是迴圈編碼每一幀了。繼續 F10,到 p_read_frame,顧名思義是在讀入當前幀待編碼影象。繼續 F10,到 Encode_frame,顧名思義,這就是執行編碼一幀影象的函數了,最核心的編碼程式碼肯定就在這裡面了。這個 for 迴圈之後還有個 do-while 迴圈,其中也呼叫了 Encode_frame 函式,根據註釋猜測是進行 B 幀編碼的。x264_mdate 剛才說了是在獲取當前時間,x264_picture_clean 顧名思義是在釋放影象的佔用記憶體,x264_encoder_close 顧名思義是執行關閉編碼器的一些操作,F11 進去後瀏覽程式碼會發現包括輸出資訊、釋放記憶體等一些操作,x264_free 也釋放記憶體,p_close_infile 和 p_close_outfile 顧名思義是關閉輸入輸出檔案。

至此 X264 編碼器第一層函式結構就分析完了。下面接著的就是採用同樣的方法分析編碼主函式 Encode_frame 了。經過上面的敘述,相信不會閱讀程式碼的朋友一定已經掌握了閱讀程式碼的入門技巧。當然越到深層函式的分析要求對 H.264 標準和 H.264 編碼原理越熟悉,所以分析編碼主函式的難度也比較大,耗時也比較長。因此學習 X264 程式碼是需要將 H.264 的編碼知識與程式碼實現結合起來的,這樣才能更容易讀懂程式碼,這一點在上面的敘述中也有體現。

好了,暫時寫到這裡吧。打這麼多字,真累啊!希望你們看著不會覺得累。呵呵~~~