What You Will Learn
- The difference between CRF mode and two-pass bitrate mode
- How to run the two-pass pipeline: analysis pass then encoding pass
- How to target a specific file size using bitrate math
- The
-passlogfileoption for custom log file locations - H.264 and H.265 two-pass examples
Tested version: FFmpeg 6.1 (ubuntu-latest / CI-validated)
Target OS: Windows / macOS / Linux
CRF Mode vs. Two-Pass Bitrate Mode
FFmpeg’s libx264 and libx265 encoders offer two main quality/bitrate control modes:
| Mode | Option | Use When |
|---|---|---|
| CRF (Constant Rate Factor) | -crf 18–28 | You want consistent quality, file size varies |
| Two-pass bitrate | -b:v N -pass 1/2 | You need to hit a specific file size or bitrate |
CRF is simpler and often preferred for archiving or streaming. Two-pass is the right choice when you must deliver a file that fits within a specific size budget (e.g., email attachment limits, disc capacity).
How Two-Pass Works
Pass 1 (Analysis): FFmpeg encodes the video quickly, writing a log file that records the complexity of each frame. No output video is produced — only the log file matters.
Pass 2 (Encoding): FFmpeg reads the log file to understand which scenes need more bits and which need fewer, then encodes the final output with the bitrate distributed according to the analysis.
The log file is named ffmpeg2pass-0.log (and ffmpeg2pass-0.log.mbtree for H.264) in the working directory.
Basic Two-Pass H.264 Example
Pass 1 — Analysis only, no output:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 1 -an -f null /dev/null
-b:v 1Msets the target video bitrate to 1 Mbit/s-pass 1runs the analysis pass-ansuppresses audio (not needed in pass 1)-f null /dev/nulldiscards the output (only the log file matters)
Pass 2 — Actual encode with analysis data:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 -c:a aac -b:a 128k output.mp4
-pass 2reads the log from pass 1 and encodes the final output- Audio is encoded in pass 2 only
Run both commands in the same directory — FFmpeg looks for ffmpeg2pass-0.log in the current working directory.
Targeting a Specific File Size
To calculate the bitrate for a target file size:
total_bitrate_kbps = (target_size_MB × 8192) ÷ duration_seconds
video_bitrate_kbps = total_bitrate_kbps − audio_bitrate_kbps
Example: Target 50 MB for a 4-minute (240-second) video with 128 kbps audio:
total = (50 × 8192) ÷ 240 ≈ 1707 kbps
video = 1707 − 128 = 1579 kbps ≈ 1.5M
Pass 1:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 1 -an -f null /dev/null
Pass 2:
ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 2 -c:a aac -b:a 128k output.mp4
Custom Log File Location
By default, FFmpeg writes the log to ./ffmpeg2pass-0.log. Use -passlogfile to specify a different path (useful in scripts or when encoding multiple files in parallel):
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 1 -passlogfile /tmp/mylog -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 -passlogfile /tmp/mylog -c:a aac output.mp4
Two-Pass H.265 (libx265)
Two-pass encoding with H.265 follows the same structure:
ffmpeg -i input.mp4 -c:v libx265 -b:v 800k -x265-params pass=1 -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx265 -b:v 800k -x265-params pass=2 -c:a aac output.mp4
Note: libx265 uses -x265-params pass=1 instead of -pass 1. The behavior is otherwise identical.
Adding Encoder Presets
You can combine two-pass with encoder presets. A slower preset gives the encoder more time to find efficient coding decisions:
ffmpeg -i input.mp4 -c:v libx264 -preset slow -b:v 1M -pass 1 -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -preset slow -b:v 1M -pass 2 -c:a aac -b:a 128k output.mp4
Use the same preset in both passes to ensure the log file and pass 2 analysis are consistent.
When to Use Two-Pass
| Scenario | Recommendation |
|---|---|
| Archiving with consistent visual quality | Use CRF mode (-crf 23) |
| File must fit a specific size limit | Use two-pass bitrate mode |
| Streaming delivery with guaranteed bitrate | Two-pass (or constrained bitrate with -maxrate) |
| Quick transcodes or previews | CRF mode (single pass) |
Common Pitfalls
Log File Not Found in Pass 2
If pass 2 runs in a different directory than pass 1, it cannot find ffmpeg2pass-0.log. Either run both in the same directory, or use -passlogfile /path/to/log in both passes.
Audio in Pass 1
Pass 1 with -an skips audio encoding, which is correct — the log only needs video complexity data. Including audio in pass 1 wastes time without benefit.
Same -b:v in Both Passes
Always use the same -b:v value in both passes. Using different values means the log from pass 1 targets a different bitrate than pass 2, producing unreliable results.
Related Articles
- Compressing Video — CRF and Bitrate Targets
- Video Format Conversion — Transcoding to MP4
- Changing Frame Rate
Tested with: ffmpeg 6.1.1 / Ubuntu 24.04 (GitHub Actions runner)
Primary sources: trac.ffmpeg.org/wiki/Encode/H.264 / ffmpeg.org/ffmpeg.html