4.FFmpeg自動エンコード+Googleフォトに無限保存編 - TS抜き環境構築
滅茶苦茶ザックリ言うと、EDCBで録画した後、ffmpegで自動エンコードして、FHD10GBまで無料無制限のGoogleフォト(ドライブ)に無限に保存しちゃおうぜっていう話。
TSファイル、バッチやPowerShell、ffmpeg、Googleフォトの厄介な仕様に対応させ、放置しても完全自動で動いてくれるようなスクリプトを書いた。
あと画質の悪いデジタル放送を出来る限り綺麗に、現実的な速さで、圧縮率の高いエンコードを行う為にかなり試行錯誤した。エンコード品質にお悩みの方はffmpegの引数だけでも見ていってくれると嬉しい。
目次 見出しにジャンプ
更新履歴 見出しにジャンプ
録画後スクリプトの更新情報(詳細はgist参照) | |
---|---|
2017-04-01 |
|
2017-05-01 |
|
2017-06-01 |
|
2017-07-01 |
|
2017-08-01 |
|
2017-09-01 |
|
2017-10-01 |
|
2017-11-01 |
|
2018-01-01 |
|
2018-02-01 |
|
2018-03-01 |
|
2018-04-01 |
|
2018-05-01 |
|
2018-06-01 |
|
2018-07-01 |
|
2018-08-01 |
|
2018-09-01 |
|
機能比較 見出しにジャンプ
表を見ると分かるように、PostRecEnd.ps1を推します。設定が一箇所で出来るから1番導入が簡単だし、ps1以外にはPID判別が無いので偶にエンコード失敗しちゃうんだよね。
機能 | PostRecEnd.batテスト構成 | PostRecEnd.bat最小構成 | PostRecEnd.batフル構成 | PostRecEnd.ps1(推奨) | |
---|---|---|---|---|---|
主な用途 | 録画後エンコードのテスト? | 録画後エンコード、ローカルに保存 | 録画後エンコード、Googleフォトに保存 | ||
放置 | X | エンコードの放置:△ ストレージ放置:X |
エンコードの放置:△ ストレージ放置:O |
O | |
ログオンせずにEpgTimerSrv.exeをサービスで動かす |
X:ユーザ環境変数使用、QSVを使用 | X:QSVを使用 | |||
タスクトレイアイコン、バルーンチップ マウスオーバー時にファイル名を表示、エンコード終了・失敗時にファイル名とts、mp4それぞれのファイルサイズを表示 |
X | X | X | O | |
ユーザ設定 環境設定、機能のオンオフ、閾値の設定時にプログラム本体を直接弄る必要が無い |
X | X | X | O | |
ログ出力 不具合の原因を探しやすい |
X | X | O | O | |
ts、mp4ファイル等の削除 放っておいてもストレージが満杯にならない為に、フォルダサイズを一定(100GB等)に丸め込むように古いファイルから削除する |
X | X | O:tsフォルダ最大サイズに合わせてts、ts.program.txt、ts.err、mp4を削除(設定が複雑、ファイルサイズ計算が不正確) | O:tsフォルダ最大サイズに合わせてts、ts.program.txt、ts.err、mp4フォルダ最大サイズに合わせてmp4を削除 | |
jpg出力 好みの番組を自動予約キーワードで判別し、連番jpgでも保存する |
X | X | O:キーワードにスペース等の記号が含まれる場合に非対応 | O | |
ffmpegでtsをmp4にエンコード |
|||||
デュアルモノの判別 デュアルモノ音声の場合正しく分離する為に番組情報ファイルで音声引数を条件分岐 |
X:手動 | O | O | O | |
PIDの判別 音声・映像チャンネルの切替でffmpegの出力が音ズレ・失敗しない為にffmpegで必要なPIDのみを取得 |
X:手動 | X:ffmpegの引数である程度は判別 | X:ffmpegの引数である程度は判別 | O | |
QSV処理失敗の回復 FFmpegのQSVエラーによるエンコ失敗からの回復の為、ffmpegの終了コードが1の間50回までループ |
X:手動 | O | O | O | |
Googleフォトにアップロード |
tsファイルサイズによる品質選択 mp4が出来るだけ10GB以内になるように大きなtsでは品質を落とす |
X | X | O:ファイルサイズ計算が不正確 | O | mp4ファイルサイズ判別 品質を落としても10GBに収まらなかったものはうp出来ない為、tsやmp4等が削除されないよう隔離 |
X | X | O:ファイルサイズ計算が不正確 | O |
ツイート警告機能 録画失敗、エンコード失敗等でうp出来なかった場合にツイートで警告(別記事参照) |
X | X | O | O | |
Discord警告機能 録画失敗、エンコード失敗等でうp出来なかった場合にDiscordのWebhookで警告 |
X | X | X | O |
※ウォーターマークが無い番組への対応が難しそうなので、今のところCMカット機能は実装していない。
※実装はしていないが、ffmpegでロゴ消しする方法についてはここを参照すると良いcf.。
PostRecEnd.batテスト構成 見出しにジャンプ
録画後エンコードのテストに使います。これだけでは自動処理としてマトモに動きません。
じゃあ後のやつはなんでこんなに長いの?エンコするだけでしょ?と思うかもしれませんが、安定した動作やログ取り、ファイルの削除の自動化を含めた機能があるためです。以下のような1行のffmpegコマンドでもエンコード自体は可能ですが、完全自動化は出来ません。
rem _EDCBX_DIRECT_
ffmpeg -y -hide_banner -nostats -fflags +discardcorrupt -i "%FilePath%" -c:a aac -vf yadif=0:-1:1,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0 -global_quality 27 -c:v h264_qsv -preset:v veryslow -g 300 -bf 16 -refs 9 -b_strategy 1 -look_ahead 1 -look_ahead_downsampling off -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 -map 0:p:%SID10%:0 -map 0:p:%SID10%:1 -movflags +faststart "出力フォルダのパス\%FileName%.mp4"
pause
PostRecEnd.bat最小構成 見出しにジャンプ
PostRecEnd.batフル構成 見出しにジャンプ
PostRecEnd.ps1 見出しにジャンプ
事前準備 見出しにジャンプ
・特にQSVを使うとマザボのチップセットがかなり熱くなるので、ファンやヒートシンクを増設したりグリスを塗り替えるなどしてしっかり冷やして下さい(cpu直結のストレージなら熱くならないかも?)。
・基本的にPCを起動しっぱなしにすることが前提。スリープや休止状態、サービスでは動作しませんがロック中は動作します。
EpgDataCap_Bonの設定 見出しにジャンプ
EpgDataCap_Bonで録画している場合はこちらを設定すること。
・現在のサービスのみ保存する:チェック
TVTestの設定 見出しにジャンプ
TVTest.exeで録画している場合はこちらを設定すること。
・全サービスを処理対象とする:チェックを外す
EDCBの設定 見出しにジャンプ
EDCBで以下の設定がされている環境で正常に動作します。
・録画情報保存フォルダを指定しない
スクリプト内で場所が$env:FilePath(録画フォルダ)になっているため。もし要望があれば設定項目に加えます。
・番組情報を出力する
デュアルモノ判別ルーチンで使用。D&Dで処理するバッチ等でもServiceIDを取得する為に使用。
・不要になったドロップログを出力する
(ps1のみ)PID判別ルーチンで使用。
・録画終了後のデフォルト動作:何もしない
Backup and Syncを動かすため。
エンコして終わりならスリープしても大丈夫。
・録画後動作の抑制条件なし
自動エンコを録画中も実行しないと詰まります。
・xtne6f版recname_macro.dllで半角リネーム
半角リネーム(ZtoH)して全角でffmpegがエラーを吐かないようにする。
$SubTitle2$
を使う場合はHead文字数
を使用し、意図しない長いサブタイトルがヒットし、長いファイル名になってうp出来なくなる等のエラーを避ける。
例:
$SDYY$$SDMM$$SDDD$_$ZtoH(Title)$$Head10~(ZtoH(SubTitle2))$.ts
・EpgTimerSrvをサービス登録しない
WindowsサービスからffmpegでQSVを使用しようと試みるとFailed to create Direct3D device
エラーが出るので、EpgTimerSrvをサービス登録しない方法で環境構築cf.する必要がある。
Backup and Syncのインストール 見出しにジャンプ
https://photos.google.com/apps からBackup and SyncをDLしてインストール。
既に(別アカウント等で)使用している場合は、メニューから"新しいアカウントを追加"すると多重起動できる。
以前のアップローダを使っていた場合は勝手に置き換わる形になる)。
以下の設定をすることでローカルから削除してもGoogleフォトに残る旧Google Photos Uploaderのような挙動にできる。
・"Backup and Sync用フォルダ"のみを指定
・"高画質(無料、容量無制限)"を選択
・"写真と動画をgoogleフォトにアップロード":チェックしなくてもドライブにはうp(表示)されるので必要はありません(チェックすると旧Google Photos Uploaderと同じ挙動、当たり前だけど)。
・削除するアイテム:他の場所からアイテムを削除しない
・マイドライブをパソコンに同期:チェックを外す
・共有フォルダからアイテムを削除する際に警告を表示する:チェックを外す
・システム起動時にバックアップと同期を開く:チェック
※家族と共有する場合は、家族用Googleアカウントでログインするか、"Googleフォト"フォルダとフォルダ分け後のフォルダを家族との共有フォルダにすると良い。ただし他人とリンクを共有したらOUT。
※April 2018 Update等のWindowsの大きなアップデートの後、ログイン・設定が初期化されるので注意
ffmpegのインストール 見出しにジャンプ
1.https://www.ffmpeg.org/download.html
2.自分のOSに合ったものを選ぶ。今回はWindows Buildsを選択。
3.Versionは安定版、ArchitectureはOSのアーキテクチャに合わせ、LinkingはShared(dll)を選択すると良い。
・Version:開発版(20170605-4705edb等)/安定版(4.0等)(3.4.xのみ-init_hw_device qsv:hw
が必要なので非推奨cf.)
・Architecture:64-bit/32-bit
・Linking:Static(exe版)/Shared(DLL版)/Dev(ソース)
4.Download FFmpegを押下してDLして解凍。
5.binフォルダの中身が必要ファイルとなる。管理者権限が必要ない、半角英数字の好みのディレクトリに配置(C:\DTV\ffmpegとか)。
6.ffmpegを環境変数Pathに設定する。
毎回"C:\bin\ffmpeg\ffmpeg.exe"のようにffmpegのフルパスを入力するのを省く為。
しない場合はバッチ内のffmpeg
をffmpegのフルパスに置き換える必要がある。
使用方法 見出しにジャンプ
以下の手順を終えたら動くはずなので、とりあえず適当に録画してみて動作確認すること。
共通 見出しにジャンプ
上記スクリプトを保存 見出しにジャンプ
選んだスクリプトをコピーし、テキストエディタ等にペーストし、拡張子.bat
又は.ps1
で保存。文字コードはShift-JIS(ANSI)。
※好みの名前.batや、_EDCBX_DIRECT_
を使用しない場合は環境変数の囲い文字を%
から$
に置き換える(例:%FilePath%
->$FilePath$
)。
※PostRecEnd.bat、PostRecEnd.ps1が両方存在する場合batが優先される。
※PostRecEnd.bat/ps1が存在する状態で録画後実行.batを登録した場合、同時に実行される。
ファイル名 | 好みの名前.bat | PostHoge.bat/ps1 |
---|---|---|
実行条件 | 個別に録画後実行batにパスを設定 ※自動予約登録で既に予約一覧にあるものにもバッチのパスを適用させるには 1.自動予約登録ウィンドウ内の予約を一旦削除 2.batのパスを通す 3."自動予約登録条件を変更"を押下して再度予約一覧に追加 |
EpgTimerSrv.exeと同じディレクトリに設置 |
実行タイミング | 録画終了時 | PostAddReserve:予約追加 PostChgReserve:予約変更 PostRecStart:録画開始 PostRecEnd:録画終了 PostNotify:通知cf. |
途中から録画、録画中にキャンセルした場合 | EpgTimerSrv.iniのSETにErrEndBatRun=1を追加すれば実行cf. | 実行 |
番組によって処理を変えるには | 別のバッチを録画後実行bat登録する | バッチ内の処理で条件分岐する |
環境変数 | $環境変数$ | $環境変数$ _EDCBX_DIRECT_で%環境変数% |
ffmpegの引数 見出しにジャンプ
・CPUの種類や世代によって使用できないオプションがあるので、環境に合わせる必要がある。
・引数次第で品質や圧縮率がかなり変わる。
/blog/ffmpeg
環境別の設定 見出しにジャンプ
・h264_qsv LA-ICQ
IntelのQSVに対応したHaswel以降のCPUであれば、LA-ICQ(先行探索固定品質)が使用できる。
グラボを付けている場合Windows8以降のOSでないと内蔵GPUを併用できず、QSVが使用できないので注意。
LA-ICQは少ないビットレートでも動きのあるシーンでもハードエンコの割にはよくこなしてくれるのが特徴だ。
… -global_quality %quality% -c:v h264_qsv -preset:v veryslow -look_ahead 1 -look_ahead_downsampling off -pix_fmt nv12 …
・h264_qsv ICQ
QSVが使用できるIvyBridgeのCPUの場合はLA-ICQが使えないのでICQ(固定品質)を使用すると良い。
LA-ICQと同じ品質を求める場合には値を小さくしてビットレートやファイルサイズを大きくする必要がある(27->25等)。
… -global_quality %quality% -c:v h264_qsv -preset:v veryslow -look_ahead 0 -pix_fmt nv12 …
・h264_qsv CQP
QSVが使用できるIvyBridgeより古いCPUの場合はCQP(固定量子化料)を使用すると良い。
多くビットレートを割り当てる場合はICQの代わりにCQPを使うのもアリだと思う。
LA-ICQ、ICQと同じ品質を求める場合には値を小さくしてビットレートやファイルサイズを大きくする必要がある(27->24
等)。
… -c:v h264_qsv -q:v %quality% -lookahead 0 -pix_fmt nv12 …
・hevc_qsv ICQ
QSVが使用できるSkyLake以降のCPUであれば、QSVでHEVCを使用できる。
GoogleフォトがHEVCに対応したので、一応色々試してみて使えるオプションだけ残した。以下の例はICQ。
私のSkyLake(i5-6500)ですらスペック不足でよくハングアップ(品質を落とせば一応動く)してタスクキルする羽目になる。
2倍くらいエンコ時間がかかるが、hwvc_qsv ICQ 1080p よりh264_qsv LA-ICQ 720pの方が綺麗なので現時点では使えない。
… -global_quality %quality% -c:v hevc_qsv -load_plugin hevc_hw -preset:v veryslow -g 300 -bf 16 -pix_fmt nv12 …
・h264_nvenc
NVEncが使用できる環境であること。CQP。
ffmpeg … -c:v mpeg2_cuvid -deint adaptive -i "%FilePath%" -c:v h264_nvenc … -preset slow -rc constqp -qmin 20 -qmax 26 -pix_fmt yuv420p …
・libx264
その他のCPUはx264でソフトウェアエンコードするのが良い。
veryfastが現実的なエンコ時間且つ圧縮率とのバランスも良いと思われる。
QSVより多くのオプションが使えるので適切なオプションと組み合わせれば高品質な結果が得られる筈だ。割愛。
… -c:v libx264 …-crf %quality% … -preset:v veryfast … -pix_fmt yuv420p …
エンコード品質・時間について 見出しにジャンプ
品質(圧縮率)と処理時間は基本的にトレードオフです。
処理時間の比較 |
---|
-vf pp=fa<pp=ac<hqdn3d |
-vf yadif=0:-1:1<bwdif=0:-1:1 |
-vf scale=1280:720<scale=1280:720:flags=lanczos<scale=1280:720:flags=lanczos+accurate_rnd |
-preset:v veryfast<faster<fast<medium<slow<slower<veryslow |
ビデオフィルタは前から適用される為、デインターレースは最初に行う。リサイズしたものをデノイズするか、デノイズしたものをリサイズするかでも品質や処理時間が大分変わってきます。
遅いけど綺麗な例:
… -vf yadif=0:-1:1,hqdn3d=4.0,scale=1280:720:flags=lanczos+accurate_rnd,unsharp=3:3:0.5:3:3:0.5:0 … -preset:v veryslow …
ちょっと速くなる代わりに品質がちょっと落ちる例:
… -vf yadif=0:-1:1,scale=1280:720,hqdn3d=4.0,unsharp=3:3:0.5:3:3:0.5:0 … -preset:v veryslow …
そこそこ速くなる代わりに品質がそこそこ落ちる例:
… -vf yadif=0:-1:1,scale=1280:720,pp=ac … -preset:v medium …
かなり速くなる代わりに品質がかなり落ちる例:
… -vf yadif=0:-1:1,scale=1280:720,pp=fa … -preset:v veryfast …
高品質&高速&低サイズなffmpeg引数になっている。
・yadifデインターレースを行います。同時にモスキートノイズを軽減する為にbwdif
ではなくyadif
を使用し、高速化しています。
・リサイズ時にぼやけた輪郭をシャープにする処理により、実写、アニメ共にリサイズによる品質低下を極力抑えます。下手な1080pよりも綺麗です。
・デフォルトではh264_qsvのLA-ICQ(先行探索固定品質)を使用し、ハードウェアエンコードの速さを活かしつつ高品質高圧縮を実現しています。
※アニメの24fps化は行いません。処理負荷に対する効果が小さいのと、ジャンルでの判別では30fps制作アニメの除外が難しい為です。
bat 見出しにジャンプ
環境変数設定 見出しにジャンプ
管理者権限が必要ないディレクトリ(program files等以外)に、4つの空フォルダ(ログ、一時的にmp4を吐き出す、backup and sync用、エラー)を作成(名前は半角英数でお好み)し、環境変数設定ルーチンでそれぞれのパスに書き換える。
"
は環境変数使用時に補完されるので付けないで下さい(付けるならset "hoge=hoo"
みたいに)。
ログフォルダのパスはログ出力機能が不要ならば指定しなくて良い。
連番jpgを出力するフォルダ用のディレクトリのパスは自動予約キーワードによる一部番組の連番jpg出力機能が不要であれば指定しなくて良い。
tweet.rb~のパス、SSL証明書のパスはツイート機能が不要ならば指定しなくて良い。
rem ====================環境変数設定====================
rem ログフォルダのパス
set "log_path=C:\DTV\EncLog"
rem 連番jpgを出力するフォルダ用のディレクトリのパス
set "jpg_path=C:\Users\Shibanyan\Desktop\TVTest"
rem 一時的にmp4を吐き出すフォルダのパス
set "tmp_folder_path=C:\DTV\MP4"
rem backup and sync用フォルダのパス
set "bas_folder_path=C:\DTV\backupandsync"
rem エンコしたファイルが10GBより大きい、処理に50回失敗した場合にts、ts.program.txt、mp4を退避するフォルダのパス
set "err_folder_path=C:\Users\Shibanyan\Desktop"
rem tweet.rb、tweet_postrecend.txtのあるディレクトリのパス
set "tweet_bin_path=C:\DTV\EDCB"
rem SSL証明書のパス
set "ssl_cert_file=C:\DTV\EDCB\cacert.pem"
ログ 見出しにジャンプ
ログ出力機能、以前のログを確認しエラーがあればツイートで警告する機能、ログ自動削除機能を使用しない場合は、ログ出力ルーチンを削除するだけで良い。
jpg出力 見出しにジャンプ
自動予約キーワードによる一部番組の連番jpg出力機能です。findstr "ポプテピピック フランキス BEATLESS"
のようにスペースで区切って下さい。不要であればルーチンごと削除するか、findstr ""
とする。
ファイルの削除機能の設定 見出しにジャンプ
※削除されたファイルは元に戻りません。正しく動作することを確認してから使用して下さい。
フォルダサイズを一定に保つファイルの削除 見出しにジャンプ
folder_max=
に録画フォルダの最大合計サイズを指定。
こちらの機能がデフォルトです。録画フォルダの合計サイズが一定サイズ(100GB)より大きくなったら古い方からtsファイル、更に同じ名前のts.program.txt、mp4ファイルを一つずつ削除します。
TSファイルのサイズはまちまちなので、例えば映画や音楽番組等が連続した場合に"録画ファイルの数"を一定に保つようにするとフォルダサイズが膨れ上がりストレージが満杯で録画できない事態に陥る場合があるため、"録画フォルダのサイズ"を一定に保つこちらをオススメする。残されるファイル数は変動する。
ファイル数を一定に保つファイルの削除 見出しにジャンプ
del_count=
に録画フォルダに一定数残すTSファイルの数を指定する。
tsファイルを一定数(デフォルト:20)残して、古い方からあぶれたTSファイル、更に同じ名前のts.program.txt、mp4ファイルを削除します。
こちらを使用する場合は「フォルダサイズを一定に保つファイルの削除」ルーチンに上書きして下さい。
EDCB側に同じような機能があるので存在意義なし。
rem ====================ファイル数を一定に保つファイルの削除====================
rem tsファイルを一定数残す数を指定
set del_count=20
rem dirで録画フォルダのtsファイルをファイル名のみ日付順で表示し、新しい方からdel_count個飛ばし、あぶれたファイルを削除する
for /f "skip=%del_count% delims=" %%a in ('dir "%FolderPath%\*.ts" /b /o-d') do (
rem ts削除
del "%FolderPath%\%%~na.ts"
rem 同名のts.program.txt削除
del "%FolderPath%\%%~na.ts.program.txt"
rem 同名のmp4削除
del "%bas_folder_path%\%%~na.mp4"
)
ファイル日時を一定に保つファイルの削除 見出しにジャンプ
del_day=
に何日以上前のファイルを削除するかを指定する。
tsファイルを一定数(デフォルト:20)残して、古い方からあぶれたTSファイル、更に同じ名前のts.program.txt、mp4ファイルを削除します。
こちらを使用する場合は「フォルダサイズを一定に保つファイルの削除」ルーチンに上書きして下さい。
※forfiles
はVista以降
rem ====================ファイル日時を一定に保つファイルの削除====================
rem 何日以上前のファイルを削除するか指定
set del_day=5
rem del_day日以上前のts、ts.program.txtを削除
forfiles /p "%FolderPath%" /d -%del_day% /m "*.*" /c "cmd /c del @file"
rem del_day日以上前のmp4を削除
forfiles /p "%bas_folder_path%" /d -%del_day% /m "*.mp4" /c "cmd /c del @file"
ツイート警告機能 見出しにジャンプ
Twitter連携についてはcf.。必要ならコメントrem
を外す。
こんな感じ
ERROR:180315_魔法使いの嫁 第22話「As you sow, so shall you reap.」.tsと関連ファイルを退避しました。ログを確認して下さい。
— shibanyan_edcb (@shibanyan_edcb) 2018年3月14日
ps1 見出しにジャンプ
####ユーザ設定####
の項目を自分の環境、用途に合わせるだけで動くようになっています。
#--------------------プロセス--------------------
#tsからmp4、jpg、waifu2x等の処理に使用される
#プロセス優先度 (Normal,Idle,High,RealTime,BelowNormal,AboveNormal) Process.PriorityClass参照
$Priority='BelowNormal'
#使用する論理コアの指定 コア5(10000)~12(100000000000)を使用=0000111111110000(2進)=4080(10進)=0x0FF0(16進) Process.ProcessorAffinity参照
$Affinity='0x0FF0'
#ffmpeg.exe、ffprobe.exeがあるディレクトリ
$ffpath='C:\DTV\ffmpeg'
tsをmp4にエンコする際、jpg出力する際、waifu2x等のプロセスを起動する際に使用するプロセスの優先度、コア指定等を行います。
プロセス優先度はProcess.PriorityClassを参照し設定すること。ただし、HighとRealTimeは非推奨。
プロセッサ親和性はProcess.ProcessorAffinity参照し設定すること。録画機兼用等で別プロセスが動いているような環境であれば、例えば12論理コアCPUの場合、5~12の8コアに制限するなどしておくと良い。
#--------------------ログ--------------------
#0=無効、1=有効
$log_toggle=1
#ログ出力ディレクトリ
$log_path='C:\DTV\EncLog'
#ログを残す数を指定
$logcnt_max=1000
ログ出力機能が不要な場合は0
にして下さい(非推奨)。この場合、$log_toggleに関わらず、設定項目にtoggleが付いている場合は、例えば$log_pathや$logcnt_max等の詳細設定は残したままでも影響はありません。
$log_path
にtsファイルと同じ名前のログファイルが出力されるようになります。管理者権限が不要なディレクトリにフォルダを作成(こういったものにはスペースや全角文字が含まれないことを推奨)し、パスをシングルクォート'
で囲って記述して下さい。
$logcnt_max
で指定した数だけログを残します。あぶれた場合は古いものから削除します。
#--------------------tsの自動削除--------------------
#0=無効(tsをローカルに残す)、1=有効(tsを自動削除)
$ts_del_toggle=1
#録画フォルダの上限 超過した場合、toggle=0:容量警告(Twitter、Discord) 1:削除
$ts_folder_max=200GB
tsファイル削除の機能が不要な場合は0
にして下さい。ファイルを完全に削除する機能なので注意してください。
$foldersize_max
でTSファイルが入っている録画フォルダの上限サイズを指定します。
100GB
と指定すると録画フォルダが100GBを超えた場合のみ、$ts_del_toggleが
・0の場合は、Discord、Twitterで警告します(警告機能の設定項目は後述、一括で無効にすることも可)。
・1の場合は、100GB以下になるまでTSファイル、同名のts.program.txt、ts.errを削除します。
1TB
とかでも大丈夫です。8EB
なんかも多分おkです。
#--------------------mp4の自動削除--------------------
#0=無効(mp4をローカルに残す)、1=有効(mp4を自動削除)
$mp4_del_toggle=1
#mp4用フォルダの上限 超過した場合、toggle=0:容量警告(Twitter、Discord) 1:削除
$mp4_folder_max=50GB
mp4ファイル削除の機能が不要な場合は0
にして下さい。後は同様です。
$mp4_folder_max
は10GB
よりも大きく、出来れば余裕を持たせてください。10GB近いサイズのmp4、又はその前後のmp4がアップロードされる前に削除される可能性があります。
#--------------------jpg出力--------------------
#0=無効、1=有効
$jpg_toggle=1
#連番jpgを出力するフォルダ用のディレクトリ
$jpg_path='C:\Users\sbn\Desktop\TVTest'
#jpg出力したい自動予約キーワード(全てjpg出力したい場合は$jpg_addkey=''のようにして下さい)
$jpg_addkey='とある魔術の禁書目録|アリシゼーション'
#自動予約キーワードに引っ掛かった場合に実行するコード 使用可能:$scale(横が1440pxの場合のみ",scale=1920:1080"が格納される、画像にはSARとか無いので)
function arg_jpg {
#連番jpg出力の例
New-Item "${jpg_path}\${env:FileName}" -ItemType Directory
$script:file="${ffpath}\ffmpeg.exe"
$script:arg="-y -hide_banner -nostats -an -skip_frame nokey -i `"${env:FilePath}`" -vf bwdif=0:-1:1,pp=ac,hqdn3d=2.0${scale} -f image2 -q:v 0 -vsync 0 `"$jpg_path\$env:FileName\%05d.png`""
Invoke-Process
#連番jpg出力したものをwaifu2xで上書きする例
$script:file="C:\DTV\waifu2x-caffe\waifu2x-caffe-cui.exe"
$script:arg="-m noise_scale -s 2 -n 3 -p cpu --model_dir models/upconv_7_photo -i `"$jpg_path\$env:FileName\*.png`" -o `"$jpg_path\$env:FileName\*.jpg`""
Invoke-Process
Remove-Item -LiteralPath "$jpg_path\$env:FileName\*.png" -ErrorAction SilentlyContinue
#tsを保持用ディレクトリにコピーする例
Copy-Item -LiteralPath "${env:FilePath}" "D:\tsfiles" -ErrorAction SilentlyContinue
}
$jpg_addkey
でjpg出力等をしたいお気に入りの番組の自動予約キーワードを指定します。複数指定時は|
で区切り、全ての番組をjpg出力する場合は$jpg_addkey=''
にします。この条件にマッチした場合のみ、以下の内容が実行される仕様です。
ユーザが柔軟に設定できるよう、function arg_jpg
の中にコードを適宜書くような形にしました。arg_jpg関数内の不要なコードは<##>
で囲ってコメントするか、削除して下さい。
New-Item "${jpg_path}\${env:FileName}" -ItemType Directory
によって、$jpg_path
内にtsファイル名のフォルダが生成され、そこに連番jpgが出力されます。
外部プロセスを実行するには、$script:file
には実行ファイル、$script:arg
にはその引数を指定し、最後にInvoke-Process
でプロセスを呼び出します。
Remove-Item -LiteralPath "$jpg_path\$env:FileName\*.png"
でwaifu2xする前のpngを削除します。ffmpegとwaifu2xの出力が共にjpg、pngのように同じ場合は上書きされるので不要、というよりffmpegの出力がpng、waifu2xの出力がjpgの場合でなければ適切に動作しないので消すこと。
Copy-Item -LiteralPath "${env:FilePath}" "D:\tsfiles"
でtsを"D:\tsfiles"
にコピーします。
#--------------------tsファイルサイズ判別--------------------
#通常品質(LA-ICQ:29,x265:25)
$quality_normal=28
#0=無効(通常品質のみ使用)、1=有効(通常・低品質を閾値を元に切り替える)
$tssize_toggle=1
#閾値
$tssize_max=20GB
#低品質(LA-ICQ:31,x265:27)
$quality_low=30
通常は$quality_normal
でエンコードされます。
$tssize_toggle=1の場合のみ、tsファイルサイズが大きい場合は$quality_low
でエンコードされます。これらの値はエンコード用の引数に使用される$quality
に格納されるので、お使いのx264やx265のcrf値、LA-ICQのglobal_quality…等に適宜合わせて下さい。
tsファイルサイズが大きいかどうかの閾値を$tssize_max
で設定します。これは特に変えなくて良いでしょう。
10GB以内に収めてGoogleフォトにうpできるようにする不完全な処置(2passなんてしたくない)ですが、この機能のお陰で容量超過は極稀です。
#--------------------エンコード--------------------
#一時的にmp4を吐き出すディレクトリ
$tmp_folder_path='C:\DTV\tmp'
#mp4保存ディレクトリ(Backup and Sync、ローカル保存用)
$mp4_folder_path='C:\DTV\backupandsync'
#例外時にts、ts.program.txt、ts.err、mp4を退避するディレクトリ(ループしてもffmpegの処理に失敗、mp4が10GBより大きい場合 etc…)
$err_folder_path='C:\Users\sbn\Desktop'
#mp4のファイルサイズ上限(GoogleDriveの10GB制限用、ローカル保存時には不要なので20GB等にすると良い)
$mp4_max=10GB
#mp4用ffmpeg引数 使用可能:$audio_option(デュアルモノの判別)、$quality(tsファイルサイズ判別)、$pid_need(PID判別)
function arg_mp4 {
$script:file="${ffpath}\ffmpeg.exe"
#QSV H.264 LA-ICQ
$script:arg="-y -hide_banner -nostats -analyzeduration 30M -probesize 100M -fflags +discardcorrupt -i `"${env:FilePath}`" ${audio_option} -vf bwdif=0:-1:1,pp=ac,hqdn3d=2.0 -global_quality ${quality} -c:v h264_qsv -preset:v veryslow -g 300 -bf 6 -refs 4 -b_strategy 1 -look_ahead 1 -look_ahead_depth 60 -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 ${pid_need} -movflags +faststart `"${tmp_folder_path}\${env:FileName}.mp4`""
#x265 fast
#$script:arg="-y -hide_banner -nostats -analyzeduration 30M -probesize 100M -fflags +discardcorrupt -i `"${env:FilePath}`" ${audio_option} -vf bwdif=0:-1:1,pp=ac -c:v libx265 -crf ${quality} -preset:v fast -g 15 -bf 2 -refs 4 -pix_fmt yuv420p -bsf:v hevc_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 ${pid_need} -movflags +faststart `"${tmp_folder_path}\${env:FileName}.mp4`""
#x265 bel9r
#$script:arg="-y -hide_banner -nostats -analyzeduration 30M -probesize 100M -fflags +discardcorrupt -i `"${env:FilePath}`" ${audio_option} -vf bwdif=0:-1:1,pp=ac -c:v libx265 -preset:v fast -x265-params crf=${quality}:rc-lookahead=40:psy-rd=0.3:keyint=15:no-open-gop:bframes=2:rect=1:amp=1:me=umh:subme=3:ref=3:rd=3 -pix_fmt yuv420p -bsf:v hevc_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 ${pid_need} -movflags +faststart `"${tmp_folder_path}\${env:FileName}.mp4`""
}
$tmp_folder_path
$mp4_folder_path
$err_folder_path
に相当するフォルダを作成し、パスを指定して下さい。
$mp4_max=10GB
はGoogleDriveの為だが、ローカル保存用途の場合は値を大きくすると良い。
function arg_mp4
の$global:arg
にはffmpegのmp4出力用の引数を記述します。環境によって引数を変える必要があるため、注意すること。
${audio_option}
には通常-c:a copy -bsf:a aac_adtstoasc
、デュアルモノでは-c:a aac -b:a 128k -filter_complex channelsplit
が格納されます。
${quality}
には#-----tsファイルサイズ判別-----
で指定した値が格納されます。
${pid_need}
には#-----PID判別-----
で-map i:0x140 -map i:0x141
のようにPIDを指定する引数が格納されます。
ffmpegの引数についてはキリがないので共通項を参照されたし。スクリプト内にも私が使っているものがいくつか載せてあるので参考にすると良い。
QSVが使用できるHaswell以降のCPU:デフォルトでおk
QSVが使用できるIvyBridgeのCPU:-look_ahead 1
->-look_ahead 0
QSVが使用できるIvyBridgeより古いCPU:-global_quality ${quality}
->-q:v ${quality}
、-look_ahead 1
->-look_ahead 0
<#
エラーメッセージ一覧
・[EDCB] 録画失敗によりエンコード不可: tsファイルが無い(パスが渡されない)場合。録画失敗?
・[EDCB] PIDの判別不可: ストリームの解析が失敗以前に不可能。Drop過多orスクランブル解除失敗?
・[GoogleDrive] 10GB以上の為アップロードできません: GoogleDriveの仕様に合わせる。
・[h264_qsv] device failed (-17): QSVのエラー。ループして復帰を試みるも失敗した場合。
・[mpegts] コーデックパラメータが見つかりません: PID判別から渡されたPIDが適切でないorffmpegが非対応のストリーム。
・[aac] 非対応のチャンネルレイアウト: ffmpeg4.0~デュアルモノを少なくとも従来の引数では扱えなくなった。
・[-c:a aac] PIDの判別に失敗: -c:a aac時。指定サービスのみ(全サービスでない)録画になっていなければ必ず発生。また一通りのストリームに対応させた筈だけど起こるかもしれない。
・[-c:a copy] PIDの判別に失敗: -c:a copy時。上に同じ。
・不明なエラー:
・[-c:a aac] PIDの判別に失敗? ExitCode:0: -c:a aac時。ffmpegの終了コードは0だが異常がある?場合。
・[-c:a copy] -c:a aacか-ss 1で治るやつ ExitCode:0: -c:a copy時。上に同じ。
#>
#--------------------ツイート警告--------------------
#0=無効、1=有効
$tweet_toggle=1
#ruby.exe
$ruby_path='C:\Ruby25-x64\bin\ruby.exe'
#tweet.rb
$tweet_rb_path='C:\DTV\EDCB\tweet.rb'
#SSL証明書(環境変数)
$env:ssl_cert_file='C:\DTV\EDCB\cacert.pem'
#--------------------Discord--------------------
#0=無効、1=有効
$discord_toggle=1
#webhook url
$hookUrl='https://discordapp.com/api/webhooks/XXXXXXXXXX'
エラーメッセージ一覧にあるものがツイートやDiscordで通知される。どちらか片方でも有効にしておくと良いと思う。もっとも、使われる機会はそうないだろうが。
Twitter連携についてはcf.。
$hookUrl
にdiscordのwebhook urlを指定するとそこでpost出来る。
#--------------------バルーンチップ表示--------------------
#0=無効、1=有効
$balloontip_toggle=1
バルーンチップ表示が要らなければ$balloontip_toggle=0
にして下さい。
Googleフォト・ドライブでの録画ファイル管理 見出しにジャンプ
高画質モードcf.cf.では、
・無料、容量無制限
・10GBまで
・再エンコされる
※一応生TSも拡張子が.m2ts
なら高画質モードでうp出来る。ただし実用性が低い上、Googleに対して迷惑でしかない(OneDriveの容量無制限廃止はDTV民が原因だとか?)のでお勧めしません。
Googleドライブでフォルダ分けする方法 見出しにジャンプ
https://drive.google.com の"パソコン"タブの中に"Backup and Sync用フォルダ"が現れる。
Backup and Sync以前のGoogleフォトのファイルをGoogleドライブで表示するには、歯車->設定->Googleフォトフォルダを作成するにチェックする。
前述の通り正しく設定してあればローカル側で削除してもクラウドにはしっかり残ってくれる。
左で必要な階層まで展開しディレクトリごとマイドライブにD&Dして(バックアップが引き続き行われるようPC側のアップローダから再設定して下さい)から検索バーで番組名検索してフォルダ分けする(マイドライブに移動してやらないとフォルダが重複してしまう)。
Shiftを押しながら↑↓でファイルの一括選択ができる。ファイル名検索でフォルダ分けする際、ファイルの詳細タブを見れば前回どこまでフォルダ分けしたか分かる。
※2017年9月30日現在、ブラウザ、iOSアプリ共に問題なく再生できることを確認しました。更に以前よりもレスポンスが良くなっています。2017年9月1日現在、2017年7月19日(数日前にBackup and Syncがリリース)以降にうpされた動画がGoogleドライブにて720p又は全ての解像度でストリーミング出来なくなっています。一応YouTubeの推奨設定でエンコしても変わりませんでした。
※2018年2月現在、必ず音声を再エンコードすることで解決しました。-c:v copy
だとちょくちょくGoogleフォト側が再エンコに失敗するみたい?2018年1月6日現在、不規則に一部ファイルが「この動画は処理中です。しばらくしてからもう一度ご確認ください。」というエラーメッセージと共にGoogleドライブにて再生できず、そのうち大半はGoogleドライブ側でDLしたりGoogleフォトで探せば再生できるものの、また一部不規則にGoogleドライブ側でDLしたものはmoov atomやファイルの一部が壊れており、Googleフォト側に存在しないファイルがあった。
Googleフォトでアルバム分けする方法 見出しにジャンプ
https://photos.google.com にズラズラとファイルが表示される。
面倒なのでオススメしません。ただし、ドライブに不具合がある場合、フォト側では問題ない場合があるので使ってみて下さい。
検索機能は役に立たない。稀に一部のファイル名において数字やアルファベット等で上手く選別出来ることがある。
←
→
キーで前後に移動出来る。
(i)
をクリックしないとファイル名が見られない。
?
をクリックしてアルバムに追加すればおk。
連続でやっていると重くなるのでF6
->Alt+Enter
で新しいタブで開き直すと良い
どうやらアルバムの順番は中に入っている最新のファイルの日時によって勝手に並べ替えてくれるらしい(Googleドライブはデフォルトではフォルダ名順だが色々な条件でソート出来る)。
古いものから整理しないと、アルバム内で順番がめちゃめちゃになってしまうので注意。
古いものから遡って整理していけば、スクロールバーの同じ位置を長押ししてれば目的のフォルダに早く辿り着く。
スクリプトの解説 見出しにジャンプ
私の頭の直訳で日本語になってない所もあるのはご了承下さい。
更新に追い付いてないことが多いのもご了承ください。Twitterとかヲチすればここに載せきれてないことを、記事更新前に試行錯誤しながら呟いてたりします。質問や要望も受けてます。逆に質問もしたいw
PostRecEnd.bat 見出しにジャンプ
※フル構成版の解説です。ただし、最小構成版を網羅しています。
これを見返しながら書いてます。
大まかな流れ(PostRecEnd.batフル構成の場合)
1.TSファイルをEpgTimerSrvから入力
2.環境変数設定
3.以下を子バッチとして実行(ログを出力し、不具合の原因を探す際に役立てる)
3-1.自動予約キーワードによるjpg出力
3-2.フォルダサイズを一定に保つファイルの削除(放っておいてもストレージが満杯にならない為に):録画フォルダの合計サイズを調べる
3-2-1.一定サイズ(100GB)より大きい:古い方から1つずつts、同名のts.program.txt、mp4ファイルを削除し4'へ
3-2-2.一定サイズ(100GB)以下:4へ
3-3.音声形式の判別(デュアルモノ音ズレ対策)
3-3-1.デュアルモノ:左右を分離し2チャンネルに分ける、128kbps
3-3-2.その他:256kbps
3-4.tsファイルサイズ判別(うpできないような10GB以上のmp4が出来るだけ出力されない為に)
3-4-1.20GB以下:品質26
3-4-2.20GBより大きい:品質28
3-5.エンコード(デインターレース->モスキートノイズ除去->280x720にリサイズ->高精度平滑化->h264_qsv LA-ICQ)
3-6.mp4ファイルサイズ判別(正常にエンコできなかった場合はやり直して、うpできるサイズならうpし、できないならエラーとして関連ファイルを退避し警告する)
3-3-1.0バイト
3-3-1-1.0~49回目:6へ
3-3-1-2.50~回目:8cへ
3-3-2.10GB以下:Backup and Sync用フォルダへmove
3-3-3.10GBより大きい、エラー:エラーフォルダへmove、Twitterで警告
メインルーチン終了
上から順番になぞっていく。
@echo off
コマンド内容を出力しなくなるが、ログファイルに出してるので関係ない。@echo on
として一応書き残してあるだけ。
rem 180309
バージョンがこんがらがらないように私が勝手に更新日時を書いているだけ。rem
はコメントでその行に何を書いても実行されない。
rem _EDCBX_DIRECT_
直接PostRecEnd.batを実行し、環境変数の括り文字を$
ではなく%
にしている。
rem _EDCBX_HIDE_
コマンドプロンプトのウィンドウを表示しないで実行できる。自分がPCを弄っている時に動作していることがパッとわかるように有効にしていない。
拡張命令cf. | 動作内容 |
---|---|
_EDCBX_BATMARGIN_={bat実行条件(分)} | このマージン以上録画予定がないときに実行(デフォルト:0) |
_EDCBX_HIDE_ | ウィンドウ非表示 |
_EDCBX_NORMAL_ | ウィンドウを最小化しない |
_EDCBX_DIRECT_ | マクロを$置換$ではなく%環境変数%で渡して直接実行する ※PowerShellスクリプトでは常に有効 ・EpgTimerSrv.exeのあるフォルダに"EpgTimer_Bon_RecEnd.bat"を作らない ・EpgTimer.exeを経由する間接実行はしない ・カレントディレクトリはそのバッチのあるフォルダ |
_EDCBX_FORMATTIME_ | 日時についてのマクロ($SDYY$など)をISO8601形式の$StartTime$と$DurationSecond$に単純化する ※PowerShellスクリプトでは常に有効 |
EpgTimerSrvから取得できるマクロ(環境変数)一覧の取得cf.
rem _EDCBX_DIRECT_
set
pause
EpgTimerから渡される環境変数RecModeが4=視聴なら処理が不要なので終了する。EpgTimerから受け渡されるマクロについてはcf.。
rem 視聴予約なら終了
if "%RecMode%" == "4" (
goto :eof
)
title
でコマンドプロンプトのタイトルバーに表示する文字列を変更できる。
%0
は"自分のフルパス"
、要するに例えば"C:\DTV\EDCB\PostRecEnd.bat"
を表す。
%~0
:C:\DTV\EDCB\PostRecEnd.bat
%~n0
:PostRecEnd
%~x0
:.bat
%~nx0
:PostRecEnd.bat
rem ファイル名をタイトルバーに表示
title %~nx0:%FileName%.ts
環境変数設定 見出しにジャンプ
set
で環境変数をセットできる。最初に「%tmp_folder_path%
は"C:\DTV\MP4"
だよ」と宣言しておけば、毎回フルパスを書かなくて済むので何度も使うパスや環境によって変更する必要がある部分は環境変数を使うと良い。
rem ====================環境変数設定====================
rem 一時的にmp4を吐き出すフォルダのパス
set "tmp_folder_path=C:\DTV\MP4"
rem backup and sync用フォルダのパス
set "bas_folder_path=C:\DTV\backupandsync"
…
ログ 見出しにジャンプ
なんかコマンド > log.txt
で標準出力をテキストファイルにリダイレクト(ログを出力)できる。log.txtが無ければ自動で作成され、log.txtは上書きされる。
>>
で追記。
2>
で標準エラー出力をリダイレクト。
2>&1
で標準出力と標準エラー出力を同じ出力先にする。
if not "%sterted%" == "1"
もし環境変数startedが1でなければ、
・set started=1
環境変数startedを1にして
・call "%~0" %* > "%log_path%\log_%FileName%.txt" 2>&1
call
呼ぶ"%~0"
自分を%*
諸々の環境変数を連れて。callした内容全てをログファイルに出力(ログ出力指定を一箇所にしたかった)。
・exit
callした自分の処理(バッチ全体の処理)が終わったら終了
rem ====================ログ====================
if not "%started%" == "1" (
set started=1
rem 自分自身を子バッチとして起動し直し、ログ出力
call "%~0" %* > "%log_path%\log_%FileName%.txt" 2>&1
exit
)
dir
で"%log_path%\*.txt"
ログフォルダ内のtxtファイルを/b
名前順で/o-d
新しい順にソート。
skip=100
100個スキップし(新しい方から101個目~)、そこからforループ処理でファイル名が順番に%~na
に格納されていき、del
で削除。
rem dirでログフォルダのtxtファイルをファイル名のみ日付順で表示し、新しい方から100個飛ばし、あぶれたファイルを削除する
for /f "skip=100 delims=" %%a in ('dir "%log_path%\*.txt" /b /o-d') do (
rem log削除
del "%log_path%\%%~na.txt"
)
jpg出力 見出しにジャンプ
EpgTimerから渡される環境変数Addkey(自動予約時のキーワード)に特定の文字が含まれている場合は連番jpgも出力する。
echo "%AddKey%"
で環境変数を展開し|
でパイプし(受渡)findstr "ポプテピピック フランキス BEATLESS"
で文字列を探す(findstrのデフォルトの区切り文字はスペースです)。
環境変数errorlevelは直前のコマンドの終了コードを表す。0
ならば1単語でも一致していたことになり、1
なら不一致、2
ならば異常終了となる。
findstr ""
とすると検索文字列がありません
でerrorlevel=2
となる為、jpg出力機能が無効になる。
0ならjpg出力、1や2なら実行しない。
md "%jpg_path%\%FileName%"
でフォルダ作成。
1440x1080のSAR4:3、DAR16:9の映像を切り出すと4:3の画像になってしまう(16:9に引き伸ばして"表示"させているだけ、コマ自体は4:3)。
そのため、ffprobeで(=
は^=
としエスケープ)得たtsの横pxを元に判別し、1440
ならば1920x1080にリサイズするようにしている。
あれおっかしいなぁ…
>ffprobe -hide_banner -v quiet -i "C:\Users\Shibanyan\Desktop\180119_ヴァイオレット・エヴァーガーデン 第2話「戻ってこない」.ts" -show_entries stream=width -of default=noprint_wrappers=1:nokey=1
1920
1920
と思ったら、録画したtsファイルでは、どうやらメタデータ内の2箇所に"width"の項目がある模様。
>ffprobe -hide_banner -v quiet -i "C:\Users\Shibanyan\Desktop\180119_ヴァイオレット・エヴァーガーデン 第2話「戻ってこない」.ts" -show_entries stream=width -print_format xml
<?xml version="1.0" encoding="UTF-8"?>
<ffprobe>
<programs>
<program >
<streams>
<stream width="1920"/>
<stream />
<stream />
</streams>
</program>
</programs>
<streams>
<stream width="1920"/>
<stream />
<stream />
</streams>
</ffprobe>
set "ts_width=%%a"
よって、forループで変数に上書きすることで、widthを1つだけ取り出す。
%05d
でファイル名は00001、00002、…のようになる。バッチ内では%
を%%
としエスケープ。
rem ====================jpg出力====================
rem 環境変数Addkey(自動予約時のキーワード)に特定の文字が含まれている場合は連番jpgも出力
rem findstr ""でerrorlevel=2となる
echo "%AddKey%" | findstr "ポプテピピック フランキス BEATLESS エヴァーガーデン"
if %errorlevel% equ 0 (
rem 出力フォルダ作成
md "%jpg_path%\%FileName%"
rem 生TSの横が1920か1440か調べる
rem 変数ts_widthに格納することで、tsファイル特有のwidthがメタデータの2箇所にあり2つ出力されちゃう問題を解決
for /f "delims=" %%a in ('ffprobe -v quiet -i "%FilePath%" -show_entries stream^=width -of default^=noprint_wrappers^=1:nokey^=1') do (
set "ts_width=%%a"
)
if "%ts_width%" == "1440" (
rem 1440ならば1920x1080にリサイズしてjpg出力
"%ffmpeg_path%" -hide_banner -nostats -an -skip_frame nokey -i "%FilePath%" -vf yadif=0:-1:1,scale=1920:1080,hqdn3d=4.0 -f image2 -q:v 0 -vsync 0 "%jpg_path%\%FileName%\%%05d.jpg"
) else (
rem 1440でなければリサイズせずにjpg出力
"%ffmpeg_path%" -hide_banner -nostats -an -skip_frame nokey -i "%FilePath%" -vf yadif=0:-1:1,hqdn3d=4.0 -f image2 -q:v 0 -vsync 0 "%jpg_path%\%FileName%\%%05d.jpg"
)
)
フォルダサイズを一定に保つファイルの削除 見出しにジャンプ
100GBは100x1024x1024x1024=107374182400Byteなんだけど、-2147483648
~2147483647
までの数値しか扱えない。
>set /a hoge=107374182400
無効な数字です。数値は 32 ビットで表記される数値です。
しょうがないから*107374
で下4桁を切り捨てて計算する。
rem ====================フォルダサイズを一定に保つファイルの削除====================
rem 録画フォルダの最大サイズを指定(1~20000の整数、単位:GB)
set folder_max=100
rem GBをbyteの下4桁切り捨て(32bit計算の為)た状態に直す
set /a folder_max=folder_max*107374
:filedeltop
これはラベルといって、例えばgoto :filedeltop
とすればそこへジャンプすることができる。
:hoge
if %foo% == foo (
goto :hoge
)
exit
:hoge
if %foo% == foo (
if %foo% == foo (
if %foo% == foo (
…
)
exit
)
exit
)
exit
call :filedeltop
とすると、サブルーチンとして呼ぶことができる。
if %foo% == foo (
call :hoge
)
exit
:hoge
echo foo
exit /b
if %foo% == foo (
echo foo
)
exit
dir ファイル名
でディレクトリ内のファイル等の情報を表示する。
%FolderPath%はEpgTimerから渡されるcf.。
>dir "C:\DTV\ts"
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は B0D0-8490 です
C:\DTV\ts のディレクトリ
~~
18/03/09 (金) 00:29 0 180309_デスマーチからはじまる異世界狂想曲 第9話「デスマーチからはじまる情緒纒綿」.ts
18/03/09 (金) 00:29 1,646 180309_デスマーチからはじまる異世界狂想曲 第9話「デスマーチからはじまる情緒纒綿」.ts.program.txt
18/03/09 (金) 00:29 4,229,182,012 180309_ヴァイオレット・エヴァーガーデン 第9話「ヴァイオレット・エヴァーガーデン」.ts
18/03/08 (木) 23:59 1,548 180309_ヴァイオレット・エヴァーガーデン 第9話「ヴァイオレット・エヴァーガーデン」.ts.program.txt
54 個のファイル 101,544,696,246 バイト
2 個のディレクトリ 163,976,073,216 バイトの空き領域
/-d
で名前の一覧表示をしない、/-c
でファイルサイズのコンマ無し。
>dir /-d /-c "C:\DTV\ts"
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は B0D0-8490 です
C:\DTV\ts のディレクトリ
~~
180309_デスマーチからはじまる異世界狂想曲 第9話「デスマーチからはじまる情緒纒綿」.ts
180309_デスマーチからはじまる異世界狂想曲 第9話「デスマーチからはじまる情緒纒綿」.ts.program.txt
180309_ヴァイオレット・エヴァーガーデン 第9話「ヴァイオレット・エヴァーガーデン」.ts
180309_ヴァイオレット・エヴァーガーデン 第9話「ヴァイオレット・エヴァーガーデン」.ts.program.txt
54 個のファイル 101544696246 バイト
2 個のディレクトリ 163384995840 バイトの空き領域
^| findstr "個のファイル"
|
パイプ(^
でエスケープ)でfindstr
に渡し、個のファイル
という文字列を探す。
>dir /-d /-c "C:\DTV\ts" | findstr "個のファイル"
54 個のファイル 101544696246 バイト
for文にて、出力結果のdelims=
区切り文字
(スペース)でtokens=3
3番目のトークンを環境変数folder_sizeに格納する。
/f "tokens=1 delims= "
:54
/f "tokens=2 delims= "
:個のファイル
/f "tokens=3 delims= "
:101544696246
rem tsのフォルダ内のファイルすべてのサイズをdirでソートし、findstrでフォルダサイズの行を抽出し、3番目のトークンを環境変数に格納
for /f "tokens=3 delims= " %%a in ('dir /-d /-c "%FolderPath%" ^| findstr "個のファイル"') do (
set folder_size=%%a
)
if
もし%folder_size:~0,-4%
先程得たフォルダのサイズの下4桁切り捨て(101544696246
->10154469
)がgtr
より大きい%folder_max%
(10737400)よりもな場合サブルーチンを実行。
dir "%FolderPath%\*.ts" /b /o-d
%FolderPath%
内の/b
でファイル名だけ表示、/o-d
で新しいファイルから順番に表示。
for文内でset "del_name=%%~na"
でファイルの名前の部分だけ(%
は%%
でエスケープ)を新しいファイルから次々と環境変数del_nameに格納していくと、最終的に最後のファイル名=一番古いファイルの名前が格納される(tokensで最後のトークンを指定したかったのだけど無理ポ)。
%del_name%
一番古いファイルの名前に合わせて.ts、.ts.program.txt、.mp4をdel
削除する(遅延環境変数はファイル名の!
をエスケープするか削除するしかないので使用しない)。
goto :filedeltop
でフォルダサイズ取得から再試行する。フォルダサイズがまだ大きいならもう一回行い、小さくなっていれば次に行く。
rem 録画フォルダのサイズがfolder_maxで指定したサイズより大きいならサブルーチンを読んでこのルーチンの最初に戻る
rem 環境変数folder_size、folder_maxがそれぞれ下4桁切り捨てのbyte単位で計算される
if %folder_size:~0,-4% gtr %folder_max% (
call :filedelsub
goto :filedeltop
)
:filedelsub
rem dirで録画フォルダのtsファイルをファイル名のみ新しいものから日付順で表示し、最後に環境変数del_nameに代入される一番古いtsファイル名を取得
for /f "delims=" %%a in ('dir "%FolderPath%\*.ts" /b /o-d') do (
set "del_name=%%~na"
)
rem ts削除
del "%FolderPath%\%del_name%.ts"
rem 同名のts.program.txt削除
del "%FolderPath%\%del_name%.ts.program.txt"
rem 同名のmp4削除
del "%bas_folder_path%\%del_name%.mp4"
exit /b
音声形式の判別 見出しにジャンプ
findstrでts.program.txt内に"デュアルモノ"という文字列を探す。
環境変数errorlevel(直前のコマンドの終了コード)が0ならば、"デュアルモノ"という文字列があるので、左日本語右英語なので2chにスプリットして、音声ビットレートを適切にあわせる。
環境変数errorlevelが1ならば、"デュアルモノ"という文字列がないので、普通にエンコする。前後の番組にデュアルモノや特殊なチャンネルの音声の番組があった場合、-c:a copy -bsf:a aac_adtstoasc
(MPEG-2/4 AAC ADTSをMPEG-4 ASCビットストリームに変換する必要あり)だと正常に処理できない場合があるので、再エンコする。
"[AVBSFContext @ 0000029810b1f260] PCE-based channel configuration without PCE as first syntax element is not implemented. Update your FFmpeg version to the newest one from Git. If the problem still occurs, it means that your file has a feature which has not been implemented. Error applying bitstream filters to an output packet for stream #0:1.
rem ====================音声形式の判別====================
rem 番組情報の中に"デュアルモノ"という文字列があれば環境変数"audio_option"に"-filter_complex channelsplit"を加え、音声ビットレートを半分にする
findstr "デュアルモノ" "%FilePath%.program.txt"
if %errorlevel% equ 0 (
set audio_option=-c:a aac -b:a 128k -filter_complex channelsplit
) else if %errorlevel% equ 1 (
rem set audio_option=-c:a copy -bsf:a aac_adtstoasc
set audio_option=-c:a aac -b:a 256k
)
tsファイルサイズ判別 見出しにジャンプ
Backup and Syncの10GB制限を超過しない為に、録画ファイルサイズによる品質自動変更機能です。
コントラスト・動きが大きい番組、長い番組等でTSファイルが20GBより大きい場合は、自動で品質を落としてエンコする為、アップロード失敗がほぼ間違いなく起こりません(2017年末紅白はtsが26.5GB、品質27エンコで12.5GB、品質29エンコで9.5GB)。
EpgTimerから渡される%FilePath%
tsファイルのサイズを取得%~z
し環境変数ts_sizeに格納する。
20GB=21474836480byteだが計算できるように下1桁切り捨て、同じように環境変数ts_sizeも下一桁切り捨てて以下leq
とより大きいgtr
で品質に差を付けて、Googleフォトの無制限モードでうpする時に10GB以内に出来るだけ収まってくれるよう試みる。
rem ====================tsファイルサイズ判別====================
rem TSファイルのサイズを環境変数"ts_size"に指定
for %%i in ("%FilePath%") do (
set ts_size=%%~zi
)
rem 20GB=21474836480byte以下ならquality 26、より大きいなら28
if %ts_size:~0,-1% leq 2147483648 (
set quality=27
) else if %ts_size:~0,-1% gtr 2147483648 (
set quality=29
)
エンコード 見出しにジャンプ
set cnt=0
はループカウント用に使うので予め指定しておく。goto :encode
して来たときには実行されないように先に書いてある。
timeout /t 10 /nobreak
10秒待つ、操作を受け付けない。
EpgTimerから渡された%FilePath%、%SID10%、%FileName%と、バッチ内で格納された%audio_option%、%quality%、%tmp_folder_path%が展開される。
%tmp_folder_path%に%FileName%.mp4として出力される。
%SID10%
複数サービスやチャンネルの中から必要なものだけをマッピングしてエンコすることで音ズレを回避する。必須。
Input #0, mpegts, from 'C:\DTV\ts\180308_・懊い繝九Γ繧ョ繝ォ繝会シ槭弱ム繝。繝励Μ ANIME CARAVAN縲冗ャャ9隧ア.ts':
Duration: 00:30:00.35, start: 11688.074111, bitrate: 11967 kb/s
Program 181
Metadata:
service_name : ?BS?|ユク?181
service_provider: ?BS?|ユク
Stream #0:0[0x111]: Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p(tv, bt709, top first), 1440x1080 [SAR 4:3 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
Stream #0:1[0x112]: Audio: aac (LC) ([15][0][0][0] / 0x000F), 48000 Hz, stereo, fltp, 256 kb/s
Stream #0:2[0x810]: Unknown: none ([13][0][0][0] / 0x000D)
Stream #0:3[0x815]: Unknown: none ([13][0][0][0] / 0x000D)
Program 182
Metadata:
service_name : ?BS?|ユク?182
service_provider: ?BS?|ユク
Program 183
Metadata:
service_name : ?BS?|ユク?183
service_provider: ?BS?|ユク
Program 188
Metadata:
service_name : ?BS?|ユク?188
rem ====================エンコード====================
rem ループ処理用
set cnt=0
:encode
rem 録画の開始終了でビジーなので負荷を減らすために10秒待つ
timeout /t 10 /nobreak
rem エンコ
ffmpeg -y -hide_banner -nostats -fflags +discardcorrupt -i "%FilePath%" %audio_option% -vf yadif=0:-1:1,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0 -global_quality %quality% -c:v h264_qsv -preset:v veryslow -g 300 -bf 16 -refs 9 -b_strategy 1 -look_ahead 1 -look_ahead_downsampling off -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 -map 0:p:%SID10%:0 -map 0:p:%SID10%:1 -movflags +faststart "%tmp_folder_path%\%FileName%.mp4"
QSVのデバイスエラーに対処しています。
・高負荷時、CPUスペックに対して背伸びしたオプションを使用した際等に、QSVが上手く利用出来ず0Byteのファイルだけが生成されてしまう既知の問題があります。極めて単純で、25回までループすることでほぼ間違いなく正常に処理されます。このような機能のないQSVを使用したスクリプトは、特定の条件下でエンコードに失敗し、自動処理が行えない可能性があります。
・~~batでは、0Byteのmp4を検知してループしていますが、~~終了コードによるループを行っている為、例えば処理中にQSVが落ちてしまった場合にも対応している(私の環境では更に稀だが、念の為)。
mp4ファイルサイズ判別 見出しにジャンプ
tsファイルサイズ判別ルーチンと同様に、mp4のファイルサイズを環境変数mp4_sizeに格納。
if %mp4_size% equ 0
もしmp4のファイルサイズが0Byteでエンコ失敗したなら、以下がループされる。
・set /a cnt=cnt+1
でカウントを1上げる。
・if %cnt% geq 50
もし%cnt%が50以上ならcall :err
エラーサブルーチンを呼ぶ。
・else
それ以外ならgoto :encode
もっかいエンコしてみる。
else if %mp4_size:~0,-1% leq 1073741824
else if
もしmp4のファイルサイズが0Byte以外で、10GB以下ならbackup and sync用フォルダに移動。tmp_folder_pathに出力してbas_folder_pathに移動するんじゃなくて最初からbas_folder_pathに出力すればいいじゃんと思うかもしれないけど、直接出力するとエンコの途中でBackup and syncが動いてしまうので駄目。
else if %mp4_size:~0,-1% gtr 1073741824
else if
もしmp4のファイルサイズが0Byte以外で、10GB以下でないなら、エラーサブルーチンを呼ぶ。
rem ====================mp4ファイルサイズ判別====================
rem エンコ後ファイルのサイズを環境変数"mp4_size"に指定
for %%i in ("%tmp_folder_path%\%FileName%.mp4") do (
set mp4_size=%%~zi
)
rem 稀にQSVのトランスコードに失敗すると"Error during encoding: device failed (-17)"が返されエンコに失敗してしまうので、ファイルサイズが0バイトなら失敗とみなしループさせ復旧を試みる
rem 10GB=10737418240byte以下ならbackup and sync用フォルダ、より大きいなら10GB以上用フォルダへ(10GB以上はうp出来ない)(mp4が約40GBを超える場合は想定していない)
if %mp4_size% equ 0 (
rem 50回までループし、それでもダメなら諦めて無限ループを回避
set /a cnt=cnt+1
if %cnt% geq 50 (
call :err
) else (
goto :encode
)
) else if %mp4_size:~0,-1% leq 1073741824 (
rem エンコしたファイルが10GB以下ならbackup and sync用フォルダに移動
move "%tmp_folder_path%\%FileName%.mp4" "%bas_folder_path%"
) else if %mp4_size:~0,-1% gtr 1073741824 (
call :err
)
rem 終了
exit
call :err
で呼ばれ実行しexit /b
でサブルーチンを終了しメインルーチンに戻る。
move 移動したいファイルのパス 移動先ディレクトリ(ファイル)パス
でts、ts.program.txt、mp4を退避する。
set "tweet_content=ERROR:%FileName%.tsと関連ファイルを退避しました。ログを確認して下さい。"
で環境変数tweet_contentにecho
ツイートしたい内容を格納。
ruby "%tweet_rb_path%"
rubyに渡す
:err
rem ffmpegによるエラーのみ対応、シンタックスエラーやコマンドプロンプトの不具合はここに到達しないので注意
rem エンコしたファイルが10GBより大きい、処理に50回失敗した場合にts、ts.program.txt、mp4を退避する
move "%FilePath%" "%err_folder_path%"
move "%FilePath%.program.txt" "%err_folder_path%"
move "%tmp_folder_path%\%FileName%.mp4" "%err_folder_path%"
rem ツイートで警告
set "tweet_content=ERROR:%FileName%.tsと関連ファイルを退避しました。ログを確認して下さい。"
ruby "%tweet_rb_path%"
exit /b
PostRecEnd.ps1 見出しにジャンプ
既出の構文、処理が必要な理由等は端折ってます。
#180707
#視聴予約なら終了
if ($env:RecMode -eq 4) { exit }
#Powershellプロセスの優先度を高に
#(Get-Process -Id $pid).PriorityClass='High'
#180326
バージョンのメモ。PowerShellではコメントは#
(複数行は<#
#>
)
EpgTimerSrvから取得できるマクロ(環境変数)一覧の取得cf.
ls env:
Read-Host "Press enter to continue"
シェル変数ではなく外部プログラムからの環境変数のため、$env:RecMode
。
文字列の中に変数を書く場合等では、境界が曖昧になるので${env:RecMode}
のように囲う。
タイトルバーにPostRecEnd.ps1:ファイル名.ts
を表示させたい場合は、以下のようにコメントを外す。
(Get-Host).UI.RawUI.WindowTitle = $MyInvocation.MyCommand.Name + ":${env:FileName}.ts"
NotifyIcon 見出しにジャンプ
#====================NotifyIcon====================
#System.Windows.FormsクラスをPowerShellセッションに追加
Add-Type -AssemblyName System.Windows.Forms
#NotifyIconクラスをインスタンス化
$balloon=New-Object System.Windows.Forms.NotifyIcon
#powershellのアイコンを使用
$balloon.Icon=[System.Drawing.Icon]::ExtractAssociatedIcon('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe')
#タスクトレイアイコンのヒントにファイル名を表示
$balloon.Text=$MyInvocation.MyCommand.Name + ":${env:FileName}.ts"
#タスクトレイアイコン表示
$balloon.Visible=$True
#ファイル名をタイトルバーに表示
#(Get-Host).UI.RawUI.WindowTitle=$MyInvocation.MyCommand.Name + ":${env:FileName}.ts"
これらはスクリプト開始直後に実行され、処理の間タスクトレイアイコンを表示している。
.NETのデフォルトで参照されてないSystem.Windows.Forms
名前空間を使用するためにAdd-Type
する。
New-Object
でSystem.Windows.Forms.NotifyIcon
クラスをインスタンス化する。
Icon
プロパティにSystem.Drawing.Icon
クラスの静的メソッドExtractAssociatedIcon
で('C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe')
のアイコンを読み取る。
>#"と'の違い
>$hoge = 'fuga'
>Write-Host "$hoge"
fuga
>Write-Host '$hoge'
$hoge
Text
プロパティはタスクトレイアイコンのヒント(マウスカーソルを合わせたときに出てくるやつ)に表示する内容を指定。
Visible
$True
でタスクトレイアイコン表示。
#====================BalloonTip関数====================
function BalloonTip {
if ("${balloontip_toggle}" -eq "1") {
#特定のTipIconのみを使用可
#[System.Windows.Forms.ToolTipIcon] | Get-Member -Static -Type Property
$balloon.BalloonTipIcon=[System.Windows.Forms.ToolTipIcon]::${ToolTipIcon}
#表示するタイトル
$balloon.BalloonTipTitle="エンコード${enc_result}"
#表示するメッセージ
$balloon.BalloonTipText="${env:FileName}`nts:$([math]::round(${ts_size}/1GB,2))GB mp4:$([math]::round(${mp4_size}/1MB,0))MB${err_detail}"
#balloontip_toggle=1なら5000ミリ秒バルーンチップ表示
$balloon.ShowBalloonTip(5000)
#5秒待って
Start-Sleep -Seconds 5
}
#タスクトレイアイコン非表示(異常終了時は実行されずトレイに亡霊が残る仕様)
$balloon.Visible=$False
}
これらは処理の最後(失敗又は終了時)に関数として呼び出され、適切なバルーンチップを表示するとともに、タスクトレイアイコンを非表示にする。
#===ユーザ設定===
のトグルのオンオフがif ("${balloontip_toggle}" -eq "1") {
で機能する。
function 関数名 {処理内容}
を先に書いておくと関数名を記述するだけで処理内容を実行できる。dosのラベルとexit /b
をcall
するみたいに使えるけど、使う前に書く必要があることに注意。
System.Windows.Forms.NotifyIcon
のBalloonTipIcon
(バルーンチップに表示されるアイコン)プロパティにSystem.Windows.Forms.ToolTipIcon
クラスの静的メソッドにInfo
Error
等を指定。ここでは、条件によってアイコンを変える為に関数BallonTip
を呼び出すと同時に変数$ToolTipIcon
に文字列を格納したものを使える状態にしている。
BalloonTipTitle
(バルーンチップのタイトル)にも"エンコード失敗"
のように補完できるようになっている。
BalloonTipText
(バルーンチップの本文)には1行目にファイル名${env:FileName}
を表示する。PowerShellでは`n
で改行。
2行目にはts:$([math]::round(${ts_size}/1GB,2))GB mp4:$([math]::round(${mp4_size}/1MB,0))MB
とし、変数$ts_size``$mp4_size
に格納されたファイルサイズ(単位:Byte)をmath
クラスの静的メソッドround
でそれぞれ/1GB``/1MB
で割り算して、小数点以下,2``,0
桁表示する。ts:1.23GB mp4:456MB
のようになる。
ShowBalloonTip
メソッドで5000
ミリ秒バルーンチップを表示。
Start-Sleep -Seconds 5
で5秒待ってVisible
を$False
。
Process 見出しにジャンプ
ハマったところ。
まず、普通にffmpegをオプションを沢山付けた状態で実行してみる。
>ffmpeg -y -hide_banner -nostats -fflags +discardcorrupt -i "${env:FilePath}" ${audio_option} -vf yadif=0:-1:1,pp= …
[NULL @ 000002b82a777840] Unable to find a suitable output format for 'yadif=0:-1:1,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0'
yadif=0:-1:1,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0: Invalid argument
適切な出力形式が見つからないとエラーが。
>ffmpeg -y -hide_banner -nostats -fflags +discardcorrupt -i "${env:FilePath}" -f mpegts ${audio_option} -vf yadif=0:-1:1,pp= …
Unknown encoder '-vf'
それじゃあ、
>&"${ffpath}\ffmpeg.exe" -y -hide_banner -nostats -fflags +discardcorrupt -i "${env:FilePath}" -f mpegts ${audio_option} -vf yadif=0:-1:1,pp= …
Unknown encoder '-vf'
もういい!Start-Process使うもん!
Start-Transcript -LiteralPath "${log_path}\${FileName}.txt"
$arg = "-y -hide_banner -nostats -fflags +discardcorrupt -i `"${FilePath}`" ${audio_option} -vf yadif=0:-1:1,pp= …"
$proc = Start-Process -FilePath "${ffmpeg_path}\ffmpeg.exe" -ArgumentList $arg -Wait -NoNewWindow
やった!できた!と思ったら、今度はStart-Transcript
にffmpegの出力が書かれてない…
~~
**********************
トランスクリプトが開始されました。出力ファイル: C:\DTV\EncLog\180325_キリングバイツ#11.txt
ログ削除:180316_ラーメン大好き小泉さん 十一杯目「おいしいラーメン/大阪/コンビニ」.txt
録画フォルダ:104.6GB
削除:180322_魔法使いの嫁 第23話「Nothing seek, nothing find.」.ts、ts.program.txt、mp4
録画フォルダ:100.73GB
削除:180323_ヴァイオレット・エヴァーガーデン 第11話「もう、誰も死なせたくない」.ts、ts.program.txt、mp4
録画フォルダ:96.77GB
quality:26
audio_option:-c:a aac -b:a 256k
>>>>>ここにffmpegの出力が欲しいのだが<<<<<
mp4:512MB
エンコード回数:1
エンコード終了:ts=2.42GB mp4=512MB
**********************
~~
おっ、どうやら-PassThru
で実行するプロセスのオブジェクトを返す(System.Diagnostics.Process
)ので、もしかして標準出力StandardOutput
や標準エラー出力StandardError
を取得できるのでは?!
$arg = "-y -hide_banner -nostats -fflags +discardcorrupt -i `"${FilePath}`" ${audio_option} -vf yadif=0:-1:1,pp= …"
$proc = Start-Process -FilePath "${ffmpeg_path}\ffmpeg.exe" -ArgumentList $arg -Wait -WindowStyle Hidden -PassThru
Write-Output $proc.StandardError
> .\test.ps1 >
プログラマー諸君は、既にStart-Processから標準出力が正常に取得できないことに気づいていると思う。しかしシェルの不具合ではない。
繰り返す。これは不具合ではなく、Windows PowerShell本来の仕様である
というわけで結局.NET型で長々と書くことに、シェルの意味…
#====================Process====================
#ffmpeg、&ffmpeg、.\ffmpeg:ffmpegが引数を正しく認識しない(ファイル名くらいなら-f mpegtsで行けるけどもういいです)
#Start-Process ffmpeg:-NoNewWindowはWrite-Host?-RedirectStandardOutput、Errorはファイルのみ、-PassThruはExitCodeは受け取れても.StandardOutput、Errorは受け取れない仕様
function ffprocess {
#設定
#ProcessStartInfoクラスをインスタンス化
$psi=New-Object System.Diagnostics.ProcessStartInfo
#アプリケーションファイル名
$psi.FileName="$file"
#引数
$psi.Arguments="$arg"
#標準エラー出力だけを同期出力(注意:$trueは1つだけにしないとデッドロックします)
$psi.UseShellExecute=$false
$psi.RedirectStandardInput=$false
$psi.RedirectStandardOutput=$false
$psi.RedirectStandardError=$true
$psi.WindowStyle=[System.Diagnostics.ProcessWindowStyle]::Hidden
#実行
#Processクラスをインスタンス化
$p=New-Object System.Diagnostics.Process
#設定を読み込む
$p.StartInfo=$psi
#プロセス開始
$p.Start() | Out-Null
#プロセス優先度を高に
(Get-Process -Id $p.Id).PriorityClass='High'
#Write-Output $p.Id
#プロセスの標準エラー出力を変数に格納(注意:WaitForExitの前に書かないとデッドロックします)
$global:StdErr=$p.StandardError.ReadToEnd()
#プロセス終了まで待機
$p.WaitForExit()
#終了コードを変数に格納
$global:ExitCode=$p.ExitCode
#リソースを開放
$p.Close()
}
これらはffmpeg、ffprobe使用時に$file
$arg
とともに実行され、標準エラー出力を標準出力に出力する。
何度か使う機会があるので関数ffprobe
としておく。
New-Object
でSystem.Diagnostics.ProcessStartInfo
クラスをインスタンス化。
FileName
プロパティにアプリケーションのファイル名を指定する。ここでは、変数に指定しているものも使用して${ffpath}\ffmpeg.exe
や${ffpath}\ffprobe.exe
が展開される。
Arguments
には変数$arg
に格納された引数が展開される。
ffmpegは標準エラー出力を使用するので、RedirectStandardError
のみ$true
にする。ほかは$false
にしないとデッドロックします。
WindowStyle
でプロセス起動時のウィンドウをSystem.Diagnostics.ProcessWindowStyle
でHidden
状態にする。
New-Object
でSystem.Diagnostics.Process
クラスをインスタンス化。
StartInfo
プロパティで、Startメソッドに渡す先程の設定$psi
を読み込む。
Start
メソッドで()
とすることでStartInfo
プロパティで指定されたプロセスリソースを起動。| Out-Null
で出力非表示。Get-Process -Id $p.Id
でプロセスIDを取得し、PriorityClass
をHigh
に変更する。
$global:StdErr=$p.StandardError.ReadToEnd()
でグローバル変数StdErrにプロセスの標準エラー出力を格納する。これはPIDの判別、ログやエラーメッセージに使用する。
標準エラー出力StandardError
をStreamReader.ReadToEnd
メソッドで"同期"でWrite-Output
で標準出力に出力(注意:WaitForExit
メソッドの前に書かないとデッドロックします)。
WaitForExit
メソッドでプロセス終了まで待機する。
$global:ExitCode=$p.ExitCode
でグローバル変数ExitCodeにプロセスの終了コードを格納する。これはエンコードが成功か失敗であるかの判別に使う。
Close
メソッドでプロセス終了。
ユーザ設定 見出しにジャンプ
#====================ユーザ設定====================
~~
#tweet.rb
$tweet_rb_path = 'C:\DTV\EDCB\tweet.rb'
#SSL証明書(環境変数)
$env:ssl_cert_file = 'C:\DTV\EDCB\cacert.pem'
変数$tweet_rb_path
はシェル内でしか使用せず、環境変数$env:ssl_cert_file
はrubyに渡された後使用される。
ログ 見出しにジャンプ
#====================ログ====================
#ログ取り開始
Start-Transcript -LiteralPath "${log_path}\${env:FileName}.txt"
#Get-ChildItemでログフォルダのtxtファイルを取得、更新日降順でソートし、100個飛ばし、Foreach-ObjectでRemove-Itemループ
Get-ChildItem "${log_path}\*.txt" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 100 | Foreach-Object {
Remove-Item -LiteralPath "$_"
Write-Output "$_"
}
#====================ログ====================
#log_toggle=1ならば実行
if ("${log_toggle}" -eq "1") {
#ログ取り開始
Start-Transcript -LiteralPath "${log_path}\${env:FileName}.txt"
#Get-ChildItemでログフォルダのtxtファイルを取得、更新日降順でソートし、logcnt_max個飛ばし、ForEach-ObjectでRemove-Itemループ
Get-ChildItem "${log_path}\*.txt" | Sort-Object LastWriteTime -Descending | Select-Object -Skip ${logcnt_max} | ForEach-Object {
Remove-Item -LiteralPath "$_"
Write-Output "ログ削除:$_"
}
}
ログ取得開始、古いログを削除を行う。
#===ユーザ設定===
のトグルのオンオフがif ("${balloontip_toggle}" -eq "1") {
で機能する。
Start-Transcript
で-LiteralPath
リテラルのファイルパス"${log_path}\${env:FileName}.txt"
にログ出力を開始。
Get-ChildItem
でログフォルダ内のすべてのテキストファイル"${log_path}\*.txt"
を取得、
Sort-Object LastWriteTime -Descending
で更新日降順でソート、
Select-Object -Skip ${logcnt_max}
で設定された数飛ばして、
Foreach-Object
でループ処理。
リテラル文字列のパス-LiteralPath
としてファイルパス"$_"
を``Remove-Itemで削除。
Write-Output`で表示。
フォルダサイズを一定に保つファイルの削除 見出しにジャンプ
※tsとmp4で別れているが中身はほぼ同じです。
#====================tsフォルダサイズを一定に保つファイルの削除====================
#ts_del_toggle=1なら実行
if ("${ts_del_toggle}" -eq "1") {
#録画フォルダの合計サイズを変数"ts_folder_size"に指定
$ts_folder_size=$(Get-ChildItem "${env:FolderPath}" | Measure-Object -Sum Length).Sum
Write-Output "録画フォルダ:$([math]::round(${ts_folder_size}/1GB,2))GB"
#録画フォルダの合計サイズがts_folder_maxGBより大きいならファイルの削除
while ($ts_folder_size -gt $ts_folder_max) {
#録画フォルダ内の1番古いtsファイルのファイル名を取得
#録画フォルダ内のtsファイルに対し、最終更新年月日でソートした1番最初にくるやつ、ファイル名(拡張子なし)を取得
$ts_del_name=$(Get-ChildItem "${env:FolderPath}\*.ts" | Sort-Object LastWriteTime | Select-Object BaseName -First 1).BaseName
#ts、同名のts.program.txt削除
Remove-Item -LiteralPath "${env:FolderPath}\${ts_del_name}.ts"
Remove-Item -LiteralPath "${env:FolderPath}\${ts_del_name}.ts.program.txt"
#Remove-Item -LiteralPath "${env:FolderPath}\${ts_del_name}.ts.err"
Write-Output "削除:${ts_del_name}.ts、ts.program.txt"
#録画フォルダの合計サイズを取得
$ts_folder_size=$(Get-ChildItem "${env:FolderPath}" | Measure-Object -Sum Length).Sum
Write-Output "録画フォルダ:$([math]::round(${ts_folder_size}/1GB,2))GB"
}
}
if ("${ts_del_toggle}" -eq "1") {
で機能のオンオフ。
$folder_max
に録画フォルダ$env:FolderPath
の最大サイズを指定。100GB
のように単位も付けられる。
Get-ChildItem "${env:FolderPath}"
をMeasure-Object
にパイプし-Sum Length
で合計サイズを測り、変数$folder_size
に格納。
while (実行条件) {処理内容}
実行条件を満たしている間処理内容を実行。$folder_size
が$folder_max
より大きい-gt
間実行。
Get-ChildItem "${env:FolderPath}\*.ts"
で録画フォルダ情報を取得し、
Sort-Object LastWriteTime
更新日時でソートし、
Select-Object BaseName -First 1).BaseName
ファイル名(dosで言うと%~n
、PowerShellはオブジェクトとして渡してるからちょっと違うけど)を取得、
それを変数$del_name
に格納している。
Remove-Item
でそれぞれファイル削除。
tsとts.program.txt、mp4でそれぞれユーザ設定通りに丸め込む。
再度$folder_size = $(Get-ChildIt…
でファイル削除後の録画フォルダサイズを取得する。これはwhile
の実行条件に反映されるため、条件から外れればループを抜ける。結果的に$folder_max
以下に保つ処理ができる。
jpg出力 見出しにジャンプ
#====================jpg出力====================
#環境変数Addkey(自動予約時のキーワード)に特定の文字が含まれている場合は連番jpgも出力(使用しない場合はコメント)
$addkey_jpg = $("${env:Addkey}" | Select-String -SimpleMatch 'ポプテピピック','フランキス','BEATLESS','エヴァーガーデン' -quiet)
if ($addkey_jpg -eq $True) {
#出力フォルダ作成
New-Item "${jpg_path}\${env:FileName}" -ItemType Directory
Write-output "jpg出力:${env:FileName}.ts"
#生TSの横が1920か1440か調べる
#xml形式で扱い、tsファイル特有のwidthがメタデータの2箇所にあり2つ出力されちゃう問題を解決
$ts_width = [xml](&"${ffpath}\ffprobe.exe" -v quiet -i "${env:FilePath}" -show_entries stream=width -print_format xml 2>&1)
$ts_width = $ts_width.ffprobe.streams.stream.width
#SAR比(1440x1080しか想定してないけど)によるフィルタ設定、jpg出力
if ("${ts_width}" -eq "1440") {
$arg = "-y -hide_banner -nostats -an -skip_frame nokey -i `"${env:FilePath}`" -vf yadif=0:-1:1,scale=1920:1080,hqdn3d=4.0 -f image2 -q:v 0 -vsync 0 `"${jpg_path}\${env:FileName}\%05d.jpg`""
} else {
$arg = "-y -hide_banner -nostats -an -skip_frame nokey -i `"${env:FilePath}`" -vf yadif=0:-1:1,hqdn3d=4.0 -f image2 -q:v 0 -vsync 0 `"${jpg_path}\${env:FileName}\%05d.jpg`""
}
$file = "${ffpath}\ffmpeg.exe"
ffprocess
}
自動予約登録時のキーワードenv:Addkey
に特定の文字が含まれているか調べSelect-String -SimpleMatch 'ポプテピピック','フランキス','BEATLESS','エヴァーガーデン'
て、変数addkey_jpg
に結果を格納。-quiet
で非表示。
if ($addkey_jpg -eq $True) {
$addkey_jpg
の結果が$True
(一致が1つでもあった)の場合実行。
New-Item
で"${jpg_path}\${env:FileName}"
jpg出力フォルダ内にファイル名の-ItemType Directory
ディレクトリを作成。
録画したtsファイルでは、メタデータの2箇所に"width"の項目があるので、
>ffprobe -hide_banner -v quiet -i "C:\Users\Shibanyan\Desktop\180119_ヴァイオレット・エヴァーガーデン 第2話「戻ってこない」.ts" -show_entries stream=width -print_format xml
<?xml version="1.0" encoding="UTF-8"?>
<ffprobe>
<programs>
<program >
<streams>
<stream width="1920"/>
<stream />
<stream />
</streams>
</program>
</programs>
<streams>
<stream width="1920"/>
<stream />
<stream />
</streams>
</ffprobe>
ffprobe
でtsファイル"${env:FilePath}"
のメタデータの横px-show_entries stream=width
のみ-print_format xml
xml形式で2>&1
標準、標準エラー出力取得。[xml]
型で変数$ts_width
に格納。
変数$ts_width
に変数$ts_width
の.ffprobe.streams.stream.width
の階層のみを格納。
これで1920
だけを取得している。
変数$ts_width
が1440
ならscale=1920:1080
でリサイズする。
$arg
と$file
を指定してffprocess
関数を呼んでjpg出力のエンコード。$arg = "-hoge `"C:\foo`""
のように、ダブルクォートはバッククォートでエスケープ。
PIDの判別 見出しにジャンプ
#====================PIDの判別====================
#前の番組の音声や映像のPIDを引数に入れないため
#-analyzeduration 30M -probesize 100Mで適切にストリームを読み込む
$StdErr=[string](&"${ffpath}\ffmpeg.exe" -hide_banner -nostats -analyzeduration 30M -probesize 100M -i "${env:FilePath}" 2>&1)
#スペース、CRを消す
$StdErr=($StdErr -replace " ","")
$StdErr=($StdErr -replace "`r","")
#if x480だけの場合はx480、else x1080だけ・x480とx1080がある場合はx1080
if (($StdErr -match 'x480') -And ($StdErr -notmatch 'x1080')) {
$res_need='x480'
} else {
$res_need='x1080'
}
#LFで分割して配列として格納
$StdErr=($StdErr -split "`n")
#配列を展開(映像)
foreach ($a in $StdErr) {
#"Video:"and"${res_need}"が含まれ、'none'が含まれない行の場合実行
if (($a -match "^(?=.*Video:)(?=.*${res_need})") -And ($a -notmatch 'none')) {
#引数に追記
$pid_need+=' -map i:0x'
#PIDの部分だけ切り取り
$pid_need+=($a -split '0x|]')[1]
}
}
#0x1なら音声も0x1**を選ぶ
#0x2なら音声も0x2**を選ぶ
if ("$pid_need" -match '0x1') {
$audio_need='0x1'
} elseif ("$pid_need" -match '0x2') {
$audio_need='0x2'
}
#配列を展開(音声)
foreach ($a in $StdErr) {
#"Audio:"and"${audio_need}"が含まれ、'0channels'が含まれない行の場合実行
if (($a -match "^(?=.*Audio:)(?=.*${audio_need})") -And ($a -notmatch '0channels')) {
#引数に追記
$pid_need+=' -map i:0x'
#PIDの部分だけ切り取り
$pid_need+=($a -split '0x|]')[1]
}
}
Write-Output "PID:${pid_need}"
デジタル放送のtsファイルには複数のストリームが含まれている。
指定サービスのみの録画でワンセグ等のストリームは削られているが、前の番組や裏番組のストリームが混ざっている場合が殆どの為、
エンコード 見出しにジャンプ
#====================エンコード====================
#ループ処理用
$cnt = 0
#プロセス引数
$file = "${ffpath}\ffmpeg.exe"
$arg = "-y -hide_banner -nostats -fflags +discardcorrupt -i `"${env:FilePath}`" ${audio_option} -vf yadif=0:-1:1,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0 -global_quality ${quality} -c:v h264_qsv -preset:v veryslow -g 300 -bf 16 -refs 9 -b_strategy 1 -look_ahead 1 -look_ahead_downsampling off -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 -map 0:p:${env:SID10}:0 -map 0:p:${env:SID10}:1 -movflags +faststart `"${tmp_folder_path}\${env:FileName}.mp4`""
Write-Output "Arguments:$arg"
$cnt = 0
ループカウント用。
$file
$arg
を指定するのは1度でいい為、do-while
の前に書いてある。
#ファイルサイズが0バイトの間実行
do {
#録画の開始終了でビジーなので負荷を減らすために10秒待つ
Start-Sleep -s 10
#エンコプロセス
ffprocess
#====================mp4ファイルサイズ判別====================
#エンコ後mp4のサイズを変数"mp4_size"に指定
$mp4_size = $(Get-ChildItem -LiteralPath "${tmp_folder_path}\${env:FileName}.mp4").Length
Write-Output "mp4:$([math]::round(${mp4_size}/1MB,0))MB"
#ループ処理用
$cnt = $cnt+1
Write-Output "エンコード回数:$cnt"
#mp4のファイルサイズ、エンコード回数による条件分岐
if (($cnt -gt 25) -Or ($mp4_size -gt 10GB)) {
#mp4が存在しない、50回失敗、10GBより大きい場合、ts、ts.program.txt、mp4を退避する
Move-Item -LiteralPath "${env:FilePath}" "${err_folder_path}"
Move-Item -LiteralPath "${env:FilePath}.program.txt" "${err_folder_path}"
Move-Item -LiteralPath "${tmp_folder_path}\${env:FileName}.mp4" "${err_folder_path}"
Write-Output "エンコード失敗:ts=$([math]::round(${ts_size}/1GB,2))GB mp4=$([math]::round(${mp4_size}/1MB,0))MB"
#ツイート
$env:tweet_content = "ERROR:${env:FileName}.tsと関連ファイルを退避しました。ログを確認して下さい。"
Start-Process "${ruby_path}" "${tweet_rb_path}" -WindowStyle Hidden -Wait
#バルーンチップ
$ToolTipIcon = 'Error'
$enc_result = '失敗'
BalloonTip
#強制終了
exit 1
} elseif ((-Not($mp4_size -eq 0)) -And ($mp4_size -le 10GB)) {
#mp4が0バイトでない且つ10GB以下ならmp4をbas_folder_pathに移動
Move-Item -LiteralPath "${tmp_folder_path}\${env:FileName}.mp4" "${bas_folder_path}"
Write-Output "エンコード終了:ts=$([math]::round(${ts_size}/1GB,2))GB mp4=$([math]::round(${mp4_size}/1MB,0))MB"
#バルーンチップ
$ToolTipIcon = 'Info'
$enc_result = '終了'
BalloonTip
}
} while ($mp4_size -eq 0)
do {処理内容} while (ループ条件)
で処理内容を行ってからループ条件ならループ、条件外なら終了。処理開始時点では$mp4_size
は0
で、必ず一度は処理内容を実行するため、do-while文を使用している。
Start-Sleep -s 10
で録画終了直後のビジー状態を避けてQSVのエラーが起こる確率を下げる。QSVのエラー時にループの効果を出すためでもある。
$file
$arg
でffprocess
を呼んでエンコード。標準エラー出力は終了時に出力される。
$cnt = $cnt+1
でループカウントを1大きくする。何度もループすると1ずつ大きくなっていく。
if (($cnt -gt 25) -Or ($mp4_size -gt 10GB)) {
25回ループしたか-Or
、mp4のファイルサイズが10GBより大きい場合実行。
Move-Item ファイルパス ディレクトリパス
でファイルをそれぞれ移動。ファイルが存在しないとか録画に失敗したとかでも何でも失敗ならここに来る雑実装だけど、そんなに機会が無いから許して。
$env:tweet_content="ERROR:${env:FileName}.ts…"
で環境変数$env:tweet_content
にツイート内容を格納。
悪名高きStart-Process
でRubyのパス"${ruby_path}"
とツイート用Rubyスクリプトのパス"${tweet_rb_path}"
を起動。-WindowStyle Hidden
でコンソール非表示、-Wait
で処理終了まで待つ。
$ToolTipIcon = 'Error'
$enc_result = '失敗'
で関数BalloonTip
を読んでエンコード失敗のバルーンチップを表示。
終了コード1
を付けてexit
強制終了。
} elseif ((-Not($mp4_size -eq 0)) -And ($mp4_size -le 10GB)) {
でif
の条件以外でもしelseif
(else if
ではない)(-Not($mp4_size -eq 0)
mp4のファイルサイズが0でない-And
且つ($mp4_size -le 10GB)
10GB以下なら実行。
Move-Item
でmp4を$bas_folder_path
に移動(うpが開始される)。
$ToolTipIcon = 'Info'
$enc_result = '終了'
で関数BalloonTip
を呼び出し、エンコード終了バルーンチップを表示。
お!わ!り!読んでくれてありがとう!!
自動エンコバッチの応用 見出しにジャンプ
ffmpegを繰り返し使う方は環境変数Pathに設定しておくと良い。ffmpegは色々なアプリケーションに使われているので競合してしまわないよう注意。
どれも基本的にD&Dで処理する用に書いてあるので、自動エンコバッチに導入する際は%1
を%FilePath%
に修正したり、rem _EDCBX_DIRECT_
を追加する等して使用して下さい。
また、内部に使用されている処理等を自分の用途に合わせたバッチをつくる際の参考にしてください。
応用編の更新情報 |
---|
2017/7
・溜まったtsの自動処理に役立ちそうなサンプルを置いといた ・役立ちそうなバッチサンプルを追加 ・実装しないが、番組情報から24fps化の選択を行うサンプルを追加 2017/8 ・夜間時間(東京電力)にエンコを実行して電気代を節約するサンプルを追加 ・ffmpegでtsを綺麗に切り出すサンプルを追加 ・ffmpeg+ImageMagickでtsからgifを作成するサンプルを追加 2017/9 ・キーフレーム切り出しの方法とffplayの使い方を追加 2018/1 ・D&Dでエンコするバッチで、ファイルかフォルダかを自動判別 ・ffmpegで動画ファイルをD&Dでキーフレーム連番jpgにするバッチを追加 ・tsをミリ秒単位でカットしてgif、mp4、avi等にエンコードするバッチを追加 2018/2 ・tsをカットするバッチはカットしない選択とgif、mp4、aviに加えjpgを作れるようにした ・カットバッチのmp4サブルーチンで0バイトならループするように雑実装した ・ウォータマーク(局ロゴ)を消す方法について記述 ・解説を若干丁寧に ・ServiceIDの判別のコードを整理 ・カットバッチでTSファイル以外への対応、カットしない場合の秒数指定の取り止め、リサイズしない選択肢の追加、jpg出力時にリサイズ出来るようにし1440x1080且つリサイズ指定が無い場合のみ1920x1080にリサイズするようにした ・更新情報を分けた ・複数aviファイルがあるフォルダをD&Dで連結するバッチを追加 ・カットバッチのビデオフィルタコンマ部分のバグ修正 ・カットバッチの修正、mp4出力時closed gop選択機能追加 ・カットバッチを動くように修正、9つまでのファイルの同時入力に対応した ・解説を追記 2018/4 ・カットバッチのバグ修正でビデオフィルタはプリセットを登録する方法に変更、処理の簡略化 2018/5 ・D&Dエンコバッチを終了コードによるループに変更した |
/blog/ffmpeg
後述のバッチ等にも多く使用されているので参考までに。
TSファイルを扱う際の基本 見出しにジャンプ
ffmpeg -i %1 -vf bwdif… -map 0:p:211:0 -map 0:p:211:1 "%~dpn1.mp4"
pause
・入力を%1
、出力を%~dpn1.mp4
に変更しただけ。
・ビデオフィルタ-vf
は前から順番に適用される。デインターレースしてプログレッシブスキャンにしてから、リサイズ時影響が大きいモスキートノイズ(デジタル放送特有の輪郭部に現れるアレ)を除去してからリサイズし、リサイズ時ジャギーもろとも諸々の・ノイズをお掃除してあげる感じにしてあげると綺麗且つ効率良くフィルタ処理を行える(例:-vf 高品質デインターレースyadif=0:-1:1
(モスキートノイズ除去pp=dr
),1280x720にリサイズscale=1280:720
,高精度平滑化hqdn3d
)。
・EpgTimerSrvで渡されていた%SID10%
を番組情報ファイルのServiceID(例えばBS11なら211、MXなら23608)に置き換える。番組情報ファイルが無い場合はffprobe 入力ファイルパス
で必要なServiceID(Program XXX)を見つける。
・D&D自動エンコバッチに使用されているcf.下記コードを使用すると、%SID10%
が使用できる。
rem ====================ServiceIDの判別====================
rem 番組情報ファイルからServiceIDのある行を抜き出し、区切り文字":""("で2番目のトークンを環境変数SID10に格納
rem ServiceID:211(0x00D3)から211だけを抜き出す
for /f "tokens=2 delims=:(" %%a in ('findstr ServiceID "%~1.program.txt"') do (
set SID10=%%a
)
トリミングしてavi出力 見出しにジャンプ
-ss
10秒目から50秒目まで(-t
40秒間)をavi出力
ffmpeg -ss 10 -i %1 -t 40 -vf yadif=0:-1:1,hqdn3d=3.000 -c:v rawvideo -c:a pcm_s16le "%~dpn1_enc.avi"
1秒あたり5コマ分png出力 見出しにジャンプ
md "%~dpn1"
ffmpeg -an -i %1 -vf yadif=0:-1:1 -c:v png -r 5 "%~dpn1\%%05d.png"
キーフレームをpng出力 見出しにジャンプ
md "%~dpn1"
ffmpeg -an -skip_frame nokey -i %1 -vf yadif=0:-1:1 -vsync 0 "%~dpn1\%%05d.png"
キーフレームをjpg出力 見出しにジャンプ
md "%~dpn1"
ffmpeg -an -skip_frame nokey -i %1 -vf yadif=0:-1:1 -f image2 -q:v 0 -vsync 0 "%~dpn1\%%05d.jpg"
再生 見出しにジャンプ
ffplay -i %1 -vf yadif=0:-1:1,hqdn3d=3.000
ffmpegだけでgifを作る 見出しにジャンプ
md "%~dpn1"
ffmpeg -y -ss 1400 -i %1 -t 1420 -an -vf yadif=0:-1:1,scale=640x360 -c:v rawvideo -r 5 "%~dpn1\temp.avi"
ffmpeg -y -i "%~dpn1\temp.avi" -vf palettegen "%~dpn1\palette.png"
ffmpeg -y -i "%~dpn1\temp.avi" -i "%~dpn1\palette.png" -lavfi paletteuse "%~dpn1\%~n1.gif"
D&D自動エンコバッチ 見出しにジャンプ
基本的には↑のバッチから削除機能、うp機能を消してD&D用に%FilePath%
から%1
等に変更しただけ。
D&Dされたのが"tsファイル"なのか"tsが溜まってるフォルダ"なのかを拡張子で判別し動作する。ファイルかフォルダかの判別は%1の末尾3文字が.ts
かどうかで行っている。
EDCBから渡されないServiceIDを番組情報ファイルから抜き出している為、番組情報ファイルがtsと同じディレクトリにあることが条件。
大まかな流れ
1.TSファイル又はTSファイルの入ったフォルダをD&Dで入力
1a.TSファイル:2以降をサブルーチンとし、一回だけ行う
1b.TSファイルの入ったフォルダ:2以降をサブルーチンとし、ファイル数分繰り返す
2.音声形式の判別
2a.デュアルモノ:左右を分離し2チャンネルに分ける
2b.その他:再エンコなし
3.ServiceIDの判別
4.tsファイルサイズ判別
4a.20GB以下:品質27
4b.20GBより大きい:品質29
5.エンコード
6.mp4ファイルサイズ判別
6a.0バイト
6a-1.0~49回目:5へ
6a-2.50~回目:警告
メインルーチン終了
カットバッチ 見出しにジャンプ
動画ファイルをミリ秒単位でカットしてgif、mp4、avi、連番jpgにエンコードするバッチ。
TSファイルに限らず動画であれば編集できる。最早動画編集ソフトなので、簡単な編集はこれで済ませている。
1度に9個までのファイルのD&Dに対応している。環境変数に登録すればWin
+R
->tscut ほげ.ts "ふ~.ts" fuga.ts
みたいに便利です。
ビデオフィルタはプリセットを登録することで使えます(最大2147483647個まで)。
・gif:ImageMagickとFFmpegを使用し、出来るだけ綺麗なgifを作成します。
・mp4:h264_qsv LA-ICQ出力です。品質を選択する機能があります。
・avi:rawvideo、pcm_s16le出力です。連番機能があるので同じソースを複数にカットし編集素材を作成するのに向いてます。
・jpg:ffmpegで直接jpgを切り出します。一定のコマ数かキーフレームを選ぶことができます。
ImageMagickとffmpegが環境変数に設定されていることが前提。
https://www.imagemagick.org/script/download.php#windows から"ImageMagick-xxx-Q16-(あなたのOSのアーキテクチャ)-(staticかdllお好みでどうぞ).exe"を(HTTPかFTPお好みでどうぞ)でDLしてインストール。
大まかな流れ
1.TSファイルをD&Dで入力(9個まで)
2.ファイル一覧を出力
3.それぞれのファイルでサブルーチンを呼ぶ
3-1.出力フォルダ作成
3-2.カット時刻の計算方式(hms、ミリ秒、カットしない)
3-2-1.hms表記のプレイヤで秒単位のカット
3-2-2.ffplayでミリ秒単位のカット
3-3.ビデオフィルタ選択
3-3-1.インターレース
3-3-1-1.解像度入力
3-3-2.プログレッシブ
3-3-2-1.解像度入力
3-4.出力形式選択
3-4-1.ImageMagickとffmpegでgif出力
3-4-2.ffmpegでmp4出力
3-4-3.ffmpegでavi出力
3-4-4.ffmpegでjpg出力
3-4-4-1.キーフレーム出力
3-4-4-2.コマ数出力
メインルーチン終了
連結バッチ 見出しにジャンプ
複数aviファイルがあるフォルダをD&Dで連結するバッチ。上のカットバッチと組み合わせて使うと良い。
@echo on
rem カレントディレクトリをD&Dしたフォルダに
cd /d %1
rem フォルダ内のaviを順番に呼んで
for %%a in ( *.avi ) do (
rem txtにファイルの名前のみ出力
@echo file %%~nxa >> input.txt
)
rem 結合
ffmpeg -f concat -safe 0 -i input.txt -global_quality 27 -c:v h264_qsv -preset:v veryslow -g 300 -bf 16 -refs 4 -b_strategy 1 -look_ahead 1 -look_ahead_downsampling off -pix_fmt nv12 -bsf:v h264_metadata=colour_primaries=1:transfer_characteristics=1:matrix_coefficients=1 -c:a aac -b:a 256k "%~1\enc.mp4"
pause
exit
ffplayにtsやmp4を関連付ける 見出しにジャンプ
ffplayは軽くて綺麗で対応する動画ファイルが多いのでオススメ。録画中のtsファイルをダブルクリックすれば追っかけ再生もできるよ。
設定 見出しにジャンプ
1.mp4等をプログラムから開く→その他のアプリ→ffplay.exeで関連付ける(ffplay "%1"
)
ってのが基本なんだが,録画したtsファイルはインターレース解除する必要があるので適切な引数(ffplay -i "%1" -vf yadif=0:-1:1
)を指定しなきゃいけない。
2.コマンドプロンプトを管理者で起動し以下コマンドを実行(CLASSES_ROOTにtsファイル実行時の挙動を設定,CURRENT_USERの"プログラムから開く"リストに引数付きffplayを追加)
".ts"ってのはtsファイルだよってwindowsに教え込む(別に"tsfile"じゃなくてもいいよ)
assoc .ts=tsfile
実行結果がこんな感じならおk
.ts=tsfile
さっきのtsファイルはffplayにほげほげな引数で実行するんだよと教え込む
ftype tsfile="C:\ffplay.exeのパス" -i "%1" -vf "yadif=0:-1:1"
※-vf "yadif=0:-1:1,hqdn3d=3.000"
にして補正処理を有効にしても良い
実行結果がこんな感じならおk
tsfile="C:\DTV\ffmpeg\ffplay.exe" -i "%1" -vf "yadif=0:-1:1"
3.レジストリエディタでHKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ts
キー(CURRENT_USERのtsファイル実行時の挙動の設定)を削除(CLASSES_ROOTの設定を反映)
4.tsをプログラムから開く→ffplay(新規とか書いてあるかも?)を選択
使い方 見出しにジャンプ
キー | 動作 |
---|---|
q,ESC | 終了 |
f | 全画面 |
p,SPC | 一時停止 |
m | ミュート |
9,0、/,* | 音量調節 |
a | 音声チャネル切り替え |
v | 映像チャンネル切り替え |
t | 字幕チャンネル切り替え |
c | プログラム切り替え |
w | 映像と音声波形切り替え |
s | コマ送り(暗黙的に一時停止) |
left/right | 10秒戻る/進む |
down/up | 1分戻る/進む |
page down/page up | チャプター戻る/進む,無い場合は10分戻る/進む |
right mouse click | 画面の幅を動画の時間とし右クリックした位置の割合までシーク |
left mouse double-click | 全画面 |
Seek to 81% ( 0:24:12) of total duration ( 0:30:00) B f=0/0
1453.65 A-V: -0.002 fd= 1 aq= 36KB vq= 411KB sq= 0B f=0/0
1453秒目を表示(PTS)という意味(カット編集に使える)。ただ録画したts等はタイムスタンプがおかしい場合が多いので、以下のバッチを関連付けすると開始がズレたPTSを修正しながら再生してくれる(無理矢理感あるが)。
ffmpeg -loglevel quiet -i %1 -c copy -mpegts_copyts 1 -f mpegts - | ffplay - -vf yadif=0:-1:1
※私の環境ではffmpeg3.3.4、3.4においてSDL advised audio format 33056 is not supported!
エラーにより音が出ないトラブルが発生した。3.3.3、3.4.1では発生しない。
番組情報から24fps化するか選択 見出しにジャンプ
危なっかしい、24fpsでないアニメがある、処理に時間がかかるので没になったやつ。
番組情報ファイルのジャンル :
と書いてある行の次の行にアニメ
又は映画
という文字列があれば24fps化する。
ジャンル : ←まずこの行を見つけて
映画 - 邦画←んでここの行をみて判断する
映画 - アニメ
映画 - その他
rem 番組情報の中に"ジャンル"という文字列がある行に行番号を付けて取得
for /f "delims=" %%a in ('findstr /n "ジャンル" "%~1.program.txt"') do (
set genre=%%a
)
rem 行番号の部分だけ取り出す
set genre=%genre:~0,-8%
rem 行番号を1大きくする(次の行に求める内容があるため)
set /a need_num=%genre%+1
rem その行に書いてある内容を抜く
for /f "delims=" %%a in ('findstr /n /r "." "%1" ^| findstr /r "^%need_num%:"') do (
rem その行にて"アニメ"又は"映画"という文字列の有無でフィルタオプションを選択
echo %%a ^| findstr "アニメ 映画"
if %ERRORLEVEL% equ 0 (
set vfilter=-yadif=0:-1:1,decimate
) else if %ERRORLEVEL% equ 1 (
set vfilter=-yadif=0:-1:1
)
)
rem エンコード
ffmpeg -i %1 -vf %vfilter%,hqdn3d …
夜間時間(東京電力)にエンコして電気代を節約? 見出しにジャンプ
先頭に追加すると動作します。ただのネタなので効果の程は知りません。詰まって時間内に処理しきれない場合は使わないで下さい。
rem dos標準の環境変数timeの先頭2文字が7以上且つ23未満の場合300秒待ってループ
:electric
if %time:~0,2% geq 7 (
if %time:~0,2% lss 23 (
timeout /t 300
goto :electric
)
)
rem ここから処理
字幕をmp4に付ける(ソフトサブ) 見出しにジャンプ
一度自動エンコに組み込んでみたが、そもそもgoogleドライブが対応していなかった。
以前EpgDataCap_Bon.exeで録画したtsではできるけどEdcbPlugin+TVTest.exeで録画したものでは字幕データが無いよって言われちゃう…
1.https://github.com/iGlitch/Caption2Ass からMPEG-TS字幕データ抽出ツールを入手
2.以下コマンドでsrt形式で字幕を抽出
"C:\hoge\Caption2AssC_x64.exe" -format srt %1
3.以下コマンドでエンコしながら結合
… -i %1 -f srt -i "C:\hogehoge.srt" … -map 0:p:hoge:0 -map 0:p:hoge:1 -map 1:0 -c:s mov_text "C:\hoge\%~n1.mp4"
ウォーターマーク(局ロゴ)を消す 見出しにジャンプ
黒背景のロゴ画像をもとにロゴを消す。CMカット等にも応用できるかもしれない。
チャンネルによって1920x1080のものと1440x1080のものがある。BS解像度変更の影響を受けるので注意。
1.以下の内容をバッチファイルとして保存する。
TSファイルD&Dでほぼほぼ真っ黒なシーンを探し、PNG出力し、2値化してffmpegで使えるロゴ解析用画像を作成するところを面倒なので自動化した。
邪道なコードだが殆ど使う機会がないので割愛。ffprobeの-f lavfi
でも出来るかも?
2.TSファイルロゴ消し
logo.png
は各局のロゴを指定。カレントディレクトリに置く必要がある。
録画後自動エンコバッチに組み込む際は、それぞれのロゴ画像のファイル名をChset5.txtの局名に合わせて(コピペ)removelogo=%ServiceName%.png
みたいにすると良いと思う。
ビデオフィルタ-vf
は前から順番に適用される為、インタレ解除yadif=0:-1:1
の後にロゴ消しremovelogo=logo.png
を行い、高精度平滑化等のノイズ除去hqdn3d
を行う必要がある。
cd /d "logo.pngのディレクトリ"
ffmpeg -i "TSファイルのフルパス" -vf yadif=0:-1:1,removelogo=logo.png,hqdn3d=4.0,scale=1280:720,unsharp=3:3:0.5:3:3:0.5:0