Skriva en fjärrapp för Holographic Remoting med hjälp av HolographicSpace-API:et

Om Holographic Remoting är nytt för dig kan du 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.

Holographic Remoting-appar kan strömma fjärrre renderat innehåll till HoloLens 2 och Windows Mixed Reality integrerande headset. Du kan också komma åt fler systemresurser och integrera fjärranslutna integrerande vyer i befintlig programvara för stationära datorer. En fjärrapp tar emot en indataström från HoloLens 2, återger innehåll i en virtuell integrerande vy och strömmar tillbaka innehållsramar till HoloLens 2. Anslutningen görs 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 återges i en integrerande vy. En typisk fjärranslutning har en svarstid på så lite 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 startpunkt är en fungerande DirectX-baserad skrivbords- eller UWP-app som riktar sig mot Windows Mixed Reality API. Mer information finns i DirectX-utvecklingsöversikten. Den holografiska projektmallen i C++ är en bra utgångspunkt.

Viktigt

Alla appar som använder Holographic Remoting ska ha redigerats för att använda en flertrådig snisk. Användning av en trådad tråd stöds, men leder till icke-optimala prestanda och eventuellt hackning under uppspelningen. När du använder C++/WinRT winrt::init_apartment standardvärdet är en flertrådig klump.

Hämta NuGet-paketet 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, se till att välja 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 med licensavtalet visas.

Anteckning

Version 1.x.x av NuGet-paketet är fortfarande tillgänglig för utvecklare som vill rikta HoloLens 1. Mer information finns i Lägg till Holographic Remoting (HoloLens (1:a 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 ersätter den Windows Mixed Reality 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å ett Windows Mixed Reality API innan fjärrkontexten skapas resultera i oväntat beteende. Den rekommenderade metoden är att skapa fjärrkontexten så tidigt som möjligt innan du interagerar med Mixed Reality API. Blanda aldrig objekt som skapats eller hämtats via 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. Du behöver inte ange en CoreWindow. Skrivbordsappar som inte har en CoreWindow kan bara skicka en nullptr .

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

Anslut 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 du metoden i fjärrkontexten Connect och anger värdnamn och port. 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:er kan det leda till Connect en winrt::hresult_error som måste hanteras.

Tips

För att undvika att använda C++/WinRT-språkprojektion kan filen som finns i build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h NuGet-paketet Holographic Remoting tas med. Den innehåller deklarationer för de underliggande COM-gränssnitten. Vi rekommenderar dock att du använder C++/WinRT.

Du kan lyssna efter inkommande anslutningar i fjärrappen genom att anropa Listen metoden . Både handskakningsporten och transportporten kan anges under anropet. Handskakningsporten används för den inledande 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

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

Hantera fjärrkommunikation för specifika 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 stängs 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 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 fråga anslutningstillståndet med hjälp av ConnectionState 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ärrstyra dem till fjärrprogrammet.

Den här ytterligare medlemmen krävs för att spåra fjärrtalens tillstånd.

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

Hämta först fjärrgränssnittet för tal.

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 initiering 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 talsyntes. Mer information finns i Skapa en grundläggande XML-grammatik.
  2. Ange genom att skicka dem inuti ordlistevektorn till ApplyParameters .

I motringningen 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 OnSendFrame kan händelsen i fjärrkontexten 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 för att ta innehållet och även lägga till 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.
    });

Omprojektion av djup

Från och med version 2.1.0stöder Holographic Remoting djupreprojection. Detta kräver att både färgbufferten och djupbufferten strömmas från fjärrprogrammet till HoloLens 2. Som standard är djupbuffertströmning 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ärden inte ska användas ConfigureDepthVideoStream måste anropas innan du upprättar en anslutning till HoloLens 2. Den bästa platsen är direkt efter att du har skapat fjärrkontexten. Möjliga värden för DepthBufferStreamResolution är:

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

Tänk på att användning av en djupbuffert med full upplösning också påverkar bandbreddskraven och måste redovisas i det maximala bandbreddsvärde som du anger för CreateRemoteContext .

Förutom att konfigurera upplösningen måste du också spara 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 djupreprojection fungerar korrekt HoloLens 2 kan du aktivera en djup visualiserare via Enhetsportalen. Mer information finns i Verifying Depth is Set Correctly (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ärrkommunikationsanslutningen. Mer information finns i Anpassade datakanaler.

Valfritt: Koordinatsystemsynkronisering

Från och med version 2.7.0kan koordinatsystemssynkronisering användas för att justera rumsliga data mellan spelaren och fjärrprogrammet. Mer information finns i Översikt över samordna systemsynkronisering med Holographic Remoting.

Se även