Escritura de una aplicación remota holográfica mediante holographicSpace API

Si no está familiarizado con la comunicación remota holográfica, es posible que quiera leer nuestra introducción.

Importante

En este documento se describe la creación de una aplicación remota para HoloLens 2 mediante holographicSpace API. Las aplicaciones remotas para HoloLens (1.ª generación) deben usar la versión 1.x.x del paquete NuGet. Esto implica que las aplicaciones remotas escritas para HoloLens 2 no son compatibles con HoloLens 1 y viceversa. La documentación de HoloLens 1 se puede encontrar aquí.

Aviso de desuso: la línea de versión 2.9.x será la última para admitir las API de Windows Holographic para el desarrollo de aplicaciones. Las próximas versiones solo admitirán OpenXR para el desarrollo de aplicaciones. Independientemente de eso, se recomienda el uso de OpenXR en la aplicación para todo el desarrollo de aplicaciones nuevo. Las aplicaciones existentes que usan la versión 2.9 o anterior seguirán funcionando sin verse afectadas por los próximos cambios.

Las aplicaciones de comunicación remota holográfica pueden transmitir contenido representado de forma remota a HoloLens 2 y Windows Mixed Reality cascos envolventes. También puede acceder a más recursos del sistema e integrar vistas inmersivas remotas en el software de pc de escritorio existente. Una aplicación remota recibe un flujo de datos de entrada de HoloLens 2, representa el contenido en una vista envolvente virtual y transmite fotogramas de contenido a HoloLens 2. La conexión se realiza mediante wi-fi estándar. La comunicación remota holográfica se agrega a una aplicación de escritorio o para UWP a través de un paquete NuGet. Se requiere código adicional que controla la conexión y se representa en una vista inmersiva. Una conexión remota típica tendrá tan solo 50 ms de latencia. La aplicación del reproductor puede notificar la latencia en tiempo real.

Todo el código de esta página y los proyectos de trabajo se pueden encontrar en el repositorio de github de ejemplos de comunicación remota holográfica.

Requisitos previos

Un buen punto de partida es una aplicación de escritorio o UWP basada en DirectX que tiene como destino la API de Windows Mixed Reality. Para obtener más información, consulte Introducción al desarrollo de DirectX. La plantilla de proyecto holográfica de C++ es un buen punto de partida.

Importante

Cualquier aplicación que use La comunicación remota holográfica debe crearse para usar un apartamento multiproceso. Se admite el uso de un apartamento de un solo subproceso , pero dará lugar a un rendimiento sub óptimo y, posiblemente, a la agitación durante la reproducción. Cuando se usa C++/WinRT winrt::init_apartment un apartamento multiproceso es el valor predeterminado.

Obtención del paquete NuGet de comunicación remota holográfica

Los pasos siguientes son necesarios para agregar el paquete NuGet a un proyecto en Visual Studio.

  1. Abra el proyecto en Visual Studio.
  2. Haga clic con el botón derecho en el nodo del proyecto y seleccione Administrar paquetes NuGet...
  3. En el panel que aparece, seleccione Examinar y busque "Comunicación remota holográfica".
  4. Seleccione Microsoft.Holographic.Remoting, asegúrese de elegir la versión 2.x.x más reciente y seleccione Instalar.
  5. Si aparece el cuadro de diálogo Vista previa , seleccione Aceptar.
  6. Seleccione Acepto cuando aparezca el cuadro de diálogo contrato de licencia.

Nota

La versión 1.x.x del paquete NuGet sigue estando disponible para los desarrolladores que quieran dirigirse a HoloLens 1. Para obtener más información, consulte Agregar comunicación remota holográfica (HoloLens (1.ª generación)).

Creación del contexto remoto

Como primer paso, la aplicación debe crear un contexto remoto.

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

...

private:
    // RemoteContext used to connect with a Holographic Remoting player and display rendered frames
    winrt::Microsoft::Holographic::AppRemoting::RemoteContext m_remoteContext = nullptr;
// class implementation
#include <HolographicAppRemoting\Streamer.h>

...

CreateRemoteContext(m_remoteContext, 20000, false, PreferredVideoCodec::Default);

Advertencia

La comunicación remota holográfica funciona reemplazando el entorno de ejecución de Windows Mixed Reality que forma parte de Windows por un entorno de ejecución específico de comunicación remota. Esto se hace durante la creación del contexto remoto. Por ese motivo, cualquier llamada en cualquier API de Windows Mixed Reality antes de crear el contexto remoto puede dar lugar a un comportamiento inesperado. El enfoque recomendado es crear el contexto remoto lo antes posible antes de la interacción con cualquier API de Mixed Reality. Nunca mezcle objetos creados o recuperados a través de cualquier API de Windows Mixed Reality antes de la llamada a CreateRemoteContext con objetos creados o recuperados posteriormente.

A continuación, debe crearse el espacio holográfico. No es necesario especificar coreWindow. Las aplicaciones de escritorio que no tienen coreWindow solo pueden pasar .nullptr

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

Conexión al dispositivo

Cuando la aplicación remota está lista para representar contenido, se puede establecer una conexión con el dispositivo del reproductor.

La conexión se puede realizar de una de estas dos maneras.

  1. La aplicación remota se conecta al reproductor que se ejecuta en el dispositivo.
  2. El reproductor que se ejecuta en el dispositivo se conecta a la aplicación remota.

Para establecer una conexión desde la aplicación remota al dispositivo del reproductor, llame al Connect método en el contexto remoto especificando el nombre de host y el puerto. El puerto usado por holographic Remoting Player es 8265.

try
{
    m_remoteContext.Connect(m_hostname, m_port);
}
catch(winrt::hresult_error& e)
{
    DebugLog(L"Connect failed with hr = 0x%08X", e.code());
}

Importante

Al igual que con cualquier API Connect de C++/WinRT, se podría producir un winrt::hresult_error que debe controlarse.

Sugerencia

Para evitar el uso de la proyección del lenguaje C++/WinRT , se puede incluir el archivo build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h ubicado dentro del paquete NuGet de comunicación remota holográfica. Contiene declaraciones de las interfaces COM subyacentes. Aunque se recomienda el uso de C++/WinRT.

La escucha de conexiones entrantes en la aplicación remota se puede realizar mediante una llamada al Listen método . Tanto el puerto de protocolo de enlace como el puerto de transporte se pueden especificar durante esta llamada. El puerto del protocolo de enlace se usa para el protocolo de enlace inicial. A continuación, los datos se envían a través del puerto de transporte. De forma predeterminada, se usan 8265 y 8266 .

try
{
    m_remoteContext.Listen(L"0.0.0.0", m_port, m_port + 1);
}
catch(winrt::hresult_error& e)
{
    DebugLog(L"Listen failed with hr = 0x%08X", e.code());
}

Importante

El build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl dentro del paquete NuGet contiene documentación detallada para la API expuesta por la comunicación remota holográfica.

Control de eventos específicos de comunicación remota

El contexto remoto expone tres eventos, que son importantes para supervisar el estado de una conexión.

  1. OnConnected: se desencadena cuando se ha establecido correctamente una conexión al dispositivo.
winrt::weak_ref<winrt::Microsoft::Holographic::AppRemoting::IRemoteContext> remoteContextWeakRef = m_remoteContext;

m_onConnectedEventRevoker = m_remoteContext.OnConnected(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});
  1. OnDisconnected: se desencadena si se cierra una conexión establecida o no se pudo establecer una conexión.
m_onDisconnectedEventRevoker =
    m_remoteContext.OnDisconnected(winrt::auto_revoke, [this, remoteContextWeakRef](ConnectionFailureReason failureReason) {
        if (auto remoteContext = remoteContextWeakRef.get())
        {
            DebugLog(L"Disconnected with reason %d", failureReason);
            // Update UI

            // Reconnect if this is a transient failure.
            if (failureReason == ConnectionFailureReason::HandshakeUnreachable ||
                failureReason == ConnectionFailureReason::TransportUnreachable ||
                failureReason == ConnectionFailureReason::ConnectionLost)
            {
                DebugLog(L"Reconnecting...");

                ConnectOrListen();
            }
            // Failure reason None indicates a normal disconnect.
            else if (failureReason != ConnectionFailureReason::None)
            {
                DebugLog(L"Disconnected with unrecoverable error, not attempting to reconnect.");
            }
        }
    });
  1. OnListening: cuando se inicia la escucha de conexiones entrantes.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

Además, se puede consultar el estado de conexión mediante la ConnectionState propiedad en el contexto remoto.

auto connectionState = m_remoteContext.ConnectionState();

Control de eventos de voz

Con la interfaz de voz remota es posible registrar desencadenadores de voz con HoloLens 2 y tenerlos remotos a la aplicación remota.

Se requiere el siguiente miembro adicional para realizar un seguimiento del estado de la voz remota:

winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech::OnRecognizedSpeech_revoker m_onRecognizedSpeechRevoker;

En primer lugar, recupere la interfaz de voz remota.

if (auto remoteSpeech = m_remoteContext.GetRemoteSpeech())
{
    InitializeSpeechAsync(remoteSpeech, m_onRecognizedSpeechRevoker, weak_from_this());
}

Con un método auxiliar asincrónico, puede inicializar la voz remota. Esto se debe hacer de forma asincrónica, ya que la inicialización puede tardar mucho tiempo. Las operaciones asincrónicas y de simultaneidad con C++/WinRT explican cómo crear funciones asincrónicas con C++/WinRT.

winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile> LoadGrammarFileAsync()
{
    const wchar_t* speechGrammarFile = L"SpeechGrammar.xml";
    auto rootFolder = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation();
    return rootFolder.GetFileAsync(speechGrammarFile);
}

winrt::fire_and_forget InitializeSpeechAsync(
    winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech remoteSpeech,
    winrt::Microsoft::Holographic::AppRemoting::IRemoteSpeech::OnRecognizedSpeech_revoker& onRecognizedSpeechRevoker,
    std::weak_ptr<SampleRemoteMain> sampleRemoteMainWeak)
{
    onRecognizedSpeechRevoker = remoteSpeech.OnRecognizedSpeech(
        winrt::auto_revoke, [sampleRemoteMainWeak](const winrt::Microsoft::Holographic::AppRemoting::RecognizedSpeech& recognizedSpeech) {
            if (auto sampleRemoteMain = sampleRemoteMainWeak.lock())
            {
                sampleRemoteMain->OnRecognizedSpeech(recognizedSpeech.RecognizedText);
            }
        });

    auto grammarFile = co_await LoadGrammarFileAsync();

    std::vector<winrt::hstring> dictionary;
    dictionary.push_back(L"Red");
    dictionary.push_back(L"Blue");
    dictionary.push_back(L"Green");
    dictionary.push_back(L"Default");
    dictionary.push_back(L"Aquamarine");

    remoteSpeech.ApplyParameters(L"", grammarFile, dictionary);
}

Hay dos maneras de especificar frases que se van a reconocer.

  1. Especificación dentro de un archivo xml de gramática de voz. Consulte How to create a basic XML Grammar (Cómo crear una gramática XML básica ) para obtener más información.
  2. Especifique pasándolos dentro del vector de diccionario a ApplyParameters.

Dentro de la devolución de llamada OnRecognizedSpeech, se pueden procesar los eventos de voz:

void SampleRemoteMain::OnRecognizedSpeech(const winrt::hstring& recognizedText)
{
    bool changedColor = false;
    DirectX::XMFLOAT4 color = {1, 1, 1, 1};

    if (recognizedText == L"Red")
    {
        color = {1, 0, 0, 1};
        changedColor = true;
    }
    else if (recognizedText == L"Blue")
    {
        color = {0, 0, 1, 1};
        changedColor = true;
    }
    else if (recognizedText == L"Green")
    {
        ...
    }

    ...
}

Vista previa del contenido transmitido localmente

Para mostrar el mismo contenido en la aplicación remota que se envía al dispositivo, se puede usar el OnSendFrame evento del contexto remoto. El OnSendFrame evento se desencadena cada vez que la biblioteca de comunicación remota holográfica envía el marco actual al dispositivo remoto. Este es el momento ideal para tomar el contenido y también dividirlo en la ventana de escritorio o UWP.

#include <windows.graphics.directx.direct3d11.interop.h>

...

m_onSendFrameEventRevoker = m_remoteContext.OnSendFrame(
    winrt::auto_revoke, [this](const winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface& texture) {
        winrt::com_ptr<ID3D11Texture2D> texturePtr;
        {
            winrt::com_ptr<ID3D11Resource> resource;
            winrt::com_ptr<::IInspectable> inspectable = texture.as<::IInspectable>();
            winrt::com_ptr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess;
            winrt::check_hresult(inspectable->QueryInterface(__uuidof(dxgiInterfaceAccess), dxgiInterfaceAccess.put_void()));
            winrt::check_hresult(dxgiInterfaceAccess->GetInterface(__uuidof(resource), resource.put_void()));
            resource.as(texturePtr);
        }

        // Copy / blit texturePtr into the back buffer here.
    });

Reproyecto de profundidad

A partir de la versión 2.1.0, La comunicación remota holográfica admite la reproyecto de profundidad. Esto requiere que el búfer de colores y el búfer de profundidad se transmitan desde la aplicación remota a la HoloLens 2. De forma predeterminada, el streaming del búfer de profundidad está habilitado y configurado para usar la mitad de la resolución del búfer de colores. Esto se puede cambiar de la siguiente manera:

// class implementation
#include <HolographicAppRemoting\Streamer.h>

...

CreateRemoteContext(m_remoteContext, 20000, false, PreferredVideoCodec::Default);

// Configure for half-resolution depth.
m_remoteContext.ConfigureDepthVideoStream(DepthBufferStreamResolution::Half_Resolution);

Tenga en cuenta que, si no se deben usar ConfigureDepthVideoStream valores predeterminados, se debe llamar a antes de establecer una conexión con el HoloLens 2. El mejor lugar es justo después de haber creado el contexto remoto. Los valores posibles para DepthBufferStreamResolution son:

  • Full_Resolution
  • Half_Resolution
  • Quarter_Resolution
  • Deshabilitado (agregado con la versión 2.1.3 y si no se usa ninguna secuencia de vídeo de profundidad adicional)

Tenga en cuenta que el uso de un búfer de profundidad de resolución completa también afecta a los requisitos de ancho de banda y debe tener en cuenta en el valor máximo de ancho de banda que proporcione a CreateRemoteContext.

Además de configurar la resolución, también tienes que confirmar un búfer de profundidad a través de HolographicCameraRenderingParameters.CommitDirect3D11DepthBuffer.


void SampleRemoteMain::Render(HolographicFrame holographicFrame)
{
    ...

    m_deviceResources->UseHolographicCameraResources([this, holographicFrame](auto& cameraResourceMap) {
        
        ...

        for (auto cameraPose : prediction.CameraPoses())
        {
            DXHelper::CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();

            ...

            m_deviceResources->UseD3DDeviceContext([&](ID3D11DeviceContext3* context) {
                
                ...

                // Commit depth buffer if available and enabled.
                if (m_canCommitDirect3D11DepthBuffer && m_commitDirect3D11DepthBuffer)
                {
                    auto interopSurface = pCameraResources->GetDepthStencilTextureInteropObject();
                    HolographicCameraRenderingParameters renderingParameters = holographicFrame.GetRenderingParameters(cameraPose);
                    renderingParameters.CommitDirect3D11DepthBuffer(interopSurface);
                }
            });
        }
    });
}

Para comprobar si la reproyecto de profundidad funciona correctamente en HoloLens 2, puede habilitar un visualizador de profundidad a través del Portal de dispositivos. Consulte Comprobar que la profundidad está establecida correctamente para obtener más información.

Opcional: Canales de datos personalizados

Los canales de datos personalizados se pueden usar para enviar datos de usuario a través de la conexión remota ya establecida. Para obtener más información, consulte Canales de datos personalizados.

Opcional: Sincronización del sistema de coordenadas

A partir de la versión 2.7.0, la sincronización del sistema de coordenadas se puede usar para alinear los datos espaciales entre el reproductor y la aplicación remota. Para obtener más información, vea Coordenadas de sincronización del sistema con información general sobre la comunicación remota holográfica.

Consulte también