Partager via


Écriture d’une application holographique à distance à distance à l’aide de l’API HolographicSpace

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

Importante

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 Windows Holographiques 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 tout développement de nouvelles 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 de communication à distance holographiques peuvent diffuser en continu du contenu rendu à distance vers des casques immersifs HoloLens 2 et Windows Mixed Reality. Vous pouvez également accéder à d’autres ressources système et intégrer des vues immersives à distance dans les logiciels pc de bureau existants. Une application distante reçoit un flux de données d’entrée de HoloLens 2, restitue le contenu dans une vue immersive virtuelle et diffuse en continu des images de contenu vers HoloLens 2. La connexion est établie à l’aide du 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 un affichage immersif. Une connexion de communication à distance classique a une latence inférieure à 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 de communication à distance Holographic.

Configuration requise

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.

Importante

Toute application utilisant la communication à distance holographique 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 éventuellement 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 nécessaires 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 « Communication à distance holographique ».
  4. Sélectionnez Microsoft.Holographic.Remoting, veillez à choisir 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.

Remarque

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 la 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

Holographic Remoting fonctionne en remplaçant le runtime Windows Mixed Reality qui fait partie de Windows par un runtime spécifique de communication à distance. 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 les objets créés ou récupérés via 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 lecteur peut être établie.

La connexion peut être effectuée de l’une des deux manières 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 sur 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());
}

Importante

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 Holographic Remoting peut être inclus. Il contient les déclarations des interfaces COM sous-jacentes. L’utilisation de C++/WinRT est toutefois recommandée.

L’écoute des connexions entrantes sur l’application distante peut être effectuée 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 de négociation est utilisé pour l’établissement d’une liaison 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());
}

Importante

Le build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl 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 la 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 passer à distance à 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 à distance. 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.

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

    ...
}

Afficher un aperçu du contenu diffusé en continu localement

Pour afficher le même contenu dans l’application distante que celui 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 l’insérer 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 de profondeur

À compter de la version 2.1.0, la communication à distance holographique prend en charge la reprojection en profondeur. Cela nécessite que la mémoire tampon de couleur et la mémoire tampon de profondeur soient 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éé)

Gardez à l’esprit 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 pris 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 Vue d’ensemble de la synchronisation du système de coordonnées avec la communication à distance holographique.

Voir aussi