Escrever uma aplicação Holographic Remoting Remote com a API OpenXR

Se não estiver familiarizado com o Holographic Remoting, poderá querer ler a nossa descrição geral.

Importante

Este documento descreve a criação de uma aplicação remota para auscultadores HoloLens 2 e Windows Mixed Reality com a API OpenXR. As aplicações remotas do HoloLens (1.ª geração) têm de utilizar a versão do pacote NuGet 1.x.x. Isto implica que as aplicações remotas escritas 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.

As aplicações Holographic Remoting podem transmitir conteúdos compostos remotamente para HoloLens 2 e Windows Mixed Reality headsets envolventes. Também pode aceder a mais recursos do sistema e integrar vistas avançadas remotas em software de PC de ambiente de trabalho existente. Uma aplicação remota recebe um fluxo de dados de entrada de HoloLens 2, compõe conteúdos numa vista envolvente virtual e transmite os pacotes de conteúdo para HoloLens 2. A ligação é efetuada com Wi-Fi padrão. A Remoting Holográfica é adicionada a uma aplicação de ambiente de trabalho ou UWP através de um pacote NuGet. É necessário código adicional que processa a ligação e é composto numa vista envolvente. Uma ligação remota típica terá até 50 ms de latência. A aplicação player pode comunicar 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 do Holographic Remoting.

Pré-requisitos

Um bom ponto de partida é uma aplicação UWP ou ambiente de trabalho baseada em OpenXR. Para obter detalhes, consulte Introdução ao OpenXR.

Importante

Qualquer aplicação que utilize o Holographic Remoting deve ser criada para utilizar um apartamento com vários threads. A utilização de um apartamento de thread único é suportada, mas levará a um desempenho sub-ideal e possivelmente gaguez durante a reprodução. Ao utilizar C++/ WinRT winrt::init_apartment um apartamento com vários threads é a predefinição.

Obter o pacote Holographic Remoting NuGet

São necessários os seguintes passos 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 rato no nó do projeto e selecione Gerir Pacotes NuGet...
  3. No painel apresentado, selecione Procurar e, em seguida, procure "Remoto Holográfico".
  4. Selecione Microsoft.Holographic.Remoting.OpenXr e, em seguida, certifique-se de que a versão 2.x.x mais recente está selecionada e, em seguida, selecione Instalar.
  5. Se a caixa de diálogo Pré-visualizar for apresentada, selecione OK.
  6. Selecione Aceito quando a caixa de diálogo do contrato de licença é apresentada.
  7. Repita os passos 3 a 6 para os seguintes Pacotes NuGet: OpenXR.Headers, OpenXR.Loader

Nota

A versão 1.x.x do pacote NuGet ainda está disponível para programadores que pretendam direcionar o HoloLens 1. Para obter detalhes, veja Adicionar Remoting Holográfico (HoloLens (1.ª geração)).

Selecione o runtime Do Holographic Remoting OpenXR

O primeiro passo que tem de fazer na sua aplicação remota é selecionar o runtime OpenXR Holographic Remoting, que faz parte do pacote NuGet Microsoft.Holographic.Remoting.OpenXr. Pode fazê-lo ao definir a XR_RUNTIME_JSON variável de ambiente para o caminho do ficheiro RemotingXR.json na sua aplicação. Esta variável de ambiente é utilizada pelo carregador OpenXR para não utilizar o runtime OpenXR predefinido do sistema, mas sim redirecionar para o runtime OpenXR Do Holographic Remoting. Ao utilizar o pacote NuGet Microsoft.Holographic.Remoting.OpenXr, o ficheiro RemotingXR.json é copiado automaticamente durante a compilação para a pasta de saída, a seleção de runtime OpenXR normalmente tem o seguinte aspeto.

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 Remoting Holográfica

As primeiras ações que uma aplicação OpenXR típica deve realizar são selecionar extensões OpenXR e criar um XrInstance. A especificação do núcleo OpenXR não fornece nenhuma API específica remota. Por esse motivo, a Holographic Remoting apresenta a sua própria extensão OpenXR denominada XR_MSFT_holographic_remoting. Certifique-se de que XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME está incluído no XrInstanceCreateInfo da chamada xrCreateInstance.

Dica

Por predefinição, o conteúdo composto da sua aplicação só é transmitido para o leitor Holographic Remoting em execução num HoloLens 2 ou num Windows Mixed Reality headsets. Para também apresentar o conteúdo composto no PC remoto, através 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 que também ativa esta extensão através XR_MSFT_HOLOGRAPHIC_REMOTING_FRAME_MIRRORING_EXTENSION_NAME do caso de pretender utilizar essa funcionalidade.

Importante

Para saber mais sobre a API de extensão Holographic Remoting OpenXR, consulte a especificação que pode ser encontrada no repositório github de exemplos De Remoting Holográfico.

Ligar ao dispositivo

Depois de a sua aplicação remota ter criado o XrInstance e consultado o XrSystemId através de xrGetSystem, pode estabelecer uma ligação ao dispositivo do leitor.

Aviso

O runtime Do Holographic Remoting OpenXR só consegue fornecer dados específicos do dispositivo, tais como configurações de visualização ou modos de mistura de ambiente após a ligação ter sido estabelecida. xrEnumerateViewConfigurations, xrEnumerateViewConfigurationViews, xrGetViewConfigurationProperties, xrEnumerateEnvironmentBlendModese xrGetSystemProperties dar-lhe-ão valores predefinidos, que correspondem ao que normalmente obteria se se ligasse a um jogador em execução num HoloLens 2, antes de estar totalmente ligado. Recomenda-se vivamente que não chame estes métodos antes de ser estabelecida uma ligação. A sugestão é utilizada nestes métodos depois de a XrSession ter sido criada com êxito e o estado da sessão ser, pelo menos, XR_SESSION_STATE_READY.

As propriedades gerais, como velocidade de transmissão máxima, áudio ativado, codec de vídeo ou resolução de fluxo de memória intermédia de profundidade, podem ser configuradas da xrRemotingSetContextPropertiesMSFT seguinte forma.

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 ligação pode ser feita de uma de duas formas.

  1. A aplicação remota liga-se ao leitor em execução no dispositivo.
  2. O leitor em execução no dispositivo liga-se à aplicação remota.

Para estabelecer uma ligação da aplicação remota ao dispositivo do leitor, chame o xrRemotingConnectMSFT método que especifica o nome do anfitrião e a porta através da XrRemotingConnectInfoMSFT estrutura. A porta utilizada 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 ligações recebidas na aplicação remota pode ser feita ao chamar o xrRemotingListenMSFT método. A porta de handshake e a porta de transporte podem ser especificadas através da XrRemotingListenInfoMSFT estrutura. A porta de handshake é utilizada para o handshake inicial. Em seguida, os dados são enviados através da porta de transporte. Por predefinição, são utilizados 8265 e 8266 .

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 da ligação tem de estar desligado quando ligar xrRemotingConnectMSFT ou xrRemotingListenMSFT. Pode obter o estado da ligação em qualquer altura depois de ter criado um XrInstance e consultado para o XrSystemId através de xrRemotingGetConnectionStateMSFT.

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

Os estados de ligaçã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 tem de ser chamado antes de tentar criar uma XrSession através de xrCreateSession. Se tentar criar um XrSession enquanto o estado da ligação for XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT a criação da sessão, a criação da sessão será bem-sucedida, mas o estado da sessão transitará imediatamente para XR_SESSION_STATE_LOSS_PENDING.

A implementação xrCreateSession de suportes do Holographic Remoting aguarda a criação de uma ligação. Pode ligar xrRemotingConnectMSFT ou xrRemotingListenMSFT imediatamente seguido de uma chamada para xrCreateSession, que bloqueará e aguardará que seja estabelecida uma ligação. O tempo limite com xrRemotingConnectMSFT é corrigido para 10 segundos e ilimitado com xrRemotingListenMSFT. Se for possível estabelecer uma ligação dentro deste momento, a criação de XrSession será bem-sucedida e o estado da sessão passará para XR_SESSION_STATE_READY. Caso não seja possível estabelecer nenhuma ligação, a criação da sessão também é bem-sucedida, mas transições imediatas para XR_SESSION_STATE_LOSS_PENDING.

Em geral, o estado da ligação é associado ao estado XrSession. Qualquer alteração ao estado de ligação também afeta o estado da sessão. Por exemplo, se o estado da ligação mudar de para XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT o estado da XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT sessão também irá transitar para XR_SESSION_STATE_LOSS_PENDING.

Processar eventos específicos de Remoting

O runtime Do Holographic Remoting OpenXR expõe três eventos, que são importantes para monitorizar o estado de uma ligação.

  1. XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT: acionado quando uma ligação ao dispositivo foi estabelecida com êxito.
  2. XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT: acionado se uma ligação estabelecida estiver fechada ou não for possível estabelecer uma ligação.
  3. XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT: ao escutar as ligações recebidas é iniciada.

Estes eventos são colocados numa fila e a sua aplicação remota tem de ler a partir da fila com regularidade através 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;
	}
}

Pré-visualizar conteúdo transmitido em fluxo localmente

Para apresentar o mesmo conteúdo na aplicação remota que é enviada para o dispositivo, a XR_MSFT_holographic_remoting_frame_mirroring extensão pode ser utilizada. Com esta extensão, pode submeter uma textura para xrEndFrame ao utilizar o XrRemotingFrameMirrorImageInfoMSFT que não está em cadeia no 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 utiliza uma textura da cadeia de troca DX11 e apresenta a janela imediatamente após a chamada para xrEndFrame. A utilização não está restrita a texturas de cadeia de troca. Além disso, não é necessária nenhuma sincronização de GPU adicional. Para obter detalhes sobre a utilização e as restrições, consulte a especificação da extensão. Se a sua aplicação remota estiver a utilizar DX12, utilize 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 utilizados com a API OpenXR para enviar dados de utilizador através da ligação remota já estabelecida. Para obter mais informações, veja Custom Data Channels with the OpenXR API (Canais de Dados Personalizados com a API OpenXR).

Opcional: Voz

A partir da versão 2.6.0, a XR_MSFT_holographic_remoting_speech extensão permite que a aplicação remota reaja aos comandos de voz detetados pela aplicação do leitor 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 voz na aplicação player, a aplicação remota pode chamar xrInitializeRemotingSpeechMSFT. Esta chamada transmite parâmetros de inicialização de voz, que consistem num idioma, num dicionário de expressões e no conteúdo de um ficheiro gramatical, para a aplicação de leitor.

Nota

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

Se a criação do reconhecedor de voz tiver sido efetuada com êxito, conforme indicado pelo XR_TYPE_EVENT_DATA_REMOTING_SPEECH_RECOGNIZER_STATE_CHANGED_MSFT evento, a aplicação remota será notificada quando for gerado um resultado de reconhecimento de voz na aplicação player. A XrEventDataRemotingSpeechRecognizerStateChangedMSFT estrutura do evento é colocada na fila de eventos quando o estado do reconhecedor de voz no lado do leitor é alterado.

XrRemotingSpeechRecognizerStateMSFT define todos os estados possíveis do reconhecedor de voz do lado do leitor e a estrutura do XrEventDataRemotingSpeechRecognizedMSFT evento é colocada na fila de eventos se o reconhecedor de voz do lado do leitor tiver uma expressão reconhecida. Depois de a aplicação remota ser notificada sobre uma expressão reconhecida, pode obter a expressão reconhecida ao chamar xrRetrieveRemotingSpeechRecognizedTextMSFT.

Nota

É XrRemotingSpeechRecognitionConfidenceMSFT um mapeamento direto da enumeração SpeechRecognitionConfidence devolvida com o resultado do reconhecimento de voz pela API de Reconhecimento de Voz do Windows.

Opcional: Coordenar a Sincronização do Sistema

A partir da versão 2.7.0, a sincronização do sistema coordenada pode ser utilizada para alinhar dados espaciais entre o leitor e a aplicação remota. Para obter mais informações, veja Coordenar a Sincronização do Sistema com a Descrição Geral da Comunicação Remota Holográfica.

Consulte também