ARM64_Lab

ffmpeg arm64のD3D11VAデコードをWindows ARM64で実測: H.264/HEVC/AV1

この記事の見出し
  1. 同じBtbNコミットのx64版とARM64版を使った
  2. 3本の入力をARM64版で生成した
  3. 測ったのはCPUへ戻せるNV12まで
  4. D3D11VAは3コーデックとも選択できた
  5. 6回のwall timeとhost CPU時間
  6. 復元フレームは4経路で一致した
  7. D3D11サーフェスはそのままscaleへ渡せなかった
  8. 私の使い分け

2026-08-11、ffmpeg arm64 の D3D11VA デコードを Windows ARM64 の Surface Pro 11 で測定。H.264、HEVC、AV1の3本とも、x64版とARM64版でQualcomm Adrenoを選択し、60秒・1800フレームをエラーなしで復元できた。4経路のフレーム単位SHA-256もコーデックごとに完全一致した。

ところが、デコードした映像をCPU側のNV12へ戻すところまで測ると、ハードウェア経路が常に速いわけではない。ARM64版の中央値は、H.264がソフトウェア4.800秒に対してD3D11VA 13.191秒、HEVCが4.220秒対9.149秒、AV1が5.354秒対5.474秒だ。D3D11VAはhost processのCPU時間を85.4%から91.1%減らしたが、H.264とHEVCの経過時間はむしろ長くなった。

前回のQualcomm H.264ハードウェアエンコード実測h264_mf-hw_encoding 1nv12、libx264との品質差が主題だった。さらに前のffmpeg x64対ARM64比較には短いscale処理が入っている。今回はエンコードもscale速度も比較していない。H.264/HEVC/AV1のデコード可否、D3D11サーフェスからCPUフィルターへの受け渡し、復元フレームの一致を別の検索意図として調べた。

同じBtbNコミットのx64版とARM64版を使った

入手元はBtbN/FFmpeg-BuildsのGPLスナップショットだ。2026-08-10版のlatest URLから取得した2本は、どちらもffmpeg version N-126039-g6bbc22dc09-20260810だった。

配布物 zipサイズ zip SHA-256 ffmpeg.exe PE ffmpeg.exe SHA-256
win64-gpl 170333563バイト c91c9bd5e396b7d65f4520e789bd0321b7747c0df3fa6e1cfb4b1bc31bb10f4e x64 0x8664 176ba7100218e3f91260637b4d928301b2ea963842c5758935dc06bd601beb38
winarm64-gpl 116158081バイト c8a612ba0cad31ff39e48d8b90d1d54d48035d2838565c04e739e8374929fca1 ARM64 0xAA64 d0e6813b1e4904ce5b83d629c833cf72fbbb43af12a6d4b2c88d5c4668428a87

ダウンロード先は次の2本。latestは将来別のファイルへ変わるので、再実行スクリプトは上のzip SHA-256と完全版が一致しなければ展開・実行前に停止する。

x64版はGCC 15.2.0、ARM64版はClang 23.1.0-rc2でビルドされている。同一FFmpegコミットでもコンパイラーとSIMD実装は同じではない。後のx64対ARM64差を、そのままエミュレーションだけのコストとは呼べない。

3本の入力をARM64版で生成した

実機はMicrosoft Surface Pro, 11th Edition、Snapdragon X Elite X1E80100、12コア、メモリ33893933056バイト、Windows 11 Pro 10.0.26200 ARM64だ。Python 3.12.10 ARM64から実行した。D3D11VA adapter 0はQualcomm(R) Adreno(TM) X1-85 GPU、device IDは4d4f4351:36334330、ドライバーは31.0.137.0だった。

入力は実写ではなく、1920x1080、30fps、60秒のtestsrc2だ。同じ決定的な映像源をARM64版のlibx264、libx265、libsvtav1で別々に圧縮した。

ffmpeg.exe -f lavfi -i "testsrc2=size=1920x1080:rate=30" -t 60 `
  -c:v libx264 -preset ultrafast -crf 18 -pix_fmt yuv420p -g 120 h264.mkv

ffmpeg.exe -f lavfi -i "testsrc2=size=1920x1080:rate=30" -t 60 `
  -c:v libx265 -preset ultrafast -crf 22 -pix_fmt yuv420p `
  -x265-params "log-level=error:keyint=120" hevc.mkv

ffmpeg.exe -f lavfi -i "testsrc2=size=1920x1080:rate=30" -t 60 `
  -c:v libsvtav1 -preset 11 -crf 30 -pix_fmt yuv420p -g 120 av1.mkv
入力 プロファイル サイズ 平均bitrate SHA-256
H.264 Constrained Baseline 149738630バイト 19965150bit/s 8439b926647c43941592dc98a40b93f3683b56793d78ddcd356e2a1d85432b78
HEVC Main 61172162バイト 8156288bit/s 7c1c6fbb5b44248a5fe4963167864ece2375e869fdb5b9fc763f9e51038c9a4e
AV1 Main 78633454バイト 10484460bit/s dec17d213e895d3695d8bdf1c89fefcca55dfb95d7cc41ff1425e8f04baeb79b

3本ともffprobe -count_framesで1800フレーム、60.000秒、yuv420p、1920x1080を確認した。ただしビットレート、圧縮率、デコーダーはコーデック間で揃っていない。H.264対HEVC対AV1の秒数を横並びにして「どのコーデックが速い」とは判定しない。

測ったのはCPUへ戻せるNV12まで

ソフトウェア経路はH.264にh264、HEVCにhevc、AV1にlibdav1dを明示し、最後にformat=nv12を通した。D3D11VA経路はadapter 0を固定し、d3d11サーフェスをhwdownload,format=nv12でCPU側へ戻した。

# software decode
ffmpeg.exe -hwaccel none -c:v h264 -stream_loop 3 -i h264.mkv `
  -map 0:v:0 -an -sn -dn -vf format=nv12 `
  -progress pipe:2 -nostats -f null NUL

# D3D11VA decode, synchronized by readback to system memory
ffmpeg.exe -init_hw_device d3d11va=hw:0 `
  -hwaccel d3d11va -hwaccel_device hw -hwaccel_output_format d3d11 `
  -c:v h264 -stream_loop 3 -i h264.mkv `
  -map 0:v:0 -an -sn -dn -vf "hwdownload,format=nv12" `
  -progress pipe:2 -nostats -f null NUL

-f nullへD3D11サーフェスを直接渡すだけなら、ピクセルをCPUが読む前に参照を捨てる測定になる。今回はhwdownloadを時間内に含め、CPUで後段処理できるNV12が届くまで待った。このため数値は「純粋な動画デコードエンジン速度」ではなく、demux、decode、D3D11サーフェス、readback、format filterを含むend-to-end処理になる。

短すぎる測定を避けるため、同じ入力を-stream_loopで繰り返した。H.264は4周・7200フレーム、HEVCは2周・3600フレーム、AV1は1周・1800フレームだ。各ケースを1回ウォームアップし、乱数seed 20260811で12ケースの順番を毎ラウンド変更。本測定は6回。wall timeはPythonのperf_counter、CPU時間はWindowsのGetProcessTimesで取得し、集計したのは最小値・中央値・最大値・標準偏差だ。

再実行スクリプトはリポジトリのscripts/ffmpeg_arm64_d3d11va_decode.pyに配置した。

python scripts\ffmpeg_arm64_d3d11va_decode.py `
  --duration 60 --rounds 6 `
  --json .bench\ffmpeg-arm64-d3d11va-decode.json

D3D11VAは3コーデックとも選択できた

verboseログで、x64版とARM64版の全ケースがadapter 0のQualcomm Adrenoを選ぶことを確認。H.264はh264、HEVCはhevc、AV1はav1 decoderになり、出力にはdecoder GUID一覧とpixfmt:d3d11も出る。各入力は1800フレーム、decode errorは0。

これはFFmpegが指定したD3D11VAデバイスとハードウェアフレーム形式を使った証拠になる。ただし、WindowsのGPU EngineカウンターやETWは採っていない。物理Video Decode engineの稼働率を測った、あるいは消費電力を測った、とは断定しない。

6回のwall timeとhost CPU時間

Codec / 経路 PE wall秒 min / median / max 標準偏差 max/min CPU秒 median x64/ARM64
H.264 software NV12 x64 5.547 / 8.285 / 12.901 2.818 2.326 31.875 1.726
H.264 software NV12 ARM64 3.628 / 4.800 / 10.953 2.487 3.019 28.273
H.264 D3D11VA readback x64 13.666 / 16.412 / 24.680 3.616 1.806 4.742 1.244
H.264 D3D11VA readback ARM64 13.033 / 13.191 / 16.399 1.320 1.258 4.117
HEVC software NV12 x64 4.179 / 8.593 / 13.195 3.311 3.157 31.938 2.036
HEVC software NV12 ARM64 2.987 / 4.220 / 7.948 1.745 2.661 20.883
HEVC D3D11VA readback x64 8.972 / 10.133 / 14.550 1.939 1.622 2.250 1.108
HEVC D3D11VA readback ARM64 8.611 / 9.149 / 9.852 0.421 1.144 1.859
AV1 software NV12 x64 9.407 / 10.979 / 14.457 1.810 1.537 19.039 2.051
AV1 software NV12 ARM64 4.722 / 5.354 / 10.764 2.105 2.280 12.015
AV1 D3D11VA readback x64 5.489 / 5.912 / 14.925 3.368 2.719 1.070 1.080
AV1 D3D11VA readback ARM64 5.096 / 5.474 / 14.826 3.511 2.909 1.070

ARM64版だけを見ると、D3D11VA/readbackのwall timeはsoftwareのH.264比2.748倍、HEVC比2.168倍、AV1比1.022倍だった。H.264とHEVCではreadback込みのハードウェア経路が遅い。AV1は中央値でほぼ同じだった。

一方、ARM64版のhost process CPU時間はH.264で28.273秒から4.117秒へ85.4%減り、HEVCは20.883秒から1.859秒へ91.1%減った。AV1も12.015秒から1.070秒へ91.1%減っている。CPUを空ける目的と、処理を最短で終える目的は同じではなかった。

x64対ARM64ではsoftwareの中央値がH.264で1.726倍、HEVCで2.036倍、AV1で2.051倍だった。D3D11VA/readbackでは差が1.244倍、1.108倍、1.080倍まで縮んだ。このend-to-endコマンドでは、ネイティブ化の差はsoftware decoder側で大きく、Qualcomm D3D11VAを共通利用する経路では小さく見える。ただしコンパイラーと各decoderの最適化が違うため、純粋なPrismエミュレーション税には分解できない。

正直、wall timeは予想以上に揺れた。max/minはH.264 software ARM64で3.019、HEVC software x64で3.157、AV1 D3D11VA ARM64で2.909に達した。後半ラウンドの遅延もあり、バックグラウンド負荷、温度、ドライバー待ちのどれかを切り分けていない。小さな差を勝敗として読む測定ではない。

復元フレームは4経路で一致した

速度測定とは別に、各入力をsoftware x64、software ARM64、D3D11VA x64、D3D11VA ARM64で1回ずつNV12へ復元し、framehash -hash sha256を取得。1800行のフレームハッシュを正規化した結果、コーデックごとのunique hashはすべて1だった。

入力 4経路共通の正規化framehash SHA-256
H.264 a06c1f93f0c178bd45e5331714945f14b9c1d0cfa450140b86223d8bd48ff0b4
HEVC 75c05c687d035d9a5eb04244a89f85b83232c0cde9c4112b7ead6e6e94e3e05a
AV1 afc7b3e1b548979e46ee1fcfae93f5818cfbfa83729fced79ebc33578254dc89

今回の8-bit 4:2:0合成入力では、異なるPEとsoftware/hardware経路がビット単位で同じNV12フレームを出している。再実行時に正規化framehashが1つに揃わなければ、スクリプトは速度測定前に停止する。壊れた出力やフレーム欠落は速度表から除外済みだ。これは今回の入力に対する結果であり、10-bit、HDR、film grain、エラーを含むストリームまで同じとは限らない。

D3D11サーフェスはそのままscaleへ渡せなかった

ここで一度、実際の失敗。D3D11VAの出力をsoftware scaleへ直接渡すと、ARM64版はFunction not implementedで終了し、出力フレームは0だった。

ffmpeg.exe -init_hw_device d3d11va=hw:0 `
  -hwaccel d3d11va -hwaccel_device hw -hwaccel_output_format d3d11 `
  -c:v h264 -i h264.mkv -vf "scale=1280:720" -f null NUL

hwdownload,format=nv12を前に入れると、同じ1800フレームを最後まで処理できる。

ffmpeg.exe -init_hw_device d3d11va=hw:0 `
  -hwaccel d3d11va -hwaccel_device hw -hwaccel_output_format d3d11 `
  -c:v h264 -i h264.mkv `
  -vf "hwdownload,format=nv12,scale=1280:720" -f null NUL

意外だったのはAV1 decoderの名前だ。-hwaccel none -c:v av1を強制すると、x64版とARM64版はどちらもreturn code 69、Function not implementedで失敗した。software AV1にはlibdav1dを指定すると通り、D3D11VA時はnative av1で通った。ffmpeg -decodersav1が見えることだけではsoftware decode成功を保証できない。

私の使い分け

Windows ARM64でFFmpegのデコード後にCPUフィルターをつなぐなら、まずARM64版のsoftware decoderを試す。今回のH.264とHEVCでは、そのほうがreadback込みD3D11VAよりwall timeが短かった。D3D11VAを使うなら、software filterの前にhwdownload,format=nv12が必要になる。

host CPUを空けたい処理ではD3D11VAを候補に残す。ARM64版のCPU時間は3コーデックで85.4%から91.1%減った。ただしwall time短縮とは別の利点だ。AV1はsoftware libdav1dとD3D11VA/readbackが5.354秒対5.474秒でほぼ並んだので、同時に走らせるCPU処理があるかどうかで選び方が変わる。

測っていないのは、GPU Engine/ETW、消費電力、発熱、バッテリー、実写、4K、10-bit、HDR、D3D12VA、動画プレイヤーの描画、長時間連続再生だ。電源プラン名も取得できずnullだった。私の環境ではD3D11VA対応そのものは3コーデックで確認できたが、「ffmpeg arm64ならハードウェアデコードが常に速い」という結論にはならなかった。

a
arm64lab — 個人運営

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