この記事でわかること

  • CRFモードと2パスビットレートモードの違い
  • 2パスパイプラインの実行方法:分析パスとエンコードパス
  • ビットレート計算で特定のファイルサイズをターゲットにする方法
  • カスタムログファイルの場所を指定する-passlogfileオプション
  • H.264とH.265の2パスの例

テスト済みバージョン: FFmpeg 6.1(ubuntu-latest / CI検証済み)
対象 OS: Windows / macOS / Linux

⚠️ 実行前に必ず確認してください(passlogファイル汚染のリスク)

  • 古い passlog ファイルが残っていると、パス2が誤ったデータを参照してエンコードが静かに破壊されます
  • 複数ファイルをバッチ処理する場合、-passlogfile にファイルごとのユニークな名前を必ず指定してください
  • エンコード完了後は passlogfile を削除してください
  • パス1とパス2は必ず同じディレクトリで実行するか、両方に同じ -passlogfile パスを指定してください

CRFモードと2パスビットレートモードの違い

FFmpegのlibx264libx265エンコーダーには2つの主要な品質/ビットレート制御モードがあります:

モードオプション使い所
CRF(固定レートファクター)-crf 18〜28品質を一定にしたい、ファイルサイズは変動してよい
2パスビットレート-b:v N -pass 1/2特定のファイルサイズやビットレートを達成する必要がある

CRFはシンプルで、アーカイブやストリーミングに多く使われます。2パスは特定のサイズ制限(メール添付ファイルの上限、ディスク容量など)に収める必要がある場合に適しています。


2パスの仕組み

パス1(分析): FFmpegが動画を高速にエンコードし、各フレームの複雑さを記録したログファイルを書き出します。出力動画は生成されません — ログファイルだけが重要です。

パス2(エンコード): FFmpegがログファイルを読み込んでどのシーンにより多くのビットが必要かを理解し、分析に基づいてビットレートを配分しながら最終出力をエンコードします。

デフォルトでは ffmpeg2pass-0.log(H.264ではffmpeg2pass-0.log.mbtreeも)が作業ディレクトリに生成されます。バッチ処理では必ず -passlogfile でユニークな名前を指定してください。


基本的な2パスH.264の例

推奨:エンコードごとにユニークな passlogfile を使う

パス1 — 分析のみ、出力なし:

ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 1 \
  -passlogfile /tmp/passlog_myproject -an -f null /dev/null

パス2 — 分析データを使った実際のエンコード:

ffmpeg -i input.mp4 -c:v libx264 -b:v 1M -pass 2 \
  -passlogfile /tmp/passlog_myproject -c:a aac -b:a 128k output.mp4

エンコード完了後に passlog を削除:

rm -f /tmp/passlog_myproject.log /tmp/passlog_myproject.log.mbtree
  • -pass 1 / -pass 2 で1パス目・2パス目を指定
  • -passlogfile でログファイルのパスを明示(デフォルトの ffmpeg2pass-0.log はバッチ処理で衝突するため非推奨
  • -an でパス1の音声エンコードをスキップ(ログには映像データのみ必要)
  • -f null /dev/null で出力を破棄(ログファイルのみ重要)

パス1とパス2で同じ -passlogfile パスを必ず指定してください。 異なるパスにすると No such file or directory エラーでパス2が失敗します。


特定のファイルサイズをターゲットにする

目標ファイルサイズからビットレートを計算する方法:

合計ビットレート_kbps = (目標サイズ_MB × 8192) ÷ 動画時間_秒
映像ビットレート_kbps = 合計ビットレート_kbps − 音声ビットレート_kbps

例: 4分(240秒)の動画を128kbps音声込みで50MBに収める場合:

合計 = (50 × 8192) ÷ 240 ≈ 1707 kbps
映像 = 1707 − 128 = 1579 kbps ≈ 1.5M

パス1:

ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 1 \
  -passlogfile /tmp/passlog_target -an -f null /dev/null

パス2:

ffmpeg -i input.mp4 -c:v libx264 -b:v 1500k -pass 2 \
  -passlogfile /tmp/passlog_target -c:a aac -b:a 128k output.mp4

クリーンアップ:

rm -f /tmp/passlog_target.log /tmp/passlog_target.log.mbtree

バッチ処理での安全な passlogfile 管理

複数ファイルをバッチエンコードする場合、デフォルトのログ名 ffmpeg2pass-0.log を使うと、各ファイルのパス1がログを上書きし合い、パス2が誤ったデータを参照します。タイムスタンプやファイル名ベースのユニーク名を必ず使ってください:

for f in *.mp4; do
  base="${f%.mp4}"
  # タイムスタンプ付きのユニークな passlogfile 名
  passlog="/tmp/passlog_${base}_$(date +%s)"

  # パス1
  ffmpeg -i "$f" -c:v libx264 -b:v 1M -pass 1 \
    -passlogfile "$passlog" -an -f null /dev/null

  # パス2
  ffmpeg -i "$f" -c:v libx264 -b:v 1M -pass 2 \
    -passlogfile "$passlog" -c:a aac "${base}_out.mp4"

  # 必ずクリーンアップ
  rm -f "${passlog}.log" "${passlog}.log.mbtree"
done

エンコード後の整合性確認

2パスエンコード後に出力の尺が正しいか確認する手順:

# 入力の尺(秒)
in_dur=$(ffprobe -v error -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 input.mp4)

# 出力の尺(秒)
out_dur=$(ffprobe -v error -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 output.mp4)

echo "入力: ${in_dur}秒 / 出力: ${out_dur}秒"
# 差が 0.5 秒以上あれば要確認

カスタムログファイルの場所(旧来の方法との対比)

デフォルトでは FFmpeg はログを ./ffmpeg2pass-0.log に書き込みます。-passlogfile で別のパスを指定できます:

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

クリーンアップ:

rm -f /tmp/mylog.log /tmp/mylog.log.mbtree

2パスH.265(libx265)

H.265での2パスエンコードも同様の構造です:

ffmpeg -i input.mp4 -c:v libx265 -b:v 800k \
  -x265-params "pass=1:stats=/tmp/passlog_h265.log" -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx265 -b:v 800k \
  -x265-params "pass=2:stats=/tmp/passlog_h265.log" -c:a aac output.mp4

クリーンアップ:

rm -f /tmp/passlog_h265.log /tmp/passlog_h265.log.cutree

注意:libx265-x265-params pass=1 / stats=パスの書式を使います。H.264の-passlogfileとは異なります。


エンコーダープリセットの追加

2パスにエンコーダープリセットを組み合わせられます:

ffmpeg -i input.mp4 -c:v libx264 -preset slow -b:v 1M -pass 1 \
  -passlogfile /tmp/passlog_slow -an -f null /dev/null
ffmpeg -i input.mp4 -c:v libx264 -preset slow -b:v 1M -pass 2 \
  -passlogfile /tmp/passlog_slow -c:a aac -b:a 128k output.mp4
rm -f /tmp/passlog_slow.log /tmp/passlog_slow.log.mbtree

ログファイルとパス2の分析が一致するよう、両パスで同じプリセットを使用してください。


2パスを使うべき場面

シナリオ推奨
一定の映像品質でアーカイブCRFモード(-crf 23)を使用
特定のサイズ制限に収める必要がある2パスビットレートモードを使用
保証されたビットレートでストリーミング配信2パス(または-maxrateによる制約付きビットレート)
クイックトランスコードやプレビューCRFモード(シングルパス)

よくある問題

パス2でログファイルが見つからない

パス2がパス1と異なるディレクトリで実行されると、ffmpeg2pass-0.logを見つけられません。両パスを同じディレクトリで実行するか、両パスで-passlogfile /path/to/logを使用してください。

パス1での音声エンコード

-anでパス1の音声エンコードをスキップするのは正しい対応です — ログには映像の複雑さデータのみ必要です。

両パスで同じ-b:vを使用する

両パスで必ず同じ-b:v値を使用してください。異なる値を使うと、パス1のログが別のビットレートをターゲットにしてしまい、信頼性の低い結果になります。


関連記事


テスト環境: ffmpeg 6.1.1 / Ubuntu 24.04(GitHub Actions)
一次ソース: trac.ffmpeg.org/wiki/Encode/H.264 / ffmpeg.org/ffmpeg.html


よくある質問

2-pass と CRF、どっちを使うべき?

出力サイズを正確に決めたいなら 2-pass、画質優先なら CRF。Web アップロード用途では CRF が現代的なベストプラクティス(YouTube も推奨)。Discord 容量制限などサイズ縛りには 2-pass。

2-pass はなぜ 2 回エンコードする?

1 回目で動画全体のシーン複雑度を分析、2 回目でその情報を元にビットレートを動的配分するため。複雑なシーンに多く、静的シーンに少なく割り当てることで同サイズで高画質を実現。

エンコード時間はどれくらい増える?

CRF 1 回と比べてだいたい 1.8〜2.2 倍。1 回目は分析のみで映像出力は破棄するため、2 回目より少し速い。

1 回目のログファイルは残しておく?

不要です — ffmpeg が ffmpeg2pass-0.log を 2 回目で読んだ後、自動的に削除する設計(オプション -passlogfile で名前変更可能)。CI では明示的に削除しておく方が安全。

2-pass でも -c:a copy で音声無劣化にできる?

はい。2-pass は映像の最適化なので、音声は通常通りコピーまたは AAC 等で再エンコード可能。音声込みのジョブなら 2 回目だけ -c:a aac -b:a 128k を指定。