What You Will Learn

  • How to use FFmpeg’s pipe protocol (pipe:0, pipe:1)
  • Reading data from standard input (-i pipe:0 or -i -)
  • Writing to standard output (pipe:1 or -)
  • Processing video from a URL directly by combining with curl
  • Calling FFmpeg from Python via pipes
  • Caveats and the no-seek limitation

Tested version: FFmpeg 6.1
Target OS: Windows / macOS / Linux


Why Use Pipes

With FFmpeg’s pipe feature you can:

  • Process without writing files: No intermediate files, reducing disk I/O
  • Stream processing: Process data in real time as it arrives
  • Integrate with other programs: Combine with curl, Python, Node.js, and more
  • Process in-memory data: Avoid creating temporary files on disk

Pipe Protocol Basics

NotationMeaning
pipe:0 or -Standard input (stdin)
pipe:1Standard output (stdout)
pipe:2Standard error (stderr, normally FFmpeg’s log)

Reading from Standard Input

Read an MP4 from stdin and convert it

cat input.mp4 | ffmpeg -i pipe:0 -c:v libx264 -crf 23 -c:a aac output.mp4

Or using the shorthand -i -:

cat input.mp4 | ffmpeg -i - -c:v libx264 -crf 23 output.mp4

Writing to Standard Output

Output the converted result to stdout and pipe it onward

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -f mp4 pipe:1 | cat > output.mp4

Note: MP4 output to stdout requires -movflags frag_keyframe+empty_moov (because a normal MP4 writes the moov atom at the end of the file):

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -movflags frag_keyframe+empty_moov -f mp4 pipe:1 > output.mp4

Integration with curl

Pass video from a URL directly into FFmpeg (no temporary file):

curl -s "https://example.com/video.mp4" | ffmpeg -i pipe:0 -c:v libx264 -crf 23 output.mp4

Alternatively, specifying the URL directly with -i is more reliable:

ffmpeg -i "https://example.com/video.mp4" -c:v libx264 -crf 23 output.mp4

Outputting a Thumbnail Image to stdout

ffmpeg -i input.mp4 -ss 00:00:01 -frames:v 1 -f image2 pipe:1 > thumbnail.jpg

This is handy when returning a binary response directly from a web server.


Calling FFmpeg from Python via Pipes

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))
"

Bidirectional stdin/stdout Pipe in Python

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('完了')
"

Pipe Limitations

Important limitations when using pipes (stdin/stdout):

LimitationDetails
No seekingstdin cannot seek (no random access)
Input format must be specifiedYou may need to declare the format explicitly, e.g. -f rawvideo
MP4 output needs careA normal MP4 writes the moov atom at the end, so fragmented MP4 is required

Processing rawvideo Through a Pipe (Advanced)

When passing raw video frames (raw data from a camera or your own program) into FFmpeg:

# Example of receiving a rawvideo stream through a pipe (environment-dependent)
# You must explicitly specify the format, size, and frame rate
ffmpeg -f rawvideo -vcodec rawvideo -s 1920x1080 -r 30 -pix_fmt rgb24 -i pipe:0 \
  -c:v libx264 -crf 23 output.mp4

Common Problems

Invalid data found when processing input (when reading from stdin)

  • Occurs when FFmpeg cannot detect the input format
  • Specify the format explicitly, e.g. -f mp4

The MP4 output to stdout is corrupted

  • The normal MP4 format is incompatible with stdout
  • Specify -movflags frag_keyframe+empty_moov to produce a fragmented MP4

Common Pitfalls

  • Symptom: an MP4 sent to stdout will not play or is corrupted. Cause: a normal MP4 writes the moov atom at the end of the file, which conflicts with non-seekable stdout. Fix: add -movflags frag_keyframe+empty_moov to produce a fragmented MP4, exactly as in the examples above.

  • Symptom: Invalid data found when processing input when reading from stdin. Cause: FFmpeg cannot auto-detect the input format. Fix: declare the format explicitly before -i, e.g. -f mp4. For raw data, use -f rawvideo along with the size, fps, and pix_fmt.

  • Symptom: seeking with -ss does not work or is slow. Cause: stdin has no random access, so a seek becomes a read-and-discard from the start. Fix: when seeking matters, pass the file directly with -i filename where possible, or use -i "URL".

  • Symptom: in Python, stderr mixes in and corrupts the data. Cause: FFmpeg’s log (stderr) is not separated from its output (stdout). Fix: as in the examples, set stdout=subprocess.PIPE and stderr=subprocess.DEVNULL separately.


FAQ

Q. Are pipe:0 and - the same thing? A. Yes. For input, -i pipe:0 and -i - are equivalent. For output, pipe:1 and - both refer to standard output.

Q. Why bother using pipes at all? A. They avoid intermediate files (reducing disk I/O), let you wire FFmpeg directly to other programs like curl or Python, and let you stream in-memory data without touching disk.

Q. For a URL, should I use a pipe or -i directly? A. When seeking or format detection matters, passing -i "URL" directly is more reliable. For a simple straight-through pass, curl ... | ffmpeg -i pipe:0 also works.

Q. Do pipes work on Windows? A. Yes. Just substitute shell-specific notation, such as NUL instead of /dev/null. The pipe:0 and pipe:1 tokens themselves are the same everywhere.

Q. Can I pass the output straight into another FFmpeg? A. Yes. You can chain them: ffmpeg ... -f mp4 pipe:1 | ffmpeg -i pipe:0 .... Just pick a format that stdout can handle, such as fragmented MP4.



Primary sources: ffmpeg.org/ffmpeg-protocols.html#pipe / ffmpeg.org/ffmpeg.html