音視頻編解碼 -- 編碼參數 CRF

語言: CN / TW / HK

之前多多少少接觸過一些編解碼參數,CRF 參數也用過,但是最近在和朋友們聊天時,説到使用 FFMPEG 過程中碰到 CRF 參數,以及具體作用流程,這個之前一直沒有跟蹤過,也沒有詳細記錄過,所以吊起了自己的好奇心,於是決定搞清楚一下,便開始了這次 CRF 的神奇之旅。CRF 簡介:

恆定速率因子(CRF,Constant Rate Factor)是一種編碼模式,可以向上或向下調整文件數據速率以達到選定的質量級別,而不是特定的數據速率。

如果要保持最佳質量,而又不怎麼擔心文件大小,這時候就可以使用 CRF 速率控制模式。 這是大多數情況下建議的速率控制模式。當輸出文件的大小不太重要時,此方法允許編碼器嘗試為整個文件實現期望目標視頻質量的文件輸出,即所謂的一次編碼便可在預期視頻質量下獲得最大的視頻壓縮效率。CRF 模式主要原理是在編碼過程中通過動態調整每幀視頻的 QP 值,以便可以獲得保持所需視頻質量水平比特率。

但是 CRF 缺點是不能告知編碼器期望獲得特定大小的文件或不超過特定大小或比特率。同時需要注意的是採用 CRF 時不建議直接用來編碼視頻以進行流媒體傳輸。

通常建議一般使用兩種速率控制模式:恆定速率因子(CRF)或 2-pass ABR。 速率控制決定每個幀將使用多少位。 這將確定文件大小以及質量分配方式。CRF 實操演示

通過 FFMPEG 二進制文件嘗試用參數 CRF 進行壓縮,如下圖所示:

FFMPEG 採用 CRF 分別為 18、24 進行壓縮,以及和源文件的比較。

ffmpeg -i test.mp4 -c:v libx264 -crf 18 test18.mp4

實際轉碼中

轉碼結束後,會顯示具體的編碼相關信息,包括 ref,crf 值,qp 量化步長等,以及 I 幀、P 幀、B 幀所佔比重。還包含了音頻相關信息如下圖:

用命令 ffmpeg -i test.mp4 -c:v libx264 -crf 24 test24.mp4,進行 CRF=24 的轉碼,轉碼結果如下圖所示:

轉碼後分別對三個文件進行參數查看,並形成對比,其結果如下圖所示:

上述參數只能大概瞭解三個視頻基本信息,之後通過 Elecard eye 專業工具查看該變化產生原因的直觀圖,三個文件碼流分析結果:

三個文件對比情況總結如下:

可以看出:CRF 參數的使用,I 幀數量急劇減少、同時引入 B 幀;熵編碼採用了 CABAC 方式,這樣壓縮率就提升很多,文件大小變小。同時隨着 CRF 值變大,P 幀和 B 幀壓縮率也變大,文件更小。CRF 代碼走讀

雖然之前走讀過 FFMPEG 代碼,但是具體 CRF 參數的品讀還沒完全注意到過。為了不是一知半解的明白該問題,還是強迫自己走一遍代碼,增強印象,深刻認識,也為關心該參數的小夥伴鋪墊一下基礎。*•CRF 定義

首先在 X264 中可以看到該值的定義:

typedef struct X264Context {
    AVClass        *class;
    x264_param_t    params;
    ......

    float crf;

    ......
    }

在 AVOption 具體定義如下:

static const AVOption options[] = {
    { "preset",        "Set the encoding preset (cf. x264 --fullhelp)",   OFFSET(preset),        AV_OPT_TYPE_STRING, { .str = "medium" }, 0, 0, VE},
    { "tune",          "Tune the encoding params (cf. x264 --fullhelp)",  OFFSET(tune),          AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE},
    { "profile",       "Set profile restrictions (cf. x264 --fullhelp) ", OFFSET(profile),       AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE},
......
    {"x264opts", "x264 options", OFFSET(x264opts), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, VE},
    { "crf",           "Select the quality for constant quality mode",    OFFSET(crf),           AV_OPT_TYPE_FLOAT,  {.dbl = -1 }, -1, FLT_MAX, VE },
    { "crf_max",       "In CRF mode, prevents VBV from lowering quality beyond this point.",OFFSET(crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE },
......
}

CRF 仍然屬於 Rate control 的一中,所以可以看到其 RC 相關定義如下:

#define X264_RC_CQP                  0
#define X264_RC_CRF                  1
#define X264_RC_ABR                  2

•FFMPEG 接口梳理

涉及到 FFMPEG 代碼走讀的部分太多了,在此只是簡述 CRF 對應的部分,其他編解碼流程大家可以根據網上其他大神的代碼走讀流程完成即可。此篇文章默認大家有足夠基礎:X264 的編解碼入口符合 FFMPEG 接口定義,對應關係如下圖所示:

此處借用雷神的一張圖説明: (https://blog.csdn.net/leixiaohua1020/article/details/45960409)

  • X264_init()

X264_init 函數主要作用就是將之前賦值和初始化的 option 值依次傳遞到 libx264 模塊中,進行 X264 參數初始化,以及 RC 參數賦值。這些值是從 AVCodecContext 傳遞過來,以及 X264Context 的默認值。熟悉 FFMPEG 的人都瞭解,AVCodecContext 中包含輸入命令行中編解碼選項值,以及 FFMPEG 命令中包含的 option 值,而 X264Context 包含 x264 的相關選項,兩者結合構成完整的 x264 編解碼選項值。

在 X264_init 的最後,進行 X264Codec 的 OPEN 動作,以及編碼全局 header 的動作。

  • x264_param_default

x264_param_default 設置默認參數,包括其他的選項值,在此只關心 CRF 相關選項。x264_param_default 中將 CRF 默認開啟,同時設置 CRF 選項 f_rf_constant 置為 23,這也是其他很多文章中講到的默認值 23 的原因。

同時注意,觀察到在 x264_param_default 默認參數中 B 幀是再次設置並置位的,而且 cabac 默認開啟。所以如果用 FFMPEG bin 文件進行轉碼出來的文件中 cabac 是默認開啟的,這也是工具端查看時會出現 CABAC 以及增加 B 幀的根本原因了。

  • x264_encoder_open

在初始化具體參數後,init 函數接下來進行 x264_encoder_open(相關代碼位於 encoder\encoder.c)的操作,這時會具體打開到 x264 中 h264 相關編碼器。

之後在 x264_encoder_open 中主要用於打開編碼器,其中校驗、初始化了 libx264 編碼所需要的各種變量,並完成 sps、pps、qm 初始化。

  • validate_parameters

調用 validate_parameters 會進行輸入參數的校驗,防止輸入參數異常導致編碼失敗。此函數中完成 CRF 相關參數校驗、更新和賦值。

其他流程部分可以參考其他大神的文章,再次不再累述。(雷神的解析非常詳盡了,敬請膜拜即可 x264源代碼簡單分析:編碼器主幹部分-1_雷霄驊(leixiaohua1020)的專欄-CSDN博客

  • x264_ratecontrol_new

x264_encoder_open 最後會調用 x264_ratecontrol_new 完成碼率控制相關變量初始化。

x264_ratecontrol_new,主要設置碼率控制的核心參數,需要對 x264 碼率控制比較瞭解才能真正明白,否則會容易看暈。

x264_ratecontrol_new 函數中依據傳入參數是 CRF 模式,以及 b_stat_read 默認值為 0 即可將 b_abr 參數的置位為 1,同時 b_2pass 置位為 0,也就是説 CRF 模式在 rate_control 中按照 abr、非 2-pass 進行處理的。

在 x264_ratecontrol_init_reconfigurable 函數中會進行 VBV 參數初始化,以及 CRF 相關參數 base_cplx、rate_factor_constant 的更新。

同時 x264_ratecontrol_init_reconfigurable 中設置被調用時,傳入 b_init=1 的參數,這時 CRF 置位了 VBV 模式,為後續的 rate_control 做了鋪墊。

  • X264_frame

X264_frame()用於依據傳入 packet 數據進行一幀視頻數據的完整編碼。該函數部分定義如下所示。

  • reconfig_encoder

reconfig_encoder 主要作用就是將 RC 相關的參數和 AVCodecContext 中參數進行比較,如果不一致,則重新配置編碼器。比如 CRF 值初始設置為 24,但是命令行中設置為 18,這時兩個值不一致,則需要按照命令行中值進行賦值並重新配置編碼器,以便最終符合用户預期。具體配置大家簡單看一下就好,這裏不再展開。

  • x264_encoder_encode

x264_encoder_encode 是真正編碼的開始,在 x264_encoder_encode 這個函數裏面將一幀完整 YUV 圖像編碼成 H264 視頻流,這個過程可以參考雷神的文章,解析非常好, https://blog.csdn.net/leixiaohua1020/article/details/45644367

這邊關心的是 CRF 中涉及到的部分內容,在 x264_encoder_encode 中和碼率控制相關的內容主要是一下接口:

x264_thread_sync_ratecontrol():

x264_ratecontrol_zone_init():

x264_ratecontrol_start():開啟碼率控制,針對每一幀進行碼率控制。在 x264_ratecontrol_start 中會根據碼率控制模式的不同,選擇不同的 qp 進行壓縮。之前分析可知,CRF 是屬於 abr 模式,同時增加了 B 幀,所以導致每幀圖像的 qp 都是不同的,這樣壓縮後相同質量的條件下編碼後文件大小就不能確定了。

x264_ratecontrol_qp():

碼率控制是一個大塊內容,設計的算法也比較複雜,該文只關注瞭如何將 crf 模式轉換到 vbv 模式,以及對影響編碼的部分參數,整個過程下一篇文章我們再進行分析和跟蹤。

以上是個人的一些看法,可能有不正確的地方,歡迎大家一起討論學習。

如果該文章對您有幫忙,歡迎點贊,收藏,轉發、關注,在下持續更新音視頻相關內容。