MRが楽しい

MRやVRについて学習したことを書き残す

HoloLens2のアプリでキャプチャの実行イベントを取得する

本日は HoloLens2 の技術調査枠です。
HoloLens2 のアプリでキャプチャの実行イベントを取得する方法を記事にします。
f:id:bluebirdofoz:20211214213301j:plain

AppCapture

HoloLens2 のアプリでキャプチャの実行状態を参照するには AppCapture クラスを利用します。
docs.microsoft.com

本クラスの CapturingChanged イベントをキャッチすることでキャプチャの開始、終了を検知することも可能です。
docs.microsoft.com

AppCapture appCapture = AppCapture.GetForCurrentView();
appCapture.CapturingChanged += AppCapture_CapturingChanged;

...

private void AppCapture_CapturingChanged(AppCapture sender, object args)
{
    ...
}

サンプルプロジェクトの作成

MRTK をインポートしたサンプルプロジェクトを作成します。

MRTK のインポートとサンプルシーンの作成

MRTK のインポートと HoloLens 向けプロジェクトの基本設定を行い、サンプルプロジェクトを作成します。
手順の詳細は以下の記事を参照してください。
bluebirdofoz.hatenablog.com

MRTKの基本設定を行ったうえで以下のUIパネルを配置したシーンを準備しました。
f:id:bluebirdofoz:20211214213341j:plain

サンプルスクリプトの作成

キャプチャの開始、終了イベントを検知して登録された UnityEvent を実行する以下のサンプルスクリプトを作成しました。
・AppCaptureEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

#if WINDOWS_UWP
using Windows.Media.Capture;
#endif

public class AppCaptureEvent : MonoBehaviour
{
    [SerializeField, Tooltip("キャプチャ開始イベント")]
    private UnityEvent CaptureStartEvent;

    [SerializeField, Tooltip("キャプチャ終了イベント")]
    private UnityEvent CaptureEndEvent;

    [SerializeField, Tooltip("エラー発生時イベント")]
    private UnityEvent CaptureErrorEvent;

    /// <summary>
    /// 現在のキャプチャ実行状態
    /// </summary>
    public bool CurrentCapturingVideo => p_CurrentCapturingVideo;
    private bool p_CurrentCapturingVideo = false;

    void Start()
    {
        // AppCaptureの参照を取得できない場合はエラー呼び出し
        bool EnableAppCapture = StartAppCapture();
        if (!EnableAppCapture) CallCaptureErrorEvent();
    }

    void OnDestroy()
    {
        EndAppCapture();
    }

    private void Update()
    {
        CheckAppCapture();
    }

    /// <summary>
    /// キャプチャ開始時処理
    /// </summary>
    private void CallCaptureStartEvent()
    {
        CaptureStartEvent.Invoke();
        p_CurrentCapturingVideo = true;
    }

    /// <summary>
    /// キャプチャ終了時処理
    /// </summary>
    private void CallCaptureEndEvent()
    {
        CaptureEndEvent.Invoke();
        p_CurrentCapturingVideo = false;
    }

    /// <summary>
    /// キャプチャ情報取得エラー時処理
    /// </summary>
    private void CallCaptureErrorEvent() => CaptureErrorEvent.Invoke();

    #region "環境依存の処理"
#if WINDOWS_UWP
    /// <summary>
    /// CapturingChangedアクションデータ
    /// </summary>
    private class CapturingChangedEventData
    {
        public AppCapture Sender;
        public object Args;

        public CapturingChangedEventData(AppCapture sender, object args)
        {
            this.Sender = sender;
            this.Args = args;
        }
    }

    /// <summary>
    /// 発生キャプチャイベントのキュー
    /// </summary>
    private Queue<CapturingChangedEventData> p_PendingCapturingChangedEvent = new Queue<CapturingChangedEventData>();

    /// <summary>
    /// キャプチャインスタンス
    /// </summary>
    private AppCapture p_AppCapture;

    private bool StartAppCapture()
    {
        p_AppCapture = AppCapture.GetForCurrentView();
        if (p_AppCapture == null) return false;

        p_AppCapture.CapturingChanged += AppCapture_CapturingChanged;
        return true;
    }

    private bool EndAppCapture()
    {
        if (p_AppCapture == null) return false;

        p_AppCapture.CapturingChanged -= AppCapture_CapturingChanged;
        return true;
    }

    private bool CheckAppCapture()
    {
        // キューのアクションを全て実行する
        lock (p_PendingCapturingChangedEvent)
        {
            while (p_PendingCapturingChangedEvent.Count > 0)
            {
                var capturingChangedEvent = p_PendingCapturingChangedEvent.Dequeue();

                if (capturingChangedEvent.Sender.IsCapturingVideo)
                {
                    CallCaptureStartEvent();
                }
                else
                {
                    CallCaptureEndEvent();
                }
            }
        }
        return true;
    }

    private void AppCapture_CapturingChanged(AppCapture sender, object args)
    {
        // データのみ取得してイベント呼び出しはメインスレッド(Update)で行う
        p_PendingCapturingChangedEvent.Enqueue(new CapturingChangedEventData(sender, args));
    }
#else
    private bool StartAppCapture() => false;
    private bool EndAppCapture() => false;
    private bool CheckAppCapture() => false;
#endif
    #endregion
}

シーン内の適当なオブジェクトにスクリプトを設定します。
キャプチャ開始時(CaptureStartEvent)に"CaptureStart"とUIに表示するイベント、キャプチャ終了時(CaptureEndEvent)に"CaptureEnd"とUIに表示するイベントを登録しました。
f:id:bluebirdofoz:20211214213357j:plain

動作確認

アプリケーションを HoloLens2 にデプロイして動作を確認します。
HoloLens2 の音量+/-ボタン同時押しをするなどしてキャプチャイベントを起こすと"CaptureStart"の文字が表示されます。
f:id:bluebirdofoz:20211214213405j:plain

この文字はキャプチャが完了するとともに終了イベントが発生し、"CaptureEnd"の文字に変わります。