1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-20 03:15:47 +02:00

Rewrite video player to use duplex pipe for process communication

This commit is contained in:
chylex 2017-08-13 17:52:46 +02:00
parent d4ecfcceec
commit 2a3dca4467
6 changed files with 88 additions and 29 deletions
Core
Program.cs
Resources/Scripts
video/TweetDuck.Video

View File

@ -389,16 +389,6 @@ protected override void WndProc(ref Message m){
BrowserProcesses.Link(m.LParam.ToInt32(), processId); 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; return;
} }
} }

View File

@ -4,6 +4,7 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Communication;
namespace TweetDuck.Core.Other.Management{ namespace TweetDuck.Core.Other.Management{
sealed class VideoPlayer : IDisposable{ sealed class VideoPlayer : IDisposable{
@ -23,23 +24,32 @@ public bool Running{
public event EventHandler ProcessExited; public event EventHandler ProcessExited;
private readonly Form owner; private readonly Form owner;
private Process currentProcess;
private string lastUrl; private string lastUrl;
private Process currentProcess;
private DuplexPipe.Server currentPipe;
private bool isClosing;
public VideoPlayer(Form owner){ public VideoPlayer(Form owner){
this.owner = owner; this.owner = owner;
this.owner.FormClosing += owner_FormClosing; this.owner.FormClosing += owner_FormClosing;
} }
public void Launch(string url){ public void Launch(string url){
Close(); if (Running){
Destroy();
isClosing = false;
}
lastUrl = url; lastUrl = url;
try{ try{
currentPipe = DuplexPipe.CreateServer();
currentPipe.DataIn += currentPipe_DataIn;
if ((currentProcess = Process.Start(new ProcessStartInfo{ if ((currentProcess = Process.Start(new ProcessStartInfo{
FileName = PlayerExe, FileName = PlayerExe,
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\"", Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true RedirectStandardOutput = true
})) != null){ })) != null){
@ -51,15 +61,61 @@ public void Launch(string url){
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data); currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data);
#endif #endif
} }
currentPipe.DisposeToken();
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Video Playback Error", "Error launching video player.", true, 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(){ public void Close(){
if (currentProcess != null){ 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{ try{
currentProcess.Kill(); currentProcess.Kill();
}catch{ }catch{
@ -68,16 +124,14 @@ public void Close(){
currentProcess.Dispose(); currentProcess.Dispose();
currentProcess = null; currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); TriggerProcessExitEventUnsafe();
} }
} }
public void Dispose(){
ProcessExited = null;
Close();
}
private void owner_FormClosing(object sender, FormClosingEventArgs e){ private void owner_FormClosing(object sender, FormClosingEventArgs e){
if (currentProcess != null){ if (currentProcess != null){
currentProcess.Exited -= process_Exited; currentProcess.Exited -= process_Exited;
@ -103,6 +157,9 @@ private void process_Exited(object sender, EventArgs e){
currentProcess.Dispose(); currentProcess.Dispose();
currentProcess = null; currentProcess = null;
currentPipe.Dispose();
currentPipe = null;
owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe); owner.InvokeAsyncSafe(TriggerProcessExitEventUnsafe);
} }

View File

@ -45,7 +45,6 @@ static class Program{
public static uint WindowRestoreMessage; public static uint WindowRestoreMessage;
public static uint SubProcessMessage; public static uint SubProcessMessage;
public static uint VideoPlayerMessage;
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
private static bool HasCleanedUp; private static bool HasCleanedUp;
@ -77,7 +76,6 @@ private static void Main(){
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore"); WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore");
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess"); SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
VideoPlayerMessage = Comms.RegisterMessage("TweetDuckVideoPlayer");
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){ if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK); FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);

View File

@ -797,7 +797,6 @@
var playVideo = function(url){ var playVideo = function(url){
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){ $('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
$TD.playVideo(null); $TD.playVideo(null);
$(this).remove();
}).appendTo(app); }).appendTo(app);
$TD.playVideo(url); $TD.playVideo(url);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Globalization;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Video.Controls; using TweetDuck.Video.Controls;
@ -12,6 +13,7 @@ partial class FormPlayer : Form{
private readonly IntPtr ownerHandle; private readonly IntPtr ownerHandle;
private readonly string videoUrl; private readonly string videoUrl;
private readonly DuplexPipe pipe;
private readonly ControlWMP player; private readonly ControlWMP player;
private bool wasCursorInside; private bool wasCursorInside;
@ -20,11 +22,13 @@ partial class FormPlayer : Form{
private WindowsMediaPlayer Player => player.Ocx; private WindowsMediaPlayer Player => player.Ocx;
public FormPlayer(IntPtr handle, int volume, string url){ public FormPlayer(IntPtr handle, int volume, string url, string token){
InitializeComponent(); InitializeComponent();
this.ownerHandle = handle; this.ownerHandle = handle;
this.videoUrl = url; this.videoUrl = url;
this.pipe = DuplexPipe.CreateClient(token);
this.pipe.DataIn += pipe_DataIn;
player = new ControlWMP{ player = new ControlWMP{
Dock = DockStyle.Fill Dock = DockStyle.Fill
@ -54,6 +58,18 @@ private void FormPlayer_Load(object sender, EventArgs e){
Player.URL = videoUrl; 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){ private void player_PlayStateChange(int newState){
WMPPlayState state = (WMPPlayState)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){ private void timerData_Tick(object sender, EventArgs e){
timerData.Stop(); 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){ private void progressSeek_MouseDown(object sender, MouseEventArgs e){

View File

@ -13,9 +13,6 @@ static class Program{
public const int CODE_OWNER_GONE = 5; public const int CODE_OWNER_GONE = 5;
public const int CODE_USER_REQUESTED = 6; public const int CODE_USER_REQUESTED = 6;
private static uint? message;
public static uint VideoPlayerMessage => message ?? (message = Comms.RegisterMessage("TweetDuckVideoPlayer")).Value;
[STAThread] [STAThread]
private static int Main(string[] args){ private static int Main(string[] args){
Application.EnableVisualStyles(); Application.EnableVisualStyles();
@ -24,17 +21,19 @@ private static int Main(string[] args){
IntPtr ownerHandle; IntPtr ownerHandle;
int defaultVolume; int defaultVolume;
string videoUrl; string videoUrl;
string pipeToken;
try{ try{
ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture)); ownerHandle = new IntPtr(int.Parse(args[0], NumberStyles.Integer, CultureInfo.InvariantCulture));
defaultVolume = int.Parse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture); defaultVolume = int.Parse(args[1], NumberStyles.Integer, CultureInfo.InvariantCulture);
videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri; videoUrl = new Uri(args[2], UriKind.Absolute).AbsoluteUri;
pipeToken = args[3];
}catch{ }catch{
return CODE_INVALID_ARGS; return CODE_INVALID_ARGS;
} }
try{ try{
Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl)); Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken));
}catch(Exception e){ }catch(Exception e){
Console.Out.WriteLine(e.Message); Console.Out.WriteLine(e.Message);
return CODE_LAUNCH_FAIL; return CODE_LAUNCH_FAIL;