OpenXR API を使用した Holographic Remoting リモート アプリの作成

Holographic Remoting を初めて使用する場合は、概要を参照してください。

重要

このドキュメントでは、OpenXR API を使用して、HoloLens 2 ヘッドセットや Windows Mixed Reality ヘッドセット向けのリモート アプリケーションを作成する方法について説明します。 HoloLens (第 1 世代) 向けのリモート アプリケーションでは、NuGet パッケージ バージョン 1.x.x を使用する必要があります。このことは、HoloLens 2 用に作成されたリモート アプリケーションは HoloLens 1 と互換性がなく、その逆も同様であることを意味しています。 HoloLens 1 向けのドキュメントについては、こちらを参照してください。

Holographic Remoting アプリでは、リモートでレンダリングされたコンテンツを HoloLens 2 ヘッドセットや Windows Mixed Reality イマーシブ ヘッドセットにストリーミングできます。 さらに多くのシステム リソースにアクセスして、リモートのイマーシブ ビューを既存のデスクトップ PC ソフトウェアに統合することもできます。 リモート アプリは HoloLens 2 から入力データ ストリームを受信し、仮想イマーシブ ビューでコンテンツをレンダリングし、コンテンツ フレームを HoloLens 2 にストリーミングで戻します。 この接続は、標準の Wi-Fi を使用して行われます。 Holographic Remoting は、NuGet パケットを介してデスクトップ アプリや UWP アプリに追加されます。 接続を処理し、イマーシブ ビューでレンダリングする追加のコードが必要です。 通常のリモート処理接続の待機時間は 50 ミリ秒程度です。 プレーヤー アプリで待機時間をリアルタイムに報告できます。

このページのすべてのコードと作業中のプロジェクトは、Holographic Remoting のサンプル GitHub リポジトリにあります。

必須コンポーネント

開始点として、実用的な OpenXR ベースのデスクトップ アプリまたは UWP アプリが役立ちます。 詳細については、「OpenXR の概要」を参照してください。

重要

Holographic Remoting を使用するすべてのアプリは、マルチスレッド アパートメントを使用するように作成する必要があります。 シングルスレッド アパートメントの使用はサポートされていますが、パフォーマンスが低下し、再生中に途切れが生じる可能性があります。 C++/WinRT winrt::init_apartment を使用する場合は、マルチスレッド アパートメントが既定値です。

Holographic Remoting NuGet パッケージを取得する

Visual Studio で NuGet パッケージをプロジェクトに追加するには、次の手順を実行する必要があります。

  1. Visual Studio でプロジェクトを開きます。
  2. プロジェクト ノードを右クリックし、[NuGet パッケージの管理...] を選択します。
  3. 表示されるパネルで、[参照] を選択してから 「Holographic Remoting」 を検索します。
  4. Microsoft.Holographic.Remoting.OpenXr を選択し、最新の 2.x.x バージョンが選択されていることを確認してから、[インストール] を選択します。
  5. [プレビュー] ダイアログが表示されたら、[OK] をクリックします。
  6. 使用許諾契約書のダイアログが表示されたら、[同意する] を選択します。
  7. 次の NuGet パッケージに対して手順 3 から 6 を繰り返します: OpenXR.Headers、OpenXR.Loader

Note

NuGet パッケージ バージョン 1.x.x は、HoloLens 1 をターゲットにしている開発者向けに引き続き提供されています。 詳細については、「Holographic Remoting の追加 (HoloLens (第 1 世代))」を参照してください。

Holographic Remoting OpenXR ランタイムを選択する

リモート アプリで最初に行う必要がある手順は、Holographic Remoting OpenXR ランタイムを選択することです。このランタイムは、Microsoft.Holographic.Remoting.OpenXr NuGet パッケージに含まれています。 これは、XR_RUNTIME_JSON環境変数を、アプリ内の RemotingXR.json ファイルのパスに設定することで行えます。 この環境変数は、システムの既定の OpenXR ランタイムを使用するのではなく、Holographic Remoting OpenXR ランタイムにリダイレクトするために、OpenXR ローダーによって使用されます。 Microsoft.Holographic.Remoting.OpenXr NuGet パッケージを使用する場合、RemotingXR.json ファイルはコンパイル時に出力フォルダーに自動的にコピーされます。通常、OpenXR ランタイムの選択は、次のようになります。

bool EnableRemotingXR() {
    wchar_t executablePath[MAX_PATH];
    if (GetModuleFileNameW(NULL, executablePath, ARRAYSIZE(executablePath)) == 0) {
        return false;
    }
    
    std::filesystem::path filename(executablePath);
    filename = filename.replace_filename("RemotingXR.json");

    if (std::filesystem::exists(filename)) {
        SetEnvironmentVariableW(L"XR_RUNTIME_JSON", filename.c_str());
            return true;
        }

    return false;
}

Holographic Remoting 拡張機能を使用して XrInstance を作成する

一般的な OpenXR アプリで最初に実行する必要のあるアクションは、OpenXR 拡張機能を選択して、XrInstance を作成することです。 OpenXR コア仕様では、リモート処理固有の API は提供されません。 このため、Holographic Remoting では、XR_MSFT_holographic_remoting という名前の独自の OpenXR 拡張機能が導入されています。 XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME が、xrCreateInstance 呼び出しの XrInstanceCreateInfo に含まれている必要があります。

ヒント

既定では、アプリのレンダリングされたコンテンツは、HoloLens 2 ヘッドセットまたは Windows Mixed Reality ヘッドセットのどちらかで実行されている Holographic Remoting プレーヤーにのみストリーミングされます。 レンダリングされたコンテンツをリモート PC にも表示するために (ウィンドウのスワップ チェーンを介するなどにより)、Holographic Remoting では、XR_MSFT_holographic_remoting_frame_mirroring という名前の 2 つ目の OpenXR 拡張機能を提供しています。 この機能を使用する必要がある場合は、XR_MSFT_HOLOGRAPHIC_REMOTING_FRAME_MIRRORING_EXTENSION_NAME を使用して、この拡張機能も有効にする必要があります。

重要

Holographic Remoting OpenXR 拡張機能 API の詳細については、Holographic Remoting のサンプル GitHub リポジトリにある仕様をご確認ください。

デバイスに接続する

リモート アプリが XrInstance を作成し、xrGetSystem 経由で XrSystemId に対してクエリを実行した後、プレーヤー デバイスへの接続を確立できます。

警告

Holographic Remoting OpenXR ランタイムは、接続が確立された後でのみ、ビュー構成や環境ブレンド モードなどのデバイス固有のデータを提供できます。 xrEnumerateViewConfigurationsxrEnumerateViewConfigurationViewsxrGetViewConfigurationPropertiesxrEnumerateEnvironmentBlendModesxrGetSystemProperties は、接続が確立する前は、HoloLens 2 で実行されているプレーヤーに接続する場合に通常取得される値と一致する既定値を提供します。 接続が確立される前にはこれらのメソッドを呼び出さないようにすることをお勧めします。 これらのメソッドは、XrSession が正常に作成され、セッションの状態が少なくとも XR_SESSION_STATE_READY になった後に使用することをお勧めします。

最大ビットレート、有効なオーディオ、ビデオ コーデック、深度バッファー ストリーム解像度などの一般的なプロパティは、次のように xrRemotingSetContextPropertiesMSFT を使用して構成できます。

XrRemotingRemoteContextPropertiesMSFT contextProperties;
contextProperties = XrRemotingRemoteContextPropertiesMSFT{static_cast<XrStructureType>(XR_TYPE_REMOTING_REMOTE_CONTEXT_PROPERTIES_MSFT)};
contextProperties.enableAudio = false;
contextProperties.maxBitrateKbps = 20000;
contextProperties.videoCodec = XR_REMOTING_VIDEO_CODEC_H265_MSFT;
contextProperties.depthBufferStreamResolution = XR_REMOTING_DEPTH_BUFFER_STREAM_RESOLUTION_HALF_MSFT;
xrRemotingSetContextPropertiesMSFT(m_instance.Get(), m_systemId, &contextProperties);

接続は、次の 2 つの方法のいずれかを使用して実行できます。

  1. デバイスで実行されているプレーヤーに、リモート アプリで接続する。
  2. リモート アプリに、デバイスで実行されているプレーヤーで接続する。

リモート アプリからプレーヤー デバイスへの接続を確立するには、xrRemotingConnectMSFT メソッドを呼び出して、XrRemotingConnectInfoMSFT 構造体を介してホスト名とポートを指定します。 Holographic Remoting Player で使用するポートは、8265 です。

XrRemotingConnectInfoMSFT connectInfo{static_cast<XrStructureType>(XR_TYPE_REMOTING_CONNECT_INFO_MSFT)};
connectInfo.remoteHostName = "192.168.x.x";
connectInfo.remotePort = 8265;
connectInfo.secureConnection = false;
xrRemotingConnectMSFT(m_instance.Get(), m_systemId, &connectInfo);

着信接続をリモート アプリでリッスンするには、xrRemotingListenMSFT メソッドを呼び出します。 ハンドシェイク ポートとトランスポート ポートの両方を、XrRemotingListenInfoMSFT 構造体を使用して指定できます。 ハンドシェイク ポートは、初期ハンドシェイクに使用されます。 データは、トランスポートポートを介して送信されます。 既定で、8265 および 8266 が使用されます。

XrRemotingListenInfoMSFT listenInfo{static_cast<XrStructureType>(XR_TYPE_REMOTING_LISTEN_INFO_MSFT)};
listenInfo.listenInterface = "0.0.0.0";
listenInfo.handshakeListenPort = 8265;
listenInfo.transportListenPort = 8266;
listenInfo.secureConnection = false;
xrRemotingListenMSFT(m_instance.Get(), m_systemId, &listenInfo);

この接続状態は、xrRemotingConnectMSFT または xrRemotingListenMSFT を呼び出すときは切断されている必要があります。 XrInstance を作成し、xrRemotingGetConnectionStateMSFT を介して XrSystemId に対するクエリを実行した後であれば、任意の時点でこの接続状態を取得できます。

XrRemotingConnectionStateMSFT connectionState;
xrRemotingGetConnectionStateMSFT(m_instance.Get(), m_systemId, &connectionState, nullptr);

使用可能な接続状態は次のとおりです。

  • XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT
  • XR_REMOTING_CONNECTION_STATE_CONNECTING_MSFT
  • XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT

重要

xrRemotingConnectMSFT または xrRemotingListenMSFT は、xrCreateSession を介して XrSession を作成しようとする前に呼び出す必要があります。 接続状態が XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT である間に XrSession を作成しようとすると、セッションの作成は成功しますが、セッションの状態はすぐに XR_SESSION_STATE_LOSS_PENDING に切り替わります。

Holographic Remoting の xrCreateSession の実装では、接続が確立されるのを待機できます。 を呼び出 xrRemotingConnectMSFT すか xrRemotingListenMSFT 、その直後に への xrCreateSession呼び出しを行うことができます。これにより、接続が確立されるまでブロックおよび待機します。 のタイムアウト xrRemotingConnectMSFT は 10 秒に固定され、 では xrRemotingListenMSFT無制限です。 この時間内に接続を確立できる場合、XrSession の作成は成功し、セッション状態はXR_SESSION_STATE_READYに遷移します。 接続を確立できない場合でもセッションの作成は成功しますが、すぐに XR_SESSION_STATE_LOSS_PENDING に切り替わります。

一般的に、接続の状態は XrSession の状態と対応します。 接続状態の変更は、セッションの状態にも影響します。 たとえば、接続の状態が XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT から XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT に切り替わると、セッションの状態も XR_SESSION_STATE_LOSS_PENDING に切り替わります。

リモート処理固有のイベントの処理

Holographic Remoting OpenXR ランタイムは、接続の状態を監視するために重要な 3 つのイベントを公開します。

  1. XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT: デバイスへの接続が正常に確立されるとトリガーされます。
  2. XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT: 確立された接続が閉じられるか、接続を確立できなかった場合にトリガーされます。
  3. XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: 着信接続のリッスンを開始したとき。

これらのイベントはキューに配置され、リモート アプリは xrPollEvent を介して定期的にキューから読み取る必要があります。

auto pollEvent = [&](XrEventDataBuffer& eventData) -> bool {
	eventData.type = XR_TYPE_EVENT_DATA_BUFFER;
	eventData.next = nullptr;
	return CHECK_XRCMD(xrPollEvent(m_instance.Get(), &eventData)) == XR_SUCCESS;
};

XrEventDataBuffer eventData{};
while (pollEvent(eventData)) {
	switch (eventData.type) {
	
	...
	
	case XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Listening on port %d",
					reinterpret_cast<const XrRemotingEventDataListeningMSFT*>(&eventData)->listeningPort);
		break;
	}
	case XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Connected.");
		break;
	}
	case XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Disconnected - Reason: %d",
					reinterpret_cast<const XrRemotingEventDataDisconnectedMSFT*>(&eventData)->disconnectReason);
		break;
	}
}

ストリーミングされたコンテンツをローカルでプレビューする

デバイスに送信されるのと同じコンテンツをリモート アプリに表示するには、XR_MSFT_holographic_remoting_frame_mirroring 拡張機能を使用できます。 この拡張機能を使用すると、XrFrameEndInfo にチェーンされていない XrRemotingFrameMirrorImageInfoMSFT を使用して、次のようにして xrEndFrame にテクスチャを送信できます。

XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO};
...

XrRemotingFrameMirrorImageD3D11MSFT mirrorImageD3D11{
    static_cast<XrStructureType>(XR_TYPE_REMOTING_FRAME_MIRROR_IMAGE_D3D11_MSFT)};
mirrorImageD3D11.texture = m_window->GetNextSwapchainTexture();

XrRemotingFrameMirrorImageInfoMSFT mirrorImageEndInfo{
    static_cast<XrStructureType>(XR_TYPE_REMOTING_FRAME_MIRROR_IMAGE_INFO_MSFT)};
mirrorImageEndInfo.image = reinterpret_cast<const XrRemotingFrameMirrorImageBaseHeaderMSFT*>(&mirrorImageD3D11);

frameEndInfo.next = &mirrorImageEndInfo;

xrEndFrame(m_session.Get(), &frameEndInfo);

m_window->PresentSwapchain();

上記の例では、DX11 スワップ チェーン テクスチャを使用し、xrEndFrame への呼び出しの直後にウィンドウを表示します。 この用途は、スワップ チェーン テクスチャに限定されません。 さらに、GPU の同期を追加する必要もありません。 使用法と制約の詳細については、拡張機能の仕様に関するページを参照してください。 リモート アプリで DX12 を使用している場合は、XrRemotingFrameMirrorImageD3D11MSFT の代わりに XrRemotingFrameMirrorImageD3D12MSFT を使用します。

省略可能: カスタム データ チャネル

バージョン 2.5.0 以降では、OpenXR API でカスタム データ チャネルを使用して、既に確立されているリモート処理接続を使用してユーザー データを送信できます。 詳細については、OpenXR API を使用したカスタム データ チャネルに関する記事を参照してください。

省略可能: 音声認識

バージョン 2.6.0 以降では、XR_MSFT_holographic_remoting_speech 拡張機能を使用すると、OpenXR API でプレーヤー アプリによって検出された音声コマンドにリモート アプリで対応できます。

[重要] 詳細な仕様については、Holographic Remoting のサンプル GitHub リポジトリを参照してください。

プレーヤー アプリで音声認識エンジンを初期化するには、リモート アプリで xrInitializeRemotingSpeechMSFT を呼び出します。 この呼び出しにより、言語、フレーズの辞書、文法ファイルの内容で構成される音声初期化パラメーターがプレーヤー アプリに送信されます。

Note

バージョン 2.6.1 より前では、音声認識エンジンは、XrSession ごとに 1 回初期化される必要があります。

音声認識エンジンの作成が成功すると (これは XR_TYPE_EVENT_DATA_REMOTING_SPEECH_RECOGNIZER_STATE_CHANGED_MSFT イベントによって示されます)、音声認識の結果がプレーヤー アプリで生成されたときに、リモート アプリに通知がなされます。 XrEventDataRemotingSpeechRecognizerStateChangedMSFT イベント構造体は、プレーヤー側の音声認識エンジンの状態が変化すると、イベント キューに配置されます。

XrRemotingSpeechRecognizerStateMSFT はプレーヤー側の音声認識エンジンのすべての可能な状態を定義し、プレーヤー側の音声認識エンジンに認識されたフレーズがある場合は、XrEventDataRemotingSpeechRecognizedMSFT イベント構造体がイベント キューに配置されます。 リモート アプリは、認識されたフレーズについて通知されると、xrRetrieveRemotingSpeechRecognizedTextMSFT を呼び出すことによって認識されたそのフレーズを取得できます。

Note

XrRemotingSpeechRecognitionConfidenceMSFT は、Windows Speech Recognition API による音声認識結果と一緒に返される SpeechRecognitionConfidence 列挙型の直接マッピングです。

省略可能: 座標系の同期

バージョン 2.7.0 以降では、座標系同期を使用して、プレーヤーとリモート アプリの間の空間データを揃えることができます。 詳細については、「Holographic Remoting を使用した座標系同期の概要」を参照してください。

参照