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.2 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.0 bf 3 25.3 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 は画質が改善するので設定した方がよいでしょう。(デフォルトは 0、x264のデフォルトは 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 の跳ね上がりには何か関係があるようです。