Escrevendo um aplicativo remoto de remota holográfica usando a API openXR

Se você for novo no Holographic Remoting, 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 NuGet versão 1.x.x do pacote. Isso implica que aplicativos remotos escritos para HoloLens 2 não são compatíveis com HoloLens 1 e vice-versa. A documentação HoloLens 1 pode ser encontrada aqui.

Os aplicativos de remotação 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 do 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 o Wi-Fi padrão. A remota holográfica é adicionada a um aplicativo de área de trabalho ou UWP por meio de um NuGet pacote. É necessário um código adicional que trata a conexão e renderiza em uma exibição imersiva. Uma conexão de remoting típica terá até 50 ms de latência. O aplicativo player pode relatar a latência em tempo real.

Todo o código nesta página e projetos de trabalho podem ser encontrados no repositório GitHub de exemplos de Remo holográfica.

Pré-requisitos

Um bom ponto de partida é um aplicativo UWP ou Área de Trabalho baseado em OpenXR em funcionamento. Para obter detalhes , consulte Getting started with OpenXR ( Como começar a trabalhar com o OpenXR).

Importante

Qualquer aplicativo que use a Holographic Remoting deve ser autor para usar um apartment multi-threaded. Há suporte para o uso de um apartment de thread único, mas levará ao desempenho abaixo do ideal e possivelmente à gagueja durante a reprodução. Ao usar C++/WinRT winrt::init_apartment um apartment multi-threaded é o padrão.

Obter o pacote de NuGet Holographic Remoting

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 NuGet Pacotes...
  3. No painel exibido, selecione Procurar e, em seguida, pesquise "Holographic Remoting".
  4. Selecione Microsoft.Holographic.Remoting.OpenXr, verifique se a versão 2.x.x mais recente está selecionada e selecione Instalar.
  5. Se a caixa de diálogo Visualização 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 querem direcionar HoloLens 1. Para obter detalhes, consulte Adicionar a HoloLens holográfica (1ª geração)).

Selecione o runtime do OpenXR de Remoção Holográfica

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

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 extensão de remoção holográfica

As primeiras ações que um aplicativo OpenXR típico deve tomar são selecionar extensões OpenXR e criar um XrInstance. A especificação do núcleo do OpenXR não fornece nenhuma API específica de remoção. Por esse motivo, a Holographic Remoting introduz sua própria extensão OpenXR chamada XR_MSFT_holographic_remoting. Verifique se XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME está incluído na XrInstanceCreateInfo da chamada xrCreateInstance.

Dica

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

Importante

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

Conexão para o dispositivo

Depois que seu 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 Remoção Holográfica só é capaz de fornecer dados específicos do dispositivo, como configurações de exibição ou modos de mesclagem de ambiente depois que uma conexão é estabelecida. xrEnumerateViewConfigurations, xrEnumerateViewConfigurationViews, xrGetViewConfigurationProperties, xrEnumerateEnvironmentBlendModese xrGetSystemProperties lhe darão valores padrão, correspondendo ao que você normalmente obteria se você se conectasse a um player 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 esses métodos depois que a XrSession tiver sido criada com êxito e o estado da sessão for pelo menos XR_SESSION_STATE_READY.

Propriedades gerais, como taxa de bits máxima, á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, xrRemotingConnectMSFT chame o método especificando o nome do host e a porta por meio da XrRemotingConnectInfoMSFT estrutura . A porta usada pelo Holographic Remoting Player é 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 de handshake e a porta de transporte podem ser especificadas por meio da XrRemotingListenInfoMSFT estrutura . A porta de 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 ter criado um XrInstance e consultado para o XrSystemId via 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 XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT enquanto o estado de conexão for a criação da sessão terá êxito, mas o estado da sessão fará a transição imediatamente para XR_SESSION_STATE_LOSS_PENDING.

A implementação do Holographic Remoting de xrCreateSession dá suporte à espera de uma conexão ser estabelecida. Você pode chamar xrRemotingConnectMSFT ou xrRemotingListenMSFT imediatamente seguido por uma chamada para, que bloqueará e aguardará que uma conexão seja estabelecida. O tempo de vida é fixo em 10 segundos. Se uma conexão puder ser estabelecida dentro desse tempo, a criação de 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 terá êxito, mas faz a transição imediatamente para XR_SESSION_STATE_LOSS_PENDING.

Em geral, o estado de conexão é junto 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 XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT para XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT o estado de sessão, a transição para XR_SESSION_STATE_LOSS_PENDING também.

Manipulando eventos específicos de remoting

O runtime do OpenXR de Remoção 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 não for possível estabelecer uma conexão.
  3. XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: ao escutar conexões de entrada é iniciado.

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 ao dispositivo, a XR_MSFT_holographic_remoting_frame_mirroring extensão pode ser usada. Com essa extensão, você pode enviar uma textura para xrEndFrame XrRemotingFrameMirrorImageInfoMSFT usando o que não está encadeado ao XrFrameEndInfo da seguinte forma.

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 permuta DX11 e apresenta a janela imediatamente após a chamada para xrEndFrame. O uso não está restrito à troca de texturas de cadeia. Além disso, nenhuma sincronização de GPU adicional é necessária. Para obter detalhes sobre o uso e as restrições, confira a especificação de extensão. Se seu aplicativo remoto estiver usando o 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 do 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 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 a comandos de fala detectados pelo aplicativo de Player com a API OpenXR.

[! IMPORTANTE] a especificação detalhada pode ser encontrada no repositório GitHub de exemplos de comunicação remota do Holographic.

Para inicializar um reconhecedor de fala no aplicativo de 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 de Player.

Observação

Antes da versão 2.6.1 , o reconhecedor de fala deve ser inicializado apenas 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 tiver sido gerado no aplicativo de Player. A XrEventDataRemotingSpeechRecognizerStateChangedMSFT estrutura de eventos é colocada na fila de eventos quando o estado do reconhecedor de fala no lado do jogador é 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 jogador 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 pelo Windows API de reconhecimento de fala.

Opcional: coordenar a sincronização do sistema

A partir da versão 2.7.0, a coordenada da sincronização do sistema pode ser usada para alinhar dados espaciais entre o Player e o aplicativo remoto. Para obter mais informações, consulte coordenar a sincronização do sistema com a visão geral do Holographic Remoting.

Consulte Também