Escritura de una aplicación remota de Holographic Remoting mediante HolographicSpace API

Si no está nuevo en la comunicación remota holográfica, puede 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 HoloLens (1.ª generación) deben usar NuGet versión 1.x.x del paquete. Esto implica que las aplicaciones remotas escritas HoloLens 2 no son compatibles con HoloLens 1 y viceversa. La documentación de HoloLens 1 se puede encontrar aquí.

Las aplicaciones de comunicación remota holográfica pueden transmitir contenido representado de forma remota 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 inmersiva virtual y transmite los 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 UWP a través de un paquete NuGet usuario. 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 player 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 Holographic Remoting.

Requisitos previos

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

Importante

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

Obtener el paquete de NuGet holographic remoting

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 NuGet paquetes...
  3. En el panel que aparece, seleccione Examinar y busque "Holographic Remoting".
  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 se abre el cuadro de diálogo del contrato de licencia.

Nota

La versión 1.x.x del paquete NuGet sigue estando disponible para los desarrolladores que desean tener como destino HoloLens 1. Para 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 tiempo de ejecución Windows Mixed Reality que forma parte de Windows por un tiempo de ejecución específico de comunicación remota. Esto se hace durante la creación del contexto remoto. Por ese motivo, cualquier llamada a cualquier API 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 Mixed Reality API. No mezcle nunca los objetos creados o recuperados a través de Windows Mixed Reality API antes de llamar a CreateRemoteContext con objetos creados o recuperados posteriormente.

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

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

Conectar 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 método en el contexto remoto especificando el nombre Connect 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 de C++/WinRT, podría producirse una Connect excepción winrt::hresult_error que debe controlarse.

Sugerencia

Para evitar el uso de la proyección del lenguaje C++/WinRT, se puede incluir el archivo que se encuentra dentro del paquete de NuGet holographic remoting. Contiene declaraciones de las interfaces COM subyacentes. Sin embargo, se recomienda usar 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 de 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

Dentro del paquete NuGet contiene documentación detallada de build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl la API expuesta por Holographic Remoting.

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 puede 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 hacer que se desencadene de forma remota a la aplicación remota.

Este miembro adicional es necesario 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 debe hacerse de forma asincrónica, ya que la inicialización puede tardar una cantidad considerable de tiempo. Operaciones de simultaneidad y asincrónicas con C++/WinRT explica 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 las frases que se van a reconocer.

  1. Especificación dentro de un archivo XML de gramática de voz. Vea 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, los eventos de voz se pueden procesar:

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 evento OnSendFrame del contexto remoto. El evento se desencadena cada vez que la biblioteca de comunicación OnSendFrame remota holográfica envía el fotograma actual al dispositivo remoto. Este es el momento ideal para tomar el contenido y también para abrirlo en el escritorio o la ventana de 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 reproducción de profundidad. Esto requiere que tanto el búfer de color como el búfer de profundidad se transmita desde la aplicación remota al 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 color. 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 los valores predeterminados, se debe llamar ConfigureDepthVideoStream 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 (se agrega con la versión 2.1.3 y, si se usa, no se crea 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 tenerse en cuenta en el valor de ancho de banda máximo que proporcione a CreateRemoteContext .

Además de configurar la resolución, también tiene 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 reproducción de profundidad funciona correctamente HoloLens 2, puede habilitar un visualizador de profundidad a través del Portal de dispositivos. Consulte Verifying Depth is Set Correctly (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, vea Canales de datos personalizados.

Opcional: Sincronización del sistema de coordenadas

A partir de la versión 2.7.0,se puede usar la sincronización del sistema de coordenadas para alinear los datos espaciales entre el reproductor y la aplicación remota. Para obtener más información, vea Coordinate System Synchronization with Holographic Remoting Overview.

Consulte también