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)