FFmpeg は、ファイルの代わりにパイプ(|)でデータを受け取ったり、渡したりできます。標準入力(stdin)はプログラムがデータを受け取る口、標準出力(stdout)は結果を出す口です。
パイプを使う理由
- 中間ファイルや一時ファイルを作らずに処理でき、ディスクへの読み書きが減ります。メモリ上のデータも、そのまま渡せます。
- データを受け取りながら、その場で処理できます(ストリーミング処理)。
- curl、Python、Node.js などのプログラムと組み合わせられます。
pipe: プロトコルの基本
ファイル名を書く位置に、次の書き方をします。
| 記法 | 意味 |
|---|---|
pipe:0 または - |
標準入力(stdin) |
pipe:1 |
標準出力(stdout) |
pipe:2 |
標準エラー(stderr、通常はFFmpegのログ) |
標準入力から読み込む
stdin から MP4 を読んで変換
cat でファイルの中身を流し込み、FFmpeg は pipe:0(標準入力)から読み込みます。
cat input.mp4 | ffmpeg -i pipe:0 -c:v libx264 -crf 23 -c:a aac output.mp4
pipe:0 の代わりに、短く -i - とも書けます。
cat input.mp4 | ffmpeg -i - -c:v libx264 -crf 23 output.mp4
ただし、MP4 をパイプで読めるのは、moov(再生に必要な目次の情報)がファイルの先頭にあるときだけです。FFmpeg で普通に書き出した MP4 のように moov が末尾にあると、partial file や Invalid data found when processing input のエラーで止まります。その場合は、ffmpeg -i input.mp4 -c copy -movflags +faststart faststart.mp4 で moov を先頭に移したファイルを流すか、パイプを使わずに -i input.mp4 で直接渡してください。TS や MKV なら、そのままパイプで読めます。
標準出力に出力する
変換結果をstdoutに出力してパイプで送る
出力ファイル名の代わりに pipe:1(標準出力)を書きます。パイプには拡張子がないので、-f mp4 のように形式も指定します。
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -movflags frag_keyframe+empty_moov -f mp4 pipe:1 > output.mp4
MP4 を標準出力に書くときは、-movflags frag_keyframe+empty_moov が必要です。通常の MP4 は、書き終えたあとでファイルの先頭に戻って情報を書き込むので、後戻りできないパイプには書けません(muxer does not support non seekable output で止まります)。この指定を付けると、目次(moov)を先頭に置き、データを細かく区切って書く形式(fragmented MP4)になります。
curl との連携
curl で受け取った動画を、一時ファイルを作らずにそのまま FFmpeg に渡せます。
curl -s "https://example.com/video.mp4" | ffmpeg -i pipe:0 -c:v libx264 -crf 23 output.mp4
この方法も、moov が末尾にある MP4 では上と同じエラーで止まります。FFmpeg は URL を直接読めるので、-i に URL を書く方が安定します。
ffmpeg -i "https://example.com/video.mp4" -c:v libx264 -crf 23 output.mp4
サムネイル画像をstdoutに出力する
動画の1秒目のフレームを1枚だけ画像にして、標準出力に出します。例では、それを thumbnail.jpg に保存しています。
ffmpeg -i input.mp4 -ss 00:00:01 -frames:v 1 -f image2pipe pipe:1 > thumbnail.jpg
Web サーバーで、画像をファイルに保存せずにそのまま返したいときに便利です。
Pythonからパイプ経由でFFmpegを呼び出す
FFmpeg の出力を Python で受け取り、ファイルに書き込む例です。stdout=subprocess.PIPE で出力を受け取り、stderr=subprocess.DEVNULL でログを捨てています。
python3 -c "
import subprocess, sys
proc = subprocess.run(
['ffmpeg', '-i', 'input.mp4', '-c:v', 'libx264', '-crf', '23',
'-movflags', 'frag_keyframe+empty_moov', '-f', 'mp4', 'pipe:1'],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL
)
with open('output.py.mp4', 'wb') as f:
f.write(proc.stdout)
print('Done, size:', len(proc.stdout))
"
Pythonでstdin/stdoutの双方向パイプ
ファイルの中身を FFmpeg の標準入力に渡し、変換結果を標準出力から受け取る例です。入力の MP4 は、moov が先頭にあるもの(-movflags +faststart で書き出したものなど)を使ってください。moov が末尾にあると FFmpeg はエラーで止まり、出力は空になります。
python3 -c "
import subprocess
with open('input.mp4', 'rb') as inp:
proc = subprocess.run(
['ffmpeg', '-i', 'pipe:0', '-c:v', 'libx264', '-crf', '23',
'-movflags', 'frag_keyframe+empty_moov', '-f', 'mp4', 'pipe:1'],
stdin=inp,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL
)
with open('output.pipe.mp4', 'wb') as out:
out.write(proc.stdout)
print('完了')
"
パイプの制限事項
パイプを使うときは、次の制限があります。
| 制限 | 内容 |
|---|---|
| シーク不可 | stdin では、好きな位置に飛ぶこと(シーク)ができない |
| 入力フォーマット指定が必要 | -f rawvideo などで形式を指定しなければならないことがある |
| MP4出力は要注意 | 通常の MP4 は末尾に moov を書くため、fragmented MP4 にする必要がある |
rawvideoをパイプで処理する(上級)
カメラや自作プログラムが出す生の映像データ(rawvideo)を FFmpeg に渡す例です。生データには形式の情報が入っていないので、画素形式・サイズ・フレームレートを -i の前で指定します。
# rawvideoストリームをパイプで受け取る例(環境依存)
# フォーマット、サイズ、フレームレートを明示的に指定する必要があります
ffmpeg -f rawvideo -vcodec rawvideo -s 1920x1080 -r 30 -pix_fmt rgb24 -i pipe:0 \
-c:v libx264 -crf 23 output.mp4
よくある問題
Invalid data found when processing input(stdinから読む場合)
FFmpeg が入力の形式を判別できないときのほか、moov が末尾にある MP4 を流したときにも出ます。MP4 の場合は -f mp4 を付けても直らないので、-movflags +faststart で moov を先頭に移したファイルを流すか、-i input.mp4 でファイルを直接渡してください。形式の情報を持たない raw データなら、-i の前に -f rawvideo とサイズ・fps・pix_fmt を指定します。
stdoutへのMP4出力が muxer does not support non seekable output で止まる
通常の MP4 は moov をファイルの末尾に書くので、シーク(書き込み位置の移動)ができない stdout には書き出せません。FFmpeg は muxer does not support non seekable output と表示してヘッダーを書かずに止まり、出力は 0 バイトになります。-movflags frag_keyframe+empty_moov を付けて、fragmented MP4 にしてください。
つまずきやすいポイント
-
-ssでのシークが効かない、または遅い:stdin は好きな位置に飛べないので、シークは先頭からの読み飛ばしになります。シークが必要な処理では、-i ファイル名でファイルを直接渡すか、-i "URL"で URL を指定してください。 -
Python で、出力データにログが混ざって壊れる:FFmpeg のログ(stderr)と出力(stdout)を分けていないのが原因です。上の例のように、
stdout=subprocess.PIPEとstderr=subprocess.DEVNULLを別々に指定してください。
よくある質問
Q. pipe:0 と - は同じですか。
A. はい。入力では -i pipe:0 と -i - は同じ意味です。出力では、pipe:1 と - がどちらも標準出力を指します。
Q. なぜわざわざパイプを使うのですか。 A. 中間ファイルを作らないので、ディスクへの読み書きが減ります。curl や Python などのプログラムと直接つなげるうえ、メモリ上のデータもそのまま渡せます。
Q. URL の動画はパイプと直接 -i のどちらがよいですか。
A. シークや形式の判別が必要な処理では、-i "URL" で直接渡す方が安定します。moov が先頭にある MP4 や TS・MKV を先頭から流すだけなら、curl ... | ffmpeg -i pipe:0 でも動きます。
Q. Windows でもパイプは使えますか。
A. 使えます。pipe:0・pipe:1 の書き方は同じです。/dev/null を NUL にするなど、シェルごとの書き方だけを置き換えてください。ただし Windows PowerShell 5.1 では、> や | で受け渡した動画などのバイナリーデータが文字コードの変換で壊れるので、コマンドプロンプト(cmd.exe)か PowerShell 7.4 以降で実行してください。
Q. 出力を別の FFmpeg にそのまま渡せますか。
A. はい。ffmpeg ... -f mp4 pipe:1 | ffmpeg -i pipe:0 ... のようにつなげます。ただし、fragmented MP4 など stdout に出せる形式を選んでください。