• FFmpegでVP9エンコード 補足

    2018年03月30日 20時12分
    AV1について色々調べた結果、動画の色関連の意味がようやく分かってきたので、
    VP9エンコードする時の設定を再確認しました。

    元になる記事はこちら。

    FFmpegでVP9エンコード


    まずはVP9のBitstream仕様を見て、色関連のデータがどのように格納されているか確認します。

    color_space
    color_space Name of color space Description
    0 CS_UNKNOWN Unknown (in this case the color space must be signaled outside the VP9 bitstream).
    1 CS_BT_601 Rec.ITU-R BT.601-7
    2 CS_BT_709 Rec.ITU-R BT.709-6
    3 CS_SMPTE_170 SMPTE-170
    4 CS_SMPTE_240 SMPTE-240
    5 CS_BT_2020 Rec.ITU-R BT.2020-2
    6 CS_RESERVED Reserved
    7 CS_RGB sRGB (IEC 61966-2-1)

    color_range
    color_range Description Details
    0 Studio swing For BitDepth equals 8:
    Y is between 16 and 235 inclusive.
    U ans V are between 16 and 240 inclusive.
    For BitDepth equals 10:
    Y is between 64 and 940 inclusive.
    U ans V are between 64 and 960 inclusive.
    For BitDepth equals 12:
    Y is between 256 and 3760 inclusive.
    U ans V are between 256 and 3840 inclusive.
    1 Full swing No restriction on Y, U, V values

    subsampling_x, subsampling_y
    subsampling_x subsampling_y Description
    0 0 YUV 4:4:4
    0 1 YUV 4:4:0
    1 0 YUV 4:2:2
    1 1 YUV 4:2:0

    ※color_space = CS_RGBの時だけは特殊でcolor_range = 1固定、subsampling_x,subsampling_yは無効。


    よくよく見てみるとsRGBが居ます。

    エンコードするソースはゲームをキャプチャしたsRGBのデータなので
    使えるならこれを使った方が良い気がしたのですが、
    Firefoxではそもそも再生出来なかったりChromeだと色がおかしくなりました。

    現状は再生側の対応がイマイチなようなので
    今まで通りYUV 4:4:4を使っておいた方が無難なようです。


    そうするとcolor_spaceはBT.709を使うことになります。

    厳密にはsRGBとBT.709ではtransfer characteristics(ガンマ)が違うのですが
    この辺りをきちんと処理している再生環境がどの程度あるか微妙だと思うので
    そこまで気にしなくていいかなと思います。

    実際にBitstreamにセットされているかが確認出来ないのが遺憾ではありますが、
    ffmpegのソースを見る限り以下のオプションでcolor_spaceにセットされるようです。
    -colorspace bt709

    color_rangeの設定はこう。
    -color_range jpeg

    -color_primariesと-color_trcは設定するとffmpegのログ的には反応するのですが、
    VP9のBitstreamの仕様上入れるところが無いので効果は無いと思います。
    (一応WebMのメタ情報には書き込まれるのですが使うソフトが有るかどうか)

    まあ入れて害があるわけでもないでしょうから入れておけば良いという見方もあります。


    参考として他の値についても記載しておきます。

    color_space
    color_space Description ffmpeg内の対応するデータ ffmpegのオプション例
    0 Unknown (in this case the color space must be signaled outside the VP9 bitstream). AVCodecContext.colorspace = AVCOL_SPC_UNSPECIFIED -colorspace unspecified
    1 Rec.ITU-R BT.601-7 AVCodecContext.colorspace = AVCOL_SPC_BT470BG -colorspace bt470bg
    2 Rec.ITU-R BT.709-6 AVCodecContext.colorspace = AVCOL_SPC_BT709 -colorspace bt709
    3 SMPTE-170 AVCodecContext.colorspace = AVCOL_SPC_SMPTE170M -colorspace smpte170m
    4 SMPTE-240 AVCodecContext.colorspace = AVCOL_SPC_SMPTE240M -colorspace smpte240m
    5 Rec.ITU-R BT.2020-2 AVCodecContext.colorspace = AVCOL_SPC_BT2020_NCL -colorspace bt2020nc
    6 Reserved
    7 sRGB (IEC 61966-2-1) AVCodecContext.colorspace = AVCOL_SPC_RGB -pix_fmt gbrp
    -colorspace rgb

    color_range
    color_range Description ffmpeg内の対応するデータ ffmpegのオプション例
    0 Studio swing AVCodecContext.color_range = AVCOL_RANGE_UNSPECIFIED
    AVCodecContext.color_range = AVCOL_RANGE_MPEG
    -color_range unspecified
    -color_range mpeg
    1 Full swing AVCodecContext.color_range = AVCOL_RANGE_JPEG -color_range jpeg



    あと謎だったオプションはAV1と一緒だったので分かりました。

    -tile-columns 6

    フレーム内を横に分割して並列エンコード/デコードする。
    2の指定した数値乗するのでこの例だと2^6 = 64分割する。
    ただ、設定してもイマイチ効果がよくわからない。
    コアが一杯あるCPUとかだと変わるのかも?


    -frame-parallel 1

    複数フレームを並列デコード出来るようにする。
    デフォルトでONなので特別指定する必要は無い。
    並列デコード出来るようにしている分他のフレームへの依存性が下がっているはずなので
    データ量的には増えるはずだが逆に僅かに減ってたりして謎。



    以上を元にffmpegでゲームをキャプチャした動画をVP9でエンコードする例。
    ffmpeg ^
    -loglevel verbose ^
    -i input.avi ^
    -vf "scale=out_color_matrix=bt709:out_range=full" ^
    -pix_fmt yuv444p ^
    -colorspace bt709 ^
    -color_range jpeg ^
    -f webm ^
    -c:v libvpx-vp9 ^
    -threads 4 ^
    -speed 1 ^
    -crf 30 ^
    -b:v 0 ^
    -c:a libopus ^
    -b:a 160k ^
    output.webm
  • FFmpegでVP9エンコード

    2017年09月21日 20時25分
    時々ゲームプレイを録画して残していまして、
    CODECをh.264、色空間はYUV420にしていました。

    ただ、ゲーム画面だと隣り合うピクセルの色が全然違ったり彩度の高いピクセルが多かったりで
    YUV420にすると全体的に色が薄く見えるのが不満でした。

    これを解決すべく色空間をYUV444に変更し
    ついでにCODECも高圧縮なVP9に変更しようというのが今回の話の始まり。


    今時高圧縮なCODECというとh.265かVP9で迷います。

    h.265はハードウェアエンコード対応が多く
    VP9はだいたいのブラウザで再生できるのがそれぞれの利点でしょうか。

    画質と圧縮率はだいたい同じくらいのようで
    私には良し悪しが判断出来ません。

    今回は大量にエンコードするわけではないので再生環境が多いVP9を採用することにしました。


    VP9のエンコーダというと、最初はlibvpxに付いているvpxencを使おうと思ったのですが
    録画したaviファイルをy4mに変換する必要があり
    y4mの変換にffmpegを使うことになったので、それならもう全部ffmpegでいいかとffmpegを採用。


    それでいざffmpegでエンコードしようとすると
    オプション大量すぎてわけわからんで苦労したので調べた情報を書いておこうと思います。


    私はffmpegについて詳しいわけではないので
    不確かなことを書くのもどうかとは思ったのですが
    あまりに情報が少なかったので全く無いよりはいいかと書いておくことにしました。

    調べる時のとっかかり程度の情報と思って見ていただきたい。


    参考情報




    CODEC


    動画CODECにVP9を指定。
    -c:v libvpx-vp9


    WebMの音声CODECはOpusまたはVorbisになりますが、後発のOpusを使えばいいと思います。(性能もいいらしい)
    音声CODECにOpusを指定。
    -c:a libopus


    コンテナは出力ファイルの拡張子から判断してくれるようですが、一応指定しておけば確実かなと思います。
    -f webm



    RGB → YUV444変換


    エンコーダに渡すピクセルの色空間指定はpix_fmtで行います。
    -pix_fmt yuv444p


    カラーマトリクスは全然別のオプションでこうです。
    -vf "scale=out_color_matrix=bt709:out_range=full"


    ffmpegのログを見ると、-vfでscaleを指定しない場合auto_scalerというのが挿入され、こいつが色空間の変換処理をしているようです。
    「-vf "scale=out_color_matrix=bt709:out_range=full"」のように明示的に指定してやるとauto_scalerが自動挿入されなくなり、指定したscalerが入ります。


    ビットレート


    VP9にはビットレートのモードが4つ。

    • Constant Quantizer (Q)
    • Constrained Quality (CQ)
    • Variable Bitrate (VBR)
    • Constant Bitrate (CBR)

    ストリーミングで配信するわけでもなく
    自分用にアーカイブするのが主な目的なので
    品質固定のConstant Quantizer (Q)を採用しました。

    Constant Quantizer (Q)の指定。
    -crf 30 -b:v 0


    crfは0-63の範囲で指定し、0に近づくほど画質がよくなりデータサイズが大きくなるので
    結果のファイルサイズと画質のバランスで決めることになると思います。

    思ったより数字を大きくしても画質は良いと感じました。
    目安としていくらか試した感じだと1080p 30fpsで30-35あたりが良いのではないかと。

    「-b:v 0」は必須です。というかこの指定がConstant Quantizer (Q)の意味。


    Opusのビットレートの指定は簡単。
    -b:a 160k

    Youtubeが160kでエンコードしているらしいのでその辺りが良さそうです。


    エンコード品質と速度


    -speed 1


    0-4の間で指定し、0に近い程エンコード速度が遅くなり画質が良くなる。
    基本的には0を指定したいところですが、ただでさえ遅いVP9のエンコードがさらに遅くなるので
    エンコード時間を考慮して決めることになります。


    カラーマトリクス


    YUVからRGBに戻す時のカラーマトリクスを指定します。
    RGB → YUVの変換で指定したものを指定すればいいと思うのですが
    なぜ3個もあるのかよくわかりません。

    ffmpegのログを見ると3つとも同じにしておけばひとまとめにして認識してくれているようなのでこれで合っている気がします。
    -colorspace bt709 -color_primaries bt709 -color_trc bt709



    デコード用オプション


    公式のエンコードガイドに、画面を分割して並列デコードできるようになるみたいなオプションがエンコード例に記載してあります。
    -tile-columns 6 -frame-parallel 1


    効果はイマイチわかりませんが今のところ特に害はないです。


    FFmpegでVP9エンコード 補足
  • libvpxのVisual Studio用ファイルをLinux上で作る

    2015年06月11日 20時38分
    libvpxをWindows用にビルドしたいけどMinGWをインストールするのが嫌でLinuxはある人向けという
    隙間産業的な記事。


    VP9のエンコードはそろそろ早くなったりしないかなあと思いつつlibvpxの中身を眺めていたら、
    なんか行けそうな気がしてやってみたら出来たのでメモっておきます。

    ちなみに私はlibvpxの中身を熟知しているわけではなく、
    部分部分をちょいちょいとつまんで解釈し
    なんとなく合ってそうというものであるとご理解ください。


    きっかけは、libvpxの中身を見ていたら「gen_msvs_sln.sh」とか「gen_msvs_vcxproj.sh」とかが有るのに気づいたことで、
    同時に「これをLinux上で動かしてslnとかvcxprojとか作れればそれを持ってきてVSでビルド出来るんじゃね?」と閃きました。

    これらのスクリプトはconfigureでtargetにvsを選択すると使われるようになっています。

    例えば、VS12で64bit用にビルドしたい場合はtargetにx86_64-win64-vs12を指定します
    # ./configure --target=x86_64-win64-vs12

    ※targetの一覧はconfigureのhelpに出てきます。


    これでmakeすると、指定したVS向けファイルが作成されます。

    configureの中のヘッダチェックとかも気になったのですが、主にtargetを確認する処理になっていて、
    実際そのヘッダが有るかないかとかは見てないので大丈夫そうです。


    細かい手順や注意点は以下になります。

    gitのcloneを取る
    # git clone https://chromium.googlesource.com/webm/libvpx
    # cd libvpx


    試した時のrevisionはこれでした
    # git rev-parse HEAD
    44afbbb72d8a5fa0528f8a571a5023a843d02f33


    作業用にまるっとコピーします
    # mkdir ../export
    # git archive --format=tar HEAD | tar -x -C ../export
    # cd ../export


    VSプロジェクトファイル内のincludeディレクトリ指定に、configureした時のLinux上のフルパスが現れないように書き換えます
    # sed -i -e 's/SRC_PATH_BARE=$source_path/SRC_PATH_BARE=./' build/make/configure.sh
    # sed -i -e 's/CFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT) -I$(SRC_PATH)/CFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT)/' -e 's/CXXFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT) -I$(SRC_PATH)/CXXFLAGS+=-I$(BUILD_PFX)$(BUILD_ROOT)/' build/make/Makefile


    vpxenc以外コンパイルしたくないんじゃあという場合は、examplesの対象をvpxencだけに書き換えます
    # sed -i -e 's/ALL_EXAMPLES    = $(UTILS) $(EXAMPLES)/ALL_EXAMPLES    = vpxenc.c/' examples.mk


    お好みのオプションを選択してconfigure
    # ./configure --log=no --target=x86_64-win64-vs12 --enable-static-msvcrt --disable-vp8 --disable-unit-tests

    vp9のdecoderも要らないのでOFFにしようと思ったのですが
    decoderが一個も無くなると一部ソースがコンパイルエラーになるのでやめました。

    makeするとVS用のプロジェクトファイルとかソリューションファイルが作成されます
    # make


    後はこのソースツリーをまるごとWindowsに持ってきてvpx.slnをVSで開けばビルドできます。
    ※yasmのインストールも必要です



    さて、VP9のエンコードは今どのくらいの速度なのでしょうか。
    1920 * 1080 FPS30の動画をエンコードしてみました。
    CPUはCore i7 3770です。

    オプションがまだ完全に把握出来ていないのですが、
    まずはおそらく最速の設定であろう「good cpu-used=8」を試しました
    vpxenc --output=output.webm --threads=4 --good --cpu-used=8 --passes=1 --codec=vp9 --webm --end-usage=cq --cq-level=10 --i420 --color-space=bt709 --width=1920 --height=1080 input.y4m


    思ったより早い!11FPSくらい出ました。
    そこそこ使えなくもない早さな気がします。
    でも、どうせなら遅くても品質を取りたいので、次は「best」を設定してみました。
    vpxenc --output=output.webm --threads=4 --best --passes=1 --codec=vp9 --webm --end-usage=cq --cq-level=10 --i420 --color-space=bt709 --width=1920 --height=1080 input.y4m


    結果は14FPMでした。
    FPSじゃないです。FPMです。
    最初表示がバグったかと思いました。
    こんな表示が用意してあるあたり開発側もまだ遅いと思っているのでしょう。

    品質についてはbestのエンコード終了を待てなかったので比較していません。