Scrittura di un'app remota Holographic Remoting usando l'API HolographicSpace

Se non si ha familiarità con Holographic Remoting, è possibile leggere la panoramica.

Importante

Questo documento descrive la creazione di un'applicazione remota per HoloLens 2 usando l'API HolographicSpace. Le applicazioni remote per HoloLens (prima generazione) devono usare il pacchetto NuGet versione 1.x.x. Ciò implica che le applicazioni remote scritte per HoloLens 2 non sono compatibili con HoloLens 1 e viceversa. La documentazione per HoloLens 1 è disponibile qui.

Avviso di deprecazione: la riga di versione 2.9.x sarà l'ultima per supportare le API Di Windows Holographic per lo sviluppo di applicazioni. Le versioni future supportano solo OpenXR per lo sviluppo di applicazioni. Indipendentemente dall'uso di OpenXR nell'applicazione per tutti i nuovi sviluppi dell'applicazione. Le applicazioni esistenti che usano 2.9 o versioni precedenti continueranno a funzionare in modo non interessato dalle prossime modifiche.

Le app Holographic Remoting possono trasmettere contenuti di cui è stato eseguito il rendering remoto in HoloLens 2 e Windows Mixed Reality visori visori immersivi. È anche possibile accedere a più risorse di sistema e integrare visualizzazioni immersive remote nel software pc desktop esistente. Un'app remota riceve un flusso di dati di input da HoloLens 2, esegue il rendering del contenuto in una visualizzazione immersiva virtuale e trasmette i frame di contenuto a HoloLens 2. La connessione viene effettuata usando Wi-Fi standard. La comunicazione remota holographic viene aggiunta a un'app desktop o UWP tramite un pacchetto NuGet. È necessario un codice aggiuntivo che gestisce la connessione e esegue il rendering in una visualizzazione immersiva. Una connessione remota tipica avrà un valore inferiore a 50 ms di latenza. L'app lettore può segnalare la latenza in tempo reale.

Tutti i codici di questa pagina e i progetti di lavoro sono disponibili nel repository github di esempi di Holographic Remoting.

Prerequisiti

Un buon punto di partenza è un'app Desktop o UWP basata su DirectX funzionante destinata all'API Windows Mixed Reality. Per informazioni dettagliate, vedere Panoramica dello sviluppo directX. Il modello di progetto olografico C++ è un buon punto di partenza.

Importante

Qualsiasi app che usa Holographic Remoting deve essere creata per usare un appartamento multi thread. L'uso di un appartamento a thread singolo è supportato , ma porterà a prestazioni sub-ottimali e possibilmente stuttering durante la riproduzione. Quando si usa C++/ WinRT winrt::init_apartment un appartamento a più thread è il valore predefinito.

Ottenere il pacchetto NuGet Holographic Remoting

I passaggi seguenti sono necessari per aggiungere il pacchetto NuGet a un progetto in Visual Studio.

  1. Aprire il progetto in Visual Studio.
  2. Fare clic con il pulsante destro del mouse sul nodo del progetto e scegliere Gestisci pacchetti NuGet...
  3. Nel pannello visualizzato selezionare Sfoglia e quindi cercare "Holographic Remoting".
  4. Selezionare Microsoft.Holographic.Remoting, assicurarsi di selezionare la versione 2.x.x più recente e selezionare Installa.
  5. Se viene visualizzata la finestra di dialogo Anteprima , selezionare OK.
  6. Selezionare Accetta quando viene visualizzata la finestra di dialogo contratto di licenza.

Nota

La versione 1.x.x del pacchetto NuGet è ancora disponibile per gli sviluppatori che vogliono destinazione HoloLens 1. Per informazioni dettagliate, vedere Aggiungere Holographic Remoting (HoloLens (prima generazione)).

Creare il contesto remoto

Come primo passaggio, l'applicazione deve creare un contesto remoto.

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

Avviso

Holographic Remoting funziona sostituendo il runtime di Windows Mixed Reality che fa parte di Windows con un runtime specifico di comunicazione remota. Questa operazione viene eseguita durante la creazione del contesto remoto. Per questo motivo, qualsiasi chiamata a qualsiasi API Windows Mixed Reality prima di creare il contesto remoto può comportare un comportamento imprevisto. L'approccio consigliato consiste nel creare il contesto remoto il prima possibile prima dell'interazione con qualsiasi API Realtà mista. Non combinare mai oggetti creati o recuperati tramite qualsiasi API Windows Mixed Reality prima della chiamata a CreateRemoteContext con oggetti creati o recuperati in seguito.

Successivamente è necessario creare lo spazio olografico. Se si specifica un coreWindow, non è necessario. Le app desktop che non dispongono di coreWindow possono semplicemente passare un nullptroggetto .

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

Connettersi al dispositivo

Quando l'app remota è pronta per il rendering del contenuto, è possibile stabilire una connessione al dispositivo lettore.

La connessione può essere eseguita in uno dei due modi.

  1. L'app remota si connette al lettore in esecuzione nel dispositivo.
  2. Il lettore in esecuzione nel dispositivo si connette all'app remota.

Per stabilire una connessione dall'app remota al dispositivo lettore, chiamare il metodo nel contesto remoto specificando il Connect nome host e la porta. La porta utilizzata da Holographic Remoting Player è 8265.

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

Importante

Come per qualsiasi API Connect C++/WinRT, potrebbe generare una funzione winrt::hresult_error che deve essere gestita.

Suggerimento

Per evitare di usare la proiezione del linguaggio C++/WinRT , è possibile includere il file build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h all'interno del pacchetto NuGet della comunicazione remota holographic. Contiene dichiarazioni delle interfacce COM sottostanti. L'uso di C++/WinRT è tuttavia consigliato.

L'ascolto delle connessioni in ingresso nell'app remota può essere eseguita chiamando il Listen metodo . È possibile specificare sia la porta handshake che la porta di trasporto durante questa chiamata. La porta handshake viene usata per l'handshake iniziale. I dati vengono quindi inviati sulla porta di trasporto. Per impostazione predefinita, vengono usati 8265 e 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());
}

Importante

L'interno build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl del pacchetto NuGet contiene documentazione dettagliata per l'API esposta da Holographic Remoting.

Gestione di eventi specifici della comunicazione remota

Il contesto remoto espone tre eventi, importanti per monitorare lo stato di una connessione.

  1. OnConnected: attivato quando è stata stabilita una connessione al dispositivo.
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: attivato se una connessione stabilita è chiusa o non è stato possibile stabilire una connessione.
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: quando si avvia l'ascolto delle connessioni in ingresso.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

È inoltre possibile eseguire query sullo stato di connessione usando la ConnectionState proprietà nel contesto remoto.

auto connectionState = m_remoteContext.ConnectionState();

Gestione degli eventi vocali

Usando l'interfaccia di riconoscimento vocale remoto è possibile registrare i trigger vocali con HoloLens 2 e renderli remoti all'applicazione remota.

Il membro aggiuntivo seguente è necessario per tenere traccia dello stato del riconoscimento vocale remoto:

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

Prima di tutto, recuperare l'interfaccia vocale remota.

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

Usando un metodo helper asincrono, è quindi possibile inizializzare il riconoscimento vocale remoto. Questa operazione deve essere eseguita in modo asincrono perché l'inizializzazione potrebbe richiedere molto tempo. La concorrenza e le operazioni asincrone con C++/WinRT spiegano come creare funzioni asincrone con 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);
}

Esistono due modi per specificare frasi da riconoscere.

  1. Specifica all'interno di un file xml della grammatica vocale. Per informazioni dettagliate, vedere Come creare una grammatica XML di base .
  2. Specificare passandoli all'interno del vettore del dizionario a ApplyParameters.

All'interno del callback OnRecognizedSpeech, gli eventi vocali possono quindi essere elaborati:

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

    ...
}

Anteprima del contenuto in streaming in locale

Per visualizzare lo stesso contenuto nell'app remota inviata al dispositivo, OnSendFrame è possibile usare l'evento del contesto remoto. L'evento OnSendFrame viene attivato ogni volta che la libreria Di comunicazione remota Holographic invia il frame corrente al dispositivo remoto. Questo è il momento ideale per prendere il contenuto e anche incollarlo nella finestra desktop o 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.
    });

Reprojection di profondità

A partire dalla versione 2.1.0, Holographic Remoting supporta la reprogettazione profondità. Ciò richiede sia il buffer di colore che il buffer di profondità da trasmettere dall'applicazione remota all'HoloLens 2. Per impostazione predefinita, lo streaming del buffer di profondità è abilitato e configurato per usare la metà della risoluzione del buffer dei colori. Questa modifica può essere modificata nel modo seguente:

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

...

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

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

Nota, se i valori predefiniti non devono essere usati ConfigureDepthVideoStream devono essere chiamati prima di stabilire una connessione al HoloLens 2. Il posto migliore è subito dopo aver creato il contesto remoto. I valori possibili per DepthBufferStreamResolution sono:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Disabilitato (aggiunto con la versione 2.1.3 e se non viene creato alcun flusso video di profondità aggiuntivo)

Tenere presente che l'uso di un buffer di profondità di risoluzione completa influisce anche sui requisiti di larghezza di banda e deve essere tenuto conto del valore massimo della larghezza di banda fornito a CreateRemoteContext.

Oltre a configurare la risoluzione, è necessario eseguire il commit di un buffer di profondità tramite 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);
                }
            });
        }
    });
}

Per verificare se la riprogettazione della profondità funziona correttamente su HoloLens 2, è possibile abilitare un visualizzatore di profondità tramite il portale del dispositivo. Per informazioni dettagliate, vedere Verifica della profondità impostata correttamente .

Facoltativo: Canali dati personalizzati

I canali dati personalizzati possono essere usati per inviare dati utente sulla connessione remota già stabilita. Per altre informazioni, vedere Canali dati personalizzati.

Facoltativo: Sincronizzazione del sistema di coordinate

A partire dalla versione 2.7.0, la sincronizzazione del sistema di coordinate può essere usata per allineare i dati spaziali tra il lettore e l'applicazione remota. Per altre informazioni, vedere Coordinata sincronizzazione del sistema con Panoramica della comunicazione remota holographic.

Vedere anche