From 8f96bb602984f00aa89e065df8b88b80ac4507a9 Mon Sep 17 00:00:00 2001 From: Mercurio <47455213+NotLugozzi@users.noreply.github.com> Date: Fri, 6 Sep 2024 15:02:18 +0200 Subject: [PATCH] 0.0.4 prep - added custom settings loading from prop.txt - added `-tune zerolatency` to FFMPEG capture - reduced latency for WASAPI audio capture - cleared up a bit of the ffmpeg spawning/killing logic --- 2dxAutoClip/iidxAutoClip.cs | 293 ++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 146 deletions(-) diff --git a/2dxAutoClip/iidxAutoClip.cs b/2dxAutoClip/iidxAutoClip.cs index 00068db..af5da94 100644 --- a/2dxAutoClip/iidxAutoClip.cs +++ b/2dxAutoClip/iidxAutoClip.cs @@ -11,29 +11,34 @@ class Program private static readonly string WebsocketAddress = "localhost"; private static readonly int WebsocketPort = 10573; private static Process? _ffmpegProcess; - private static string _ffmpegFolderPath = GetFolderPath(); + private static string _ffmpegFolderPath = GetDefaultVideosFolderPath(); private static WasapiCapture? _waveSource; private static WaveFileWriter _writer = null!; private static string _audioFilePath = null!; private static string _videoFilePath = null!; + private static string _resolution = "1920x1080"; // Default resolution + private static int _framerate = 60; // Default framerate + private static float _crf = 23; // Default CRF value + private static string _gameProcessName = "spice64"; // Default game process name private static async Task Main(string[] args) { - var spiceProcesses = Process.GetProcessesByName("spice64"); + // Load settings from prop.txt + LoadSettingsFromPropFile(); - if (spiceProcesses.Length > 0) + var gameProcesses = Process.GetProcessesByName(_gameProcessName); + if (gameProcesses.Length > 0) { - Console.WriteLine("Found spice64, Attempting connection to TickerHookWS..."); + Console.WriteLine($"Found {_gameProcessName}, Attempting connection to TickerHookWS..."); await TryConnectWebSocket(); } else { - Console.WriteLine("Unable to find Spice64. Are you sure Beatmania IIDX is running and TickerHook is enabled?"); + Console.WriteLine($"Unable to find {_gameProcessName}. Is the game running and TickerHook enabled?"); } } - - - private static string GetFolderPath() + + private static void LoadSettingsFromPropFile() { const string filePath = "prop.txt"; if (File.Exists(filePath)) @@ -47,142 +52,153 @@ class Program var path = line["path:".Length..].Trim(); if (Directory.Exists(path)) { - return path; + _ffmpegFolderPath = path; // Recording path + } - Console.WriteLine($"The path specified in {filePath} does not exist."); + else + { + Console.WriteLine($"The path specified in {filePath} does not exist. Using default recording path."); + _ffmpegFolderPath = GetDefaultVideosFolderPath(); + } + } + if (line.StartsWith("resolution:")) + { + _resolution = line["resolution:".Length..].Trim(); + Console.WriteLine($"Custom Resolution: {_resolution}"); + } + if (line.StartsWith("framerate:")) + { + _framerate = int.Parse(line["framerate:".Length..].Trim()); + Console.WriteLine($"Custom framerate: {_framerate}"); + } + if (line.StartsWith("crf:")) + { + _crf = float.Parse(line["crf:".Length..].Trim()); + Console.WriteLine($"custom crf: {_crf}"); + } + if (line.StartsWith("game_process_name:")) + { + _gameProcessName = line["game_process_name:".Length..].Trim(); + Console.WriteLine($"custom process name: {_gameProcessName}"); } } } else { - Console.WriteLine($"The file {filePath} does not exist."); + Console.WriteLine($"The file {filePath} does not exist. Using default values."); + _ffmpegFolderPath = GetDefaultVideosFolderPath(); } - - return GetDefaultVideosFolderPath(); } private static string GetDefaultVideosFolderPath() { var userName = Environment.UserName; - var defaultPath = Path.Combine($@"C:\Users\{userName}", "Videos"); - return defaultPath; + return Path.Combine($@"C:\Users\{userName}", "Videos"); } - -private static async Task TryConnectWebSocket() -{ - const int maxRetries = 5; - int attempt = 0; - - while (attempt < maxRetries) + + private static async Task TryConnectWebSocket() { - Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect..."); - - bool success = await ConnectWebSocket(); - - if (success) + const int maxRetries = 5; + int attempt = 0; + while (attempt < maxRetries) { - break; // Exit the loop if connection was successful + Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect..."); + bool success = await ConnectWebSocket(); + if (success) break; + attempt++; + if (attempt < maxRetries) + { + Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining."); + await Task.Delay(10000); + } } - - attempt++; - - if (attempt < maxRetries) + if (attempt == maxRetries) { - Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining."); - await Task.Delay(10000); // Wait for 10 seconds before retrying + Console.WriteLine("Failed to connect after 5 attempts."); } } - if (attempt == maxRetries) + private static async Task ConnectWebSocket() { - Console.WriteLine("Failed to connect after 5 attempts."); - } -} + var tickerUri = new Uri($"ws://{WebsocketAddress}:{WebsocketPort}"); + var reconnecting = false; + var lastMessage = string.Empty; + var consecutiveMessageCount = 0; + var isRecording = false; + var currentSongName = string.Empty; + var shouldCheckForMusicSelect = false; -private static async Task ConnectWebSocket() -{ - var tickerUri = new Uri($"ws://{WebsocketAddress}:{WebsocketPort}"); - var reconnecting = false; - - var lastMessage = string.Empty; - var consecutiveMessageCount = 0; - var isRecording = false; - var currentSongName = string.Empty; - - var shouldCheckForMusicSelect = false; - - using var clientWebSocket = new ClientWebSocket(); - try - { - await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None); - Console.WriteLine("Connected to TickerHook WebSocket."); - } - catch (Exception ex) - { - Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}"); - return false; // Indicate connection failure - } - - var buffer = new byte[1024]; - - while (clientWebSocket.State == WebSocketState.Open) - { - WebSocketReceiveResult result; + using var clientWebSocket = new ClientWebSocket(); try { - result = await clientWebSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None); + Console.WriteLine("Connected to TickerHook WebSocket."); } catch (Exception ex) { - Console.WriteLine($"Error receiving message: {ex.Message}"); - reconnecting = true; - break; + Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}"); + return false; } - var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper(); - Console.WriteLine($"Received message: {message}"); - - if (message == lastMessage && !message.Contains("SELECT FROM")) + var buffer = new byte[1024]; + while (clientWebSocket.State == WebSocketState.Open) { - consecutiveMessageCount++; - } - else - { - consecutiveMessageCount = 1; - lastMessage = message; - } - - if (consecutiveMessageCount >= 2 && !message.Contains("SELECT FROM") && !isRecording) - { - currentSongName = message; - Console.WriteLine("Starting recording..."); - StartRecording(currentSongName); - isRecording = true; - } - - if (isRecording) - { - if (shouldCheckForMusicSelect && message.Contains("MUSIC SELECT!!")) + WebSocketReceiveResult result; + try { - Console.WriteLine("Stopping recording..."); - StopRecording(currentSongName); - isRecording = false; - shouldCheckForMusicSelect = false; + result = await clientWebSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); } - else if (message.EndsWith("CLEAR!") || message.EndsWith("FAILED..")) - { - shouldCheckForMusicSelect = true; + catch (Exception ex) + { + Console.WriteLine($"Error receiving message: {ex.Message}"); + reconnecting = true; + break; + } + + var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper(); + Console.WriteLine($"Received message: {message}"); + + if (message == lastMessage && !message.Contains("SELECT FROM")) + { + consecutiveMessageCount++; + } + else + { + consecutiveMessageCount = 1; + lastMessage = message; + } + + if (consecutiveMessageCount >= 2 && !message.Contains("SELECT FROM") && !isRecording) + { + currentSongName = message; + Console.WriteLine("Starting recording..."); + StartRecording(currentSongName); + isRecording = true; + } + + if (isRecording) + { + if (shouldCheckForMusicSelect && message.Contains("MUSIC SELECT!!")) + { + Console.WriteLine("Stopping recording..."); + StopRecording(currentSongName); + isRecording = false; + shouldCheckForMusicSelect = false; + } + else if (message.EndsWith("CLEAR!") || message.EndsWith("FAILED..")) + { + shouldCheckForMusicSelect = true; + } } } - } - return !reconnecting; -} + return !reconnecting; + } private static void StartRecording(string songName) { Task.Run(() => StartAudioRecording(songName)); - StartFfmpegRecording(songName); + StartFfmpegRecording(songName); } private static void StartAudioRecording(string songName) @@ -191,8 +207,7 @@ private static async Task ConnectWebSocket() { var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); _audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav"; - var outputFolder = Path.GetDirectoryName(_audioFilePath)!; - Directory.CreateDirectory(outputFolder); + Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!); _waveSource = new WasapiLoopbackCapture(); _writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat); _waveSource.DataAvailable += (sender, args) => @@ -202,11 +217,8 @@ private static async Task ConnectWebSocket() _waveSource.RecordingStopped += (sender, args) => { _writer.Dispose(); - _writer = null!; _waveSource.Dispose(); - _waveSource = null; }; - _waveSource.StartRecording(); Console.WriteLine("WASAPI Audio recording started."); } @@ -216,65 +228,54 @@ private static async Task ConnectWebSocket() } } - - private static void StartFfmpegRecording(string songName) { var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); _videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv"; - - var ffmpegArguments = $"-framerate 60 " + + var ffmpegArguments = $"-framerate {_framerate} " + $"-filter_complex \"ddagrab=0,hwdownload,format=bgra\" " + - $"-c:v libx264 -movflags +faststart -crf 20 -y \"{_videoFilePath}\""; - - _ffmpegProcess = new Process(); - _ffmpegProcess.StartInfo.FileName = "ffmpeg"; - _ffmpegProcess.StartInfo.Arguments = ffmpegArguments; - _ffmpegProcess.StartInfo.UseShellExecute = false; - _ffmpegProcess.StartInfo.RedirectStandardOutput = false; - _ffmpegProcess.StartInfo.RedirectStandardError = true; - _ffmpegProcess.StartInfo.CreateNoWindow = true; - + $"-c:v libx264 -tune zerolatency -crf {_crf} -video_size {_resolution} -movflags +faststart -y \"{_videoFilePath}\""; + _ffmpegProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "ffmpeg", + Arguments = ffmpegArguments, + UseShellExecute = false, + RedirectStandardError = true, + CreateNoWindow = true + } + }; _ffmpegProcess.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data); - _ffmpegProcess.Start(); _ffmpegProcess.BeginErrorReadLine(); + + Console.WriteLine("FFmpeg recording started."); } private static void StopRecording(string songName) { - StopFfmpegRecording(); StopAudioRecording(); + StopFfmpegRecording(); CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName); } private static void StopAudioRecording() { - if (_waveSource == null) return; - - _waveSource.StopRecording(); - _waveSource.Dispose(); - _waveSource = null; - if (_writer != null) - { - _writer.Dispose(); - _writer = null!; - } - - Console.WriteLine("Audio recording stopped."); + _waveSource?.StopRecording(); + Console.WriteLine("WASAPI Audio recording stopped."); } - private static void StopFfmpegRecording() { - if (_ffmpegProcess != null && _ffmpegProcess.HasExited) return; - _ffmpegProcess?.Kill(); - _ffmpegProcess?.WaitForExit(); - _ffmpegProcess?.Dispose(); - _ffmpegProcess = null; - Console.WriteLine("FFMPEG process stopped."); + if (_ffmpegProcess != null && !_ffmpegProcess.HasExited) + { + _ffmpegProcess.Kill(); + _ffmpegProcess.WaitForExit(); + _ffmpegProcess = null!; + Console.WriteLine("FFmpeg recording stopped."); + } } - private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName) { var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mp4";