diff --git a/FlashCap.Core/CameraControlProperty.cs b/FlashCap.Core/CameraControlProperty.cs new file mode 100644 index 0000000..b322163 --- /dev/null +++ b/FlashCap.Core/CameraControlProperty.cs @@ -0,0 +1,13 @@ +namespace FlashCap +{ + public enum CameraControlProperty + { + Pan = 0, + Tilt, + Roll, + Zoom, + Exposure, + Iris, + Focus + } +} \ No newline at end of file diff --git a/FlashCap.Core/CaptureDevice.cs b/FlashCap.Core/CaptureDevice.cs index f1bf3dd..6129190 100644 --- a/FlashCap.Core/CaptureDevice.cs +++ b/FlashCap.Core/CaptureDevice.cs @@ -70,6 +70,8 @@ protected abstract Task OnInitializeAsync( protected abstract void OnCapture( IntPtr pData, int size, long timestampMicroseconds, long frameIndex, PixelBuffer buffer); + protected abstract void SetControlProperty(CameraControlProperty property, int value); + ////////////////////////////////////////////////////////////////////////// internal Task InternalInitializeAsync( @@ -101,4 +103,7 @@ internal async Task InternalStopAsync(CancellationToken ct) internal void InternalOnCapture( IntPtr pData, int size, long timestampMicroseconds, long frameIndex, PixelBuffer buffer) => this.OnCapture(pData, size, timestampMicroseconds, frameIndex, buffer); + + internal void InternalSetControlProperty(CameraControlProperty property, int value) => + this.SetControlProperty(property, value); } diff --git a/FlashCap.Core/Devices/DirectShowDevice.cs b/FlashCap.Core/Devices/DirectShowDevice.cs index 9bb8b8d..1520bb5 100644 --- a/FlashCap.Core/Devices/DirectShowDevice.cs +++ b/FlashCap.Core/Devices/DirectShowDevice.cs @@ -15,6 +15,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using static FlashCap.Internal.NativeMethods_DirectShow; namespace FlashCap.Devices; @@ -40,12 +41,14 @@ public void ResetFrameIndex() => this.frameIndex = 0; // whichMethodToCallback: 0 - [PreserveSig] public int SampleCB( + [PreserveSig] + public int SampleCB( double sampleTime, NativeMethods_DirectShow.IMediaSample sample) => unchecked((int)0x80004001); // E_NOTIMPL // whichMethodToCallback: 1 - [PreserveSig] public int BufferCB( + [PreserveSig] + public int BufferCB( double sampleTime, IntPtr pBuffer, int bufferLen) { // HACK: Avoid stupid camera devices... @@ -75,6 +78,7 @@ [PreserveSig] public int BufferCB( private NativeMethods_DirectShow.IGraphBuilder? graphBuilder; private SampleGrabberSink? sampleGrabberSink; private IntPtr pBih; + private IAMCameraControl cameraControl; #pragma warning disable CS8618 internal DirectShowDevice(object identity, string name) : @@ -183,6 +187,20 @@ protected override Task OnInitializeAsync( /////////////////////////////// + Guid PinCategory_Capture = new(0xfb6c4281, 0x0353, 0x11d1, 0x90, 0x5f, 0x00, 0x00, 0xc0, 0xcc, 0x16, 0xba); + Guid MediaType_Interleaved = new(0x73766169, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); + if (captureGraphBuilder.FindInterface(PinCategory_Capture, MediaType_Interleaved, captureSource, typeof(IAMCameraControl).GUID, out object? intf) < 0) + { + // Maybe there are cameras that don't have a camera control interface? + throw new ArgumentException($"FlashCap: Couldn't get camera control interface: DevicePath={devicePath}"); + } + if (intf != null) + { + cameraControl = (IAMCameraControl)intf; + } + + /////////////////////////////// + if (captureGraphBuilder.RenderStream( in NativeMethods_DirectShow.PIN_CATEGORY_CAPTURE, in NativeMethods_DirectShow.MEDIATYPE_Video, @@ -320,4 +338,15 @@ protected override void OnCapture( long timestampMicroseconds, long frameIndex, PixelBuffer buffer) => buffer.CopyIn(this.pBih, pData, size, timestampMicroseconds, frameIndex, this.transcodeIfYUV); + + protected override void SetControlProperty(CameraControlProperty property, int value) + { + lock (this) + { + if (this.IsRunning) + { + cameraControl.Set(property, value, CameraControlFlags.None); + } + } + } } diff --git a/FlashCap.Core/Devices/V4L2Device.cs b/FlashCap.Core/Devices/V4L2Device.cs index 366ec2a..64ee5f9 100644 --- a/FlashCap.Core/Devices/V4L2Device.cs +++ b/FlashCap.Core/Devices/V4L2Device.cs @@ -427,4 +427,9 @@ protected override void OnCapture( long timestampMicroseconds, long frameIndex, PixelBuffer buffer) => buffer.CopyIn(this.pBih, pData, size, timestampMicroseconds, frameIndex, this.transcodeIfYUV); + + protected override void SetControlProperty(CameraControlProperty property, int value) + { + throw new NotImplementedException(); + } } diff --git a/FlashCap.Core/Devices/VideoForWindowsDevice.cs b/FlashCap.Core/Devices/VideoForWindowsDevice.cs index 2407142..b9305c6 100644 --- a/FlashCap.Core/Devices/VideoForWindowsDevice.cs +++ b/FlashCap.Core/Devices/VideoForWindowsDevice.cs @@ -286,4 +286,9 @@ protected override void OnCapture( IntPtr pData, int size, long timestampMicroseconds, long frameIndex, PixelBuffer buffer) => buffer.CopyIn(this.pBih, pData, size, timestampMicroseconds, frameIndex, this.transcodeIfYUV); + + protected override void SetControlProperty(CameraControlProperty property, int value) + { + throw new NotImplementedException(); + } } diff --git a/FlashCap.Core/Internal/NativeMethods_DirectShow.cs b/FlashCap.Core/Internal/NativeMethods_DirectShow.cs index 3c40c48..c250788 100644 --- a/FlashCap.Core/Internal/NativeMethods_DirectShow.cs +++ b/FlashCap.Core/Internal/NativeMethods_DirectShow.cs @@ -572,6 +572,44 @@ [PreserveSig] int get_RegFilterCollection( [PreserveSig] int StopWhenReady(); } + [Flags] + public enum CameraControlFlags + { + None = 0x0, + Auto = 0x0001, + Manual = 0x0002 + } + + [SuppressUnmanagedCodeSecurity] + [Guid("C6E13370-30AC-11d0-A18C-00A0C9118956")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IAMCameraControl + { + [PreserveSig] + int GetRange( + [In] CameraControlProperty Property, + [Out] out int pMin, + [Out] out int pMax, + [Out] out int pSteppingDelta, + [Out] out int pDefault, + [Out] out CameraControlFlags pCapsFlags + ); + + [PreserveSig] + int Set( + [In] CameraControlProperty Property, + [In] int lValue, + [In] CameraControlFlags Flags + ); + + [PreserveSig] + int Get( + [In] CameraControlProperty Property, + [Out] out int lValue, + [Out] out CameraControlFlags Flags + ); + } + //////////////////////////////////////////////////////////////////////// public static readonly Guid CLSID_SystemDeviceEnum = diff --git a/FlashCap/CaptureDeviceExtension.cs b/FlashCap/CaptureDeviceExtension.cs index d7fcedf..900a06d 100644 --- a/FlashCap/CaptureDeviceExtension.cs +++ b/FlashCap/CaptureDeviceExtension.cs @@ -28,6 +28,9 @@ public static Task StartAsync(this CaptureDevice captureDevice, CancellationToke public static Task StopAsync(this CaptureDevice captureDevice, CancellationToken ct = default) => captureDevice.InternalStopAsync(ct); + public static void SetControlProperty(this CaptureDevice captureDevice, CameraControlProperty property, int value) => + captureDevice.InternalSetControlProperty(property, value); + [Obsolete("Start method will be deprecated. Switch to use StartAsync method.")] public static void Start(this CaptureDevice captureDevice) => _ = captureDevice.InternalStartAsync(default);