diff --git a/src/Baballonia.VFTCapture/Baballonia.VFTCapture.csproj b/src/Baballonia.VFTCapture/Baballonia.VFTCapture.csproj index a6014097..a29bd3fc 100644 --- a/src/Baballonia.VFTCapture/Baballonia.VFTCapture.csproj +++ b/src/Baballonia.VFTCapture/Baballonia.VFTCapture.csproj @@ -7,8 +7,22 @@ true + + WINDOWS + + + + LINUX + + + + + PreserveNewest + + + diff --git a/src/Baballonia.VFTCapture/VFTCapture.cs b/src/Baballonia.VFTCapture/VFTCapture.cs index 257144b3..7b45da1f 100644 --- a/src/Baballonia.VFTCapture/VFTCapture.cs +++ b/src/Baballonia.VFTCapture/VFTCapture.cs @@ -1,4 +1,4 @@ - +using System.Net; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; using OpenCvSharp; @@ -17,8 +17,15 @@ public sealed class VftCapture(string source, ILogger logger) : Capture(source, public override bool CanConnect(string connectionString) { - var lowered = connectionString.ToLower(); - return lowered.StartsWith("/dev/video") && OperatingSystem.IsLinux(); + if (OperatingSystem.IsLinux()) + { + var lowered = connectionString.ToLower(); + return lowered.StartsWith("/dev/video"); + } else if (OperatingSystem.IsWindows()) + { + return true; + } + return false; } /// @@ -39,9 +46,18 @@ public override async Task StartCapture() // Initialize VideoCapture with URL, timeout for robustness // Set capture mode to YUYV // Prevent automatic conversion to RGB +#if LINUX _videoCapture = await Task.Run(() => new VideoCapture(Source, VideoCaptureAPIs.V4L2), cts.Token); _videoCapture.Set(VideoCaptureProperties.Mode, 3); _videoCapture.Set(VideoCaptureProperties.ConvertRgb, 0); +#else + if (int.TryParse(Source, out var index)) + { + _videoCapture = await Task.Run(() => VideoCapture.FromCamera(index, VideoCaptureAPIs.DSHOW), cts.Token); + _videoCapture.Set(VideoCaptureProperties.Mode, 3); + _videoCapture.Set(VideoCaptureProperties.ConvertRgb, 0); + } +#endif _loop = true; _ = Task.Run(VideoCapture_UpdateLoop); @@ -98,9 +114,13 @@ private Task VideoCapture_UpdateLoop() private void SetTrackerState(bool setActive) { // Prev: var fd = ViveFacialTracker.open(Url, ViveFacialTracker.FileOpenFlags.O_RDWR); +#if LINUX var vftFileStream = File.Open(Source, FileMode.Open, FileAccess.ReadWrite); var fd = vftFileStream.SafeFileHandle.DangerousGetHandle(); if (fd != IntPtr.Zero) +#elif WINDOWS + int fd = 0; +#endif { try { @@ -113,8 +133,10 @@ private void SetTrackerState(bool setActive) } finally { +#if LINUX // Prev: ViveFacialTracker.close((int)fd); vftFileStream.Close(); +#endif } } } diff --git a/src/Baballonia.VFTCapture/VFTCaptureFactory.cs b/src/Baballonia.VFTCapture/VFTCaptureFactory.cs index 0169e1e5..24fdb08b 100644 --- a/src/Baballonia.VFTCapture/VFTCaptureFactory.cs +++ b/src/Baballonia.VFTCapture/VFTCaptureFactory.cs @@ -19,8 +19,16 @@ public Capture Create(string address) public bool CanConnect(string address) { - var lowered = address.ToLower(); - return lowered.StartsWith("/dev/video"); + if (OperatingSystem.IsLinux()) + { + var lowered = address.ToLower(); + return lowered.StartsWith("/dev/video"); + } + else if (OperatingSystem.IsWindows()) + { + return true; + } + return false; } public string GetProviderName() diff --git a/src/Baballonia.VFTCapture/ViveFacialTracker.cs b/src/Baballonia.VFTCapture/ViveFacialTracker_Linux.cs similarity index 99% rename from src/Baballonia.VFTCapture/ViveFacialTracker.cs rename to src/Baballonia.VFTCapture/ViveFacialTracker_Linux.cs index 32573df8..7b606fa6 100644 --- a/src/Baballonia.VFTCapture/ViveFacialTracker.cs +++ b/src/Baballonia.VFTCapture/ViveFacialTracker_Linux.cs @@ -2,6 +2,7 @@ namespace Baballonia.VFTCapture; +#if LINUX public partial class ViveFacialTracker { public enum FileOpenFlags @@ -308,3 +309,4 @@ public static bool deactivate_tracker(int fd) return true; } } +#endif diff --git a/src/Baballonia.VFTCapture/ViveFacialTracker_Windows.cs b/src/Baballonia.VFTCapture/ViveFacialTracker_Windows.cs new file mode 100644 index 00000000..3db5f039 --- /dev/null +++ b/src/Baballonia.VFTCapture/ViveFacialTracker_Windows.cs @@ -0,0 +1,33 @@ +using System.Runtime.InteropServices; + +namespace Baballonia.VFTCapture; + +#if WINDOWS +public partial class ViveFacialTracker +{ + public enum ViveFacialTrackerError_t + { + VFT_OK = 0, + VFT_COM_ERR = -1, + VFT_TRACKER_NOT_FOUND = -2, + VFT_FAIL = -3, + }; + + private const string UsbCommunicatorLibrary = "blluc"; + + [LibraryImport(UsbCommunicatorLibrary)] + public static partial int enableViveFacialTracker(); + [LibraryImport(UsbCommunicatorLibrary)] + public static partial int disableViveFacialTracker(); + + public static bool activate_tracker(int fd) + { + return enableViveFacialTracker() == (int)ViveFacialTrackerError_t.VFT_OK; + } + + public static bool deactivate_tracker(int fd) + { + return disableViveFacialTracker() == (int)ViveFacialTrackerError_t.VFT_OK; + } +} +#endif diff --git a/src/Baballonia.VFTCapture/blluc.dll b/src/Baballonia.VFTCapture/blluc.dll new file mode 100644 index 00000000..afb81f16 Binary files /dev/null and b/src/Baballonia.VFTCapture/blluc.dll differ diff --git a/src/Baballonia/ViewModels/SplitViewPane/HomePageViewModel.cs b/src/Baballonia/ViewModels/SplitViewPane/HomePageViewModel.cs index 951b2a91..bb32e245 100644 --- a/src/Baballonia/ViewModels/SplitViewPane/HomePageViewModel.cs +++ b/src/Baballonia/ViewModels/SplitViewPane/HomePageViewModel.cs @@ -601,7 +601,10 @@ public async Task StartCamera(CameraControllerModel model) var backend = model.SelectedCaptureMethod; if (!model.CaptureMethodVisible) backend = ""; - + if (OperatingSystem.IsWindows() && address == "HTC Multimedia Camera" && string.IsNullOrEmpty(backend)) + { + backend = "VFTCapture"; + } bool success = false; switch (model.Camera)