ffmpeg arm64のD3D11VAデコードをWindows ARM64で実測: H.264/HEVC/AV1
この記事の見出し
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 1、nv12、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 -decodersにav1が見えることだけでは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ならハードウェアデコードが常に速い」という結論にはならなかった。