Skriva en Holographic Remoting Remote-app med hjälp av HolographicSpace-API:et

Om du inte har använt Holographic Remoting tidigare kanske du vill läsa vår översikt.

Viktigt

Det här dokumentet beskriver hur du skapar ett fjärrprogram för HoloLens 2 med hjälp av HolographicSpace-API:et. Fjärrprogram för HoloLens (första generationen) måste använda NuGet-paketversion 1.x.x. Detta innebär att fjärrprogram som skrivits för HoloLens 2 inte är kompatibla med HoloLens 1 och vice versa. Dokumentationen för HoloLens 1 finns här.

Meddelande om utfasning: Versionsraden 2.9.x blir den sista som stöder Windows Holographic API:er för programutveckling. Kommande versioner stöder endast OpenXR för programutveckling. Oberoende av detta rekommenderar vi användning av OpenXR i ditt program för all ny programutveckling. Befintliga program som använder 2.9 eller äldre kommer att fortsätta att fungera som opåverkade av kommande ändringar.

Holographic Remoting-appar kan strömma fjärråtergivet innehåll till HoloLens 2 och Windows Mixed Reality integrerande headset. Du kan också komma åt fler systemresurser och integrera avancerade fjärrvyer i befintlig programvara för stationära datorer. En fjärrapp tar emot en indataström från HoloLens 2, renderar innehåll i en virtuell fördjupad vy och strömmar innehållsramar tillbaka till HoloLens 2. Anslutningen upprättas med standard wi-fi. Holographic Remoting läggs till i en skrivbords- eller UWP-app via ett NuGet-paket. Ytterligare kod krävs som hanterar anslutningen och renderas i en fördjupad vy. En typisk fjärranslutning har så låg svarstid som 50 ms. Spelarappen kan rapportera svarstiden i realtid.

All kod på den här sidan och arbetsprojekt finns på github-lagringsplatsen Holographic Remoting Samples.

Förutsättningar

En bra utgångspunkt är en fungerande DirectX-baserad skrivbords- eller UWP-app som riktar sig till Windows Mixed Reality-API:et. Mer information finns i Översikt över DirectX-utveckling. Den holografiska C++-projektmallen är en bra utgångspunkt.

Viktigt

Alla appar som använder Holographic Remoting bör skapas för att använda en flertrådad lägenhet. Användningen av en entrådig lägenhet stöds men leder till sämre prestanda och eventuellt stamning under uppspelningen. När du använder C++/WinRT winrt::init_apartment är en flertrådad lägenhet standard.

Hämta NuGet-paketet för Holographic Remoting

Följande steg krävs för att lägga till NuGet-paketet i ett projekt i Visual Studio.

  1. Öppna projektet i Visual Studio.
  2. Högerklicka på projektnoden och välj Hantera NuGet-paket...
  3. I panelen som visas väljer du Bläddra och söker sedan efter "Holographic Remoting".
  4. Välj Microsoft.Holographic.Remoting, välj den senaste versionen av 2.x.x och välj Installera.
  5. Om dialogrutan Förhandsgranska visas väljer du OK.
  6. Välj Jag accepterar när dialogrutan licensavtal öppnas.

Anteckning

Version 1.x.x av NuGet-paketet är fortfarande tillgänglig för utvecklare som vill rikta in sig på HoloLens 1. Mer information finns i Add Holographic Remoting (HoloLens (1st gen)).

Skapa fjärrkontexten

Som ett första steg bör programmet skapa en fjärrkontext.

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

Varning

Holographic Remoting fungerar genom att ersätta Windows Mixed Reality-körning som är en del av Windows med en fjärrkommunikationsspecifik körning. Detta görs när fjärrkontexten skapas. Därför kan alla anrop på alla Windows Mixed Reality API innan du skapar fjärrkontexten resultera i oväntat beteende. Den rekommenderade metoden är att skapa fjärrkontexten så tidigt som möjligt innan du interagerar med något Mixed Reality API. Blanda aldrig objekt som skapats eller hämtats via något Windows Mixed Reality API före anropet till CreateRemoteContext med objekt som skapats eller hämtats efteråt.

Därefter måste det holografiska utrymmet skapas. Det krävs inte att du anger en CoreWindow. Skrivbordsappar som inte har en CoreWindow kan bara skicka en nullptr.

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

Ansluta till enheten

När fjärrappen är redo för rendering av innehåll kan en anslutning till spelarenheten upprättas.

Anslutningen kan göras på ett av två sätt.

  1. Fjärrappen ansluter till spelaren som körs på enheten.
  2. Spelaren som körs på enheten ansluter till fjärrappen.

Om du vill upprätta en anslutning från fjärrappen till spelarenheten anropar Connect du metoden i fjärrkontexten som anger värdnamnet och porten. Porten som används av Holographic Remoting Player är 8265.

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

Viktigt

Precis som med alla C++/WinRT API Connect kan utlösa en winrt::hresult_error som måste hanteras.

Tips

Om du vill undvika att använda C++/WinRT-språkprojektionen kan filen build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h som finns i NuGet-paketet Holographic Remoting ingå. Den innehåller deklarationer av de underliggande COM-gränssnitten. Användning av C++/WinRT rekommenderas dock.

Du kan lyssna efter inkommande anslutningar i fjärrappen Listen genom att anropa metoden . Både handskakningsporten och transportporten kan anges under det här anropet. Handskakningsporten används för den första handskakningen. Data skickas sedan via transportporten. Som standard används 8265 och 8266 .

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

Viktigt

Inuti build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl NuGet-paketet finns detaljerad dokumentation för API:et som exponeras av Holographic Remoting.

Hantera fjärrkommunikationsspecifika händelser

Fjärrkontexten exponerar tre händelser, som är viktiga för att övervaka tillståndet för en anslutning.

  1. OnConnected: Utlöses när en anslutning till enheten har upprättats.
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: Utlöses om en upprättad anslutning har stängts eller om det inte gick att upprätta en anslutning.
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: När du lyssnar efter inkommande anslutningar startar.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Dessutom kan du köra frågor mot anslutningstillståndet ConnectionState med hjälp av egenskapen i fjärrkontexten.

auto connectionState = m_remoteContext.ConnectionState();

Hantera talhändelser

Med hjälp av fjärrgränssnittet för tal kan du registrera talutlösare med HoloLens 2 och fjärransluta dem till fjärrprogrammet.

Följande extra medlem krävs för att spåra tillståndet för fjärrtal:

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

Hämta först fjärrtalsgränssnittet.

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

Med hjälp av en asynkron hjälpmetod kan du sedan initiera fjärrtal. Detta bör göras asynkront eftersom initieringen kan ta lång tid. Samtidighet och asynkrona åtgärder med C++/WinRT förklarar hur du skapar asynkrona funktioner med 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);
}

Det finns två sätt att ange fraser som ska identifieras.

  1. Specifikation i en XML-fil för tal grammatik. Mer information finns i Så här skapar du en grundläggande XML-grammatik .
  2. Ange genom att skicka dem i ordlistevektorn till ApplyParameters.

I återanropet OnRecognizedSpeech kan talhändelserna sedan bearbetas:

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")
    {
        ...
    }

    ...
}

Förhandsgranska strömmat innehåll lokalt

Om du vill visa samma innehåll i fjärrappen som skickas till enheten kan händelsen för fjärrkontexten OnSendFrame användas. Händelsen OnSendFrame utlöses varje gång Holographic Remoting-biblioteket skickar den aktuella ramen till fjärrenheten. Det här är den perfekta tiden att ta innehållet och även blit det i skrivbordet eller UWP-fönstret.

#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.
    });

Återskapa djup

Från och med version 2.1.0 stöder Holographic Remoting djupåtergivning. Detta kräver att både färgbufferten och djupbufferten strömmas från fjärrprogrammet till HoloLens 2. Som standard är strömning av djupbuffert aktiverat och konfigurerat för att använda hälften av färgbuffertens upplösning. Detta kan ändras på följande sätt:

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

...

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

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

Observera att om standardvärdena inte ska användas ConfigureDepthVideoStream måste anropas innan du upprättar en anslutning till HoloLens 2. Det bästa stället är direkt när du har skapat fjärrkontexten. Möjliga värden för DepthBufferStreamResolution är:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Inaktiverad (läggs till med version 2.1.3 och om den används skapas ingen ytterligare djupvideoström)

Tänk på att användning av en fullständig lösningsdjupbuffert också påverkar bandbreddskraven och måste redovisas i det maximala bandbreddsvärdet som du anger för CreateRemoteContext.

Förutom att konfigurera lösningen måste du också checka in en djupbuffert via 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);
                }
            });
        }
    });
}

Om du vill kontrollera om djupreprojektionen fungerar korrekt på HoloLens 2 kan du aktivera en djupvisualiserare via enhetsportalen. Mer information finns i Verifiera att djup har angetts korrekt .

Valfritt: Anpassade datakanaler

Anpassade datakanaler kan användas för att skicka användardata via den redan etablerade fjärranslutningen. Mer information finns i Anpassade datakanaler.

Valfritt: Samordna systemsynkronisering

Från och med version 2.7.0 kan koordinatsystemsynkronisering användas för att justera rumsliga data mellan spelaren och fjärrprogrammet. Mer information finns i Översikt över koordinatsystemsynkronisering med holografisk fjärrkommunikation.

Se även