2パスエンコードは、1回目で動画を分析し、2回目でその結果を使ってエンコードする方法です。ファイルサイズやビットレートを目標の値に合わせたいときに使います。
⚠️ 実行前に確認してください(passlog の取り違えに注意)
- 古い passlog ファイルが残っていると、パス2がそのデータを読みます。別の動画のログだと、エラーで止まるか、エラーなしで終わってもビットレートが目標から外れます
- 複数のファイルを続けて処理するときは、
-passlogfileにファイルごとに別の名前を指定してください - エンコードが終わったら
passlogfileを削除してください - パス1とパス2は同じディレクトリで実行するか、両方に同じ
-passlogfileを指定してください
CRFモードと2パスビットレートモードの違い
libx264 と libx265 には、画質とビットレートを決める主なモードが2つあります。
| モード | オプション | 使い所 |
|---|---|---|
| CRF(固定レートファクター) | -crf 18〜28 |
画質を一定にしたい(ファイルサイズは変わってよい) |
| 2パスビットレート | -b:v N -pass 1/2 |
ファイルサイズやビットレートを決まった値にしたい |
CRFは設定が簡単で、保存用や配信用によく使われます。2パスは、メール添付の上限やディスク容量など、サイズの上限に収めたいときに向いています。
2パスの仕組み
パス1(分析): FFmpegが動画を高速にエンコードし、各フレームの複雑さをログファイルに書き出します。動画ファイルは出力しません。使うのはログファイルだけです。
パス2(エンコード): ログファイルを読んで、どのシーンに多くのビット(データ量)が必要かを判断し、それに合わせてビットレートを配分しながら本番の動画をエンコードします。
何も指定しないと、作業ディレクトリに ffmpeg2pass-0.log(H.264ではffmpeg2pass-0.log.mbtreeも)が作られます。
基本的な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の出力を捨てます。必要なのはログファイルだけです。
パス1とパス2には、必ず同じ -passlogfile を指定してください。 パスが違うと、パス2で libx264 が ratecontrol_init: can't open stats file と出し、Error while opening encoder で失敗します。
特定のファイルサイズをターゲットにする
目標のファイルサイズから、ビットレートを次の式で計算します。
合計ビットレート_kbps = (目標サイズ_MB × 8000) ÷ 動画時間_秒
映像ビットレート_kbps = 合計ビットレート_kbps − 音声ビットレート_kbps
例: 4分(240秒)の動画を、128kbpsの音声込みで50MBに収める場合:
合計 = (50 × 8000) ÷ 240 ≈ 1666.67 kbps
映像の計算上限 = 1666.67 − 128 ≈ 1538.67 kbps
以下の例ではコンテナ等の余裕を取り、映像を1500 kbpsに下げる
この計算では 1 MB = 1,000,000 バイトとしています。映像1500kbpsと音声128kbpsを240秒分合わせると、計算上は48.84MBです。コンテナなどの分は含まないので、できあがったファイルが50MB以内かを確かめてください。
パス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 のまま、同じフォルダで複数の2パスを並列に動かしたり、全ファイルのパス1を先にまとめて実行したりすると、ほかのファイルのパス1がログを上書きし、パス2が間違ったデータを読みます。1ファイルずつパス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
エンコード後の整合性確認
エンコードが終わったら、出力の長さが入力と合っているかを確かめます。
# 入力の尺(秒)
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 秒以上あれば要確認
カスタムログファイルの場所(旧来の方法との対比)
ログは、何も指定しないと ./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
上の例では、-x265-params の中に pass=1 / stats=パス の形で書いています。この書き方は古いバージョンの FFmpeg でも使えます。FFmpeg 8.1 からは、libx265 でも H.264 と同じように -pass 1 / -pass 2 と -passlogfile 名前 を使えます(ログは 名前-0.log と 名前-0.log.cutree になります)。
エンコーダープリセットの追加
2パスでも -preset を組み合わせられます。
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パスビットレートモード |
| ビットレートの上限を守って配信する | -maxrate と -bufsize で上限を付ける(2パスは平均を合わせるだけ。-bufsize がないと -maxrate は無視されます) |
| 手早い変換やプレビュー | CRFモード(エンコードは1回) |
よくある問題
パス2でログファイルが見つからない
パス2をパス1と別のディレクトリで実行すると、ffmpeg2pass-0.logが見つかりません。両方を同じディレクトリで実行するか、両方に-passlogfile /path/to/logを付けてください。
パス1での音声エンコード
パス1では、-anで音声を処理しなくてかまいません。ログに必要なのは映像の複雑さのデータだけです。
両パスで同じ-b:vを使用する
パス1とパス2で-b:vの値が違うと、パス1のログが別のビットレートを前提に作られるため、結果が当てになりにくくなります。
関連記事
よくある質問
2-pass と CRF、どっちを使うべき?
出力サイズを正確に決めたいなら 2-pass、画質を優先するなら CRF です。Web にアップロードする動画なら、今は CRF を使うのが一般的です。Discord の容量制限のようにサイズの上限があるときは 2-pass を使います。
2-pass はなぜ 2 回エンコードする?
1 回目で動画全体のシーンの複雑さを調べ、2 回目でその情報をもとにビットレートを配分するためです。複雑なシーンに多く、動きの少ないシーンに少なく割り当てるので、同じサイズでも画質が上がります。
エンコード時間はどれくらい増える?
Tears of Steel の 1080p・10 秒のクリップ(音声なし)を、FFmpeg 8.1 の libx264(-preset medium、Core i9-14900KF)で 1 回ずつ変換して測りました。CRF 23 の 1 回が 1.24 秒、同じくらいのビットレート(-b:v 2900k)の 2 パスはパス1 が 0.69 秒、パス2 が 1.05 秒で、合計は CRF の約 1.4 倍でした。パス1 が速いのは、libx264 がパス1 を軽い設定で動かす(-fastfirstpass、既定で有効)からです。素材:Tears of Steel © Blender Foundation | mango.blender.org、CC BY 3.0。
1 回目のログファイルは残しておく?
残す必要はありません。ffmpeg は 2 回目が終わっても ffmpeg2pass-0.log(H.264 では ffmpeg2pass-0.log.mbtree も)を削除しないので、上の例のように rm で削除します。
2-pass でも -c:a copy で音声無劣化にできる?
できます。2-pass で最適化するのは映像だけなので、音声はふつうどおりコピーしても、AAC などで再エンコードしてもかまいません。音声も出力するなら、2 回目にだけ -c:a aac -b:a 128k などを指定します。