FFmpegで素早く正確に動画をカットする自分的ベストプラクティス


ちょっと昔のリリースになるのですが、FFmpeg 2.1から動画の一部分を切り出す性能が大幅に上がりました。ここでは、バージョン2.1以降のFFmpegを前提に、動画を素早く正確にカットする自分的ベストプラクティスをメモします。

*※ドキュメントをよく読めば書いてあるのですが、結構ハマりどころなので自分でテストもしてみました*

## はじめに...結論を

はじめに結論を言ってしまうと、バージョン2.1以降であれば

* 開始地点（-ss）はソースの指定（-i）より手前に置く
* 必ずエンコードする（-codec copyとかやらない）

とやるのが一番正確に（かつそこそこ速く）カットできます！例えばこんな感じです。

```
ffmpeg -ss [開始地点(秒)] -i [入力する動画パス] -t [切り出す秒数] [出力する動画パス]
```

## 背景を少し

### これまで

FFmpegはバージョン2.1以前でも簡単に動画を切り出すことができました。例えば、

```
ffmpeg -i input.mp4 -ss 300 -t 60 output.mp4
```

とやると、input.mp4の5分地点から1分間の動画を切り出すことができます。しかしこれには問題があり、 **切り出し開始地点までシークするのにめっちゃ時間かかるんです！**

このあたりの挙動は公式ドキュメントの[Seeking-FFmpeg](https://trac.ffmpeg.org/wiki/Seeking)に書かれていて、曰く「-ssを書く位置によってシークの早さが変わるよ。-ssを-iより手前に置くと爆速だよ。」とのこと。

実際に-ssを-iより手前に置いてやってみると、確かにシークは早くなる（ほぼ一瞬）のですが、トレードオフとして正確性が損なわれます。10秒くらい平気でズレます。

### バージョン2.1以降

しかし、2.1リリース後に上記のドキュメントにこんな内容が追記されました。

    As of FFmpeg 2.1, when transcoding with ffmpeg (i.e. not just stream copying), -ss is now also "frame-accurate" even when used as an input option. Previous behavior (seeking only to the nearest preceding keyframe, even if not precisely accurate) can be restored with the -noaccurate_seek option.

つまり、-ssを-iより手前に置いても **トランスコードさえすれば** 正確にシークできるようになったと。これは嬉しい！

## 各パターンでテストしてみる

テストといっても4パターンだけなのですが、それぞれ何が起こるのか実際に動かしてみました。

### パターン

以下の変数を掛け合わせた計4パターンで、処理時間の違いや、切り出される動画の正確性の違いを確認していきます。

* -ssの位置
  * -iより手前
  * -iより後ろ
* トランスコード
  * やらない（-codec copyを指定）
  * やる

### 実施内容

[スイスアルプスを優雅に駆け抜ける約2時間のフライト動画](https://www.youtube.com/watch?v=3bztcDL5v3I)から、ちょうどマッターホルンが見えてくる1時間15分地点から1分間の動画を切り出します。

|パターン|コマンド|
|:---|:---|
|1a. -ss手前,エンコなし|ffmpeg -ss 4500 -i input.mp4 -t 60 -vcodec copy -acodec copy 1a.mp4|
|1b. -ss手前,エンコあり|ffmpeg -ss 4500 -i input.mp4 -t 60 1b.mp4|
|2a. -ss後ろ,エンコなし|ffmpeg -i input.mp4 -ss 4500 -t 60 -vcodec copy -acodec copy 2a.mp4|
|2b. -ss後ろ,エンコあり|ffmpeg -i input.mp4 -ss 4500 -t 60 2b.mp4|

### 結果

正確に切り出しができたのはトランスコードを行った1bと2bですが、パフォーマンス面で1bの方が優れています。トランスコードを行わなかった1aと2aは、正確性面で何らかの問題がありました。バージョン2.1で何が嬉しくなったかといえば、1bのやり方でも正確に切り出せるようになったことなのです！

|パターン|処理時間|ffprobeより抜粋|その他備考|結果動画|
|:---|:---|:---|:---|:---|
|1a. -ss手前,エンコなし|0.19秒|Duration: 00:01:01.86<br>start: -1.853605|指定した-tより1.86秒<br>長く切り出されている。|[見る](https://dl.dropboxusercontent.com/u/259122/Qiita/FFmpeg%E3%81%A7%E7%B4%A0%E6%97%A9%E3%81%8F%E6%AD%A3%E7%A2%BA%E3%81%AB%E5%8B%95%E7%94%BB%E3%82%92%E3%82%AB%E3%83%83%E3%83%88%E3%81%99%E3%82%8B%E8%87%AA%E5%88%86%E7%9A%84%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9/1a.mp4)|
|1b. -ss手前,エンコあり|24.8秒|Duration: 00:01:00.04<br>start: 0.036281||[見る](https://dl.dropboxusercontent.com/u/259122/Qiita/FFmpeg%E3%81%A7%E7%B4%A0%E6%97%A9%E3%81%8F%E6%AD%A3%E7%A2%BA%E3%81%AB%E5%8B%95%E7%94%BB%E3%82%92%E3%82%AB%E3%83%83%E3%83%88%E3%81%99%E3%82%8B%E8%87%AA%E5%88%86%E7%9A%84%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9/1b.mp4)|
|2a. -ss後ろ,エンコなし|1.38秒|Duration: 00:01:00.00<br>start: 0.002993|はじめの3秒くらい<br>映像が存在しない。|[見る](https://dl.dropboxusercontent.com/u/259122/Qiita/FFmpeg%E3%81%A7%E7%B4%A0%E6%97%A9%E3%81%8F%E6%AD%A3%E7%A2%BA%E3%81%AB%E5%8B%95%E7%94%BB%E3%82%92%E3%82%AB%E3%83%83%E3%83%88%E3%81%99%E3%82%8B%E8%87%AA%E5%88%86%E7%9A%84%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9/2a.mp4)|
|2b. -ss後ろ,エンコあり|133.26秒|Duration: 00:01:00.04<br>start: 0.036281|-ssの値が大きければ<br>大きいほどシークに<br>時間がかかる。|[見る](https://dl.dropboxusercontent.com/u/259122/Qiita/FFmpeg%E3%81%A7%E7%B4%A0%E6%97%A9%E3%81%8F%E6%AD%A3%E7%A2%BA%E3%81%AB%E5%8B%95%E7%94%BB%E3%82%92%E3%82%AB%E3%83%83%E3%83%88%E3%81%99%E3%82%8B%E8%87%AA%E5%88%86%E7%9A%84%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9/2b.mp4)|

---

以上、何かのお役に立てれば幸いです。