< Summary

Class:DCL.Components.DCLVideoTexture
Assembly:DCL.Components.Video
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Video/DCLVideoTexture.cs
Covered lines:161
Uncovered lines:21
Coverable lines:182
Total lines:390
Line coverage:88.4% (161 of 182)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
DCLVideoTexture()0%110100%
Model()0%110100%
GetDataFromJSON(...)0%110100%
DCLVideoTexture()0%110100%
ApplyChanges()0%34.3219065.12%
Initialize(...)0%3.013091.67%
GetVolume()0%110100%
OnUpdate()0%330100%
UpdateDirtyState()0%220100%
UpdateVideoTexture()0%440100%
UpdateProgressReport()0%440100%
ReportVideoProgress()0%110100%
IsTimeToReportVideoProgress()0%110100%
CalculateVideoVolumeAndPlayStatus()0%880100%
OnVirtualAudioMixerChangedValue(...)0%2100%
UpdateVolume()0%4.014090.91%
IsPlayerInSameSceneAsComponent(...)0%5.024060%
OnPlayerCoordsChanged(...)0%110100%
OnSceneNumberChanged(...)0%110100%
AttachTo(...)0%22090%
DetachFrom(...)0%3.013091.67%
SetPlayStateDirty(...)0%110100%
OnAudioSettingsChanged(...)0%110100%
Dispose()0%440100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Video/DCLVideoTexture.cs

#LineLine coverage
 1using System.Collections;
 2using System.Collections.Generic;
 3using DCL.Controllers;
 4using DCL.Models;
 5using UnityEngine;
 6using DCL.Components.Video.Plugin;
 7using DCL.Helpers;
 8using DCL.Interface;
 9using DCL.SettingsCommon;
 10using UnityEngine.Assertions;
 11using AudioSettings = DCL.SettingsCommon.AudioSettings;
 12
 13namespace DCL.Components
 14{
 15    public class DCLVideoTexture : DCLTexture
 16    {
 117        public static bool VERBOSE = false;
 118        public static Logger logger = new Logger("DCLVideoTexture") {verboseEnabled = VERBOSE};
 19
 20        private const float OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS = 1.5f;
 21        private const float VIDEO_PROGRESS_UPDATE_INTERVAL_IN_SECONDS = 1f;
 22
 123        public static System.Func<IVideoPluginWrapper> videoPluginWrapperBuilder = () => new VideoPluginWrapper_WebGL();
 24
 25        [System.Serializable]
 26        new public class Model : BaseModel
 27        {
 28            public string videoClipId;
 29            public bool playing = false;
 4530            public float volume = 1f;
 4531            public float playbackRate = 1f;
 32            public bool loop = false;
 4533            public float seek = -1;
 34            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 4535            public FilterMode samplingMode = FilterMode.Bilinear;
 36
 1537            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 38        }
 39
 40        internal WebVideoPlayer texturePlayer;
 41        private Coroutine texturePlayerUpdateRoutine;
 42        private float baseVolume;
 1543        private float distanceVolumeModifier = 1f;
 44        private bool isPlayStateDirty = false;
 45        internal bool isVisible = false;
 46
 47        private bool isInitialized = false;
 1548        private bool isPlayerInScene = true;
 1549        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS;
 50        private float lastVideoProgressReportTime;
 51
 1552        internal Dictionary<string, ISharedComponent> attachedMaterials = new Dictionary<string, ISharedComponent>();
 53        private string lastVideoClipID;
 54        private VideoState previousVideoState;
 55
 1556        public DCLVideoTexture()
 57        {
 1558            model = new Model();
 59
 1560            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1561        }
 62
 63        public override IEnumerator ApplyChanges(BaseModel newModel)
 64        {
 3065            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 66
 67            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 68            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1569            if (isDisposed)
 70            {
 071                yield break;
 72            }
 73
 1574            var model = (Model) newModel;
 75
 1576            unitySamplingMode = model.samplingMode;
 77
 1578            switch (model.wrap)
 79            {
 80                case BabylonWrapMode.CLAMP:
 1581                    unityWrap = TextureWrapMode.Clamp;
 1582                    break;
 83                case BabylonWrapMode.WRAP:
 084                    unityWrap = TextureWrapMode.Repeat;
 085                    break;
 86                case BabylonWrapMode.MIRROR:
 087                    unityWrap = TextureWrapMode.Mirror;
 88                    break;
 89            }
 90
 1591            lastVideoClipID = model.videoClipId;
 92
 1593            if (texturePlayer == null)
 94            {
 1595                DCLVideoClip dclVideoClip = scene.componentsManagerLegacy.GetSceneSharedComponent(lastVideoClipID) as DC
 96
 1597                if (dclVideoClip == null)
 98                {
 099                    logger.Error("Wrong video clip type when playing VideoTexture!!");
 0100                    yield break;
 101                }
 102
 15103                Initialize(dclVideoClip);
 104            }
 105
 15106            if (texture == null)
 107            {
 0108                yield return new WaitUntil(() => texturePlayer == null || ((texturePlayer.texture != null && texturePlay
 109
 0110                if (texturePlayer.isError)
 111                {
 0112                    if (texturePlayerUpdateRoutine != null)
 113                    {
 0114                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0115                        texturePlayerUpdateRoutine = null;
 116                    }
 117
 0118                    texturePlayer.SetAsError();
 0119                    yield break;
 120                }
 121
 0122                texture = texturePlayer.texture;
 0123                SetPlayStateDirty();
 124            }
 125
 15126            if (texturePlayer != null)
 127            {
 15128                if (model.seek >= 0)
 129                {
 1130                    texturePlayer.SetTime(model.seek);
 1131                    model.seek = -1;
 132
 133                    // Applying seek is not immediate
 1134                    yield return null;
 135                }
 136
 15137                if (model.playing)
 138                {
 3139                    texturePlayer.Play();
 140                }
 141                else
 142                {
 12143                    texturePlayer.Pause();
 144                }
 145
 15146                ReportVideoProgress();
 147
 15148                if (baseVolume != model.volume)
 149                {
 15150                    baseVolume = model.volume;
 15151                    UpdateVolume();
 152                }
 153
 15154                texturePlayer.SetPlaybackRate(model.playbackRate);
 15155                texturePlayer.SetLoop(model.loop);
 156            }
 15157        }
 158
 159        private void Initialize(DCLVideoClip dclVideoClip)
 160        {
 15161            if (isInitialized) return;
 15162            isInitialized = true;
 163
 15164            string videoId = scene.sceneData.sceneNumber > 0 ? scene.sceneData.sceneNumber + id : scene.GetHashCode().To
 15165            texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream, videoPluginWrapper
 15166            texturePlayerUpdateRoutine = CoroutineStarter.Start(OnUpdate());
 167
 15168            CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 15169            CommonScriptableObjects.sceneNumber.OnChange += OnSceneNumberChanged;
 15170            scene.OnEntityRemoved += SetPlayStateDirty;
 15171            Settings.i.audioSettings.OnChanged += OnAudioSettingsChanged;
 172
 15173            OnSceneNumberChanged(CommonScriptableObjects.sceneNumber.Get(), -1);
 15174        }
 175
 2176        public float GetVolume() { return ((Model) model).volume; }
 177
 178        private IEnumerator OnUpdate()
 179        {
 96180            while (true)
 181            {
 111182                UpdateDirtyState();
 111183                UpdateVideoTexture();
 111184                UpdateProgressReport();
 111185                yield return null;
 186            }
 187        }
 188
 189        private void UpdateDirtyState()
 190        {
 111191            if (!isPlayStateDirty)
 84192                return;
 193
 27194            CalculateVideoVolumeAndPlayStatus();
 27195            isPlayStateDirty = false;
 27196        }
 197
 198        private void UpdateVideoTexture()
 199        {
 111200            if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS)
 201            {
 12202                currUpdateIntervalTime += Time.unscaledDeltaTime;
 203            }
 99204            else if (texturePlayer != null)
 205            {
 99206                currUpdateIntervalTime = 0;
 99207                texturePlayer.Update();
 99208                texture = texturePlayer.texture;
 209            }
 99210        }
 211
 212        private void UpdateProgressReport()
 213        {
 111214            var currentState = texturePlayer.GetState();
 215
 111216            if ( currentState == VideoState.PLAYING
 217                 && IsTimeToReportVideoProgress()
 218                 || previousVideoState != currentState)
 219            {
 29220                ReportVideoProgress();
 221            }
 111222        }
 223
 224        private void ReportVideoProgress()
 225        {
 44226            lastVideoProgressReportTime = Time.unscaledTime;
 44227            VideoState videoState = texturePlayer.GetState();
 44228            previousVideoState = videoState;
 44229            var videoStatus = (int)videoState;
 44230            var currentOffset = texturePlayer.GetTime();
 44231            var length = texturePlayer.GetDuration();
 44232            WebInterface.ReportVideoProgressEvent(id, scene.sceneData.sceneNumber, lastVideoClipID, videoStatus, current
 44233        }
 234
 235        private bool IsTimeToReportVideoProgress()
 236        {
 6237            return Time.unscaledTime - lastVideoProgressReportTime > VIDEO_PROGRESS_UPDATE_INTERVAL_IN_SECONDS;
 238        }
 239
 240        private void CalculateVideoVolumeAndPlayStatus()
 241        {
 27242            isVisible = false;
 27243            float minDistance = float.MaxValue;
 27244            distanceVolumeModifier = 0;
 245
 27246            if (attachedMaterials.Count > 0)
 247            {
 27248                using (var iterator = attachedMaterials.GetEnumerator())
 249                {
 45250                    while (iterator.MoveNext())
 251                    {
 27252                        var materialInfo = iterator.Current;
 27253                        bool isComponentVisible = DCLVideoTextureUtils.IsComponentVisible(materialInfo.Value);
 254
 27255                        if (isComponentVisible)
 256                        {
 9257                            isVisible = true;
 258
 9259                            var entityDist = DCLVideoTextureUtils.GetClosestDistanceSqr(materialInfo.Value,
 260                                CommonScriptableObjects.playerUnityPosition);
 261
 9262                            if (entityDist < minDistance)
 9263                                minDistance = entityDist;
 264
 265                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9266                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9267                                break;
 268                        }
 269                    }
 18270                }
 271            }
 272
 27273            if (isVisible)
 274            {
 275                const float maxDistanceBlockForSound = 12;
 9276                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9277                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 278            }
 279
 27280            if (texturePlayer != null)
 281            {
 27282                texturePlayer.visible = isVisible;
 283            }
 284
 27285            UpdateVolume();
 27286        }
 287
 0288        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue) { UpdateVolume(); }
 289
 290        private void UpdateVolume()
 291        {
 44292            if (texturePlayer == null)
 0293                return;
 294
 44295            float targetVolume = 0f;
 296
 44297            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent(CommonScriptableObjects.sc
 298            {
 33299                targetVolume = baseVolume * distanceVolumeModifier;
 33300                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 33301                float sceneSFXSetting = Settings.i.audioSettings.Data.sceneSFXVolume;
 33302                float masterSetting = Settings.i.audioSettings.Data.masterVolume;
 33303                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 304            }
 305
 44306            texturePlayer.SetVolume(targetVolume);
 44307        }
 308
 309        private bool IsPlayerInSameSceneAsComponent(int currentSceneNumber)
 310        {
 54311            if (scene == null)
 0312                return false;
 54313            if (currentSceneNumber <= 0)
 0314                return false;
 315
 54316            return (scene.sceneData.sceneNumber == currentSceneNumber) || (scene.isPersistent);
 317        }
 318
 4319        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) => SetPlayStateDirty();
 320
 17321        private void OnSceneNumberChanged(int current, int previous) => isPlayerInScene = IsPlayerInSameSceneAsComponent
 322
 323        public override void AttachTo(ISharedComponent component)
 324        {
 10325            Assert.IsTrue(component != null, "Attachment must not be null!");
 326
 10327            if (attachedMaterials.ContainsKey(component.id))
 0328                return;
 329
 10330            AddReference(component);
 331
 10332            SetPlayStateDirty();
 10333            attachedMaterials.Add(component.id, component);
 334
 10335            component.OnAttach += SetPlayStateDirty;
 10336            component.OnDetach += SetPlayStateDirty;
 10337            DCLVideoTextureUtils.SubscribeToEntityUpdates(component, SetPlayStateDirty);
 10338        }
 339
 340        public override void DetachFrom(ISharedComponent component)
 341        {
 1342            Assert.IsTrue(component != null, "Component must not be null!");
 343
 1344            if (!attachedMaterials.ContainsKey(component.id))
 0345                return;
 346
 1347            if (texturePlayer != null)
 1348                texturePlayer.Pause();
 349
 1350            attachedMaterials.Remove(component.id);
 351
 1352            component.OnAttach -= SetPlayStateDirty;
 1353            component.OnDetach -= SetPlayStateDirty;
 1354            DCLVideoTextureUtils.UnsubscribeToEntityShapeUpdate(component, SetPlayStateDirty);
 355
 1356            RemoveReference(component);
 1357            SetPlayStateDirty();
 1358        }
 359
 46360        private void SetPlayStateDirty(IDCLEntity entity = null) => isPlayStateDirty = true;
 361
 2362        private void OnAudioSettingsChanged(AudioSettings settings) => UpdateVolume();
 363
 364        public override void Dispose()
 365        {
 1366            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 1367            Settings.i.audioSettings.OnChanged -= OnAudioSettingsChanged;
 1368            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 1369            CommonScriptableObjects.sceneNumber.OnChange -= OnSceneNumberChanged;
 370
 1371            if (scene != null)
 1372                scene.OnEntityRemoved -= SetPlayStateDirty;
 373
 1374            if (texturePlayerUpdateRoutine != null)
 375            {
 1376                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 1377                texturePlayerUpdateRoutine = null;
 378            }
 379
 1380            if (texturePlayer != null)
 381            {
 1382                texturePlayer.Dispose();
 1383                texturePlayer = null;
 384            }
 385
 1386            Utils.SafeDestroy(texture);
 1387            base.Dispose();
 1388        }
 389    }
 390}