< 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:183
Uncovered lines:60
Coverable lines:243
Total lines:523
Line coverage:75.3% (183 of 243)
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%36.3524072.22%
GetVolume()0%2100%
HasTexturePropertiesChanged()0%6200%
ApplyTextureProperties()0%2100%
OnUpdate()0%330100%
UpdateDirtyState()0%220100%
UpdateVideoTexture()0%9.665042.86%
UpdateProgressReport()0%440100%
ReportVideoProgress()0%110100%
IsTimeToReportVideoProgress()0%2100%
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;
 8using DCL.Interface;
 9using DCL.SettingsCommon;
 10
 11namespace DCL.Components
 12{
 13    public class DCLVideoTexture : DCLTexture
 14    {
 15#if UNITY_EDITOR
 116        internal static bool isTest = true;
 17#else
 18        internal static bool isTest = false;
 19#endif
 20
 21        private const float OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS = 1.5f;
 22        private const float VIDEO_PROGRESS_UPDATE_INTERVAL_IN_SECONDS = 1f;
 23
 24        [System.Serializable]
 25        new public class Model : BaseModel
 26        {
 27            public string videoClipId;
 28            public bool playing = false;
 4229            public float volume = 1f;
 4230            public float playbackRate = 1f;
 31            public bool loop = false;
 4232            public float seek = -1;
 33            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 4234            public FilterMode samplingMode = FilterMode.Bilinear;
 35
 1436            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 37        }
 38
 39        internal WebVideoPlayer texturePlayer;
 40        private Coroutine texturePlayerUpdateRoutine;
 41        private float baseVolume;
 1442        private float distanceVolumeModifier = 1f;
 43        private bool isPlayStateDirty = false;
 44        internal bool isVisible = false;
 45
 1446        private bool isPlayerInScene = true;
 1447        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS;
 48        private float lastVideoProgressReportTime;
 49
 1450        internal Dictionary<string, MaterialInfo> attachedMaterials = new Dictionary<string, MaterialInfo>();
 51        private string lastVideoClipID;
 52        private VideoState previousVideoState;
 53
 1454        public DCLVideoTexture()
 55        {
 1456            model = new Model();
 57
 1458            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1459        }
 60
 61        public override IEnumerator ApplyChanges(BaseModel newModel)
 62        {
 2863            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 64
 65            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 66            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1467            if (isDisposed)
 068                yield break;
 69
 1470            var model = (Model) newModel;
 71
 1472            unitySamplingMode = model.samplingMode;
 73
 1474            switch (model.wrap)
 75            {
 76                case BabylonWrapMode.CLAMP:
 1477                    unityWrap = TextureWrapMode.Clamp;
 1478                    break;
 79                case BabylonWrapMode.WRAP:
 080                    unityWrap = TextureWrapMode.Repeat;
 081                    break;
 82                case BabylonWrapMode.MIRROR:
 083                    unityWrap = TextureWrapMode.Mirror;
 84                    break;
 85            }
 1486            lastVideoClipID = model.videoClipId;
 87
 1488            if (texturePlayer == null)
 89            {
 1490                DCLVideoClip dclVideoClip = scene.GetSharedComponent(lastVideoClipID) as DCLVideoClip;
 91
 1492                if (dclVideoClip == null)
 93                {
 094                    Debug.LogError("Wrong video clip type when playing VideoTexture!!");
 095                    yield break;
 96                }
 97
 1498                string videoId = (!string.IsNullOrEmpty(scene.sceneData.id)) ? scene.sceneData.id + id : scene.GetHashCo
 1499                texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream, new WebVideoPl
 14100                texturePlayerUpdateRoutine = CoroutineStarter.Start(OnUpdate());
 14101                CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 14102                CommonScriptableObjects.sceneID.OnChange += OnSceneIDChanged;
 14103                scene.OnEntityRemoved += OnEntityRemoved;
 14104                Settings.i.generalSettings.OnChanged += OnSettingsChanged;
 105
 14106                OnSceneIDChanged(CommonScriptableObjects.sceneID.Get(), null);
 107            }
 108
 109            // NOTE: create texture for testing cause real texture will only be created on web platform
 14110            if (isTest)
 111            {
 14112                if (texture == null)
 14113                    texture = new Texture2D(1, 1);
 114            }
 115
 14116            if (texture == null)
 117            {
 0118                while (texturePlayer.texture == null && !texturePlayer.isError)
 119                {
 0120                    yield return null;
 121                }
 122
 0123                if (texturePlayer.isError)
 124                {
 0125                    if (texturePlayerUpdateRoutine != null)
 126                    {
 0127                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0128                        texturePlayerUpdateRoutine = null;
 129                    }
 130
 0131                    yield break;
 132                }
 133
 0134                texture = texturePlayer.texture;
 0135                isPlayStateDirty = true;
 136            }
 137
 14138            if (texturePlayer != null)
 139            {
 14140                if (model.seek >= 0)
 141                {
 1142                    texturePlayer.SetTime(model.seek);
 1143                    model.seek = -1;
 144
 145                    // Applying seek is not immediate
 1146                    yield return null;
 147                }
 148
 14149                if (model.playing)
 150                {
 2151                    texturePlayer.Play();
 2152                }
 153                else
 154                {
 12155                    texturePlayer.Pause();
 156                }
 157
 14158                ReportVideoProgress();
 159
 14160                if (baseVolume != model.volume)
 161                {
 14162                    baseVolume = model.volume;
 14163                    UpdateVolume();
 164                }
 165
 14166                texturePlayer.SetPlaybackRate(model.playbackRate);
 14167                texturePlayer.SetLoop(model.loop);
 168            }
 14169        }
 170
 0171        public float GetVolume() { return ((Model) model).volume; }
 172
 0173        private bool HasTexturePropertiesChanged() { return texture.wrapMode != unityWrap || texture.filterMode != unity
 174
 175        private void ApplyTextureProperties()
 176        {
 0177            texture.wrapMode = unityWrap;
 0178            texture.filterMode = unitySamplingMode;
 0179            texture.Compress(false);
 0180            texture.Apply(unitySamplingMode != FilterMode.Point, true);
 0181        }
 182
 183        private IEnumerator OnUpdate()
 184        {
 49185            while (true)
 186            {
 63187                UpdateDirtyState();
 63188                UpdateVideoTexture();
 63189                UpdateProgressReport();
 63190                yield return null;
 191            }
 192        }
 193        private void UpdateDirtyState()
 194        {
 63195            if (isPlayStateDirty)
 196            {
 20197                CalculateVideoVolumeAndPlayStatus();
 20198                isPlayStateDirty = false;
 199            }
 63200        }
 201        private void UpdateVideoTexture()
 202        {
 63203            if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS)
 204            {
 0205                currUpdateIntervalTime += Time.unscaledDeltaTime;
 0206            }
 63207            else if (texturePlayer != null && !isTest)
 208            {
 0209                currUpdateIntervalTime = 0;
 0210                texturePlayer.UpdateWebVideoTexture();
 211            }
 63212        }
 213        private void UpdateProgressReport()
 214        {
 63215            var currentState = texturePlayer.GetState();
 63216            if ( currentState == VideoState.PLAYING
 217                 && IsTimeToReportVideoProgress()
 218                 || previousVideoState != currentState)
 219            {
 14220                ReportVideoProgress();
 221            }
 63222        }
 223        private void ReportVideoProgress()
 224        {
 28225            lastVideoProgressReportTime = Time.unscaledTime;
 28226            VideoState videoState = texturePlayer.GetState();
 28227            previousVideoState = videoState;
 28228            var videoStatus = (int)videoState;
 28229            var currentOffset = texturePlayer.GetTime();
 28230            var length = texturePlayer.GetDuration();
 28231            WebInterface.ReportVideoProgressEvent(id, scene.sceneData.id, lastVideoClipID, videoStatus, currentOffset, l
 28232        }
 0233        private bool IsTimeToReportVideoProgress() { return Time.unscaledTime - lastVideoProgressReportTime > VIDEO_PROG
 234
 235        private void CalculateVideoVolumeAndPlayStatus()
 236        {
 20237            isVisible = false;
 20238            float minDistance = float.MaxValue;
 20239            distanceVolumeModifier = 0;
 240
 20241            if (attachedMaterials.Count > 0)
 242            {
 20243                using (var iterator = attachedMaterials.GetEnumerator())
 244                {
 31245                    while (iterator.MoveNext())
 246                    {
 20247                        var materialInfo = iterator.Current;
 20248                        if (materialInfo.Value.IsVisible())
 249                        {
 9250                            isVisible = true;
 9251                            var entityDist = materialInfo.Value.GetClosestDistanceSqr(DCLCharacterController.i.transform
 9252                            if (entityDist < minDistance)
 9253                                minDistance = entityDist;
 254                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9255                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9256                                break;
 257                        }
 258                    }
 11259                }
 260            }
 261
 20262            if (isVisible)
 263            {
 264                const float maxDistanceBlockForSound = 6;
 9265                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9266                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 267            }
 268
 20269            if (texturePlayer != null)
 270            {
 20271                texturePlayer.visible = isVisible;
 272            }
 273
 20274            UpdateVolume();
 20275        }
 276
 0277        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue) { UpdateVolume(); }
 278
 279        private void UpdateVolume()
 280        {
 34281            if (texturePlayer == null)
 0282                return;
 283
 34284            float targetVolume = 0f;
 285
 34286            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent((CommonScriptableObjects.s
 287            {
 27288                targetVolume = baseVolume * distanceVolumeModifier;
 27289                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 27290                float sceneSFXSetting = Settings.i.audioSettings.Data.sceneSFXVolume;
 27291                float masterSetting = Settings.i.audioSettings.Data.masterVolume;
 27292                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 293            }
 294
 34295            texturePlayer.SetVolume(targetVolume);
 34296        }
 297
 298        private bool IsPlayerInSameSceneAsComponent(string currentSceneId)
 299        {
 50300            if (scene == null)
 0301                return false;
 50302            if (string.IsNullOrEmpty(currentSceneId))
 0303                return false;
 304
 50305            return (scene.sceneData.id == currentSceneId) || (scene is GlobalScene globalScene && globalScene.isPortable
 306        }
 307
 8308        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) { isPlayStateDirty = true; }
 309
 32310        private void OnSceneIDChanged(string current, string previous) { isPlayerInScene = IsPlayerInSameSceneAsComponen
 311
 312        public override void AttachTo(PBRMaterial material)
 313        {
 0314            base.AttachTo(material);
 0315            AttachToMaterial(material);
 0316        }
 317
 318        public override void DetachFrom(PBRMaterial material)
 319        {
 0320            base.DetachFrom(material);
 0321            DetachFromMaterial(material);
 0322        }
 323
 324        public override void AttachTo(BasicMaterial material)
 325        {
 10326            base.AttachTo(material);
 10327            AttachToMaterial(material);
 10328        }
 329
 330        public override void DetachFrom(BasicMaterial material)
 331        {
 11332            base.DetachFrom(material);
 11333            DetachFromMaterial(material);
 11334        }
 335
 336        private void AttachToMaterial(BaseDisposable baseDisposable)
 337        {
 10338            if (!attachedMaterials.ContainsKey(baseDisposable.id))
 339            {
 10340                attachedMaterials.Add(baseDisposable.id, new MaterialComponent(baseDisposable));
 10341                baseDisposable.OnAttach += OnEntityAttachedMaterial;
 10342                baseDisposable.OnDetach += OnEntityDetachedMaterial;
 10343                isPlayStateDirty = true;
 344
 10345                if (baseDisposable.attachedEntities.Count > 0)
 346                {
 8347                    using (var iterator = baseDisposable.attachedEntities.GetEnumerator())
 348                    {
 16349                        while (iterator.MoveNext())
 350                        {
 8351                            var entity = iterator.Current;
 8352                            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 8353                            entity.OnShapeUpdated += OnEntityShapeUpdated;
 354                        }
 8355                    }
 356                }
 357            }
 10358        }
 359
 360        private void DetachFromMaterial(BaseDisposable baseDisposable)
 361        {
 11362            if (attachedMaterials.ContainsKey(baseDisposable.id))
 363            {
 10364                attachedMaterials.Remove(baseDisposable.id);
 10365                baseDisposable.OnAttach -= OnEntityAttachedMaterial;
 10366                baseDisposable.OnDetach -= OnEntityDetachedMaterial;
 10367                isPlayStateDirty = true;
 368            }
 11369        }
 370
 371        // TODO: we will need an event for visibility change on UI for supporting video
 372        public override void AttachTo(UIImage image)
 373        {
 0374            if (!attachedMaterials.ContainsKey(image.id))
 375            {
 0376                attachedMaterials.Add(image.id, new UIShapeComponent(image));
 0377                isPlayStateDirty = true;
 378            }
 0379        }
 380
 381        public override void DetachFrom(UIImage image)
 382        {
 0383            if (attachedMaterials.ContainsKey(image.id))
 384            {
 0385                attachedMaterials.Remove(image.id);
 0386                isPlayStateDirty = true;
 387            }
 0388        }
 389
 2390        void OnEntityRemoved(IDCLEntity entity) { isPlayStateDirty = true; }
 391
 0392        void OnSettingsChanged(GeneralSettings settings) { UpdateVolume(); }
 393
 394        public override void Dispose()
 395        {
 15396            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 15397            Settings.i.generalSettings.OnChanged -= OnSettingsChanged;
 15398            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 15399            CommonScriptableObjects.sceneID.OnChange -= OnSceneIDChanged;
 15400            if (scene != null)
 15401                scene.OnEntityRemoved -= OnEntityRemoved;
 15402            if (texturePlayerUpdateRoutine != null)
 403            {
 14404                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 14405                texturePlayerUpdateRoutine = null;
 406            }
 407
 15408            if (texturePlayer != null)
 409            {
 14410                texturePlayer.Dispose();
 14411                texturePlayer = null;
 412            }
 413
 15414            Utils.SafeDestroy(texture);
 15415            base.Dispose();
 15416        }
 417
 0418        private void OnEntityAttachedMaterial(IDCLEntity entity) { entity.OnShapeUpdated += OnEntityShapeUpdated; }
 419
 420        private void OnEntityDetachedMaterial(IDCLEntity entity)
 421        {
 1422            if (texturePlayer != null)
 1423                texturePlayer.Pause();
 424
 1425            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 1426        }
 427
 28428        private void OnEntityShapeUpdated(IDCLEntity entity) { isPlayStateDirty = true; }
 429
 430        internal interface MaterialInfo
 431        {
 432            float GetClosestDistanceSqr(Vector3 fromPosition);
 433            bool IsVisible();
 434        }
 435
 436        struct MaterialComponent : MaterialInfo
 437        {
 438            BaseDisposable component;
 439
 440            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition)
 441            {
 9442                float dist = int.MaxValue;
 9443                if (component.attachedEntities.Count > 0)
 444                {
 9445                    using (var iterator = component.attachedEntities.GetEnumerator())
 446                    {
 18447                        while (iterator.MoveNext())
 448                        {
 9449                            var entity = iterator.Current;
 9450                            if (IsEntityVisible(entity))
 451                            {
 9452                                var entityDist = (entity.meshRootGameObject.transform.position - fromPosition).sqrMagnit
 9453                                if (entityDist < dist)
 9454                                    dist = entityDist;
 455                            }
 456                        }
 9457                    }
 458                }
 459
 9460                return dist;
 461            }
 462
 463            bool MaterialInfo.IsVisible()
 464            {
 20465                if (component.attachedEntities.Count > 0)
 466                {
 18467                    using (var iterator = component.attachedEntities.GetEnumerator())
 468                    {
 27469                        while (iterator.MoveNext())
 470                        {
 18471                            if (IsEntityVisible(iterator.Current))
 472                            {
 9473                                return true;
 474                            }
 475                        }
 9476                    }
 477                }
 478
 11479                return false;
 9480            }
 481
 482            bool IsEntityVisible(IDCLEntity entity)
 483            {
 27484                if (entity.meshesInfo == null)
 0485                    return false;
 27486                if (entity.meshesInfo.currentShape == null)
 7487                    return false;
 20488                return entity.meshesInfo.currentShape.IsVisible();
 489            }
 490
 0491            public MaterialComponent(BaseDisposable component) { this.component = component; }
 492        }
 493
 494        struct UIShapeComponent : MaterialInfo
 495        {
 496            UIShape shape;
 497
 0498            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition) { return 0; }
 499
 500            bool MaterialInfo.IsVisible()
 501            {
 0502                if (!((UIShape.Model) shape.GetModel()).visible)
 0503                    return false;
 0504                return IsParentVisible(shape);
 505            }
 506
 507            bool IsParentVisible(UIShape shape)
 508            {
 0509                UIShape parent = shape.parentUIComponent;
 0510                if (parent == null)
 0511                    return true;
 0512                if (parent.referencesContainer.canvasGroup.alpha == 0)
 513                {
 0514                    return false;
 515                }
 516
 0517                return IsParentVisible(parent);
 518            }
 519
 0520            public UIShapeComponent(UIShape image) { shape = image; }
 521        }
 522    }
 523}