< Summary

Class:DCL.ECSComponents.VideoPlayerHandler
Assembly:DCL.ECSComponents.VideoPlayer
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/ECSComponents/VideoPlayer/VideoPlayerHandler.cs
Covered lines:78
Uncovered lines:6
Coverable lines:84
Total lines:198
Line coverage:92.8% (78 of 84)
Covered branches:0
Total branches:0
Covered methods:13
Total methods:14
Method coverage:92.8% (13 of 14)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
VideoPlayerHandler()0%110100%
VideoPlayerHandler(...)0%110100%
OnComponentCreated(...)0%220100%
OnComponentRemoved(...)0%220100%
OnComponentModelUpdated(...)0%12.6712083.33%
ConditionsToPlayVideoChanged()0%5.055087.5%
OnCursorLockChanged(...)0%2.022083.33%
OnLoadingScreenStateChanged(...)0%110100%
OnSceneSfxVolumeChanged(...)0%110100%
OnAudioSettingsChanged(...)0%110100%
OnPlayerSceneChanged(...)0%2100%
UpdateVolume(...)0%4.14081.82%
IsPlayerInSameSceneAsComponent(...)0%6.296080%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/ECS7/ECSComponents/VideoPlayer/VideoPlayerHandler.cs

#LineLine coverage
 1using DCL.Components;
 2using DCL.Components.Video.Plugin;
 3using DCL.Controllers;
 4using DCL.ECS7.InternalComponents;
 5using DCL.ECSRuntime;
 6using DCL.Models;
 7using DCL.SettingsCommon;
 8using System;
 9using System.Collections.Generic;
 10using System.Linq;
 11using AudioSettings = DCL.SettingsCommon.AudioSettings;
 12
 13namespace DCL.ECSComponents
 14{
 15    public class VideoPlayerHandler : IECSComponentHandler<PBVideoPlayer>
 16    {
 117        private static readonly string[] NO_STREAM_EXTENSIONS = new[] { ".mp4", ".ogg", ".mov", ".webm" };
 18
 19        internal DataStore_LoadingScreen.DecoupledLoadingScreen loadingScreen;
 20        private readonly ISettingsRepository<AudioSettings> audioSettings;
 21        private readonly DataStore_VirtualAudioMixer audioMixerDataStore;
 22        private readonly IntVariable currentPlayerSceneNumber;
 23
 24        internal PBVideoPlayer lastModel = null;
 25        internal WebVideoPlayer videoPlayer;
 26
 27        // Flags to check if we can activate the video
 28        internal bool isRendererActive = false;
 29        internal bool hadUserInteraction = false;
 30        internal bool isValidUrl = false;
 31
 32        private readonly IInternalECSComponent<InternalVideoPlayer> videoPlayerInternalComponent;
 33        private IParcelScene currentScene;
 734        private bool canVideoBePlayed => isRendererActive && hadUserInteraction && isValidUrl;
 35
 736        public VideoPlayerHandler(
 37            IInternalECSComponent<InternalVideoPlayer> videoPlayerInternalComponent,
 38            DataStore_LoadingScreen.DecoupledLoadingScreen loadingScreen,
 39            ISettingsRepository<AudioSettings> audioSettings,
 40            DataStore_VirtualAudioMixer audioMixerDataStore,
 41            IntVariable currentPlayerSceneNumber)
 42        {
 743            this.videoPlayerInternalComponent = videoPlayerInternalComponent;
 744            this.loadingScreen = loadingScreen;
 745            this.audioSettings = audioSettings;
 746            this.audioMixerDataStore = audioMixerDataStore;
 747            this.currentPlayerSceneNumber = currentPlayerSceneNumber;
 748        }
 49
 50        public void OnComponentCreated(IParcelScene scene, IDCLEntity entity)
 51        {
 652            isRendererActive = !loadingScreen.visible.Get();
 53
 54            // We need to check if the user interacted with the application before playing the video,
 55            // otherwise browsers won't play the video, ending up in a fake 'playing' state.
 656            hadUserInteraction = Helpers.Utils.IsCursorLocked;
 57
 658            if (!hadUserInteraction)
 259                Helpers.Utils.OnCursorLockChanged += OnCursorLockChanged;
 660            loadingScreen.visible.OnChange += OnLoadingScreenStateChanged;
 661            audioSettings.OnChanged += OnAudioSettingsChanged;
 662            audioMixerDataStore.sceneSFXVolume.OnChange += OnSceneSfxVolumeChanged;
 663            currentPlayerSceneNumber.OnChange += OnPlayerSceneChanged;
 664            currentScene = scene;
 665        }
 66
 67        public void OnComponentRemoved(IParcelScene scene, IDCLEntity entity)
 68        {
 369            Helpers.Utils.OnCursorLockChanged -= OnCursorLockChanged;
 370            loadingScreen.visible.OnChange -= OnLoadingScreenStateChanged;
 371            audioSettings.OnChanged -= OnAudioSettingsChanged;
 372            audioMixerDataStore.sceneSFXVolume.OnChange -= OnSceneSfxVolumeChanged;
 373            currentPlayerSceneNumber.OnChange -= OnPlayerSceneChanged;
 74
 75            // ECSVideoPlayerSystem.Update() will run a video events check before the component is removed
 376            videoPlayerInternalComponent.RemoveFor(scene, entity, new InternalVideoPlayer()
 77            {
 78                removed = true
 79            });
 80
 381            videoPlayer?.Dispose();
 382            currentScene = null;
 383        }
 84
 85        public void OnComponentModelUpdated(IParcelScene scene, IDCLEntity entity, PBVideoPlayer model)
 86        {
 87            // Setup video player
 888            if (lastModel == null || lastModel.Src != model.Src)
 89            {
 890                videoPlayer?.Dispose();
 91
 892                var entityId = entity.entityId.ToString();
 893                string id = scene.sceneData.sceneNumber > 0 ? scene.sceneData.sceneNumber + entityId : scene.GetHashCode
 94
 895                VideoType videoType = VideoType.Common;
 896                if (model.Src.StartsWith("livekit-video://"))
 097                    videoType = VideoType.LiveKit;
 1698                else if (!NO_STREAM_EXTENSIONS.Any(x => model.Src.EndsWith(x)))
 099                    videoType = VideoType.Hls;
 100
 8101                string videoUrl = videoType != VideoType.LiveKit
 102                    ? model.GetVideoUrl(scene.contentProvider, scene.sceneData.requiredPermissions, scene.sceneData.allo
 103                    : model.Src;
 104
 8105                isValidUrl = !string.IsNullOrEmpty(videoUrl);
 8106                if (!isValidUrl)
 0107                    return;
 108
 8109                videoPlayer = new WebVideoPlayer(id, videoUrl, videoType, DCLVideoTexture.videoPluginWrapperBuilder.Invo
 8110                videoPlayerInternalComponent.PutFor(scene, entity, new InternalVideoPlayer()
 111                {
 112                    videoPlayer = videoPlayer,
 113                    assignedMaterials = new List<InternalVideoPlayer.MaterialAssigned>(),
 114                });
 115            }
 116
 117            // Apply model values except 'Playing'
 8118            float lastPosition = lastModel?.GetPosition() ?? 0.0f;
 8119            if (Math.Abs(lastPosition - model.GetPosition()) > 0.01f) // 0.01s of tolerance
 0120                videoPlayer.SetTime(model.GetPosition());
 121
 8122            UpdateVolume(model, audioSettings.Data, audioMixerDataStore.sceneSFXVolume.Get(), currentPlayerSceneNumber);
 123
 8124            videoPlayer.SetPlaybackRate(model.GetPlaybackRate());
 8125            videoPlayer.SetLoop(model.GetLoop());
 126
 8127            lastModel = model;
 128
 8129            ConditionsToPlayVideoChanged();
 8130        }
 131
 132        private void ConditionsToPlayVideoChanged()
 133        {
 10134            if (lastModel == null) return;
 135
 10136            bool shouldBePlaying = lastModel.IsPlaying() && canVideoBePlayed;
 10137            if (shouldBePlaying != videoPlayer.playing)
 138            {
 6139                if (shouldBePlaying)
 5140                    videoPlayer.Play();
 141                else
 1142                    videoPlayer.Pause();
 143            }
 5144        }
 145
 146        private void OnCursorLockChanged(bool isLocked)
 147        {
 1148            if (!isLocked) return;
 149
 1150            hadUserInteraction = true;
 1151            Helpers.Utils.OnCursorLockChanged -= OnCursorLockChanged;
 1152            ConditionsToPlayVideoChanged();
 1153        }
 154
 155        private void OnLoadingScreenStateChanged(bool isScreenEnabled, bool prevState)
 156        {
 1157            isRendererActive = !isScreenEnabled;
 1158            ConditionsToPlayVideoChanged();
 1159        }
 160
 161        private void OnSceneSfxVolumeChanged(float current, float previous) =>
 1162            UpdateVolume(lastModel, audioSettings.Data, current, currentPlayerSceneNumber);
 163
 164        private void OnAudioSettingsChanged(AudioSettings settings) =>
 3165            UpdateVolume(lastModel, settings, audioMixerDataStore.sceneSFXVolume.Get(), currentPlayerSceneNumber);
 166
 167        private void OnPlayerSceneChanged(int current, int previous) =>
 0168            UpdateVolume(lastModel, audioSettings.Data, audioMixerDataStore.sceneSFXVolume.Get(), current);
 169
 170        private void UpdateVolume(PBVideoPlayer model, AudioSettings settings, float sceneMixerSfxVolume, int currentSce
 171        {
 12172            if (model == null) return;
 12173            if (videoPlayer == null) return;
 174
 12175            float volume = 0;
 176
 12177            if (IsPlayerInSameSceneAsComponent(currentSceneNumber))
 178            {
 11179                float sceneSFXSetting = settings.sceneSFXVolume;
 11180                float masterSetting = settings.masterVolume;
 11181                volume = model.GetVolume() * sceneMixerSfxVolume * sceneSFXSetting * masterSetting;
 182            }
 183
 12184            videoPlayer.SetVolume(volume);
 12185        }
 186
 187        private bool IsPlayerInSameSceneAsComponent(int currentSceneNumber)
 188        {
 12189            if (currentScene == null)
 1190                return false;
 191
 11192            if (currentSceneNumber <= 0)
 0193                return false;
 194
 11195            return currentScene.sceneData?.sceneNumber == currentSceneNumber || currentScene.isPersistent;
 196        }
 197    }
 198}