2021年6月10日木曜日

ffmpegで「動画の途中を削除」する方法

 

はじめに

動画の途中だけ削除したい時がある。簡単そうな処理だが、ffmpegはシンプルなコマンド一発でこれを行うことが出来ない。ffmpegでこの処理を行う方法は主に2つ。

まず普通に思いつくであろう1つ目の方法は、先頭から任意時点までと、任意時点から末尾までを切り出した2つの動画を最後に結合するというもの。ffmpegは「真ん中だけを削除」する簡単なコマンドが無いだけで、これらは最後に示すように簡単に出来る。

2つ目は、ffmpegのfilter_complexオプションにffmpegのフィルタリング用スクリプトのようなものを渡し、指定区間切り取りと結合を一気に処理させるというもの。

1つ目のものは比較的誰でも思いつき、検索結果にもすぐに現れやすいので、2つ目の方法について説明する。

また、1つ目の方法で処理を行った場合、後半の動画の開始部分(結合部分直後)で動画並びに音声が不安定化する現象に遭遇したので、記事執筆時点でのffmpegを用いる場合にはあまりおすすめしない。

やり方

コマンドは以下の様になり、Windowsバッチファイルに記述することを想定しているので改行が^であることに注意。下のコマンドでは0秒から5秒までの区間と55秒から59秒までの区間のみを切り出して結合する処理を行っている。元動画の長さは1分。別の言い方をすれば5秒後から55秒までの区間が削除された動画を作成している。

ffmpeg -y -i in.mp4 -filter_complex ^
"[0:v]trim=start=00\\:00\\:00:end=00\\:00\\:05,setpts=PTS-STARTPTS[v0]; ^
[0:a]atrim=start=00\\:00\\:00:end=00\\:00\\:05,asetpts=PTS-STARTPTS[a0]; ^
[0:v]trim=start=00\\:00\\:55:end=00\\:00\\:59,setpts=PTS-STARTPTS[v1]; ^
[0:a]atrim=start=00\\:00\\:55:end=00\\:00\\:59,asetpts=PTS-STARTPTS[a1] ;^
[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]^
" -map "[v]" -map "[a]" out.mp4

注意点

fileter_complexのフィルターを書くときに軽くハマったのだが、タイムスタンプに使う「:」は二重バックスラッシュ「\」でエスケープしないと正常に動作しない。なぜなら、フィルターの区切り文字のような物に「:」が割り当てられているから。二重にする理由はバックスラ自体もエスケープしなければならないから。その他のコロンはエスケープしなくても問題ない部分もある。startやendはタイムスタンプ形式ではなく秒数やフレームナンバーも指定できる。

また、ビデオストリームとオーディオストリームでtrimとatrim、並びにsetptsとasetptsを使い分けなくてもエラーになる。

実際にはタイムスタンプや入出力ファイル名等をバッチファイルの引数の変数にすることで一般的に扱える様になるだろう。

フィルター文法の詳細については正直調べる気も起こらないし詳しく知らない。自分で動画編集プログラムでも書いてしまったほうが早そうな気がするほど煩雑で説明が不親切に感じるが、一応フィルタリングガイドなるものは存在する。しかし正直StackOverflowが一番役に立つ。

簡単なコマンドでこの処理を行う方法もあるのかもしれないが検索結果にすぐに出てこない。理解し難い仕様だ。

バッチ処理用に少し一般化

set e0=%2
set b1=%3
set e1=%4
ffmpeg -y -i %1 -filter_complex ^
"[0:v]trim=start=00\\:00\\:00:end=%e0::=\\:%,setpts=PTS-STARTPTS[v0]; ^
[0:a]atrim=start=00\\:00\\:00:end=%e0::=\\:%,asetpts=PTS-STARTPTS[a0]; ^
[0:v]trim=start=%b1::=\\:%:end=%e1::=\\:%,setpts=PTS-STARTPTS[v1]; ^
[0:a]atrim=start=%b1::=\\:%:end=%e1::=\\:%,asetpts=PTS-STARTPTS[a1] ;^
[v0][a0][v1][a1]concat=n=2:v=1:a=1[v][a]^
" -map "[v]" -map "[a]" %5

バッチファイル名をdel.batとしたとき、del.bat [入力ファイル][削除開始ts][削除終了ts][動画末尾ts][出力ファイル]、となっている。動画末尾tsを再生時間より前にした場合、その後の区間は削除される挙動となる。

del.bat in.mp4 00:00:05 00:00:55 00:01:00 out.mp4、の様に使う。

filter_complexを使用しない方法

ついでだが、簡単なffmpegコマンドの組み合わせで動画の途中を削除する方法は以下のようになる。動画の任意指定区間切り取りは非常に簡単に出来、最初から途中までや、途中から最後まで切り取るという処理と結合コマンドは簡単に書けるため、それらを利用した方法である。

ffmpeg -y -ss 0 -i %1 -to %2 -c copy head.mp4
ffmpeg -y -ss %3 -i %1 -c copy tail.mp4
ffmpeg -y -f concat -i list.txt -c copy res.mp4

%1とか%2はWindowsバッチファイルの引数の変数で、ファイル名やタイムスタンプが入ると思ってくれれば良い。しかしこの方法で出来上がった動画は、先にも書いたとおり、後半の動画の最初の部分で動画と音声ともに不安定になるという現象を確認したのであまりおすすめできない。

手軽にやりたいからそういうのは別に気にならないという場合には良いかも。list.txtというマージ対象ファイルを指定するファイルが必要になるがそれは以下のような内容になる。

file head.mp4
file tail.mp4

0 件のコメント:

コメントを投稿

Google Cloud SDKにおけるAPIキーとOAuthキーの使い所の違い

  Google Cloud SDKを使っていると、APIキーのみで済むものとOAuthキーを求められるものとの使い分けを要求される。認証方式の堅牢性によって使い分けが必然的に要求されるのだろうが、用途は以下のようにわけられているらしい。 アプリケーションに求められる内容と実行さ...