From 2c51e791ad894ca9c0e3741e527bb634594e84a0 Mon Sep 17 00:00:00 2001 From: penev92 Date: Thu, 2 Feb 2023 22:54:20 +0200 Subject: [PATCH] Added VideoPlayerWidget.LoadAndPlayAsync method Previous commits removed the async loading of videos, which can be a problem for videos played in the radar widget mid-game because it can cause a lag spike. This loads the video on a separate thread and runs it on the main thread whenever it is loaded, not blocking the main thread meanwhile and allowing the game to continue while the video loads. Also add back cancelling of already playing video and add a check to not try to run onComplete if it is null. --- OpenRA.Mods.Common/Scripting/Media.cs | 17 +----- .../Widgets/VideoPlayerWidget.cs | 56 ++++++++++++++++++- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/OpenRA.Mods.Common/Scripting/Media.cs b/OpenRA.Mods.Common/Scripting/Media.cs index 641b02e5e0..dae7e5fd35 100644 --- a/OpenRA.Mods.Common/Scripting/Media.cs +++ b/OpenRA.Mods.Common/Scripting/Media.cs @@ -62,22 +62,7 @@ namespace OpenRA.Mods.Common.Scripting public static void PlayFMVInRadar(string videoFileName, Action onComplete) { var player = Ui.Root.Get("PLAYER"); - - try - { - player.LoadAndPlay(videoFileName); - } - catch (FileNotFoundException) - { - onComplete(); - return; - } - - player.PlayThen(() => - { - onComplete(); - player.CloseVideo(); - }); + player.LoadAndPlayAsync(videoFileName, onComplete); } } } diff --git a/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs b/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs index 507caef7c2..f7d2d2ec8c 100644 --- a/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/VideoPlayerWidget.cs @@ -10,6 +10,8 @@ #endregion using System; +using System.IO; +using System.Threading.Tasks; using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Video; @@ -50,11 +52,56 @@ namespace OpenRA.Mods.Common.Widgets if (filename == cachedVideoFileName) return; + cachedVideoFileName = filename; var stream = Game.ModData.DefaultFileSystem.Open(filename); var video = VideoLoader.GetVideo(stream, true, Game.ModData.VideoLoaders); Play(video); + } + + /// + /// Tries to load a video from the specified file and play it. Does nothing if the file name matches the already loaded video. + /// + /// Name of the file, including the extension. + /// Action to perform after the video ends. + public void LoadAndPlayAsync(string filename, Action after) + { + if (filename == cachedVideoFileName) + return; cachedVideoFileName = filename; + + if (!stopped) + CloseVideo(); + + Task.Run(() => + { + try + { + var stream = Game.ModData.DefaultFileSystem.Open(filename); + var video = VideoLoader.GetVideo(stream, true, Game.ModData.VideoLoaders); + + // Safeguard against race conditions with two videos being loaded at the same time - prefer to play only the last one. + if (filename != cachedVideoFileName) + { + after(); + return; + } + + Game.RunAfterTick(() => + { + Play(video); + PlayThen(() => + { + after(); + CloseVideo(); + }); + }); + } + catch (FileNotFoundException) + { + after(); + } + }); } /// @@ -239,7 +286,14 @@ namespace OpenRA.Mods.Common.Widgets Game.Sound.StopVideo(); video.Reset(); videoSprite.Sheet.GetTexture().SetData(video.CurrentFrameData, textureSize, textureSize); - Game.RunAfterTick(onComplete); + Game.RunAfterTick(() => + { + if (onComplete != null) + { + onComplete(); + onComplete = null; + } + }); } public void CloseVideo()