Version: 2019.1
Google VR 機能の概要
Google VR のリソースとトラブルシューティング

Unity の Google VR ビデオ非同期投影

ビデオ非同期投影 (Video Async Reprojection) とは何ですか?

ビデオ非同期投影はレイヤー (外部サーフェス と呼ばれます) で、アプリケーションが非同期再投影システムにビデオフレームを直接供給することができます。API を使用する主な利点は以下のとおりです。

  1. API を使用しない場合、ビデオは 1 度サンプリングされてアプリケーションの色のバッファにレンダリングされます。その後、色のバッファが再度サンプリングされて、歪み補正が行われます。これにより、ダブルサンプリングアーティファクトが発生します。外部のサーフェスはビデオを直接 EDS コンポジッターに渡します。サンプリングが 1 度だけのため、ビデオの視覚的な品質が向上します。

  2. 外部サーフェス API を使用する場合、ビデオフレームレートはアプリケーションのフレームレートから切り離されます。新しいフレームをレンダリングするのに 1 秒かかり、頭を動かすときに黒いバーが表示されるだけで、ビデオは正常に再生されます。これにより、ビデオフレームが欠けることが大幅に減り、AV 同期を維持します。

  3. アプリケーションは、DRM ビデオの再生をマークすることができ、API は保護されたビデオを表示し、非同期投影のフレームレートを維持する保護されたパスを作成します。

既知の問題

  1. ビデオ非同期投影を使用する場合、カメラは原点 (0、0、0) から始める必要があります。カメラの位置が 0,0,0 に設定されていない場合は、エラーが発生することがあります。

  2. 非同期投影のための、public にアクセス可能な C# インターフェースはありません。public の API は Java のみです。

ビデオ非同期投影を有効にする

ビデオ非同期投影は Daydream VR デバイス設定の一部です。

Virtual Reality SDKs リストに Daydream を加える
Virtual Reality SDKs リストに Daydream を加える

Daydream の横にある灰色の矢印をクリックし、Enable Video Surface チェックボックスをチェックしてビデオ非同期投影機能を有効にします。

Virtual Reality SDK リストの Daydream 設定
Virtual Reality SDK リストの Daydream 設定

すべてのコンテンツに対しメモリ保護する必要がある場合のみ Use Protected Memory オプションを選択してください。これを有効にすると、アプリケーションの生存期間中有効になるためです。

Enable Video Surface チェックボックスを有効にしてビデオの非同期再投影を有効にします
Enable Video Surface チェックボックスを有効にしてビデオの非同期再投影を有効にします

API ドキュメント

Google VR API を利用するには、UnityPlayerActivity を拡張する必要があります。詳細は、UnityPlayerActivity の拡張 を参照してください。

Java プラグインはシーン内のオブジェクトに直接アクセスすることはできません。そのため、C# コードに簡単な API を提供する必要があります。この API を使用すると、トランスフォームを Java 側に渡すことができ、レンダリングを開始するタイミングを Java コードに伝えることができます。

注意 このコードは完全ではありません。クライアント固有の実装の詳細なので、ビデオプレイヤーの実装は含まれていません。また、再生コントロールもありません。再生コントロールはシーン内のオブジェクトとして実装する必要があり、それらのオブジェクトのアクションは Java で呼び出す必要があります。

Unity で Java を使用し UnityPlayerActivity を拡張する方法については、Unity の Android 開発 を参照してください。

Google のビデオ非同期投影システムの詳細は、Android デベロッパーネットワークのドキュメント Video Viewports を参照してください。

Java のサンプルコード

package com.unity3d.samplevideoplayer;

import com.unity3d.player.GoogleVrVideo;

import com.unity3d.player.GoogleVrApi;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.Surface;

public class GoogleAVRPlayer implements GoogleVrVideo.GoogleVrVideoCallbacks {

    private static final String TAG = GoogleAVRPlayer.class.getSimpleName();

    private MyOwnVideoPlayer videoPlayer;

    private boolean canPlayVideo = false;

    private boolean isSceneLoaded = false;

    // ビデオシステムの初期化を処理するため、

    // C# コードに提供する API 

    public void initVideoPlayer(UnityPlayerActivity activity) {

     // ビデオプレイヤーと必要なサポートを初期化…

     // このインスタンスを Google Vr ビデオリスナーとして登録し、

     // 生存期間と制御コールバックを取得します

         GoogleVrVideo gvrv = GoogleVrApi.getGoogleVrVideo();

         if (gvrv != null) gvrv.registerGoogleVrVideoListener(this); 

    }

 

    // ビデオシステムを開始し、ビデオを再生するために、 

    // C# コードに提供する API

    public void play()

    {

           if (canPlayVideo && videoPlayer != null && videoPlayer.isPaused())

            videoPlayer.play();

    }

    // ビデオシステムにビデオを止めさせるために 

    // C# コードに提供する API

    public void pause()

    {

            if (canPlayVideo && videoPlayer != null && !videoPlayer.isPaused())

            videoPlayer.pause();        

    }

    // Google Vr ビデオリスナー

    @Override

    public void onSurfaceAvailable(Surface surface) {

     // Google Vr にはレンダリングするサーフェスがあります

     // 必要に応じて、ビデオプレイヤーでこのサーフェスを使います

         if (videoPlayer != null){

            videoPlayer.setSurface(surface);

            canPlayVideo = true;

            if (isSceneLoaded)

            {

                videoPlayer.play();

            }

          }

    }

    @Override

    public void onSurfaceUnavailable() {

     //  Google Vr ビデオサーフェスは遠のきます。

     // それを持つものからビデオサーフェスを削除し、ビデオプレイヤーを停止します

        if (videoPlayer != null){

            videoPlayer.pause();

            canPlayVideo = false;

          }

     }

    @Override

    public void onFrameAvailable() {

     //  Google Vr フレームの使用可能なコールバックを処理

    }
}

Unity C# サンプルコード

using System;

using System.Collections;

using System.Collections.Generic;

using System.Text;

using UnityEngine;

public class GoogleVRVideo : MonoBehaviour {

 private AndroidJavaObject googleAvrPlayer = null;

 private AndroidJavaObject googleVrVideo = null;

 void Awake()

 {

    if (googleAvrPlayer == null)

    {

      googleAvrPlayer = new AndroidJavaObject("com.unity3d.samplevideoplayer.GoogleAVRPlayer");

    }

    AndroidJavaObject googleVrApi = new AndroidJavaClass("com.unity3d.player.GoogleVrApi");

    if (googleVrApi != null) googleVrVideo = googleVrApi.CallStatic<AndroidJavaObject>("getGoogleVrVideo");

 }

 void Start()

 {

  if (googleVrVideo != null)

  {

   //Google VR にワールド空間のビデオサーフェスを教える必要があります。

   // Java からその情報を取得する方法がないため、

   // ここでそれを行い、次に計算した行列を 

   // UnityPlayerActivity サブクラスでアクセス可能な API に渡します。

   Matrix4x4 wm = transform.localToWorldMatrix;
   
   wm = Camera.main.transform.parent.worldToLocalMatrix * wm;
   
   wm = wm * Matrix4x4.Scale(new Vector3(0.5f, 0.5f, 1));


   // 4x4 行順の行列を 16 要素の列順の平らな行列に変換します。

   // この置換は、行列が Google の使用できる順番に

   // するために行われ、

   // JNI 範囲を簡単に渡せるようにそれを平らにします。

   // 難しいのは、Java 側で 4x4 行列に再度変換して戻す必要があることです。

   float[] matrix = new float[16];

   for (int i = 0; i < 4; i++)

   {

    for (int j = 0; j < 4; j++)

    {

     matrix[i * 4 + j] = wm[j,i];

    }

   }

   googleVrVideo.Call("setVideoLocationTransform", matrix);  

  }

  

  if (googleAvrPlayer != null)

  {

    AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

    AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");

    googleAvrPlayer.Call("initVideoPlayer", jo);

    googleAvrPlayer.Call("play");

  }

 }

}


Google VR 機能の概要
Google VR のリソースとトラブルシューティング