こんにちはHAVRMです.
ちょっとした動画編集とかでいちいち専用のソフトをインストールするのが面倒でいつもWSLでffmpegを使って加工しています.ただこの時気を付けないといけないのはffmpegのオプションを適切にしないと予期せぬ画像劣化が起きます.そのあたりの情報が様々なブログに散乱しているのでまとめようと思います.いつもは参考サイトをその時々に出していますが,今回はそれなりに多いので最後にまとめて書きます.文中の「[x]」のxが参考サイトの番号にあたります.
なおWSLを使っているのでGPU等を使っていないです.WSL2ではGPUを使えるみたいですが,処理時間にそこまで苦労していない(使っていない古いゲーミングに変換させるとか)のでCPUで処理しています.
画面録画(Windows Xbox Game キャプチャ や GeForce Experience 録画 等)の動画の圧縮
これらの画面録画の機能は前者であればWindows 10でゲームとして設定したもの,後者であればGeForceを搭載したPCでGeForceで描画しているもの(ソフトだけじゃないみたいで,自分はGPUで出力している画面自体もキャプチャできました...)の画面を録画できるものです.ただこれらの動画は圧縮をきちんとしていないせいか容量が莫大になることが多く(HDサイズで1時間8GBとか),保存が大変なことが多いです.そこでffmpegで同サイズほぼ同クオリティで変換することで容量の削減をしています.
実際のコマンドは以下の通りです
$ ffmpeg -y -i (変換元ファイル名) -max_muxing_queue_size 9999 -crf 15 -vf "scale=iw:ih" -vsync 1 (保存名)
実際に使っているコマンドなので少し無駄がある気もしますが,大事なのは次の3点です
- -max_muxing_queue_size 9999:なんかエラーが出ないようにするため[1]
- -crf 15 :ここが画質に大きく影響します[2]
- -vf "scale=iw:ih" :入力動画のサイズをそのまま指定
とくに「-crf」を指定しないと画質がそれなりに劣化します(あくまでそれなりです.気にしない人も多いと思います).また値を「15」としていますが,参考サイトにある通り「16」とかでも見分けはほぼつかないと思います.
上の画像はゲーム画面の一部を切り取っただけですが,拡大してもよくわからないと思います.右端のもの(CRFを指定していない)のほうが×の周りの円の線が細くなっているように見えるぐらいです.重ねると色味がわずかに変わっているのもわかりますが,気のせいかなのレベルです.
なおこの変換で容量は次のように変わります(この画像の動画で比較しています).
- オリジナル:449MB
- CRF=15 :144MB
- 指定なし :61MB
なので画質とどの程度圧縮したいかでこのCRFの値を変えればいいと思います.
コマ送り画像の作成(できるだけ非圧縮)
動画をコマ送りの画像に分解するときのコマンドです.よく紹介されているサイトの方法だと画質が劣化することが多いので記載します.
$ ffmpeg -i (変換元ファイル名) -qscale:v 1 -qmin 1 -qmax 1 -r (出力画像のフレームレート) -f image2 (出力名,%05d等のprintfと同様のコードが使用可能)
ここで大事なのはこの一連のオプションです.
- -qscale:v 1 -qmin 1 -qmax 1:画質指定[3]
参考サイトだと「15」で指定していますが,上のCRFと同様だと思い,特に非圧縮が欲しかったので最高値(数が小さいほど圧縮率が下がります)の「1」としています.
先ほどと同じ動画をqscale等のオプションを「1」にした場合と指定しなかった場合の比較ですが,これはわかりやすく差が出ます.
これも結局画像の容量が変わるのでそれ次第でいいところを決めることになるとは思います.
- 指定した場合 :188KB
- 指定しない場合:28KB
個人的にはコマ送り画像の場合すべてを保存するわけではなく,加工して使うことが多いので高画質に越したことはないと思っています...
スロー動画の処理
最近のスマホとかでは240fpsでスローモーションを取れるようになっています.ただこれらの画像をWindowsで再生しようとすると表示が乱れたり画面がフリーズしたりします.それもあってスローモーションの程度を決めた動画に出力すれば見やすくなりますし,どの程度高速なのかもわかります.
ただ,画像についてはFPS基準でいろいろいじれますが,音は難しいので結局0.5x単位になります.
まず音をきちんとするという意味で0.5倍速のスローモーションの出力[4][5]
$ ffmpeg -r (FPS,詳細は後程) -i (変換元ファイル名) -max_muxing_queue_size 9999 -crf 15 -af atempo=0.5 (出力名)
ここでは[4]の音声の低速化と[5]のフレームレート変換を合わせています.というのも[4]のやり方ではあくまで自分の記憶の中でですが,フレームレートを返還する過程でコマ画像をいくつか削除したと思います.[5]のやり方だとフレームレートを変更するだけなのでコマ画像の欠損は起きません.ただそのためにフレームレートを適切に指定する必要があります.動画のフレームレートは次のコマンドで取得できます[6].
$ ffmpeg -i (変換元ファイル名) 2>&1 | grep fps | sed -e "s/fps,.*//g" -e "s/.*,.//g" 240
例えば240FPSの動画を0.5倍速にする場合はフレームレートを120に指定すれば0.5倍速になります.この計算を自動ですると次のようになります(小数点以下の計算をできるようにしています[7],ここでは4倍遅くするフレームレートの計算です).
$ FPS=$(ffmpeg -i (変換元ファイル名) 2>&1 | grep fps | sed -e "s/fps,.*//g" -e "s/.*,.//g") $ echo "scale=2; $FPS / (スロー倍率,「4.0」等の小数点表示)" | bc 60
逆にあるフレームレートにしたときの倍率は次のように計算できます(30FPSに設定する場合です,小数点前の0を表示するようにしています[8])
$ echo "scale=2; (FPS,30とか) / $FPS" | bc | xargs printf %.2f 0.125
なお[4]のサイトでは「atempo」は何度でも重ねられるとしていましたが,自分は2回まで(すなわち0.25倍速)までしか繋げられませんでした...
参考サイト
- FFMPEG: Too many packets buffered for output stream 0:1 - Stack Overflow
- x264のcrf値はどれくらいが適切なのか? | もにっき
- 【ffmpeg】動画から特定フレームを画像で出力する方法 - ニコニコ動画研究所
- ffmpegを使って動画の再生速度を変えてみる - 脳内メモ++
- ffmpeg : set fps|Nobu|note
- ffmpegを使って動画のフレームレートを取得するには : 犬ターネット
- シェルスクリプトで小数演算する - Qiita
- [コマンド] bc(数値計算ソフト)の結果出力で、小数点前の0(ゼロ)を省略させないようにする方法