HoloLens(第 1 代)的全息远程处理

如果你是全息远程处理新手,建议阅读概述

重要

本文档介绍如何创建适用于 HoloLens 1 的主机应用程序。 适用于 “HoloLens(第一代)”的主机应用程序必须使用 NuGet 包版本 “1.xx”。这意味着,为 HoloLens 1 编写的主机应用程序与 HoloLens 2 不兼容,反之亦然。

HoloLens 2

使用全息远程处理的 HoloLens 开发人员需要更新其应用,使其与 HoloLens 2 兼容。 这需要新版全息远程处理 NuGet 包。 在连接到 HoloLens 2 上的全息远程处理播放器时,请确保使用 2.0.0.0 或更高版本的全息远程处理 NuGet 包,否则连接会失败。 否则,连接将失败。

注意

可在此处找到特定于 HoloLens 2 的指南。

将全息远程处理添加到桌面或 UWP 应用

本页介绍如何向桌面或 UWP 应用添加全息远程处理。

使用全息远程处理,你的应用可以将其全息内容托管在台式电脑或 UWP 设备(例如 Xbox One)上的 HoloLens 作为目标。 你还可以访问更多系统资源,因此可以将远程沉浸式视图集成到现有的台式电脑软件中。 远程处理主机应用从 HoloLens 接收输入数据流,在虚拟沉浸式视图中渲染内容,然后将内容帧流式传输回 HoloLens。 使用标准 Wi-Fi 建立连接。 若要使用远程处理,请使用 NuGet 包将全息远程处理添加到桌面或 UWP 应用,然后编写代码来处理连接并渲染沉浸式视图。 帮助程序库包括在代码示例中,用于简化处理设备连接的任务。

典型的远程连接会有低至 50 毫秒的延迟。 播放器应用可以实时报告延迟。

注意

本文中的代码片段当前演示了如何使用 C++/CX,而不是 C++17 兼容的 C++/WinRT,后者在 C++ 全息项目模板中使用。 C++/WinRT 项目的这些概念是等效的,但你需要进行代码转换。

获取远程处理 NuGet 包

请按以下步骤获取用于全息远程处理的 NuGet 包,并从项目中添加引用:

  1. 转到 Visual Studio 中的项目。
  2. 右键单击项目节点,然后选择“管理 NuGet 包...”
  3. 在出现的面板中选择“浏览”,然后搜索“全息远程处理”
  4. 选择“Microsoft.Holographic.Remoting”,然后选择“安装”
  5. 如果出现了“预览”对话框,请选择“确定”
  6. 出现许可协议对话框时,请选择“我接受”

创建 HolographicStreamerHelpers

首先,需要将 HolographicStreamerHelpers 的实例添加到负责远程处理的类。

#include <HolographicStreamerHelpers.h>

   private:
       Microsoft::Holographic::HolographicStreamerHelpers^ m_streamerHelpers;

还需要跟踪连接状态。 如果要渲染此预览,则需要有一个可将它复制到其中的纹理。 还需要一些功能(如连接状态锁)、存储 HoloLens IP 地址的某种方法,等等。

private:
       Microsoft::Holographic::HolographicStreamerHelpers^ m_streamerHelpers;

       Microsoft::WRL::Wrappers::SRWLock                   m_connectionStateLock;

       RemotingHostSample::AppView^                        m_appView;
       Platform::String^                                   m_ipAddress;
       Microsoft::Holographic::HolographicStreamerHelpers^ m_streamerHelpers;

       Microsoft::WRL::Wrappers::CriticalSection           m_deviceLock;
       Microsoft::WRL::ComPtr<IDXGISwapChain1>             m_swapChain;
       Microsoft::WRL::ComPtr<ID3D11Texture2D>             m_spTexture;

初始化 HolographicStreamerHelpers 并连接到 HoloLens

若要连接到 HoloLens 设备,请创建 HolographicStreamerHelpers 的实例并连接到目标 IP 地址。 需要将视频帧大小设置为与 HoloLens 显示器宽度和高度匹配,因为全息远程处理库要求编码器和解码器的分辨率完全匹配。

m_streamerHelpers = ref new HolographicStreamerHelpers();
       m_streamerHelpers->CreateStreamer(m_d3dDevice);

       // We currently need to stream at 720p because that's the resolution of our remote display.
       // There is a check in the holographic streamer that makes sure the remote and local
       // resolutions match. The default streamer resolution is 1080p.
       m_streamerHelpers->SetVideoFrameSize(1280, 720);

       try
       {
           m_streamerHelpers->Connect(m_ipAddress->Data(), 8001);
       }
       catch (Platform::Exception^ ex)
       {
           DebugLog(L"Connect failed with hr = 0x%08X", ex->HResult);
       }

设备连接是异步的。 应用需要提供针对连接事件、断开连接事件和帧发送事件的事件处理程序。

OnConnected 事件可以更新 UI,启动渲染,等等。 在我们的桌面代码示例中,我们使用“已连接”消息来更新窗口标题。

m_streamerHelpers->OnConnected += ref new ConnectedEvent(
           [this]()
           {
               UpdateWindowTitle();
           });

OnDisconnected 事件可以负责重新连接、UI 更新等。 在此示例中,如果发生暂时性故障,我们会重新连接。

Platform::WeakReference streamerHelpersWeakRef = Platform::WeakReference(m_streamerHelpers);
       m_streamerHelpers->OnDisconnected += ref new DisconnectedEvent(
           [this, streamerHelpersWeakRef](_In_ HolographicStreamerConnectionFailureReason failureReason)
           {
               DebugLog(L"Disconnected with reason %d", failureReason);
               UpdateWindowTitle();

               // Reconnect if this is a transient failure.
               if (failureReason == HolographicStreamerConnectionFailureReason::Unreachable ||
                   failureReason == HolographicStreamerConnectionFailureReason::ConnectionLost)
               {
                   DebugLog(L"Reconnecting...");

                   try
                   {
                       auto helpersResolved = streamerHelpersWeakRef.Resolve<HolographicStreamerHelpers>();
                       if (helpersResolved)
                       {
                           helpersResolved->Connect(m_ipAddress->Data(), 8001);
                       }
                       else
                       {
                           DebugLog(L"Failed to reconnect because a disconnect has already occurred.\n");
                       }
                   }
                   catch (Platform::Exception^ ex)
                   {
                       DebugLog(L"Connect failed with hr = 0x%08X", ex->HResult);
                   }
               }
               else
               {
                   DebugLog(L"Disconnected with unrecoverable error, not attempting to reconnect.");
               }
           });

当远程处理组件准备好发送某个帧时,应用将有机会在 SendFrameEvent 中创建该帧的副本。 在这里,我们将帧复制到一个交换链,这样就可以在预览窗口中显示它。

m_streamerHelpers->OnSendFrame += ref new SendFrameEvent(
           [this](_In_ const ComPtr<ID3D11Texture2D>& spTexture, _In_ FrameMetadata metadata)
           {
               if (m_showPreview)
               {
                   ComPtr<ID3D11Device1> spDevice = m_appView->GetDeviceResources()->GetD3DDevice();
                   ComPtr<ID3D11DeviceContext> spContext = m_appView->GetDeviceResources()->GetD3DDeviceContext();

                   ComPtr<ID3D11Texture2D> spBackBuffer;
                   ThrowIfFailed(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&spBackBuffer)));

                   spContext->CopySubresourceRegion(
                       spBackBuffer.Get(), // dest
                       0,                  // dest subresource
                       0, 0, 0,            // dest x, y, z
                       spTexture.Get(),    // source
                       0,                  // source subresource
                       nullptr);           // source box, null means the entire resource

                   DXGI_PRESENT_PARAMETERS parameters = { 0 };
                   ThrowIfFailed(m_swapChain->Present1(1, 0, &parameters));
               }
           });

渲染全息内容

若要使用远程处理来渲染内容,请在桌面或 UWP 应用中设置一个虚拟 IFrameworkView,并处理远程处理中的全息帧。 此视图通过相同的方式来使用所有 Windows 全息 API,但其设置方式略有不同。

全息空间和语音组件来自 HolographicRemotingHelpers 类,而不是你自行创建的:

m_appView->Initialize(m_streamerHelpers->HolographicSpace, m_streamerHelpers->RemoteSpeech);

你不在 Run 方法中使用更新循环,而是通过桌面或 UWP 应用的主循环来提供时钟周期更新。 这样一来,桌面或 UWP 应用就可以保持对消息处理的控制。

void DesktopWindow::Tick()
   {
       auto lock = m_deviceLock.Lock();
       m_appView->Tick();

       // display preview, etc.
   }

全息应用视图的 Tick () 方法可完成对更新、绘图、呈现循环的一次迭代。

void AppView::Tick()
   {
       if (m_main)
       {
           // When running on Windows Holographic, we can use the holographic rendering system.
           HolographicFrame^ holographicFrame = m_main->Update();

           if (holographicFrame && m_main->Render(holographicFrame))
           {
               // The holographic frame has an API that presents the swap chain for each
               // holographic camera.
               m_deviceResources->Present(holographicFrame);
           }
       }
   }

全息应用视图的更新、渲染和呈现循环与在 HoloLens 上运行时的效果完全相同,唯一区别是你有权访问台式电脑上数量多得多的系统资源。 你可以渲染更多的三角形,可以有更多的绘图轮次,可以实现更多的物理效果,并且可以使用 x64 进程加载那些需要 2 GB 以上 RAM 的内容。

断开并结束远程会话

若要断开连接(例如,用户单击某个 UI 按钮来断开连接),请在 HolographicStreamerHelpers 上调用 Disconnect(),然后释放该对象。

void DesktopWindow::DisconnectFromRemoteDevice()
   {
       // Disconnecting from the remote device can change the connection state.
       auto exclusiveLock = m_connectionStateLock.LockExclusive();

       if (m_streamerHelpers != nullptr)
       {
           m_streamerHelpers->Disconnect();

           // Reset state
           m_streamerHelpers = nullptr;
       }
   }

获取远程处理播放器

Windows 全息远程处理播放器在 Windows 应用商店中提供,作为一个供远程处理主机应用进行连接的终结点。 若要获取 Windows 全息远程处理播放器,请从 HoloLens 访问 Windows 应用商店,搜索“远程处理”,然后下载相应的应用。 远程处理播放器包含一项在屏幕上显示统计信息的功能,该功能在调试远程处理主机应用时可能很有用。

注释和资源

全息应用视图需要一种方法来为应用提供 Direct3D 设备,必须使用此设备来初始化全息空间。 应用应使用此 Direct3D 设备来复制和显示预览帧。

internal:
       const std::shared_ptr<DX::DeviceResources>& GetDeviceResources()
       {
           return m_deviceResources;
       }

“代码示例:”提供完整的全息远程处理代码示例,其中包含一个全息应用程序视图,该视图与用于桌面 Win32、UWP DirectX 和具有 XAML 的 UWP 的远程处理和远程处理主机项目兼容。

“调试说明:”全息远程处理库可能会引发第一次出现的异常。 这些异常可能会在调试会话中显示,具体取决于目前生效的 Visual Studio 异常设置。 这些异常由全息远程处理库在内部捕获,可以忽略。

另请参阅