< 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:181
Uncovered lines:59
Coverable lines:240
Total lines:516
Line coverage:75.4% (181 of 240)
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%330100%
ReportVideoProgress()0%110100%
IsTimeToReportVideoProgress()0%110100%
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;
 9
 10namespace DCL.Components
 11{
 12    public class DCLVideoTexture : DCLTexture
 13    {
 14#if UNITY_EDITOR
 115        internal static bool isTest = true;
 16#else
 17        internal static bool isTest = false;
 18#endif
 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
 23        [System.Serializable]
 24        new public class Model : BaseModel
 25        {
 26            public string videoClipId;
 27            public bool playing = false;
 4228            public float volume = 1f;
 4229            public float playbackRate = 1f;
 30            public bool loop = false;
 4231            public float seek = -1;
 32            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 4233            public FilterMode samplingMode = FilterMode.Bilinear;
 34
 1435            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 36        }
 37
 38        internal WebVideoPlayer texturePlayer;
 39        private Coroutine texturePlayerUpdateRoutine;
 40        private float baseVolume;
 1441        private float distanceVolumeModifier = 1f;
 42        private bool isPlayStateDirty = false;
 43        internal bool isVisible = false;
 44
 1445        private bool isPlayerInScene = true;
 1446        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS;
 47        private float lastVideoProgressReportTime;
 48
 1449        internal Dictionary<string, MaterialInfo> attachedMaterials = new Dictionary<string, MaterialInfo>();
 50        private string lastVideoClipID;
 51
 1452        public DCLVideoTexture()
 53        {
 1454            model = new Model();
 55
 1456            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1457        }
 58
 59        public override IEnumerator ApplyChanges(BaseModel newModel)
 60        {
 2861            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 62
 63            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 64            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1465            if (isDisposed)
 066                yield break;
 67
 1468            var model = (Model) newModel;
 69
 1470            unitySamplingMode = model.samplingMode;
 71
 1472            switch (model.wrap)
 73            {
 74                case BabylonWrapMode.CLAMP:
 1475                    unityWrap = TextureWrapMode.Clamp;
 1476                    break;
 77                case BabylonWrapMode.WRAP:
 078                    unityWrap = TextureWrapMode.Repeat;
 079                    break;
 80                case BabylonWrapMode.MIRROR:
 081                    unityWrap = TextureWrapMode.Mirror;
 82                    break;
 83            }
 1484            lastVideoClipID = model.videoClipId;
 85
 1486            if (texturePlayer == null)
 87            {
 1488                DCLVideoClip dclVideoClip = scene.GetSharedComponent(lastVideoClipID) as DCLVideoClip;
 89
 1490                if (dclVideoClip == null)
 91                {
 092                    Debug.LogError("Wrong video clip type when playing VideoTexture!!");
 093                    yield break;
 94                }
 95
 1496                string videoId = (!string.IsNullOrEmpty(scene.sceneData.id)) ? scene.sceneData.id + id : scene.GetHashCo
 1497                texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream, new WebVideoPl
 1498                texturePlayerUpdateRoutine = CoroutineStarter.Start(OnUpdate());
 1499                CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 14100                CommonScriptableObjects.sceneID.OnChange += OnSceneIDChanged;
 14101                scene.OnEntityRemoved += OnEntityRemoved;
 14102                Settings.i.OnGeneralSettingsChanged += OnSettingsChanged;
 103
 14104                OnSceneIDChanged(CommonScriptableObjects.sceneID.Get(), null);
 105            }
 106
 107            // NOTE: create texture for testing cause real texture will only be created on web platform
 14108            if (isTest)
 109            {
 14110                if (texture == null)
 14111                    texture = new Texture2D(1, 1);
 112            }
 113
 14114            if (texture == null)
 115            {
 0116                while (texturePlayer.texture == null && !texturePlayer.isError)
 117                {
 0118                    yield return null;
 119                }
 120
 0121                if (texturePlayer.isError)
 122                {
 0123                    if (texturePlayerUpdateRoutine != null)
 124                    {
 0125                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0126                        texturePlayerUpdateRoutine = null;
 127                    }
 128
 0129                    yield break;
 130                }
 131
 0132                texture = texturePlayer.texture;
 0133                isPlayStateDirty = true;
 134            }
 135
 14136            if (texturePlayer != null)
 137            {
 14138                if (model.seek >= 0)
 139                {
 1140                    texturePlayer.SetTime(model.seek);
 1141                    model.seek = -1;
 142
 143                    // Applying seek is not immediate
 1144                    yield return null;
 145                }
 146
 14147                if (model.playing)
 148                {
 2149                    texturePlayer.Play();
 2150                }
 151                else
 152                {
 12153                    texturePlayer.Pause();
 154                }
 155
 14156                ReportVideoProgress();
 157
 14158                if (baseVolume != model.volume)
 159                {
 14160                    baseVolume = model.volume;
 14161                    UpdateVolume();
 162                }
 163
 14164                texturePlayer.SetPlaybackRate(model.playbackRate);
 14165                texturePlayer.SetLoop(model.loop);
 166            }
 14167        }
 168
 0169        public float GetVolume() { return ((Model) model).volume; }
 170
 0171        private bool HasTexturePropertiesChanged() { return texture.wrapMode != unityWrap || texture.filterMode != unity
 172
 173        private void ApplyTextureProperties()
 174        {
 0175            texture.wrapMode = unityWrap;
 0176            texture.filterMode = unitySamplingMode;
 0177            texture.Compress(false);
 0178            texture.Apply(unitySamplingMode != FilterMode.Point, true);
 0179        }
 180
 181        private IEnumerator OnUpdate()
 182        {
 50183            while (true)
 184            {
 64185                UpdateDirtyState();
 64186                UpdateVideoTexture();
 64187                UpdateProgressReport();
 64188                yield return null;
 189            }
 190        }
 191        private void UpdateDirtyState()
 192        {
 64193            if (isPlayStateDirty)
 194            {
 20195                CalculateVideoVolumeAndPlayStatus();
 20196                isPlayStateDirty = false;
 197            }
 64198        }
 199        private void UpdateVideoTexture()
 200        {
 64201            if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS)
 202            {
 0203                currUpdateIntervalTime += Time.unscaledDeltaTime;
 0204            }
 64205            else if (texturePlayer != null && !isTest)
 206            {
 0207                currUpdateIntervalTime = 0;
 0208                texturePlayer.UpdateWebVideoTexture();
 209            }
 64210        }
 211        private void UpdateProgressReport()
 212        {
 64213            if (texturePlayer.playing && IsTimeToReportVideoProgress())
 214            {
 2215                lastVideoProgressReportTime = Time.unscaledTime;
 2216                ReportVideoProgress();
 217            }
 64218        }
 219        private void ReportVideoProgress()
 220        {
 16221            var videoStatus = (int)texturePlayer.GetState();
 16222            var currentOffset = texturePlayer.GetTime();
 16223            var length = texturePlayer.GetDuration();
 16224            WebInterface.ReportVideoProgressEvent(id, scene.sceneData.id, lastVideoClipID, videoStatus, currentOffset, l
 16225        }
 2226        private bool IsTimeToReportVideoProgress() { return Time.unscaledTime - lastVideoProgressReportTime > VIDEO_PROG
 227
 228        private void CalculateVideoVolumeAndPlayStatus()
 229        {
 20230            isVisible = false;
 20231            float minDistance = float.MaxValue;
 20232            distanceVolumeModifier = 0;
 233
 20234            if (attachedMaterials.Count > 0)
 235            {
 20236                using (var iterator = attachedMaterials.GetEnumerator())
 237                {
 31238                    while (iterator.MoveNext())
 239                    {
 20240                        var materialInfo = iterator.Current;
 20241                        if (materialInfo.Value.IsVisible())
 242                        {
 9243                            isVisible = true;
 9244                            var entityDist = materialInfo.Value.GetClosestDistanceSqr(DCLCharacterController.i.transform
 9245                            if (entityDist < minDistance)
 9246                                minDistance = entityDist;
 247                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9248                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9249                                break;
 250                        }
 251                    }
 11252                }
 253            }
 254
 20255            if (isVisible)
 256            {
 257                const float maxDistanceBlockForSound = 6;
 9258                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9259                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 260            }
 261
 20262            if (texturePlayer != null)
 263            {
 20264                texturePlayer.visible = isVisible;
 265            }
 266
 20267            UpdateVolume();
 20268        }
 269
 0270        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue) { UpdateVolume(); }
 271
 272        private void UpdateVolume()
 273        {
 34274            if (texturePlayer == null)
 0275                return;
 276
 34277            float targetVolume = 0f;
 278
 34279            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent((CommonScriptableObjects.s
 280            {
 27281                targetVolume = baseVolume * distanceVolumeModifier;
 27282                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 27283                float sceneSFXSetting = Settings.i.currentAudioSettings.sceneSFXVolume;
 27284                float masterSetting = Settings.i.currentAudioSettings.masterVolume;
 27285                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 286            }
 287
 34288            texturePlayer.SetVolume(targetVolume);
 34289        }
 290
 291        private bool IsPlayerInSameSceneAsComponent(string currentSceneId)
 292        {
 50293            if (scene == null)
 0294                return false;
 50295            if (string.IsNullOrEmpty(currentSceneId))
 0296                return false;
 297
 50298            return (scene.sceneData.id == currentSceneId) || (scene is GlobalScene globalScene && globalScene.isPortable
 299        }
 300
 8301        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) { isPlayStateDirty = true; }
 302
 32303        private void OnSceneIDChanged(string current, string previous) { isPlayerInScene = IsPlayerInSameSceneAsComponen
 304
 305        public override void AttachTo(PBRMaterial material)
 306        {
 0307            base.AttachTo(material);
 0308            AttachToMaterial(material);
 0309        }
 310
 311        public override void DetachFrom(PBRMaterial material)
 312        {
 0313            base.DetachFrom(material);
 0314            DetachFromMaterial(material);
 0315        }
 316
 317        public override void AttachTo(BasicMaterial material)
 318        {
 10319            base.AttachTo(material);
 10320            AttachToMaterial(material);
 10321        }
 322
 323        public override void DetachFrom(BasicMaterial material)
 324        {
 11325            base.DetachFrom(material);
 11326            DetachFromMaterial(material);
 11327        }
 328
 329        private void AttachToMaterial(BaseDisposable baseDisposable)
 330        {
 10331            if (!attachedMaterials.ContainsKey(baseDisposable.id))
 332            {
 10333                attachedMaterials.Add(baseDisposable.id, new MaterialComponent(baseDisposable));
 10334                baseDisposable.OnAttach += OnEntityAttachedMaterial;
 10335                baseDisposable.OnDetach += OnEntityDetachedMaterial;
 10336                isPlayStateDirty = true;
 337
 10338                if (baseDisposable.attachedEntities.Count > 0)
 339                {
 8340                    using (var iterator = baseDisposable.attachedEntities.GetEnumerator())
 341                    {
 16342                        while (iterator.MoveNext())
 343                        {
 8344                            var entity = iterator.Current;
 8345                            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 8346                            entity.OnShapeUpdated += OnEntityShapeUpdated;
 347                        }
 8348                    }
 349                }
 350            }
 10351        }
 352
 353        private void DetachFromMaterial(BaseDisposable baseDisposable)
 354        {
 11355            if (attachedMaterials.ContainsKey(baseDisposable.id))
 356            {
 10357                attachedMaterials.Remove(baseDisposable.id);
 10358                baseDisposable.OnAttach -= OnEntityAttachedMaterial;
 10359                baseDisposable.OnDetach -= OnEntityDetachedMaterial;
 10360                isPlayStateDirty = true;
 361            }
 11362        }
 363
 364        // TODO: we will need an event for visibility change on UI for supporting video
 365        public override void AttachTo(UIImage image)
 366        {
 0367            if (!attachedMaterials.ContainsKey(image.id))
 368            {
 0369                attachedMaterials.Add(image.id, new UIShapeComponent(image));
 0370                isPlayStateDirty = true;
 371            }
 0372        }
 373
 374        public override void DetachFrom(UIImage image)
 375        {
 0376            if (attachedMaterials.ContainsKey(image.id))
 377            {
 0378                attachedMaterials.Remove(image.id);
 0379                isPlayStateDirty = true;
 380            }
 0381        }
 382
 2383        void OnEntityRemoved(IDCLEntity entity) { isPlayStateDirty = true; }
 384
 0385        void OnSettingsChanged(SettingsData.GeneralSettings settings) { UpdateVolume(); }
 386
 387        public override void Dispose()
 388        {
 15389            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 15390            Settings.i.OnGeneralSettingsChanged -= OnSettingsChanged;
 15391            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 15392            CommonScriptableObjects.sceneID.OnChange -= OnSceneIDChanged;
 15393            if (scene != null)
 15394                scene.OnEntityRemoved -= OnEntityRemoved;
 15395            if (texturePlayerUpdateRoutine != null)
 396            {
 14397                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 14398                texturePlayerUpdateRoutine = null;
 399            }
 400
 15401            if (texturePlayer != null)
 402            {
 14403                texturePlayer.Dispose();
 14404                texturePlayer = null;
 405            }
 406
 15407            Utils.SafeDestroy(texture);
 15408            base.Dispose();
 15409        }
 410
 0411        private void OnEntityAttachedMaterial(IDCLEntity entity) { entity.OnShapeUpdated += OnEntityShapeUpdated; }
 412
 413        private void OnEntityDetachedMaterial(IDCLEntity entity)
 414        {
 1415            if (texturePlayer != null)
 1416                texturePlayer.Pause();
 417
 1418            entity.OnShapeUpdated -= OnEntityShapeUpdated;
 1419        }
 420
 28421        private void OnEntityShapeUpdated(IDCLEntity entity) { isPlayStateDirty = true; }
 422
 423        internal interface MaterialInfo
 424        {
 425            float GetClosestDistanceSqr(Vector3 fromPosition);
 426            bool IsVisible();
 427        }
 428
 429        struct MaterialComponent : MaterialInfo
 430        {
 431            BaseDisposable component;
 432
 433            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition)
 434            {
 9435                float dist = int.MaxValue;
 9436                if (component.attachedEntities.Count > 0)
 437                {
 9438                    using (var iterator = component.attachedEntities.GetEnumerator())
 439                    {
 18440                        while (iterator.MoveNext())
 441                        {
 9442                            var entity = iterator.Current;
 9443                            if (IsEntityVisible(entity))
 444                            {
 9445                                var entityDist = (entity.meshRootGameObject.transform.position - fromPosition).sqrMagnit
 9446                                if (entityDist < dist)
 9447                                    dist = entityDist;
 448                            }
 449                        }
 9450                    }
 451                }
 452
 9453                return dist;
 454            }
 455
 456            bool MaterialInfo.IsVisible()
 457            {
 20458                if (component.attachedEntities.Count > 0)
 459                {
 18460                    using (var iterator = component.attachedEntities.GetEnumerator())
 461                    {
 27462                        while (iterator.MoveNext())
 463                        {
 18464                            if (IsEntityVisible(iterator.Current))
 465                            {
 9466                                return true;
 467                            }
 468                        }
 9469                    }
 470                }
 471
 11472                return false;
 9473            }
 474
 475            bool IsEntityVisible(IDCLEntity entity)
 476            {
 27477                if (entity.meshesInfo == null)
 0478                    return false;
 27479                if (entity.meshesInfo.currentShape == null)
 7480                    return false;
 20481                return entity.meshesInfo.currentShape.IsVisible();
 482            }
 483
 0484            public MaterialComponent(BaseDisposable component) { this.component = component; }
 485        }
 486
 487        struct UIShapeComponent : MaterialInfo
 488        {
 489            UIShape shape;
 490
 0491            float MaterialInfo.GetClosestDistanceSqr(Vector3 fromPosition) { return 0; }
 492
 493            bool MaterialInfo.IsVisible()
 494            {
 0495                if (!((UIShape.Model) shape.GetModel()).visible)
 0496                    return false;
 0497                return IsParentVisible(shape);
 498            }
 499
 500            bool IsParentVisible(UIShape shape)
 501            {
 0502                UIShape parent = shape.parentUIComponent;
 0503                if (parent == null)
 0504                    return true;
 0505                if (parent.referencesContainer.canvasGroup.alpha == 0)
 506                {
 0507                    return false;
 508                }
 509
 0510                return IsParentVisible(parent);
 511            }
 512
 0513            public UIShapeComponent(UIShape image) { shape = image; }
 514        }
 515    }
 516}