FFmpegにおける最低限のエンコードで動画のカット編集を行う方法を考えた
背景
FFmpegを用いて動画のカット編集機能を実装したいと考えた。
しかし、エンコードは負荷の大きな処理かつ時間もかかる。
そのため、何も考えずに実装してしまうと、サーバーのリソースが枯渇する可能性がある。
そこで、最低限のエンコードでカット編集機能を実現できないかを考えた。
実現したいこと
指定した区間をカットし、分割された動画を結合して1本の動画にする。
イメージは以下のような感じ。
エンコードを気にしない場合
エンコードの負荷を気にしないのであれば、以下のコマンドでカット編集ができる。
動画のカット
「0秒地点からカット区間の左端」「カット区間の右端から最後まで」の2カットを以下のコマンドで実行する。
ffmpeg -i { 入力ファイルパス } -ss { 開始時間 } -to { 終了時間 } { 出力ファイルパス }
例えば、100秒の動画で40秒地点から50秒地点をカットしたい場合、
ffmpeg -i { 入力ファイルパス } -ss 0 -to 40 { 出力ファイルパス }
ffmpeg -i { 入力ファイルパス } -ss 50 -to 100 { 出力ファイルパス }
となる。
動画の結合
ファイルリストを作成する。
ファイルリストには結合したいファイルのパスを記載する。
例えば、「video.mp4」と「video2.mp4」を結合する場合は以下のように記載する。
file video1.mp4
file video2.mp4
上記のファイルをテキストファイルとして保存する。
※今回の例ではfilelist.txt
として保存
以下のコマンドを実行することで、結合した動画が生成される。
ffmpeg -f concat -i filelist.txt { 出力ファイルパス }
最低限のエンコードで実装する場合
方針
エンコードなしでカット処理を走らせることはできるが、正確性が失われてしまう。
これは、再エンコードなしでカットする場合はキーフレーム位置でしかカットできないためである。
そのため、任意の位置でカットしたい場合はエンコードが必要となる。
つまり、エンコードの有無によって、速度と正確性どちらかを失うことを意味する。
カット編集において速度と正確性どちらも非常に重要な要素であるため、どちらかを失うことは避けたい。
そこで、速度と正確性を考慮した以下のロジックを考案した。
まず、以下のように動画1, 動画2, 動画3, 動画4にカットを行う。
- 動画1
- 0秒地点から左端のカット地点直前のキーフレームまでをカット
- キーフレームでカットするため、エンコードなし
- 動画2
- 左端のカット地点直前のキーフレームから左端のカット地点まで
- 任意の地点でカットするため、エンコードあり
- 動画3
- 右端のカット地点から右端のカット地点直後のキーフレームまでをカット
- 任意の地点でカットするため、エンコードあり
- 動画4
- 右端のカット地点直後のキーフレームから最後まで
- キーフレームでカットするため、エンコードなし
上記4つの動画をエンコードなしで結合することで、カット編集を行った1本の動画を生成する。
この方法の場合、必要最低限のエンコードに抑えているため、高速かつ正確にカットすることができる。
動画のカット
まず、動画1, 動画2, 動画3, 動画4を生成する。
各動画の定義は以下となる。
- 動画1
- 0秒地点から左端のカット地点直前のキーフレームまでをカット
- キーフレームでカットするため、エンコードなし
- 動画2
- 左端のカット地点直前のキーフレームから左端のカット地点まで
- 任意の地点でカットするため、エンコードあり
- 動画3
- 右端のカット地点から右端のカット地点直後のキーフレームまでをカット
- 任意の地点でカットするため、エンコードあり
- 動画4
- 右端のカット地点直後のキーフレームから最後まで
- キーフレームでカットするため、エンコードなし
各コマンドは以下のようになる。
-c copy
を付けることでエンコードなしで処理が実行される。
※キーフレーム位置はffprobe
を用いることで調べることができる(説明は割愛)
// 動画1
ffmpeg -i { 入力ファイルパス } -ss 0 -to { キーフレームの時間 } -c copy { 出力ファイルパス }
// 動画2
ffmpeg -i { 入力ファイルパス } -ss { キーフレームの時間 } -to { カット地点の時間 } { 出力ファイルパス }
// 動画3
ffmpeg -i { 入力ファイルパス } -ss { カット地点の時間 } -to { キーフレームの時間 } { 出力ファイルパス }
// 動画4
ffmpeg -i { 入力ファイルパス } -ss { キーフレームの時間 } -to { 動画尺 } -c copy { 出力ファイルパス }
動画の結合
ファイルリストを作成する。
ファイルリストには結合したいファイルのパスを記載する。
今回は4本の動画を結合するため、例えば以下のようになる。
file 動画1.mp4
file 動画2.mp4
file 動画3.mp4
file 動画4.mp4
上記のファイルをテキストファイルとして保存する。
※今回の例ではfilelist.txt
として保存
以下のコマンドを実行することで、結合した動画が生成される。
ffmpeg -f concat -i filelist.txt -c copy { 出力ファイルパス }
まとめ
今回紹介したロジックを用いることで高速かつ正確性を担保したカット処理を実現することができる。
Discussion