ARM64_Lab

ffmpeg arm64をWindows ARM64で実測: Qualcomm H.264変換はネイティブ7.26秒

この記事の見出し
  1. 同じBtbNスナップショットを2本取得した
  2. 入力と測り方
  3. h264_mfだけではハードウェア使用を証明できなかった
  4. 6回の実測結果
  5. 速さだけでなく出力を突き合わせた
  6. 私の使い分け

2026-08-10、ffmpeg arm64 の H.264 ハードウェア変換を Windows ARM64 の Surface Pro 11 で測った。60秒の1080p入力に対し、ARM64ネイティブ版の中央値は7.262秒、x64版は9.855秒。x64の所要時間は1.357倍だった。

ただし、速いという数字だけを持ち帰ると失敗する。h264_mf-hw_encoding 1 を付けても、出力を yuv420p にしたままでは両ビルドとも開始前に落ちた。nv12 へ変えると、ログに QCOM Hardware Encoder - H264 と出て完走する。さらに同じ約45MBの出力で比べると、ハードウェア版のPSNRとSSIMはlibx264より低かった。

以前のffmpeg x64対ARM64の3ジョブ比較は、20秒の合成素材を3回ずつ回しただけだった。2026-06-30の測定ではジョブが短く、版も完全には揃っていない。今回はその数字を言い換えず、同一コミットの配布物、60秒入力、6回測定、強制ハードウェア指定、出力検証までを1本のスクリプトにした。

同じBtbNスナップショットを2本取得した

入手元は BtbN/FFmpeg-Builds のGPL版だ。スクリプトは次の2 URLからzipを取得する。

取得できた完全な版は、両方とも ffmpeg version N-125998-g2a20737f66-20260809。x64 zipは170324423バイト、SHA-256は7d9756960483ae7e9688f8f14f8779a4e4319f91b4c975aff56c50612b6eee2dだった。ARM64 zipは116165718バイトで、SHA-256は2502849debdae3bac3abc415ae55d5e9aeb531daaf3a529857c8cc1f8baca0b3になる。

latest URLは将来別のスナップショットを返す。スクリプトは取得した2本の版が一致しなければ速度比較を拒否するが、今回と完全に同じ再実行には上記ハッシュのzipを保存しておく必要がある。

パッケージ名だけでは判定せず、ffmpeg.exe のPEヘッダーも読んだ。x64はMachine 0x8664、ARM64は0xAA64。インストール先から推測して誤る例はWindows ARM64で実行ファイル20本の正体を調べた記録にも書いている。

同じFFmpegコミットではあるものの、x64はGCC 15.2.0、ARM64はClang 23.1.0-rc2でビルドされていた。libx264のアセンブリ実装もアーキテクチャで違う。したがって、後の差を純粋な「エミュレーション税」とは断定できない。

入力と測り方

実機はMicrosoft Surface Pro, 11th Edition。Snapdragon X Elite X1E80100、12コア、メモリ33893933056バイト、Windows 11 Pro 10.0.26200 ARM64で、Python 3.12.10 ARM64から実行した。AC電源には接続したが、スクリプトから電源プラン名を取得できずnullだった。ここは未検証として残す。

入力はARM64版で決定的に生成したtestsrc2だ。実写ではない。映像は1920x1080、30fps、60秒、音声なしで、libx264 mediumのCRF 12へ一度圧縮している。

ffmpeg.exe -f lavfi -i "testsrc2=size=1920x1080:rate=30" -t 60 `
  -c:v libx264 -preset medium -crf 12 -pix_fmt yuv420p `
  -movflags +faststart input-1080p-60s.mp4

できた入力は1800フレーム、108813804バイト、平均14508507bit/s。SHA-256は0920d1089146b3b5cf5041bd55789800ecc819cb445c9669d4c016129331b8d6だった。ffprobe -count_framesで1800フレームを実際にデコードできるところまで確認している。

各ケースはウォームアップ1回を捨て、その後6回を測定した。実行順はx64→ARM64とARM64→x64を釣り合わせている。経過時間はPythonのperf_counter、CPU時間はWindowsのGetProcessTimesから取り、集計は最小値・中央値・最大値だ。CPU時間は使用率ではなく、全スレッドのuserとkernelを足した秒数になる。

再実行コマンドはこれだけでよい。ダウンロード、PE判定、入力生成、失敗テスト、変換、ffprobe、PSNR、SSIMまで自動で進む。

python scripts\ffmpeg_arm64_windows_h264.py `
  --duration 60 --rounds 6 `
  --json .bench\ffmpeg-arm64-windows-h264.json

h264_mfだけではハードウェア使用を証明できなかった

最初に-c:v h264_mfだけで試したところ、ログが選んだのは汎用のH264 Encoder MFTだった。正直、名前を見ただけではハードウェアか分からない。そこで-hw_encoding 1を追加している。

するとx64版とARM64版の両方で、MFT名はQCOM Hardware Encoder - H264、hardware URLはQC H264 Encode HW-MFT、CLSIDは{7790ee16-08e3-426d-aada-f96774308ea1}になった。QualcommのHardware MFTを選べた証拠がログに残る。

ただし、GPU EngineやVideo EngineのETWカウンターは採っていない。ここで証明できたのはQualcommがハードウェアとして登録したMFTの選択であり、物理エンジンの稼働率ではない。

ここで一度失敗した。次のyuv420p指定ではformat negotiation failed (1/0)となり、出力は0バイトで終わる。

ffmpeg.exe -i input-1080p-60s.mp4 -an `
  -c:v h264_mf -hw_encoding 1 -b:v 6M -maxrate 6M -bufsize 12M `
  -g 60 -pix_fmt yuv420p output.mp4

-pix_fmt nv12へ替えると通った。今回の成功コマンドは次の形だ。

ffmpeg.exe -i input-1080p-60s.mp4 -an `
  -c:v h264_mf -hw_encoding 1 -b:v 6M -maxrate 6M -bufsize 12M `
  -g 60 -pix_fmt nv12 output.mp4

この失敗はx64とARM64の両方で再現した。私の環境では、h264_mfがエンコーダー一覧に出ることと、任意のピクセル形式でHardware MFTが使えることは別問題になる。

6回の実測結果

経路 PE wall秒 min / median / max CPU秒 median 出力サイズ median
libx264 x64 0x8664 27.364 / 30.863 / 44.146 162.695 45297754.5バイト
libx264 ARM64 0xAA64 11.326 / 15.036 / 23.177 89.906 45304151バイト
QCOM H.264 Hardware MFT x64 0x8664 9.600 / 9.855 / 11.227 25.859 45004511バイト
QCOM H.264 Hardware MFT ARM64 0xAA64 6.335 / 7.262 / 9.323 17.805 45004511バイト

libx264の全wall秒は、x64が30.917 / 27.364 / 31.807 / 30.336 / 30.808 / 44.146、ARM64が12.863 / 14.013 / 16.863 / 11.326 / 16.059 / 23.177だった。中央値ではx64が2.053倍長い。ただし最遅回が両方とも6回目に出ており、バックグラウンド負荷か温度の影響を切り分けられていない。6回でも揺れは残った。

Hardware MFTは、x64が9.938 / 9.740 / 10.142 / 9.772 / 9.600 / 11.227秒、ARM64が6.335 / 7.288 / 9.323 / 7.186 / 7.236 / 8.556秒だった。x64中央値9.855秒をARM64中央値7.262秒で割ると1.357。CPU時間も25.859秒対17.805秒で、ARM64側が少なかった。

この結果から言えるのは、この2本のBtbNビルドとend-to-endコマンドではARM64版が短時間だった、というところまでだ。ハードウェアのエンコード本体だけを測ったわけではない。入力のH.264デコード、色形式変換、フレーム受け渡し、mux、プロセス起動が含まれ、ハードウェアデコードも強制していない。

速さだけでなく出力を突き合わせた

全出力はH.264、1920x1080、60.000秒、1800デコードフレームだった。壊れたファイルやフレーム欠落を速度表には入れていない。

Hardware MFTの12出力は、サイズがすべて45004511バイトで、SHA-256もすべてbb861b146439ac576195e4c76910cfdfe7b53d721759d6f3e3431ac23b1ba4f8になった。x64とARM64が同じQualcomm MFTへ同じフレームを渡し、ビット単位で同じ結果を得た形だ。代表出力のPSNR averageは42.2793dB、SSIM Allは0.99321だった。

libx264は少し違う。中央値に最も近い所要時間の回を代表に選ぶと、x64のSHA-256はee822ce6f6276b8ead40fc8d6b07ed94101a8c57d669aae3277b9fc8c75ac230、ARM64はf5daf049014f2b608a51c86ea997f06e4fb71d1164cd1af7ce129abcf33c2af1だった。各アーキテクチャ内でも6回すべてハッシュが異なり、この測定のlibx264出力はビット単位で固定されなかった。原因は切り分けていない。それでもサイズ、1800フレーム、長さは揃い、代表出力はx64が44.19154dB / 0.99505、ARM64が44.19955dB / 0.99506で、品質指標は近かった。PSNRとSSIMは各1出力の値で、6回分の分布ではない。

同程度の45MBで、libx264のPSNRは約44.19dB、Hardware MFTは42.2793dB。ハードウェア版はARM64で約2.07倍速い一方、今回の合成入力では品質指標が下がった。速度と品質が同じ条件だとは扱えないため、「Hardware MFTがlibx264より優秀」とは結論づけない。

私の使い分け

短時間でH.264を作るなら、Windows ARM64ではBtbNのARM64版とh264_mf -hw_encoding 1 -pix_fmt nv12を選ぶ。今回の60秒素材は7.262秒で終わり、出力もx64版と完全一致した。

画質を優先する変換ではlibx264を残す。ARM64版なら中央値15.036秒まで短縮でき、代表PSNRも44.19955dBだった。なお、実写、10bit、HDR、4K、HEVC、長時間の熱飽和、バッテリー消費、Qualcommメディアドライバーの版は測っていない。Hardware MFTが常に同じ品質や速度になるとは、まだ言えない。

意外だったのは、このend-to-endコマンドでハードウェアMFTを選んでも2本の所要時間に1.357倍の差が残ったことだ。同時に、yuv420pのままでは1フレームも出なかった。今回の条件ではffmpeg arm64をWindows ARM64へ入れる価値があったが、実用上いちばん効いたのはアーキテクチャ名より、Hardware MFTを明示し、nv12まで揃えることだった。

a
arm64lab — 個人運営

Surface Pro 11th Edition(Snapdragon X Elite)を2025年5月から常用機にしている個人の記録です。ARM64 版 Windows で詰まったところと、その場で測った値をそのまま書き残しています。特定の企業・団体とは関係がなく、いかなる組織を代表する見解でもありません。