使用 OpenXR API 撰寫全像攝影遠端應用程式

如果您不熟悉全像攝影遠端功能,建議您 閱讀我們的概觀

重要

本檔說明如何使用OpenXR API為HoloLens 2和Windows Mixed Reality頭戴式裝置建立遠端應用程式。 HoloLens (第 1 代) 的遠端應用程式必須使用 NuGet 套件1.x.x版。這表示針對 HoloLens 2撰寫的遠端應用程式與 HoloLens 1 不相容,反之亦然。 您可以在這裡找到 HoloLens 1 的檔。

全像攝影遠端應用程式可以將遠端轉譯的內容串流至HoloLens 2和Windows Mixed Reality沉浸式頭戴裝置。 您也可以存取更多系統資源,並將遠端 沉浸式檢視 整合到現有的桌上型電腦軟體中。 遠端應用程式會從HoloLens 2接收輸入資料流程、在虛擬沉浸式檢視中轉譯內容,並將內容框架串流回HoloLens 2。 連線是使用標準 Wi-Fi 進行。 全像攝影遠端功能會透過 NuGet 封包新增至桌面或 UWP 應用程式。 需要額外的程式碼,才能處理連線,並在沉浸式檢視中轉譯。 一般遠端連線的延遲時間最低為 50 毫秒。 播放機應用程式可以即時報告延遲。

此頁面上的所有程式碼和工作專案都可以在 Holographic Remoting 範例 github 存放庫中找到。

必要條件

良好的起點是一個正常運作的 OpenXR 型桌面或 UWP 應用程式。 如需詳細資訊,請參閱 開始使用 OpenXR

重要

任何使用全像攝影遠端處理的應用程式都應該撰寫為使用 多執行緒 Apartment。 支援 使用單一執行緒 Apartment ,但會導致播放期間效能變佳且可能雜亂。 使用 C++/WinRT winrt::init_apartment 多執行緒 Apartment 是預設值。

取得全像攝影遠端 NuGet 套件

下列步驟需要將 NuGet 套件新增至 Visual Studio 中的專案。

  1. 在 Visual Studio 中開啟專案。
  2. 以滑鼠右鍵按一下專案節點,然後選取 [管理 NuGet 套件...
  3. 在出現的面板中,選取 [ 流覽 ],然後搜尋 [全像攝影遠端處理]。
  4. 選取 [Microsoft.Holographic.Remoting.OpenXr],然後確定已選取最新的 2.x.x 版本 ,然後選取 [ 安裝]。
  5. 如果出現 [預覽 ] 對話方塊,請選取 [確定]。
  6. 選取 [授權合約] 對話方塊快顯時,選取 [ 我接受 ]。
  7. 針對下列 NuGet 套件重複步驟 3 到 6:OpenXR.Headers、OpenXR.Loader

注意

NuGet 套件 1.x.x 版仍然可供以 HoloLens 1 為目標的開發人員使用。 如需詳細資訊 ,請參閱新增 HoloLens (HoloLens (第 1 代) )

選取全像攝影遠端 OpenXR 執行時間

您需要在遠端應用程式中執行的第一個步驟是選取全像攝影遠端 OpenXR 執行時間,這是 Microsoft.Holographic.Remoting.OpenXr NuGet 套件的一部分。 您可以將環境變數設定 XR_RUNTIME_JSON 為應用程式內的 RemotingXR.json 檔案路徑,以執行此動作。 OpenXR 載入器會使用此環境變數來不使用系統預設的 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;
}

使用全像攝影遠端延伸模組建立 XrInstance

一般 OpenXR 應用程式應該採取的第一個動作是選取 OpenXR 延伸模組並建立 XrInstance。 OpenXR 核心規格不提供任何遠端特定 API。 基於這個理由,全像攝影遠端導入自己的 OpenXR 擴充功能,名為 XR_MSFT_holographic_remoting 。 請確定 XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME 包含在 xrCreateInstance 呼叫的 XrInstanceCreateInfo 中。

提示

根據預設,應用程式的轉譯內容只會串流至在HoloLens 2或Windows Mixed Reality頭戴式裝置上執行的全像攝影遠端播放程式。 為了在遠端電腦上顯示轉譯的內容,透過視窗的交換鏈結,全像攝影遠端提供名為 XR_MSFT_holographic_remoting_frame_mirroring 的第二個 OpenXR 延伸模組。 請務必使用 啟用此延伸模組 XR_MSFT_HOLOGRAPHIC_REMOTING_FRAME_MIRRORING_EXTENSION_NAME ,以防您想要使用該功能。

重要

若要瞭解 Holographic Remoting OpenXR 延伸模組 API,請參閱Holographic Remoting 範例 github 存放庫中可以找到的規格

連線到裝置

在遠端應用程式建立 XrInstance 並透過 xrGetSystem 查詢 XrSystemId 之後,即可建立與播放機裝置的連線。

警告

全像攝影遠端 OpenXR 執行時間只能在建立連線之後提供裝置特定資料,例如檢視組態或環境混合模式。 xrEnumerateViewConfigurationsxrEnumerateViewConfigurationViewsxrGetViewConfigurationPropertiesxrEnumerateEnvironmentBlendModes 、 和 xrGetSystemProperties 會提供預設值,比對在完全連線之前,如果您連接到在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);

連線可以透過兩種方式之一來完成。

  1. 遠端應用程式會連線到裝置上執行的播放機。
  2. 在裝置上執行的播放機會連線到遠端應用程式。

若要建立從遠端應用程式到播放機裝置的連線,請透過 結構呼叫 xrRemotingConnectMSFT 指定主機名稱和埠 XrRemotingConnectInfoMSFT 的方法。 全像攝影遠端播放程式所使用的埠為 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 結構來指定交握埠和傳輸埠。 交握埠用於初始交握。 然後,資料會透過傳輸埠傳送。 預設會使用 82658266

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);

當您呼叫 xrRemotingConnectMSFTxrRemotingListenMSFT 時,線上狀態必須中斷連線。 建立 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

重要

xrRemotingConnectMSFTxrRemotingListenMSFT 必須先呼叫,才能嘗試透過 xrCreateSession 建立 XrSession。 如果您嘗試在線上狀態為 XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT 會話建立時建立 XrSession 將會成功,但會話狀態會立即轉換為XR_SESSION_STATE_LOSS_PENDING。

全像攝影遠端實作 xrCreateSession 支援等待建立連線。 您可以呼叫 xrRemotingConnectMSFTxrRemotingListenMSFT 緊接呼叫 xrCreateSession ,這會封鎖並等候建立連線。 的逾時 xrRemotingConnectMSFT 會固定為 10 秒,且不受 限制 xrRemotingListenMSFT 。 如果在此時間內可以建立連線,XrSession 建立將會成功,而會話狀態將會轉換為XR_SESSION_STATE_READY。 如果無法建立連線,會話建立也會成功,但會立即轉換為XR_SESSION_STATE_LOSS_PENDING。

一般而言,連接狀態會與 XrSession 狀態結合。 對線上狀態的任何變更也會影響會話狀態。 例如,如果線上狀態從 XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFTXR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT 切換到會話狀態,也會轉換至XR_SESSION_STATE_LOSS_PENDING。

處理遠端處理特定事件

全像攝影遠端 OpenXR 執行時間會公開三個事件,這對於監視連線的狀態很重要。

  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 的 ,將紋理提交至 xrEndFrame XrRemotingFrameMirrorImageInfoMSFT ,如下所示。

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,請使用 XrRemotingFrameMirrorImageD3D12MSFT,而不是 XrRemotingFrameMirrorImageD3D11MSFT。

選擇性:自訂資料通道

2.5.0版開始,自訂資料通道可以搭配 OpenXR API 使用,透過已建立的遠端連線傳送使用者資料。 如需詳細資訊,請參閱 使用 OpenXR API 自訂資料通道

選擇性:語音

2.6.0版開始, XR_MSFT_holographic_remoting_speech 擴充功能可讓遠端應用程式使用 OpenXR API 回應播放機應用程式偵測到的語音命令。

[!重要] 詳細 規格 可以在 Holographic Remoting 範例 github 存放庫中找到。

若要在播放機應用程式上初始化語音辨識器,遠端應用程式可以呼叫 xrInitializeRemotingSpeechMSFT 。 此呼叫會將由語言、片語字典和文法檔案內容組成的語音初始化參數傳送給播放程式應用程式。

注意

在 2.6.1版之前,語音辨識器每個 只能初始化一次 XrSession

如果建立語音辨識器成功,如事件所示 XR_TYPE_EVENT_DATA_REMOTING_SPEECH_RECOGNIZER_STATE_CHANGED_MSFT ,遠端應用程式會在播放程式應用程式上產生語音辨識結果時收到通知。 XrEventDataRemotingSpeechRecognizerStateChangedMSFT當玩家端的語音辨識器狀態變更時,事件結構會放在事件佇列中。

XrRemotingSpeechRecognizerStateMSFT 定義玩家端 XrEventDataRemotingSpeechRecognizedMSFT 語音辨識器的所有可能狀態,而且如果玩家端的語音辨識器具有辨識的片語,事件結構就會放在事件佇列中。 在遠端應用程式收到已辨識片語的通知之後,可以呼叫 xrRetrieveRemotingSpeechRecognizedTextMSFT 來擷取已辨識的片語。

注意

XrRemotingSpeechRecognitionConfidenceMSFT是 Windows 語音辨識 API 以語音辨識結果傳回的SpeechRecognitionConfidence列舉直接對應。

選擇性:座標系統同步處理

2.7.0版開始,座標系統同步處理可用來對齊播放機與遠端應用程式之間的空間資料。 如需詳細資訊,請參閱 使用全像攝影遠端處理協調系統同步處理概觀

另請參閱