mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-25 10:05:49 +08:00
obs
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.Managed" Version="1.18.1" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
||||
<PackageReference Include="NAudio" Version="2.2.1" />
|
||||
<PackageReference Include="obs-websocket-dotnet" Version="5.0.0.3" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="OpenCvSharp4.WpfExtensions" Version="4.8.0.20230708" />
|
||||
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.8.0.20230708" />
|
||||
|
||||
@@ -42,4 +42,8 @@ public partial class CommonConfig : ObservableObject
|
||||
// 关闭时还原分辨率
|
||||
[ObservableProperty]
|
||||
private bool _restoreResolutionOnExit;
|
||||
|
||||
// 录制工具 ffmpeg/obs
|
||||
[ObservableProperty]
|
||||
private string _recorder = "ffmpeg";
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
|
||||
|
||||
// private SharpAviRecorder _sharpAviRecorder;
|
||||
|
||||
private FfmpegRecorder? _ffmpegRecorder;
|
||||
private IVideoRecorder? _videoRecorder;
|
||||
|
||||
private readonly Dictionary<Keys, bool> _keyDownState = [];
|
||||
|
||||
@@ -64,6 +64,11 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
|
||||
Toast.Warning("已经在录制状态,请不要重复启动录制功能");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TaskContext.Instance().Config.CommonConfig.Recorder == "obs")
|
||||
{
|
||||
TaskControl.Logger.LogInformation("当前选择使用OBS录制,OBS启动较慢,请耐心等待...");
|
||||
}
|
||||
|
||||
_keyMouseMacroRecordHotkey = TaskContext.Instance().Config.HotKeyConfig.KeyMouseMacroRecordHotkey;
|
||||
_paimonSwitchEnabled = TaskContext.Instance().Config.RecordConfig.PaimonSwitchEnabled;
|
||||
@@ -80,7 +85,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
|
||||
// _sharpAviRecorder = new SharpAviRecorder( Path.Combine(videoPath, $"{DateTime.Now:yyyyMMddHH_mmssffff.avi}"),
|
||||
// CodecIds.MotionJpeg, 90, 0, SupportedWaveFormat.WAVE_FORMAT_44M16, false, 0);
|
||||
|
||||
_ffmpegRecorder = new FfmpegRecorder(fileName);
|
||||
_videoRecorder = VideoRecorderFactory.Create(TaskContext.Instance().Config.CommonConfig.Recorder, fileName);
|
||||
_directInputMonitor = new DirectInputMonitor();
|
||||
|
||||
// TaskTriggerDispatcher.Instance().StopTimer();
|
||||
@@ -98,7 +103,8 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
|
||||
}
|
||||
|
||||
|
||||
_ffmpegRecorder.Start();
|
||||
var videoEnabled = _videoRecorder.Start();
|
||||
|
||||
_directInputMonitor.Start();
|
||||
_recorder = new KeyMouseRecorderJsonLine(fileName);
|
||||
|
||||
@@ -120,7 +126,7 @@ public class GlobalKeyMouseRecord : Singleton<GlobalKeyMouseRecord>
|
||||
_directInputMonitor?.Dispose();
|
||||
_directInputMonitor = null;
|
||||
|
||||
_ffmpegRecorder?.Stop();
|
||||
_videoRecorder?.Stop();
|
||||
|
||||
if (_timer.Enabled)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ using Serilog.Core;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Video;
|
||||
|
||||
public class FfmpegRecorder
|
||||
public class FfmpegRecorder : IVideoRecorder
|
||||
{
|
||||
// ffmpeg进程
|
||||
private readonly Process _process;
|
||||
@@ -132,4 +132,9 @@ public class FfmpegRecorder
|
||||
TaskControl.Logger.LogError("ffmpeg录制: 停止时异常:{Text}", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
10
BetterGenshinImpact/Core/Video/IVideoRecorder.cs
Normal file
10
BetterGenshinImpact/Core/Video/IVideoRecorder.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Video;
|
||||
|
||||
public interface IVideoRecorder : IDisposable
|
||||
{
|
||||
public bool Start();
|
||||
|
||||
public void Stop();
|
||||
}
|
||||
159
BetterGenshinImpact/Core/Video/ObsRecorder.cs
Normal file
159
BetterGenshinImpact/Core/Video/ObsRecorder.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OBSWebsocketDotNet;
|
||||
using OBSWebsocketDotNet.Communication;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Video;
|
||||
|
||||
public class ObsRecorder : IVideoRecorder
|
||||
{
|
||||
private static readonly string ObsPath = Global.Absolute(@"video\bin\OBS-Studio-31.0.0-Windows\bin\64bit\obs64.exe");
|
||||
private Process? _obs64Process;
|
||||
|
||||
private OBSWebsocket obs;
|
||||
private bool isConnected = false;
|
||||
|
||||
public ObsRecorder()
|
||||
{
|
||||
// 判断 OBS 是否已经启动
|
||||
if (Process.GetProcessesByName("obs64").Length == 0)
|
||||
{
|
||||
// 启动 OBS 并等待启动完成
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = ObsPath,
|
||||
Arguments = "--disable-shutdown-check --disable-missing-files-check --disable-updater",
|
||||
WorkingDirectory = Path.GetDirectoryName(ObsPath)
|
||||
};
|
||||
|
||||
_obs64Process = Process.Start(startInfo);
|
||||
if (_obs64Process != null)
|
||||
{
|
||||
_obs64Process.WaitForInputIdle(); // Wait for the process to be ready for input
|
||||
Debug.WriteLine("OBS has started and is ready for input.");
|
||||
TaskControl.Logger.LogInformation("OBS: 启动完成");
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskControl.Logger.LogError("OBS: 启动失败");
|
||||
throw new Exception("OBS启动失败");
|
||||
}
|
||||
}
|
||||
|
||||
obs = new OBSWebsocket();
|
||||
|
||||
// 注册连接事件处理
|
||||
obs.Connected += OnConnected;
|
||||
obs.Disconnected += OnDisconnected;
|
||||
|
||||
Connect();
|
||||
}
|
||||
|
||||
// 连接到 OBS
|
||||
public void Connect(string url = "ws://localhost:44557", string password = "huiyadanli@789")
|
||||
{
|
||||
try
|
||||
{
|
||||
obs.ConnectAsync(url, password);
|
||||
}
|
||||
catch (AuthFailureException)
|
||||
{
|
||||
TaskControl.Logger.LogError("OBS: 验证失败 - 密码错误");
|
||||
throw;
|
||||
}
|
||||
catch (ErrorResponseException ex)
|
||||
{
|
||||
TaskControl.Logger.LogError($"连接失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TaskControl.Logger.LogError($"连接失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// 开始录制
|
||||
public bool Start()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
if (obs.IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
obs.StartRecord();
|
||||
TaskControl.Logger.LogInformation("OBS: 开始录制");
|
||||
return true;
|
||||
}
|
||||
catch (ErrorResponseException ex)
|
||||
{
|
||||
if (ex.ErrorCode == 207)
|
||||
{
|
||||
TaskControl.Logger.LogInformation("207错误,等待连接 OBS 就绪...重试次数: {Count}", i + 1);
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskControl.Logger.LogError($"OBS: 开始录制失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
TaskControl.Logger.LogInformation("等待连接 OBS 连接...重试次数: {Count}", i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TaskControl.Logger.LogError("OBS: 启动录制失败,未连接到 OBS");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 停止录制
|
||||
public void Stop()
|
||||
{
|
||||
if (obs.IsConnected)
|
||||
{
|
||||
var path = obs.StopRecord();
|
||||
TaskControl.Logger.LogInformation("OBS: 停止录制录制");
|
||||
TaskControl.Logger.LogInformation("OBS: 文件存储在 {Path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConnected(object? sender, EventArgs e)
|
||||
{
|
||||
isConnected = true;
|
||||
TaskControl.Logger.LogInformation("OBS: 成功连接");
|
||||
}
|
||||
|
||||
private void OnDisconnected(object? sender, ObsDisconnectionInfo e)
|
||||
{
|
||||
if (isConnected)
|
||||
{
|
||||
TaskControl.Logger.LogWarning("OBS: 断开连接, 原因: {Reason}", e.DisconnectReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskControl.Logger.LogError("OBS: 断开连接, 原因: {Reason}", e.DisconnectReason);
|
||||
}
|
||||
|
||||
isConnected = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (obs.IsConnected)
|
||||
{
|
||||
obs.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
BetterGenshinImpact/Core/Video/VideoRecorderFactory.cs
Normal file
25
BetterGenshinImpact/Core/Video/VideoRecorderFactory.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Video;
|
||||
|
||||
public class VideoRecorderFactory
|
||||
{
|
||||
|
||||
private static ObsRecorder? _obsRecorder;
|
||||
|
||||
public static IVideoRecorder Create(string recorderType, string fileName)
|
||||
{
|
||||
switch (recorderType)
|
||||
{
|
||||
case "ffmpeg":
|
||||
return new FfmpegRecorder(fileName);
|
||||
case "obs":
|
||||
_obsRecorder ??= new ObsRecorder();
|
||||
return _obsRecorder;
|
||||
default:
|
||||
throw new ArgumentException("不支持的录制工具");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,14 +103,20 @@ public static class TouchpadManager
|
||||
|
||||
public static bool HasTouchInput()
|
||||
{
|
||||
foreach (TabletDevice tabletDevice in Tablet.TabletDevices)
|
||||
bool hasTouchInput = false;
|
||||
UIDispatcherHelper.Invoke(()=>
|
||||
{
|
||||
//Only detect if it is a touch Screen not how many touches (i.e. Single touch or Multi-touch)
|
||||
if(tabletDevice.Type == TabletDeviceType.Touch)
|
||||
return true;
|
||||
}
|
||||
foreach (TabletDevice tabletDevice in Tablet.TabletDevices)
|
||||
{
|
||||
//Only detect if it is a touch Screen not how many touches (i.e. Single touch or Multi-touch)
|
||||
if(tabletDevice.Type == TabletDeviceType.Touch)
|
||||
{
|
||||
hasTouchInput= true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
return hasTouchInput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
@@ -68,6 +70,16 @@ public class StartEndSingleton: Singleton<StartEndSingleton>
|
||||
SysDpi.Instance.ResetDpi();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process.GetProcessesByName("obs64").ToList().ForEach(p => p.Kill());
|
||||
Process.GetProcessesByName("ffmpeg").ToList().ForEach(p => p.Kill());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -501,6 +501,33 @@
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.CommonConfig.RestoreResolutionOnExit, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="显示实时任务启用状态"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="在遮罩内显示实时任务启用状态"
|
||||
TextWrapping="Wrap" />
|
||||
<ComboBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
ItemsSource="{Binding RecorderTypes, Mode=OneWay}"
|
||||
SelectedItem="{Binding Config.CommonConfig.Recorder, Mode=TwoWay}">
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
[ObservableProperty] private bool _isLoading;
|
||||
|
||||
[ObservableProperty] private string _webhookStatus = string.Empty;
|
||||
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private string[] _recorderTypes = ["ffmpeg", "obs"];
|
||||
|
||||
public CommonSettingsPageViewModel(IConfigService configService, INavigationService navigationService, NotificationService notificationService)
|
||||
{
|
||||
|
||||
@@ -128,10 +128,10 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fileName = $"{DateTime.Now:yyyy_MM_dd_HH_mm_ss}";
|
||||
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
@@ -139,7 +139,11 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
|
||||
var pcFolder = Global.Absolute(@$"User/KeyMouseScript/{fileName}");
|
||||
Directory.CreateDirectory(pcFolder);
|
||||
// 移动PC信息
|
||||
File.Copy(Global.Absolute(@$"User/pc.json"), Path.Combine(pcFolder, "pc.json"), true);
|
||||
var src= Global.Absolute(@$"User/pc.json");
|
||||
if (File.Exists(src))
|
||||
{
|
||||
File.Copy(Global.Absolute(@$"User/pc.json"), Path.Combine(pcFolder, "pc.json"), true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -148,15 +152,22 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (!IsRecording)
|
||||
{
|
||||
IsRecording = true;
|
||||
SystemSettingsManager.GetSystemSettings();
|
||||
SystemSettingsManager.SetSystemSettings();
|
||||
|
||||
|
||||
await GlobalKeyMouseRecord.Instance.StartRecord(fileName);
|
||||
try
|
||||
{
|
||||
await GlobalKeyMouseRecord.Instance.StartRecord(fileName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogDebug(e, "启动录制时发生异常");
|
||||
_logger.LogError(e.Message);
|
||||
IsRecording = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,9 +188,30 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogDebug(e, "停止录制时发生异常");
|
||||
_logger.LogWarning(e.Message);
|
||||
_logger.LogError(e.Message);
|
||||
}
|
||||
|
||||
SystemSettingsManager.RestoreSystemSettings();
|
||||
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var pcFolder = Global.Absolute(@$"User/KeyMouseScript/{fileName}");
|
||||
Directory.CreateDirectory(pcFolder);
|
||||
// 移动PC信息
|
||||
var src= Global.Absolute(@$"User/pc.json");
|
||||
if (File.Exists(src))
|
||||
{
|
||||
File.Copy(Global.Absolute(@$"User/pc.json"), Path.Combine(pcFolder, "pc.json"), true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TaskControl.Logger.LogDebug("移动PC信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user