FFmpeg の使い方 (基本)

[FFmpeg](https://www.ffmpeg.org/) の基本的な使い方です。

<!--
## 概要

FFmpeg の処理は以下のような流れになります。

![基本フロー](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/589796dc-3a6f-4654-afea-1f8e1b1b2234.png)

- muxer/demuxer(マルチプレクサー/デマルチプレクサー) : ファイルやストリームから動画･音声データを取り出したり、特定の形式に変換する。
- encoder/decoder(エンコーダー/デコーダー) : 動画・音声を圧縮・展開する。
- filter(フィルター) : 特定のフレームを抜き出す。
-->

## コマンド形式

ffmpeg コマンドの指定は以下のような形になっており、入力、出力をそれぞれ複数指定できます。個々の入出力に対してオプションが指定できるため、オプションを指定する位置によりどの入出力に対する指定になるかが変わることに注意が必要です。(例えば入力に対するオプションは `-i` の**前に**指定する。)

    ffmpeg \
        (グローバルオプション) \
        (入力1オプション) -i (入力ソース1) \
        (入力2オプション) -i (入力ソース2) \
        ... \
        (出力1オプション) (出力先1) \
        (出力2オプション) (出力先2) \
        ...

参考)

- [ffmpeg](https://www.ffmpeg.org/ffmpeg.html)

## 入力の指定

### 動画ファイルから入力

![動画ファイルから](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/db501f18-d37a-4b70-b057-1d54433c3f60.png)

入力ソースに動画ファイルを指定した場合、動画形式は自動判別されます。(`-f` オプションで指定も可。)

    ffmpeg \
        -i input.mp4 \
        (出力)

同じ動画を繰り返したい場合は `-stream_loop` オプションでループ回数を指定します。エンドレスで繰り返したい場合は `-1` を指定します。

    ffmpeg \
        -stream_loop -1 -i input.mp4 \
        (出力)

### 動画ファイルから入力 (複数ファイルを連結)

![動画ファイルから](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/ce336d9a-1167-47be-aa95-99fb626e1b1c.png)

先に、動画ファイルのリストをテキストファイルで以下のような内容で作成しておきます。(例: input.txt)

    ffconcat version 1.0
    file 'input1.mp4'
    file 'input2.mp4'
    file 'input3.mp4'

`-f` オプションで入力形式として concat を指定し、作成したテキストファイルを入力ソースに指定します。

    ffmpeg \
        -f concat -i input.txt \
        (出力)

ファイルリスト内から動画ファイルを絶対パスで指定したい場合は `-safe 0` オプションが必要です。

    ffmpeg \
        -f concat -safe 0 -i input.txt \
        (出力)

同じ動画リストを繰り返したい場合は、`-stream_loop` オプションを使います。

    ffmpeg \
        -stream_loop -1 -i input.txt \
        (出力)

参考)

- [FFmpeg Formats Documentation - concat](https://ffmpeg.org/ffmpeg-formats.html#concat-1)
- [FFmpeg Wiki - Concatenate](https://trac.ffmpeg.org/wiki/Concatenate)

### RTSP から入力

![RTSP から](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/0eac842a-6190-4d24-a1e0-cd0dcdada7e4.png)

ネットワークカメラなどから RTSP で動画を取得します。

    ffmpeg \
        -i rtsp://... \
        (出力)

動画データはデフォルトではオーバーヘッドが少なくリアルタイム性の高い UDP が使われますが、ロスなく信頼性を高い TCP で転送をおこなう場合は `-rtsp_transport tcp` オプションを指定します。

    ffmpeg \
        -rtsp_transport tcp -i rtsp://... \
        (出力)

参考)

- [FFmpeg Protocols Documentation - rtsp](https://ffmpeg.org/ffmpeg-protocols.html#rtsp)

### MotionJPEG から入力

![MotionJPEG から](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/1cd4ea7a-0af9-4649-bb0a-a0f9d76b2b9a.png)

MotionJPEG ではフレームレートを自動判別できないため、入力に合った値を `-r` オプションで正しく指定する必要があります。(指定しないと出力の再生スピードがおかしくなります。)

    ffmpeg \
        -r 5 -i http://... \
        (出力)

## 出力の指定

### 動画ファイルへ出力

![動画ファイルへ](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/ce9e115d-4e14-499f-8da2-487a88905a9d.png)

ファイルへ出力する場合、指定したファイル名の拡張子に合わせた形式で自動的に出力されます。(`-f` オプションで指定も可。)

    ffmpeg \
        (入力) \
        output.mp4

### 画像ファイルへ出力

![動画ファイルへ](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/5190743a-a2c4-4176-9e45-caa15d481f8d.png)

出力に画像形式を指定すると、全てのフレームを指定したパターンのファイル名に出力します。(以下の例の場合は output01.jpg, output02.jpg, ... というファイルがそれぞれ生成されます。)

    ffmpeg \
        (入力) \
        output%02d.jpg

一部だけ出力する場合は `-frames:v` オプションでフレーム数を指定します。

    ffmpeg \
        (入力) \
        -frames:v 10 output%02d.jpg

切り抜く間隔を指定する場合は `-r` オプションでフレームレートを指定します。(以下の例の場合は 5 フレーム/秒となり、2 秒ぶんの 10 フレームが保存されます。)

    ffmpeg \
        (入力) \
        -frames:v 10 -r 5 output%02d.jpg

動画の途中から切り抜きたい場合は、入力側のオプションで先頭位置を指定します。(以下の例の場合は開始 10 秒後から 10 フレームが保存されます。)

    ffmpeg \
        -ss 10 -i ... \
        -frames:v 10 output%02d.jpg

### HLS 配信用ファイルを出力

`-f` オプションで出力形式に hls を指定します。出力にはプレイリストのファイル名を指定し、`-hls_segment_filename` オプションで出力するセグメントファイル名のパターンを指定します。(以下の例の場合は output.m3u8 と output01.ts, output02.ts, ... というファイルがそれぞれ生成されます。)

    ffmpeg \
        (入力) \
        -f hls -hls_time 3 -hls_list_size 20 -hls_segment_filename output%02d.ts output.m3u8

キーフレームがない場所で動画ファイルを分割することができないため、セグメント長を期待する長さに揃えたい場合は調整する必要があります。

例)

    ffmpeg \
        (入力) \
        -codec:v libx264 -g 30 -keyint_min 30 -sc_threshold 0 -r 30 \
        -f hls -hls_time 3 -hls_list_size 20 -hls_segment_filename output%02d.ts output.m3u8

参考)

- [FFmpeg Formats Documentation - hls](https://ffmpeg.org/ffmpeg-formats.html#hls-2)

### RTMP サーバーへ出力

![RTMP サーバーへ](https://hirose-public.s3-ap-northeast-1.amazonaws.com/qiita/2021/04/30/85730965-be26-4b23-ab6f-bb680bfc7fb3.png)

`-f` オプションで出力形式に flv を指定し、出力先に URL を指定します。

    ffmpeg \
        (入力) \
        -f flv rtmp://...

参考)

- [FFmpeg Formats Documentation - flv, live_flv, kux](https://ffmpeg.org/ffmpeg-formats.html#flv_002c-live_005fflv_002c-kux)

## 出力オプション: エンコード

`-codec:v` オプション(または `-c:v`/`-vcodec` でも同じ)で動画のコーデックを、`-codec:a` オプション(または `-c:a`/`-acodec` でも同じ)で音声のコーデックを指定します。

### 無変換

動画も音声も無変換の場合は `-codec copy` と指定できます。

    ffmpeg \
        (入力)
        -codec copy (出力)

### 動画を H.264 でエンコード

動画のコーデックに `libx264` を指定します。

    ffmpeg \
        (入力) \
        -codec:v libx264 (出力)

参考)

- [FFmpeg Codecs Documentation - libx264, libx264rgb](https://ffmpeg.org/ffmpeg-codecs.html#libx264_002c-libx264rgb)
- [FFmpeg Wiki - H.264 Video Encoding Guide](https://trac.ffmpeg.org/wiki/Encode/H.264)

エンコードオプションは `ffmpeg -h encoder=libx264` でも確認できます。

#### H.264: プリセット選択

`-preset` オプションで ultrafast, medium, veryslow などエンコード処理の速さを選択できる形になっており、サイズや品質とのトレードオフがあります。デフォルトは medium です。よりエンコードに時間をかけてサイズを減らしたい場合は slow、サイズはそこそこにより速くエンコードしたい場合は fast といった形で調整します。

    ffmpeg \
        (入力) \
        -codec:v libx264 -preset superfast (出力)

#### H.264: 最適化

`-tune` オプションで映像の種類に応じた最適化をおこなうことができます。`film`(映画), `animation`(アニメーション), `stillimage`(スライドショーなど動きが少ないもの) などが指定できます。

    ffmpeg \
        (入力) \
        -codec:v libx264 -tune film (出力)

#### H.264: プロファイル指定

`-profile:v` オプションで H.264 プロファイルを指定することができます。古いプレイヤーなど特定のプロファイルしか再生できない場合のみ使用します。デフォルトは `auto` です。

    ffmpeg \
        (入力) \
        -codec:v libx264 -profile:v baseline (出力)

#### H.264: 品質設定

H.264 エンコードの品質は `-crf` オプションで CRF (Constant Rate Factor) を指定します。CRF のデフォルト値は 23 で、数値が少ないほうが高品質でサイズ増となります。18 でほぼ無劣化に見える品質とのこと。

    ffmpeg \
        (入力) \
        -codec:v libx264 -crf 28 (出力)

ビットレートの上限を制限したい場合は `-maxrate` を指定できます。この場合、上限を超える場合は CRF が自動調整されます。

    ffmpeg \
        (入力) \
        -codec:v libx264 -crf 18 -maxrate 1M -bufsize 2M (出力)

#### H.264: ビットレート指定

出力サイズをある程度一定にする場合は CRF ではなく `-b:v` オプションで平均ビットレート(bps)を指定します。厳密な固定ビットレート (CBR) ではありません。

    ffmpeg \
        (入力) \
        -codec:v libx264 -b:v 1M (出力)

H.264 に CBR モードは存在しませんが、以下のように指定することで実現することができます。

    ffmpeg \
        (入力) \
        -codec:v libx264 -x264-params "nal-hrd=cbr" -b:v 1M -minrate 1M -maxrate 1M -bufsize 2M (出力)

#### H.264: フレームレート指定

出力のフレームレートを変更するには、`-r` オプションで指定します。

    ffmpeg \
        (入力) \
        -codec:v libx264 -r 5 (出力)

#### H.264: キーフレーム間隔

キーフレームを調整するオプションは以下のとおりです。

|オプション|概要|単位|例|
|:--|:--|:--|:--|
|g|キーフレーム(最大)間隔 = GOP 長|frames|`-g 30`|
|keyint_min|キーフレーム最小間隔|frames|`-keyint_min 30`|
|sc_threshold|シーン変更検出閾値 (デフォルト 40%)|frames|`-sc_threshold 20`|

キーフレーム間隔(GOP 長)はキーフレーム間の「最大」フレーム数で、この間隔に満たない場合でもシーン変更検出閾値を超える変化があった場合はキーフレームが挿入されます。

    ffmpeg \
        (入力) \
        -codec:v libx264 -g 30 (出力)

プリセット指定時に調整したい場合は、これらのオプションではなく `-x264-params` オプションで上書きします。

    ffmpeg \
        (入力) \
        -codec:v libx264 -preset slow -x264-params keyint=123:min-keyint=20 (出力)