From 7d7f52bcd847b40512dc8341bb3635cbed6a3f5a Mon Sep 17 00:00:00 2001
From: Martin Finkel <me@martinfinkel.com>
Date: Wed, 26 Feb 2025 13:49:24 +0700
Subject: [PATCH] samples: update 360 sample

use projection mode on sphere
fix up/down navigation
refactor code
---
 .../VLCUnity/Demos/Scenes/VLCThreeSixty.unity | 264 ++++++++++++++++--
 .../VLCUnity/Demos/Scripts/VLCThreeSixty.cs   | 165 +++++------
 2 files changed, 317 insertions(+), 112 deletions(-)

diff --git a/Assets/VLCUnity/Demos/Scenes/VLCThreeSixty.unity b/Assets/VLCUnity/Demos/Scenes/VLCThreeSixty.unity
index f5a8ce4..0a41c55 100644
--- a/Assets/VLCUnity/Demos/Scenes/VLCThreeSixty.unity
+++ b/Assets/VLCUnity/Demos/Scenes/VLCThreeSixty.unity
@@ -13,7 +13,7 @@ OcclusionCullingSettings:
 --- !u!104 &2
 RenderSettings:
   m_ObjectHideFlags: 0
-  serializedVersion: 9
+  serializedVersion: 10
   m_Fog: 0
   m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
   m_FogMode: 3
@@ -38,13 +38,12 @@ RenderSettings:
   m_ReflectionIntensity: 1
   m_CustomReflection: {fileID: 0}
   m_Sun: {fileID: 0}
-  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
   m_UseRadianceAmbientProbe: 0
 --- !u!157 &3
 LightmapSettings:
   m_ObjectHideFlags: 0
-  serializedVersion: 11
-  m_GIWorkflowMode: 1
+  serializedVersion: 13
+  m_BakeOnSceneLoad: 0
   m_GISettings:
     serializedVersion: 2
     m_BounceScale: 1
@@ -67,9 +66,6 @@ LightmapSettings:
     m_LightmapParameters: {fileID: 0}
     m_LightmapsBakeMode: 1
     m_TextureCompression: 1
-    m_FinalGather: 0
-    m_FinalGatherFiltering: 1
-    m_FinalGatherRayCount: 256
     m_ReflectionCompression: 2
     m_MixedBakeMode: 2
     m_BakeBackend: 1
@@ -98,13 +94,13 @@ LightmapSettings:
     m_TrainingDataDestination: TrainingData
     m_LightProbeSampleCountMultiplier: 4
   m_LightingDataAsset: {fileID: 0}
-  m_UseShadowmask: 1
+  m_LightingSettings: {fileID: 757142518}
 --- !u!196 &4
 NavMeshSettings:
   serializedVersion: 2
   m_ObjectHideFlags: 0
   m_BuildSettings:
-    serializedVersion: 2
+    serializedVersion: 3
     agentTypeID: 0
     agentRadius: 0.5
     agentHeight: 2
@@ -117,10 +113,133 @@ NavMeshSettings:
     cellSize: 0.16666667
     manualTileSize: 0
     tileSize: 256
-    accuratePlacement: 0
+    buildHeightMesh: 0
+    maxJobWorkers: 0
+    preserveTilesOutsideBounds: 0
     debug:
       m_Flags: 0
   m_NavMeshData: {fileID: 0}
+--- !u!1 &246358331
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 246358335}
+  - component: {fileID: 246358334}
+  - component: {fileID: 246358333}
+  - component: {fileID: 246358332}
+  - component: {fileID: 246358336}
+  m_Layer: 0
+  m_Name: Sphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!135 &246358332
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 246358331}
+  m_Material: {fileID: 0}
+  m_IncludeLayers:
+    serializedVersion: 2
+    m_Bits: 0
+  m_ExcludeLayers:
+    serializedVersion: 2
+    m_Bits: 0
+  m_LayerOverridePriority: 0
+  m_IsTrigger: 0
+  m_ProvidesContacts: 0
+  m_Enabled: 1
+  serializedVersion: 3
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &246358333
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 246358331}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 0
+  m_StaticShadowCaster: 0
+  m_MotionVectors: 1
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RayTracingAccelStructBuildFlagsOverride: 0
+  m_RayTracingAccelStructBuildFlags: 1
+  m_SmallMeshCulling: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: dda856af5216c6d45874c074cf358a10, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &246358334
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 246358331}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &246358335
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 246358331}
+  serializedVersion: 2
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 4, y: 0.48, z: 0}
+  m_LocalScale: {x: 5, y: 5, z: 5}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &246358336
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 246358331}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 699ff01a0b194194c91de64b70b01b1d, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1 &363825485
 GameObject:
   m_ObjectHideFlags: 0
@@ -146,9 +265,8 @@ Light:
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 363825485}
   m_Enabled: 1
-  serializedVersion: 10
+  serializedVersion: 11
   m_Type: 1
-  m_Shape: 0
   m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
   m_Intensity: 1
   m_Range: 10
@@ -197,8 +315,13 @@ Light:
   m_UseColorTemperature: 0
   m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
   m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ForceVisible: 0
   m_ShadowRadius: 0
   m_ShadowAngle: 0
+  m_LightUnit: 1
+  m_LuxAtDistance: 1
+  m_EnableSpotReflector: 1
 --- !u!4 &363825487
 Transform:
   m_ObjectHideFlags: 0
@@ -206,12 +329,13 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 363825485}
+  serializedVersion: 2
   m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
   m_LocalPosition: {x: 0, y: 3, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 1
   m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
 --- !u!1 &603997761
 GameObject:
@@ -253,9 +377,17 @@ Camera:
   m_projectionMatrixMode: 1
   m_GateFitMode: 2
   m_FOVAxisMode: 0
+  m_Iso: 200
+  m_ShutterSpeed: 0.005
+  m_Aperture: 16
+  m_FocusDistance: 10
+  m_FocalLength: 50
+  m_BladeCount: 5
+  m_Curvature: {x: 2, y: 11}
+  m_BarrelClipping: 0.25
+  m_Anamorphism: 0
   m_SensorSize: {x: 36, y: 24}
   m_LensShift: {x: 0, y: 0}
-  m_FocalLength: 50
   m_NormalizedViewPortRect:
     serializedVersion: 2
     x: 0
@@ -289,13 +421,75 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 603997761}
+  serializedVersion: 2
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 1, z: -10}
   m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!850595691 &757142518
+LightingSettings:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Settings.lighting
+  serializedVersion: 9
+  m_EnableBakedLightmaps: 1
+  m_EnableRealtimeLightmaps: 0
+  m_RealtimeEnvironmentLighting: 1
+  m_BounceScale: 1
+  m_AlbedoBoost: 1
+  m_IndirectOutputScale: 1
+  m_UsingShadowmask: 1
+  m_BakeBackend: 1
+  m_LightmapMaxSize: 1024
+  m_LightmapSizeFixed: 0
+  m_UseMipmapLimits: 1
+  m_BakeResolution: 40
+  m_Padding: 2
+  m_LightmapCompression: 3
+  m_AO: 0
+  m_AOMaxDistance: 1
+  m_CompAOExponent: 1
+  m_CompAOExponentDirect: 0
+  m_ExtractAO: 0
+  m_MixedBakeMode: 2
+  m_LightmapsBakeMode: 1
+  m_FilterMode: 1
+  m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
+  m_ExportTrainingData: 0
+  m_EnableWorkerProcessBaking: 1
+  m_TrainingDataDestination: TrainingData
+  m_RealtimeResolution: 2
+  m_ForceWhiteAlbedo: 0
+  m_ForceUpdates: 0
+  m_PVRCulling: 1
+  m_PVRSampling: 1
+  m_PVRDirectSampleCount: 32
+  m_PVRSampleCount: 512
+  m_PVREnvironmentSampleCount: 256
+  m_PVREnvironmentReferencePointCount: 2048
+  m_LightProbeSampleCountMultiplier: 4
+  m_PVRBounces: 2
+  m_PVRMinBounces: 2
+  m_PVREnvironmentImportanceSampling: 1
+  m_PVRFilteringMode: 1
+  m_PVRDenoiserTypeDirect: 1
+  m_PVRDenoiserTypeIndirect: 1
+  m_PVRDenoiserTypeAO: 1
+  m_PVRFilterTypeDirect: 0
+  m_PVRFilterTypeIndirect: 0
+  m_PVRFilterTypeAO: 0
+  m_PVRFilteringGaussRadiusDirect: 1
+  m_PVRFilteringGaussRadiusIndirect: 5
+  m_PVRFilteringGaussRadiusAO: 2
+  m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+  m_PVRFilteringAtrousPositionSigmaIndirect: 2
+  m_PVRFilteringAtrousPositionSigmaAO: 1
+  m_RespectSceneVisibilityWhenBakingGI: 0
 --- !u!1 &798205350
 GameObject:
   m_ObjectHideFlags: 0
@@ -309,7 +503,7 @@ GameObject:
   - component: {fileID: 798205353}
   - component: {fileID: 798205354}
   m_Layer: 0
-  m_Name: VLC 360
+  m_Name: Screen
   m_TagString: Untagged
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
@@ -322,12 +516,13 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 798205350}
+  serializedVersion: 2
   m_LocalRotation: {x: -0.6598657, y: 0, z: 0, w: 0.7513836}
-  m_LocalPosition: {x: 0, y: 1.39, z: 0}
-  m_LocalScale: {x: -1, y: 1, z: 1}
+  m_LocalPosition: {x: -2, y: 1.39, z: 0}
+  m_LocalScale: {x: -0.5, y: 0.5, z: 0.5}
+  m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 3
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!33 &798205352
 MeshFilter:
@@ -348,10 +543,15 @@ MeshRenderer:
   m_CastShadows: 1
   m_ReceiveShadows: 0
   m_DynamicOccludee: 1
+  m_StaticShadowCaster: 0
   m_MotionVectors: 1
   m_LightProbeUsage: 1
   m_ReflectionProbeUsage: 1
   m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RayTracingAccelStructBuildFlagsOverride: 0
+  m_RayTracingAccelStructBuildFlags: 1
+  m_SmallMeshCulling: 1
   m_RenderingLayerMask: 1
   m_RendererPriority: 0
   m_Materials:
@@ -376,6 +576,7 @@ MeshRenderer:
   m_SortingLayerID: 0
   m_SortingLayer: 0
   m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
 --- !u!114 &798205354
 MonoBehaviour:
   m_ObjectHideFlags: 0
@@ -439,10 +640,15 @@ MeshRenderer:
   m_CastShadows: 1
   m_ReceiveShadows: 1
   m_DynamicOccludee: 1
+  m_StaticShadowCaster: 0
   m_MotionVectors: 1
   m_LightProbeUsage: 1
   m_ReflectionProbeUsage: 1
   m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RayTracingAccelStructBuildFlagsOverride: 0
+  m_RayTracingAccelStructBuildFlags: 1
+  m_SmallMeshCulling: 1
   m_RenderingLayerMask: 1
   m_RendererPriority: 0
   m_Materials:
@@ -467,6 +673,7 @@ MeshRenderer:
   m_SortingLayerID: 0
   m_SortingLayer: 0
   m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
 --- !u!4 &1092884458
 Transform:
   m_ObjectHideFlags: 0
@@ -474,12 +681,13 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 1092884455}
+  serializedVersion: 2
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
-  m_LocalPosition: {x: -23, y: -1, z: 1}
+  m_LocalPosition: {x: -10, y: -3, z: 1}
   m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 4
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!1 &1573063551
 GameObject:
@@ -511,6 +719,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
+  m_SendPointerHoverToParent: 1
   m_HorizontalAxis: Horizontal
   m_VerticalAxis: Vertical
   m_SubmitButton: Submit
@@ -540,10 +749,21 @@ Transform:
   m_PrefabInstance: {fileID: 0}
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 1573063551}
+  serializedVersion: 2
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 2
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1660057539 &9223372036854775807
+SceneRoots:
+  m_ObjectHideFlags: 0
+  m_Roots:
+  - {fileID: 603997764}
+  - {fileID: 363825487}
+  - {fileID: 1573063554}
+  - {fileID: 798205351}
+  - {fileID: 1092884458}
+  - {fileID: 246358335}
diff --git a/Assets/VLCUnity/Demos/Scripts/VLCThreeSixty.cs b/Assets/VLCUnity/Demos/Scripts/VLCThreeSixty.cs
index fa818cd..ea482d1 100644
--- a/Assets/VLCUnity/Demos/Scripts/VLCThreeSixty.cs
+++ b/Assets/VLCUnity/Demos/Scripts/VLCThreeSixty.cs
@@ -6,143 +6,128 @@
 public class VLCThreeSixty : MonoBehaviour
 {
     LibVLC _libVLC;
-    MediaPlayer _mediaPlayer;
+    MediaPlayer _mediaPlayerScreen;
+    MediaPlayer _mediaPlayerSphere;
     const int seekTimeDelta = 5000;
-    Texture2D tex = null;
+    Texture2D texScreen, texSphere;
+    Renderer screen, sphere;
     bool playing;
-    
-    float Yaw;
-    float Pitch;
-    float Roll;
+    float Yaw, Pitch, Roll;
 
     void Awake()
     {
         Core.Initialize(Application.dataPath);
-
         _libVLC = new LibVLC(enableDebugLogs: true);
 
-        Application.SetStackTraceLogType(LogType.Log, StackTraceLogType.None);
-        //_libVLC.Log += (s, e) => UnityEngine.Debug.Log(e.FormattedLog); // enable this for logs in the editor
-
-        PlayPause();
+        screen = GameObject.Find("Screen").GetComponent<Renderer>();
+        sphere = GameObject.Find("Sphere").GetComponent<Renderer>();
     }
 
-    void OnDisable() 
-    {
-        _mediaPlayer?.Stop();
-        _mediaPlayer?.Dispose();
-        _mediaPlayer = null;
+    void Start() => PlayPause();
 
+    void OnDisable()
+    {
+        _mediaPlayerScreen?.Stop();
+        _mediaPlayerScreen?.Dispose();
+        _mediaPlayerSphere?.Dispose();
         _libVLC?.Dispose();
-        _libVLC = null;
     }
 
     public void PlayPause()
     {
-        Debug.Log ("[VLC] Toggling Play Pause !");
-        if (_mediaPlayer == null)
+        if (_mediaPlayerScreen == null)
         {
-            _mediaPlayer = new MediaPlayer(_libVLC);
+            // keep default projection for the screen, thus with viewpoint navigation enabled
+            _mediaPlayerScreen = new MediaPlayer(_libVLC);
         }
-        if (_mediaPlayer.IsPlaying)
+        if (_mediaPlayerSphere == null)
+        {
+            // disable default projection for the sphere, no libvlc viewpoint navigation
+            _mediaPlayerSphere = new MediaPlayer(_libVLC);
+            _mediaPlayerSphere.SetProjectionMode(VideoProjection.Rectangular);
+        }
+
+        Debug.Log("[VLC] Toggling Play Pause!");
+
+        if (_mediaPlayerScreen.IsPlaying || _mediaPlayerSphere.IsPlaying)
         {
-            _mediaPlayer.Pause();
+            _mediaPlayerScreen.Pause();
+            _mediaPlayerSphere.Pause();
         }
         else
         {
             playing = true;
-
-            if(_mediaPlayer.Media == null)
+            if (_mediaPlayerScreen.Media == null)
             {
-                // download https://streams.videolan.org/streams/360/eagle_360.mp4 
-                // to your computer (to avoid network requests for smoother navigation)
-                // and adjust the Uri to the local path
+                // better save the media locally to avoid slow networks
                 var media = new Media(new Uri("https://streams.videolan.org/streams/360/eagle_360.mp4"));
-                
-                Task.Run(async () => 
+                Task.Run(async () =>
                 {
                     var result = await media.ParseAsync(_libVLC, MediaParseOptions.ParseNetwork);
-                    var trackList = media.TrackList(TrackType.Video);
-                    var is360 = trackList[0].Data.Video.Projection == VideoProjection.Equirectangular;
-                    
-                    if(is360)
-                        Debug.Log("The video is a 360 video");
-                    else
-                        Debug.Log("The video was not identified as a 360 video by VLC, make sure it is properly tagged");
-
-                    trackList.Dispose();
+                    Debug.Log(media.TrackList(TrackType.Video)[0].Data.Video.Projection == VideoProjection.Equirectangular
+                        ? "The video is a 360 video" : "The video was not identified as a 360 video by VLC");
                 });
-                
-                _mediaPlayer.Media = media;
+                _mediaPlayerScreen.Media = _mediaPlayerSphere.Media = media;
             }
-
-            _mediaPlayer.Play();
+            _mediaPlayerScreen.Play();
+            _mediaPlayerSphere.Play();
         }
     }
 
     void Update()
     {
-        if(!playing) return;
+        if (!playing) return;
+        UpdateTexture(ref texScreen, _mediaPlayerScreen, screen);
+        UpdateTexture(ref texSphere, _mediaPlayerSphere, sphere);
+    }
 
-        if (tex == null)
+    void UpdateTexture(ref Texture2D texture, MediaPlayer player, Renderer renderer)
+    {
+        if (texture == null)
         {
-            // If received size is not null, it and scale the texture
-            uint i_videoHeight = 0;
-            uint i_videoWidth = 0;
+            uint width = 0, height = 0;
+            player.Size(0, ref width, ref height);
+            var texPtr = player.GetTexture(width, height, out bool updated);
 
-            _mediaPlayer.Size(0, ref i_videoWidth, ref i_videoHeight);
-            var texptr = _mediaPlayer.GetTexture(i_videoWidth, i_videoHeight, out bool updated);
-            if (i_videoWidth != 0 && i_videoHeight != 0 && updated && texptr != IntPtr.Zero)
+            if (width != 0 && height != 0 && updated && texPtr != IntPtr.Zero)
             {
-                Debug.Log("Creating texture with height " + i_videoHeight + " and width " + i_videoWidth);
-                tex = Texture2D.CreateExternalTexture((int)i_videoWidth,
-                    (int)i_videoHeight,
-                    TextureFormat.RGBA32,
-                    false,
-                    true,
-                    texptr);
-                GetComponent<Renderer>().material.mainTexture = tex;
+                Debug.Log($"Creating texture with height {height} and width {width}");
+                texture = Texture2D.CreateExternalTexture((int)width, (int)height, TextureFormat.RGBA32, false, true, texPtr);
+                renderer.material.mainTexture = texture;
             }
         }
-        else if (tex != null)
+        else
         {
-            var texptr = _mediaPlayer.GetTexture((uint)tex.width, (uint)tex.height, out bool updated);
-            if (updated)
-            {
-                tex.UpdateExternalTexture(texptr);
-            }
+            var texPtr = player.GetTexture((uint)texture.width, (uint)texture.height, out bool updated);
+            if (updated) texture.UpdateExternalTexture(texPtr);
         }
     }
 
-    void OnGUI()
-    {
-        Do360Navigation();
-    }
-    
+    void OnGUI() => Do360Navigation();
+
     void Do360Navigation()
     {
         var range = Math.Max(UnityEngine.Screen.width, UnityEngine.Screen.height);
-
-        Yaw = _mediaPlayer.Viewpoint.Yaw;
-        Pitch = _mediaPlayer.Viewpoint.Pitch;
-        Roll = _mediaPlayer.Viewpoint.Roll;
+        Yaw = _mediaPlayerScreen.Viewpoint.Yaw;
+        Pitch = _mediaPlayerScreen.Viewpoint.Pitch;
+        Roll = _mediaPlayerScreen.Viewpoint.Roll;
         var fov = 80;
 
-        if (Input.GetKey(KeyCode.RightArrow))
-        {
-            _mediaPlayer.UpdateViewpoint(Yaw + (float)( 80 * + 40 / range), Pitch, Roll, fov);
-        }
-        else if(Input.GetKey(KeyCode.LeftArrow))
-        {
-            _mediaPlayer.UpdateViewpoint(Yaw - (float)( 80 * + 40 / range), Pitch, Roll, fov);
-        }
-        else if(Input.GetKey(KeyCode.DownArrow))
-        {
-            _mediaPlayer.UpdateViewpoint(Yaw, Pitch + (float)( 80 * + 20 / range), Roll, fov);
-        }
-        else if(Input.GetKey(KeyCode.UpArrow))
-        {
-            _mediaPlayer.UpdateViewpoint(Yaw, Pitch - (float)( 80 * + 20 / range), Roll, fov);
-        }
+        if (Input.GetKey(KeyCode.RightArrow)) RotateView(20f, 80 * 40 / range, 0);
+        else if (Input.GetKey(KeyCode.LeftArrow)) RotateView(-20f, -80 * 40 / range, 0);
+        else if (Input.GetKey(KeyCode.DownArrow)) RotateView(0, 0, 1);
+        else if (Input.GetKey(KeyCode.UpArrow)) RotateView(0, 0, -1);
+    }
+
+    void RotateView(float sphereRotation, float yawDelta, float pitchDelta)
+    {
+        // no viewpoint changes for the sphere as the whole video is shown on the texture
+        // we can rotate the texture instead
+        sphere.transform.Rotate(Vector3.up * sphereRotation * Time.deltaTime);
+        sphere.transform.Rotate(Vector3.right * pitchDelta * 10 * Time.deltaTime);
+
+        Pitch = Mathf.Clamp(Pitch + pitchDelta, -90f, 90f);    
+        _mediaPlayerScreen.UpdateViewpoint(Yaw + yawDelta, Pitch, Roll, 80);
     }
-}
+
+}
\ No newline at end of file
-- 
GitLab