From da1e341721f7ecbf5038d5f9d6f163e68eed4c84 Mon Sep 17 00:00:00 2001 From: sonicv6 Date: Tue, 20 Sep 2022 20:17:24 +0100 Subject: [PATCH 1/4] fix: Incorrect video duration. Used ffmpeg to obtain correct video file duration. --- Classes/Recorders/LibObsRecorder.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Classes/Recorders/LibObsRecorder.cs b/Classes/Recorders/LibObsRecorder.cs index c9f7f3b6..a6afd24d 100644 --- a/Classes/Recorders/LibObsRecorder.cs +++ b/Classes/Recorders/LibObsRecorder.cs @@ -74,9 +74,6 @@ public override async void Start() { } else if (formattedMsg == "[game-capture: 'gameplay'] capture stopped") { signalGCHookSuccess = false; - - //TODO: Read video recording lenght from file or in another way. - RecordingService.lastVideoDuration = RecordingService.recordingElapsed; } } } @@ -360,7 +357,7 @@ public override async Task StopRecording() { Logger.WriteLine(string.Format("Session recording saved to {0}", videoSavePath)); Logger.WriteLine(string.Format("LibObs stopped recording {0} {1} [{2}]", session.Pid, session.GameTitle, bnum_allocs())); - + RecordingService.lastVideoDuration = GetVideoDuration(videoSavePath); try { var t = await Task.Run(() => GetAllVideos(WebMessage.videoSortSettings.game, WebMessage.videoSortSettings.sortBy)); WebMessage.SendMessage(t); From e62cf65a7d20bf23d7a7ea685e50d48b79ae2b26 Mon Sep 17 00:00:00 2001 From: sonicv6 Date: Wed, 21 Sep 2022 15:33:38 +0100 Subject: [PATCH 2/4] refactor: Refactored hotkeys to work around low-level keyboard hook. --- Classes/Recorders/LibObsRecorder.cs | 2 +- Classes/Services/BookmarkService.cs | 31 +----- Classes/Services/HotkeyService.cs | 117 +++++++++++++------- Classes/Services/Hotkeys/BookmarkHotkey.cs | 25 +++++ Classes/Services/Hotkeys/Hotkey.cs | 48 ++++++++ Classes/Services/Hotkeys/RecordingHotkey.cs | 25 +++++ Classes/Services/KeyboardHookService.cs | 90 --------------- Classes/Services/RecordingService.cs | 3 - 8 files changed, 177 insertions(+), 164 deletions(-) create mode 100644 Classes/Services/Hotkeys/BookmarkHotkey.cs create mode 100644 Classes/Services/Hotkeys/Hotkey.cs create mode 100644 Classes/Services/Hotkeys/RecordingHotkey.cs delete mode 100644 Classes/Services/KeyboardHookService.cs diff --git a/Classes/Recorders/LibObsRecorder.cs b/Classes/Recorders/LibObsRecorder.cs index a6afd24d..ada881f7 100644 --- a/Classes/Recorders/LibObsRecorder.cs +++ b/Classes/Recorders/LibObsRecorder.cs @@ -367,7 +367,7 @@ public override async Task StopRecording() { } //Adding bookmarks - KeyboardHookService.Stop("/" + MakeValidFolderNameSimple(session.GameTitle) + "/" + videoNameTimeStamp + "-ses.mp4"); + BookmarkService.ApplyBookmarkToSavedVideo("/" + MakeValidFolderNameSimple(session.GameTitle) + "/" + videoNameTimeStamp + "-ses.mp4"); return true; } diff --git a/Classes/Services/BookmarkService.cs b/Classes/Services/BookmarkService.cs index 77766351..219e60f3 100644 --- a/Classes/Services/BookmarkService.cs +++ b/Classes/Services/BookmarkService.cs @@ -9,9 +9,8 @@ namespace RePlays.Classes.Services { internal static class BookmarkService { - private static Keys bookmarkKey = Keys.F8; - static List bookmarks = new List(); - static int latestBookmarkKeyPress = 0; + static List bookmarks = new(); + static int latestBookmarkKeyPress; public static void AddBookmark() { @@ -36,32 +35,6 @@ public static void ApplyBookmarkToSavedVideo(string videoName) bookmarks.Clear(); } - public static void SetBookmarkKeyFromSettings() - { - //Get bookmark key - string[] keybind; - SettingsService.Settings.keybindings.TryGetValue("CreateBookmark", out keybind); - for (int i = 0; i < keybind.Length; i++) - { - Keys key = Keys.None; - Enum.TryParse(keybind[i], out key); - if (i == 0) bookmarkKey = key; - else bookmarkKey = key; - - //TODO: Make it possible to use multiple keys - //else bookmarkKey |= key; - } - } - - [DllImport("user32.dll")] - static public extern short GetKeyState(Keys nVirtKey); - - public static bool IsPressingBookmark() - { - int state = GetKeyState(bookmarkKey); - if (state > 1 || state < -1) return true; - return false; - } } } diff --git a/Classes/Services/HotkeyService.cs b/Classes/Services/HotkeyService.cs index 100c0179..0a4f3a74 100644 --- a/Classes/Services/HotkeyService.cs +++ b/Classes/Services/HotkeyService.cs @@ -1,55 +1,90 @@ -using RePlays.Utils; -using NHotkey.WindowsForms; -using System.Windows.Forms; +using RePlays.Services; +using RePlays.Utils; using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using RePlays.Classes.Services.Hotkeys; -namespace RePlays.Services { - public static class HotkeyService { - public static string EditId = null; +namespace RePlays.Services +{ + public static class HotkeyService + { + public delegate int CallbackDelegate(int Code, int W, int L); - public static void Start() { - foreach (var keybind in SettingsService.Settings.keybindings) { - RegisterHotkey(keybind.Key, keybind.Value); - } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct KBDLLHookStruct + { + public Int32 vkCode; + public Int32 scanCode; + public Int32 flags; + public Int32 time; + public Int32 dwExtraInfo; } - public static void Stop() { - foreach (var keybind in SettingsService.Settings.keybindings) { - HotkeyManager.Current.Remove(keybind.Key); - } + [DllImport("user32", CallingConvention = CallingConvention.StdCall)] + private static extern int SetWindowsHookEx(int idHook, CallbackDelegate lpfn, int hInstance, int threadId); + + [DllImport("user32", CallingConvention = CallingConvention.StdCall)] + private static extern bool UnhookWindowsHookEx(int idHook); + + [DllImport("user32", CallingConvention = CallingConvention.StdCall)] + private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); + + [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] + private static extern int GetCurrentThreadId(); + public static string EditId = null; + private static List _hotkeys = new(); + private static int HookID = 0; + static CallbackDelegate TheHookCB = null; + + public static void Start() + { + //Create hotkeys + _hotkeys.Add(new BookmarkHotkey()); + _hotkeys.Add(new RecordingHotkey()); + //Create hook + TheHookCB = KeybHookProc; + HookID = SetWindowsHookEx(13, TheHookCB, 0, 0); + Logger.WriteLine("Loaded KeyboardHook..."); } - public static void RegisterHotkey(string name, string[] keys) { - Keys keybind = Keys.None; + public static void Stop() + { + _hotkeys.Clear(); + UnhookWindowsHookEx(HookID); + Logger.WriteLine("Unloaded KeyboardHook..."); + } - for (int i = 0; i < keys.Length; i++) { - Keys key = Keys.None; - Enum.TryParse(keys[i], out key); + private static int KeybHookProc(int Code, int W, int L) + { + if (Code < 0) + return CallNextHookEx(HookID, Code, W, L); - if(i == 0) keybind = key; - else keybind |= key; + try + { + KeyEvents kEvent = (KeyEvents)W; + if (kEvent == KeyEvents.KeyDown) + { + foreach (Hotkey h in _hotkeys) + { + if (h.IsPressed()) h.Action(); + } + } + } + catch (Exception e) + { + Logger.WriteLine("Error getting current keypress: " + e.ToString()); } - HotkeyManager.Current.AddOrReplace(name, keybind, (s, e) => { - switch (name) { - case "StartStopRecording": { - if (!RecordingService.IsRecording) { - Logger.WriteLine("Manual Start Recording"); - RecordingService.StartRecording(); - } - else { - Logger.WriteLine("Manual Stop Recording"); - RecordingService.StopRecording(); - } - } - break; - default: - Logger.WriteLine($"No hotkey event match for {name}"); - break; - } - e.Handled = true; - }); - Logger.WriteLine($"Registered Hotkey: {name} / [{string.Join(" | ", keys)}]"); + return CallNextHookEx(HookID, Code, W, L); + } + + + public enum KeyEvents + { + KeyDown = 0x0100, + KeyUp = 0x0101 } } } diff --git a/Classes/Services/Hotkeys/BookmarkHotkey.cs b/Classes/Services/Hotkeys/BookmarkHotkey.cs new file mode 100644 index 00000000..ca50b71b --- /dev/null +++ b/Classes/Services/Hotkeys/BookmarkHotkey.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using RePlays.Services; + +namespace RePlays.Classes.Services.Hotkeys +{ + public class BookmarkHotkey : Hotkey + { + public override void Action() + { + if (RecordingService.IsRecording) BookmarkService.AddBookmark(); + } + + protected override void SetKeybind() + { + string[] keybind; + SettingsService.Settings.keybindings.TryGetValue("CreateBookmark", out keybind); + _keybind = ParseKeys(keybind); + } + } +} diff --git a/Classes/Services/Hotkeys/Hotkey.cs b/Classes/Services/Hotkeys/Hotkey.cs new file mode 100644 index 00000000..a62ef339 --- /dev/null +++ b/Classes/Services/Hotkeys/Hotkey.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Microsoft.Web.WebView2.Core; + +namespace RePlays.Classes.Services.Hotkeys +{ + public abstract class Hotkey + { + protected Keys _keybind; + + protected Hotkey() + { + SetKeybind(); + } + + [DllImport("user32.dll")] + private static extern short GetKeyState(Keys nVirtKey); + public static Keys ParseKeys(string[] keys) + { + Keys keybind = Keys.None; + + for (int i = 0; i < keys.Length; i++) { + Keys key = Keys.None; + Enum.TryParse(keys[i], out key); + + if(i == 0) keybind = key; + else keybind |= key; + } + + return keybind; + } + + public abstract void Action(); + + public bool IsPressed() + { + int state = GetKeyState(_keybind); + if (state > 1 || state < -1) return true; + return false; + } + protected abstract void SetKeybind(); + } +} diff --git a/Classes/Services/Hotkeys/RecordingHotkey.cs b/Classes/Services/Hotkeys/RecordingHotkey.cs new file mode 100644 index 00000000..d409cf58 --- /dev/null +++ b/Classes/Services/Hotkeys/RecordingHotkey.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RePlays.Services; + +namespace RePlays.Classes.Services.Hotkeys +{ + public class RecordingHotkey : Hotkey + { + public override void Action() + { + if (RecordingService.IsRecording) RecordingService.StopRecording(); + else RecordingService.StartRecording(); + } + + protected override void SetKeybind() + { + string[] keybind; + SettingsService.Settings.keybindings.TryGetValue("StartStopRecording", out keybind); + _keybind = ParseKeys(keybind); + } + } +} diff --git a/Classes/Services/KeyboardHookService.cs b/Classes/Services/KeyboardHookService.cs deleted file mode 100644 index 290e5f50..00000000 --- a/Classes/Services/KeyboardHookService.cs +++ /dev/null @@ -1,90 +0,0 @@ -using RePlays.Services; -using RePlays.Utils; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Windows.Forms; - -namespace RePlays.Classes.Services -{ - public static class KeyboardHookService - { - - public delegate void LocalKeyEventHandler(Keys key, bool Shift, bool Ctrl, bool Alt); - - public delegate int CallbackDelegate(int Code, int W, int L); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - public struct KBDLLHookStruct - { - public Int32 vkCode; - public Int32 scanCode; - public Int32 flags; - public Int32 time; - public Int32 dwExtraInfo; - } - - [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern int SetWindowsHookEx(int idHook, CallbackDelegate lpfn, int hInstance, int threadId); - - [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern bool UnhookWindowsHookEx(int idHook); - - [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); - - [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] - private static extern int GetCurrentThreadId(); - - private static int HookID = 0; - static CallbackDelegate TheHookCB = null; - - public static void Start() - { - //Set bookmark key - BookmarkService.SetBookmarkKeyFromSettings(); - - //Create hook - TheHookCB = new CallbackDelegate(KeybHookProc); - HookID = SetWindowsHookEx(13, TheHookCB, 0, 0); - Logger.WriteLine("Loaded KeyboardHook..."); - } - - public static void Stop(string videoName) - { - UnhookWindowsHookEx(HookID); - BookmarkService.ApplyBookmarkToSavedVideo(videoName); - Logger.WriteLine("Unloaded KeyboardHook..."); - } - - private static int KeybHookProc(int Code, int W, int L) - { - if (Code < 0) - return CallNextHookEx(HookID, Code, W, L); - - try - { - KeyEvents kEvent = (KeyEvents)W; - if (kEvent == KeyEvents.KeyUp) - { - bool pressingBookmarkKey = BookmarkService.IsPressingBookmark(); - if (pressingBookmarkKey) - { - BookmarkService.AddBookmark(); - } - } - } - catch (Exception e) - { - Logger.WriteLine("Error getting current keypress: " + e.ToString()); - } - - return CallNextHookEx(HookID, Code, W, L); - } - - public enum KeyEvents - { - KeyUp = 0x0101, - } - } -} diff --git a/Classes/Services/RecordingService.cs b/Classes/Services/RecordingService.cs index 389034bd..85708c36 100644 --- a/Classes/Services/RecordingService.cs +++ b/Classes/Services/RecordingService.cs @@ -79,9 +79,6 @@ public static async void StartRecording() { IsRecording = true; IsPreRecording = false; - KeyboardHookService.Start(); - Application.Run(); - //frmMain.Instance.DisplayNotification("Recording Started", $"Currently recording {currentSession.GameTitle}"); } //DetectionService.DisposeDetections(); From 4fc849b8f0a6646d367b00ee7c308e18c2cd9a51 Mon Sep 17 00:00:00 2001 From: sonicv6 Date: Wed, 21 Sep 2022 17:28:20 +0100 Subject: [PATCH 3/4] fix: Handle multi-inputs correctly Refactored input checking to be cleaner and correctly handle multi-input hotkeys correctly. --- Classes/Services/HotkeyService.cs | 16 +++++++++------- Classes/Services/Hotkeys/Hotkey.cs | 18 +++++------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Classes/Services/HotkeyService.cs b/Classes/Services/HotkeyService.cs index 0a4f3a74..e98d4f85 100644 --- a/Classes/Services/HotkeyService.cs +++ b/Classes/Services/HotkeyService.cs @@ -10,7 +10,7 @@ namespace RePlays.Services { public static class HotkeyService { - public delegate int CallbackDelegate(int Code, int W, int L); + public delegate IntPtr CallbackDelegate(int Code, IntPtr W, IntPtr L); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct KBDLLHookStruct @@ -23,19 +23,19 @@ public struct KBDLLHookStruct } [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern int SetWindowsHookEx(int idHook, CallbackDelegate lpfn, int hInstance, int threadId); + private static extern IntPtr SetWindowsHookEx(int idHook, CallbackDelegate lpfn, int hInstance, int threadId); [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern bool UnhookWindowsHookEx(int idHook); + private static extern bool UnhookWindowsHookEx(IntPtr idHook); [DllImport("user32", CallingConvention = CallingConvention.StdCall)] - private static extern int CallNextHookEx(int idHook, int nCode, int wParam, int lParam); + private static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] private static extern int GetCurrentThreadId(); public static string EditId = null; private static List _hotkeys = new(); - private static int HookID = 0; + private static IntPtr HookID; static CallbackDelegate TheHookCB = null; public static void Start() @@ -56,7 +56,7 @@ public static void Stop() Logger.WriteLine("Unloaded KeyboardHook..."); } - private static int KeybHookProc(int Code, int W, int L) + private static IntPtr KeybHookProc(int Code, IntPtr W, IntPtr L) { if (Code < 0) return CallNextHookEx(HookID, Code, W, L); @@ -66,9 +66,11 @@ private static int KeybHookProc(int Code, int W, int L) KeyEvents kEvent = (KeyEvents)W; if (kEvent == KeyEvents.KeyDown) { + Keys vkCode = (Keys)Marshal.ReadInt32(L); + vkCode |= Control.ModifierKeys; foreach (Hotkey h in _hotkeys) { - if (h.IsPressed()) h.Action(); + if (vkCode == h.Keybind) h.Action(); } } } diff --git a/Classes/Services/Hotkeys/Hotkey.cs b/Classes/Services/Hotkeys/Hotkey.cs index a62ef339..0238f387 100644 --- a/Classes/Services/Hotkeys/Hotkey.cs +++ b/Classes/Services/Hotkeys/Hotkey.cs @@ -12,24 +12,22 @@ namespace RePlays.Classes.Services.Hotkeys public abstract class Hotkey { protected Keys _keybind; + public Keys Keybind => _keybind; protected Hotkey() { SetKeybind(); } - [DllImport("user32.dll")] - private static extern short GetKeyState(Keys nVirtKey); public static Keys ParseKeys(string[] keys) { Keys keybind = Keys.None; - for (int i = 0; i < keys.Length; i++) { - Keys key = Keys.None; + for (int i = 0; i < keys.Length; i++) + { + Keys key; Enum.TryParse(keys[i], out key); - - if(i == 0) keybind = key; - else keybind |= key; + keybind |= key; } return keybind; @@ -37,12 +35,6 @@ public static Keys ParseKeys(string[] keys) public abstract void Action(); - public bool IsPressed() - { - int state = GetKeyState(_keybind); - if (state > 1 || state < -1) return true; - return false; - } protected abstract void SetKeybind(); } } From c547a92a4cb9f2141dbe47abd1e92eb5ce9eccba Mon Sep 17 00:00:00 2001 From: sonicv6 Date: Wed, 21 Sep 2022 21:42:46 +0100 Subject: [PATCH 4/4] fix: Encoders not loaded on first launch. --- Classes/Recorders/LibObsRecorder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/Recorders/LibObsRecorder.cs b/Classes/Recorders/LibObsRecorder.cs index ada881f7..8ea857c8 100644 --- a/Classes/Recorders/LibObsRecorder.cs +++ b/Classes/Recorders/LibObsRecorder.cs @@ -298,6 +298,7 @@ private IntPtr GetVideoEncoder(string encoder) { } private void GetAvailableEncoders() { + SettingsService.LoadSettings(); //Hacky fix for weird first launch issue, should be investigated further. UIntPtr idx = UIntPtr.Zero; string id = ""; List availableEncoders = new();