Создание удаленного голографического приложения удаленного взаимодействия с помощью API HolographicSpace

Если вы не знакомы с голографическим удаленным взаимодействием, ознакомьтесь с нашим обзором.

Важно!

В этом документе описывается создание удаленного приложения для HoloLens 2 с помощью API HolographicSpace. Удаленные приложения для HoloLens (1-го поколения) должны использовать пакет NuGet версии 1.x.x. Это означает, что удаленные приложения, написанные для HoloLens 2, несовместимы с HoloLens 1 и наоборот. Документацию по HoloLens 1 можно найти здесь.

Уведомление об устаревании. Последняя строка выпуска 2.9.x будет поддерживать API-интерфейсы Windows Holographic для разработки приложений. Будущие версии будут поддерживать Только OpenXR для разработки приложений. Независимо от этого мы рекомендуем использовать OpenXR в приложении для разработки всех новых приложений. Существующие приложения, использующие версию 2.9 или более раннюю версию, будут продолжать работать без влияния предстоящих изменений.

Голографические приложения удаленного взаимодействия могут передавать удаленно отрисованное содержимое в HoloLens 2 и Windows Mixed Reality иммерсивные гарнитуры. Вы также можете получить доступ к дополнительным системным ресурсам и интегрировать удаленные иммерсивные представления в существующее программное обеспечение для настольных компьютеров. Удаленное приложение получает поток входных данных из HoloLens 2, отрисовывает содержимое в виртуальном иммерсивном представлении и передает кадры содержимого обратно в HoloLens 2. Подключение осуществляется с помощью стандартной сети Wi-Fi. Голографическое удаленное взаимодействие добавляется в классическое приложение или приложение UWP с помощью пакета NuGet. Требуется дополнительный код, который обрабатывает подключение и отрисовывается в иммерсивном представлении. Обычное удаленное подключение будет иметь низкую задержку в 50 мс. Приложение проигрывателя может сообщать о задержке в режиме реального времени.

Весь код на этой странице и рабочие проекты можно найти в репозитории примеров голографического удаленного взаимодействия GitHub.

Предварительные требования

Хорошей отправной точкой является работающее классическое приложение или приложение UWP на основе DirectX, предназначенное для API Windows Mixed Reality. Дополнительные сведения см. в статье Обзор разработки DirectX. Шаблон голографического проекта C++ является хорошей отправной точкой.

Важно!

Любое приложение, использующе голографическое удаленное взаимодействие, должно быть создано для использования многопотокового подразделения. Использование однопотокового подразделения поддерживается, но приведет к неоптимальной производительности и, возможно, заикание во время воспроизведения. При использовании C++/WinRT winrt::init_apartment по умолчанию используется многопоточное подразделение.

Получение пакета NuGet для голографического удаленного взаимодействия

Чтобы добавить пакет NuGet в проект в Visual Studio, необходимо выполнить следующие действия.

  1. Откройте проект в Visual Studio.
  2. Щелкните правой кнопкой мыши узел проекта и выберите Управление пакетами NuGet...
  3. На появиющейся панели выберите Обзор и выполните поиск по запросу "Голографическое удаленное взаимодействие".
  4. Выберите Microsoft.Holographic.Remoting, выберите последнюю версию 2.x.x и нажмите кнопку Установить.
  5. Если появится диалоговое окно Предварительный просмотр , нажмите кнопку ОК.
  6. Выберите Я принимаю , когда откроется диалоговое окно лицензионного соглашения.

Примечание

Версия 1.x.x пакета NuGet по-прежнему доступна для разработчиков, которые хотят использовать HoloLens 1. Дополнительные сведения см. в разделе Добавление голографического удаленного взаимодействия (HoloLens (1-го поколения)).

Создание удаленного контекста

В качестве первого шага приложение должно создать удаленный контекст.

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

Предупреждение

Голографическое удаленное взаимодействие работает путем замены среды выполнения Windows Mixed Reality, которая является частью Windows, определенной средой удаленного взаимодействия. Это делается во время создания удаленного контекста. По этой причине любой вызов api Windows Mixed Reality перед созданием удаленного контекста может привести к непредвиденному поведению. Рекомендуемый подход — как можно раньше создать удаленный контекст перед взаимодействием с api Смешанная реальность. Никогда не смешивайте объекты, созданные или полученные с помощью API Windows Mixed Reality перед вызовом CreateRemoteContext, с объектами, созданными или извлеченными после этого.

Далее необходимо создать голографическое пространство. Указывать CoreWindow не требуется. Классические приложения, у которых нет CoreWindow, могут просто передать .nullptr

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

Подключение к устройству

Когда удаленное приложение будет готово к просмотру содержимого, можно установить подключение к устройству проигрывателя.

Подключение можно выполнить одним из двух способов.

  1. Удаленное приложение подключается к проигрывателю, работающему на устройстве.
  2. Проигрыватель, работающий на устройстве, подключается к удаленному приложению.

Чтобы установить подключение удаленного приложения к устройству проигрывателя, вызовите Connect метод в удаленном контексте, указав имя узла и порт. Порт, используемый голографическим проигрывателем удаленного взаимодействия, — 8265.

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

Важно!

Как и в случае с любым API Connect C++/WinRT, может вызвать winrt::hresult_error который необходимо обработать.

Совет

Чтобы избежать использования проекции языка C++/WinRT , можно включить файл build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h , расположенный в пакете NuGet для голографического удаленного взаимодействия. Он содержит объявления базовых интерфейсов COM. Однако рекомендуется использовать C++/WinRT.

Прослушивание входящих подключений в удаленном приложении можно выполнить, вызвав Listen метод . Во время этого вызова можно указать как порт подтверждения, так и транспортный порт. Порт подтверждения используется для начального подтверждения. Затем данные отправляются через транспортный порт. По умолчанию используются 8265 и 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());
}

Важно!

В build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl пакете NuGet содержится подробная документация по API, предоставляемому Голографическим удаленным взаимодействием.

Обработка определенных событий удаленного взаимодействия

Удаленный контекст предоставляет три события, которые важны для отслеживания состояния подключения.

  1. OnConnected: активируется при успешном подключении к устройству.
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: активируется, если установленное соединение закрыто или не удается установить подключение.
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: при прослушивании входящих подключений начинается.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Кроме того, состояние подключения можно запросить с помощью ConnectionState свойства в удаленном контексте.

auto connectionState = m_remoteContext.ConnectionState();

Обработка речевых событий

С помощью интерфейса удаленного распознавания речи можно зарегистрировать речевые триггеры с помощью HoloLens 2 и сделать их удаленными для удаленного приложения.

Для отслеживания состояния удаленной речи требуется следующий дополнительный элемент:

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

Сначала получите удаленный речевой интерфейс.

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

Затем с помощью асинхронного вспомогательного метода можно инициализировать удаленную речь. Это следует делать асинхронно, так как инициализация может занять значительное время. В статье Параллелизм и асинхронные операции с C++/WinRT объясняется, как создавать асинхронные функции с помощью 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);
}

Существует два способа указания распознаваемых фраз.

  1. Спецификация в XML-файле грамматики речи. Дополнительные сведения см. в статье Создание базовой грамматики XML .
  2. Укажите, передав их внутри вектора словаря в ApplyParameters.

В обратном вызове OnRecognizedSpeech можно обрабатывать события речи:

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

    ...
}

Локальный просмотр потокового содержимого

Для отображения того же содержимого в удаленном приложении, которое отправляется на устройство, OnSendFrame можно использовать событие удаленного контекста. Событие OnSendFrame активируется каждый раз, когда библиотека голографического удаленного взаимодействия отправляет текущий кадр на удаленное устройство. Это идеальное время, чтобы взять содержимое, а также перерезать его на рабочем столе или в окне 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.
    });

Перепроецирование глубины

Начиная с версии 2.1.0 голографическое удаленное взаимодействие поддерживает перепроектирование глубины. Для этого требуется потоковая передача буфера цвета и буфера глубины из удаленного приложения в HoloLens 2. По умолчанию потоковая передача буфера глубины включена и настроена для использования половины разрешения цветового буфера. Это можно изменить следующим образом:

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

...

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

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

Обратите внимание, что если значения по умолчанию не следует использоватьConfigureDepthVideoStream, необходимо вызвать перед установкой подключения к HoloLens 2. Лучше всего сразу после создания удаленного контекста. Возможные значения для DepthBufferStreamResolution:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Отключено (добавлено в версии 2.1.3 и при использовании не создается дополнительный видеопоток глубины)

Имейте в виду, что использование буфера глубины с полным разрешением также влияет на требования к пропускной способности и должно учитываться в максимальном значении пропускной способности, которое вы предоставляете для CreateRemoteContext.

Помимо настройки разрешения необходимо также зафиксировать буфер глубины с помощью 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);
                }
            });
        }
    });
}

Чтобы проверить, правильно ли работает перепроецирование глубины на HoloLens 2, можно включить визуализатор глубины на портале устройств. Дополнительные сведения см. в разделе Проверка правильной настройки глубины .

Необязательно. Пользовательские каналы данных

Пользовательские каналы данных можно использовать для отправки пользовательских данных через уже установленное удаленное подключение. Дополнительные сведения см. в разделе Пользовательские каналы данных.

Необязательно: синхронизация системы координат

Начиная с версии 2.7.0 синхронизацию системы координат можно использовать для выравнивания пространственных данных между проигрывателем и удаленным приложением. Дополнительные сведения см. в статье Общие сведения о синхронизации системы координат с голографическим удаленным взаимодействием.

См. также: