Schreiben einer Holographic Remoting-Remote-App mithilfe der holographicspace-APIWriting a Holographic Remoting remote app using the HolographicSpace API

Wichtig

In diesem Dokument wird die Erstellung einer Remote Anwendung für hololens 2 mithilfe der holographicspace-APIbeschrieben.This document describes the creation of a remote application for HoloLens 2 using the HolographicSpace API. Für Remote Anwendungen für hololens (1st Gen) muss das nuget-Paketversion 1. x. x verwendet werden.Remote applications for HoloLens (1st gen) must use NuGet package version 1.x.x. Dies bedeutet, dass für hololens 2 geschriebene Remote Anwendungen nicht mit hololens 1 und umgekehrt kompatibel sind.This implies that remote applications written for HoloLens 2 are not compatible with HoloLens 1 and vice versa. Die Dokumentation für hololens 1 finden Sie hier.The documentation for HoloLens 1 can be found here.

Holographic Remoting-Apps können per Remote Zugriff gerenderten Inhalt in hololens 2-und Windows Mixed Reality-immersive Headsets streamen.Holographic Remoting apps can stream remotely rendered content to HoloLens 2 and Windows Mixed Reality immersive headsets. Sie können auch auf weitere Systemressourcen zugreifen und die in vorhandene Desktop-PC-Software integrieren.You can also access more system resources and integrate remote immersive views into existing desktop PC software. Eine Remote-app empfängt einen Eingabedaten Strom von hololens 2, rendert Inhalte in einer virtuellen immersiven Ansicht und streamt Inhalts Frames zurück an hololens 2.A remote app receives an input data stream from HoloLens 2, renders content in a virtual immersive view, and streams content frames back to HoloLens 2. Die Verbindung wird mithilfe von Standard-Wi-Fi hergestellt.The connection is made using standard Wi-Fi. Holographic-Remoting wird mithilfe eines nuget-Pakets zu einer Desktop-oder UWP-app hinzugefügt.Holographic Remoting is added to a desktop or UWP app via a NuGet packet. Zusätzlicher Code ist erforderlich, der die Verbindung verarbeitet und in einer immersiven Ansicht rendert.Additional code is required which handles the connection and renders in an immersive view. Eine typische remotingverbindung verfügt über bis zu 50 ms Latenzzeit.A typical remoting connection will have as low as 50 ms of latency. Die Player-App kann die Latenzzeit in Echtzeit melden.The player app can report the latency in real time.

Sämtlicher Code auf dieser Seite und in den Arbeitsprojekten finden Sie im GitHub-Repository "Holographic Remoting Samples".All code on this page and working projects can be found in the Holographic Remoting samples github repository.

VoraussetzungenPrerequisites

Ein guter Ausgangspunkt ist eine funktionierende DirectX-basierte Desktop-oder UWP-APP, die auf die holographicspace-APIabzielt.A good starting point is a working DirectX based Desktop or UWP app, which targets the HolographicSpace API. Weitere Informationen finden Sie unter Übersicht über die DirectX-Entwicklung.For details see DirectX development overview. Die Projektvorlage C++ Holographic ist ein guter Ausgangspunkt.The C++ holographic project template is a good starting point.

Wichtig

Jede APP, die Holographic Remoting verwendet, sollte für die Verwendung eines Multithread-Apartmenterstellt werden.Any app using Holographic Remoting should be authored to use a multi-threaded apartment. Die Verwendung eines Single Thread-Apartment wird unterstützt, führt jedoch zu einer nicht optimalen Leistung und kann während der Wiedergabe möglicherweise zu einem stutor werden.The use of a single-threaded apartment is supported but will lead to sub-optimal performance and possibly stuttering during playback. Bei Verwendung von C++/WinRT WinRT:: init_apartment ist ein Multithread-Apartment der Standard.When using C++/WinRT winrt::init_apartment a multi-threaded apartment is the default.

Holen Sie sich das Holographic Remoting-nuget-Paket.Get the Holographic Remoting NuGet package

Die folgenden Schritte sind erforderlich, um das nuget-Paket einem Projekt in Visual Studio hinzuzufügen.The following steps are required to add the NuGet package to a project in Visual Studio.

  1. Öffnen Sie das Projekt in Visual Studio.Open the project in Visual Studio.
  2. Klicken Sie mit der rechten Maustaste auf den Projekt Knoten, und wählen Sie nuget-Pakete verwalten aus.Right-click the project node and select Manage NuGet Packages...
  3. Wählen Sie im angezeigten Bereich Durchsuchen aus, und suchen Sie dann nach "Holographic Remoting".In the panel that appears, select Browse and then search for "Holographic Remoting".
  4. Wählen Sie Microsoft. Holographic. Remoting aus, stellen Sie sicher, dass die neueste Version 2. x. x ausgewählt ist, und wählen Sie Installieren aus.Select Microsoft.Holographic.Remoting, ensure to pick the latest 2.x.x version and select Install.
  5. Wenn das Dialogfeld Vorschau angezeigt wird, klicken Sie auf OK.If the Preview dialog appears, select OK.
  6. Wählen Sie Ich akzeptiere aus, wenn das Dialogfeld Lizenzvertrag angezeigt wird.Select I Accept when the license agreement dialog pops up.

Hinweis

Version 1. x. x des nuget-Pakets ist weiterhin für Entwickler verfügbar, die auf hololens 1 abzielen möchten.Version 1.x.x of the NuGet package is still available for developers who want to target HoloLens 1. Weitere Informationen finden Sie unter Hinzufügen von Holographic Remoting (hololens (1st Gen)).For details see Add Holographic Remoting (HoloLens (1st gen)).

Erstellen des Remote KontextsCreate the remote context

Als ersten Schritt sollte die Anwendung einen Remote Kontext erstellen.As a first step the application should create a remote context.

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

Warnung

Holographic Remoting funktioniert durch das Ersetzen der Windows Mixed Reality-Laufzeit, die Teil von Windows ist, durch eine Remoting-spezifische Laufzeit.Holographic Remoting works by replacing the Windows Mixed Reality runtime which is part of Windows with a remoting specific runtime. Dies erfolgt während der Erstellung des Remote Kontexts.This is done during the creation of the remote context. Aus diesem Grund kann jeder beliebige Windows Mixed Reality-API-Aufrufe vor dem Erstellen des Remote Kontexts zu unerwartetem Verhalten führen.For that reason any call on any Windows Mixed Reality API before creating the remote context can result in unexpected behavior. Die empfohlene Vorgehensweise besteht darin, den Remote Kontext so früh wie möglich zu erstellen, bevor Sie mit jeder gemischten Reality-API interagieren.The recommended approach is to create the remote context as early as possible before interaction with any Mixed Reality API. Erstellen oder Abrufen von Objekten, die über eine Windows Mixed Reality-API erstellt oder abgerufen wurden, vor dem Aufrufen von "" mit Objekten, die später erstellt oder abgerufen werden.Never mix objects created or retrieved through any Windows Mixed Reality API before the call to CreateRemoteContext with objects created or retrieved afterwards.

Als nächstes muss der holografische Speicherplatz erstellt werden.Next the holographic space needs to be created. Das Angeben eines corewindow ist nicht erforderlich.Specifying a CoreWindow isn't required. Desktop-Apps, die nicht über ein corewindow verfügen, können nur einen übergeben nullptr .Desktop apps that don't have a CoreWindow can just pass a nullptr.

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

Verbindung mit dem Gerät herstellenConnect to the device

Wenn die Remote-app zum Rendern von Inhalten bereit ist, kann eine Verbindung mit dem Player Gerät hergestellt werden.When the remote app is ready for rendering content a connection to the player device can be established.

Die Verbindung kann auf zwei Arten erreicht werden.Connection can be done in one of two ways.

  1. Die Remote-app stellt eine Verbindung zum Player her, der auf dem Gerät ausgeführt wird.The remote app connects to the player running on the device.
  2. Der Player, der auf dem Gerät ausgeführt wird, stellt eine Verbindung zur Remote-app herThe player running on the device connects to the remote app.

Um eine Verbindung zwischen der Remote-app und dem Player-Gerät herzustellen, müssen Sie die- Connect Methode im Remote Kontext angeben, die den Hostnamen und den Port angibt.To establish a connection from the remote app to the player device call the Connect method on the remote context specifying the hostname and port. Der von Holographic Remoting Player verwendete Port ist 8265.The port used by the 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());
}

Wichtig

Wie bei jeder C++/WinRT-API Connect könnte eine WinRT:: hresult_error ausgelöst werden, die behandelt werden muss.As with any C++/WinRT API Connect might throw an winrt::hresult_error which needs to be handled.

Tipp

Um die Verwendung der C++/WinRT -sprach Projektion zu vermeiden, kann die Datei build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h im Holographic Remoting-nuget-Paket eingeschlossen werden.To avoid using C++/WinRT language projection the file build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h located inside the Holographic Remoting NuGet package can be included. Sie enthält Deklarationen der zugrunde liegenden COM-Schnittstellen.It contains declarations of the underlying COM interfaces. Die Verwendung von C++/WinRT wird jedoch empfohlen.The use of C++/WinRT is recommended though.

Das Lauschen auf eingehende Verbindungen in der Remote-app kann durch Aufrufen der- Listen Methode erfolgen.Listening for incoming connections on the remote app can be done by calling the Listen method. Der Handshake-Port und der Transporttyp können während dieses Aufrufes angegeben werden.Both the handshake port and transport port can be specified during this call. Der Handshake-Port wird für den ersten Handshake verwendet.The handshake port is used for the initial handshake. Die Daten werden dann über den transportport gesendet.The data is then sent over the transport port. Standardmäßig werden 8265 und 8266 verwendet.By default 8265 and 8266 are used.

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

Wichtig

Der build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl im nuget-Paket enthält eine ausführliche Dokumentation für die API, die von Holographic Remoting verfügbar gemacht wird.The build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl inside the NuGet package contains detailed documentation for the API exposed by Holographic Remoting.

Behandeln von Remoting-spezifischen EreignissenHandling Remoting specific events

Der Remote Kontext macht drei Ereignisse verfügbar, die für die Überwachung des Zustands einer Verbindung wichtig sind.The remote context exposes three events, which are important to monitor the state of a connection.

  1. Onconnected: wird ausgelöst, wenn eine Verbindung mit dem Gerät erfolgreich hergestellt wurde.OnConnected: Triggered when a connection to the device has been successfully established.
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. Ongetrennte: wird ausgelöst, wenn eine etablierte Verbindung geschlossen wird oder keine Verbindung hergestellt werden konnte.OnDisconnected: Triggered if an established connection is closed or a connection couldn't be established.
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: beim lauschen auf eingehende Verbindungen wird gestartet.OnListening: When listening for incoming connections starts.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Außerdem kann der Verbindungsstatus mithilfe der- ConnectionState Eigenschaft im Remote Kontext abgefragt werden.Additionally the connection state can be queried using the ConnectionState property on the remote context.

auto connectionState = m_remoteContext.ConnectionState();

Behandeln von sprach EreignissenHandling speech events

Mithilfe der Remote Sprachschnittstelle ist es möglich, sprach Trigger bei hololens 2 zu registrieren und Sie Remote mit der Remote Anwendung zu verbinden.Using the remote speech interface it's possible to register speech triggers with HoloLens 2 and have them remoted to the remote application.

Dieser zusätzliche Member ist erforderlich, um den Status der Remote Sprache zu verfolgen.This additional member is required to track the state of the remote speech.

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

Zuerst muss die Remote Sprachschnittstelle abgerufen werden.First the remote speech interface needs to be retrieved.

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

Mithilfe einer asynchronen Hilfsmethode können Sie dann die Remote Sprache initialisieren.Using an asynchronous helper method you can then initialize the remote speech. Dies sollte asynchron erfolgen, da die Initialisierung eine beträchtliche Zeit in Anspruch nehmen kann.This should be done asynchronously as initializing might take a considerable amount of time. Parallelitäts -und asynchrone Vorgänge mit C++/WinRT erläutert, wie asynchrone Funktionen mit C++ erstellt werden können/WinRT.Concurrency and asynchronous operations with C++/WinRT explains how to author asynchronous functions with 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);
}

Es gibt zwei Möglichkeiten zum Angeben von Ausdrücken, die erkannt werden sollen.There are two ways of specifying phrases to be recognized.

  1. Spezifikation in einer Sprachgrammatik-XML-Datei.Specification inside a speech grammar xml file. Weitere Informationen finden Sie unter Erstellen einer grundlegenden XML-Grammatik .See How to create a basic XML Grammar for details.
  2. Geben Sie an, indem Sie Sie im Wörterbuch Vektor an übergeben ApplyParameters .Specify by passing them inside the dictionary vector to ApplyParameters.

Innerhalb des onerkenzedspeech-Rückrufs können die sprach Ereignisse verarbeitet werden:Inside the OnRecognizedSpeech callback, the speech events can then be processed:

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

    ...
}

Vorschau der lokal gestreuten InhaltePreview streamed content locally

Zum Anzeigen desselben Inhalts in der Remote-app, die an das Gerät gesendet wird, kann das- OnSendFrame Ereignis des Remote Kontexts verwendet werden.To display the same content in the remote app that is sent to the device the OnSendFrame event of the remote context can be used. Das- OnSendFrame Ereignis wird jedes Mal ausgelöst, wenn die Holographic Remoting-Bibliothek den aktuellen Frame an das Remote Gerät sendet.The OnSendFrame event is triggered every time the Holographic Remoting library sends the current frame to the remote device. Dies ist der ideale Zeitpunkt, um den Inhalt zu übernehmen und ihn im Desktop-oder UWP-Fenster zu blitten.This is the ideal time to take the content and also blit it into the desktop or UWP window.

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

Tiefen neuprojektionDepth Reprojection

Ab Version 2.1.0unterstützt Holographic Remoting die tiefen neuprojektion.Starting with version 2.1.0, Holographic Remoting supports Depth Reprojection. Dies erfordert, dass sowohl der Farb Puffer als auch der tiefen Puffer von der Remote Anwendung auf die hololens 2 gestreamt werden.This requires both the color buffer and depth buffer to be streamed from the Remote application to the HoloLens 2. Standardmäßig ist das tiefen Puffer Streaming aktiviert und so konfiguriert, dass die Hälfte der Auflösung des Farb Puffers verwendet wird.By default depth buffer streaming is enabled and configured to use half the resolution of the color buffer. Dies kann wie folgt geändert werden:This can be changed as follows:

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

...

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

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

Beachten Sie, dass, wenn Standardwerte nicht verwendet werden sollten, ConfigureDepthVideoStream vor dem Herstellen einer Verbindung mit den hololens 2 aufgerufen werden muss.Note, if default values should not be used ConfigureDepthVideoStream must be called before establishing a connection to the HoloLens 2. Der beste Ort ist direkt, nachdem Sie den Remote Kontext erstellt haben.The best place is right after you have created the remote context. Mögliche Werte für depthbufferstreamresolution:Possible values for DepthBufferStreamResolution are:

  • Full_ResolutionFull_Resolution
  • Half_ResolutionHalf_Resolution
  • Quarter_ResolutionQuarter_Resolution
  • Deaktiviert (mit Version 2.1.3 hinzugefügt und wird verwendet, wenn kein zusätzlicher ausführungsvideostream erstellt wird)Disabled (added with version 2.1.3 and if used no additional depth video stream is created)

Beachten Sie, dass die Verwendung eines tiefen Puffers für die vollständige Auflösung auch die Bandbreitenanforderungen beeinflusst und in dem maximalen Bandbreitenwert berücksichtigt werden muss, der für bereitgestellt wird CreateRemoteContext .Keep in mind that using a full resolution depth buffer also affects bandwidth requirements and needs to be accounted for in the maximum bandwidth value you provide to CreateRemoteContext.

Neben dem Konfigurieren der Auflösung müssen Sie auch einen tiefen Puffer über holographiccamerderingparameters. CommitDirect3D11DepthBufferübertragen.Beside configuring the resolution, you also have to commit a depth buffer 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);
                }
            });
        }
    });
}

Um zu überprüfen, ob die tiefen neuprojektion ordnungsgemäß auf hololens 2 funktioniert, können Sie eine tiefen Schnellansicht über das Geräte Portal aktivieren.To verify if depth reprojection is correctly working on HoloLens 2, you can enable a depth visualizer via the Device Portal. Weitere Informationen finden Sie unter über Prüfen der Tiefe festgelegt wurde .See Verifying Depth is Set Correctly for details.

Optional: benutzerdefinierte DatenkanäleOptional: Custom data channels

Benutzerdefinierte Datenkanäle können zum Senden von Benutzerdaten über die bereits festgelegte Remote Verbindung verwendet werden.Custom data channels can be used to send user data over the already established remoting connection. Weitere Informationen finden Sie unter benutzerdefinierte Datenkanäle .See custom data channels for more information.

Weitere InformationenSee Also