ドラッグ&ドロップで使えるビデオエンコード用スクリプトを作った(ffmpeg libx264)

github

エクスプローラーからビデオファイルをドラッグ&ドロップしてエンコードできるffmpeg用VBScriptを作りました。ファイル名に%や&^等がが入っていても動きます。Unicodeの文字😃👍🈙🈑🈔🈡などがあってもOKです。
エンコーダはlibx264を使います。出力解像度に合わせてcrf値を計算するので解像度の異なるビデオもまとめてエンコードできます。



まえがき

今までbatファイルを使っていたのですがファイル名に"%","&","^"があるとbatファイルが正しくファイルを受け取れないのが悩みでした。
これはbatファイルの引数の区切り文字が半角スペースだけではない事と、コマンドとして特殊な意味を持つ文字が現れるとその特殊な意味として解釈してしまうのが主な原因です。

以前はbatファイルで無理やり回避していました。
関連記事:ファイルエクスプローラからバッチファイルへドラッグ&ドロップして正しいファイル名を得る方法
厄介な文字が現れたら違う文字列へ置き換え、処理が必要になった直前に元へ戻すという事をしています。
しかしこの方法では不完全です。そもそも原因が仕様として明示されているわけではなくダメだったら回避策を考えるという行き当たりばったりでした。しかたなくファイル名の方を全角文字にして回避したり。
コマンドプロンプトなんてMS-DOS時代の名残なので仕方ありません。MS-DOS…今の人に通じるのだろうか?

Windows 10な時代なのでbatファイルを使わずPowerShellスクリプトを使えばすぐに解決!…と思ったらそうは行きません。PowerShellスクリプトはとても強力なスクリプトでWindowsのほとんどをいじれてしまいます。セキュリティがうるさい昨今、PowerShellスクリプトはコマンドを打たなければ実行できないのです。エクスプローラ画面でスクリプトをダブルクリックしてもファイルをドロップしても動きません。

そこで少し前の時代のスクリプトを使う事にします。VBScriptはbatファイルのように使えます。.NETコンポーネントが使えるので複雑な処理も数行書けばできてしまいます。似たものにJScriptがあります。
両方とも.NETが使えるのでVBScriptとJScript .NETの違いが良くわかりませんが、今時のネットワークを使った通信処理をしたい時はJScriptを、目の前のPCだけの処理をしたい時はVBScriptを使うと良さそうです。
Windows 10ではほとんど統合されているのでどちらで書いても互いの関数を呼び出せます。処理によって簡単に書けそうな方を使えば良いようです。

ちゃちゃっと書けばすぐにできるだろう・・・と思いきや、かなり複雑になってしまいました。

余談が長い…

WrapScriptforFFmpegX264

ffmpegでビデオファイルをエンコードするスクリプトです。エンコーダはlibx264のみ使います。

特徴

  • 複数ファイルをドラッグ&ドロップしてエンコードできます。
  • ファイル名にUnicode😃👍🈙🈑🈔🈡があっても動きます。
  • libx264でエンコードします。
  • 出力解像度でcrf値を決定するので元の解像度を気にせずエンコードできます。
  • 縦解像度を指定して好きなサイズへリサイズできます。
  • インターレースは解除しプログレッシブにします。
  • 24fpsへの変換時はテレシネ解除します。
  • 音声、字幕やチャプター情報は変換せずコピー出力します。

インストール

スクリプトはGitHubで公開しています。
SignalFlagZ/WrapScriptforFFmpegX264
https://github.com/SignalFlagZ/WrapScriptforFFmpegX264
"clone or download"の"Download Zip"でダウンロードしてください。
github_download
解凍し任意のフォルダへ置いてください。
別途ffmpeg.exeとffprobe.exeを入手しmain.wsfファイルのあるフォルダへ置いてください。
FFmpegは例えばZeranoe様よりダウンロードできます。最新バージョン(記事公開時Ver.4.0.2)を使います。Ver.3以上なら動くと思います。
Zeranoe https://ffmpeg.zeranoe.com/builds/
あるいはFFmpegを自分でビルドしてみてはどうでしょうか。
関連記事:Windows 10のUbuntuアプリ18.04でFFmpegをビルドする

使い方

"main.wsf"を使います。このファイルにビデオファイルをドロップするとエンコードが始まるでしょう。
コマンドラインで使う場合は"main.wsf"の引数にビデオファイルを指定してください。
出力ファイルは入力のビデオファイルと同じフォルダへ作られます。ファイル名は入力ファイルと同じで拡張子は"mkv"になります。出力ファイルと同じファイル名が既に存在する場合はmkvフォルダを作りそこへ出力します。そのmkvフォルダに同じファイル名がある時は上書きします。

例:
main.wsf arg1 [arg2 arg3 ...] [/Option1:value /Option2:value ...]

オプション

  • /Test:
    • エンコードせずffmpegのコマンドを表示、クリップボードへコピーする。
  • /Height:h
    • 縦解像度をhへリサイズする。
  • /Gop:
    • Gop長を指定する。
  • /Crf:
    • Crf値を指定する。
  • /Tune:
    • tune値を指定する。
  • /Preset:
    • preset値を指定する。defaultはmedium。
  • /A:
    • a値を指定する。defaultは12.85097209。
  • /B:
    • b値を指定する。defaultは-55.17605647。
  • /Ext:
    • 出力ファイルの拡張子、コンテナを指定する。defaultはmkv。
  • /Fps:
    • fpsを指定する。defaultは入力と同じ。
  • /Crop:
    • 43 or 43+ 4:3にクロップする。43+は43より1.1倍幅広。
  • /Detelecine:
    • テレシネ解除をする。
  • /Bsfa:
    • bitstream filterを指定する。

コマンドの例

720p 23.97fpsへエンコード
main.wsf /Height:720 /Fps:24000/1001 video1.mp4

4:3にクロップ
main.wsf /Crop:43 video1.mp4

エンコードせずffmpegのコマンドを表示
main.wsf /Test /Height:720 /Fps:24000/1001 video1.mp4

crf値の計算式

x264のエンコード品質はcrf値で指定します。crf値は映像の解像度により変える方が良いです。高解像度の時はcrfに大きな値を、低解像度の時はcrfに小さな値を指定します。
より正確にはモニタやテレビで表示した時の画素サイズによりcrf値を変えると良いです。大きなテレビで見るなら小さなcrf値に、スマホなら大きなcrf値にします。

このスクリプトでは次の式でcrf値を決めています。
crf = a * log(width * height) / log(10) + b
widthとheightはエンコード後の映像の幅と高さです。
aとbは先ほどのオプションで指定できます。logの底を10としていますがExcelのlog関数に合わせたためです。
初期値のaとbでは次のようなcrf値になります。

/A: /B:
Width Height Target Crf Calculated Crf
1920 1080 0
720 480 0
3840 2160
1440 1080
1280 720
960 720
640 480

フルHDで26、SDで16となるようaとbを決定しています。 crfの目標値を入力すると自動的にa値とb値が更新されます。
【自動計算する表に入れ替えました。
関連記事:ビデオのエンコード解像度でx264/x265のcrf値を決めてみる

オプションを設定したショートカットを作る

main.wsfへのドラッグ&ドロップではオプションを設定できません。こんな時はmain.wsfのショートカットを作りオプションを指定しておきます。
ですが、ここに罠がありました。ただショートカットを作っただけではビデオファイルをドロップした時にオプションが無視されてしまいます。せっかく作ったのに使えない…。

回避するにはショートカットのプロパティを表示して"リンク先"にcscriptを追加します。

shortcut
このショートカットへD&Dすればオプションも認識するようになります。
オプション指定を変えたショートカットを複数作っておくと便利です。

スクリプトの説明

スクリプトは4つのファイルに分かれています。
  • main.wsf
    • 引数を処理してエンコード用関数へ渡す
  • ffmpeg.class
    • ffmpeg関連の処理をする
  • stream.class
    • ffprobeで入力ビデオファイルの情報を取得する
  • exec.vbs
    • コマンド実行用関数など
main.wsfはユーザーインターフェースを担当します。引数からビデオファイルをひとつづつエンコード用関数へ送ります。
ffmpeg.classはffmpeg用のパラメータや関数をまとめています。
stream.classはエンコードするビデオファイルの情報をffprobeで取得します。今のところ必要最低限の情報しか取得していません。JScriptにしてjsonで扱う方が簡単だったかな?
exec.vbsは共用できそうな関数をまとめています。

VBScriptはクリップボードを使うことができません。クリップボードへ文字列を送るためWindowsに最初から入っている実行ファイル"mshta.exe"を使ってjavascriptを動かしています。

エンコード用プロセスは通常以下の優先度で動きます。エンコード中に他の作業を妨げる事はないでしょう。

エラー処理は最低限のことしかしていません。ffmpeg.exeやffprobe.exeがmain.wsfと同じフォルダにないとエラーを出すでしょう。
ffmpegがエラーで終了してもエラーメッセージが見れません。/Testオプションでffmpegコマンドがクリップボードへコピーされるので、コマンドプロンプトへペーストして実行しエラーを確認しましょう。

あとがき

1日でできるだろうと軽い気持ちでスクリプトを作り始めました。作り始めるとソースビデオの映像サイズの違いやインターレースかプログレッシブかの違いでffmpegのオプション指定を微妙に変えたスクリプトファイルをたくさん作る事になると気づきました。今までのbatファイルはそんな感じで似たbatファイルがたくさんあります。でも、これではスクリプトのメンテナンス性が悪すぎます。
自由度は下がるが一つのスクリプトでいろいろなビデオファイルに対応できるように変更を加えていったらスクリプトが肥大化して行きました。
結局1週間かかることになりました。
これでUnicode文字が問題なく使えるはずです。

Windows Script Fileは便利ですね。VBScriptにはなかった外部ファイルのインポート機能を補ったり、JScriptと共存できたり、Windowsの歴史で育まれたスクリプトを無理やりつなぎ合わせる感じです。VBScriptもJScriptも中身は同じみたいなものですからね。

PowerShellスクリプトにもう少し自由が欲しいです。システム管理者が使うスクリプトになっているのでユーザーが使うスクリプトではありません。Windowsは企業が使う道具になってしまいパーソナルなコンピュータからどんどん離れて行くのが残念です。

コメント

最近のコメント

Threaded Recent Comments will be here.