MRが楽しい

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

MediaCaptureを使ってHoloLens2でスクリプトからホログラムを合成したビデオ録画を行う

本日は HoloLens2 の技術調査枠です。
MediaCaptureを使ってHoloLens2でスクリプトからホログラムを合成したビデオ録画を行う手順を記事にします。
f:id:bluebirdofoz:20210819022736j:plain

MediaCapture

UWP アプリでは MediaCapture クラスを使ってカメラデバイスから写真やビデオのキャプチャを実行することができます。
docs.microsoft.com

HoloLens でのキャプチャ時に、アプリケーションの仮想ホログラムやオーディオをビデオに含めたい場合は追加で MRC 効果の設定を行う必要があります。
docs.microsoft.com
f:id:bluebirdofoz:20210819022949j:plain

本記事ではこの MediaCapture クラスを使ってHoloLens2でホログラムを合成したビデオ録画を行ってみます。

VideoCaptureによる録画

VideoCapture クラスを使ってもビデオ録画を行うことができます。
詳細は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

ただし、VideoCapture の showHolograms オプションは Unity 2020 以降では現在、無効化されています。
docs.unity3d.com

このため、Unity 2020 以降の環境でホログラムの合成を行いたい場合は本記事の MediaCapture を利用してください。

実行例

実際に MediaCapture を用いて録画を実行したサンプルを記述します。

サンプルシーン

動作確認のため、以下のサンプルシーンを作成しました。
ホログラムが同時に録画されていることを確認するため、Cube オブジェクトを配置しています。
f:id:bluebirdofoz:20210819023156j:plain

サンプルコード

録画を行うための以下のスクリプトを作成しました。
StartRecord 関数で録画の開始、StopRecord 関数で録画を停止します。
・HologramMediaCapture.cs

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

using System;
using System.IO;

public class HologramMediaCapture : MonoBehaviour
{
#if WINDOWS_UWP
    /// <summary>
    /// Mediaキャプチャの参照
    /// </summary>
    Windows.Media.Capture.MediaCapture p_MediaCapture;

    /// <summary>
    /// Mediaレコーダの参照
    /// </summary>
    Windows.Media.Capture.LowLagMediaRecording p_MediaRecording;
#endif

    /// <summary>
    /// 初期化完了フラグ
    /// </summary>
    bool isInitialized;

    /// <summary>
    /// 録画実行中フラグ
    /// </summary>
    bool isRecording;

    // Start is called before the first frame update
    void Start()
    {
        // 初期化完了フラグをOFF
        isInitialized = false;

        // Mediaキャプチャの初期化
        MediaInitialize();
    }

    // Update is called once per frame
    void Update()
    {
    }

    /// <summary>
    /// 動画記録の開始
    /// </summary>
    public void StartRecord()
    {
        StartRecordAsync();
    }

    /// <summary>
    /// 動画記録の停止
    /// </summary>
    public void StopRecord()
    {
        StopRecordAsync();
    }

    /// <summary>
    /// Mediaキャプチャの初期化
    /// </summary>
    private async void MediaInitialize()
    {
        Debug.Log("MediaInitialize");

#if WINDOWS_UWP
        // MRC 効果の定義
        Windows.Media.Effects.IVideoEffectDefinition define = new MrcVideoEffectDefinition();
        Windows.Media.Capture.MediaStreamType type = Windows.Media.Capture.MediaStreamType.VideoRecord;

        // Mediaキャプチャの初期化
        p_MediaCapture = new Windows.Media.Capture.MediaCapture();
        await p_MediaCapture.InitializeAsync();
        await p_MediaCapture.AddVideoEffectAsync(define, type);

        p_MediaCapture.Failed += MediaCapture_Failed;
        p_MediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;
#endif

        // 初期化完了フラグをON
        isInitialized = true;
    }

#if WINDOWS_UWP
    /// <summary>
    /// Mediaキャプチャ失敗時の呼び出し関数
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="errorEventArgs"></param>
    private void MediaCapture_Failed(
        Windows.Media.Capture.MediaCapture sender, Windows.Media.Capture.MediaCaptureFailedEventArgs errorEventArgs)
    {
        Debug.LogError("MediaCapture_Failed");
    }

    /// <summary>
    /// 録画上限時の呼び出し関数
    /// </summary>
    /// <param name="sender"></param>
    private void MediaCapture_RecordLimitationExceeded(
        Windows.Media.Capture.MediaCapture sender)
    {
        Debug.LogError("MediaCapture_RecordLimitationExceeded");

        StopRecordAsync();
    }
#endif

    /// <summary>
    /// レコードの開始
    /// </summary>
    private async void StartRecordAsync()
    {
        Debug.Log("StartRecordAsync");

        // 初期化未了時は処理しない
        if (!isInitialized) return;

        // 録画実行中は処理しない
        if (isRecording) return;

        // 録画実行中フラグをON
        isRecording = true;

        // タイムスタンプを元にファイル名を作成する
        string timeStamp = Time.time.ToString().Replace(".", "").Replace(":", "");
        string fileName = string.Format("Video_{0}.mp4", timeStamp);

#if WINDOWS_UWP
        // 保存先のファイル設定
        Windows.Storage.StorageLibrary myVideos =
            await Windows.Storage.StorageLibrary.GetLibraryAsync(Windows.Storage.KnownLibraryId.Videos);
        Windows.Storage.StorageFile file =
            await myVideos.SaveFolder.CreateFileAsync(fileName, Windows.Storage.CreationCollisionOption.GenerateUniqueName);

        // 録画フォーマットの設定(LowLagMediaRecordingセッションの開始)
        p_MediaRecording = await p_MediaCapture.PrepareLowLagRecordToStorageFileAsync(
            Windows.Media.MediaProperties.MediaEncodingProfile.CreateMp4(
                Windows.Media.MediaProperties.VideoEncodingQuality.Auto),
            file);

        // 録画の開始
        await p_MediaRecording.StartAsync();
#endif
    }

    /// <summary>
    /// レコードの停止
    /// </summary>
    private async void StopRecordAsync()
    {
        Debug.Log("StopRecordAsync");

#if WINDOWS_UWP
        // 録画の停止
        await p_MediaRecording.StopAsync();

        // LowLagMediaRecordingセッションの停止
        await p_MediaRecording.FinishAsync();
#endif

        // 録画実行中フラグをOFF
        isRecording = false;
    }
}

f:id:bluebirdofoz:20210819023212j:plain

また MRC ビデオ効果の定義クラスを以下の通り作成しました。
・MrcVideoEffectDefinition.cs

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

#if WINDOWS_UWP
public class MrcVideoEffectDefinition : Windows.Media.Effects.IVideoEffectDefinition
{
    public string ActivatableClassId
    {
        get
        {
            return "Windows.Media.MixedRealityCapture.MixedRealityCaptureVideoEffect";
        }
    }

    public Windows.Foundation.Collections.IPropertySet Properties
    {
        get
        {
            return _Properties;
        }
    }

    private Windows.Foundation.Collections.IPropertySet _Properties;

    public MrcVideoEffectDefinition()
    {
        _Properties = (Windows.Foundation.Collections.IPropertySet)new Windows.Foundation.Collections.PropertySet();
        _Properties.Add("HologramCompositionEnabled", true);  // ビデオ キャプチャでホログラムを有効または無効にするフラグ
        _Properties.Add("RecordingIndicatorEnabled", false);  // ホログラムキャプチャ中に画面の記録インジケーターを有効または無効にするフラグ
        _Properties.Add("VideoStabilizationEnabled", false);  // HoloLens トラッカーを利用したビデオ安定化を有効または無効にするフラグ
        _Properties.Add("VideoStabilizationBufferLength", 0); // ビデオの安定化に使用される履歴フレームの数
        _Properties.Add("GlobalOpacityCoefficient", 0.9f);    // ホログラムのグローバル不透明度係数:0.0 (透明) ~ 1.0 (不透明)
    }
}
#endif

f:id:bluebirdofoz:20210819023225j:plain

作成したスクリプトを適当なゲームオブジェクトにアタッチします。
f:id:bluebirdofoz:20210819023236j:plain

ボタンの配置

ボタンオブジェクトを利用するため、Unity プロジェクトに MRTK をインポートします。
MRTK のインポート手順は以下の記事などを参考にしてください。
bluebirdofoz.hatenablog.com

シーンにトグルボタンを配置し、開始/停止関数をイベントに割り当てました。
f:id:bluebirdofoz:20210819023253j:plain

Capabilitiesの設定

録画を行うため、カメラの権限許可を行います。
メニューから[Edit -> Project Settings..]を開き、[Player]タブの[Publishing Settings]を開きます。
[Capabilities]の項目で[WebCam]にチェックを入れます。
また、今回はスクリプトで録画ファイルの保存先にビデオライブラリを指定しているので、[VideoLibrary]にもチェックを入れておきます。
f:id:bluebirdofoz:20210819023304j:plain

ホログラムの位置合わせ

デフォルト設定でカメラ映像とホログラムの合成を行うと、カメラ位置のズレにより、動画内でホログラムの位置ズレが発生します。
以下の設定を行うことで、ホログラムの位置ズレ修正することができます。
1. [Hierarchy]から[MixedRealityToolkit]オブジェクトを選択し、Inspector ビューを開きます。
2. MRTK のプロファイルから[Camera -> Camera Settings Providers -> Windows Mixed Reality Camera Settings]を開きます。
3. [Render from PV Camera]のチェックを入れます。
f:id:bluebirdofoz:20210819023317j:plain

HoloLens2での動作確認

アプリを HoloLens2 にインストールして動作を確認します。
[Toggle]ボタンをクリックすると録画が開始/停止されます。
f:id:bluebirdofoz:20210819023326j:plain

録画したファイルは HoloLens の Video フォルダに保存されます。
DevicePortal のファイルエクスプローラからダウンロード可能です。
f:id:bluebirdofoz:20210819023337j:plain

ダウンロードした動画を確認すると、ホログラムが映った動画が撮影できています
f:id:bluebirdofoz:20210819023347j:plain

スペシャルサンクス

VideoCapture が Unity 2020 で動作しなかったとき、本代替案を教えてくれた方。
・たるこす氏
tarukosu.hatenablog.com