Holographic Remoting リモート アプリの作成Writing a Holographic Remoting remote app

重要

このドキュメントでは、HoloLens 2 用のリモートアプリケーションの作成について説明します。This document describes the creation of a remote application for HoloLens 2. HoloLens のリモートアプリケーション (第1世代) では、NuGet パッケージバージョン 1.x を使用する必要があります。Remote applications for HoloLens (1st gen) must use NuGet package version 1.x.x. これは、HoloLens 2 用に作成されたリモートアプリケーションが HoloLens 1 と互換性がないことを意味します。This implies that remote applications written for HoloLens 2 are not compatible with HoloLens 1 and vice versa. HoloLens 1 のドキュメントについては、 こちらを参照してください。The documentation for HoloLens 1 can be found here.

Holographic リモート処理リモートアプリを作成することにより、リモートコンピューターでレンダリングされるリモートコンテンツを HoloLens 2 にストリーミングできます。By creating a Holographic Remoting remote app, remote content that is rendered on a remote machine can be streamed to HoloLens 2. この記事では、これを実現する方法について説明します。This article describes how this can be achieved. このページのすべてのコードと作業中のプロジェクトは、 Holographic リモート処理のサンプル github リポジトリにあります。All code on this page and working projects can be found in the Holographic Remoting samples github repository.

Holographic リモート処理を使用すると、アプリは HoloLens 2 をターゲットにすることができます。これにより、デスクトップ PC または、Xbox One などの UWP デバイスでレンダリングされた Holographic コンテンツを使用して、より多くのシステムリソースにアクセスしたり、リモートの イマーシブビュー を既存のデスクトップ PC ソフトウェアに統合できるようになります。Holographic remoting allows an app to target HoloLens 2 with holographic content rendered on a desktop PC or on a UWP device such as the Xbox One, allowing access to more system resources and making it possible to integrate remote immersive views into existing desktop PC software. リモートアプリは HoloLens 2 から入力データストリームを受け取り、仮想イマーシブビューでコンテンツをレンダリングし、コンテンツフレームを HoloLens 2 にストリームバックします。A remote app receives an input data stream from HoloLens 2, renders content in a virtual immersive view, and streams content frames back to HoloLens 2. 接続は標準の Wi-fi を使用して行われます。The connection is made using standard Wi-Fi. Holographic リモート処理は、NuGet パケット経由でデスクトップまたは UWP アプリに追加されます。Holographic Remoting is added to a desktop or UWP app via a NuGet packet. 接続を処理し、イマーシブビューでレンダリングする追加のコードが必要です。Additional code is required which handles the connection and renders in an immersive view.

一般的なリモート処理接続では、待機時間が50ミリ秒に抑えられます。A typical remoting connection will have as low as 50 ms of latency. プレーヤーアプリは、リアルタイムで待機時間を報告できます。The player app can report the latency in real-time.

必須コンポーネントPrerequisites

開始点として、Windows Mixed Reality API を対象とする、動作する DirectX ベースのデスクトップまたは UWP アプリを使用することをお勧めします。A good starting point is a working DirectX based Desktop or UWP app which targets the Windows Mixed Reality API. 詳細については、「 DirectX 開発の概要」を参照してください。For details see DirectX development overview. C++ holographic プロジェクトテンプレートは、出発点として適しています。The C++ holographic project template is a good starting point.

重要

Holographic リモート処理を使用するすべてのアプリは、 マルチスレッドアパートメントを使用するように作成する必要があります。Any app using Holographic Remoting should be authored to use a multi-threaded apartment. シングルスレッドアパートメントの使用はサポートされていますが、パフォーマンスが低下し、再生中に途切れが生じる可能性があります。The use of a single-threaded apartment is supported but will lead to sub-optimal performance and possibly stuttering during playback. C++ を使用している場合、winrt winrt:: init_apartment 、マルチスレッドアパートメントが既定値です。When using C++/WinRT winrt::init_apartment a multi-threaded apartment is the default.

Holographic リモート処理 NuGet パッケージを取得するGet the Holographic Remoting NuGet package

Visual Studio で NuGet パッケージをプロジェクトに追加するには、次の手順を実行する必要があります。The following steps are required to add the NuGet package to a project in Visual Studio.

  1. Visual Studio でプロジェクトを開きます。Open the project in Visual Studio.
  2. プロジェクトノードを右クリックし、[ NuGet パッケージの管理... ] を選択します。Right-click the project node and select Manage NuGet Packages...
  3. 表示されるパネルで、[ 参照 ] をクリックし、"Holographic Remoting" を検索します。In the panel that appears, click Browse and then search for "Holographic Remoting".
  4. [ Holographic] を選択し 、最新の 2.x バージョンを選択して [ インストール] をクリックします。Select Microsoft.Holographic.Remoting, ensure to pick the latest 2.x.x version and click Install.
  5. [ プレビュー ] ダイアログが表示されたら、[ OK] をクリックします。If the Preview dialog appears, click OK.
  6. 次に表示されるダイアログは、使用許諾契約書です。The next dialog that appears is the license agreement. [ 同意 する] をクリックして、使用許諾契約書に同意します。Click on I Accept to accept the license agreement.

注意

HoloLens 1 を対象とする開発者は、NuGet パッケージ のバージョン 1.x を引き続き利用できます。Version 1.x.x of the NuGet package is still available for developers who want to target HoloLens 1. 詳細については、「 Add Holographic Remoting (HoloLens (第1世代))」を参照してください。For details see Add Holographic Remoting (HoloLens (1st gen)).

リモートコンテキストを作成するCreate the remote context

最初の手順として、アプリケーションでリモートコンテキストを作成する必要があります。As a first step the application should create a remote context.

// 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);

警告

Holographic リモート処理は、Windows の一部である Windows Mixed Reality ランタイムをリモート処理固有のランタイムに置き換えることによって機能します。Holographic Remoting works by replacing the Windows Mixed Reality runtime which is part of Windows with a remoting specific runtime. これは、リモートコンテキストの作成時に実行されます。This is done during the creation of the remote context. そのため、リモートコンテキストを作成する前に Windows Mixed Reality API を呼び出すと、予期しない動作が発生する可能性があります。For that reason any call on any Windows Mixed Reality API before creating the remote context can result in unexpected behavior. 推奨される方法は、混合の現実 API と対話する前に、できるだけ早くリモートコンテキストを作成することです。The recommended approach is to create the remote context as early as possible before interaction with any Mixed Reality API. Windows Mixed Reality API を使用して作成または取得したオブジェクトを、後で作成または取得したオブジェクトと共に使用することは避けてください。Never mix objects created or retrieved through any Windows Mixed Reality API before the call to CreateRemoteContext with objects created or retrieved afterwards.

次に、holographic 領域を作成する必要があります。Next the holographic space needs to be created. CoreWindow の指定は必要ありません。Specifying a CoreWindow is not required. CoreWindow を持たないデスクトップアプリは、のみを渡すことができ nullptr ます。Desktop apps that do not have a CoreWindow can just pass a nullptr.

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

デバイスへの接続Connect to the device

リモートアプリがコンテンツを表示できる状態になったら、デバイスへの接続を確立できます。Once the remote app is ready for rendering content a connection to the device can be established.

接続は、次の2つの方法のいずれかで実行できます。Connection can be done in one of two ways.

  1. リモートアプリは、デバイスで実行されているプレーヤーに接続します。The remote app connects to the player running on the device.
  2. デバイスで実行されているプレーヤーは、リモートアプリに接続します。The player running on the device connects to the remote app.

リモートアプリから HoloLens 2 への接続を確立するには、 Connect リモートコンテキストでホスト名とポートを指定してメソッドを呼び出します。To establish a connection from the remote app to HoloLens 2 call the Connect method on the remote context specifying the hostname and port. Holographic リモート処理プレーヤーによって使用されるポートは 8265 です。The port used by the Holographic Remoting Player is 8265.

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

重要

C++/WinRT API と同様に、 Connect 処理する必要がある winrt:: hresult_error をスローすることがあります。As with any C++/WinRT API Connect might throw an winrt::hresult_error which needs to be handled.

ヒント

C++/WinRT言語のプロジェクションを使用しないようにするには、 build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h Holographic リモート処理 NuGet パッケージ内にあるファイルを含めることができます。To avoid using C++/WinRT language projection the file build\native\include\<windows sdk version>\abi\Microsoft.Holographic.AppRemoting.h located inside the Holographic Remoting NuGet package can be included. これには、基になる COM インターフェイスの宣言が含まれます。It contains declarations of the underlying COM interfaces. ただし、C++/WinRT を使用することをお勧めします。The use of C++/WinRT is recommended though.

リモートアプリでの着信接続のリッスンは、メソッドを呼び出すことによって行うことができ Listen ます。Listening for incoming connections on the remote app can be done by calling the Listen method. この呼び出しでは、ハンドシェイクポートとトランスポートポートの両方を指定できます。Both the handshake port and transport port can be specified during this call. ハンドシェイクポートは、初期ハンドシェイクに使用されます。The handshake port is used for the initial handshake. データは、トランスポートポートを介して送信されます。The data is then send over the transport port. 既定では、 8265 および 8266 が使用されます。By default 8265 and 8266 are used.

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());
}

重要

build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idlNuGet パッケージ内には、Holographic Remoting によって公開される API に関する詳細なドキュメントが含まれています。The build\native\include\HolographicAppRemoting\Microsoft.Holographic.AppRemoting.idl inside the NuGet package contains detailed documentation for the API exposed by Holographic Remoting.

リモート処理固有のイベントの処理Handling Remoting specific events

リモートコンテキストは、接続の状態を監視するために重要な3つのイベントを公開します。The remote context exposes three events which are important to monitor the state of a connection.

  1. OnConnected: デバイスへの接続が正常に確立されたときにトリガーされます。OnConnected: Triggered when a connection to the device has been successfully established.
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: 確立された接続が閉じられた場合、または接続を確立できなかった場合にトリガーされます。OnDisconnected: Triggered if an established connection is closed or a connection could not be established.
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: 受信接続のリッスンが開始されます。OnListening: When listening for incoming connections starts.
m_onListeningEventRevoker = m_remoteContext.OnListening(winrt::auto_revoke, [this, remoteContextWeakRef]() {
    if (auto remoteContext = remoteContextWeakRef.get())
    {
        // Update UI state
    }
});

さらに、リモートコンテキストのプロパティを使用して接続状態を照会することもでき ConnectionState ます。Additionally the connection state can be queried using the ConnectionState property on the remote context.

auto connectionState = m_remoteContext.ConnectionState();

音声イベントの処理Handling speech events

リモート音声インターフェイスを使用すると、音声トリガーを HoloLens 2 に登録し、リモートアプリケーションにリモート処理することができます。Using the remote speech interface it is possible to register speech triggers with HoloLens 2 and have them remoted to the remote application.

この追加のメンバーは、リモート音声の状態を追跡するために必要です。This additional member is required to track the state of the remote speech.

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

まず、リモート音声インターフェイスを取得する必要があります。First the remote speech interface needs to be retrieved.

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

非同期ヘルパーメソッドを使用すると、リモート音声を初期化できます。Using an asynchronous helper method you can then initialize the remote speech. 初期化にはかなりの時間がかかる場合があるため、非同期的に実行する必要があります。This should be done asynchronously as initializing might take a considerable amount of time. C++ での同時実行と非同期操作c++/winrtで非同期関数を作成する方法について説明します。Concurrency and asynchronous operations with C++/WinRT explains how to author asynchronous functions with 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);
}

認識する語句を指定する方法は2つあります。There are two ways of specifying phrases to be recognized.

  1. 音声文法 xml ファイル内の仕様。Specification inside a speech grammar xml file. 詳細について は、「基本的な XML 文法を作成する方法 」を参照してください。See How to create a basic XML Grammar for details.
  2. を指定するには、をディクショナリベクター内に渡し ApplyParameters ます。Specify by passing them inside the dictionary vector to ApplyParameters.

On認識された音声コールバック内で、音声イベントを処理できます。Inside the OnRecognizedSpeech callback the speech events can then be processed:

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")
    {
        ...
    }

    ...
}

ストリーミングされるコンテンツをローカルでプレビューするPreview streamed content locally

デバイスに送信されるリモートアプリで同じコンテンツを表示するには、 OnSendFrame リモートコンテキストのイベントを使用できます。To display the same content in the remote app that is send to the device the OnSendFrame event of the remote context can be used. OnSendFrameイベントは、Holographic リモート処理ライブラリが現在のフレームをリモートデバイスに送信するたびにトリガーされます。The OnSendFrame event is triggered every time the Holographic Remoting library sends the current frame to the remote device. これは、コンテンツを撮影し、デスクトップや UWP ウィンドウに array.blit するのに最適な時間です。This is the ideal time to take the content and also blit it into the desktop or UWP window.

#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.
    });

深さの再投影Depth Reprojection

バージョン 2.1.0以降では、Holographic Remoting は 深さの reprojectionをサポートしています。Starting with version 2.1.0, Holographic Remoting supports Depth Reprojection. そのためには、カラーバッファーに加えて、リモートアプリケーションから HoloLens 2 に深度バッファーをストリーム転送する必要があります。This requires, in addition to the color buffer, to also stream the depth buffer from the Remote application to the HoloLens 2. 既定では、深度バッファーストリーミングが有効になり、カラーバッファーの半分の解像度を使用するように構成されます。By default depth buffer streaming is enabled and configured to use half the resolution of the color buffer. これは、次のように変更できます。This can be changed as follows:

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

...

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

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

ConfigureDepthVideoStreamHoloLens 2 への接続を確立する前に、既定値を使用しないようにする必要があることに注意してください。Note, if default values should not be used ConfigureDepthVideoStream must be called before establishing a connection to the HoloLens 2. 最適な場所は、リモートコンテキストを作成した直後です。The best place is right after you have created the remote context. DepthBufferStreamResolution に指定できる値は次のとおりです。Possible values for DepthBufferStreamResolution are:

  • Full_ResolutionFull_Resolution
  • Half_ResolutionHalf_Resolution
  • Quarter_ResolutionQuarter_Resolution
  • 無効 (バージョン 2.1.3 を使用して追加され、追加の深度ビデオストリームが作成されない場合)Disabled (added with version 2.1.3 and if used no additional depth video stream is created)

完全な解像度の深度バッファーを使用することは、帯域幅の要件にも影響し、指定した最大帯域幅の値について考慮する必要があることに注意してください CreateRemoteContextKeep in mind that using a full resolution depth buffer also affects bandwidth requirements and needs to be accounted for in the maximum bandwidth value you provide to CreateRemoteContext.

解決を構成すると、HolographicCameraRenderingParameters を使用して深度バッファーをコミットする必要もあります 。 CommitDirect3D11DepthBufferを使用します。Beside configuring the resolution you also have to commit a depth buffer via 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);
                }
            });
        }
    });
}

HoloLens 2 で深度の再プロジェクションが正しく動作しているかどうかを確認するには、デバイスポータルを使用して深度ビジュアライザーを有効にします。To verify if depth reprojection is correctly working on HoloLens 2 you can enable a depth visualizer via the Device Portal. 詳細については、「 奥行が正しく設定されている ことを確認していますSee Verifying Depth is Set Correctly for details.

省略可能: カスタムデータチャネルOptional: Custom data channels

カスタムデータチャネルは、既に確立されているリモート処理接続を介してユーザーデータを送信するために使用できます。Custom data channels can be used to send user data over the already established remoting connection. 詳細については、「 カスタムデータチャネル 」を参照してください。See custom data channels for more information.

参照See Also