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

如果您不熟悉全像攝影遠端處理,您可能會想要 閱讀我們的概觀

重要

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

取代注意事項:2.9.x 版本行將是最後一個支援 Windows Holographic API 進行應用程式開發。 即將推出的版本僅支援 OpenXR 進行應用程式開發。 與無關,我們建議您在應用程式中使用 OpenXR 來進行所有新的應用程式開發。 使用 2.9 或更舊版本的現有應用程式將繼續運作,而不會影響即將進行的變更。

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

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

必要條件

良好的起點是以Windows Mixed Reality API為目標的工作 DirectX 型桌面或 UWP 應用程式。 如需詳細資訊,請參閱 DirectX 開發概觀C++ 全像攝影專案範本是不錯的起點。

重要

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

取得全像攝影遠端 NuGet 套件

若要將 NuGet 套件新增至 Visual Studio 中的專案,必須執行下列步驟。

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

注意

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

建立遠端內容

在第一個步驟中,應用程式應該建立遠端內容。

// class declaration
#include <winrt/Microsoft.Holographic.AppRemoting.h>

...

private:
    // RemoteContext used to connect with a Holographic Remoting player and display rendered frames
    winrt::Microsoft::Holographic::AppRemoting::RemoteContext m_remoteContext = nullptr;
// class implementation
#include <HolographicAppRemoting\Streamer.h>

...

CreateRemoteContext(m_remoteContext, 20000, false, PreferredVideoCodec::Default);

警告

全像攝影遠端處理的運作方式是將屬於 Windows 的Windows Mixed Reality執行時間取代為遠端特定執行時間。 這會在建立遠端內容期間完成。 因此,在建立遠端內容之前,任何Windows Mixed Reality API 上的呼叫都可能會導致非預期的行為。 建議的方法是儘快建立遠端內容,再與任何Mixed Reality API 互動。 在呼叫 CreateRemoteCoNtext 之後建立或擷取的物件之前,請勿混合透過任何Windows Mixed Reality API 建立或擷取的物件。

接下來必須建立全像攝影空間。 不需要指定 CoreWindow。 沒有 CoreWindow 的桌面應用程式可以只傳遞 nullptr

m_holographicSpace = winrt::Windows::Graphics::Holographic::HolographicSpace::CreateForCoreWindow(nullptr);

連線到裝置

當遠端應用程式準備好轉譯內容時,可以建立播放機裝置的連線。

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

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

若要建立從遠端應用程式到播放機裝置的連線,請在指定主機名稱和埠的遠端內容上呼叫 Connect 方法。 全像攝影遠端播放程式所使用的埠為 8265

try
{
    m_remoteContext.Connect(m_hostname, m_port);
}
catch(winrt::hresult_error& e)
{
    DebugLog(L"Connect failed with hr = 0x%08X", e.code());
}

重要

如同任何 C++/WinRT API Connect ,可能會擲回需要處理的 winrt::hresult_error。

提示

若要避免使用 C++/WinRT 語言投影,可以包含位於全像攝影遠端 NuGet 套件內的檔案 build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h 。 它包含基礎 COM 介面的宣告。 不過,建議使用 C++/WinRT。

您可以藉由呼叫 方法,在遠端應用程式上接聽連入連線 Listen 。 交握埠和傳輸埠都可以在此呼叫期間指定。 交握埠用於初始交握。 然後,資料會透過傳輸埠傳送。 預設會使用 82658266

try
{
    m_remoteContext.Listen(L"0.0.0.0", m_port, m_port + 1);
}
catch(winrt::hresult_error& e)
{
    DebugLog(L"Listen failed with hr = 0x%08X", e.code());
}

重要

build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idlNuGet 套件內的 包含全像攝影遠端公開之 API 的詳細檔。

處理遠端處理特定事件

遠端內容會公開三個事件,這對於監視線上狀態很重要。

  1. OnConnected:成功建立裝置連線時觸發。
winrt::weak_ref<winrt::Microsoft::Holographic::AppRemoting::IRemoteContext> remoteContextWeakRef = m_remoteContext;

m_onConnectedEventRevoker = m_remoteContext.OnConnected(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});
  1. OnDisconnected:如果已建立的連接已關閉或無法建立連線,就會觸發。
m_onDisconnectedEventRevoker =
    m_remoteContext.OnDisconnected(winrt::auto_revoke, [this, remoteContextWeakRef](ConnectionFailureReason failureReason) {
        if (auto remoteContext = remoteContextWeakRef.get())
        {
            DebugLog(L"Disconnected with reason %d", failureReason);
            // Update UI

            // Reconnect if this is a transient failure.
            if (failureReason == ConnectionFailureReason::HandshakeUnreachable ||
                failureReason == ConnectionFailureReason::TransportUnreachable ||
                failureReason == ConnectionFailureReason::ConnectionLost)
            {
                DebugLog(L"Reconnecting...");

                ConnectOrListen();
            }
            // Failure reason None indicates a normal disconnect.
            else if (failureReason != ConnectionFailureReason::None)
            {
                DebugLog(L"Disconnected with unrecoverable error, not attempting to reconnect.");
            }
        }
    });
  1. OnListening:接聽連入連線開始時。
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

此外,也可以使用遠端內容上的 屬性來查詢 ConnectionState 線上狀態。

auto connectionState = m_remoteContext.ConnectionState();

處理語音事件

使用遠端語音介面,即可向 HoloLens 2 註冊語音觸發程式,並將其遠端傳送至遠端應用程式。

需要下列額外的成員才能追蹤遠端語音的狀態:

winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech::OnRecognizedSpeech_revoker m_onRecognizedSpeechRevoker;

首先,擷取遠端語音介面。

if (auto remoteSpeech = m_remoteContext.GetRemoteSpeech())
{
    InitializeSpeechAsync(remoteSpeech, m_onRecognizedSpeechRevoker, weak_from_this());
}

接著,您可以使用非同步協助程式方法來初始化遠端語音。 這應該以非同步方式完成,因為初始化可能需要相當長的時間。 使用 C++/WinRT 的並行和非同步作業 說明如何使用 C++/WinRT 撰寫非同步函式。

winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile> LoadGrammarFileAsync()
{
    const wchar_t* speechGrammarFile = L"SpeechGrammar.xml";
    auto rootFolder = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation();
    return rootFolder.GetFileAsync(speechGrammarFile);
}

winrt::fire_and_forget InitializeSpeechAsync(
    winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech remoteSpeech,
    winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech::OnRecognizedSpeech_revoker& onRecognizedSpeechRevoker,
    std::weak_ptr<SampleRemoteMain> sampleRemoteMainWeak)
{
    onRecognizedSpeechRevoker = remoteSpeech.OnRecognizedSpeech(
        winrt::auto_revoke, [sampleRemoteMainWeak](const winrt::Microsoft::Holographic::AppRemoting::RecognizedSpeech& recognizedSpeech) {
            if (auto sampleRemoteMain = sampleRemoteMainWeak.lock())
            {
                sampleRemoteMain->OnRecognizedSpeech(recognizedSpeech.RecognizedText);
            }
        });

    auto grammarFile = co_await LoadGrammarFileAsync();

    std::vector<winrt::hstring> dictionary;
    dictionary.push_back(L"Red");
    dictionary.push_back(L"Blue");
    dictionary.push_back(L"Green");
    dictionary.push_back(L"Default");
    dictionary.push_back(L"Aquamarine");

    remoteSpeech.ApplyParameters(L"", grammarFile, dictionary);
}

有兩種方式可以指定要辨識的片語。

  1. 語音文法 xml 檔案內的規格。 如需詳細資訊 ,請參閱如何建立基本 XML 文法
  2. 將字典向量內傳遞至 ApplyParameters 來指定 。

在 OnRecognizedSpeech 回呼內,接著可以處理語音事件:

void SampleRemoteMain::OnRecognizedSpeech(const winrt::hstring& recognizedText)
{
    bool changedColor = false;
    DirectX::XMFLOAT4 color = {1, 1, 1, 1};

    if (recognizedText == L"Red")
    {
        color = {1, 0, 0, 1};
        changedColor = true;
    }
    else if (recognizedText == L"Blue")
    {
        color = {0, 0, 1, 1};
        changedColor = true;
    }
    else if (recognizedText == L"Green")
    {
        ...
    }

    ...
}

在本機預覽串流內容

若要在傳送至裝置的遠端應用程式中顯示相同的內容, OnSendFrame 可以使用遠端內容的事件。 OnSendFrame每次全像攝影遠端程式庫傳送目前畫面到遠端裝置時,就會觸發此事件。 這是讓內容進入桌面或 UWP 視窗的理想時間。

#include <windows.graphics.directx.direct3d11.interop.h>

...

m_onSendFrameEventRevoker = m_remoteContext.OnSendFrame(
    winrt::auto_revoke, [this](const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface& texture) {
        winrt::com_ptr<ID3D11Texture2D> texturePtr;
        {
            winrt::com_ptr<ID3D11Resource> resource;
            winrt::com_ptr<::IInspectable> inspectable = texture.as<::IInspectable>();
            winrt::com_ptr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess;
            winrt::check_hresult(inspectable->QueryInterface(__uuidof(dxgiInterfaceAccess), dxgiInterfaceAccess.put_void()));
            winrt::check_hresult(dxgiInterfaceAccess->GetInterface(__uuidof(resource), resource.put_void()));
            resource.as(texturePtr);
        }

        // Copy / blit texturePtr into the back buffer here.
    });

深度重現

2.1.0 版開始,全像攝影遠端支援 深度重現。 這需要將色彩緩衝區和深度緩衝區從遠端應用程式串流處理到HoloLens 2。 預設會啟用深度緩衝區串流,並設定為使用色彩緩衝區的半解析度。 這可以變更如下:

// class implementation
#include <HolographicAppRemoting\Streamer.h>

...

CreateRemoteContext(m_remoteContext, 20000, false, PreferredVideoCodec::Default);

// Configure for half-resolution depth.
m_remoteContext.ConfigureDepthVideoStream(DepthBufferStreamResolution::Half_Resolution);

請注意,如果不應該使用 ConfigureDepthVideoStream 預設值,則必須先呼叫預設值,才能建立與HoloLens 2的連線。 建立遠端內容之後,最佳位置就位在一起。 DepthBufferStreamResolution 的可能值為:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • 已停用 (新增 2.1.3 版,且如果使用時未建立其他深度視訊串流)

請記住,使用完整解析度深度緩衝區也會影響頻寬需求,而且必須考慮您提供給 CreateRemoteContext 的最大頻寬值。

除了設定解析度之外,您也必須透過 HolographicCameraRenderingParameters.CommitDirect3D11DepthBuffer認可深度緩衝區。


void SampleRemoteMain::Render(HolographicFrame holographicFrame)
{
    ...

    m_deviceResources->UseHolographicCameraResources([this, holographicFrame](auto& cameraResourceMap) {
        
        ...

        for (auto cameraPose : prediction.CameraPoses())
        {
            DXHelper::CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();

            ...

            m_deviceResources->UseD3DDeviceContext([&](ID3D11DeviceContext3* context) {
                
                ...

                // Commit depth buffer if available and enabled.
                if (m_canCommitDirect3D11DepthBuffer && m_commitDirect3D11DepthBuffer)
                {
                    auto interopSurface = pCameraResources->GetDepthStencilTextureInteropObject();
                    HolographicCameraRenderingParameters renderingParameters = holographicFrame.GetRenderingParameters(cameraPose);
                    renderingParameters.CommitDirect3D11DepthBuffer(interopSurface);
                }
            });
        }
    });
}

若要確認深度重新投影是否正常運作HoloLens 2,您可以透過裝置入口網站啟用深度視覺化檢視。 如需詳細資訊,請參閱 驗證深度已正確設定

選擇性:自訂資料通道

自訂資料通道可用來透過已建立的遠端連線傳送使用者資料。 如需詳細資訊,請參閱 自訂資料通道

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

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

另請參閱