Escrever um aplicativo Remoto de Comunicação Remota Holográfica usando a API OpenXR

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 remoto para headsets HoloLens 2 e Windows Mixed Reality usando a API OpenXR. Aplicativos remotos para HoloLens (1ª geração) devem usar o pacote NuGet versão 1.x.x. Isso implica que aplicativos remotos gravados para HoloLens 2 não são compatíveis com o HoloLens 1 e vice-versa. A documentação do HoloLens 1 pode ser encontrada aqui.

Os aplicativos de Comunicação Remota Holográfica podem transmitir conteúdo renderizado remotamente para HoloLens 2 e Windows Mixed Reality headsets imersivos. Você também pode acessar mais recursos do sistema e integrar exibições imersivas remotas ao software de computador desktop existente. Um aplicativo remoto recebe um fluxo de dados de entrada de HoloLens 2, renderiza o conteúdo em uma exibição imersiva virtual e transmite quadros de conteúdo de volta para HoloLens 2. A conexão é feita usando Wi-Fi padrão. A Comunicação Remota Holográfica é adicionada a um aplicativo da área de trabalho ou UWP por meio de um pacote NuGet. É necessário código adicional que manipula a conexão e renderiza em uma exibição imersiva. Uma conexão remota típica terá até 50 ms de latência. O aplicativo player pode relatar a latência em tempo real.

Todos os códigos nesta página e projetos de trabalho podem ser encontrados no repositório github de exemplos de Comunicação Remota Holográfica.

Pré-requisitos

Um bom ponto de partida é um aplicativo UWP ou área de trabalho baseado em OpenXR. Para obter detalhes , consulte Introdução ao OpenXR.

Importante

Qualquer aplicativo que use a Comunicação Remota Holográfica deve ser criado para usar um apartment de vários threads. Há suporte para o uso de um apartamento de thread único , mas levará a um desempenho abaixo do ideal e possivelmente gaguejar durante a reprodução. Ao usar C++/WinRT winrt::init_apartment um apartamento multi-threaded é 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, em seguida, pesquise "Comunicação Remota Holográfica".
  4. Selecione Microsoft.Holographic.Remoting.OpenXr e verifique se a versão mais recente do 2.x.x está selecionada 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.
  7. Repita as etapas de 3 a 6 para os seguintes Pacotes NuGet: OpenXR.Headers, OpenXR.Loader

Observação

A versão 1.x.x do pacote NuGet ainda está disponível para desenvolvedores que desejam direcionar o HoloLens 1. Para obter detalhes, consulte Adicionar Comunicação Remota Holográfica (HoloLens (1ª geração)).

Selecione o runtime do OpenXR de Comunicação Remota Holográfica

A primeira etapa que você precisa fazer em seu aplicativo remoto é selecionar o runtime do OpenXR de Comunicação Remota Holográfica, que faz parte do pacote NuGet Microsoft.Holographic.Remoting.OpenXr. Você pode fazer isso definindo a XR_RUNTIME_JSON variável de ambiente para o caminho do arquivo RemotingXR.json em seu aplicativo. Essa variável de ambiente é usada pelo carregador OpenXR para não usar o runtime padrão do Sistema OpenXR, mas redirecionar para o runtime do OpenXR de Comunicação Remota Holográfica. Ao usar o pacote NuGet Microsoft.Holographic.Remoting.OpenXr, o arquivo RemotingXR.json é automaticamente copiado durante a compilação para a pasta de saída, a seleção de runtime do OpenXR normalmente tem a seguinte aparência.

bool EnableRemotingXR() {
    wchar_t executablePath[MAX_PATH];
    if (GetModuleFileNameW(NULL, executablePath, ARRAYSIZE(executablePath)) == 0) {
        return false;
    }
    
    std::filesystem::path filename(executablePath);
    filename = filename.replace_filename("RemotingXR.json");

    if (std::filesystem::exists(filename)) {
        SetEnvironmentVariableW(L"XR_RUNTIME_JSON", filename.c_str());
            return true;
        }

    return false;
}

Criar XrInstance com a extensão de comunicação remota holográfica

As primeiras ações que um aplicativo OpenXR típico deve executar são selecionar extensões OpenXR e criar um XrInstance. A especificação principal do OpenXR não fornece nenhuma API específica de comunicação remota. Por esse motivo, a Comunicação Remota Holográfica apresenta sua própria extensão OpenXR chamada XR_MSFT_holographic_remoting. XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME Verifique se está incluído no XrInstanceCreateInfo da chamada xrCreateInstance.

Dica

Por padrão, o conteúdo renderizado do aplicativo só é transmitido para o player de Comunicação Remota Holográfica em execução em um HoloLens 2 ou em um Windows Mixed Reality headsets. Para também exibir o conteúdo renderizado no computador remoto, por meio de uma cadeia de troca de uma janela, por exemplo, a Comunicação Remota Holográfica fornece uma segunda extensão OpenXR chamada XR_MSFT_holographic_remoting_frame_mirroring. Certifique-se de também habilitar essa extensão usando XR_MSFT_HOLOGRAPHIC_REMOTING_FRAME_MIRRORING_EXTENSION_NAME caso você deseje usar essa funcionalidade.

Importante

Para saber mais sobre a API de extensão OpenXR de Comunicação Remota Holográfica, marcar a especificação que pode ser encontrada no repositório github de exemplos de Comunicação Remota Holográfica.

Conectar-se ao dispositivo

Depois que o aplicativo remoto tiver criado o XrInstance e consultado o XrSystemId via xrGetSystem, uma conexão com o dispositivo player poderá ser estabelecida.

Aviso

O runtime do OpenXR de Comunicação Remota Holográfica só é capaz de fornecer dados específicos do dispositivo, como configurações de exibição ou modos de combinação de ambiente após a criação de uma conexão. xrEnumerateViewConfigurations, xrEnumerateViewConfigurationViews, xrGetViewConfigurationProperties, xrEnumerateEnvironmentBlendModese xrGetSystemProperties fornecerão valores padrão, correspondendo ao que você normalmente obteria se você se conectasse a um jogador em execução em um HoloLens 2, antes de estar totalmente conectado. É altamente recomendável não chamar esses métodos antes que uma conexão seja estabelecida. A sugestão é usada nesses métodos depois que o XrSession foi criado com êxito e o estado da sessão é pelo menos XR_SESSION_STATE_READY.

Propriedades gerais como taxa máxima de bits, áudio habilitado, codec de vídeo ou resolução de fluxo de buffer de profundidade podem ser configuradas por meio xrRemotingSetContextPropertiesMSFT da seguinte maneira.

XrRemotingRemoteContextPropertiesMSFT contextProperties;
contextProperties = XrRemotingRemoteContextPropertiesMSFT{static_cast<XrStructureType>(XR_TYPE_REMOTING_REMOTE_CONTEXT_PROPERTIES_MSFT)};
contextProperties.enableAudio = false;
contextProperties.maxBitrateKbps = 20000;
contextProperties.videoCodec = XR_REMOTING_VIDEO_CODEC_H265_MSFT;
contextProperties.depthBufferStreamResolution = XR_REMOTING_DEPTH_BUFFER_STREAM_RESOLUTION_HALF_MSFT;
xrRemotingSetContextPropertiesMSFT(m_instance.Get(), m_systemId, &contextProperties);

A conexão pode ser feita de duas maneiras.

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

Para estabelecer uma conexão do aplicativo remoto com o dispositivo player, chame o xrRemotingConnectMSFT método que especifica o nome do host e a porta por meio da XrRemotingConnectInfoMSFT estrutura. A porta usada pelo Player de Comunicação Remota Holográfica é 8265.

XrRemotingConnectInfoMSFT connectInfo{static_cast<XrStructureType>(XR_TYPE_REMOTING_CONNECT_INFO_MSFT)};
connectInfo.remoteHostName = "192.168.x.x";
connectInfo.remotePort = 8265;
connectInfo.secureConnection = false;
xrRemotingConnectMSFT(m_instance.Get(), m_systemId, &connectInfo);

A escuta de conexões de entrada no aplicativo remoto pode ser feita chamando o xrRemotingListenMSFT método . A porta handshake e a porta de transporte podem ser especificadas por meio da XrRemotingListenInfoMSFT estrutura. A porta handshake é usada para o handshake inicial. Em seguida, os dados são enviados pela porta de transporte. Por padrão , 8265 e 8266 são usados.

XrRemotingListenInfoMSFT listenInfo{static_cast<XrStructureType>(XR_TYPE_REMOTING_LISTEN_INFO_MSFT)};
listenInfo.listenInterface = "0.0.0.0";
listenInfo.handshakeListenPort = 8265;
listenInfo.transportListenPort = 8266;
listenInfo.secureConnection = false;
xrRemotingListenMSFT(m_instance.Get(), m_systemId, &listenInfo);

O estado de conexão deve ser desconectado quando você chama xrRemotingConnectMSFT ou xrRemotingListenMSFT. Você pode obter o estado de conexão a qualquer momento depois de criar um XrInstance e consultar o XrSystemId por meio de xrRemotingGetConnectionStateMSFT.

XrRemotingConnectionStateMSFT connectionState;
xrRemotingGetConnectionStateMSFT(m_instance.Get(), m_systemId, &connectionState, nullptr);

Os estados de conexão disponíveis são:

  • XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT
  • XR_REMOTING_CONNECTION_STATE_CONNECTING_MSFT
  • XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT

Importante

xrRemotingConnectMSFT ou xrRemotingListenMSFT deve ser chamado antes de tentar criar uma XrSession por meio de xrCreateSession. Se você tentar criar uma XrSession enquanto o estado de conexão for XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT a criação da sessão terá êxito, mas o estado da sessão fará a transição imediata para XR_SESSION_STATE_LOSS_PENDING.

A implementação de xrCreateSession suportes da Comunicação Remota Holográfica aguardando a criação de uma conexão. Você pode chamar xrRemotingConnectMSFT ou xrRemotingListenMSFT imediatamente seguido por uma chamada para xrCreateSession, que bloqueará e aguardará que uma conexão seja estabelecida. O tempo limite com xrRemotingConnectMSFT é fixo para 10 segundos e ilimitado com xrRemotingListenMSFT. Se uma conexão puder ser estabelecida dentro desse tempo, a criação do XrSession terá êxito e o estado da sessão fará a transição para XR_SESSION_STATE_READY. Caso nenhuma conexão possa ser estabelecida, a criação da sessão também é bem-sucedida, mas faz a transição imediata para XR_SESSION_STATE_LOSS_PENDING.

Em geral, o estado de conexão é par com o estado XrSession. Qualquer alteração no estado de conexão também afeta o estado da sessão. Por exemplo, se o estado de conexão mudar de para XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT o estado da XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT sessão também fará a transição para XR_SESSION_STATE_LOSS_PENDING.

Tratamento de eventos específicos de comunicação remota

O runtime do OpenXR de Comunicação Remota Holográfica expõe três eventos, que são importantes para monitorar o estado de uma conexão.

  1. XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT: disparado quando uma conexão com o dispositivo foi estabelecida com êxito.
  2. XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT: disparado se uma conexão estabelecida for fechada ou se uma conexão não puder ser estabelecida.
  3. XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: quando a escuta para conexões de entrada é iniciada.

Esses eventos são colocados em uma fila e seu aplicativo remoto deve ler da fila com regularidade por meio de xrPollEvent.

auto pollEvent = [&](XrEventDataBuffer& eventData) -> bool {
	eventData.type = XR_TYPE_EVENT_DATA_BUFFER;
	eventData.next = nullptr;
	return CHECK_XRCMD(xrPollEvent(m_instance.Get(), &eventData)) == XR_SUCCESS;
};

XrEventDataBuffer eventData{};
while (pollEvent(eventData)) {
	switch (eventData.type) {
	
	...
	
	case XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Listening on port %d",
					reinterpret_cast<const XrRemotingEventDataListeningMSFT*>(&eventData)->listeningPort);
		break;
	}
	case XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Connected.");
		break;
	}
	case XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT: {
		DEBUG_PRINT("Holographic Remoting: Disconnected - Reason: %d",
					reinterpret_cast<const XrRemotingEventDataDisconnectedMSFT*>(&eventData)->disconnectReason);
		break;
	}
}

Visualizar conteúdo transmitido localmente

Para exibir o mesmo conteúdo no aplicativo remoto que é enviado para o dispositivo, a XR_MSFT_holographic_remoting_frame_mirroring extensão pode ser usada. Com essa extensão, você pode enviar uma textura para xrEndFrame usando o XrRemotingFrameMirrorImageInfoMSFT que não está encadeado ao XrFrameEndInfo da seguinte maneira.

XrFrameEndInfo frameEndInfo{XR_TYPE_FRAME_END_INFO};
...

XrRemotingFrameMirrorImageD3D11MSFT mirrorImageD3D11{
    static_cast<XrStructureType>(XR_TYPE_REMOTING_FRAME_MIRROR_IMAGE_D3D11_MSFT)};
mirrorImageD3D11.texture = m_window->GetNextSwapchainTexture();

XrRemotingFrameMirrorImageInfoMSFT mirrorImageEndInfo{
    static_cast<XrStructureType>(XR_TYPE_REMOTING_FRAME_MIRROR_IMAGE_INFO_MSFT)};
mirrorImageEndInfo.image = reinterpret_cast<const XrRemotingFrameMirrorImageBaseHeaderMSFT*>(&mirrorImageD3D11);

frameEndInfo.next = &mirrorImageEndInfo;

xrEndFrame(m_session.Get(), &frameEndInfo);

m_window->PresentSwapchain();

O exemplo acima usa uma textura de cadeia de troca DX11 e apresenta a janela imediatamente após a chamada para xrEndFrame. O uso não está restrito a texturas de cadeia de troca. Além disso, nenhuma sincronização de GPU adicional é necessária. Para obter detalhes sobre uso e restrições, marcar a especificação da extensão. Se seu aplicativo remoto estiver usando DX12, use XrRemotingFrameMirrorImageD3D12MSFT em vez de XrRemotingFrameMirrorImageD3D11MSFT.

Opcional: canais de dados personalizados

A partir da versão 2.5.0, os canais de dados personalizados podem ser usados com a API OpenXR para enviar dados do usuário pela conexão remota já estabelecida. Para obter mais informações, consulte Canais de dados personalizados com a API do OpenXR.

Opcional: Fala

A partir da versão 2.6.0, a XR_MSFT_holographic_remoting_speech extensão permite que o aplicativo remoto reaja aos comandos de fala detectados pelo aplicativo player com a API OpenXR.

[! IMPORTANTE] A especificação detalhada pode ser encontrada no repositório github de exemplos de Comunicação Remota Holográfica.

Para inicializar um reconhecedor de fala no aplicativo player, o aplicativo remoto pode chamar xrInitializeRemotingSpeechMSFT. Essa chamada transmite parâmetros de inicialização de fala, que consistem em um idioma, um dicionário de frases e o conteúdo de um arquivo de gramática, para o aplicativo player.

Observação

Antes da versão 2.6.1 , o reconhecedor de fala só deve ser inicializado uma vez por XrSession.

Se a criação do reconhecedor de fala tiver sido bem-sucedida, conforme indicado pelo XR_TYPE_EVENT_DATA_REMOTING_SPEECH_RECOGNIZER_STATE_CHANGED_MSFT evento, o aplicativo remoto será notificado quando um resultado de reconhecimento de fala for gerado no aplicativo player. A XrEventDataRemotingSpeechRecognizerStateChangedMSFT estrutura de eventos é colocada na fila de eventos quando o estado do reconhecedor de fala no lado do player é alterado.

XrRemotingSpeechRecognizerStateMSFT define todos os estados possíveis do reconhecedor de fala no lado do player e a estrutura do XrEventDataRemotingSpeechRecognizedMSFT evento é colocada na fila de eventos se o reconhecedor de fala no lado do player tiver uma frase reconhecida. Depois que o aplicativo remoto é notificado sobre uma frase reconhecida, ele pode recuperar a frase reconhecida chamando xrRetrieveRemotingSpeechRecognizedTextMSFT.

Observação

O XrRemotingSpeechRecognitionConfidenceMSFT é um mapeamento direto da enumeração SpeechRecognitionConfidence retornada com o resultado do reconhecimento de fala pela API de Reconhecimento de Fala do Windows.

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 Visão geral da sincronização do sistema de coordenadas com a comunicação remota holográfica.

Consulte Também