< 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:159
Uncovered lines:64
Coverable lines:223
Total lines:483
Line coverage:71.3% (159 of 223)
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%
OnVirtualAudioMixerChangedValue(...)0%2100%
UpdateVolume()0%4.014090.91%
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
 1148        public DCLVideoTexture()
 49        {
 1150            model = new Model();
 51
 1152            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1153        }
 54
 55        public override IEnumerator ApplyChanges(BaseModel newModel)
 56        {
 2257            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 58
 59            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 60            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1161            if (isDisposed)
 062                yield break;
 63
 1164            var model = (Model) newModel;
 65
 1166            unitySamplingMode = model.samplingMode;
 67
 1168            switch (model.wrap)
 69            {
 70                case BabylonWrapMode.CLAMP:
 1171                    unityWrap = TextureWrapMode.Clamp;
 1172                    break;
 73                case BabylonWrapMode.WRAP:
 074                    unityWrap = TextureWrapMode.Repeat;
 075                    break;
 76                case BabylonWrapMode.MIRROR:
 077                    unityWrap = TextureWrapMode.Mirror;
 78                    break;
 79            }
 80
 1181            if (texturePlayer == null)
 82            {
 1183                DCLVideoClip dclVideoClip = scene.GetSharedComponent(model.videoClipId) as DCLVideoClip;
 84
 1185                if (dclVideoClip == null)
 86                {
 087                    Debug.LogError("Wrong video clip type when playing VideoTexture!!");
 088                    yield break;
 89                }
 90
 1191                string videoId = (!string.IsNullOrEmpty(scene.sceneData.id)) ? scene.sceneData.id + id : scene.GetHashCo
 1192                texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream);
 1193                texturePlayerUpdateRoutine = CoroutineStarter.Start(VideoTextureUpdate());
 1194                CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 1195                CommonScriptableObjects.sceneID.OnChange += OnSceneIDChanged;
 1196                scene.OnEntityRemoved += OnEntityRemoved;
 1197                Settings.i.OnGeneralSettingsChanged += OnSettingsChanged;
 98
 1199                OnSceneIDChanged(CommonScriptableObjects.sceneID.Get(), null);
 100            }
 101
 102            // NOTE: create texture for testing cause real texture will only be created on web platform
 11103            if (isTest)
 104            {
 11105                if (texture == null)
 11106                    texture = new Texture2D(1, 1);
 107            }
 108
 11109            if (texture == null)
 110            {
 0111                while (texturePlayer.texture == null && !texturePlayer.isError)
 112                {
 0113                    yield return null;
 114                }
 115
 0116                if (texturePlayer.isError)
 117                {
 0118                    if (texturePlayerUpdateRoutine != null)
 119                    {
 0120                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0121                        texturePlayerUpdateRoutine = null;
 122                    }
 123
 0124                    yield break;
 125                }
 126
 0127                texture = texturePlayer.texture;
 0128                isPlayStateDirty = true;
 129            }
 130
 11131            if (texturePlayer != null)
 132            {
 11133                if (model.seek >= 0)
 134                {
 0135                    texturePlayer.SetTime(model.seek);
 0136                    model.seek = -1;
 137
 138                    // Applying seek is not immediate
 0139                    yield return null;
 140                }
 141
 11142                if (model.playing)
 0143                    texturePlayer.Play();
 144                else
 11145                    texturePlayer.Pause();
 146
 11147                if (baseVolume != model.volume)
 148                {
 11149                    baseVolume = model.volume;
 11150                    UpdateVolume();
 151                }
 152
 11153                texturePlayer.SetPlaybackRate(model.playbackRate);
 11154                texturePlayer.SetLoop(model.loop);
 155            }
 11156        }
 157
 0158        public float GetVolume() { return ((Model) model).volume; }
 159
 0160        private bool HasTexturePropertiesChanged() { return texture.wrapMode != unityWrap || texture.filterMode != unity
 161
 162        private void ApplyTextureProperties()
 163        {
 0164            texture.wrapMode = unityWrap;
 0165            texture.filterMode = unitySamplingMode;
 0166            texture.Compress(false);
 0167            texture.Apply(unitySamplingMode != FilterMode.Point, true);
 0168        }
 169
 170        private IEnumerator VideoTextureUpdate()
 171        {
 46172            while (true)
 173            {
 57174                if (isPlayStateDirty)
 175                {
 20176                    CalculateVideoVolumeAndPlayStatus();
 20177                    isPlayStateDirty = false;
 178                }
 179
 57180                if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL)
 181                {
 0182                    currUpdateIntervalTime += Time.unscaledDeltaTime;
 0183                }
 57184                else if (texturePlayer != null && !isTest)
 185                {
 0186                    currUpdateIntervalTime = 0;
 0187                    texturePlayer.UpdateWebVideoTexture();
 188                }
 189
 57190                yield return null;
 191            }
 192        }
 193
 194        private void CalculateVideoVolumeAndPlayStatus()
 195        {
 20196            isVisible = false;
 20197            float minDistance = float.MaxValue;
 20198            distanceVolumeModifier = 0;
 199
 20200            if (attachedMaterials.Count > 0)
 201            {
 20202                using (var iterator = attachedMaterials.GetEnumerator())
 203                {
 31204                    while (iterator.MoveNext())
 205                    {
 20206                        var materialInfo = iterator.Current;
 20207                        if (materialInfo.Value.IsVisible())
 208                        {
 9209                            isVisible = true;
 9210                            var entityDist = materialInfo.Value.GetClosestDistanceSqr(DCLCharacterController.i.transform
 9211                            if (entityDist < minDistance)
 9212                                minDistance = entityDist;
 213                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9214                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9215                                break;
 216                        }
 217                    }
 11218                }
 219            }
 220
 20221            if (isVisible)
 222            {
 223                const float maxDistanceBlockForSound = 6;
 9224                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9225                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 226            }
 227
 20228            if (texturePlayer != null)
 229            {
 20230                texturePlayer.visible = isVisible;
 231            }
 232
 20233            UpdateVolume();
 20234        }
 235
 236        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue) {
 0237            UpdateVolume();
 0238        }
 239
 240        private void UpdateVolume()
 241        {
 31242            if (texturePlayer == null)
 0243                return;
 244
 31245            float targetVolume = 0f;
 246
 31247            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent((CommonScriptableObjects.s
 24248                targetVolume = baseVolume * distanceVolumeModifier;
 24249                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 24250                float sceneSFXSetting = Settings.i.currentAudioSettings.sceneSFXVolume;
 24251                float masterSetting = Settings.i.currentAudioSettings.masterVolume;
 24252                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 253            }
 254
 31255            texturePlayer.SetVolume(targetVolume);
 31256        }
 257
 258        private bool IsPlayerInSameSceneAsComponent(string currentSceneId)
 259        {
 44260            if (scene == null)
 0261                return false;
 44262            if (string.IsNullOrEmpty(currentSceneId))
 0263                return false;
 264
 44265            return (scene.sceneData.id == currentSceneId) || (scene is GlobalScene globalScene && globalScene.isPortable
 266        }
 267
 8268        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) { isPlayStateDirty = true; }
 269
 26270        private void OnSceneIDChanged(string current, string previous) { isPlayerInScene = IsPlayerInSameSceneAsComponen
 271
 272        public override void AttachTo(PBRMaterial material)
 273        {
 0274            base.AttachTo(material);
 0275            AttachToMaterial(material);
 0276        }
 277
 278        public override void DetachFrom(PBRMaterial material)
 279        {
 0280            base.DetachFrom(material);
 0281            DetachFromMaterial(material);
 0282        }
 283
 284        public override void AttachTo(BasicMaterial material)
 285        {
 10286            base.AttachTo(material);
 10287            AttachToMaterial(material);
 10288        }
 289
 290        public override void DetachFrom(BasicMaterial material)
 291        {
 11292            base.DetachFrom(material);
 11293            DetachFromMaterial(material);
 11294        }
 295
 296        private void AttachToMaterial(BaseDisposable baseDisposable)
 297        {
 10298            if (!attachedMaterials.ContainsKey(baseDisposable.id))
 299            {
 10300                attachedMaterials.Add(baseDisposable.id, new MaterialComponent(baseDisposable));
 10301                baseDisposable.OnAttach += OnEntityAttachedMaterial;
 10302                baseDisposable.OnDetach += OnEntityDetachedMaterial;
 10303                isPlayStateDirty = true;
 304
 10305                if (baseDisposable.attachedEntities.Count > 0)
 306                {
 8307                    using (var iterator = baseDisposable.attachedEntities.GetEnumerator())
 308                    {
 16309                        while (iterator.MoveNext())
 310                        {
 8311                            var entity = iterator.Current;
 8312                            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 8313                            entity.OnShapeUpdated += OnEntityShapeUpdated;
 314                        }
 8315                    }
 316                }
 317            }
 10318        }
 319
 320        private void DetachFromMaterial(BaseDisposable baseDisposable)
 321        {
 11322            if (attachedMaterials.ContainsKey(baseDisposable.id))
 323            {
 10324                attachedMaterials.Remove(baseDisposable.id);
 10325                baseDisposable.OnAttach -= OnEntityAttachedMaterial;
 10326                baseDisposable.OnDetach -= OnEntityDetachedMaterial;
 10327                isPlayStateDirty = true;
 328            }
 11329        }
 330
 331        // TODO: we will need an event for visibility change on UI for supporting video
 332        public override void AttachTo(UIImage image)
 333        {
 0334            if (!attachedMaterials.ContainsKey(image.id))
 335            {
 0336                attachedMaterials.Add(image.id, new UIShapeComponent(image));
 0337                isPlayStateDirty = true;
 338            }
 0339        }
 340
 341        public override void DetachFrom(UIImage image)
 342        {
 0343            if (attachedMaterials.ContainsKey(image.id))
 344            {
 0345                attachedMaterials.Remove(image.id);
 0346                isPlayStateDirty = true;
 347            }
 0348        }
 349
 2350        void OnEntityRemoved(IDCLEntity entity) { isPlayStateDirty = true; }
 351
 0352        void OnSettingsChanged(SettingsData.GeneralSettings settings) { UpdateVolume(); }
 353
 354        public override void Dispose()
 355        {
 12356            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 12357            Settings.i.OnGeneralSettingsChanged -= OnSettingsChanged;
 12358            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 12359            CommonScriptableObjects.sceneID.OnChange -= OnSceneIDChanged;
 12360            if (scene != null)
 12361                scene.OnEntityRemoved -= OnEntityRemoved;
 12362            if (texturePlayerUpdateRoutine != null)
 363            {
 11364                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 11365                texturePlayerUpdateRoutine = null;
 366            }
 367
 12368            if (texturePlayer != null)
 369            {
 11370                texturePlayer.Dispose();
 11371                texturePlayer = null;
 372            }
 373
 12374            Utils.SafeDestroy(texture);
 12375            base.Dispose();
 12376        }
 377
 0378        private void OnEntityAttachedMaterial(IDCLEntity entity) { entity.OnShapeUpdated += OnEntityShapeUpdated; }
 379
 380        private void OnEntityDetachedMaterial(IDCLEntity entity)
 381        {
 1382            if (texturePlayer != null)
 1383                texturePlayer.Pause();
 384
 1385            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 1386        }
 387
 28388        private void OnEntityShapeUpdated(IDCLEntity entity) { isPlayStateDirty = true; }
 389
 390        internal interface MaterialInfo
 391        {
 392            float GetClosestDistanceSqr(Vector3 fromPosition);
 393            bool IsVisible();
 394        }
 395
 396        struct MaterialComponent : MaterialInfo
 397        {
 398            BaseDisposable component;
 399
 400            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition)
 401            {
 9402                float dist = int.MaxValue;
 9403                if (component.attachedEntities.Count > 0)
 404                {
 9405                    using (var iterator = component.attachedEntities.GetEnumerator())
 406                    {
 18407                        while (iterator.MoveNext())
 408                        {
 9409                            var entity = iterator.Current;
 9410                            if (IsEntityVisible(entity))
 411                            {
 9412                                var entityDist = (entity.meshRootGameObject.transform.position - fromPosition).sqrMagnit
 9413                                if (entityDist < dist)
 9414                                    dist = entityDist;
 415                            }
 416                        }
 9417                    }
 418                }
 419
 9420                return dist;
 421            }
 422
 423            bool MaterialInfo.IsVisible()
 424            {
 20425                if (component.attachedEntities.Count > 0)
 426                {
 18427                    using (var iterator = component.attachedEntities.GetEnumerator())
 428                    {
 27429                        while (iterator.MoveNext())
 430                        {
 18431                            if (IsEntityVisible(iterator.Current))
 432                            {
 9433                                return true;
 434                            }
 435                        }
 9436                    }
 437                }
 438
 11439                return false;
 9440            }
 441
 442            bool IsEntityVisible(IDCLEntity entity)
 443            {
 27444                if (entity.meshesInfo == null)
 0445                    return false;
 27446                if (entity.meshesInfo.currentShape == null)
 7447                    return false;
 20448                return entity.meshesInfo.currentShape.IsVisible();
 449            }
 450
 0451            public MaterialComponent(BaseDisposable component) { this.component = component; }
 452        }
 453
 454        struct UIShapeComponent : MaterialInfo
 455        {
 456            UIShape shape;
 457
 0458            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition) { return 0; }
 459
 460            bool MaterialInfo.IsVisible()
 461            {
 0462                if (!((UIShape.Model) shape.GetModel()).visible)
 0463                    return false;
 0464                return IsParentVisible(shape);
 465            }
 466
 467            bool IsParentVisible(UIShape shape)
 468            {
 0469                UIShape parent = shape.parentUIComponent;
 0470                if (parent == null)
 0471                    return true;
 0472                if (parent.referencesContainer.canvasGroup.alpha == 0)
 473                {
 0474                    return false;
 475                }
 476
 0477                return IsParentVisible(parent);
 478            }
 479
 0480            public UIShapeComponent(UIShape image) { shape = image; }
 481        }
 482    }
 483}