编写自定义全息远程处理播放器应用

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

重要

本文档介绍如何创建适用于 HoloLens 2 的自定义播放器应用程序。 为 HoloLens 2 编写的自定义播放器与为 HoloLens 1 编写的远程应用程序不兼容。 这意味着两个应用程序必须使用 NuGet 包版本 2.x.x

通过创建自定义全息远程处理播放器应用,可以创建能够将远程计算机上的沉浸式视图显示在 HoloLens 2 上的自定义应用程序。 本页上的所有代码和工作项目可以在全息远程处理示例 github 存储库中找到。

使用全息远程处理播放器,你的应用可以将台式电脑或 UWP 设备(如具有访问权限的 Xbox One)上呈现的全息内容显示到更多系统资源。 全息远程处理播放器应用将输入数据流式传输到“全息远程处理”远程应用程序,并收回沉浸式视图作为视频和音频流。 使用标准 Wi-Fi 建立连接。 若要创建播放器应用,请使用 NuGet 包将“全息远程处理”添加到 UWP 应用。 然后编写代码来处理连接并显示沉浸式视图。

先决条件

一个很好的起点是已面向 Windows Mixed Reality API 的基于 DirectX 的 UWP 应用。 有关详细信息,请参阅 DirectX 开发概述。 如果没有现有应用,并且想要从头开始,则 C++ 全息项目模板是一个很好的起点。

重要

应将任何使用全息远程处理的应用编写成使用多线程单元。 支持使用单线程单元,但这会导致性能欠佳,并可能在播放过程中出现卡顿。 使用 C++/WinRT winrt::init_apartment 时,多线程单元是默认设置。

获取全息远程处理 NuGet 包

若要在 Visual Studio 中将 NuGet 包添加到某个项目,需要执行以下步骤。

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

重要

NuGet 包中的 build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl 包含全息远程处理公开的 API 的详细文档。

修改应用程序的 Package.appxmanifest

若要使应用程序知道 NuGet 包添加的 Microsoft.Holographic.AppRemoting.dll,需要对项目执行以下步骤:

  1. 在解决方案资源管理器中,右键单击“Package.appxmanifest”文件并选择“打开方式”
  2. 依次选择“XML (文本)编辑器”、“确定”
  3. 将以下行添加到文件并保存
  </Capabilities>

  <!--Add lines below -->
  <Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>Microsoft.Holographic.AppRemoting.dll</Path>
        <ActivatableClass ActivatableClassId="Microsoft.Holographic.AppRemoting.PlayerContext" ThreadingModel="both" />
      </InProcessServer>
    </Extension>
  </Extensions>
  <!--Add lines above -->

</Package>

创建播放器上下文

首先,应用程序应创建一个播放器上下文。

// class declaration:

#include <winrt/Microsoft.Holographic.AppRemoting.h>

...

private:
// PlayerContext used to connect with a Holographic Remoting remote app and display remotely rendered frames
winrt::Microsoft::Holographic::AppRemoting::PlayerContext m_playerContext = nullptr;
// class implementation:

// Create the player context
// IMPORTANT: This must be done before creating the HolographicSpace (or any other call to the Holographic API).
m_playerContext = winrt::Microsoft::Holographic::AppRemoting::PlayerContext::Create();

警告

自定义播放器在播放器应用与 Windows 随附的 Windows Mixed Reality 运行时之间注入中间层。 此操作是在创建播放器上下文期间完成的。 因此,在创建播放器上下文之前对任何 Windows Mixed Reality API 进行任何调用都可能导致意外行为。 建议的方法是在与任何混合现实 API 交互之前尽早创建播放器上下文。 切勿将在调用 PlayerContext::Create 之前通过任何 Windows Mixed Reality API 创建或检索的对象与之后创建或检索的对象混合使用。

接下来,可以通过调用 HolographicSpace.CreateForCoreWindow 来创建 HolographicSpace。

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

连接到远程应用

当播放器应用准备好呈现内容时,可与远程应用建立连接。

可以通过以下方式之一建立连接:

  1. 在 HoloLens 2 上运行的播放器应用连接到远程应用。
  2. 远程应用连接到 HoloLens 2 上运行的播放器应用。

若要从播放器应用连接到远程应用,请在指定主机名和端口的播放器上下文中调用 Connect 方法。 默认端口是 8265

try
{
    m_playerContext.Connect(m_hostname, m_port);
}
catch(winrt::hresult_error& e)
{
    // Failed to connect. Get an error details via e.code() and e.message()
}

重要

与任何 C++/WinRT API 一样,Connect 可能会引发需要处理的 winrt::hresult_error。

可以通过调用 Listen 方法来侦听播放器应用上的传入连接。 在进行这种调用期间,可以指定握手端口和传输端口。 握手端口用于初始握手。 然后通过传输端口发送数据。 默认情况下,使用端口号 8265 和 8266

try
{
    m_playerContext.Listen(L"0.0.0.0", m_port, m_port + 1);
}
catch(winrt::hresult_error& e)
{
    // Failed to listen. Get an error details via e.code() and e.message()
}

PlayerContext 公开三个事件以监视连接状态

  1. OnConnected:与远程应用成功建立连接时触发。
m_onConnectedEventToken = m_playerContext.OnConnected([]() 
{
    // Handle connection successfully established
});
  1. OnDisconnected:建立的连接终止或无法建立连接时触发。
m_onDisconnectedEventToken = m_playerContext.OnDisconnected([](ConnectionFailureReason failureReason)
{
    switch (failureReason)
    {
        // Handle connection failed or terminated.
        // See ConnectionFailureReason for possible reasons.
    }
}

注意

可能的 ConnectionFailureReason 值记录在Microsoft.Holographic.AppRemoting.idl文件中。

  1. OnListening:开始侦听传入连接时触发。
m_onListeningEventToken = m_playerContext.OnListening([]()
{
    // Handle start listening for incoming connections
});

此外,可以使用播放器上下文中的 ConnectionState 属性查询连接状态。

winrt::Microsoft::Holographic::AppRemoting::ConnectionState state = m_playerContext.ConnectionState();

显示远程呈现的帧

若要显示远程呈现的内容,请在呈现 HolographicFrame 的同时调用 PlayerContext::BlitRemoteFrame

BlitRemoteFrame 要求当前 HolographicFrame 的后台缓冲区绑定为呈现器目标。 可以通过 Direct3D11BackBuffer 属性从 HolographicCameraRenderingParameters 接收后台缓冲区。

调用时,BlitRemoteFrame 将远程应用程序中收到的最新帧复制到 HolographicFrame 的后台缓冲区中。 此外,如果远程应用程序在呈现远程帧期间指定了焦点,则设置了焦点集。

// Blit the remote frame into the backbuffer for the HolographicFrame.
winrt::Microsoft::Holographic::AppRemoting::BlitResult result = m_playerContext.BlitRemoteFrame();

注意

PlayerContext::BlitRemoteFrame 可能会覆盖当前帧的焦点。

成功后,BlitRemoteFrame 将返回 BlitResult::Success_Color。 否则,它将返回失败原因:

  • BlitResult::Failed_NoRemoteFrameAvailable:因没有可用的远程帧而失败。
  • BlitResult::Failed_NoCamera:因没有相机而失败。
  • BlitResult::Failed_RemoteFrameTooOld:因远程帧太旧而失败(请参见 PlayerContext::BlitRemoteFrameTimeout 属性)。

重要

从版本 2.1.0 开始,自定义播放器可以通过全息远程处理使用深度重新投影。

在以下情况下,BlitResult 还可以返回 BlitResult::Success_Color_Depth

如果满足这些条件,BlitRemoteFrame 会将远程深度传送到当前绑定的本地深度缓冲区中。 然后,你可以呈现其他本地内容,这将与远程呈现的内容深度相交。 此外,可以通过自定义播放器中的 HolographicCameraRenderingParameters.CommitDirect3D11DepthBuffer 提交本地深度缓冲区,以对远程和本地呈现的内容进行深度重新投影。

投影转换模式

通过全息远程处理使用深度重新投影时出现的一个问题是,可以使用与自定义播放器应用直接呈现的本地内容不同的投影转换来呈现远程内容。 常见用例是在播放器端和远程端指定近面和远面的不同值(通过 HolographicCamera::SetNearPlaneDistanceHolographicCamera::SetFarPlaneDistance)。 在这种情况下,并不清楚播放器端的投影转换是应反映远程近面/远面距离还是本地距离。

从版本 2.1.0 开始,可以通过 PlayerContext::ProjectionTransformConfig 控制投影转换模式。 支持的值是:

  • Local - HolographicCameraPose::ProjectionTransform 返回投影转换,这将反映 HolographicCamera 上自定义播放器应用设置的近面/远面距离。
  • Remote - 投影转换反映远程应用指定的近面/远面距离。
  • Merged - 合并远程应用和自定义播放器应用中的近面/远面距离。 默认情况下,这是通过使用最小近面距离和最大远面距离来完成的。 如果远程端或本地端反转(例如远面 < 近面),则会翻转远程近面/远面距离。

可选:设置 BlitRemoteFrameTimeout

重要

从版本 2.0.9 开始支持 PlayerContext::BlitRemoteFrameTimeout

如果未收到新的远程帧,则 PlayerContext::BlitRemoteFrameTimeout 属性指定重复使用远程帧的时间长度。

常见用例是,如果在一段时间内没有收到新帧,则启用 BlitRemoteFrame 超时以显示空白屏幕。 启用时,BlitRemoteFrame 方法的返回类型还可用于切换到本地呈现的回退内容。

若要启用超时,请将属性值设置为等于或大于 100 毫秒的持续时间。 若要禁用超时,请将属性设置为零持续时间。 如果在设置的持续时间内启用了超时且未收到任何远程帧,则 BlitRemoteFrame 将失败并返回 Failed_RemoteFrameTooOld,直到接收到新的远程帧。

using namespace std::chrono_literals;

// Set the BlitRemoteFrame timeout to 0.5s
m_playerContext.BlitRemoteFrameTimeout(500ms);

可选:获取有关最后一个远程帧的统计信息

若要诊断性能或网络问题,可以通过 PlayerContext::LastFrameStatistics 属性检索有关最后一个远程帧的统计信息。 在调用 HolographicFrame::PresentUsingCurrentPrediction 期间更新统计信息。

// Get statistics for the last presented frame.
winrt::Microsoft::Holographic::AppRemoting::PlayerFrameStatistics statistics = m_playerContext.LastFrameStatistics();

有关详细信息,请参阅Microsoft.Holographic.AppRemoting.idl文件中的 PlayerFrameStatistics 文档。

可选:自定义数据通道

使用自定义数据通道可以通过已建立的远程连接发送用户数据。 有关详细信息,请参阅自定义数据通道

可选:过度呈现

全息远程处理预测显示器显示呈现的图像时用户头部的位置。 但是,此预测采取近似值。 因此,远程应用上的预测视区和播放器应用上的实际视区可能会有所不同。 更强的偏差(例如,由于无法预测的运动)可能导致在视锥的边框上出现黑色区域。 从版本 2.6.0 开始,可以使用过度呈现来减少黑色区域,并通过在视锥之外人为地增加视区来提高视觉质量。

可以通过 PlayerContext::ConfigureOverRendering 启用过度呈现。

OverRenderingConfig 指定对实际视区的小数大小增加,使预测视区变得更大且更少切割。 视区大小增加,像素密度就会下降,因此,通过 OverRenderingConfig 还会增加分辨率。 如果视区增加等于分辨率增加,则像素密度保持不变。 OverRenderingConfig 定义为:

struct OverRenderingConfig
{
    float HorizontalViewportIncrease; // The fractional horizontal viewport increase. (e.g. 10% -> 0.1).
    float VerticalViewportIncrease; // The fractional vertical viewport increase. (e.g. 10% -> 0.1).
                
    float HorizontalResolutionIncrease; // The fractional horizontal resolution increase. (e.g. 10% -> 0.1).
    float VerticalResolutionIncrease; // The fractional vertical resolution increase. (e.g. 10% -> 0.1).
};

可选:坐标系统同步

从版本 2.7.0 开始,坐标系统同步可用于对齐播放器与远程应用之间的空间数据。 有关详细信息,请参阅 Holographic Remoting 概述中的“坐标系同步”

另请参阅