Udostępnij za pośrednictwem


Pisanie aplikacji zdalnej holograficznej za pomocą interfejsu API HolographicSpace

Jeśli jesteś nowym użytkownikm holographic Remoting, możesz przeczytać nasze omówienie.

Ważne

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

Powiadomienie o wycofaniu: wiersz wydania 2.9.x będzie ostatnim, który będzie obsługiwał interfejsy API systemu Windows Holographic na potrzeby tworzenia aplikacji. Nadchodzące wersje będą obsługiwać tylko bibliotekę OpenXR na potrzeby tworzenia aplikacji. Niezależnie od tego zalecamy użycie biblioteki OpenXR w aplikacji do tworzenia wszystkich nowych aplikacji. Istniejące aplikacje korzystające z wersji 2.9 lub starszej będą nadal działać bez wpływu na nadchodzące zmiany.

Aplikacje Holographic Remoting mogą przesyłać strumieniowo zdalnie renderowaną 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 immersyjne z istniejącym oprogramowaniem komputerowym. Aplikacja zdalna odbiera strumień danych wejściowych z HoloLens 2, renderuje zawartość w wirtualnym widoku immersywnym oraz przesyła ramki zawartości z powrotem do HoloLens 2. Połączenie jest nawiązywane przy użyciu standardowego sieci Wi-Fi. Holographic Remoting jest dodawany do aplikacji klasycznej lub uniwersalnej systemu Windows za pośrednictwem pakietu NuGet. Wymagany jest dodatkowy kod, który obsługuje połączenie i renderuje w widoku immersywnym. Typowe połączenie zdalnie będzie miało opóźnienie nawet 50 ms. Aplikacja odtwarzacza może zgłaszać opóźnienie w czasie rzeczywistym.

Cały kod na tej stronie i roboczych projektów można znaleźć w repozytorium GitHub przykładów holographic Remoting.

Wymagania wstępne

Dobrym punktem wyjścia jest działająca aplikacja klasyczna oparta na protokole DirectX lub uwP przeznaczona dla interfejsu API Windows Mixed Reality. Aby uzyskać szczegółowe informacje, zobacz Omówienie programowania directx. Szablon projektu holograficznego języka C++ jest dobrym punktem wyjścia.

Ważne

Każda aplikacja korzystająca z komunikacji zdalnie holograficznej powinna być utworzona w celu korzystania z apartamentu wielowątkowego. Korzystanie z jednego wątkowego mieszkania jest obsługiwane, ale doprowadzi do nie optymalnej wydajności i ewentualnie zacinania podczas odtwarzania. W przypadku korzystania z C++/ WinRT winrt::init_apartment mieszkanie wielowątkowa jest ustawieniem domyślnym.

Pobieranie pakietu NuGet Holographic Remoting

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

  1. Otwórz projekt w programie Visual Studio.
  2. Kliknij prawym przyciskiem myszy węzeł projektu i wybierz polecenie Zarządzaj pakietami NuGet...
  3. W wyświetlonym panelu wybierz pozycję Przeglądaj , a następnie wyszukaj ciąg "Holographic Remoting".
  4. Wybierz pozycję Microsoft.Holographic.Remoting, upewnij się, że wybrano najnowszą wersję 2.x.x i 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 pakietu NuGet jest nadal dostępna dla deweloperów, którzy chcą kierować urządzenie HoloLens 1. Aby uzyskać szczegółowe informacje, zobacz Add Holographic Remoting (HoloLens (1st gen))).

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

Holographic Remoting działa poprzez zastąpienie środowiska uruchomieniowego Windows Mixed Reality, które jest częścią systemu Windows za pomocą konkretnej komunikacji zdalniej określonego środowiska uruchomieniowego. 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 utworzenie kontekstu zdalnego tak wcześnie, jak to możliwe przed interakcją z dowolnym interfejsem API Mixed Reality. Nigdy nie mieszaj obiektów utworzonych lub pobranych za pośrednictwem dowolnego interfejsu API Windows Mixed Reality przed wywołaniem metody CreateRemoteContext z obiektami utworzonymi lub pobranymi później.

Następnie należy utworzyć przestrzeń holograficzną. Określanie systemu 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);

Nawiązywanie połączenia z urządzeniem

Gdy aplikacja zdalna jest gotowa do renderowania zawartości, można ustanowić połączenie z urządzeniem odtwarzacza.

Połączenie można wykonać 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 z urządzeniem odtwarzacza, wywołaj Connect metodę w kontekście zdalnym określającym nazwę hosta i port. Port używany przez 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 języka C++/WinRT może zgłosić winrt::hresult_error, które należy obsłużyć.

Porada

Aby uniknąć używania projekcji języka C++/WinRT , plik build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h znajdujący się wewnątrz pakietu NuGet holographic Remoting można uwzględnić. Zawiera deklaracje podstawowych interfejsów COM. Zaleca się jednak korzystanie z języka 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 uzgadniania, jak i port transportu. Port uzgadniania jest używany do początkowego uzgadniania. Dane są następnie wysyłane za pośrednictwem portu transportowego. Domyślnie używane są 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

Wewnątrz build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl pakietu NuGet znajduje się szczegółowa dokumentacja interfejsu API uwidoczniona przez usługę Holographic Remoting.

Obsługa komunikacji zdalniej 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: wyzwolony, jeśli nawiązane połączenie jest zamknięte lub nie można nawiązać 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 wykonywać zapytania dotyczące stanu połączenia przy użyciu ConnectionState właściwości w kontekście zdalnym.

auto connectionState = m_remoteContext.ConnectionState();

Obsługa zdarzeń mowy

Za pomocą interfejsu zdalnego rozpoznawania mowy można zarejestrować wyzwalacze mowy za pomocą HoloLens 2 i mieć je zdalnie z aplikacją zdalną.

Do śledzenia stanu mowy zdalnej jest wymagany następujący dodatkowy element członkowski:

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

Najpierw pobierz interfejs zdalnego rozpoznawania mowy.

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

Za pomocą metody pomocnika asynchronicznego można następnie 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śnia, jak tworzyć funkcje asynchroniczne za pomocą języka 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 zdarzenia mowy można następnie przetworzyć:

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 zawartości przesyłanej strumieniowo lokalnie

Aby wyświetlić tę samą zawartość w aplikacji zdalnej, która jest wysyłana do urządzenia, OnSendFrame 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 podjąć zawartość, a także rozbić ją w oknie pulpitu lub platformy 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.
    });

Ponowneprojekty głębokości

Począwszy od wersji 2.1.0, komunikacja zdalna Holographic obsługuje reprojektowanie głębokości. Wymaga to przesyłania strumieniowego zarówno bufora kolorów, jak i buforu głębokości z aplikacji zdalnej do HoloLens 2. Domyślnie przesyłanie strumieniowe bufora głębokości jest włączone i skonfigurowane do używania połowy rozdzielczości bufora 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. Najlepsze miejsce znajduje się tuż po utworzeniu kontekstu zdalnego. Możliwe wartości parametru DepthBufferStreamResolution to:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Wyłączone (dodane z wersją 2.1.3 i jeśli nie utworzono dodatkowego strumienia wideo głębokości)

Należy pamiętać, że użycie buforu głębokości pełnej rozdzielczości wpływa również na wymagania dotyczące przepustowości i musi być uwzględniane w wartości maksymalnej przepustowości podanej w CreateRemoteContextusłudze .

Oprócz skonfigurowania rozdzielczości należy również zatwierdzić bufor głębokości za pośrednictwem 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 ponowny projekt głębokości działa prawidłowo na HoloLens 2, możesz włączyć wizualizator głębokości za pośrednictwem portalu 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 wirtualnej. 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ównania danych przestrzennych między odtwarzaczem a aplikacją zdalną. Aby uzyskać więcej informacji, zobacz Temat Synchronizacja systemu współrzędnych za pomocą komunikacji zdalnie holograficznej — omówienie.

Zobacz też