< 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:167
Uncovered lines:28
Coverable lines:195
Total lines:413
Line coverage:85.6% (167 of 195)
Covered branches:0
Total branches:0
Covered methods:24
Total methods:26
Method coverage:92.3% (24 of 26)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
DCLVideoTexture()0%110100%
Model()0%110100%
GetDataFromJSON(...)0%110100%
GetDataFromPb(...)0%1101000%
DCLVideoTexture()0%110100%
ApplyChanges()0%26.720074.42%
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%5.015092.86%
SetPlayStateDirty(...)0%110100%
OnAudioSettingsChanged(...)0%110100%
Dispose()0%110100%
DisposeTexture()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;
 12using Decentraland.Sdk.Ecs6;
 13
 14namespace DCL.Components
 15{
 16    public class DCLVideoTexture : DCLTexture
 17    {
 118        public static bool VERBOSE = false;
 119        public static Logger logger = new Logger("DCLVideoTexture") { verboseEnabled = VERBOSE };
 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
 124        public static System.Func<IVideoPluginWrapper> videoPluginWrapperBuilder = () => new VideoPluginWrapper_WebGL();
 25
 26        [System.Serializable]
 27        public new class Model : BaseModel
 28        {
 29            public string videoClipId;
 30            public bool playing = false;
 5131            public float volume = 1f;
 5132            public float playbackRate = 1f;
 33            public bool loop = false;
 5134            public float seek = -1;
 35            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 5136            public FilterMode samplingMode = FilterMode.Bilinear;
 37
 38            public override BaseModel GetDataFromJSON(string json) =>
 1739                Utils.SafeFromJson<Model>(json);
 40
 41            public override BaseModel GetDataFromPb(ComponentBodyPayload pbModel)
 42            {
 043                if (pbModel.PayloadCase != ComponentBodyPayload.PayloadOneofCase.VideoTexture)
 044                    return Utils.SafeUnimplemented<DCLVideoTexture, Model>(expected: ComponentBodyPayload.PayloadOneofCa
 45
 046                var pb = new Model();
 047                if (pbModel.VideoTexture.HasVideoClipId) pb.videoClipId = pbModel.VideoTexture.VideoClipId;
 048                if (pbModel.VideoTexture.HasPlaying) pb.playing = pbModel.VideoTexture.Playing;
 049                if (pbModel.VideoTexture.HasVolume) pb.volume = pbModel.VideoTexture.Volume;
 050                if (pbModel.VideoTexture.HasPlaybackRate) pb.playbackRate = pbModel.VideoTexture.PlaybackRate;
 051                if (pbModel.VideoTexture.HasLoop) pb.loop = pbModel.VideoTexture.Loop;
 052                if (pbModel.VideoTexture.HasSeek) pb.seek = pbModel.VideoTexture.Seek;
 053                if (pbModel.VideoTexture.HasWrap) pb.wrap = (BabylonWrapMode)pbModel.VideoTexture.Wrap;
 054                if (pbModel.VideoTexture.HasSamplingMode) pb.samplingMode = (FilterMode)pbModel.VideoTexture.SamplingMod
 55
 056                return pb;
 57            }
 58        }
 59
 60        internal WebVideoPlayer texturePlayer;
 61        private Coroutine texturePlayerUpdateRoutine;
 62        private float baseVolume;
 1763        private float distanceVolumeModifier = 1f;
 64        private bool isPlayStateDirty = false;
 65        internal bool isVisible = false;
 66
 67        private bool isInitialized = false;
 1768        private bool isPlayerInScene = true;
 1769        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS;
 70        private float lastVideoProgressReportTime;
 71
 1772        internal Dictionary<string, ISharedComponent> attachedMaterials = new Dictionary<string, ISharedComponent>();
 73        private string lastVideoClipID;
 74        private VideoState previousVideoState;
 75
 1776        public DCLVideoTexture()
 77        {
 1778            model = new Model();
 79
 1780            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1781        }
 82
 83        public override IEnumerator ApplyChanges(BaseModel newModel)
 84        {
 3685            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 86
 87            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 88            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1889            if (isDisposed) { yield break; }
 90
 1891            var model = (Model)newModel;
 92
 1893            unitySamplingMode = model.samplingMode;
 94
 1895            switch (model.wrap)
 96            {
 97                case BabylonWrapMode.CLAMP:
 1898                    unityWrap = TextureWrapMode.Clamp;
 1899                    break;
 100                case BabylonWrapMode.WRAP:
 0101                    unityWrap = TextureWrapMode.Repeat;
 0102                    break;
 103                case BabylonWrapMode.MIRROR:
 0104                    unityWrap = TextureWrapMode.Mirror;
 105                    break;
 106            }
 107
 18108            lastVideoClipID = model.videoClipId;
 109
 18110            if (texturePlayer == null)
 111            {
 18112                DCLVideoClip dclVideoClip = scene.componentsManagerLegacy.GetSceneSharedComponent(lastVideoClipID) as DC
 113
 18114                if (dclVideoClip == null)
 115                {
 0116                    logger.Error("Wrong video clip type when playing VideoTexture!!");
 0117                    yield break;
 118                }
 119
 18120                Initialize(dclVideoClip);
 121            }
 122
 18123            if (texture == null)
 124            {
 1322125                yield return new WaitUntil(() => texturePlayer == null || (texturePlayer.texture != null && texturePlaye
 126
 18127                if (texturePlayer == null || texturePlayer.isError)
 128                {
 0129                    if (texturePlayerUpdateRoutine == null) yield break;
 130
 0131                    CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0132                    texturePlayerUpdateRoutine = null;
 133
 0134                    yield break;
 135                }
 136
 18137                texture = texturePlayer.texture;
 18138                SetPlayStateDirty();
 139            }
 140
 18141            if (texturePlayer != null)
 142            {
 18143                if (model.seek >= 0)
 144                {
 1145                    texturePlayer.SetTime(model.seek);
 1146                    model.seek = -1;
 147
 148                    // Applying seek is not immediate
 1149                    yield return null;
 150                }
 151
 21152                if (model.playing) { texturePlayer.Play(); }
 15153                else { texturePlayer.Pause(); }
 154
 18155                ReportVideoProgress();
 156
 18157                if (baseVolume != model.volume)
 158                {
 17159                    baseVolume = model.volume;
 17160                    UpdateVolume();
 161                }
 162
 18163                texturePlayer.SetPlaybackRate(model.playbackRate);
 18164                texturePlayer.SetLoop(model.loop);
 165            }
 18166        }
 167
 168        private void Initialize(DCLVideoClip dclVideoClip)
 169        {
 18170            if (isInitialized) return;
 18171            isInitialized = true;
 172
 18173            string videoId = scene.sceneData.sceneNumber > 0 ? scene.sceneData.sceneNumber + id : scene.GetHashCode().To
 18174            texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream, videoPluginWrapper
 18175            texturePlayerUpdateRoutine = CoroutineStarter.Start(OnUpdate());
 176
 18177            CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 18178            CommonScriptableObjects.sceneNumber.OnChange += OnSceneNumberChanged;
 18179            scene.OnEntityRemoved += SetPlayStateDirty;
 18180            Settings.i.audioSettings.OnChanged += OnAudioSettingsChanged;
 181
 18182            OnSceneNumberChanged(CommonScriptableObjects.sceneNumber.Get(), -1);
 18183        }
 184
 185        public float GetVolume()
 186        {
 2187            return ((Model)model).volume;
 188        }
 189
 190        private IEnumerator OnUpdate()
 191        {
 1398192            while (true)
 193            {
 1416194                UpdateDirtyState();
 1416195                UpdateVideoTexture();
 1416196                UpdateProgressReport();
 1416197                yield return null;
 198            }
 199        }
 200
 201        private void UpdateDirtyState()
 202        {
 1416203            if (!isPlayStateDirty)
 1375204                return;
 205
 41206            CalculateVideoVolumeAndPlayStatus();
 41207            isPlayStateDirty = false;
 41208        }
 209
 210        private void UpdateVideoTexture()
 211        {
 2644212            if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS) { currUpdateInte
 188213            else if (texturePlayer != null)
 214            {
 188215                currUpdateIntervalTime = 0;
 188216                texturePlayer.Update();
 188217                texture = texturePlayer.texture;
 218            }
 188219        }
 220
 221        private void UpdateProgressReport()
 222        {
 1416223            var currentState = texturePlayer.GetState();
 224
 1416225            if (currentState == VideoState.PLAYING
 226                && IsTimeToReportVideoProgress()
 36227                || previousVideoState != currentState) { ReportVideoProgress(); }
 1416228        }
 229
 230        private void ReportVideoProgress()
 231        {
 54232            lastVideoProgressReportTime = Time.unscaledTime;
 54233            VideoState videoState = texturePlayer.GetState();
 54234            previousVideoState = videoState;
 54235            var videoStatus = (int)videoState;
 54236            var currentOffset = texturePlayer.GetTime();
 54237            var length = texturePlayer.GetDuration();
 54238            WebInterface.ReportVideoProgressEvent(id, scene.sceneData.sceneNumber, lastVideoClipID, videoStatus, current
 54239        }
 240
 241        private bool IsTimeToReportVideoProgress()
 242        {
 14243            return Time.unscaledTime - lastVideoProgressReportTime > VIDEO_PROGRESS_UPDATE_INTERVAL_IN_SECONDS;
 244        }
 245
 246        private void CalculateVideoVolumeAndPlayStatus()
 247        {
 41248            isVisible = false;
 41249            float minDistance = float.MaxValue;
 41250            distanceVolumeModifier = 0;
 251
 41252            if (attachedMaterials.Count > 0)
 253            {
 32254                using (var iterator = attachedMaterials.GetEnumerator())
 255                {
 55256                    while (iterator.MoveNext())
 257                    {
 32258                        var materialInfo = iterator.Current;
 32259                        bool isComponentVisible = DCLVideoTextureUtils.IsComponentVisible(materialInfo.Value);
 260
 32261                        if (isComponentVisible)
 262                        {
 9263                            isVisible = true;
 264
 9265                            var entityDist = DCLVideoTextureUtils.GetClosestDistanceSqr(materialInfo.Value,
 266                                CommonScriptableObjects.playerUnityPosition);
 267
 9268                            if (entityDist < minDistance)
 9269                                minDistance = entityDist;
 270
 271                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9272                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9273                                break;
 274                        }
 275                    }
 23276                }
 277            }
 278
 41279            if (isVisible)
 280            {
 281                const float maxDistanceBlockForSound = 12;
 9282                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9283                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 284            }
 285
 82286            if (texturePlayer != null) { texturePlayer.visible = isVisible; }
 287
 41288            UpdateVolume();
 41289        }
 290
 291        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue)
 292        {
 0293            UpdateVolume();
 0294        }
 295
 296        private void UpdateVolume()
 297        {
 60298            if (texturePlayer == null)
 0299                return;
 300
 60301            float targetVolume = 0f;
 302
 60303            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent(CommonScriptableObjects.sc
 304            {
 48305                targetVolume = baseVolume * distanceVolumeModifier;
 48306                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 48307                float sceneSFXSetting = Settings.i.audioSettings.Data.sceneSFXVolume;
 48308                float masterSetting = Settings.i.audioSettings.Data.masterVolume;
 48309                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 310            }
 311
 60312            texturePlayer.SetVolume(targetVolume);
 60313        }
 314
 315        private bool IsPlayerInSameSceneAsComponent(int currentSceneNumber)
 316        {
 72317            if (scene == null)
 0318                return false;
 319
 72320            if (currentSceneNumber <= 0)
 0321                return false;
 322
 72323            return (scene.sceneData.sceneNumber == currentSceneNumber) || (scene.isPersistent);
 324        }
 325
 326        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) =>
 4327            SetPlayStateDirty();
 328
 329        private void OnSceneNumberChanged(int current, int previous) =>
 20330            isPlayerInScene = IsPlayerInSameSceneAsComponent(current);
 331
 332        public override void AttachTo(ISharedComponent component)
 333        {
 13334            Assert.IsTrue(component != null, "Attachment must not be null!");
 335
 13336            if (attachedMaterials.ContainsKey(component.id))
 0337                return;
 338
 13339            AddReference(component);
 340
 13341            SetPlayStateDirty();
 13342            attachedMaterials.Add(component.id, component);
 343
 13344            component.OnAttach += SetPlayStateDirty;
 13345            component.OnDetach += SetPlayStateDirty;
 13346            DCLVideoTextureUtils.SubscribeToEntityUpdates(component, SetPlayStateDirty);
 13347        }
 348
 349        public override void DetachFrom(ISharedComponent component)
 350        {
 3351            Assert.IsTrue(component != null, "Component must not be null!");
 352
 3353            if (!attachedMaterials.ContainsKey(component.id))
 0354                return;
 355
 3356            if (texturePlayer != null)
 3357                texturePlayer.Pause();
 358
 3359            attachedMaterials.Remove(component.id);
 360
 3361            component.OnAttach -= SetPlayStateDirty;
 3362            component.OnDetach -= SetPlayStateDirty;
 3363            DCLVideoTextureUtils.UnsubscribeToEntityShapeUpdate(component, SetPlayStateDirty);
 364
 3365            SetPlayStateDirty();
 366
 3367            if (RemoveReference(component))
 368            {
 3369                if (attachedEntitiesByComponent.Count == 0)
 3370                    DisposeTexture();
 371            }
 3372        }
 373
 374        private void SetPlayStateDirty(IDCLEntity entity = null) =>
 71375            isPlayStateDirty = true;
 376
 377        private void OnAudioSettingsChanged(AudioSettings settings) =>
 2378            UpdateVolume();
 379
 380        public override void Dispose()
 381        {
 1382            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 1383            base.Dispose();
 1384        }
 385
 386        protected override void DisposeTexture()
 387        {
 4388            isInitialized = false;
 4389            textureDisposed = true;
 390
 4391            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 4392            CommonScriptableObjects.sceneNumber.OnChange -= OnSceneNumberChanged;
 4393            Settings.i.audioSettings.OnChanged -= OnAudioSettingsChanged;
 394
 4395            if (scene != null)
 4396                scene.OnEntityRemoved -= SetPlayStateDirty;
 397
 4398            if (texturePlayerUpdateRoutine != null)
 399            {
 3400                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 3401                texturePlayerUpdateRoutine = null;
 402            }
 403
 4404            if (texturePlayer != null)
 405            {
 3406                texturePlayer.Dispose();
 3407                texturePlayer = null;
 408            }
 409
 4410            Utils.SafeDestroy(texture);
 4411        }
 412    }
 413}