< Summary

Class:DCL.Components.DCLVideoTexture
Assembly:MainScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Video/DCLVideoTexture.cs
Covered lines:151
Uncovered lines:62
Coverable lines:213
Total lines:468
Line coverage:70.8% (151 of 213)
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%53.7724062.75%
GetVolume()0%2100%
HasTexturePropertiesChanged()0%6200%
ApplyTextureProperties()0%2100%
VideoTextureUpdate()0%11.088063.64%
CalculateVideoVolumeAndPlayStatus()0%880100%
UpdateVolume()0%4.054085.71%
IsPlayerInSameSceneAsComponent(...)0%6.65060%
OnPlayerCoordsChanged(...)0%110100%
OnSceneIDChanged(...)0%110100%
AttachTo(...)0%2100%
DetachFrom(...)0%2100%
AttachTo(...)0%110100%
DetachFrom(...)0%110100%
AttachToMaterial(...)0%440100%
DetachFromMaterial(...)0%220100%
AttachTo(...)0%6200%
DetachFrom(...)0%6200%
OnEntityRemoved(...)0%110100%
OnSettingsChanged(...)0%2100%
Dispose()0%440100%
OnEntityAttachedMaterial(...)0%2100%
OnEntityDetachedMaterial(...)0%220100%
OnEntityShapeUpdated(...)0%110100%
GetClosestDistanceSqr(...)0%550100%
IsVisible()0%440100%
IsEntityVisible(...)0%3.073080%
MaterialComponent(...)0%2100%
GetClosestDistanceSqr(...)0%2100%
IsVisible()0%6200%
IsParentVisible(...)0%12300%
UIShapeComponent(...)0%2100%

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;
 8
 9namespace DCL.Components
 10{
 11    public class DCLVideoTexture : DCLTexture
 12    {
 13#if UNITY_EDITOR
 114        internal static bool isTest = true;
 15#else
 16        internal static bool isTest = false;
 17#endif
 18
 19        private const float OUTOFSCENE_TEX_UPDATE_INTERVAL = 1.5f;
 20
 21        [System.Serializable]
 22        new public class Model : BaseModel
 23        {
 24            public string videoClipId;
 25            public bool playing = false;
 3326            public float volume = 1f;
 3327            public float playbackRate = 1f;
 28            public bool loop = false;
 3329            public float seek = -1;
 30            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 3331            public FilterMode samplingMode = FilterMode.Bilinear;
 32
 1133            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 34        }
 35
 36        internal WebVideoPlayer texturePlayer;
 37        private Coroutine texturePlayerUpdateRoutine;
 38        private float baseVolume;
 1139        private float distanceVolumeModifier = 1f;
 40        private bool isPlayStateDirty = false;
 41        internal bool isVisible = false;
 42
 1143        private bool isPlayerInScene = true;
 1144        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL;
 45
 1146        internal Dictionary<string, MaterialInfo> attachedMaterials = new Dictionary<string, MaterialInfo>();
 47
 3348        public DCLVideoTexture() { model = new Model(); }
 49
 50        public override IEnumerator ApplyChanges(BaseModel newModel)
 51        {
 2252            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 53
 54            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 55            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1156            if (isDisposed)
 057                yield break;
 58
 1159            var model = (Model) newModel;
 60
 1161            unitySamplingMode = model.samplingMode;
 62
 1163            switch (model.wrap)
 64            {
 65                case BabylonWrapMode.CLAMP:
 1166                    unityWrap = TextureWrapMode.Clamp;
 1167                    break;
 68                case BabylonWrapMode.WRAP:
 069                    unityWrap = TextureWrapMode.Repeat;
 070                    break;
 71                case BabylonWrapMode.MIRROR:
 072                    unityWrap = TextureWrapMode.Mirror;
 73                    break;
 74            }
 75
 1176            if (texturePlayer == null)
 77            {
 1178                DCLVideoClip dclVideoClip = scene.GetSharedComponent(model.videoClipId) as DCLVideoClip;
 79
 1180                if (dclVideoClip == null)
 81                {
 082                    Debug.LogError("Wrong video clip type when playing VideoTexture!!");
 083                    yield break;
 84                }
 85
 1186                string videoId = (!string.IsNullOrEmpty(scene.sceneData.id)) ? scene.sceneData.id + id : scene.GetHashCo
 1187                texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream);
 1188                texturePlayerUpdateRoutine = CoroutineStarter.Start(VideoTextureUpdate());
 1189                CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 1190                CommonScriptableObjects.sceneID.OnChange += OnSceneIDChanged;
 1191                scene.OnEntityRemoved += OnEntityRemoved;
 1192                Settings.i.OnGeneralSettingsChanged += OnSettingsChanged;
 93
 1194                OnSceneIDChanged(CommonScriptableObjects.sceneID.Get(), null);
 95            }
 96
 97            // NOTE: create texture for testing cause real texture will only be created on web platform
 1198            if (isTest)
 99            {
 11100                if (texture == null)
 11101                    texture = new Texture2D(1, 1);
 102            }
 103
 11104            if (texture == null)
 105            {
 0106                while (texturePlayer.texture == null && !texturePlayer.isError)
 107                {
 0108                    yield return null;
 109                }
 110
 0111                if (texturePlayer.isError)
 112                {
 0113                    if (texturePlayerUpdateRoutine != null)
 114                    {
 0115                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0116                        texturePlayerUpdateRoutine = null;
 117                    }
 118
 0119                    yield break;
 120                }
 121
 0122                texture = texturePlayer.texture;
 0123                isPlayStateDirty = true;
 124            }
 125
 11126            if (texturePlayer != null)
 127            {
 11128                if (model.seek >= 0)
 129                {
 0130                    texturePlayer.SetTime(model.seek);
 0131                    model.seek = -1;
 132
 133                    // Applying seek is not immediate
 0134                    yield return null;
 135                }
 136
 11137                if (model.playing)
 0138                    texturePlayer.Play();
 139                else
 11140                    texturePlayer.Pause();
 141
 11142                if (baseVolume != model.volume)
 143                {
 11144                    baseVolume = model.volume;
 11145                    UpdateVolume();
 146                }
 147
 11148                texturePlayer.SetPlaybackRate(model.playbackRate);
 11149                texturePlayer.SetLoop(model.loop);
 150            }
 11151        }
 152
 0153        public float GetVolume() { return ((Model) model).volume; }
 154
 0155        private bool HasTexturePropertiesChanged() { return texture.wrapMode != unityWrap || texture.filterMode != unity
 156
 157        private void ApplyTextureProperties()
 158        {
 0159            texture.wrapMode = unityWrap;
 0160            texture.filterMode = unitySamplingMode;
 0161            texture.Compress(false);
 0162            texture.Apply(unitySamplingMode != FilterMode.Point, true);
 0163        }
 164
 165        private IEnumerator VideoTextureUpdate()
 166        {
 46167            while (true)
 168            {
 57169                if (isPlayStateDirty)
 170                {
 20171                    CalculateVideoVolumeAndPlayStatus();
 20172                    isPlayStateDirty = false;
 173                }
 174
 57175                if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL)
 176                {
 0177                    currUpdateIntervalTime += Time.unscaledDeltaTime;
 0178                }
 57179                else if (texturePlayer != null && !isTest)
 180                {
 0181                    currUpdateIntervalTime = 0;
 0182                    texturePlayer.UpdateWebVideoTexture();
 183                }
 184
 57185                yield return null;
 186            }
 187        }
 188
 189        private void CalculateVideoVolumeAndPlayStatus()
 190        {
 20191            isVisible = false;
 20192            float minDistance = float.MaxValue;
 20193            distanceVolumeModifier = 0;
 194
 20195            if (attachedMaterials.Count > 0)
 196            {
 20197                using (var iterator = attachedMaterials.GetEnumerator())
 198                {
 31199                    while (iterator.MoveNext())
 200                    {
 20201                        var materialInfo = iterator.Current;
 20202                        if (materialInfo.Value.IsVisible())
 203                        {
 9204                            isVisible = true;
 9205                            var entityDist = materialInfo.Value.GetClosestDistanceSqr(DCLCharacterController.i.transform
 9206                            if (entityDist < minDistance)
 9207                                minDistance = entityDist;
 208                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9209                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9210                                break;
 211                        }
 212                    }
 11213                }
 214            }
 215
 20216            if (isVisible)
 217            {
 218                const float maxDistanceBlockForSound = 6;
 9219                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9220                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 221            }
 222
 20223            if (texturePlayer != null)
 224            {
 20225                texturePlayer.visible = isVisible;
 226            }
 227
 20228            UpdateVolume();
 20229        }
 230
 231        private void UpdateVolume()
 232        {
 31233            if (texturePlayer == null)
 0234                return;
 235
 31236            float targetVolume = 0f;
 237
 31238            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent((CommonScriptableObjects.s
 24239                targetVolume = baseVolume * distanceVolumeModifier * Settings.i.generalSettings.sfxVolume;
 240
 31241            texturePlayer.SetVolume(targetVolume);
 31242        }
 243
 244        private bool IsPlayerInSameSceneAsComponent(string currentSceneId)
 245        {
 44246            if (scene == null)
 0247                return false;
 44248            if (string.IsNullOrEmpty(currentSceneId))
 0249                return false;
 250
 44251            return (scene.sceneData.id == currentSceneId) || (scene is GlobalScene globalScene && globalScene.isPortable
 252        }
 253
 8254        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) { isPlayStateDirty = true; }
 255
 26256        private void OnSceneIDChanged(string current, string previous) { isPlayerInScene = IsPlayerInSameSceneAsComponen
 257
 258        public override void AttachTo(PBRMaterial material)
 259        {
 0260            base.AttachTo(material);
 0261            AttachToMaterial(material);
 0262        }
 263
 264        public override void DetachFrom(PBRMaterial material)
 265        {
 0266            base.DetachFrom(material);
 0267            DetachFromMaterial(material);
 0268        }
 269
 270        public override void AttachTo(BasicMaterial material)
 271        {
 10272            base.AttachTo(material);
 10273            AttachToMaterial(material);
 10274        }
 275
 276        public override void DetachFrom(BasicMaterial material)
 277        {
 11278            base.DetachFrom(material);
 11279            DetachFromMaterial(material);
 11280        }
 281
 282        private void AttachToMaterial(BaseDisposable baseDisposable)
 283        {
 10284            if (!attachedMaterials.ContainsKey(baseDisposable.id))
 285            {
 10286                attachedMaterials.Add(baseDisposable.id, new MaterialComponent(baseDisposable));
 10287                baseDisposable.OnAttach += OnEntityAttachedMaterial;
 10288                baseDisposable.OnDetach += OnEntityDetachedMaterial;
 10289                isPlayStateDirty = true;
 290
 10291                if (baseDisposable.attachedEntities.Count > 0)
 292                {
 8293                    using (var iterator = baseDisposable.attachedEntities.GetEnumerator())
 294                    {
 16295                        while (iterator.MoveNext())
 296                        {
 8297                            var entity = iterator.Current;
 8298                            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 8299                            entity.OnShapeUpdated += OnEntityShapeUpdated;
 300                        }
 8301                    }
 302                }
 303            }
 10304        }
 305
 306        private void DetachFromMaterial(BaseDisposable baseDisposable)
 307        {
 11308            if (attachedMaterials.ContainsKey(baseDisposable.id))
 309            {
 10310                attachedMaterials.Remove(baseDisposable.id);
 10311                baseDisposable.OnAttach -= OnEntityAttachedMaterial;
 10312                baseDisposable.OnDetach -= OnEntityDetachedMaterial;
 10313                isPlayStateDirty = true;
 314            }
 11315        }
 316
 317        // TODO: we will need an event for visibility change on UI for supporting video
 318        public override void AttachTo(UIImage image)
 319        {
 0320            if (!attachedMaterials.ContainsKey(image.id))
 321            {
 0322                attachedMaterials.Add(image.id, new UIShapeComponent(image));
 0323                isPlayStateDirty = true;
 324            }
 0325        }
 326
 327        public override void DetachFrom(UIImage image)
 328        {
 0329            if (attachedMaterials.ContainsKey(image.id))
 330            {
 0331                attachedMaterials.Remove(image.id);
 0332                isPlayStateDirty = true;
 333            }
 0334        }
 335
 2336        void OnEntityRemoved(IDCLEntity entity) { isPlayStateDirty = true; }
 337
 0338        void OnSettingsChanged(SettingsData.GeneralSettings settings) { UpdateVolume(); }
 339
 340        public override void Dispose()
 341        {
 12342            Settings.i.OnGeneralSettingsChanged -= OnSettingsChanged;
 12343            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 12344            CommonScriptableObjects.sceneID.OnChange -= OnSceneIDChanged;
 12345            if (scene != null)
 12346                scene.OnEntityRemoved -= OnEntityRemoved;
 12347            if (texturePlayerUpdateRoutine != null)
 348            {
 11349                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 11350                texturePlayerUpdateRoutine = null;
 351            }
 352
 12353            if (texturePlayer != null)
 354            {
 11355                texturePlayer.Dispose();
 11356                texturePlayer = null;
 357            }
 358
 12359            Utils.SafeDestroy(texture);
 12360            base.Dispose();
 12361        }
 362
 0363        private void OnEntityAttachedMaterial(IDCLEntity entity) { entity.OnShapeUpdated += OnEntityShapeUpdated; }
 364
 365        private void OnEntityDetachedMaterial(IDCLEntity entity)
 366        {
 1367            if (texturePlayer != null)
 1368                texturePlayer.Pause();
 369
 1370            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 1371        }
 372
 28373        private void OnEntityShapeUpdated(IDCLEntity entity) { isPlayStateDirty = true; }
 374
 375        internal interface MaterialInfo
 376        {
 377            float GetClosestDistanceSqr(Vector3 fromPosition);
 378            bool IsVisible();
 379        }
 380
 381        struct MaterialComponent : MaterialInfo
 382        {
 383            BaseDisposable component;
 384
 385            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition)
 386            {
 9387                float dist = int.MaxValue;
 9388                if (component.attachedEntities.Count > 0)
 389                {
 9390                    using (var iterator = component.attachedEntities.GetEnumerator())
 391                    {
 18392                        while (iterator.MoveNext())
 393                        {
 9394                            var entity = iterator.Current;
 9395                            if (IsEntityVisible(entity))
 396                            {
 9397                                var entityDist = (entity.meshRootGameObject.transform.position - fromPosition).sqrMagnit
 9398                                if (entityDist < dist)
 9399                                    dist = entityDist;
 400                            }
 401                        }
 9402                    }
 403                }
 404
 9405                return dist;
 406            }
 407
 408            bool MaterialInfo.IsVisible()
 409            {
 20410                if (component.attachedEntities.Count > 0)
 411                {
 18412                    using (var iterator = component.attachedEntities.GetEnumerator())
 413                    {
 27414                        while (iterator.MoveNext())
 415                        {
 18416                            if (IsEntityVisible(iterator.Current))
 417                            {
 9418                                return true;
 419                            }
 420                        }
 9421                    }
 422                }
 423
 11424                return false;
 9425            }
 426
 427            bool IsEntityVisible(IDCLEntity entity)
 428            {
 27429                if (entity.meshesInfo == null)
 0430                    return false;
 27431                if (entity.meshesInfo.currentShape == null)
 7432                    return false;
 20433                return entity.meshesInfo.currentShape.IsVisible();
 434            }
 435
 0436            public MaterialComponent(BaseDisposable component) { this.component = component; }
 437        }
 438
 439        struct UIShapeComponent : MaterialInfo
 440        {
 441            UIShape shape;
 442
 0443            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition) { return 0; }
 444
 445            bool MaterialInfo.IsVisible()
 446            {
 0447                if (!((UIShape.Model) shape.GetModel()).visible)
 0448                    return false;
 0449                return IsParentVisible(shape);
 450            }
 451
 452            bool IsParentVisible(UIShape shape)
 453            {
 0454                UIShape parent = shape.parentUIComponent;
 0455                if (parent == null)
 0456                    return true;
 0457                if (parent.referencesContainer.canvasGroup.alpha == 0)
 458                {
 0459                    return false;
 460                }
 461
 0462                return IsParentVisible(parent);
 463            }
 464
 0465            public UIShapeComponent(UIShape image) { shape = image; }
 466        }
 467    }
 468}