Hori Blog

フリーランスでバックエンドエンジニアとして活動している Ryota Hori のブログです。
最近はテック系記事より雑記ブログ気味。

FFmpegで動画の一部を無音にするときは音声を削除せずに無音データで埋めないとダメ

FFmpeg に限らずですが、動画の一部を無音にするとき”音声データの削除”と”無音データに置き換え”の区別がついていないと爆死する。

作業手順

  1. 動画を切り分ける(音声を抜く部分、その前後、の 3 つに)
  2. 音声を無音に変換する
  3. 1 で分けた動画を結合する

音を消すだけじゃダメ

無音音声の動画と音声データがない動画は別のものです。

無音音声の動画と音声データがない動画の違い

音声データがない動画を音声データのある動画と結合した場合、図のように部分的に音声データが欠落している動画となるのですが、 プレーヤーが上手いこと補ったりスルーしてくれるわけでもなく、再生時に不具合となります。

なので、代わりの音声データ(無音)をきちんと入れてあげましょう、という話。

以下、正しい作業手順を貼っておきます

具体的な手順

1. 動画を切り分ける

FFmpeg で秒数を指定して動画を切り出すワンライナー

上記は切り出すコマンドなので、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 で MP4 を結合(concat)するワンライナー

MP4Box で MP4 を結合(concat)するワンライナー

らへんを参考にして結合。

ffmpeg -i "concat:before.mp4|silence.mp4|after.mp4" -c copy dst.mp4

以上。

動画の fps が途中で変わっているとか、音声の周波数が〜とか、細かい条件がなければこんな感じで 5 コマンドくらいですね。

無音ではなくピー音で置き換える場合は工程 2 の入力データをピー音に差し替えてあげてください。拾ってきてもいいし、 aevalsrc

aevalsrc="sin(880*2*PI*t)"

のように指定して音声を作ってあげてもいいかと。 880 が周波数なので大きくすれば高音に、小さくすれば低音になります。