Como escrever um aplicativo do Holographic Remoting Player personalizado

Se você não estiver familiarizado com a Comunicação Remota Holográfica, talvez queira ler nossa visão geral.

Importante

Este documento descreve a criação de um aplicativo de player personalizado para o HoloLens 2. Os players personalizados escritos para o HoloLens 2 não são compatíveis com aplicativos remotos escritos para o HoloLens 1. Isso implica que ambos os aplicativos devem usar o pacote NuGet versão 2.x.x.

Ao criar um aplicativo player de Comunicação Remota Holográfica personalizado, você pode criar um aplicativo personalizado capaz de exibir exibições imersivas de um computador remoto em seu HoloLens 2. Todo o código nesta página e os projetos de trabalho podem ser encontrados no repositório github de exemplos de Comunicação Remota Holográfica.

Um player de Comunicação Remota Holográfica permite que seu aplicativo exiba conteúdo holográfico renderizado em um computador desktop ou dispositivo UWP, como o Xbox One, com acesso a mais recursos do sistema. Um aplicativo player de Comunicação Remota Holográfica transmite dados de entrada para um aplicativo remoto de Comunicação Remota Holográfica e recebe de volta uma exibição imersiva como fluxo de vídeo e áudio. A conexão é feita usando Wi-Fi padrão. Para criar um aplicativo de player, use um pacote NuGet para adicionar a Comunicação Remota Holográfica ao seu aplicativo UWP. Em seguida, escreva o código para lidar com a conexão e exibir uma exibição imersiva.

Pré-requisitos

Um bom ponto de partida é um aplicativo UWP baseado em DirectX funcional que já tem como destino a API Windows Mixed Reality. Para obter detalhes, consulte Visão geral do desenvolvimento do DirectX. Se você não tiver um aplicativo existente e quiser começar do zero, o modelo de projeto holográfico C++ é um bom ponto de partida.

Importante

Qualquer aplicativo que usa a Comunicação Remota Holográfica deve ser criado para usar um apartamento multithread. Há suporte para o uso de um apartamento de thread único, mas levará a um desempenho abaixo do ideal e, possivelmente, a gagueira durante a reprodução. Ao usar o C++/WinRT winrt::init_apartment um apartamento multithread é o padrão.

Obter o pacote NuGet de Comunicação Remota Holográfica

As etapas a seguir são necessárias para adicionar o pacote NuGet a um projeto no Visual Studio.

  1. Abra o projeto no Visual Studio.
  2. Clique com o botão direito do mouse no nó do projeto e selecione Gerenciar Pacotes NuGet...
  3. No painel exibido, selecione Procurar e pesquise "Comunicação Remota Holográfica".
  4. Selecione Microsoft.Holographic.Remoting, certifique-se de escolher a versão 2.x.x mais recente e selecione Instalar.
  5. Se a caixa de diálogo Visualizar for exibida, selecione OK.
  6. Selecione Aceito quando a caixa de diálogo do contrato de licença for exibida.

Importante

O build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl pacote interno do NuGet contém documentação detalhada para a API exposta pela Comunicação Remota Holográfica.

Modifique o Package.appxmanifest do aplicativo

Para tornar o aplicativo ciente dos Microsoft.Holographic.AppRemoting.dll adicionados pelo pacote NuGet, as seguintes etapas precisam ser executadas no projeto:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no arquivo Package.appxmanifest e selecione Abrir Com...
  2. Selecione Editor XML (Texto) e selecione OK
  3. Adicione as seguintes linhas ao arquivo e salve
  </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>

Criar o contexto do player

Como primeira etapa, o aplicativo deve criar um contexto de player.

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

Aviso

Um player personalizado injeta uma camada intermediária entre o aplicativo player e o Windows Mixed Reality runtime fornecido com o Windows. Isso é feito durante a criação do contexto do jogador. Por esse motivo, qualquer chamada em qualquer API Windows Mixed Reality antes de criar o contexto do player pode resultar em um comportamento inesperado. A abordagem recomendada é criar o contexto do jogador o mais cedo possível antes da interação com qualquer API de Realidade Misturada. Nunca misture objetos criados ou recuperados por meio de qualquer API do Windows Mixed Reality antes da chamada com PlayerContext::Create objetos criados ou recuperados posteriormente.

Em seguida, o HolographicSpace pode ser criado, chamando HolographicSpace.CreateForCoreWindow.

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

Conectar-se ao aplicativo remoto

Depois que o aplicativo player estiver pronto para renderizar conteúdo, uma conexão com o aplicativo remoto poderá ser estabelecida.

A conexão pode ser estabelecida de uma das seguintes maneiras:

  1. O aplicativo player em execução no HoloLens 2 se conecta ao aplicativo remoto.
  2. O aplicativo remoto se conecta ao aplicativo player em execução no HoloLens 2.

Para se conectar do aplicativo player ao aplicativo remoto, chame o Connect método no contexto do player especificando o nome do host e a porta. A porta padrão é 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()
}

Importante

Assim como acontece com qualquer API Connect do C++/WinRT, pode gerar um winrt::hresult_error que precisa ser tratado.

A escuta de conexões de entrada no aplicativo player pode ser feita chamando o Listen método. A porta de handshake e a porta de transporte podem ser especificadas durante essa chamada. A porta de handshake é usada para o handshake inicial. Os dados são então enviados pela porta de transporte. Por padrão, os números de porta 8265 e 8266 são usados.

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()
}

O PlayerContext expõe três eventos para monitorar o estado da conexão

  1. OnConnected: disparado quando uma conexão com o aplicativo remoto foi estabelecida com êxito.
m_onConnectedEventToken = m_playerContext.OnConnected([]() 
{
    // Handle connection successfully established
});
  1. OnDisconnected: disparado se uma conexão estabelecida for encerrada ou uma conexão não puder ser estabelecida.
m_onDisconnectedEventToken = m_playerContext.OnDisconnected([](ConnectionFailureReason failureReason)
{
    switch (failureReason)
    {
        // Handle connection failed or terminated.
        // See ConnectionFailureReason for possible reasons.
    }
}

Observação

Os valores possíveis ConnectionFailureReason estão documentados no Microsoft.Holographic.AppRemoting.idl arquivo.

  1. OnListening: Quando a escuta de conexões de entrada é iniciada.
m_onListeningEventToken = m_playerContext.OnListening([]()
{
    // Handle start listening for incoming connections
});

Além disso, o estado da conexão pode ser consultado usando a ConnectionState propriedade no contexto do player.

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

Exibir o quadro renderizado remotamente

Para exibir o conteúdo renderizado remotamente, chame PlayerContext::BlitRemoteFrame durante a renderização de um HolographicFrame.

BlitRemoteFrame requer que o buffer traseiro do HolographicFrame atual esteja associado como destino de renderização. O buffer traseiro pode ser recebido do HolographicCameraRenderingParameters por meio da propriedade Direct3D11BackBuffer .

Quando chamado, BlitRemoteFrame copia o quadro recebido mais recente do aplicativo remoto para o BackBuffer do HolographicFrame. Além disso, o conjunto de pontos de foco é definido, se o aplicativo remoto tiver especificado um ponto de foco durante a renderização do quadro remoto.

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

Observação

PlayerContext::BlitRemoteFrame potencialmente substitui o ponto de foco do quadro atual.

Em caso de sucesso, BlitRemoteFrame retorna BlitResult::Success_Color. Caso contrário, ele retornará o motivo da falha:

  • BlitResult::Failed_NoRemoteFrameAvailable: Falha porque nenhum quadro remoto está disponível.
  • BlitResult::Failed_NoCamera: Falha porque não há câmera presente.
  • BlitResult::Failed_RemoteFrameTooOld: Falha porque o quadro remoto é muito antigo (consulte a propriedade PlayerContext::BlitRemoteFrameTimeout).

Importante

A partir da versão 2.1.0 , é possível usar a reprojeção de profundidade por meio da Comunicação Remota Holográfica com um player personalizado.

BlitResult também pode retornar BlitResult::Success_Color_Depth nas seguintes condições:

Se essas condições forem atendidas, BlitRemoteFrame a profundidade remota será lançada no buffer de profundidade local atualmente vinculado. Em seguida, você pode renderizar conteúdo local adicional, que terá interseção de profundidade com o conteúdo renderizado remotamente. Além disso, você pode confirmar o buffer de profundidade local por meio de HolographicCameraRenderingParameters.CommitDirect3D11DepthBuffer em seu player personalizado para ter reprojeção de profundidade para conteúdo renderizado remoto e local.

Modo de transformação de projeção

Um problema, que surge ao usar a reprojeção de profundidade por meio da Comunicação Remota Holográfica, é que o conteúdo remoto pode ser renderizado com uma transformação de projeção diferente do conteúdo local renderizado diretamente pelo aplicativo player personalizado. Um caso de uso comum é especificar valores diferentes para o plano próximo e distante (por meio de HolographicCamera::SetNearPlaneDistance e HolographicCamera::SetFarPlaneDistance) no lado do jogador e no lado remoto. Nesse caso, não está claro se a transformação de projeção no lado do jogador deve refletir as distâncias remotas do plano próximo/distante ou as locais.

A partir da versão 2.1.0 , você pode controlar o modo de transformação de projeção via PlayerContext::ProjectionTransformConfig. Os valores com suporte são:

  • Local - HolographicCameraPose::P rojectionTransform retorna uma transformação de projeção, que reflete as distâncias de plano próximo/distante definidas pelo aplicativo de player personalizado no HolographicCamera.
  • Remote - A transformação de projeção reflete as distâncias de plano próximo/distante especificadas pelo aplicativo remoto.
  • Merged - As distâncias de plano próximo/distante do aplicativo remoto e do aplicativo de player personalizado são mescladas. Por padrão, isso é feito tomando o mínimo das distâncias do plano próximo e o máximo das distâncias do plano distante. Caso o lado remoto ou local esteja invertido, digamos muito < próximo, as distâncias remotas do plano próximo/distante são invertidas.

Opcional: Definir BlitRemoteFrameTimeout

Importante

PlayerContext::BlitRemoteFrameTimeout é suportado a partir da versão 2.0.9.

A PlayerContext::BlitRemoteFrameTimeout propriedade especifica a quantidade de tempo que um quadro remoto é reutilizado se nenhum novo quadro remoto for recebido.

Um caso de uso comum é habilitar o tempo limite de BlitRemoteFrame para exibir uma tela em branco se nenhum novo quadro for recebido por um determinado período de tempo. Quando habilitado, o tipo de retorno do BlitRemoteFrame método também pode ser usado para alternar para um conteúdo de fallback renderizado localmente.

Para habilitar o tempo limite, defina o valor da propriedade como uma duração igual ou superior a 100 ms. Para desabilitar o tempo limite, defina a propriedade como duração zero. Se o tempo limite estiver habilitado e nenhum quadro remoto for recebido pela duração definida, BlitRemoteFrame falhará e retornará Failed_RemoteFrameTooOld até que um novo quadro remoto seja recebido.

using namespace std::chrono_literals;

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

Opcional: Obter estatísticas sobre o último quadro remoto

Para diagnosticar problemas de desempenho ou rede, as estatísticas sobre o último quadro remoto podem ser recuperadas por meio da PlayerContext::LastFrameStatistics propriedade. As estatísticas são atualizadas durante a chamada para HolographicFrame::P resentUsingCurrentPrediction.

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

Para obter mais informações, consulte a PlayerFrameStatistics Microsoft.Holographic.AppRemoting.idl documentação no arquivo.

Opcional: Canais de dados personalizados

Os canais de dados personalizados podem ser usados para enviar dados do usuário pela conexão remota já estabelecida. Para mais informações, consulte Canais de dados personalizados.

Opcional: Renderização excessiva

A Comunicação Remota Holográfica prevê onde a cabeça do usuário estará no momento em que as imagens renderizadas aparecerem nas exibições. No entanto, essa previsão é uma aproximação. Portanto, o visor previsto no aplicativo remoto e o visor real posterior no aplicativo do player podem ser diferentes. Desvios mais fortes (por exemplo, devido a movimentos imprevisíveis) podem causar regiões pretas nas bordas do tronco de visualização. A partir da versão 2.6.0 , você pode usar a renderização excessiva para reduzir as regiões pretas e melhorar a qualidade visual aumentando artificialmente a janela de visualização além do tronco de visualização.

A renderização excessiva pode ser ativada por meio de PlayerContext::ConfigureOverRendering.

O OverRenderingConfig especifica um aumento de tamanho fracionário para a viewport real, de modo que a viewport prevista se torne maior e ocorra menos corte. Com um tamanho de visor aumentado, a densidade de pixels diminui, portanto, o OverRenderingConfig também permite aumentar a resolução. Se o aumento da janela de visualização for igual ao aumento da resolução, a densidade de pixels permanecerá a mesma. OverRenderingConfig é definido como:

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).
};

Opcional: Sincronização do Sistema de Coordenadas

A partir da versão 2.7.0 , a sincronização do sistema de coordenadas pode ser usada para alinhar dados espaciais entre o player e o aplicativo remoto. Para obter mais informações, consulte Sincronização do sistema de coordenadas com visão geral da comunicação remota holográfica.

Confira também