FFmpegで動画の一部を無音にするときは音声を削除せずに無音データで埋めないとダメ
FFmpeg に限らずですが、動画の一部を無音にするとき”音声データの削除”と”無音データに置き換え”の区別がついていないと爆死する。
作業手順
- 動画を切り分ける(音声を抜く部分、その前後、の 3 つに)
- 音声を無音に変換する
- 1 で分けた動画を結合する
音を消すだけじゃダメ
無音音声の動画と音声データがない動画は別のものです。
音声データがない動画を音声データのある動画と結合した場合、図のように部分的に音声データが欠落している動画となるのですが、 プレーヤーが上手いこと補ったりスルーしてくれるわけでもなく、再生時に不具合となります。
なので、代わりの音声データ(無音)をきちんと入れてあげましょう、という話。
以下、正しい作業手順を貼っておきます
具体的な手順
1. 動画を切り分ける
上記は切り出すコマンドなので、3 回叩きます。切り分けるコマンドもあるのかな?
50 秒-60 秒を無音にしたいのであれば
ffmpeg -i src.mp4 -t 50 before.mp4
ffmpeg -ss 50 -i src.mp4 -t 10 target.mp4
ffmpeg -ss 60 -i src.mp4 after.mp4
ですね。 -c copy
オプションは速いですが時間がズレるので、つけずにトランスコードしたほうが良いかと思います。
2. 音声を無音に変換する
ffmpeg -i target.mp4 -f lavfi -i aevalsrc=0 -c:v copy -map 0:0 -map 1:0 -shortest -ac 2 -strict -2 silence.mp4
軽く解説すると、ffmpeg は -i
オプションで指定されるデータ元を 0 からの連番で数えているので
- 0 番目のデータ
-i target.mp4
- 1 番目のデータ
-f lavfi -i aevalsrc=0
となります。この 1 番目のデータは FFmpeg のフィルタで無音データを生成しています。
出力部ですが、
-map [n番目の入力データの]:[n番目のストリーム]
という書式です。なので
-map 0:0 -map 1:0
は 0番目の入力データの0番目のストリーム
と 1番目の入力データの0番目のストリーム
を使うという意味になります。
他のオプションは
-c:v copy
: 映像データをトランスコードせずにそのまま使う-shortest
: 入力データのうち最も短いものに長さを合わせる。今回は 1 番目の入力がフィルタで無限なので、つけないと終わらない-ac 2
:1
はモノラル、2
はステレオ。元動画に合わせるといいかと-strict -2
: 音声コーデックが aac の場合に必要なもの。今回は明示的に-c:a
でコーデックを指定していないが、aac なので。
です。
3. 動画を結合する
らへんを参考にして結合。
ffmpeg -i "concat:before.mp4|silence.mp4|after.mp4" -c copy dst.mp4
以上。
動画の fps が途中で変わっているとか、音声の周波数が〜とか、細かい条件がなければこんな感じで 5 コマンドくらいですね。
無音ではなくピー音で置き換える場合は工程 2 の入力データをピー音に差し替えてあげてください。拾ってきてもいいし、 aevalsrc
を
aevalsrc="sin(880*2*PI*t)"
のように指定して音声を作ってあげてもいいかと。 880
が周波数なので大きくすれば高音に、小さくすれば低音になります。