#ChannelPlayer · Go · Unity

ALVR導入と#ChannelPlayer

#ChannelPlayerのOculus Go版の開発において、Webdavを使えるようにしてサーバーの動画ファイルアクセスの実装を進めていたけれども、.Netのhttpアクセスは、UnityのAndroid向けbuildでは現在サポートされておらず、こちら一旦保留にしました。

そのかわりALVRの存在を知り、#ChannelPlayerのSteamVR版を試しに作ったところ、すんなりGoで実行できることが判明しました。これだとGoの使いにくいコントローラーを使わずに、PC側でX-Boxのコントローラーを使うことが可能なのでより利便性は高いかなと思います。もっともGoがすぐ熱くなるので、映像をずっとデコードするというのは重いのかなとも思います。

広告
#ChannelPlayer · Go · Unity

WebDav対応(2)

Oculud Go版の#ChannelPlayer(まだ公開していない)から、WebDavのコンテンツをソースコード上で指定して再生することは出来ている。問題は、BASIC認証を通すことができない。以下のAVProVideoに関する質疑応答では、AndroidでExoPlayerを使用している場合にはCustom httpヘッダーを付けることが出来るという現在進行形の話題があるが、どうもhttp接続ではなく、rtsp接続の場合じゃないのだろうかと思う。

Request video url with header · Issue #87 RenderHeads/UnityPlugin-AVProVideo https://github.com/RenderHeads/UnityPlugin-AVProVideo/issues/87

BASIC認証はあきらめ、とりあえずファイル一覧を取得する方を進めようと思う。

#ChannelPlayer · Go · Unity · VRソフト

WebDav対応を考える

#ChannelPlayerのOculus Go版では、内蔵ストレージ容量が小さいので、外部のサーバーへのアクセスが必要となる。他のプレイヤーアプリでは、DLNAクライアントだったり、SMB/Cifs対応していたりするが、いずれも実装コストが高い。

DLNAの方はあまり調べていないが、SMBの方を実装しようと当初考えていた。SMBの実装には、smbjやJCIFS などがあるが、いずれもjarなので、Unityのソースに組み込むには、JAR プラグインを使う必要があり、まずそこに課題があった。またうまくコードに組み込んだとしても、デコーダーであるAVPro Video Playerで、どのように読み込ませるかも課題であった。例えばYoutube API Assetでは、Youtube APIによるYoutubeサイトに対しコンテンツをアクセスし、それをUnityのPlayerやAVPro Video Playerに渡すことが出来るので、そのようなことを、SMBサーバーに対してアクセスして、動画ファイルのデーターをVideo Playerに渡すような実装を考えれば実現できるかもと考えていた。

しかし、それは少し面倒そうだし、性能が出るかどうかも怪しい。そこでWebDavはどうだろうかと調査してみた。WebDavは、Windows 10 Home版でもIISを組み込めばサーバーを立てることが可能だし、その他Linuxサーバーでも実現可能である。またクライアントも概ねWebアクセスと同じと考え、ソースコードを自前でゴリゴリ書くか、WebDav Asset(まだbuildできないので、コメントを先日投稿した。)を導入しても良い。

ここで、WebDavサーバーを立て、動画コンテンツを置いたときに、示されるURLをAVPro VideoPlayerにファイルパスではなく、URLを記述して再生が可能か試してみた。

結論は良好である。PlayerがURLで示された動画ファイルの再生に対応しているから、あとはWebDavサーバーにログインして動画コンテンツへのURLを取得すれば、再生アプリとして外部サーバーアクセス出来ることになる。

#ChannelPlayer · FOVE · Rift · Unity · VRソフト

#ChannelPlayer 1.10リリース

#ChannelPlayerのFOVE0 & Rift版の新しいバージョンを公開しました。変更履歴は以下となります。
ソフトウェアのページからサンプル版のダウンロードや、製品版の御購入が可能です。

改版履歴
2019-06-17 1.10
・変更
 動画再生時のフォーマットのデフォルトを 180, LRから2D, Monoに変更しました。
・修正
 2Dのサムネイル撮影時のカメラ位置を調整しました。なおカメラの向きのまま撮影されるため、トラッキングオフ時が良い場合があります。

#ChannelPlayer · Go · VRソフト

#ChannelPlayer Oculus Go対応

Oculus Go上でも、#ChannelPlayer 1.08相当では動いていますが、1.09のFishEye180対応を入れていないことや、Oculus Goのコントローラーでは、ほとんどまともに操作できないので、他のアプリのように小さいところをレーザーポインターでクリックするようなUIを不本意ながら実装するなど進めるか思案中です。不本意というのは、テレビのリモコンのように、どこに向けても、その位置には関係なく操作したいという思いがあります。またストレージ容量が小さいので、ローカルファイルのファイル再生機能だけでは訴求力が足りないこともリリース阻害要因のひとつです。それを解決するために、SAMBA対応にトライする予定です。

#ChannelPlayer · Unity · VRソフト

FishEye180 Side by Side対応

昨日リリースした#ChannelPlayer 1.09では、VR180において、Equirectangular180 Side By Sideとは異なる形式であるFishEye180 Side by Side対応を以下のShaderソースコードを移植して対応しました。

Unity Asset Store https://assetstore.unity.com/packages/tools/utilities/panorama180-render-141234
Panorama180 Render FT-LAB
上のAsset Storeのものか、あるいは以下のgithubのShaderコードを参考にしています。
https://github.com/ft-lab/Unity_Panorama180View/blob/master/README_jp.md
MIT License. Copyright (c) 2019 ft-lab.
https://github.com/ft-lab/Unity_Panorama180View/blob/master/Panorama180View/Assets/Panorama180View/Panorama180View/Resources/Shaders/panoramaSphereRendering.shader

AVPro Video Assetで言うところのHIGH_QUALITYと、scaleOffsetによる補正が実現できていないので以下のコードでは、FishEye180時にそれらは機能していませんが、一応絵は出せるようなったので、1.09では、一旦以下のコードでフリーズとしました。AVProのShaderに明るい方で、こうすれば良いよという御助言が頂ければ幸いです。

AVProVideo-VR-InsideSphere-Transparent.shader

Shader "AVProVideo/VR/InsideSphere Unlit Transparent(stereo+color+fog+alpha)"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "black" {}
        _ChromaTex("Chroma", 2D) = "white" {}
        _Color("Main Color", Color) = (1,1,1,1)

        [KeywordEnum(None, Top_Bottom, Left_Right, Custom_UV)] Stereo ("Stereo Mode", Float) = 0
        [KeywordEnum(None, Top_Bottom, Left_Right)] AlphaPack("Alpha Pack", Float) = 0
        [Toggle(STEREO_DEBUG)] _StereoDebug ("Stereo Debug Tinting", Float) = 0
        [KeywordEnum(None, EquiRect180, Fisheye180)] Layout("Layout", Float) = 0
        [Toggle(HIGH_QUALITY)] _HighQuality ("High Quality", Float) = 1
        [Toggle(APPLY_GAMMA)] _ApplyGamma("Apply Gamma", Float) = 0
        [Toggle(USE_YPCBCR)] _UseYpCbCr("Use YpCbCr", Float) = 0
        [Toggle(FLIPX)] _FlipX("X Flip", Float) = 0
        [Toggle(FLIPY)] _FlipY("Y Flip", Float) = 0
        _Strech ("Strech", Vector)=(1,1,0,0)
        _EdgeFeather("Edge Feather", Range (0, 1)) = 0.02
        _Fov("Fov", Range (0,360)) = 180
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
        ZWrite On
        //ZTest Always
        Blend SrcAlpha OneMinusSrcAlpha
        Cull Front
        Lighting Off

        Pass
        {
            CGPROGRAM
            #include "UnityCG.cginc"
            #include "AVProVideo.cginc"
            #define MY_PI (3.1415926535897932384626433832795)
            #define MY_PI2 (2.0 * 3.1415926535897932384626433832795)

#if HIGH_QUALITY || APPLY_GAMMA
            #pragma target 3.0
#endif
            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_fog
            #pragma multi_compile MONOSCOPIC STEREO_TOP_BOTTOM STEREO_LEFT_RIGHT STEREO_CUSTOM_UV
            #pragma multi_compile ALPHAPACK_NONE ALPHAPACK_TOP_BOTTOM ALPHAPACK_LEFT_RIGHT

            // TODO: Change XX_OFF to __ for Unity 5.0 and above
            // this was just added for Unity 4.x compatibility as __ causes
            // Android and iOS builds to fail the shader
            #pragma multi_compile STEREO_DEBUG_OFF STEREO_DEBUG
            #pragma multi_compile HIGH_QUALITY_OFF HIGH_QUALITY
            #pragma multi_compile APPLY_GAMMA_OFF APPLY_GAMMA
            #pragma multi_compile USE_YPCBCR_OFF USE_YPCBCR
            #pragma multi_compile LAYOUT_NONE LAYOUT_EQUIRECT180 LAYOUT_FISHEYE180

            struct appdata
            {
                float4 vertex : POSITION; // vertex position
#if HIGH_QUALITY
                float3 normal : NORMAL;
#else
                float2 uv : TEXCOORD0; // texture coordinate            
    #if STEREO_CUSTOM_UV
                float2 uv2 : TEXCOORD1;    // Custom uv set for right eye (left eye is in TEXCOORD0)
    #endif
#endif
                
            };

            struct v2f
            {
                float4 vertex : SV_POSITION; // clip space position
#if HIGH_QUALITY
                float3 normal : TEXCOORD0;
                
    #if STEREO_TOP_BOTTOM || STEREO_LEFT_RIGHT
                float4 scaleOffset : TEXCOORD1; // texture coordinate
        #if UNITY_VERSION >= 500
                UNITY_FOG_COORDS(2)
        #endif
    #else
        #if UNITY_VERSION >= 500
                UNITY_FOG_COORDS(1)
        #endif
    #endif
#else
                float4 uv : TEXCOORD0; // texture coordinate
    #if UNITY_VERSION >= 500
                UNITY_FOG_COORDS(1)
    #endif
#endif

#if STEREO_DEBUG
                float4 tint : COLOR;
#endif
            };

            uniform sampler2D _MainTex;
#if USE_YPCBCR
            uniform sampler2D _ChromaTex;
            uniform float4x4 _YpCbCrTransform;
#endif
            uniform float4 _MainTex_ST;
            uniform float4 _MainTex_TexelSize;
            uniform float3 _cameraPosition;
            uniform fixed4 _Color;
            uniform float _EdgeFeather;
            uniform float _FlipX;
            uniform float _FlipY;
            uniform float4 _Strech;
            uniform float _Fov;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = XFormObjectToClip(v.vertex);
#if !HIGH_QUALITY
                o.uv.zw = 0.0;
                o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
    #if !LAYOUT_FISHEYE180                
        #if LAYOUT_EQUIRECT180
                o.uv.x = ((o.uv.x - 0.5) * 2.0) + 0.5;
                // Set value for clipping if UV area is behind viewer
                o.uv.z = -1.0;
                if (v.uv.x > 0.25 && v.uv.x < 0.75)
                {
                    o.uv.z = 1.0;
                }
        #endif
    #endif    
                o.uv.xy = float2(1.0 - o.uv.x, o.uv.y);    // 反転
#endif

#if LAYOUT_FISHEYE180 && ( STEREO_TOP_BOTTOM || STEREO_LEFT_RIGHT ) && HIGH_QUALITY
                o.scaleOffset = 1.0;
#endif
#if !LAYOUT_FISHEYE180
    #if STEREO_TOP_BOTTOM || STEREO_LEFT_RIGHT
                float4 scaleOffset = GetStereoScaleOffset(IsStereoEyeLeft(_cameraPosition, UNITY_MATRIX_V[0].xyz), _MainTex_ST.y < 0.0);

        #if !HIGH_QUALITY
                o.uv.xy *= scaleOffset.xy;
                o.uv.xy += scaleOffset.zw;
        #else
                o.scaleOffset = scaleOffset;
        #endif
    #elif STEREO_CUSTOM_UV && !HIGH_QUALITY
                if (!IsStereoEyeLeft(_cameraPosition, UNITY_MATRIX_V[0].xyz))
                {
                    o.uv.xy = TRANSFORM_TEX(v.uv2, _MainTex);
                    o.uv.xy = float2(1.0 - o.uv.x, o.uv.y);    // 反転
                }
    #endif
#endif
#if !HIGH_QUALITY
    #if ALPHAPACK_TOP_BOTTOM || ALPHAPACK_LEFT_RIGHT
                o.uv = OffsetAlphaPackingUV(_MainTex_TexelSize.xy, o.uv.xy, _MainTex_ST.y > 0.0);
    #endif
#endif
#if HIGH_QUALITY
                o.normal = v.normal;
#endif

#if STEREO_DEBUG
                o.tint = GetStereoDebugTint(IsStereoEyeLeft(_cameraPosition, UNITY_MATRIX_V[0].xyz));
#endif

#if UNITY_VERSION >= 500
                UNITY_TRANSFER_FOG(o, o.vertex);
#endif
                return o;
            }

            float2 calcUV (float2 _uv) {
                // from https://github.com/ft-lab/Unity_Panorama180View/blob/master/README_jp.md
                // MIT License. Copyright (c) 2019 ft-lab.
                float2 uv = _uv;

                // FishEyeからequirectangularの変換.
                // reference : http://paulbourke.net/dome/fish2/
                float theta = MY_PI2 * (uv.x - 0.5);
                float phi   = MY_PI * (uv.y - 0.5);
                float FOV   = ( MY_PI / 180 ) * _Fov;
                float sinP = sin(phi);
                float cosP = cos(phi);
                float sinT = sin(theta);
                float cosT = cos(theta);
                float3 vDir = float3(cosP * sinT, cosP * cosT, sinP);

                theta = atan2(vDir.z, vDir.x);
                phi   = atan2(sqrt(vDir.x * vDir.x + vDir.z * vDir.z), vDir.y);
                float r = phi / FOV; 

                // 左右の映像が横50%のサイズで正面(180度)出力。
                uv.x = 0.5 + r * cos(theta);
                uv.y = 0.5 + r * sin(theta);

                // 左右を2倍(0.5倍すると2倍になる)にする。360度出力。
                uv.x *= 0.5;

                // 右を描画しているときは、+0.5して移動させる。
                if (unity_StereoEyeIndex == 1) {
                    uv.x += 0.5;
                }
                return uv;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                float4 uv = 0;
#if HIGH_QUALITY
                float3 n = normalize(i.normal);
                #if LAYOUT_EQUIRECT180 || LAYOUT_FISHEYE180
                clip(-n.z);    // Clip pixels on the back of the sphere
                #endif

    #if LAYOUT_NONE || LAYOUT_EQUIRECT180
                float M_1_PI = 1.0 / MY_PI;
                float M_1_2PI = 1.0 / MY_PI2;
                uv.x = 0.5 - atan2(n.z, n.x) * M_1_2PI;
                uv.y = 0.5 - asin(-n.y) * M_1_PI;
                uv.x += 0.75;
                uv.x = fmod(uv.x, 1.0);
                //uv.x = uv.x % 1.0;
    #elif LAYOUT_FISHEYE180
                uv.xy = calcUV(uv.xy);
    #endif
                uv.xy = TRANSFORM_TEX(uv, _MainTex);
    #if LAYOUT_EQUIRECT180 || LAYOUT_FISHEYE180
                uv.x = ((uv.x - 0.5) * 2.0) + 0.5;
    #endif
    #if STEREO_TOP_BOTTOM || STEREO_LEFT_RIGHT
                uv.xy *= i.scaleOffset.xy;
                uv.xy += i.scaleOffset.zw;
    #endif
    #if ALPHAPACK_TOP_BOTTOM || ALPHAPACK_LEFT_RIGHT
                uv = OffsetAlphaPackingUV(_MainTex_TexelSize.xy, uv.xy, _MainTex_ST.y < 0.0);
    #endif
#else    // !HIGH_QUALITY
                uv = i.uv;
    #if LAYOUT_FISHEYE180
                // FOVを変更したときに、描画スタート位置と終了位置を設定する。180度の場合は0.25から0.75
                float _start = 0.5 - ( _Fov / 720 );
                float _end = _start + ( _Fov/ 360 );
                if (uv.x < _start || uv.x > _end) return float4(0.0, 0.0, 0.0, 1.0);
                uv.xy = calcUV(uv.xy);
    #elif LAYOUT_EQUIRECT180 //|| LAYOUT_FISHEYE180
                clip(i.uv.z);    // Clip pixels on the back of the sphere
    #endif
#endif  // HIGH_QUALITY
                // 鏡像 _FlipX, _FlipY
                if(_FlipX > 0) uv.xy = float2(1.0 - uv.x, uv.y); // 反転
                if(_FlipY > 0) uv.xy = float2(uv.x, 1.0 - uv.y); // 反転

                // ストレッチ _Strech.xy と、歪み補正 _Strech.zw
                uv.xy = uv + float2(sin(uv.x * MY_PI2) * _Strech.z, sin(uv.y * MY_PI2) * _Strech.w);
                uv.xy = 0.5 + (uv - 0.5) / _Strech.xy;
                fixed4 col;
#if USE_YPCBCR
                col = SampleYpCbCr(_MainTex, _ChromaTex, uv.xy, _YpCbCrTransform);
#else
                col = SampleRGBA(_MainTex, uv.xy);
#endif

#if ALPHAPACK_TOP_BOTTOM || ALPHAPACK_LEFT_RIGHT
                col.a = SamplePackedAlpha(_MainTex, uv.zw);
#endif

#if STEREO_DEBUG
                col *= i.tint;
#endif

                col *= _Color;

#if UNITY_VERSION >= 500
                UNITY_APPLY_FOG(i.fogCoord, col);
#endif

#if LAYOUT_EQUIRECT180 || LAYOUT_FISHEYE180
                // Apply edge feathering based on UV mapping - this is useful if you're using a hemisphere mesh for 180 degree video and want to have soft edges
                if (_EdgeFeather > 0.0)
                {
                    float4 featherDirection = float4(0.0, 0.0, 1.0, 1.0);
                    
#if STEREO_TOP_BOTTOM 
                    if (uv.y > 0.5)
                    {
                        featherDirection.y = 0.5;
                    }
                    else
                    {
                        featherDirection.w = 0.5;
                    }
#endif

#if STEREO_LEFT_RIGHT
                    if (uv.x > 0.5)
                    {
                        featherDirection.x = 0.5;
                    }
                    else
                    {
                        featherDirection.z = 0.5;
                    }
#endif


#if ALPHAPACK_TOP_BOTTOM
                    featherDirection.w *= 0.5;
#endif

#if ALPHAPACK_LEFT_RIGHT
                    featherDirection.z *= 0.5;
#endif

                    float d = min(uv.x - featherDirection.x, min((uv.y - featherDirection.y), min(featherDirection.z - uv.x, featherDirection.w - uv.y)));
                    float a = smoothstep(0.0, _EdgeFeather, d);
                    col.a *= a;
                }
#endif

                return col;

            }
            ENDCG
        }
    }
}


未分類

#ChannelPlayer 1.09リリース

#ChannelPlayerのFOVE0 & Rift版の新しいバージョンを公開しました。変更履歴は以下となります。
ソフトウェアのページからサンプル版のダウンロードや、製品版の御購入が可能です。

改版履歴
2019-06-03 1.09
・追加
 VR180の対応フォーマットとして FishEye180 Side by Sideをサポート

・変更
アプリ起動時およびフォルダー変更ダイアログから戻るときのフォルダ一覧取得をバックグラウンドで実行するよう変更しました。

カメラトラッキングオフ、スクリーンキャプチャのボタン変更
  カメラトラッキングオフ XBoxコントローラー Menu -> Xボタン
  スクリーンキャプチャ  XBoxコントローラー View -> Yボタン

2Dスクリーンのアスペクト比変更ボタン 16:9<->4:3
   XBoxコントローラー Yボタン -> DPad Upトリプルクリック

XBoxコントローラー 左スティックによるカメラ移動
前後上下切り替えにXBoxコントローラー LBボタンによるモード切替としていましたが、押したままなら上下、押さないと前後になるよう変更しました。

・修正
 動画再生中のキーボードのカーソル左右移動時にカメラの左右移動が誤って連動していた不具合を直しました。
 アプリのウィンドウが非アクティブでも動作していましたが一時停止するように修正しました。
 フォルダー選択画面にて、Clearボタンから上に移動する場合、リストの途中になっていましたが、リストの一番下に直しました。

#ChannelPlayer · FOVE · Rift · VRソフト

#ChannelPlayer 1.08リリース

#ChannelPlayerのFOVE0 & Rift版の新しいバージョンを公開しました。変更履歴は以下となります。ソフトウェアのページからサンプル版のダウンロードや、製品版の御購入が可能です。

2019-04-27 1.08
・機能追加
 再生中において、以下の機能を追加しました。
 カメラ移動 上下に移動できるモードを追加
  Qキー: XBoxコントローラー LBにて、カメラ移動 move Up/down, In/Outモード切り替えします。
デフォルトはIn/Outです。
 2Dスクリーン
  Eキー: XBoxコントローラー RBにて、 2Dスクリーンを 90度ずつ右回転させます。
Tキー: XBoxコントローラー Yボタンにて、2Dスクリーンのアスペクト比を 16:9<->4:3 に切り替えます。
デフォルトは16:9です。
 コンテンツ選択画面において、XBoxコントローラ 左スティックでもタイトルが変更できるようにしました。

・機能修正
 トラッキングオフ時にカメラの位置ずれを最小にしました。

#ChannelPlayer · FOVE · Rift · Unity · VRソフト

トラッキングオフ実装の調整

#ChannelPlayerのStore版と、FOVE/Rift共用版では実装が異なり、カメラに前者ではOVRCameraRigを使っていて、後者では普通のUnityのCamera+FoveInterface2を使っている。トラッキングオフを両方とも実装しているが、Unityのカメラと異なり、OVRの方はトラッキングを停止すると、どうしてもカメラの向きや位置がずれてしまっていた。位置が合うように現在のHMDの位置と角度を取得して反映させるようにしたが、FOVE/Rift共用版では問題無いのだが、OVRの方は一瞬ブレてしまう。どうにも直せないのでトラッキングオフ/オンの際に、1フレームだけOVRのカメラを停止させるようなワークアラウンドを入れてみた。良好になった。以下にコードを示すが、SetActive()とpauseWAを使って、カメラのトラッキングオフ/オンの瞬間だけ、カメラを停止している。

    void LateUpdate()
    {
        trackingPos = UnityEngine.XR.InputTracking.GetLocalPosition(UnityEngine.XR.XRNode.CenterEye);
        trackingRot = UnityEngine.XR.InputTracking.GetLocalRotation(UnityEngine.XR.XRNode.CenterEye);

        if (pauseWA)
        {
            pauseWA = false;
            _Camera.SetActive(true);
        }

        // Camera Tracking off/on
        if (Input.GetButtonDown("XB1Menu") || Input.GetKeyDown(KeyCode.X))
        {
            if (_fixed)
            {
                _Camera.SetActive(false);
                pauseWA = true;
                // 非固定にする。
                OVRManager.instance.usePositionTracking = true;
                OVRManager.instance.useRotationTracking = true;
                //XRDevice.DisableAutoXRCameraTracking(_camera, false);
                _fixed = false;
                
            }
            else
            {
                _Camera.SetActive(false);
                pauseWA = true;
                // 固定にする。
                OVRManager.instance.usePositionTracking = false;
                OVRManager.instance.useRotationTracking = false;
                //XRDevice.DisableAutoXRCameraTracking(_camera, true);
                _fixed = true;
                // 現在位置をベースにする
                basePos = trackingPos;
                baseRot = trackingRot;
            }
        }
#ChannelPlayer · FOVE · Rift · VRソフト

#ChannelPlayer 1.07リリース

#ChannelPlayerのFOVE0 & Rift版の新しいバージョンを公開しました。変更履歴は以下となります。ソフトウェアのページからサンプル版のダウンロードや、製品版の御購入が可能です。 なお現在Oculus Store版は、2019-04-10までの期限でキーを配布しているので、Riftオーナーの方々においては、無料で製品版を入手可能なので、そちらがお勧めです。 ソフトウェアのページ をご覧ください。

2019-04-06 1.07
・機能修正
 Rift版にて、HMDトラッキング有効時にカメラの向き制御量が2倍になっていたのを修正しました。