Pisanie zdalnej aplikacji holographic remoting przy użyciu interfejsu API HolographicSpace

Jeśli jesteś nowym użytkownikom systemu Holographic Remoting, zapoznaj się z naszym omówieniem.

Ważne

W tym dokumencie opisano tworzenie aplikacji zdalnej dla platformy HoloLens 2 przy użyciu interfejsu API HolographicSpace. Aplikacje zdalne dla HoloLens (1. generacji) muszą używać NuGet pakietu w wersji 1.x.x. Oznacza to, że aplikacje zdalne napisane dla HoloLens 2 nie są zgodne HoloLens 1 i odwrotnie. Dokumentację dla HoloLens 1 można znaleźć tutaj.

Aplikacje Holographic Remoting mogą przesyłać strumieniowo zdalnie renderowana zawartość do HoloLens 2 i Windows Mixed Reality immersywnych. Możesz również uzyskać dostęp do większej liczby zasobów systemowych i zintegrować zdalne widoki immersyjne z istniejącym oprogramowaniem na komputerze stacjonarnym. Aplikacja zdalna odbiera strumień danych wejściowych z HoloLens 2, renderuje zawartość w wirtualnym widoku immersyjnym i przesyła strumieniowo ramki zawartości z powrotem do HoloLens 2. Połączenie jest nawiązane przy użyciu standardowej sieci Wi-Fi. Komunikacja zdalna platformy Holographic jest dodawana do aplikacji klasycznej lub aplikacji platformy UWP za pośrednictwem NuGet pakietów. Wymagany jest dodatkowy kod, który obsługuje połączenie i renderuje w widoku immersyjnym. Typowe połączenie komunikacji zdalnej będzie mieć nawet 50 ms opóźnienia. Aplikacja odtwarzacza może zgłaszać opóźnienie w czasie rzeczywistym.

Cały kod na tej stronie i projekty robocze można znaleźć w repozytorium GitHub przykładów komunikacji zdalnej systemu Holographic.

Wymagania wstępne

Dobrym punktem początkowym jest działanie aplikacji klasycznej lub platformy uniwersalnej systemu Windows opartej na directx, która jest skierowana do Windows Mixed Reality API. Aby uzyskać szczegółowe informacje, zobacz Omówienie tworzenia aplikacji DirectX. Szablon projektu holograficznego języka C++ jest dobrym punktem początkowym.

Ważne

Każdą aplikację korzystającą z komunikacji zdalnej Holographic należy ować tak, aby używać wielowątkowego miski. Użycie jednowątkowej klatki jest obsługiwane, ale prowadzi do nie optymalnej wydajności i być może stuttering podczas odtwarzania. W przypadku korzystania z C++/WinRT winrt::init_apartment domyślną wartością domyślną jest wielowątkowy rozmiar).

Pobierz pakiet komunikacji zdalnej NuGet Holographic

Poniższe kroki są wymagane do dodania pakietu NuGet do projektu w Visual Studio.

  1. Otwórz projekt w programie Visual Studio.
  2. Kliknij prawym przyciskiem myszy węzeł projektu i wybierz pozycję Zarządzaj NuGet pakietów...
  3. W wyświetlonym panelu wybierz pozycję Przeglądaj, a następnie wyszukaj pozycję "Holographic Remoting".
  4. Wybierz pozycję Microsoft.Holographic.Remoting, upewnij się, że wybierz najnowszą wersję 2.x.x, a następnie wybierz pozycję Zainstaluj.
  5. Jeśli zostanie wyświetlone okno dialogowe Podgląd, wybierz przycisk OK.
  6. Wybierz pozycję Akceptuję, gdy zostanie wyświetlone okno dialogowe umowy licencyjnej.

Uwaga

Wersja 1.x.x pakietu NuGet jest nadal dostępna dla deweloperów, którzy chcą korzystać z wersji HoloLens 1. Aby uzyskać szczegółowe informacje, zobacz Add Holographic Remoting (HoloLens (1. generacja)).

Tworzenie kontekstu zdalnego

W pierwszym kroku aplikacja powinna utworzyć kontekst zdalny.

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

Ostrzeżenie

Funkcja Holographic Remoting działa przez zastąpienie środowiska uruchomieniowego Windows Mixed Reality, które jest częścią Windows z konkretnym środowiskiem uruchomieniowym komunikacji zdalnej. Odbywa się to podczas tworzenia kontekstu zdalnego. Z tego powodu każde wywołanie dowolnego interfejsu API Windows Mixed Reality przed utworzeniem kontekstu zdalnego może spowodować nieoczekiwane zachowanie. Zalecanym podejściem jest jak najwcześniejsza utworzenie kontekstu zdalnego przed interakcją z dowolnym Mixed Reality API. Nigdy nie mieszaj obiektów utworzonych lub pobranych za pośrednictwem dowolnego interfejsu API Windows Mixed Reality przed wywołaniem funkcji CreateRemoteContext z obiektami utworzonymi lub pobranymi później.

Następnie należy utworzyć przestrzeń holograficzną. Określanie coreWindow nie jest wymagane. Aplikacje klasyczne, które nie mają systemu CoreWindow, mogą po prostu przekazać element nullptr .

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

Połączenie z urządzeniem

Gdy aplikacja zdalna będzie gotowa do renderowania zawartości, można nawiązane połączenie z urządzeniem odtwarzacza.

Połączenie można nawiązaniu na jeden z dwóch sposobów.

  1. Aplikacja zdalna łączy się z odtwarzaczem uruchomionym na urządzeniu.
  2. Odtwarzacz uruchomiony na urządzeniu łączy się z aplikacją zdalną.

Aby nawiązać połączenie z aplikacji zdalnej do urządzenia odtwarzacza, wywołaj metodę w kontekście zdalnym, określając Connect nazwę hosta i port. Port używany przez urządzenie Holographic Remoting Player to 8265.

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

Ważne

Podobnie jak w przypadku dowolnego interfejsu API C++/WinRT, może zostać hresult_error Connect winrt::hresult_error który musi być obsługiwany.

Porada

Aby uniknąć używania projekcji języka C++/WinRT, można donąć plik znajdujący się w build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h pakiecie NuGet Holographic Remoting. Zawiera on deklaracje podstawowych interfejsów COM. Zaleca się jednak korzystanie z C++/WinRT.

Nasłuchiwanie połączeń przychodzących w aplikacji zdalnej można wykonać przez wywołanie Listen metody . Podczas tego wywołania można określić zarówno port uściśli, jak i port transportu. Port uściślinia jest używany do początkowego ugody. Dane są następnie wysyłane za pośrednictwem portu transportu. Domyślnie używane są wartości 8265 i 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());
}

Ważne

Plik wewnątrz pakietu NuGet zawiera szczegółową dokumentację build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl interfejsu API udostępnianego przez usługę Holographic Remoting.

Obsługa komunikacji zdalnej określonych zdarzeń

Kontekst zdalny uwidacznia trzy zdarzenia, które są ważne do monitorowania stanu połączenia.

  1. OnConnected: wyzwalane po pomyślnym nawiązaniu połączenia z urządzeniem.
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: wyzwalane w przypadku zamknięcia nawiązanego połączenia lub nie można nawiązanego połączenia.
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: Podczas nasłuchiwania połączeń przychodzących rozpoczyna się.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Ponadto można zbadać stan połączenia przy użyciu właściwości ConnectionState w kontekście zdalnym.

auto connectionState = m_remoteContext.ConnectionState();

Obsługa zdarzeń mowy

Za pomocą zdalnego interfejsu mowy można zarejestrować wyzwalacze mowy za pomocą interfejsu HoloLens 2 i zdalnie odległe do aplikacji zdalnej.

Ten dodatkowy element członkowski jest wymagany do śledzenia stanu zdalnej mowy.

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

Najpierw pobierz interfejs zdalnej mowy.

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

Za pomocą asynchronicznej metody pomocnika można zainicjować mowę zdalną. Należy to zrobić asynchronicznie, ponieważ inicjowanie może zająć dużo czasu. Współbieżność i operacje asynchroniczne w języku C++/WinRT wyjaśniają sposób tworzenia funkcji asynchronicznych w języku 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);
}

Istnieją dwa sposoby określania fraz do rozpoznania.

  1. Specyfikacja wewnątrz pliku XML gramatyki mowy. Szczegółowe informacje można znaleźć w temacie How to create a basic XML Grammar (Jak utworzyć podstawową gramatykę XML).
  2. Określ, przekazując je wewnątrz wektora słownika do ApplyParameters .

Wewnątrz wywołania zwrotnego OnRecognizedSpeech zdarzenia mowy mogą być przetwarzane:

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

    ...
}

Podgląd przesyłanej strumieniowo zawartości lokalnie

Aby wyświetlić tę samą zawartość w aplikacji zdalnej, która jest wysyłana do urządzenia, można użyć zdarzenia kontekstu OnSendFrame zdalnego. Zdarzenie OnSendFrame jest wyzwalane za każdym razem, gdy biblioteka holograficzna komunikacji zdalnej wysyła bieżącą ramkę do urządzenia zdalnego. Jest to idealny czas, aby zabrać zawartość, a także ją w oknie pulpitu lub platformy uniwersalnej systemu Windows.

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

Depth Reprojection

Począwszy od wersji 2.1.0,funkcja Holographic Remoting obsługuje reprojekcję głębokości. Wymaga to, aby bufor kolorów i bufor głębokości był przesyłany strumieniowo z aplikacji zdalnej do HoloLens 2. Domyślnie przesyłanie strumieniowe buforu głębokości jest włączone i skonfigurowane do używania połowy rozdzielczości buforu kolorów. Można to zmienić w następujący sposób:

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

...

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

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

Należy pamiętać, że jeśli nie należy używać wartości domyślnych, przed nawiązaniem połączenia z HoloLens ConfigureDepthVideoStream 2. Najlepszym miejscem jest tuż po utworzeniu kontekstu zdalnego. Możliwe wartości dla depthBufferStreamResolution są:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Wyłączone (dodane w wersji 2.1.3 i jeśli nie są używane żadne dodatkowe głębokości strumienia wideo jest tworzony)

Należy pamiętać, że użycie buforu głębokości o pełnej rozdzielczości ma również wpływ na wymagania dotyczące przepustowości i musi zostać uwzględnione w maksymalnej wartości przepustowości, która jest zapewniana dla programu CreateRemoteContext .

Oprócz skonfigurowania rozwiązania należy również zatwierdzić bufor głębokości za pośrednictwem obiektu 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);
                }
            });
        }
    });
}

Aby sprawdzić, czy reprojekcja głębokości działa poprawnie na HoloLens 2, możesz włączyć wizualizator głębokości za pośrednictwem Portal urządzeń. Aby uzyskać szczegółowe informacje, zobacz Sprawdzanie, czy głębokość jest ustawiona poprawnie.

Opcjonalnie: Niestandardowe kanały danych

Niestandardowe kanały danych mogą służyć do wysyłania danych użytkownika za pośrednictwem już ustanowionego połączenia komunikacji zdalnej. Aby uzyskać więcej informacji, zobacz Niestandardowe kanały danych.

Opcjonalnie: synchronizacja systemu współrzędnych

Począwszy od wersji 2.7.0,synchronizacja systemu współrzędnych może służyć do wyrównywania danych przestrzennych między odtwarzaczem i aplikacją zdalną. Aby uzyskać więcej informacji, zobacz Coordinate System Synchronization with Holographic Remoting Overview (Synchronizacja systemu współrzędnych z usługą Holographic Remoting — omówienie).

Zobacz też