< 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:166
Uncovered lines:17
Coverable lines:183
Total lines:389
Line coverage:90.7% (166 of 183)
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%23.5419076.74%
Initialize(...)0%3.013091.67%
GetVolume()0%2100%
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%
OnSceneIDChanged(...)0%110100%
AttachTo(...)0%22090%
DetachFrom(...)0%3.013091.67%
SetPlayStateDirty(...)0%110100%
OnAudioSettingsChanged(...)0%110100%
Dispose()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;
 12
 13namespace DCL.Components
 14{
 15    public class DCLVideoTexture : DCLTexture
 16    {
 117        public static bool VERBOSE = false;
 118        public static Logger logger = new Logger("DCLVideoTexture") {verboseEnabled = VERBOSE};
 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
 123        public static System.Func<IVideoPluginWrapper> videoPluginWrapperBuilder = () => new VideoPluginWrapper_WebGL();
 24
 25        [System.Serializable]
 26        new public class Model : BaseModel
 27        {
 28            public string videoClipId;
 29            public bool playing = false;
 4530            public float volume = 1f;
 4531            public float playbackRate = 1f;
 32            public bool loop = false;
 4533            public float seek = -1;
 34            public BabylonWrapMode wrap = BabylonWrapMode.CLAMP;
 4535            public FilterMode samplingMode = FilterMode.Bilinear;
 36
 1537            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 38        }
 39
 40        internal WebVideoPlayer texturePlayer;
 41        private Coroutine texturePlayerUpdateRoutine;
 42        private float baseVolume;
 1543        private float distanceVolumeModifier = 1f;
 44        private bool isPlayStateDirty = false;
 45        internal bool isVisible = false;
 46
 47        private bool isInitialized = false;
 1548        private bool isPlayerInScene = true;
 1549        private float currUpdateIntervalTime = OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS;
 50        private float lastVideoProgressReportTime;
 51
 1552        internal Dictionary<string, ISharedComponent> attachedMaterials = new Dictionary<string, ISharedComponent>();
 53        private string lastVideoClipID;
 54        private VideoState previousVideoState;
 55
 1556        public DCLVideoTexture()
 57        {
 1558            model = new Model();
 59
 1560            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange += OnVirtualAudioMixerChangedValue;
 1561        }
 62
 63        public override IEnumerator ApplyChanges(BaseModel newModel)
 64        {
 3065            yield return new WaitUntil(() => CommonScriptableObjects.rendererState.Get());
 66
 67            //If the scene creates and destroy the component before our renderer has been turned on bad things happen!
 68            //TODO: Analyze if we can catch this upstream and stop the IEnumerator
 1569            if (isDisposed)
 70            {
 071                yield break;
 72            }
 73
 1574            var model = (Model) newModel;
 75
 1576            unitySamplingMode = model.samplingMode;
 77
 1578            switch (model.wrap)
 79            {
 80                case BabylonWrapMode.CLAMP:
 1581                    unityWrap = TextureWrapMode.Clamp;
 1582                    break;
 83                case BabylonWrapMode.WRAP:
 084                    unityWrap = TextureWrapMode.Repeat;
 085                    break;
 86                case BabylonWrapMode.MIRROR:
 087                    unityWrap = TextureWrapMode.Mirror;
 88                    break;
 89            }
 90
 1591            lastVideoClipID = model.videoClipId;
 92
 1593            if (texturePlayer == null)
 94            {
 1595                DCLVideoClip dclVideoClip = scene.componentsManagerLegacy.GetSceneSharedComponent(lastVideoClipID) as DC
 96
 1597                if (dclVideoClip == null)
 98                {
 099                    logger.Error("Wrong video clip type when playing VideoTexture!!");
 0100                    yield break;
 101                }
 102
 15103                Initialize(dclVideoClip);
 104            }
 105
 15106            if (texture == null)
 107            {
 434108                yield return new WaitUntil(() => texturePlayer == null || ((texturePlayer.texture != null && texturePlay
 109
 15110                if (texturePlayer.isError)
 111                {
 0112                    if (texturePlayerUpdateRoutine != null)
 113                    {
 0114                        CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 0115                        texturePlayerUpdateRoutine = null;
 116                    }
 117
 0118                    yield break;
 119                }
 120
 15121                texture = texturePlayer.texture;
 15122                SetPlayStateDirty();
 123            }
 124
 15125            if (texturePlayer != null)
 126            {
 15127                if (model.seek >= 0)
 128                {
 1129                    texturePlayer.SetTime(model.seek);
 1130                    model.seek = -1;
 131
 132                    // Applying seek is not immediate
 1133                    yield return null;
 134                }
 135
 15136                if (model.playing)
 137                {
 3138                    texturePlayer.Play();
 3139                }
 140                else
 141                {
 12142                    texturePlayer.Pause();
 143                }
 144
 15145                ReportVideoProgress();
 146
 15147                if (baseVolume != model.volume)
 148                {
 15149                    baseVolume = model.volume;
 15150                    UpdateVolume();
 151                }
 152
 15153                texturePlayer.SetPlaybackRate(model.playbackRate);
 15154                texturePlayer.SetLoop(model.loop);
 155            }
 15156        }
 157
 158        private void Initialize(DCLVideoClip dclVideoClip)
 159        {
 15160            if (isInitialized) return;
 15161            isInitialized = true;
 162
 15163            string videoId = (!string.IsNullOrEmpty(scene.sceneData.id)) ? scene.sceneData.id + id : scene.GetHashCode()
 15164            texturePlayer = new WebVideoPlayer(videoId, dclVideoClip.GetUrl(), dclVideoClip.isStream, videoPluginWrapper
 15165            texturePlayerUpdateRoutine = CoroutineStarter.Start(OnUpdate());
 166
 15167            CommonScriptableObjects.playerCoords.OnChange += OnPlayerCoordsChanged;
 15168            CommonScriptableObjects.sceneID.OnChange += OnSceneIDChanged;
 15169            scene.OnEntityRemoved += SetPlayStateDirty;
 15170            Settings.i.audioSettings.OnChanged += OnAudioSettingsChanged;
 171
 15172            OnSceneIDChanged(CommonScriptableObjects.sceneID.Get(), null);
 15173        }
 174
 0175        public float GetVolume() { return ((Model) model).volume; }
 176
 177        private IEnumerator OnUpdate()
 178        {
 499179            while (true)
 180            {
 514181                UpdateDirtyState();
 514182                UpdateVideoTexture();
 514183                UpdateProgressReport();
 514184                yield return null;
 185            }
 186        }
 187
 188        private void UpdateDirtyState()
 189        {
 514190            if (!isPlayStateDirty)
 472191                return;
 192
 42193            CalculateVideoVolumeAndPlayStatus();
 42194            isPlayStateDirty = false;
 42195        }
 196
 197        private void UpdateVideoTexture()
 198        {
 514199            if (!isPlayerInScene && currUpdateIntervalTime < OUTOFSCENE_TEX_UPDATE_INTERVAL_IN_SECONDS)
 200            {
 359201                currUpdateIntervalTime += Time.unscaledDeltaTime;
 359202            }
 155203            else if (texturePlayer != null)
 204            {
 155205                currUpdateIntervalTime = 0;
 155206                texturePlayer.Update();
 155207                texture = texturePlayer.texture;
 208            }
 155209        }
 210
 211        private void UpdateProgressReport()
 212        {
 514213            var currentState = texturePlayer.GetState();
 214
 514215            if ( currentState == VideoState.PLAYING
 216                 && IsTimeToReportVideoProgress()
 217                 || previousVideoState != currentState)
 218            {
 30219                ReportVideoProgress();
 220            }
 514221        }
 222
 223        private void ReportVideoProgress()
 224        {
 45225            lastVideoProgressReportTime = Time.unscaledTime;
 45226            VideoState videoState = texturePlayer.GetState();
 45227            previousVideoState = videoState;
 45228            var videoStatus = (int)videoState;
 45229            var currentOffset = texturePlayer.GetTime();
 45230            var length = texturePlayer.GetDuration();
 45231            WebInterface.ReportVideoProgressEvent(id, scene.sceneData.id, lastVideoClipID, videoStatus, currentOffset, l
 45232        }
 233
 234        private bool IsTimeToReportVideoProgress()
 235        {
 14236            return Time.unscaledTime - lastVideoProgressReportTime > VIDEO_PROGRESS_UPDATE_INTERVAL_IN_SECONDS;
 237        }
 238
 239        private void CalculateVideoVolumeAndPlayStatus()
 240        {
 42241            isVisible = false;
 42242            float minDistance = float.MaxValue;
 42243            distanceVolumeModifier = 0;
 244
 42245            if (attachedMaterials.Count > 0)
 246            {
 27247                using (var iterator = attachedMaterials.GetEnumerator())
 248                {
 45249                    while (iterator.MoveNext())
 250                    {
 27251                        var materialInfo = iterator.Current;
 27252                        bool isComponentVisible = DCLVideoTextureUtils.IsComponentVisible(materialInfo.Value);
 253
 27254                        if (isComponentVisible)
 255                        {
 9256                            isVisible = true;
 257
 9258                            var entityDist = DCLVideoTextureUtils.GetClosestDistanceSqr(materialInfo.Value,
 259                                CommonScriptableObjects.playerUnityPosition);
 260
 9261                            if (entityDist < minDistance)
 9262                                minDistance = entityDist;
 263
 264                            // NOTE: if current minDistance is enough for full volume then there is no need to keep iter
 9265                            if (minDistance <= DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSe
 9266                                break;
 267                        }
 268                    }
 18269                }
 270            }
 271
 42272            if (isVisible)
 273            {
 274                const float maxDistanceBlockForSound = 12;
 9275                float sqrParcelDistance = DCL.Configuration.ParcelSettings.PARCEL_SIZE * DCL.Configuration.ParcelSetting
 9276                distanceVolumeModifier = 1 - Mathf.Clamp01(Mathf.FloorToInt(minDistance / sqrParcelDistance) / maxDistan
 277            }
 278
 42279            if (texturePlayer != null)
 280            {
 42281                texturePlayer.visible = isVisible;
 282            }
 283
 42284            UpdateVolume();
 42285        }
 286
 0287        private void OnVirtualAudioMixerChangedValue(float currentValue, float previousValue) { UpdateVolume(); }
 288
 289        private void UpdateVolume()
 290        {
 59291            if (texturePlayer == null)
 0292                return;
 293
 59294            float targetVolume = 0f;
 295
 59296            if (CommonScriptableObjects.rendererState.Get() && IsPlayerInSameSceneAsComponent(CommonScriptableObjects.sc
 297            {
 47298                targetVolume = baseVolume * distanceVolumeModifier;
 47299                float virtualMixerVolume = DataStore.i.virtualAudioMixer.sceneSFXVolume.Get();
 47300                float sceneSFXSetting = Settings.i.audioSettings.Data.sceneSFXVolume;
 47301                float masterSetting = Settings.i.audioSettings.Data.masterVolume;
 47302                targetVolume *= Utils.ToVolumeCurve(virtualMixerVolume * sceneSFXSetting * masterSetting);
 303            }
 304
 59305            texturePlayer.SetVolume(targetVolume);
 59306        }
 307
 308        private bool IsPlayerInSameSceneAsComponent(string currentSceneId)
 309        {
 69310            if (scene == null)
 0311                return false;
 69312            if (string.IsNullOrEmpty(currentSceneId))
 0313                return false;
 314
 69315            return (scene.sceneData.id == currentSceneId) || (scene.isPersistent);
 316        }
 317
 4318        private void OnPlayerCoordsChanged(Vector2Int coords, Vector2Int prevCoords) => SetPlayStateDirty();
 319
 17320        private void OnSceneIDChanged(string current, string previous) => isPlayerInScene = IsPlayerInSameSceneAsCompone
 321
 322        public override void AttachTo(ISharedComponent component)
 323        {
 10324            Assert.IsTrue(component != null, "Attachment must not be null!");
 325
 10326            if (attachedMaterials.ContainsKey(component.id))
 0327                return;
 328
 10329            AddReference(component);
 330
 10331            SetPlayStateDirty();
 10332            attachedMaterials.Add(component.id, component);
 333
 10334            component.OnAttach += SetPlayStateDirty;
 10335            component.OnDetach += SetPlayStateDirty;
 10336            DCLVideoTextureUtils.SubscribeToEntityUpdates(component, SetPlayStateDirty);
 10337        }
 338
 339        public override void DetachFrom(ISharedComponent component)
 340        {
 1341            Assert.IsTrue(component != null, "Component must not be null!");
 342
 1343            if (!attachedMaterials.ContainsKey(component.id))
 0344                return;
 345
 1346            if (texturePlayer != null)
 1347                texturePlayer.Pause();
 348
 1349            attachedMaterials.Remove(component.id);
 350
 1351            component.OnAttach -= SetPlayStateDirty;
 1352            component.OnDetach -= SetPlayStateDirty;
 1353            DCLVideoTextureUtils.UnsubscribeToEntityShapeUpdate(component, SetPlayStateDirty);
 354
 1355            RemoveReference(component);
 1356            SetPlayStateDirty();
 1357        }
 358
 31359        private void SetPlayStateDirty(IDCLEntity entity = null) => isPlayStateDirty = true;
 360
 2361        private void OnAudioSettingsChanged(AudioSettings settings) => UpdateVolume();
 362
 363        public override void Dispose()
 364        {
 1365            DataStore.i.virtualAudioMixer.sceneSFXVolume.OnChange -= OnVirtualAudioMixerChangedValue;
 1366            Settings.i.audioSettings.OnChanged -= OnAudioSettingsChanged;
 1367            CommonScriptableObjects.playerCoords.OnChange -= OnPlayerCoordsChanged;
 1368            CommonScriptableObjects.sceneID.OnChange -= OnSceneIDChanged;
 369
 1370            if (scene != null)
 1371                scene.OnEntityRemoved -= SetPlayStateDirty;
 372
 1373            if (texturePlayerUpdateRoutine != null)
 374            {
 1375                CoroutineStarter.Stop(texturePlayerUpdateRoutine);
 1376                texturePlayerUpdateRoutine = null;
 377            }
 378
 1379            if (texturePlayer != null)
 380            {
 1381                texturePlayer.Dispose();
 1382                texturePlayer = null;
 383            }
 384
 1385            Utils.SafeDestroy(texture);
 1386            base.Dispose();
 1387        }
 388    }
 389}