デスクトップ NVR サンプル¶
amdva-nvr CONFIG.json は、登録したすべての RTSP ソースを録画し、選択した最大
16 個のソースを Qt のデスクトップウィンドウに表示します。YOLOv8n は、共有の MIGraphX
バッチ実行器を使って選択中のソースで実行され、オプションで非表示のソースのキーフレームも解析できます。登録できるソースの数に固定の上限はありませんが、実際の配備はカメラ、ストレージ、ネットワーク、デコーダーの能力に制約されます。対応する入力形式は、H.264 または H.265 の 30 FPS で、プログレッシブ、縦横が偶数、8 ビットの NV12、最大 2560×1440、BT.709 リミテッドレンジです。音声は無視します。
ビルドと起動¶
python3 scripts/bootstrap.py を実行すると、Qt 6、SQLite、RTSP/RTP と Matroska のプラグインを含む、バージョンを固定した依存関係をビルドします。このサンプルは既定で有効です。-DAMDVA_BUILD_NVR_GUI=OFF を指定すると、バックエンドとヘッドレスの amdva-streams
サービスは残したまま Qt が不要になり、-DAMDVA_BUILD_NVR=OFF を指定するとサンプルをビルドしません。依存関係は
.deps の下に置かれ、システムパッケージをインストールしたり置き換えたりすることはありません。このサンプルは、インストール済みの SDK に対して単独で構成することもできます。cmake -S examples/nvr -B build-nvr -DCMAKE_PREFIX_PATH=/path/to/amdva-sdk。
同じ YOLOv8n COCO の重みを、CPU 専用の別のエクスポート環境で用意します。
source scripts/env.sh
python3 scripts/setup_model_export.py
export PATH="$AMDVA_ROOT/build/src/gst:$PATH"
.deps/model-export/bin/python examples/nvr/scripts/export_nvr_model.py \
/path/to/yolov8n.pt artifacts/nvr-model --batch=4
cp examples/nvr/config/nvr.json artifacts/nvr-local.json
# Edit paths and camera URLs in the copied config; paths are relative to that config.
WAYLAND_DISPLAY=wayland-0 QT_QPA_PLATFORM=wayland-egl \
build/examples/nvr/amdva-nvr artifacts/nvr-local.json
X11 のデスクトップで QT_QPA_PLATFORM=xcb を指定して実行することもできます。EGL との統合方法は環境に応じて選ばれます。AMD のハードウェア OpenGL ES 3 コンテキストと DMA-BUF のインポートが必要です。ソフトウェアレンダリングは受け付けません。CPU から画像をアップロードする代替経路はありません。
デモなどでフルスクリーン表示で起動するには、設定ファイルのパスの前に --fullscreen を付けます。F11 キーでフルスクリーン表示とウィンドウ表示を切り替えられます。
Live では、カメラを 1/4/9/16 分割の固定タイルに割り当てます。タイルをダブルクリックすると単一表示に切り替わり、録画範囲のタイムライン、その場でのホバープレビュー、録画の再生、ライブ表示へ戻る Back to Live 操作を使えます。Cameras では、カメラの追加/編集/削除、状態の確認、保持期間の設定、録画の削除を行えます。登録したカメラは、ライブ表示で選択しているかどうかに関係なく、すべて録画します。操作方法、再生できる範囲、カタログの移行については
VMS GUI ガイド を参照してください。設定の変更は、所有者だけが読み書きできる権限でアトミックに保存します。UI の配置は <config>.ui.json に別途保存します。状態メッセージには、認証情報を含む可能性のある URL を表示しません。
設定キーの例は examples/nvr/config/nvr.json にあります。バッチサイズは
1、2、4、8、16 のいずれかです。model はそのサイズに一致する必要があり、single_model は要素数 1 の部分バッチに使うバッチ 1 のモデルである必要があります。model-info は 1 枚の画像を記述します。schema-1 のモデルは引き続き読み込めます。新しいモデルは汎用の schema-2
GPU/hash/device-I/O マニフェストを使い、バッチサイズを記録します。動的バッチの ONNX は重みからエクスポートします。固定バッチの ONNX の入力だけを変更しても不十分です。
構成、共有サービス、トラッキング、結果の送信¶
| ディレクトリ | 内容 |
|---|---|
backend/ |
Qt に依存しない amdva_nvr_backend ライブラリ(amdva::nvr):カメラコントローラー、設定の保存、AI のスケジューリング(AiRuntime)、人物検索とトラッキング、同一プロセスのMobileCLIPランタイム |
app/ |
Qt デスクトップ(amdva-nvr) |
headless.cpp |
表示を持たないバックエンドである amdva-streams |
tools/, tests/, scripts/ |
検証プログラム、契約テスト、モデルのエクスポート、ベンチマーク |
config/, benchmarks/, docs/ |
設定例、確認済みのテスト用素材、ガイド、検証記録 |
バックエンドは公開されたフレームワークだけを使います。RTSP の受信、セグメントの録画、録画カタログ、キーフレームの検査、再生は amdva_streaming の機能です。人物検索のテーブルは Catalog::extend によって録画カタログのデータベースに含まれるため、その行は録画とともに削除され、容量上限の計算にも含まれます。GPU での画像のパッキング、バッチ実行、画像を使わないトラッキング、オーバーレイの形状、結果の送信には、フレームワークの共有ライブラリを使います。SDK とヘッドレスでの使い方はフレームワークのインターフェース を参照してください。
tracking_enabled の既定値は true です。選択中のチャンネルでは、デコードしたフレームに対して画像を使わない DL Streamer のトラッキングを実行し、クラスラベルとトラッキング ID を描画します。ソースの再接続、選択の変更、互換性のない caps のいずれかが起きると、そのソースのトラッカーをリセットします。非表示のキーフレームの検出結果は、トラッキングしない独立した結果として扱います。
オプションの results 設定は、amdmetapublish と同じ非同期の送信処理を使います。
"results": {
"method": "file", "file_path": "events.jsonl",
"address": "", "topic": "amdva/analytics",
"queue_limit": 256, "byte_limit": 4194304
}
出力を無効にするには method=none(既定)を、MQTT には tcp://127.0.0.1:1883 のようなアドレスとともに mqtt を、Kafka には 127.0.0.1:9092 とともに kafka を指定します。JSON には、選択中のソースのトラッキング済みの観測結果と、バックグラウンドの観測結果が、ソースの世代番号と PTS とともに含まれます。ファイルパスは設定ファイルからの相対パスです。キューが満杯になると、待機中の最も古いレコードを破棄します。送信エラーが起きても録画や表示は止まりません。UI には、送信済み、破棄、エラーの件数を表示します。出力はベストエフォートであり、永続的なイベントログではありません。
非表示のソースに対するバックグラウンド AI¶
background_ai_enabled を true にすると、現在のライブ表示のレイアウトで選択されていないすべてのソースを解析します。background_ai_interval_seconds は 1 ~ 3600 の整数で、既定は 5 です。既存の設定で有効化フラグを省略した場合、既定は false になります。examples/nvr/config/nvr.json ではこの機能を有効にしています。選択中のソースでは、引き続き通常の表示用推論スケジューラーを使います。
非表示の各ソースでは、サンプリングの期限を過ぎた後に、単独でデコードできる次のアクセスユニットを取り出します。H.264 では IDR ピクチャを、H.265 では IDR、BLA、CRA ピクチャを受け付けます。圧縮データのパーサーは、パラメーターセットを ID ごとにキャッシュし(ソースごとに最大 1 MiB)、受け付ける前にスライスヘッダーの参照を検証します。通常の
I ピクチャであることや、DELTA_UNIT がないことだけでは不十分です。破損したユニット、H.264 の拡張、マルチレイヤーの H.265 は対象外です。既存のプログレッシブ、8 ビット NV12、サイズ、色域の制限も引き続き適用されます。
新しく作成した VA デコーダーに、キャッシュしたパラメーターと選択した AU を 1 つだけ渡し、その後 EOS を送って並べ替え済みの出力を取り出します。依存するピクチャはデコードしません。デコードのタイムアウトは 2 秒で、エラーはバックグラウンド AI にだけ影響します。ストリーム全体のデコードや CPU でのデコードに切り替える代替経路はありません。録画には、常に元の完全な圧縮ストリームを渡します。圧縮データのヘッダーは CPU でコピーする場合がありますが、デコードした画素は VPE、HIP、MIGraphX を通じて VAMemory 上に保持されます。
デコードから推論までの負荷全体は、4 つのバックグラウンドスロットで制限します。各スロットが持つのは再利用可能な AI 用サーフェスとそのマッピングだけで、プレビュー/EGL のリソースは持ちません。すべてのスロットが使用中のときは、候補をキューに入れずにスキップします。サンプリングの期限が古いものを優先し、同じ場合は順番に回します。5 秒の期限は受け付けた時点から数えるため、長い GOP や競合があると実際の間隔は長くなります。サンプリングのためにカメラへ追加のキーフレームを要求することはありません。
バックグラウンドの画像と表示中の画像は、既存のバッチ実行器を共有します。混在したバッチにはバックグラウンドの画像を最大 1 枚だけ入れ、バックグラウンドだけのバッチでは利用できるすべてのバッチ位置を埋めることができます。部分バッチでは、既存のバッチ 1 と GPU でのパディングの動作をそのまま使います。選択、削除、再接続のいずれかが起きると、待機中のバックグラウンドジョブをキャンセルし、世代番号の確認によって古い完了結果を拒否します。バックグラウンドのボックスを表示用のオーバーレイとして再利用することはありません。
状態の列には、バックグラウンドのモード、完了した推論の回数、検出数、結果の経過時間を表示します。Controller::status() と検証用 JSON のテレメトリでは、デコード数/試行数、実行中のジョブ、キャプチャのタイムスタンプ、最新のクラス/スコア/ボックス座標も提供します。結果はメモリ上にだけ保持します。この機能では、永続的な検出イベントのデータベースは作成しません。
パイプラインと障害時の処理¶
各ソースには、それぞれ独立して制御する 3 つのパイプラインがあります。
rtspsrc → depay → parse → compressed appsink
├─ buffer references → recorder appsrc → parse → splitmuxsink
└─ selected only → decoder appsrc → VA decoder → VAMemory appsink
├─ VPE → EGL tiles
└─ VPE → HIP → batch MIGraphX → GPU NMS
圧縮データのキューは、受け取り側ごとに 16 MiB かつ約 2 秒に制限しています。appsrc のキューにも上限があります。ソースのコールバックが、ディスク I/O、AI、GUI を待つことはありません。圧縮データの滞留が発生すると、依存するピクチャを黙って破棄するのではなく、影響を受けた録画またはデコーダーをキーフレームから再開します。録画のエラーと容量上限への到達は画面に表示され、受信と選択中のソースの表示は継続します。
RTSP では、TCP、100 ms のジッター遅延、映像が 5 秒途切れたときに働くウォッチドッグを使います。再接続の待ち時間はジッターを加えながら 1 秒から 30 秒まで延び、ソースが削除されるまで再接続を繰り返します。再接続のたびに新しい世代番号と録画セッションを割り当てます。デコーダーの形式が変わった場合は再ネゴシエーションし、対応していない形式ではデコーダーのエラーを表示します。古い世代の検出結果は拒否します。解像度が変わるとオーバーレイを無効にし、500 ms より古い結果は表示しません。
録画は元のエンコード済み映像で、パーサーのフレーミングだけを Matroska の要件に合わせて変換します。ファイルは segment_seconds 前後のキーフレームで分割するため、GOP が長いと、セグメントが設定した目標より長くなることがあります。ファイル名には、UTC エポックのマイクロ秒、チャンネル ID、セッション UUID、フラグメント番号が含まれます。ウィンドウを閉じたとき、SIGINT、SIGTERM のいずれでも正常にシャットダウンします。シャットダウン時には
EOS を送り、非同期の mux が完了するまで最大 5 秒待ちます。
このアプリケーションが作成したファイルは SQLite のカタログで管理します。録画のルートディレクトリには書き込み側の排他ロックがかかります。管理対象のファイルが quota_gib を超えるか、空き容量が reserve_gib を下回ると、閉じられた管理対象ファイルのうち最も古いものから削除します。録画中のファイル、登録されていないファイル、シンボリックリンクは対象外です。削除できるファイルで容量を回復できない場合、録画を一時停止し、空き容量ができた時点でキーフレームから再開します。異常終了した後は、前回録画中だったエントリーが削除の候補になります。これらが正しく終了処理された MKV であるとは見なしません。
retention_minutes の既定は 60 で、録画中でないファイルを既知の終了時刻に基づいて削除します。0 にすると時間による削除を無効にします。削除の境界時刻をまたぐファイルや、長さが不明なファイルは対象外です。再生や切り出しで使用中のファイルは、解放されるまで保護されます。VMS の録画操作では、保持期間の編集と、閉じられた録画を確認のうえで削除する操作を行えます。
GPU リソースの所有とスケジューリング¶
起動時には、専用の VPE エンジンのカウンターが増えることを確認する必要があります。アプリケーションは、共有の GstVaDisplay を 1 つと、常設の表示スロットを 16 個持ちます。プレビューと AI は、別々の VA 処理コンテキストを使います。プレビューのコンテキストは、ソースまたは出力の形状が変わったときに作り直し、AI のコンテキストはソースの形状が変わったときに作り直します。プレビューの縮小は検証済みの 6:1 の範囲に制限し、それ以上のウィンドウの縮小は GL のサンプリングで行います。そのため、ウィンドウを最小化しても、極端な縮小率が VPE に送られることはありません。
各スロットは、常設の HIP マッピングを持つ 640×640 のリニア BGRA の VPE 出力と、EGL に一度だけインポートする 2560×1440 のリニア BGRA のプレビューサーフェス 3 枚を持ちます。使用中の矩形だけをサンプリングするため、タイルのサイズが変わっても GPU の割り当てを再インポートしません。16 スロット分の大きくなったプレビュープールは、以前の 1080p のプールと比べて、デコーダー/アロケーターのオーバーヘッドを除いた画素の格納領域が約 295 MiB 増えます。検出器、ReID、埋め込みのテンソルのサイズは、ソースの解像度に依存しません。レンダリング中は、GL フェンスによってプレビューサーフェスの再利用を防ぎます。選択を変更したときは、カメラの再接続をまたいでも、これらのプールを再利用します。
プレビューの変換は推論とは別に実行します。各ソースは最新のデコード済みバッファーだけを保持します。HIP は、再利用可能なデバイス上のバッチ領域の対応するスライスに RGB NCHW を直接パッキングし、共有のバッチ実行部がそれぞれのスライスを自身のバッチの入力へ GPU 内でコピーします。ホストからデバイスへのテンソルのコピーはありません。スケジューラーは対象となるソースを順番に回し、バッチがそろうのを最大 20 ms 待ちます。画像が 1 枚だけのときはバッチ 1 を使い、大きなバッチの未使用スロットは HIP カーネルで埋めます。結果を出力するのは有効なスロットだけです。後処理は画像ごとに GPU 上で個別に実行します。アロケーターのレイアウト調査では、ピッチを調べるために、新しく作成して一度もレンダリングしていないテスト用サーフェスを derive することがあります。この処理ではフレームの画素をマップしたり転送したりしません。コピー監査ツールはこれらを別に数え、レンダリング済みのサーフェスの derive は引き続き拒否します。有効な画像ごとに 8404 バイトのコンパクトな検出結果の構造体(最大 300 件の枠と候補の番号)を、印を付けたメタデータとして読み戻し、フレームの画素は GPU 上に保持されます。
AI は、選択中のチャンネルごとに 5 FPS で開始します。計測したバッチの処理時間とフレームの経過時間に応じて、チャンネルごとに最大 30 FPS までサンプリングを調整し、経過時間の目標は 200 ms です。これらはスケジューリングの目標であり、スループットや最大遅延を保証するものではありません。各結果は、チャンネル ID、全体で単調に増える世代番号、形状を保持します。推論のために、増え続けるフレームのキューを保持することはありません。従来の amdpreproc と
amdmigraphx 要素も引き続き使え、同じ推論の実装を共有します。
検証¶
ctest --test-dir build --output-on-failure には、既存のハードウェアとメタデータのテストに加えて、所有権、容量上限、録画中ファイルの保護、シンボリックリンク、パストラバーサル、書き込み側の同時実行の排除に関するカタログのテストが含まれます。
amdva-batch-reference と examples/nvr/tools/compare_batch_reference.py は、異なる画像と有効なバッチ占有数 1–4 について、CPU の FP32 ONNX と比較します。テスト用素材のアップロードとフレームバッファーのキャプチャは、明示的にオフラインで行うテスト操作であり、ライブの映像処理の経路には含まれません。
amdva-nvr-rtsp-server H264_FILE H265_FILE [PORT] は、ループ再生するローカルのカメラのパスを 32 個配信し、H.264/H.265 を交互に割り当てます。先頭がキーフレームで、B フレームを含まない、エンコード済みの 1080p30 ファイルを使ってください。このテスト用サーバーは映像のエンコードもデコードも行いません。
任意の MP4/MKV ファイルと、ファイルごとのカメラ URL には
amdva-rtsp-files を使ってください。1080p30 を前提とせずにファイルのタイミングを保ち、テキストのファイル一覧または位置引数のパスを受け付けます。
20 ソースのうち最初の 16 ソースを選択する構成の場合は、次のようにします。
source scripts/env.sh
python3 examples/nvr/tools/nvr_soak.py /path/to/20-source-config.json \
/path/to/1080p30.h264 /path/to/1080p30.h265 \
--seconds=260 --cycles=100 --restart-at=100 \
--output=artifacts/nvr-dynamic-validation
python3 examples/nvr/tools/nvr_soak.py /path/to/20-source-config.json \
/path/to/1080p30.h264 /path/to/1080p30.h265 \
--seconds=3600 --restart-at=600 --output=artifacts/nvr-soak
このテストハーネスは、表示数の上限、追加/削除/選択/選択解除の繰り返し、サーバー再起動後の復旧、ライブでのコピー監査、録画の終了処理、MKV のメタデータを検証します。EGL で実際にレンダリングしたタイルの画像をキャプチャし、チャンネルごとのレートと GPU のバッチ処理時間を報告します。リソースの監視対象は、RSS、重複を除いた DRM の割り当てカウンター、FD です。RSS/GPU の割り当てには 64 MiB の増加量の上限を設け、非同期の録画終了処理には上限付きの許容量を設けています。テレメトリでは、glibc ヒープの使用中バイト数と空きバイト数も報告します。NVR のエントリーポイントは、ワーカーを作成する前に、glibc ヒープアリーナの既定の上限を 4 にして GStreamer を初期化します。環境変数
MALLOC_ARENA_MAX を明示的に設定した場合は、そちらを優先します。ネイティブの呼び出し元は、Qt や GStreamer のワーカーを作成する前に initialize_runtime() を呼び出してください。パイプラインを破棄または再接続した後、コントローラーはフレームの所有者がすべて解放するのを待ち、5 秒のクールダウンを挟んで空きヒープページを切り詰めます。これにより、一時的な GStreamer ワーカーのアリーナが保持する RSS を抑えます。際限のない増加でホストのメモリを使い果たす前に中断します。記録した検証結果は
docs/dev/nvr/nvr-validation-results.json にあります。
Ryzen AI 9 HX 370 / Radeon 890M では、1 時間のローカル RTSP テストに合格しました。条件は、録画するソース 20 個と選択中の表示 16 個、ソース/選択の動的な変更 100 サイクル、同じ URL で H.264/H.265 を入れ替えるサーバー再起動です。すべてのソースが 7 秒で復旧しました。復旧後の表示ごとの表示レートと AI レートの中央値は、29.88 FPS と 5.98 FPS でした。終了処理された 1,304 個の MKV はすべてメタデータの検証に合格し、20 チャンネルそれぞれの最新の録画から 60 フレームを取り出したデコードも成功しました。ライブでのコピー監査で報告された違反は 0 件でした。ウォームアップ後の RSS と GPU 割り当ての最大増加量は 11,200 KiB と 7,124 KiB で、FD の増加はありませんでした。
amdva-nvr-faults CONFIG.json は、最初のソースを使って、RTSP の登録失敗、認証情報のマスク、64×64 へのウィンドウのリサイズ、録画ディレクトリの権限エラーとその復旧を試験します。このテストは録画ディレクトリの権限を一時的に変更するため、専用の録画ディレクトリを使ってください。
このサンプルは、再生、ONVIF による検出、音声の録画、4K での性能について何も主張しません。本番の NVR では、さらにクラッシュ時の修復、保持期間のポリシー、監視、アクセス制御、カメラごとの適合性確認を定める必要があります。
フレームワークのオプションの keyframe-tests H264|H265 FILE OUTPUT_PREFIX 実行ファイル(AMDVA_HARDWARE_TESTS=ON)は、単一 AU の VA デコードを確認し、オフラインでの参照作業のために抽出した AU を書き出します。通常のイントラピクチャと有効なランダムアクセスピクチャを区別するために、closed-GOP のテスト用素材に加えて、B フレーム/open-GOP のテスト用素材も使ってください。抽出した AU と
amdva-dump-tensor で読み戻したデータは、オフラインのテスト用の成果物であり、ライブ処理ではありません。examples/nvr/tools/compare_background_reference.py は、ライブのバックグラウンドのボックスを、対応するオフラインのキーフレームのテンソルから求めた CPU FP32 ONNX の検出結果と比較します。
バックグラウンドの参照比較ツールは、クラス/個数の一致、IoU >= 0.95、スコアの差 <= 0.03 を確認します。--allow-fp16-nms-ties を指定すると、CPU 側で抑制された重なりのある候補のうち、スコアが CPU の勝者との差 0.005 以内のものも、形状/信頼度の確認を維持したまま受け付けます。この方法は、IoU のしきい値を下げるのではなく、実測された FP16 での順位の入れ替わりに対処するものです。レポートには、標準の NMS 後の IoU と、代替の勝者の数の両方を記録します。bus の
2 番目の IDR では、信頼度の低いボックスの 1 つが FP32 の NMS の勝者と異なります(IoU
約 0.93)が、CPU の代替候補とは 0.994 を超えて一致します。既存のオフラインの GPU 実行器でも、同じテンソルでバックグラウンドの結果を再現できます。
バックグラウンド AI を有効にした状態で、20 ソース / 16 表示の 1 時間のテストを、動的な変更 100 サイクルとコーデックを入れ替える再接続を含めて完了し、受信/録画/表示は 7.5 秒で復旧しました。1,309 個の MKV はすべてメタデータの確認に合格し、全チャンネルの最新の録画から 60 フレームを取り出したデコードも成功しました。コピー監査の違反は 0 件で、RSS/GPU の最大増加量は 22.3/21.2 MiB、バックグラウンドジョブは最大 4 個でした。バックグラウンド AI を有効/無効にして 70 秒ずつ順に実行したところ、表示の中央値は 29.86/29.90 FPS、選択中のソースの AI の中央値は 5.98/5.98 FPS でした。これらはローカルのテスト用素材での観測結果です。全結果と参照比較の条件はバックグラウンド AI の検証結果 にあります。
デコードと表示のタイミング¶
オプションの telemetry 設定の既定値は
{ "enabled": true, "stall_ms": 1000 } です。NVR は、実際のデコーダー出力と新しい画像のスワップの FPS、および入力→デコード/入力→スワップの P99 の列を表示します。FPS のツールチップには詳細な統計が含まれます。表示が非表示/最小化されている間は表示の観測を一時停止しますが、デコードは継続します。従来の SDK の decoded/display_fps フィールドは、引き続きプレビューの処理を表します。実際の段階ごとの件数、遅延、更新間隔、停止、明示的な破棄には Controller::telemetry() を使ってください。ヘッドレスのコマンドと
GUI の検証ツールは、定期的に出力する JSON の最上位に telemetry 配列を追加します。遅延/FPS の合否基準はありません。正確な計測範囲と、バッファースワップの通知を表示の計測として使う場合の制約については、テレメトリ を参照してください。
人物検索¶
ネイティブの EmbeddingGemma 2 のセットアップ、収集ポリシー、テキストと領域によるクエリ、結果のインライン再生については、NVR の意味検索 を参照してください。
1440p での検証(2026-10-22)¶
~/work/videos/ にある元の 2560×1440、30 FPS、8 ビット HEVC ファイル 16 本を、トランスコードせずに RTSP で配信しました。3 分間の実行で、16 台のカメラを表示、4 台を表示、全カメラを非表示の順に試しました。すべての入力で元の解像度が保たれ、トラッキングはカメラごとに 1.98–2.02 Hz で安定し、トラッキングのキューでの破棄やカメラのエラーはありませんでした。プロセスの RSS は約 6.3 GiB(ピーク 7.1 GiB)でした。これは短時間の機能確認であり、長時間試験ではありません。既存の上限付きの YOLO、ReID、埋め込みの入力サイズは変更していません。
テキスト検索、録画の領域検索、インライン再生、削除は合格しました。フルサイズの再生では 2560×1440 の出力が得られました。H.264 1080p と HEVC 1440p を交互に切り替える 100 回のホバープレビューは、コピー監査と VPE のエラーが 0 件で合格しました(p95 89 ms)。このリビジョンでは、VPE の 6:1 の縮小制限に従うため、1440p のホバープレビューに 428×240 の GPU テクスチャを使い、OpenGL で以前の 320×180 のポップアップにサンプリングしていました。32 個の CTest テストもすべて合格しました。
用意したローカルの起動スクリプトは bash artifacts/1440p-demo/run-demo.sh です。元の映像をポート 18570 で配信し、artifacts/1440p-demo/ の下にある独立したデモ用の設定と録画を使います(これらのローカルの成果物はリポジトリにコミットしていません)。