ffmpeg によるビデオファイルの結合方法は公式に載っているのだけどたまにしかやらなくて忘れるのでメモっておく。
この方法で検索するとだいたいファイルリスト作って読み込ませる方法を使っているようなのだけどファイルリストを作るのが面倒なのでできればパイプで済ませたいところ。ポイントは下記の通り。
- ファイルリスト内のファイルはクォートされてなくてもよい
- ファイルリスト内のファイルパスがフルパスまたは相対パスでない場合は
-i FILE
で指定したファイルのプレフィックスが使用される - ファイルリストの標準入力に
-
は使用できない
ファイルリスト内のファイルはクォートされてなくてもよいがファイル名による
ファイル名にスペースが入っている場合はシングルクォートで括る必要がある。ファイル名自体にシングルクォートを使われているとパースがおかしくなり、この場合はダブルクォオートで括ったとしても入力が -i /dev/stdin
の場合は変な結合をされてしまう。
# OK file /path/to/file.mp4 file '/path/to/file.mp4' file '/path/to/file name.mp4' # NG: ファイル名にスペースが入っていてシングルクォートで括られていない file /path/to/file name.mp4 # NG: ダブルクォートが使われている file "/path/to/file.mp4" # NG: ファイル名にシングルクォートが使われている file /path/to/file's.mp4 # NG: ファイル名にシングルクォートが使われている file "/path/to/file's.mp4"
ファイルリスト内のファイルパスがフルパスまたは相対パスでない場合やダブルクォートが使われている場合は -i /path/to/file
で指定したファイルのディレクトリ名が使用される
ffmpeg -f concat -safe 0 -i /dev/stdin -c copy out.mp4 <<\! file file.mp4 !
Impossible to open '/dev/file.mp4'
/dev/stdin: No such file or directory
ファイルパスをダブルクォートで括った場合はそのままプレフィックスに結合される。
ffmpeg -f concat -safe 0 -i /dev/stdin -c copy out.mp4 <<\! file "/path/to/file.mp4" !
Impossible to open '/dev/"/path/to/file.mp4"'
/dev/stdin: No such file or directory
ファイルリストの標準入力に -
は使用できない
ffmpeg -f concat -safe 0 -i - -c copy out.mp4 <<\! file /path/to/file.mp4 !
これは -i
でファイルリストではなく動画ファイルを開こうとしてしまうため NG。
Protocol 'pipe' not on whitelist 'crypto,data'!
Impossible to open 'pipe:0001.mp4'
公式ドキュメントのプロセス置換を使った方法のうち、find
を使用したものは macOS では BSD find に -printf
オプションが無いため使用できない。プロセス置換が使えないシェルに関しても同様にこれらの方法は使えない。
ffmpeg -f concat -safe 0 -i <(for f in ./*.wav; do echo "file '$PWD/$f'"; done) -c copy output.wav ffmpeg -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" ./*.wav) -c copy output.wav ffmpeg -f concat -safe 0 -i <(find . -name '*.wav' -printf "file '$PWD/%p'\n") -c copy output.wav
プロセス置換も使わない場合の単純な方法は下記の方法になる。ただしこれでもファイル名にシングルクォートが使われている場合は正しくファイルパスが結合されないのでファイル名を変えるなどした方がいいかもしれない。
\ls $PWD/*.mp4 \ | sed 's/\(.*\)/"\1"/;s/^/file /' \ | ffmpeg -f concat -safe 0 -i /dev/stdin -c copy out.mp4
Homebrew 版の ffmpeg を使用している場合に Automator のクイックアクションに雑に登録するならこんな感じ。Finder のファイル選択はフルパスで飛んでくるので難しく考える必要は無いが、注意する点として Automator の Finder 入力では「ファイルのみ」という指定ができないためディレクトリが飛んできた場合は除外しなければならない。入力に「stdin」を選択して行頭に file
を追加しただけではディレクトリが紛れ込む可能性があるので「引数として」にして 1 ファイルごとに判別した方が良い。
cd "$(dirname "${1}")" for f do test -f "${f}" || continue echo "file '${f}'" done \ | /usr/local/bin/ffmpeg -f concat -safe 0 -i /dev/stdin -c copy -y $(date +%Y%m%d%H%M%S).mp4