Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CameraControlProperty interface #64

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Added CameraControlProperty interface
  • Loading branch information
macaba committed Jun 19, 2023
commit 64e5e4d99f437adb48bf72370e486b5bca2d1b88
13 changes: 13 additions & 0 deletions FlashCap.Core/CameraControlProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace FlashCap
{
public enum CameraControlProperty
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: This seems to expose the raw definition of DirectShow. Perhaps there is a better way to define it, considering multi-platform. But for now I think this is fine as it is; we may change it when we consider making it V4L2 compliant.

{
Pan = 0,
Tilt,
Roll,
Zoom,
Exposure,
Iris,
Focus
}
}
5 changes: 5 additions & 0 deletions FlashCap.Core/CaptureDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public async Task DisposeAsync()
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(
Expand Down Expand Up @@ -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);
}
33 changes: 31 additions & 2 deletions FlashCap.Core/Devices/DirectShowDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static FlashCap.Internal.NativeMethods_DirectShow;

namespace FlashCap.Devices;

Expand All @@ -40,12 +41,14 @@ private sealed class SampleGrabberSink :
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...
Expand Down Expand Up @@ -75,6 +78,7 @@ private sealed class SampleGrabberSink :
private NativeMethods_DirectShow.IGraphBuilder? graphBuilder;
private SampleGrabberSink? sampleGrabberSink;
private IntPtr pBih;
private IAMCameraControl cameraControl;

#pragma warning disable CS8618
internal DirectShowDevice(object identity, string name) :
Expand Down Expand Up @@ -183,6 +187,20 @@ private sealed class SampleGrabberSink :

///////////////////////////////

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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the GUID property did not declare in netstandard1.3. So switch using self-declared field instead of it. Example and good place at IID_ICaptureGraphBuilder2 symbol.

{
// 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,
Expand Down Expand Up @@ -320,4 +338,15 @@ protected override Task OnStopAsync(CancellationToken ct)
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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call to Set() corresponds to a cross-apartment call on COM interface, so a procedure over the apartment is required. Use this.workingContext.InvokeAsync() to ensure that it is called on the correct thread. The implementation of the OnStartAsync() method will be helpful.
Perhaps it needs to be asynchronized, like SetControlPropertyAsync().

}
}
}
}
5 changes: 5 additions & 0 deletions FlashCap.Core/Devices/V4L2Device.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,4 +427,9 @@ protected override Task OnStopAsync(CancellationToken ct)
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();
}
}
5 changes: 5 additions & 0 deletions FlashCap.Core/Devices/VideoForWindowsDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,9 @@ protected override Task OnStopAsync(CancellationToken ct)
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();
}
}
38 changes: 38 additions & 0 deletions FlashCap.Core/Internal/NativeMethods_DirectShow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,44 @@ public interface IMediaControl
[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 =
Expand Down
3 changes: 3 additions & 0 deletions FlashCap/CaptureDeviceExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public static class CaptureDeviceExtension
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);
Expand Down