Een externe Holographic Remoting-app schrijven met behulp van de HolographicSpace-API

Als u geen tijd hebt voor Holographic Remoting, kunt u ons overzicht lezen.

Belangrijk

In dit document wordt beschreven hoe u een externe toepassing voor HoloLens 2 met behulp van de HolographicSpace-API. Externe toepassingen voor HoloLens (1e generatie) moeten Gebruikmaken van NuGet-pakketversie 1.x.x. Dit betekent dat externe toepassingen die zijn geschreven voor HoloLens 2 niet compatibel zijn met HoloLens 1 en vice versa. De documentatie voor HoloLens 1 vindt u hier.

Holographic Remoting-apps kunnen extern gerenderde inhoud streamen naar HoloLens 2 en Windows Mixed Reality immersive headsets. U kunt ook toegang krijgen tot meer systeembronnen en externe in immersive weergaven integreren in bestaande desktop-pc-software. Een externe app ontvangt een invoergegevensstroom van HoloLens 2, geeft inhoud weer in een virtuele in immersieve weergave en streamt inhoudframes terug naar HoloLens 2. De verbinding wordt gemaakt met behulp van standaard Wi-Fi. Holographic Remoting wordt via een NuGet-pakket toegevoegd aan een desktop- of UWP-app. Er is aanvullende code vereist waarmee de verbinding wordt verwerkt en wordt weergegeven in een in immersieve weergave. Een typische verbinding voor remoting heeft een latentie van 50 ms. De speler-app kan de latentie in realtime rapporteren.

Alle code op deze pagina en werkende projecten vindt u in de GitHub-opslagplaats met Holographic Remoting-voorbeelden.

Vereisten

Een goed uitgangspunt is een werkende DirectX-desktop- of UWP-app die is gericht op Windows Mixed Reality API. Zie Overzicht van DirectX-ontwikkeling voor meer informatie. De C++-holographic-projectsjabloon is een goed uitgangspunt.

Belangrijk

Elke app die Holographic Remoting gebruikt, moet worden gemaakt voor het gebruik van een multithreaded-versie. Het gebruik van een single-threaded loper wordt ondersteund, maar leidt tot sub-optimale prestaties en mogelijk stuttering tijdens het afspelen. Bij het gebruik van C++/WinRT winrt::init_apartment is een multi-threaded de standaardinstelling.

Het Holographic Remoting NuGet-pakket op te halen

De volgende stappen zijn vereist om het NuGet-pakket toe te voegen aan een project in Visual Studio.

  1. Open het project in Visual Studio.
  2. Klik met de rechtermuisknop op het project-knooppunt en selecteer NuGet-pakketten beheren...
  3. Selecteer bladeren in het deelvenster dat wordt weergegeven en zoek vervolgens naar 'Holographic Remoting'.
  4. Selecteer Microsoft.Holographic.Remoting, zorg ervoor dat u de nieuwste versie 2.x.x kiest en selecteer Installeren.
  5. Als het dialoogvenster Preview wordt weergegeven, selecteert u OK.
  6. Selecteer Ik ga akkoord wanneer het dialoogvenster licentieovereenkomst wordt weergegeven.

Notitie

Versie 1.x.x van het NuGet-pakket is nog steeds beschikbaar voor ontwikkelaars die zich willen richten op HoloLens 1. Zie Holographic Remoting (HoloLens (1e generatie)) voor meer informatie.

De externe context maken

Als eerste stap moet de toepassing een externe context maken.

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

Waarschuwing

Holographic Remoting werkt door de Windows Mixed Reality runtime, die deel uitmaakt van Windows, te vervangen door een specifieke runtime voor remoting. Dit wordt gedaan tijdens het maken van de externe context. Daarom kan elke aanroep op een Windows Mixed Reality API voordat de externe context wordt aangeroepen, onverwacht gedrag veroorzaken. De aanbevolen aanpak is om de externe context zo vroeg mogelijk te maken voordat u interactie met een Mixed Reality API. Combineer nooit objecten die zijn gemaakt of opgehaald via een Windows Mixed Reality API vóór de aanroep van CreateRemoteContext met objecten die later worden gemaakt of opgehaald.

Vervolgens moet de holografische ruimte worden gemaakt. Het opgeven van een CoreWindow is niet vereist. Desktop-apps die geen CoreWindow hebben, kunnen gewoon een nullptr doorgeven.

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

Verbinding maken apparaat maken

Wanneer de externe app klaar is voor het weergeven van inhoud, kan er een verbinding met het spelerapparaat tot stand worden gebracht.

Verbinding kan op twee manieren worden uitgevoerd.

  1. De externe app maakt verbinding met de speler die op het apparaat wordt uitgevoerd.
  2. De speler die op het apparaat wordt uitgevoerd, maakt verbinding met de externe app.

Als u vanuit de externe app verbinding wilt maken met het spelerapparaat, roept u de methode aan in de externe context waarin de hostnaam en Connect poort worden opgegeven. De poort die wordt gebruikt door de Holographic Remoting Player is 8265.

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

Belangrijk

Net als bij elke C++/WinRT-API kan een Connect winrt::hresult_error die moet worden verwerkt.

Tip

Om te voorkomen dat U C++/WinRT-taalprojectie gebruikt, kan het bestand dat zich in het build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h Holographic Remoting NuGet-pakket bevindt, worden opgenomen. Het bevat declaraties van de onderliggende COM-interfaces. Het gebruik van C++/WinRT wordt echter aanbevolen.

U kunt luisteren naar binnenkomende verbindingen op de externe app door de methode aan te Listen roepen. Tijdens deze aanroep kunnen zowel de handshakepoort als de transportpoort worden opgegeven. De handshake-poort wordt gebruikt voor de eerste handshake. De gegevens worden vervolgens verzonden via de transportpoort. Standaard worden 8265 en 8266 gebruikt.

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

Belangrijk

Het build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl in het NuGet-pakket bevat gedetailleerde documentatie voor de API die beschikbaar wordt gemaakt door Holographic Remoting.

Afhandeling van specifieke gebeurtenissen voor het gebruik van de remoting

De externe context geeft drie gebeurtenissen weer, die belangrijk zijn voor het bewaken van de status van een verbinding.

  1. OnConnected: Wordt geactiveerd wanneer er een verbinding met het apparaat tot stand is gebracht.
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: Wordt geactiveerd als er een tot stand gebrachte verbinding wordt gesloten of als er geen verbinding tot stand kan worden gebracht.
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: Bij het luisteren naar binnenkomende verbindingen wordt gestart.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Daarnaast kan de verbindingstoestand worden opgevraagd met behulp van ConnectionState de eigenschap in de externe context.

auto connectionState = m_remoteContext.ConnectionState();

Spraakgebeurtenissen verwerken

Met behulp van de externe spraakinterface is het mogelijk om spraaktriggers te registreren bij HoloLens 2 en deze extern naar de externe toepassing te laten gaan.

Dit extra lid is vereist om de status van de externe spraak bij te houden.

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

Haal eerst de externe spraakinterface op.

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

Met behulp van een asynchrone helpermethode kunt u vervolgens de externe spraak initialiseren. Dit moet asynchroon worden uitgevoerd, omdat het initialiseren een aanzienlijke hoeveelheid tijd kan duren. In gelijktijdigheid en asynchrone bewerkingen met C++/WinRT wordt uitgelegd hoe u asynchrone functies maakt met 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);
}

Er zijn twee manieren om zinnen op te geven die moeten worden herkend.

  1. Specificatie in een XML-bestand voor de spraakgrammatica. Zie How to create a basic XML Grammar (Een eenvoudige XML-grammatica maken) voor meer informatie.
  2. Geef op door ze in de woordenlijstvector door te geven aan ApplyParameters .

In de OnRecognizedSpeech-callback kunnen de spraakgebeurtenissen vervolgens worden verwerkt:

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

    ...
}

Voorbeeld van gestreamde inhoud lokaal bekijken

Als u dezelfde inhoud wilt weergeven in de externe app die naar het apparaat wordt verzonden, kan de OnSendFrame gebeurtenis van de externe context worden gebruikt. De OnSendFrame gebeurtenis wordt steeds geactiveerd wanneer de Holographic Remoting-bibliotheek het huidige frame naar het externe apparaat verzendt. Dit is het ideale moment om de inhoud naar het bureaublad of UWP-venster te gaan.

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

Diepte-reprojectie

Vanaf versie 2.1.0ondersteunt Holographic Remoting Depth Reprojection. Hiervoor moeten zowel de kleurbuffer als de dieptebuffer worden gestreamd van de externe toepassing naar de HoloLens 2. Dieptebufferstreaming is standaard ingeschakeld en geconfigureerd om de helft van de resolutie van de kleurbuffer te gebruiken. Dit kan als volgt worden gewijzigd:

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

...

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

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

Houd er rekening mee dat als standaardwaarden niet moeten worden ConfigureDepthVideoStream gebruikt, moet worden aangeroepen voordat u een verbinding met de HoloLens 2. De beste plaats is direct nadat u de externe context hebt gemaakt. Mogelijke waarden voor DepthBufferStreamResolution zijn:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Uitgeschakeld (toegevoegd met versie 2.1.3 en als er geen aanvullende dieptevideostream wordt gemaakt)

Houd er rekening mee dat het gebruik van een dieptebuffer met volledige resolutie ook van invloed is op de bandbreedtevereisten en moet worden meegenomen in de maximale bandbreedtewaarde die u aan CreateRemoteContext opdraagt.

Naast het configureren van de oplossing moet u ook een dieptebuffer doorvoeren 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);
                }
            });
        }
    });
}

Als u wilt controleren of de dieptereprojectie correct werkt op HoloLens 2, kunt u een dieptevisualiseerder inschakelen via de Apparaatportal. Zie Verifying Depth is Set correct (Controleren of diepte juist is ingesteld) voor meer informatie.

Optioneel: Aangepaste gegevenskanalen

Aangepaste gegevenskanalen kunnen worden gebruikt voor het verzenden van gebruikersgegevens via de reeds bestaande verbinding voor communicatie met andere gebruikers. Zie Aangepaste gegevenskanalen voor meer informatie.

Optioneel: Coördineren van systeemsynchronisatie

Vanaf versie 2.7.0kan synchronisatie van coördinatensysteem worden gebruikt om ruimtelijke gegevens uit te lijnen tussen de speler en de externe toepassing. Zie Coördinaatsysteemsynchronisatie met Holographic Remoting Overview (Overzicht van Coördinatensysteemsynchronisatie met Holographic Remoting) voor meer informatie.

Zie ook