【ffmpeg】fps情報がおかしい動画ファイルの修正

# 概要
次のようなフレームレート(fps)がおかしい動画ファイルの修正を実施。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/db2e6c68-69c7-eb97-712f-83a6cf1ce0a2.png)


# 原因
恐らく、PTSやDTSといった情報が不整合を起こしているためこのようになっていると思われる。
参考：[ffmpeg を使うなら知っておきたい話 PTSとかDTSの話：音ずれ問題や時間が変になるときのために ヽ\(ﾟｰﾟ\*ヽ\)\(ﾉ\*ﾟｰﾟ\)ﾉわぁい \- Qiita](https://qiita.com/scleen_x_x/items/7f857f2d08de22dee274)

# 前提
- ffmpeg及びffprobeを導入済みであること

- ffmpeg/ffprobe version
`version N-105859-g37480b1b85-20220305`

- 対象とする動画ファイル名は「inputFile.mp4」とする

# 解決方法
音声ストリーム、動画ストリームをそれぞれを書き出し、fpsを指定して結合(Mux)し直す。

## 手順1 ファイル情報の確認
Windowsではファイルのプロパティから最初の図のようにフレームレートを確認することができるが、
念のため`ffprobe`を用いて確認する。

```
ffprobe -i inputFile.mp4 -hide_banner
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/1ffdb50b-3154-4a04-92a3-29aac2d13895.png)


## 手順2 元の動画ファイルのフレームレートを確認する
元の動画ファイルのフレームレートが不明な場合は`ffprobe`を使用して求める。
※フレームレートが判明している場合はこの手順は不要

### 手順2-1 総フレーム数の取得
```
ffprobe -v error -select_streams v -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 inputFile.mp4
```

**ここで得られたフレーム数を「108000」とする。**

<details><summary>各種オプションの補足</summary>

- `-v error` 
ffprobeの出力結果のみにする
- `-select_streams v`
動画ストリームのみを選択
- `-show_entries stream=nb_frames`
エントリーの表示。 `stream=nb_freams`でフレーム数の表示。
- `-of default=nokey=1:noprint_wrappers=1`
`default=nokey=1`：キーの非表示
`noprint_wrappers=1`：セクションの非表示

</details>

### 手順2-2 再生時間の取得
音声ファイルは正常と考え、音声ストリームの再生時間を取得する。（単位は秒）
```
ffprobe -v error -select_streams a -show_entries stream=duration -of default=nokey=1:noprint_wrappers=1 inputFile.mp4
```
**ここで得られた再生時間を「3600」とする。**

<details><summary>各種オプションの補足</summary>

- `-select_streams a`
音声ストリームのみを選択
- `-show_entries stream=duration`
エントリーの表示。 `stream=duration`で再生時間の表示。
</details>

### 手順2-3 フレームレートの算出
手順2-1で取得した総フレーム数を、
手順2-2で取得した再生時間で割る。

108000 / 3600 = 30

よって元動画ファイルのフレームレートは「30fps」となる。


## 手順3 音声ファイルのみを抜き出す
「audio.aac」というファイル名で音声ファイルを抜き出す

```
ffmpeg -i inputFile.mp4 -vn -c:a copy audio.aac
```

<details><summary>各種オプションの補足</summary>

- `-vn` 
動画ストリームを無効にする。
- `-c:a copy`
入力ファイルの音声コーデックと出力ファイルのコーデックを同じにする。
`-codec:a copy`や`-acodec copy`と同等
</details>


## 手順4 動画ストリームだけを抜き出す
H.264形式で書き出す。
```
ffmpeg -i inputFile.mp4 -an -c:v copy -f h264 noaudio.h264
```

<details><summary>各種オプションの補足</summary>

- `-an` 
音声ストリームを無効にする
- `-c:v copy`
入力ファイルの動画コーデックと出力ファイルのコーデックを同じにする。
`-codec:v copy`や`-vcodec copy`と同等
- `-f h264`
フォーマットを指定する。「H264」を指定する。
</details>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/e4a37f55-d115-cc1a-53e3-7ad3e715a32b.png)
上図のようにエラーが出る場合があるが無視。
※DTSは増加していく情報であるが、そのようになっていないフレームがあるとのエラーのよう。
「Application provided～」のログレベルはエラーに設定されているが、処理が止まることはなく問題なく出力される。
ソースコードを見ると、今回はそれ以外の原因でエラーとなっている気がする。
https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mux.c#L540-L549


## 手順5 音声と動画を結合(Mux)する
手順3で作成した音声ファイルと、手順4で作成した動画ファイルを結合する。
`-r 30`には、元ファイルと同じフレームレートを指定する。（今回は30）
```
ffmpeg -r 30 -i noaudio.h264 -i audio.aac -c:v copy -c:a copy outputFile.mp4
```
<details><summary>各種オプションの補足</summary>

- `-r` 
フレームレートの指定
</details>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1180204/16d77149-7637-b68a-c0b7-475fd83ff22e.png)
noaudio.h264ファイルにPTS情報がないので警告が出ているが、ffmpegがいい感じにやってくれる。

# 完成
です。
# 蛇足
- 手順2でフレームレートを算出しているが、手順4で作成した.h264ファイルに対して
```
ffprobe -v error -show_entries stream=r_frame_rate -of default=nokey=1:noprint_wrappers=1 noaudio.h264
```
とすることでフレームレートがわかる。

- そもそもフレームレートを算出しておかなくても、手順5の出力時に`-r`で指定しなければ.h264ファイルのフレームレート情報を見に行ってか、ffmpegがなんかいい感じにやってくれる。
今回はなんとなく指定した。

- 可変フレームレートの場合は知りません。

- ストリームの順番が入力ファイルと異なってしまって困る場合は、`-map`オプションを使用して、明示的に指定してください。

# 参考
- [Using ffmpeg to change framerate \- Stack Overflow](https://stackoverflow.com/questions/45462731/using-ffmpeg-to-change-framerate)
- [ffprobe の使い方 \| ニコラボ](https://nico-lab.net/how_to_use_ffprobe/)
- [ffprobe Documentation](https://ffmpeg.org/ffprobe.html)
- [ffmpeg を使うなら知っておきたい話 PTSとかDTSの話：音ずれ問題や時間が変になるときのために ヽ\(ﾟｰﾟ\*ヽ\)\(ﾉ\*ﾟｰﾟ\)ﾉわぁい \- Qiita](https://qiita.com/scleen_x_x/items/7f857f2d08de22dee274)