libx264 で動画をエンコードしようとすると、height not divisible by 2(または width not divisible by 2)というエラーで止まり、出力ファイルが一切できない——これはFFmpegで最も多いエラーの1つです。原因はただ1つ。既定のピクセルフォーマット yuv420p は幅と高さの両方が偶数であることを要求しますが、スケールやクロップの結果、動画の幅か高さが奇数になってしまったためです。本記事では、この制約がなぜあるのかを説明し、そのまま貼り付けて使える3つの解決法を紹介します。
動作確認: ffmpeg 8.1 で確認済み
この記事でわかること
libx264×yuv420pが奇数の幅・高さを拒否する理由を、わかりやすく- 何気ない
scaleやcropがどうやって奇数サイズを生むのか - 即実行できる3つの解決法:
scale=trunc(...)、scale=-2:...、pad=ceil(...) - 3つの使い分け: 黙って切り捨てる/アスペクト比を保つ/余白で囲う
エラーメッセージはエンコーダを指していますが、本当の修正点は上流にあります。エンコーダに渡すフレームの幅と高さを両方とも偶数にすればよいのです。これさえ腑に落ちれば、このエラーのどのバリエーションも1行のフィルタ変更で解決できます。
なぜ yuv420p は偶数サイズが必要なのか
H.264出力の既定ピクセルフォーマットは yuv420p です。要は 420 の部分が鍵を握ります。これは クロマサブサンプリング 4:2:0 を意味し、色(クロマ)情報を、明るさ(ルマ)に対して水平・垂直それぞれ半分の解像度で保存します。言い換えると、1つのクロマサンプルがルマの 2×2 ブロックを受け持ちます。
幅または高さが奇数だと、この 2×2 のグリッドがフレームをきれいに敷き詰められず、対応するクロマサンプルを持たないルマの行または列が1つ余ります。エンコーダは推測で埋めるのではなく処理を拒否し、width not divisible by 2 または height not divisible by 2 と報告するのです。
# 失敗時の表示例
[libx264 @ ...] height not divisible by 2 (1280x721)
[vost#0:0/libx264 ...] Error while opening encoder - maybe incorrect
parameters such as bit_rate, rate, width or height.
Conversion failed!
括弧内のサイズ(上の例では 1280x721)が、どちらの辺が問題かをそのまま教えてくれます。ここでは高さ 721 が奇数です。
奇数サイズが生まれる最も多い原因は、比率指定によるスケールです。たとえば 1280x721 の素材を半分にすると 640x360.5 になり、FFmpegは 640x360 に切り下げます——しかし計算結果が 721 のような奇数の整数に着地すると、エンコーダはそれを拒否します。手入力の奇数値でクロップする(crop=641:480)のも同じことです。
解決法1: trunc で偶数に切り下げる
入力サイズが何であっても効く、最も汎用的な解決法がこれです。trunc を使って両方のサイズを最も近い偶数に強制します。trunc(iw/2)*2 は、幅を2で割って小数部を捨て、再び2を掛けます——これにより、元の値以下で常に偶数になる値が得られます。
ffmpeg -i input.mp4 -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -c:a copy output.mp4
iw/ih— 入力の幅と高さtrunc(iw/2)*2— 幅を偶数に切り下げるtrunc(ih/2)*2— 高さを偶数に切り下げる-c:a copy— 音声はそのまま(再エンコードしない)
これは奇数だった辺から最大で1ピクセル削るだけで、見た目にはわかりません。フレームを拡大することは決してないため、「とにかくエンコードを通したい」「特定の解像度を厳密に守る必要はない」という場合の安全な既定値です。
解決法2: scale=-2 でアスペクト比を保つ
意図的にリサイズするとき——たとえば「高さを720pにして比率は保つ」——は、もう一方のサイズをFFmpegに計算させつつ、偶数に着地するよう指示します。それがまさに scale フィルタの -2 の意味です。「この辺はアスペクト比を保つように計算し、2の倍数に丸める」という指定になります。
ffmpeg -i input.mp4 -vf "scale=-2:720" output.mp4
ここでは高さを 720 に固定し、幅は素材のアスペクト比から導出され、自動的に偶数へスナップされます。-1 ではなく -2 を使うのは、-1 だと奇数の幅を生んで同じエラーを再発させかねないためです。幅のほうを固定したい場合も同じ考え方で書けます。
ffmpeg -i input.mp4 -vf "scale=1280:-2" output.mp4
720pや1080pといった目標へダウンスケールするときは、常にこの解決法を選びます。片方のサイズを指定すれば、-2 がもう一方を「比率どおり」かつ「偶数」にすることを保証してくれます。
解決法3: pad で次の偶数まで余白を足す
絵を1ピクセルも失いたくない場合は、縮めるのではなくキャンバスを広げます。pad フィルタは細い余白を足してサイズを偶数にし、元のフレームを完全にそのまま残します。
ffmpeg -i input.mp4 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" output.mp4
ceil(iw/2)*2— 幅を次の偶数へ 切り上げるceil(ih/2)*2— 高さを次の偶数へ 切り上げる
奇数の辺には、ちょうど1行または1列の余白(既定では黒)が足されます。元の画像は丸ごと残り、奇数だった辺に1ピクセルの縁が増えるだけです。素材が貴重な場合——たとえばアーカイブ映像や、1ピクセルのクロップが重要な内容を欠けさせかねない画面録画など——にこれを選びます。
どの解決法を選ぶべきか
| 解決法 | フィルタ | サイズへの影響 | 向いている場面 |
|---|---|---|---|
| 切り下げ | scale=trunc(iw/2)*2:trunc(ih/2)*2 | 奇数の辺ごとに最大1px縮む | 汎用「とにかく通したい」 |
| 比率保持 | scale=-2:720(または scale=1280:-2) | 目標サイズに偶数でリサイズ | 720p/1080pへのダウンスケール |
| 余白追加 | pad=ceil(iw/2)*2:ceil(ih/2)*2 | 奇数の辺ごとに最大1px増える | 全ピクセルを保持したい |
3つとも最終的に幅・高さが偶数になり、それがエンコーダの求めるすべてです。あとは「少し削るか・きれいにリサイズするか・少し縁を足すか」で選べば良いだけです。
よくある質問
scale の -1 と -2 の違いは何ですか?
どちらもアスペクト比からそのサイズを計算させる指定です。-1 は数学的に正確な値を出すため奇数になり得て、height not divisible by 2 を再発させることがあります。-2 は同じ計算をしたうえで結果を2の倍数に丸めるので、yuv420p で常に安全です。H.264出力では -2 を選んでください。
クロップがこのエラーを起こすのはなぜですか?
crop は指定したサイズちょうどを出力するため、奇数を入力すると——たとえば crop=641:480——クロップ後のフレームが奇数の幅を持ち、エンコーダに拒否されます。偶数の目標値を使うか、crop=trunc(iw/2)*2:trunc(ih/2)*2 で自動的に計算してください。
ピクセルフォーマットを変えるだけでもいいですか?
-pix_fmt yuv444p のように4:2:0サブサンプリングでないフォーマットに切り替えれば、偶数サイズの要件はなくなります。ただし多くのプレーヤーやデバイスは yuv420p しか確実に再生できないため、できあがったファイルがどこでも再生できるとは限りません。サイズを直すほうが互換性の高い解決策です。
エラーは width と言っているのに、高さしか変えていません。なぜですか?
scale や crop は両方の辺を同時に変えられ、エンコーダは最初にぶつかった辺を報告します。trunc や ceil で 両方 のサイズを偶数に丸めればすべてのケースをカバーできるので、どちらが実際に奇数なのかを突き止める必要はありません。
libx264 以外のコーデックでも起きますか?
はい。yuv420p を書き出すエンコーダ(H.264、H.265/HEVC、その他)はすべて、同じ偶数サイズの要件を引き継ぎます。要件はコーデックではなくピクセルフォーマットから来るためです。同じ scale/pad の解決法が適用できます。
関連記事
動作確認: ffmpeg 8.1(検証スクリプトで実行確認)
一次ソース: ffmpeg.org/ffmpeg.html / ffmpeg.org/ffmpeg-filters.html#scale / trac.ffmpeg.org/wiki/Scaling