ffmpeg NVENCの画質と最適設定

NVENCとfdk-aacが使えるffmpegをビルドし、h264_nvenc cq (constant quality mode) の動作を解析し最適なエンコード設定を求めた。

x264 の crf とは設定が根本的に違う。crf は動きの少ない時の目標、cq は動きの多い時の目標。

動きの少ない時は cq - 5 程度になるので cq 28 が crf 23 相当。

nvdec を使わない場合の cq 28 の画質は x264 デフォルト crf 23 と同程度。(動きが激しい時は除く)

ただしビットレートは3割程度大きい。(cq 30 なら x264 並)

問題点

・Bフレームは画質が悪くて使えない

・nvdec を使うと画質が少し悪い(画質と速度、どちらを取るか)

・同じ qp 設定でも nvenc の方がビットレートが1割程度高い(constant QP mode)

・cq の q の変化幅が広い影響で動きが少ないと x264 比のビットレートは増加する(動きが多いと減少)

追加情報

 NVENC SDK  API 10.0 以降ではBフレームのデフォルト値が 3 になっているのでBフレームなしにするには -bf 0 が必要です。この記事では API 9.0 使用(Bフレームのデフォルトは 0 GPUはGT 710

H265(HEVC)の場合は  -b_ref_mode 1 を指定すればBフレームを使っても問題なさそうです。(Turing 世代でH265のBフレーム対応 追加データ参照

Turing 以降では H264 の画質が改善しているので qp設定のビットレートは x264 とほぼ同じですBフレームの画質も改善していますが、まだ 不十分なのでBフレームなしの方がよい。(RTX 4060 で確認)


デコーダ(nvdec)の画質確認

Full HD から 720p の mp4 を作成する場合

-c:v mpeg2_cuvid -deint 2 -drop_second_field 1 -resize 1280x720

・インターレース解除は-deint 1(bob) と-deint 2(adaptive) があるがbobは高速だが画質が悪い

 adaptiveの画質はffmpegのbwdifより悪い(ffmpegのyadifの画質はいまいち、線の上に斑点が散るのでアニメには不向き)

 60fps化はしないので2番目のフィールドは捨てる

・リサイズの画質はffmpegのデフォルトbicubicより悪い

デコーダは速度重視ならnvdec、画質重視ならffmpeg

エンコード オプションの検討

デフォルトでは 2000kbps の vbr になるが、品質を指定する場合は -b:v 0 -cq を使う

-b:v 0 なしだと使う ffmpeg によって挙動が変わるので -b:v 0 は必要(ビルドしたものと q の上限が違った)

h264_nvenc の cq (constant quality mode) は x264 と違い、動きが少ない時に q の値が大幅に低下する

q は quantization parameter (QP)

・h264_nvenc の -b:v 0 -cq

 q は設定値 +5 ~ -7 (エンコード時の q 表示、-qp 換算だと +6 ~ -6、q 表示は整数で設定より 1 低い、切り捨て?)

 動きが少ない時は -7

・libx264 の -crf

 q は設定値 +6 ~ -4 (エンコード後のPフレームの q 表示、Bフレームは +6 を越えない範囲で P + 2 程度)

 -4 は静止の場合、動きが少ない時は -1~-2 程度 (実質的な q の範囲は +6 ~ -2 )

x264 とは q の下がり方に 5 の差があるので

-b:v 0 -cq 28 で動きの少ない時の q は x264 のデフォルト -crf 23 とほぼ同じになる( q の最大値は 4 大きい)

これでも x264 よりファイルサイズはかなり大きいので -cq 30 でも良いと思う(ファイルサイズ優先)

Constant QP mode で画質を確認(nvdec未使用)

 -qp 21 ◎ 細部の描画良好

 -qp 23 ◎ 静止画でも気にならない

 -qp 25 ◎ 静止画だと少し気になる

 -qp 28 ○ 動いているなら気にならない

 -qp 32 △ 動きが大きい時なら許容できる

 -qp 36  ×  動きが大きくてもかなり気になる

これを目安に設定を決める、動きの多い時でも32程度に抑えたい

-rc constqp (Constant QP mode) のデフォルトは -qp 28(エンコード時の q 表示は 設定値 -1 )

x264 のデフォルト crf 23 の q の範囲 19~29 は非常に優秀であることがわかる

Bフレームなしの x264 と h264_nvenc で同じ -qp なら画質はほぼ同じ、ビットレートは nvenc が1割程度大きい


ビットレート指定の vbr では qmin qmax を指定することができる

-b:v 2000k -qmin 23 -qmax 36

-qp 23 より下げても画質はあまり改善しないので -qmin 23

-qp 36 を越えると急激に画質が悪化するので -qmax 36

720p のビットレートは 2000k 以上が必要

その他のオプション

-g 150  GOPサイズ(キーフレーム間隔) 30fps で 5 秒、デフォルトの 250 は長すぎる

  250 だとシークが遅くなる、ファイルサイズ的にもほとんどメリットない

  x264 は可変で max 250 なので、150 程度がちょうどよい

-profile:v high  high profile(デフォルトはmain) high の方が薄い背景の潰れが少ない

  main だと q が大きい時に階調が潰れやすい(アニメだとわかりやすい)

-rc vbr_hq  high quality mode 速度低下に見合った効果があるかは疑問、ビットレートは僅かに小さくなる

-bf 3  Bフレーム(max 4) 画質が非常に悪くなる

  Constant QP mode で確認すると q の値が時々 +8 跳ね上がる

画質で注目したポイントは

薄い背景などの潰れ、文字やロゴの潰れ、線の綺麗さとノイズ、青色部分の潰れ


最適なエンコード設定

品質指定の場合 -profile:v high -g 150 -b:v 0 -cq 30

ffmpeg -y -c:v mpeg2_cuvid -deint 2 -drop_second_field 1 -resize 1280x720 -i "input file" -vf fps=30000/1001 -async 1 -aspect 16:9 -c:v h264_nvenc -profile:v high -g 150 -b:v 0 -cq 30 -c:a libfdk_aac -ac 2 -b:a 128k -brand mp42 "output.mp4"

-cq 30 で q の範囲は -qmin 24 -qmax 36 相当

ビットレートは実写で 2000kbps 程度、-cq 28 だと3割増加するが高画質(x264 デフォルト並)

nvdec を使わない場合

ffmpeg -y -i "input file" -vf bwdif,fps=30000/1001 -async 1 -s 1280x720 -aspect 16:9 -c:v h264_nvenc -profile:v high -g 150 -b:v 0 -cq 30 -c:a libfdk_aac -ac 2 -b:a 128k -brand mp42 "output.mp4"

画質がクリアになるのでビットレートは少し増加するインターレース解除にはbwdifを使用

ビットレート指定の場合

ffmpeg -y -c:v mpeg2_cuvid -deint 2 -drop_second_field 1 -resize 1280x720 -i "input file" -vf fps=30000/1001 -async 1 -aspect 16:9 -c:v h264_nvenc -profile:v high -g 150 -b:v 2000k -qmin 23 -qmax 36 -c:a libfdk_aac -ac 2 -b:a 128k -brand mp42 "output.mp4"

nvdec nvenc 以外のオプションの説明はこちら

ffmpeg のログ出力の q の値 をグラフ化

品質指定の方が q の変動が小さく優れている、2M bpsでは動きの多い時に足りない、Bフレームを使うと非常に悪い

エンコードしたのは動きが多い部分( cq 30 アニメ 約 2M bps、実写 約 3M bps )

x264 に対抗するには q をもう少し下げる必要がある( x264 の q の範囲はデフォルトで実質 21~29 )

cq 28 なら x264 のデフォルト crf 23 に見劣りはしない(動きが激しい時は除く)

cq 30 → 28 でビットレートは3割増加

x264 のデフォルトはBフレームが簡易なのでノイズが多め、この点では nvenc の方が画質がよい

Bフレームを完全にすれば改善するが、それでもノイズに関して cq 28 の方がよさそう

動きが多い時の q は負けているのでトータルで画質は同等か( cq 28 の q の範囲は 21~33 )

q の最大が高いので動きが激しい時にビットレートが抑えられるメリットもある

色々な動画を数分間切り出した時のビットレート

1~3 はアニメ、4~8 は実写、前のグラフは 3 と 7 の動画

nvenc の cq30 と x264 の crf 23 は 2000kbps 程度まではほぼ同じ、それ以上では q の上限の違いの影響が出る

x264のBフレーム有無の差が1割程度と小さいのはBフレームありの方が q が少し小さいため、 Bフレームで画質が悪くなる分を q を下げて相殺しているようだ

x264 crf 23 と nvenc cq 28 の q の比較

x264 は3秒間隔で切り出し、nvenc の log は瞬時値なの速度を落として移動平均4(約3秒) 動画の番号はビットレートのグラフと同じ

cq 28 の q は振れ幅は大きいが、動きの激しい動画7以外では crf 23 と同程度、画質もほぼ同じ

cq (constant quality mode) の動作は「動きが多い時に平均を設定値に合わせる」のようだ(動画7 cq28 の q の平均は28程度)

動きが少ない時は cq 28 の方が q が下がりやすいので画質は良く、ビットレートは増加 (Bフレームなしの x264 と h264_nvenc は q が同じなら画質はほぼ同じ)

q の振れ幅が大きいのもビットレートが増える要因になっている

動きが少ない時は nvenc の方が先に q が下がる、動きが多い時は nvenc の方が q が上がる、 全体の x264 比のビットレートはどちらの影響が大きいかで決まる

各動画の q の平均

    crf 23 cq 28   差  bitrate cq28/crf23

動画3 23.9  23.4   -0.5  1.48

動画6 25.3  25.0   -0.3  1.28

動画7 25.6  27.7  +2.1  1.03 

NVENCのエンコード速度

Full HD  > 720p

CPU  Ryzen 5 2600 GPU  GT 710

nvenc(nvdec未使用)     181fps

nvenc(nvdec bob)   181fps

nvenc(nvdec adaptive)    136fps

x264 veryfast      174fps 

エンコーダの設定はデフォルトnvdec未使用時のインターレース解除はyadif(bwdifより高速)

インターレース解除の bob と adaptive の差は結構大きい

速いCPUと遅いGPUの組み合わせの場合、x264 veryfast でも十分速いのでNVENCのメリットはない

CPU  AthlonⅡ X4 645 GPU  GT 640

nvenc(nvdec未使用)   72fps

nvenc(nvdec bob)   178fps

nvenc(nvdec adaptive)    135fps

x264 veryfast     56fps 

遅いCPUでもnvdecを使えば高速エンコードが可能

追加データ

 RTX 4060 でデータを取得、ffmpeg のログ出力の q の値 をグラフ化

比較しやすいようにビットレート 2000 k 指定とした

H264でBフレームを使うとq値が悪化する、-b_ref_mode 1 で少し良くなるが -bf 0 よりかなり悪い。

最新のGPUでもBフレームの問題は解決できなかったようです。

q の平均値は bf 0  24.1    bf 3 27.1   bf 3  b_ref_mode 1 26.0

GT 710  では  bf 0  25. bf 3 28.6 なので画質はかなり改善されています。

ちなみに x264  2000k の場合はBフレームありでもかなり良い値です。

x264 bf 0 frame P 23.54  bf 3 frame P 22.12 frame B 24.37

cq の場合は q を合わせるので RTX 4060 の方がビットレートは低くなります。

GT 710  cq 30   bf 0 1924kbps (q 25.1)  bf 3 1792kbps (q 27.3) 

RTX 4060 cq 30   bf 0 1765kbps (q 24.6) bf 3 1689kbps (q 26.9) b_ref_mode 1 1667kbps (q 26.4) 

qは平均値 RTX 4060 の方が q が 少し低い、nvdecは未使用

x264  crf 23 はビットレートは cq 30 とほぼ同じですが q の値は低いです。

x264 crf23 bf 0 1830kbps frame P 23.79  bf 3 1638kbps frame P 23.39 frame B 25.59

H265 (HEVC) の場合は -b_ref_mode 1 なら変動は少し大きいですがBフレームなしとほぼ同じなので問題ないと思います。 -b_ref_mode 0  (無効 デフォルト) だと変動がかなり大きく値も悪い

q の平均値は bf 0  24.  bf 3 25. bf 3  b_ref_mode 1 24.4

グラフは載せていませんが cq (constant quality mode) の挙動は H264 と変わらないようです。

cq の場合はBフレームなしだと H264 と q がほぼ同じなのでビットレートもほぼ同じ、Bフレームありだと H264 より q が低いのでビットレートは低くなります。

H264 cq 30   bf 0 1765kbps (q 24.6)  bf 3 1689kbps (q 26.9) b_ref_mode 1 1667kbps (q 26.4)  

H265 cq 30   bf 0 1760kbps (q 24.5)  bf 3 1493kbps (q 26.1) b_ref_mode 1 1425kbps (q 25.7) 

H264 ではBフレーム使用時のビットレートの低下 (5.6%) と q の増加 (1.8) が釣り合っていない、H265 では ビットレートの低下が大き (19%)  割りにq の増加が少ない (1.2) ので問題ないと思います。(bf 3   b_ref_mode 1 の場合)

あと -rc-lookahead は画質が改善するので設定した方がよいでしょう。(デフォルトは 0x264のデフォルト40

測定方法はエンコードをできるだけ遅くするため  CPUクロック最低、スレッド1、nvdec未使用

NVENC SDK API  10.0

nvdec (mpeg2_cuvid) で -deint 2 と -resize を使うと画像が崩れる不具合が発生、ドライバのバグ?


Constant QP mode の確認

Bフレームを使用すると相変わらず q が跳ね上がるのでNGです(設定値 +4 か +8 に跳ぶ)

H264 b_ref_mode 0   +8 b_ref_mode 1  +4 b_ref_mode 2  +4/+8

H265 b_ref_mode  によらず  +8

b_ref_mode と q の跳ね上がりには何か関係があるようです。