Écriture d’une application Holographic Remoting Remote à l’aide de l’API HolographicSpace

Si vous débutez avec Holographic Remoting, vous pouvez lire notre vue d’ensemble.

Important

Ce document décrit la création d’une application distante pour HoloLens 2 à l’aide de l’API HolographicSpace. Les applications distantes pour HoloLens (1ère génération) doivent utiliser le package NuGet version 1.x.x. Cela implique que les applications distantes écrites pour HoloLens 2 ne sont pas compatibles avec HoloLens 1 et vice versa. La documentation relative à HoloLens 1 est disponible ici.

Avis de dépréciation : la ligne de publication 2.9.x sera la dernière à prendre en charge les API Holographiques Windows pour le développement d’applications. Les versions à venir prendront uniquement en charge OpenXR pour le développement d’applications. Indépendamment de cela, nous recommandons l’utilisation d’OpenXR dans votre application pour tous les nouveaux développements d’applications. Les applications existantes utilisant la version 2.9 ou une version antérieure continueront de fonctionner sans être affectées par les modifications à venir.

Les applications holographiques de communication à distance peuvent diffuser du contenu rendu à distance vers HoloLens 2 et Windows Mixed Reality casques immersifs. Vous pouvez également accéder à d’autres ressources système et intégrer des vues immersives à distance dans un logiciel PC de bureau existant. Une application distante reçoit un flux de données d’entrée de HoloLens 2, affiche le contenu dans une vue immersive virtuelle et retransit les trames de contenu vers HoloLens 2. La connexion s’effectue à l’aide d’une connexion Wi-Fi standard. La communication à distance holographique est ajoutée à une application de bureau ou UWP via un paquet NuGet. Un code supplémentaire est nécessaire pour gérer la connexion et le rendu dans une vue immersive. Une connexion de communication à distance classique a une latence de 50 ms. L’application lecteur peut signaler la latence en temps réel.

Tout le code de cette page et les projets de travail se trouvent dans le référentiel github d’exemples holographiques de communication à distance.

Prérequis

Un bon point de départ est une application de bureau ou UWP basée sur DirectX qui cible l’API Windows Mixed Reality. Pour plus d’informations, consultez Vue d’ensemble du développement DirectX. Le modèle de projet holographique C++ est un bon point de départ.

Important

Toute application utilisant holographique Remoting doit être créée pour utiliser un appartement multithread. L’utilisation d’un appartement monothread est prise en charge, mais entraîne des performances sous-optimales et peut-être un bégaiement pendant la lecture. Lorsque vous utilisez C++/WinRT winrt ::init_apartment un appartement multithread est la valeur par défaut.

Obtenir le package NuGet de communication à distance holographique

Les étapes suivantes sont requises pour ajouter le package NuGet à un projet dans Visual Studio.

  1. Ouvrez le projet dans Visual Studio.
  2. Cliquez avec le bouton droit sur le nœud du projet et sélectionnez Gérer les packages NuGet...
  3. Dans le panneau qui s’affiche, sélectionnez Parcourir , puis recherchez « Holographic Remoting ».
  4. Sélectionnez Microsoft.Holographic.Remoting, veillez à sélectionner la dernière version 2.x.x, puis sélectionnez Installer.
  5. Si la boîte de dialogue Aperçu s’affiche, sélectionnez OK.
  6. Sélectionnez J’accepte lorsque la boîte de dialogue contrat de licence s’affiche.

Notes

La version 1.x.x du package NuGet est toujours disponible pour les développeurs qui souhaitent cibler HoloLens 1. Pour plus d’informations, consultez Ajouter une communication à distance holographique (HoloLens (1ère génération)).

Créer le contexte distant

Dans un premier temps, l’application doit créer un contexte distant.

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

Avertissement

La communication à distance holographique fonctionne en remplaçant le runtime Windows Mixed Reality qui fait partie de Windows par un runtime de communication à distance spécifique. Cette opération est effectuée lors de la création du contexte distant. Pour cette raison, tout appel sur n’importe quelle API Windows Mixed Reality avant de créer le contexte distant peut entraîner un comportement inattendu. L’approche recommandée consiste à créer le contexte distant le plus tôt possible avant l’interaction avec n’importe quelle API Mixed Reality. Ne mélangez jamais d’objets créés ou récupérés par le biais d’une API Windows Mixed Reality avant l’appel à CreateRemoteContext avec les objets créés ou récupérés par la suite.

Ensuite, l’espace holographique doit être créé. La spécification d’un CoreWindow n’est pas obligatoire. Les applications de bureau qui n’ont pas de CoreWindow peuvent simplement passer un nullptr.

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

Se connecter à l’appareil

Lorsque l’application distante est prête pour le rendu du contenu, une connexion à l’appareil de lecteur peut être établie.

La connexion peut être effectuée de l’une des deux façons suivantes.

  1. L’application distante se connecte au lecteur en cours d’exécution sur l’appareil.
  2. Le lecteur en cours d’exécution sur l’appareil se connecte à l’application distante.

Pour établir une connexion entre l’application distante et l’appareil lecteur, appelez la Connect méthode dans le contexte distant en spécifiant le nom d’hôte et le port. Le port utilisé par le lecteur de communication à distance holographique est 8265.

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

Important

Comme avec n’importe quelle API Connect C++/WinRT peut lever un winrt ::hresult_error qui doit être géré.

Conseil

Pour éviter d’utiliser la projection de langage C++/WinRT , le fichier build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h situé à l’intérieur du package NuGet holographique remotive peut être inclus. Il contient des déclarations des interfaces COM sous-jacentes. L’utilisation de C++/WinRT est toutefois recommandée.

Vous pouvez écouter les connexions entrantes sur l’application distante en appelant la Listen méthode . Le port de négociation et le port de transport peuvent être spécifiés pendant cet appel. Le port d’établissement d’une négociation est utilisé pour la négociation initiale. Les données sont ensuite envoyées via le port de transport. Par défaut , 8265 et 8266 sont utilisés.

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

Important

L’intérieur build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl du package NuGet contient une documentation détaillée pour l’API exposée par Holographic Remoting.

Gestion des événements spécifiques de communication à distance

Le contexte distant expose trois événements, qui sont importants pour surveiller l’état d’une connexion.

  1. OnConnected : déclenché lorsqu’une connexion à l’appareil a été établie avec succès.
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 : déclenché si une connexion établie est fermée ou si une connexion n’a pas pu être établie.
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 : lorsque l’écoute des connexions entrantes démarre.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

En outre, l’état de connexion peut être interrogé à l’aide de la ConnectionState propriété sur le contexte distant.

auto connectionState = m_remoteContext.ConnectionState();

Gestion des événements vocaux

À l’aide de l’interface vocale à distance, il est possible d’inscrire des déclencheurs vocaux avec HoloLens 2 et de les faire distants vers l’application distante.

Le membre supplémentaire suivant est requis pour suivre l’état de la voix distante :

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

Tout d’abord, récupérez l’interface vocale distante.

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

À l’aide d’une méthode d’assistance asynchrone, vous pouvez ensuite initialiser la voix distante. Cette opération doit être effectuée de manière asynchrone, car l’initialisation peut prendre beaucoup de temps. La concurrence et les opérations asynchrones avec C++/WinRT explique comment créer des fonctions asynchrones avec 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);
}

Il existe deux façons de spécifier des expressions à reconnaître.

  1. Spécification à l’intérieur d’un fichier xml de grammaire vocale. Pour plus d’informations, consultez Comment créer une grammaire XML de base .
  2. Spécifiez en les transmettant à l’intérieur du vecteur de dictionnaire à ApplyParameters.

À l’intérieur du rappel OnRecognizedSpeech, les événements vocaux peuvent ensuite être traités :

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

    ...
}

Aperçu du contenu diffusé localement

Pour afficher le même contenu dans l’application distante qui est envoyé à l’appareil, l’événement OnSendFrame du contexte distant peut être utilisé. L’événement OnSendFrame est déclenché chaque fois que la bibliothèque de communication à distance holographique envoie l’image actuelle à l’appareil distant. C’est le moment idéal pour prendre le contenu et également le placer dans la fenêtre de bureau ou 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 en profondeur

À compter de la version 2.1.0, la communication à distance holographique prend en charge la reprojection en profondeur. Pour cela, la mémoire tampon de couleur et la mémoire tampon de profondeur doivent être diffusées en continu de l’application distante vers le HoloLens 2. Par défaut, le streaming de mémoire tampon de profondeur est activé et configuré pour utiliser la moitié de la résolution de la mémoire tampon de couleur. Cela peut être modifié comme suit :

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

...

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

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

Notez que si les valeurs par défaut ne doivent pas être utiliséesConfigureDepthVideoStream, vous devez appeler avant d’établir une connexion au HoloLens 2. Le meilleur endroit est juste après avoir créé le contexte distant. Les valeurs possibles pour DepthBufferStreamResolution sont les suivantes :

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Désactivé (ajouté avec la version 2.1.3 et s’il est utilisé, aucun flux vidéo de profondeur supplémentaire n’est créé)

N’oubliez pas que l’utilisation d’une mémoire tampon de profondeur de résolution complète affecte également les besoins en bande passante et doit être prise en compte dans la valeur de bande passante maximale que vous fournissez à CreateRemoteContext.

Outre la configuration de la résolution, vous devez également valider une mémoire tampon de profondeur 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);
                }
            });
        }
    });
}

Pour vérifier si la reprojection de profondeur fonctionne correctement sur HoloLens 2, vous pouvez activer un visualiseur de profondeur via le portail d’appareil. Pour plus d’informations, consultez Vérifier que la profondeur est définie correctement .

Facultatif : canaux de données personnalisés

Les canaux de données personnalisés peuvent être utilisés pour envoyer des données utilisateur via la connexion de communication à distance déjà établie. Pour plus d’informations, consultez Canaux de données personnalisés.

Facultatif : Synchronisation du système de coordonnées

À compter de la version 2.7.0, la synchronisation du système de coordonnées peut être utilisée pour aligner les données spatiales entre le lecteur et l’application distante. Pour plus d’informations, consultez Coordination de la synchronisation du système avec vue d’ensemble de la communication à distance holographique.

Voir aussi