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 na odwrót. 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 zestawów nagłownych. Możesz również uzyskać dostęp do większej liczby zasobów systemowych i zintegrować zdalne widoki immersywne z istniejącym oprogramowaniem na komputerze stacjonarnym. Aplikacja zdalna odbiera strumień danych wejściowych z platformy HoloLens 2, renderuje zawartość w wirtualnym widoku immersywnie 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 holograficzna jest dodawana do aplikacji klasycznej lub UWP za pośrednictwem NuGet zdalnego. Wymagany jest dodatkowy kod, który obsługuje połączenie i renderuje w widoku immersyjnym. Typowe połączenie komunikacji zdalnej będzie mieć opóźnienie do 50 ms. 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łady dotyczące komunikacji zdalnej systemu Holographic.

Wymagania wstępne

Dobrym punktem początkowym jest dobrą aplikację komputerową opartą na directx lub UWP, 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żda aplikacja korzystająca z usługi Holographic Remoting powinna być authored w celu używania wielowątkowego miski. Korzystanie z jednowątkowego typu schowek 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ą jest wielowątkowy kompilator.

Pobierz pakiet aplikacji Holographic Remoting NuGet

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 wybierasz 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 pojawi się 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

Komunikacja zdalna holograficzna działa przez zastąpienie Windows Mixed Reality uruchomieniowego, które jest częścią Windows ś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 najszybciej utworzyć kontekst zdalny przed interakcją z dowolnym interfejsem API Mixed Reality API. Nigdy nie mieszaj obiektów utworzonych lub pobranych za pośrednictwem dowolnego Windows Mixed Reality API 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, Connect wywołaj metodę w kontekście zdalnym, określając 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 Connect C++/WinRT, może zostać hresult_error winrt::hresult_error który musi być obsługiwany.

Porada

Aby uniknąć używania projekcji języka C++/WinRT, build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h można donąć plik znajdujący się w 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 8265i 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 build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl wewnątrz pakietu NuGet zawiera szczegółową dokumentację interfejsu API udostępnianego przez usługę Holographic Remoting.

Obsługa komunikacji zdalnej określonych zdarzeń

Zdalny kontekst 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.

Następujący dodatkowy członek jest wymagany do śledzenia stanu zdalnej mowy:

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

Najpierw pobierz zdalny interfejs mowy.

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

Za pomocą asynchronicznej metody pomocnika można zainicjować zdalną mowę. 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. Aby uzyskać szczegółowe informacje, zobacz 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 można przetwarzać zdarzenia mowy:

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 OnSendFrame wysyłana do urządzenia, można użyć zdarzenia kontekstu zdalnego. Zdarzenie OnSendFrame jest wyzwalane za każdym razem, gdy biblioteka Holographic Remoting 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 zarówno bufor kolorów, jak 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ć ConfigureDepthVideoStream wartości domyślnych, przed nawiązaniem połączenia z HoloLens 2. Najlepszym miejscem jest zaraz po utworzeniu kontekstu zdalnego. Możliwe wartości dla depthBufferStreamResolution to:

  • 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 zapewnianej programowi CreateRemoteContext.

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