From 2a3dca446771db5688eac311b1f178817f159471 Mon Sep 17 00:00:00 2001 From: chylex <contact@chylex.com> Date: Sun, 13 Aug 2017 17:52:46 +0200 Subject: [PATCH] Rewrite video player to use duplex pipe for process communication --- Core/FormBrowser.cs | 10 ---- Core/Other/Management/VideoPlayer.cs | 77 ++++++++++++++++++++++++---- Program.cs | 2 - Resources/Scripts/code.js | 1 - video/TweetDuck.Video/FormPlayer.cs | 20 +++++++- video/TweetDuck.Video/Program.cs | 7 ++- 6 files changed, 88 insertions(+), 29 deletions(-) diff --git a/Core/FormBrowser.cs b/Core/FormBrowser.cs index 00c930ab..8846fd1f 100644 --- a/Core/FormBrowser.cs +++ b/Core/FormBrowser.cs @@ -389,16 +389,6 @@ protected override void WndProc(ref Message m){ BrowserProcesses.Link(m.LParam.ToInt32(), processId); } - return; - } - else if (m.Msg == Program.VideoPlayerMessage){ - int volume = m.WParam.ToInt32(); - - if (Handle == m.LParam && volume != Config.VideoPlayerVolume){ - Config.VideoPlayerVolume = volume; - Config.Save(); - } - return; } } diff --git a/Core/Other/Management/VideoPlayer.cs b/Core/Other/Management/VideoPlayer.cs index 616a77ae..6dfb0239 100644 --- a/Core/Other/Management/VideoPlayer.cs +++ b/Core/Other/Management/VideoPlayer.cs @@ -4,6 +4,7 @@ using System.Windows.Forms; using TweetDuck.Core.Controls; using TweetDuck.Core.Utils; +using TweetLib.Communication; namespace TweetDuck.Core.Other.Management{ sealed class VideoPlayer : IDisposable{ @@ -23,23 +24,32 @@ public bool Running{ public event EventHandler ProcessExited; private readonly Form owner; - private Process currentProcess; private string lastUrl; + private Process currentProcess; + private DuplexPipe.Server currentPipe; + private bool isClosing; + public VideoPlayer(Form owner){ this.owner = owner; this.owner.FormClosing += owner_FormClosing; } public void Launch(string url){ - Close(); + if (Running){ + Destroy(); + isClosing = false; + } lastUrl = url; try{ + currentPipe = DuplexPipe.CreateServer(); + currentPipe.DataIn += currentPipe_DataIn; + if ((currentProcess = Process.Start(new ProcessStartInfo{ FileName = PlayerExe, - Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\"", + Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"", UseShellExecute = false, RedirectStandardOutput = true })) != null){ @@ -51,15 +61,61 @@ public void Launch(string url){ currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data); #endif } + + currentPipe.DisposeToken(); }catch(Exception e){ Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, e); } } + private void currentPipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ + owner.InvokeSafe(() => { + switch(e.Key){ + case "vol": + if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){ + Program.UserConfig.VideoPlayerVolume = volume; + Program.UserConfig.Save(); + } + + break; + + case "rip": + currentPipe.Dispose(); + currentPipe = null; + + currentProcess.Dispose(); + currentProcess = null; + + isClosing = false; + TriggerProcessExitEventUnsafe(); + break; + } + }); + } + public void Close(){ if (currentProcess != null){ - currentProcess.Exited -= process_Exited; + if (isClosing){ + Destroy(); + isClosing = false; + } + else{ + isClosing = true; + currentProcess.Exited -= process_Exited; + currentPipe.Write("die"); + } + } + } + public void Dispose(){ + ProcessExited = null; + + isClosing = true; + Destroy(); + } + + private void Destroy(){ + if (currentProcess != null){ try{ currentProcess.Kill(); }catch{ @@ -68,16 +124,14 @@ public void Close(){ currentProcess.Dispose(); currentProcess = null; + + currentPipe.Dispose(); + currentPipe = null; - owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); + TriggerProcessExitEventUnsafe(); } } - public void Dispose(){ - ProcessExited = null; - Close(); - } - private void owner_FormClosing(object sender, FormClosingEventArgs e){ if (currentProcess != null){ currentProcess.Exited -= process_Exited; @@ -103,6 +157,9 @@ private void process_Exited(object sender, EventArgs e){ currentProcess.Dispose(); currentProcess = null; + + currentPipe.Dispose(); + currentPipe = null; owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); } diff --git a/Program.cs b/Program.cs index 7fb24971..3775e772 100644 --- a/Program.cs +++ b/Program.cs @@ -45,7 +45,6 @@ static class Program{ public static uint WindowRestoreMessage; public static uint SubProcessMessage; - public static uint VideoPlayerMessage; private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static bool HasCleanedUp; @@ -77,7 +76,6 @@ private static void Main(){ WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore"); SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess"); - VideoPlayerMessage = Comms.RegisterMessage("TweetDuckVideoPlayer"); if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){ FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK); diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js index 328aa9c5..2a5ce9ff 100644 --- a/Resources/Scripts/code.js +++ b/Resources/Scripts/code.js @@ -797,7 +797,6 @@ var playVideo = function(url){ $('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){ $TD.playVideo(null); - $(this).remove(); }).appendTo(app); $TD.playVideo(url); diff --git a/video/TweetDuck.Video/FormPlayer.cs b/video/TweetDuck.Video/FormPlayer.cs index da7103a3..168343fa 100644 --- a/video/TweetDuck.Video/FormPlayer.cs +++ b/video/TweetDuck.Video/FormPlayer.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Globalization; using System.Runtime.InteropServices; using System.Windows.Forms; using TweetDuck.Video.Controls; @@ -12,6 +13,7 @@ partial class FormPlayer : Form{ private readonly IntPtr ownerHandle; private readonly string videoUrl; + private readonly DuplexPipe pipe; private readonly ControlWMP player; private bool wasCursorInside; @@ -20,11 +22,13 @@ partial class FormPlayer : Form{ private WindowsMediaPlayer Player => player.Ocx; - public FormPlayer(IntPtr handle, int volume, string url){ + public FormPlayer(IntPtr handle, int volume, string url, string token){ InitializeComponent(); this.ownerHandle = handle; this.videoUrl = url; + this.pipe = DuplexPipe.CreateClient(token); + this.pipe.DataIn += pipe_DataIn; player = new ControlWMP{ Dock = DockStyle.Fill @@ -54,6 +58,18 @@ private void FormPlayer_Load(object sender, EventArgs e){ Player.URL = videoUrl; } + private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ + switch(e.Key){ + case "die": + timerSync.Stop(); + Visible = false; + pipe.Write("rip"); + + Close(); + break; + } + } + private void player_PlayStateChange(int newState){ WMPPlayState state = (WMPPlayState)newState; @@ -134,7 +150,7 @@ private void timerSync_Tick(object sender, EventArgs e){ private void timerData_Tick(object sender, EventArgs e){ timerData.Stop(); - Comms.BroadcastMessage(Program.VideoPlayerMessage, new UIntPtr((uint)trackBarVolume.Value), ownerHandle); + pipe.Write("vol", trackBarVolume.Value.ToString(CultureInfo.InvariantCulture)); } private void progressSeek_MouseDown(object sender, MouseEventArgs e){ diff --git a/video/TweetDuck.Video/Program.cs b/video/TweetDuck.Video/Program.cs index 4e1de6f7..8f514203 100644 --- a/video/TweetDuck.Video/Program.cs +++ b/video/TweetDuck.Video/Program.cs @@ -13,9 +13,6 @@ static class Program{ public const int CODE_OWNER_GONE = 5; public const int CODE_USER_REQUESTED = 6; - private static uint? message; - public static uint VideoPlayerMessage => message ?? (message = Comms.RegisterMessage("TweetDuckVideoPlayer")).Value; - [STAThread] private static int Main(string[] args){ Application.EnableVisualStyles(); @@ -24,17 +21,19 @@ private static int Main(string[] args){ IntPtr ownerHandle; int defaultVolume; string videoUrl; + string pipeToken; try{ ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture)); defaultVolume = int.Parse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture); videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri; + pipeToken = args[3]; }catch{ return CODE_INVALID_ARGS; } try{ - Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl)); + Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken)); }catch(Exception e){ Console.Out.WriteLine(e.Message); return CODE_LAUNCH_FAIL;