ffmpeg覚書


# はじめに
ffmpegコマンドの使い方が複雑でいつも分からなくなる。
使ったことのあるコマンドをここに書いておこう。

# ストリーム内容の確認
以下のコマンドでファイルの概要っぽいのが（標準エラー出力に）表示される。
```sh
ffprobe <input_file>
```

ストリーム内容の詳細を表示。概要情報は邪魔なので`/dev/null`に捨てる。
```sh
ffprobe -show_streams <input_file> 2> /dev/null
```

参考: https://qiita.com/hapoon/items/0e7d2f941e5da8cec8d1

# 基本のエンコード
`-i`は入力ファイルを指定するオプション。
出力ファイルの拡張子をみてコーデックやビットレートをよしなに選んでくれるらしい。
出力が.mp4なら、ビデオコーデックはH.264で品質(crf値)は23, オーディオコーデックはAAC.
```sh
ffmpeg -i <input_file> <output_file>
```

# 末尾をカット
`-t`で出力する持続時間を指定できるので、入力ファイルより短い時間を指定すれば末尾をカットできる。
`<duration>`は秒単位の数値か、もしくはhh:mm&zwnj;:ss.msの形式。
`-c`はコーデックの指定。`-c copy`はエンコードせず入力データをコピーするの意。
末尾カットだけなら再エンコード不要なのでこれを指定する。
```sh
ffmpeg -i <input_file> -t <duration> -c copy <output_file>
```

参考: https://nico-lab.net/cutting_ffmpeg/

# 先頭をカット
`-ss`で出力の開始点を指定できる。秒単位の数値。
`-ss`を`-i`より前に書くのがポイントらしい。
再エンコードしないとうまくいかないので`-c copy`は無し。
```sh
ffmpeg -ss <start_time> -i <input_file> <output_file>
```

参考: https://nico-lab.net/cutting_ffmpeg/

# 先頭・末尾をカット
先頭の指定は`-ss`で行う。
末尾の指定は`-t`ではなく`-to`を使った方がよい。
なぜなら`-t`だと「`-ss`後の時点からの時間」を指定する必要があるが、
`-to`だと「入力動画開始時点から数えた時間」を指定できるから。
hh:mm&zwnj;:ss.ms形式で指定すると分かりやすい。
```sh
ffmpeg -ss <start_time> -to <duration> -i <input_file> <output_file>
```

参考:
https://nico-lab.net/cutting_ffmpeg/
https://jp.videoproc.com/edit-convert/cut-videos-using-ffmpeg.htm

# 動画ファイルの映像と音声を分離
`-c:v`と`-c:a`はそれぞれビデオコーデックとオーディオコーデックの意。
`copy`を指定してエンコードせず入力データをコピーする。
`-an`は音声を消去。
```sh
ffmpeg -i <input_file> -c:v copy -an <output_video_file> # 映像だけを取り出す
ffmpeg -i <input_file> -c:a copy <output_audio_file> # 音声だけを取り出す
```

# 動画の音ズレを修正
映像と音がずれた動画ファイルは、上記コマンドで映像と音声に分離した後に音声をずらして再結合すれば音ズレを修正できる。

音が先行している場合（音声先頭に無音を追加）：
`-map 0:v`は0番目のファイルのビデオストリーム、`-map 1:a`は1番目のファイルのオーディオストリームの意。
`-af`はオーディオフィルタの指定。`adelay`は先頭に無音を挿入するフィルタ。左右チャネルそれぞれの無音時間を'|'で区切って指定する。
`<left_delay>`と`<right_delay>`部分に数値（ミリ秒単位）を入れる。なお数値に's'を後置すれば秒単位、'S'を後置すればサンプル単位で指定できる。
```sh
ffmpeg -i <input_video_file> -i <input_audio_file> -map 0:v -map 1:a -c:v copy -af 'adelay=<left_delay>|<right_delay>' <output_file>
```

音が遅れている場合（音声先頭をカット）：
音声入力ファイルに対して`-ss`で先頭部分をカットする。

```sh
ffmpeg -i <input_video_file> -ss <start_time> -i <input_audio_file> -map 0:v -map 1:a -c copy <output_file>
```

# tbnの修正
上記コマンドで末尾や先頭の不要部分をカットした複数の動画(.mp4ファイル)を繋げようとしたところ、
どうもうまくいかない。
どうやら`-ss`で先頭カットしたときにtbnという数値が変わるのが原因のようだ。
tbnとは、コンテナ側に書かれたストリームのタイムベース、だそうだがよくわからん（後述）。
この数値を修正するには`-video_track_timescale`というオプションを使うようだ。MOV, MP4コンテナ専用。
```sh
ffmpeg -i <input_file> -video_track_timescale <tbn> -c copy <output_file>
```

# 連結
コーデック、解像度、fpsなどが同じ複数の動画ファイルを連結する場合、再エンコードなしでできる。

次のような内容のlist.txtを用意して、
```text
file input1.mp4
file input2.mp4
```
次のコマンドでinput1.mp4, input2.mp4の順で連結。
```sh
ffmpeg -f concat -i list.txt -c copy <output_file>
```

list.txtの中身を絶対パスで指定したときは、`-safe 0`をつけるっぽい。

参考: https://nico-lab.net/concat_with_ffmpeg/

# メタデータの引継ぎ
カメラで撮影した動画(.MOV)ファイルのサイズが大きかったので、適当に再エンコードしたところ、
元ファイルに付与されていたcreation_timeというメタデータが出力ファイルに引き継がれなかった。
creation_timeは、Windowsのエクスプローラでは「メディアの作成日時」と表示される。つまり元ファイルの撮影日時。
これが消えてしまうと不便なので、出力ファイルに同じ日時を引き継ぎたい。

まず、元の動画ファイルに含まれるメタデータをmetadata.txtというテキストファイルにすべて書き出す。
`-map_metadata 0`は、0番目のファイルのメタデータを使えという指示。
`-f ffmetadata`は、入出力形式がメタデータだという指示。
`-map_metadata:s:v 0:s:v -map_metadata:s:a 0:s:a`が、全てのメタデータを出力する指定らしい。これがないと、creation_timeが書き出されない。
```sh
ffmpeg -i <input_file> -map_metadata 0 -map_metadata:s:v 0:s:v -map_metadata:s:a 0:s:a -f ffmetadata metadata.txt
```
そして動画を変換する際、metadata.txtも入力データとして使う。
```sh
ffmpeg -i <input_file> -f ffmetadata -i metadata.txt -map_metadata 1 <output_file>
```

上記コマンドでは再エンコードしているが、新たなメタデータを付与するだけなら`-c copy`もつければいいと思う。

参考:
https://so-zou.jp/software/tool/video/ffmpeg/
https://nico-lab.net/adding_metadata_with_ffmpeg/
https://stackoverflow.com/questions/11706049/converting-video-formats-and-copying-tags-with-ffmpeg

# 複数の画像ファイルを動画にする
画像をたくさん用意して、パラパラ漫画のような動画を生成したい。
予めファイル名に連番をつけておくこと。img000.jpgからimg999.jpgのように。
`-r <fps>`は生成する動画のfps指定。秒間何枚の画像を表示するか。
`-i img%03d.jpg`は、3桁の連番の入力ファイル。
`-c:v libx264 -pix_fmt yuv420p`はコーデック指定。正直yuv420pの意味はよく分かっていない。
`-s <WxH>`は出力の横サイズと縦サイズ。指定しなくてもいいのかも。入力画像が不揃いのとき用？
```sh
ffmpeg -r <fps> -i img%03d.jpg -c:v libx264 -pix_fmt yuv420p -s <WxH> <output_file>
```

参考: https://qiita.com/livlea/items/a94df4667c0eb37d859f

# fps, tbr, tbn, tbc
ffprobeしたときに表示されるfpsとかtbrとかtbnとかが何を意味しているのか調べた。

fps(AVStream.avg_frame_rate): 「動画ファイルのフレーム数 / 動画ファイルの長さ（秒数）」で算出されるフレームレート。
tbr(AVStream.r_frame_rate): ビデオストリームから推測されるフレームレート。
tbn(AVStream.time_base): タイムスケール(tick/秒). 一秒を何tickとして数えるか。これの逆数をタイムベースという。
tbc(AVCodecContext.time_base): コーデック用のタイムスケール。deprecatedらしい。

fpsとtbrは同じはずなんだけど、インターレース動画だとtbrが2倍になっていたりする。
**なっていなかったりもする**ので混乱する。

tbnの修正には`-video_track_timescale`オプションを使う。

参考:
https://superuser.com/questions/1362410/what-is-fps-tbr-tbn-tbc-in-ffmpeg/1759811
https://stackoverflow.com/questions/3199489/meaning-of-ffmpeg-output-tbc-tbn-tbr
https://video.stackexchange.com/questions/20789/ffmpeg-default-output-frame-rate
https://nico-lab.net/setting_fps_with_ffmpeg/

# 入力がインターレース動画のとき
ソニーのDSC-W200で撮った動画がAVCHD形式(m2tsファイル)の動画だった。
重たいし、mp4にしたかったのでffmpegすることにした。
しかし何のオプションも指定せずにmp4に変換すると、横縞ノイズが発生してしまう。
どうやらインターレース動画だったようだ。

インターレース解除してプログレッシブ動画にするには
```sh
ffmpeg -i <inputfile> -vf bwdif=0:-1:0 <outputfile>
```
とすればできた。
`bwdif`がインターレース解除のフィルタ。
`0:-1:0`の3つの数値はそれぞれ「2枚のフィールドから1枚のフレームを作る」「トップフィールドファーストかボトムフィールドファーストか自動判別」「全フレームを処理する」の意。

参考: https://nico-lab.net/bwdif_with_ffmpeg/

しかし、出力動画がカクついて見えるため、結局インターレース解除を諦めて
インターレース維持して再エンコードすることにした。
色々試行錯誤の結果、
```sh
ffmpeg -i <inputfile> -flags +ilme+ildct -top -1 <outputfile>
```
というコマンドでいけたが、もうオプションの意味とか覚えてない。疲れた。

参考: 
http://anpcf.blog71.fc2.com/blog-entry-16.html
https://ugcj.com/ffmpeg%E3%81%A7%E3%83%9C%E3%83%88%E3%83%A0%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%AC%E3%83%BC/
http://mobilehackerz.jp/archive/wiki/index.php?%BA%C7%BF%B7ffmpeg%2F%A5%D3%A5%C7%A5%AA%A5%AA%A5%D7%A5%B7%A5%E7%A5%F3

トップフィールドファーストかボトムフィールドファーストか判別するには:
https://stackoverflow.com/questions/24945378/progressive-or-interlace-detection-in-ffmpeg

# フレーム数のカウント
後で書く。