Skip to content

Commit

Permalink
Merge pull request #30 from vignetteapp/camera-config-management
Browse files Browse the repository at this point in the history
Camera config management
  • Loading branch information
Speykious authored Oct 29, 2022
2 parents b51602b + a5fe069 commit 040cb44
Show file tree
Hide file tree
Showing 18 changed files with 2,536 additions and 38 deletions.
29 changes: 25 additions & 4 deletions SeeShark.Example.Ascii/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using SeeShark.Decode;
using SeeShark.Device;
Expand Down Expand Up @@ -44,7 +45,7 @@ static void Main(string[] args)

manager = new CameraManager();

string devicePath;
CameraInfo device;
if (args.Length < 1)
{
/// Select an available camera device.
Expand All @@ -60,21 +61,41 @@ static void Main(string[] args)
Console.Out.Flush();
if (int.TryParse(Console.ReadLine(), out int index) && index < manager.Devices.Count && index >= 0)
{
devicePath = manager.Devices[index].Path;
device = manager.Devices[index];
break;
}
}
}
else
{
devicePath = args[0];
device = manager.Devices.First((ci) => ci.Path == args[0]);
}

/// Select video input options for the given device path.
VideoInputOptions? vios = null;
if (device.AvailableVideoInputOptions != null)
{
while (true)
{
Console.WriteLine("\nVideo input options available:");
for (int i = 0; i < device.AvailableVideoInputOptions.Length; i++)
Console.WriteLine($"| #{i}: {device.AvailableVideoInputOptions[i]}");

Console.Write("\nChoose an input option by index: ");
Console.Out.Flush();
if (int.TryParse(Console.ReadLine(), out int index) && index < device.AvailableVideoInputOptions.Length && index >= 0)
{
vios = device.AvailableVideoInputOptions[index];
break;
}
}
}

/// You can get a <see cref="Camera"/> from either a string
/// representing the device path, or a <see cref="CameraInfo">.

// Unfortunately, she saw the manager
karen = manager.GetDevice(devicePath);
karen = manager.GetDevice(device, vios);

/// Attach our <see cref="OnNewFrame"/> method to the camera's frame event handler,
/// so that we can process every coming frame the way we want.
Expand Down
30 changes: 12 additions & 18 deletions SeeShark/Device/CameraManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using System;
using System.Runtime.InteropServices;
using DirectShowLib;
using SeeShark.Utils;

namespace SeeShark.Device
{
Expand Down Expand Up @@ -51,24 +51,18 @@ protected override CameraInfo[] EnumerateDevices()
{
// FFmpeg doesn't implement avdevice_list_input_sources() for the DShow input format yet.
// See first SeeShark issue: https://github.com/vignetteapp/SeeShark/issues/1
if (InputFormat == DeviceInputFormat.DShow)
{
var dsDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
var devices = new CameraInfo[dsDevices.Length];
for (int i = 0; i < dsDevices.Length; i++)
{
var dsDevice = dsDevices[i];
devices[i] = new CameraInfo
{
Name = dsDevice.Name,
Path = $"video={dsDevice.Name}",
};
}
return devices;
}
else

// Supported formats won't use the default method to allow better video input options handling.
switch (InputFormat)
{
return base.EnumerateDevices();
case DeviceInputFormat.DShow:
return DShowUtils.EnumerateDevices();
case DeviceInputFormat.V4l2:
CameraInfo[] devices = base.EnumerateDevices();
V4l2Utils.FillDeviceOptions(devices);
return devices;
default:
return base.EnumerateDevices();
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions SeeShark/Device/VideoDeviceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ public class VideoDeviceInfo : IEquatable<VideoDeviceInfo>
/// <summary>
/// Name of the camera. Can be null.
/// </summary>
public string? Name { get; init; }
public string? Name { get; internal set; }
/// <summary>
/// Path of the camera device. It can be anything from a file on the system (on Linux for instance) or a UUID (on Windows for example).
/// </summary>
public string Path { get; init; } = "";
public string Path { get; internal set; } = "";
/// <summary>
/// Available sets of video input options for this device.
/// </summary>
public VideoInputOptions[]? AvailableVideoInputOptions { get; internal set; }

public bool Equals(VideoDeviceInfo? other) => Path == other?.Path;
public override bool Equals(object? obj) => obj is VideoDeviceInfo info && Equals(info);
Expand Down
16 changes: 8 additions & 8 deletions SeeShark/Device/VideoDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ protected virtual TDeviceInfo[] EnumerateDevices()
AVDeviceInfoList* avDeviceInfoList = null;
ffmpeg.avdevice_list_input_sources(AvInputFormat, null, null, &avDeviceInfoList).ThrowExceptionIfError();
int nDevices = avDeviceInfoList->nb_devices;
var avDevices = avDeviceInfoList->devices;
AVDeviceInfo** avDevices = avDeviceInfoList->devices;

var devices = new TDeviceInfo[nDevices];
TDeviceInfo[] devices = new TDeviceInfo[nDevices];
for (int i = 0; i < nDevices; i++)
{
var avDevice = avDevices[i];
var name = new string((sbyte*)avDevice->device_description);
var path = new string((sbyte*)avDevice->device_name);
AVDeviceInfo* avDevice = avDevices[i];
string name = new string((sbyte*)avDevice->device_description);
string path = new string((sbyte*)avDevice->device_name);

if (path == null)
throw new InvalidOperationException($"Device at index {i} doesn't have a path!");
Expand All @@ -122,15 +122,15 @@ protected virtual TDeviceInfo[] EnumerateDevices()
/// </summary>
public void SyncDevices()
{
var newDevices = EnumerateDevices().ToImmutableList();
ImmutableList<TDeviceInfo> newDevices = EnumerateDevices().ToImmutableList();

if (Devices.SequenceEqual(newDevices))
return;

foreach (var device in newDevices.Except(Devices))
foreach (TDeviceInfo device in newDevices.Except(Devices))
OnNewDevice?.Invoke(device);

foreach (var device in Devices.Except(newDevices))
foreach (TDeviceInfo device in Devices.Except(newDevices))
OnLostDevice?.Invoke(device);

Devices = newDevices;
Expand Down
14 changes: 14 additions & 0 deletions SeeShark/Interop/Libc/FileOpenFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

namespace SeeShark.Interop.Libc
{
internal enum FileOpenFlags
{
O_RDONLY = 0x00,
O_RDWR = 0x02,
O_NONBLOCK = 0x800,
O_SYNC = 0x101000
}
}
40 changes: 40 additions & 0 deletions SeeShark/Interop/Libc/Ioctl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;
using System.Runtime.InteropServices;

namespace SeeShark.Interop.Libc
{
internal partial class Ioctl
{
const int ioc_nrbits = 8;
const int ioc_typebits = 8;
const int ioc_sizebits = 14;
// const int ioc_dirbits = 2;

// const int ioc_nrmask = (1 << ioc_nrbits) - 1;
// const int ioc_typemask = (1 << ioc_typebits) - 1;
// const int ioc_sizemask = (1 << ioc_sizebits) - 1;
// const int ioc_dirmask = (1 << ioc_dirbits) - 1;

const int ioc_nrshift = 0;
const int ioc_typeshift = ioc_nrshift + ioc_nrbits;
const int ioc_sizeshift = ioc_typeshift + ioc_typebits;
const int ioc_dirshift = ioc_sizeshift + ioc_sizebits;

const int ioc_none = 0;
const int ioc_write = 1;
const int ioc_read = 2;

internal static int IOC(int dir, int type, int nr, int size)
=> dir << ioc_dirshift | type << ioc_typeshift | nr << ioc_nrshift | size << ioc_sizeshift;

internal static int IO(int type, int nr) => IOC(ioc_none, type, nr, 0);
internal static int IOR(int type, int nr, Type size) => IOC(ioc_read, type, nr, IOC_TYPECHECK(size));
internal static int IOW(int type, int nr, Type size) => IOC(ioc_write, type, nr, IOC_TYPECHECK(size));
internal static int IOWR(int type, int nr, Type size) => IOC(ioc_read | ioc_write, type, nr, IOC_TYPECHECK(size));
internal static int IOC_TYPECHECK(Type t) => Marshal.SizeOf(t);
}
}
44 changes: 44 additions & 0 deletions SeeShark/Interop/Libc/Libc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;
using System.Runtime.InteropServices;

namespace SeeShark.Interop.Libc
{
internal class Libc
{
private const string libc_library = "libc";
private const string explain_library = "explain";

[DllImport(libc_library, SetLastError = true)]
internal static extern int open([MarshalAs(UnmanagedType.LPStr)] string pathname, FileOpenFlags flags);

[DllImport(libc_library)]
internal static extern int close(int fd);

[DllImport(libc_library, SetLastError = true)]
internal static extern int read(int fd, IntPtr buf, int count);

[DllImport(libc_library, SetLastError = true)]
internal static extern int write(int fd, IntPtr buf, int count);

#region ioctl
[DllImport(libc_library, SetLastError = true)]
internal static extern int ioctl(int fd, int request, IntPtr argp);

[DllImport(explain_library, SetLastError = true)]
internal static extern unsafe sbyte* explain_ioctl(int fd, int request, IntPtr argp);

[DllImport(explain_library, SetLastError = true)]
internal static extern unsafe sbyte* explain_errno_ioctl(int errno, int fd, int request, IntPtr argp);
#endregion

[DllImport(libc_library, SetLastError = true)]
internal static extern IntPtr mmap(IntPtr addr, int length, MemoryMappedProtections prot, MemoryMappedFlags flags, int fd, int offset);

[DllImport(libc_library)]
internal static extern int munmap(IntPtr addr, int length);
}
}
16 changes: 16 additions & 0 deletions SeeShark/Interop/Libc/MemoryMappedFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;

namespace SeeShark.Interop.Libc
{
[Flags]
internal enum MemoryMappedFlags
{
MAP_SHARED = 0x01,
MAP_PRIVATE = 0x02,
MAP_FIXED = 0x10
}
}
17 changes: 17 additions & 0 deletions SeeShark/Interop/Libc/MemoryMappedProtections.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;

namespace SeeShark.Interop.Libc
{
[Flags]
internal enum MemoryMappedProtections
{
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
PROT_EXEC = 0x4
}
}
80 changes: 80 additions & 0 deletions SeeShark/Interop/Libc/RawVideoSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;

namespace SeeShark.Interop.Libc
{
/// <summary>
/// videodev2.h Request Definition
/// </summary>
internal static class RawVideoSettings
{
public static readonly int VIDIOC_QUERYCAP = Ioctl.IOR('V', 0, typeof(v4l2_capability));
public static readonly int VIDIOC_ENUM_FMT = Ioctl.IOWR('V', 2, typeof(v4l2_fmtdesc));
public static readonly int VIDIOC_G_FMT = Ioctl.IOWR('V', 4, typeof(v4l2_format));
public static readonly int VIDIOC_S_FMT = Ioctl.IOWR('V', 5, typeof(v4l2_format));
public static readonly int VIDIOC_REQBUFS = Ioctl.IOWR('V', 8, typeof(v4l2_requestbuffers));
public static readonly int VIDIOC_QUERYBUF = Ioctl.IOWR('V', 9, typeof(v4l2_buffer));
public static readonly int VIDIOC_OVERLAY = Ioctl.IOW('V', 14, typeof(int));
public static readonly int VIDIOC_QBUF = Ioctl.IOWR('V', 15, typeof(v4l2_buffer));
public static readonly int VIDIOC_DQBUF = Ioctl.IOWR('V', 17, typeof(v4l2_buffer));
public static readonly int VIDIOC_STREAMON = Ioctl.IOW('V', 18, typeof(int));
public static readonly int VIDIOC_STREAMOFF = Ioctl.IOW('V', 19, typeof(int));
public static readonly int VIDIOC_G_PARM = Ioctl.IOWR('V', 21, typeof(v4l2_streamparm));
public static readonly int VIDIOC_S_PARM = Ioctl.IOWR('V', 22, typeof(v4l2_streamparm));
public static readonly int VIDIOC_G_CTRL = Ioctl.IOWR('V', 27, typeof(v4l2_control));
public static readonly int VIDIOC_S_CTRL = Ioctl.IOWR('V', 28, typeof(v4l2_control));
public static readonly int VIDIOC_QUERYCTRL = Ioctl.IOWR('V', 36, typeof(v4l2_queryctrl));
public static readonly int VIDIOC_G_INPUT = Ioctl.IOR('V', 38, typeof(int));
public static readonly int VIDIOC_S_INPUT = Ioctl.IOWR('V', 39, typeof(int));
public static readonly int VIDIOC_G_OUTPUT = Ioctl.IOR('V', 46, typeof(int));
public static readonly int VIDIOC_S_OUTPUT = Ioctl.IOWR('V', 47, typeof(int));
public static readonly int VIDIOC_CROPCAP = Ioctl.IOWR('V', 58, typeof(v4l2_cropcap));
public static readonly int VIDIOC_G_CROP = Ioctl.IOWR('V', 59, typeof(v4l2_crop));
public static readonly int VIDIOC_S_CROP = Ioctl.IOW('V', 60, typeof(v4l2_crop));
public static readonly int VIDIOC_TRY_FMT = Ioctl.IOWR('V', 64, typeof(v4l2_format));
public static readonly int VIDIOC_G_PRIORITY = Ioctl.IOR('V', 67, typeof(uint));
public static readonly int VIDIOC_S_PRIORITY = Ioctl.IOW('V', 68, typeof(uint));
public static readonly int VIDIOC_ENUM_FRAMESIZES = Ioctl.IOWR('V', 74, typeof(v4l2_frmsizeenum));
public static readonly int VIDIOC_ENUM_FRAMEINTERVALS = Ioctl.IOWR('V', 75, typeof(v4l2_frmivalenum));
public static readonly int VIDIOC_PREPARE_BUF = Ioctl.IOWR('V', 93, typeof(v4l2_buffer));

public static void PrintConstants()
{
Console.WriteLine($" internal enum VideoSettings : int");
Console.WriteLine($" {{");
Console.WriteLine($" {nameof(VIDIOC_QUERYCAP)} = {VIDIOC_QUERYCAP},");
Console.WriteLine($" {nameof(VIDIOC_ENUM_FMT)} = {VIDIOC_ENUM_FMT},");
Console.WriteLine($" {nameof(VIDIOC_G_FMT)} = {VIDIOC_G_FMT},");
Console.WriteLine($" {nameof(VIDIOC_S_FMT)} = {VIDIOC_S_FMT},");
Console.WriteLine($" {nameof(VIDIOC_REQBUFS)} = {VIDIOC_REQBUFS},");
Console.WriteLine($" {nameof(VIDIOC_QUERYBUF)} = {VIDIOC_QUERYBUF},");
Console.WriteLine($" {nameof(VIDIOC_OVERLAY)} = {VIDIOC_OVERLAY},");
Console.WriteLine($" {nameof(VIDIOC_QBUF)} = {VIDIOC_QBUF},");
Console.WriteLine($" {nameof(VIDIOC_DQBUF)} = {VIDIOC_DQBUF},");
Console.WriteLine($" {nameof(VIDIOC_STREAMON)} = {VIDIOC_STREAMON},");
Console.WriteLine($" {nameof(VIDIOC_STREAMOFF)} = {VIDIOC_STREAMOFF},");
Console.WriteLine($" {nameof(VIDIOC_G_PARM)} = {VIDIOC_G_PARM},");
Console.WriteLine($" {nameof(VIDIOC_S_PARM)} = {VIDIOC_S_PARM},");
Console.WriteLine($" {nameof(VIDIOC_G_CTRL)} = {VIDIOC_G_CTRL},");
Console.WriteLine($" {nameof(VIDIOC_S_CTRL)} = {VIDIOC_S_CTRL},");
Console.WriteLine($" {nameof(VIDIOC_QUERYCTRL)} = {VIDIOC_QUERYCTRL},");
Console.WriteLine($" {nameof(VIDIOC_G_INPUT)} = {VIDIOC_G_INPUT},");
Console.WriteLine($" {nameof(VIDIOC_S_INPUT)} = {VIDIOC_S_INPUT},");
Console.WriteLine($" {nameof(VIDIOC_G_OUTPUT)} = {VIDIOC_G_OUTPUT},");
Console.WriteLine($" {nameof(VIDIOC_S_OUTPUT)} = {VIDIOC_S_OUTPUT},");
Console.WriteLine($" {nameof(VIDIOC_CROPCAP)} = {VIDIOC_CROPCAP},");
Console.WriteLine($" {nameof(VIDIOC_G_CROP)} = {VIDIOC_G_CROP},");
Console.WriteLine($" {nameof(VIDIOC_S_CROP)} = {VIDIOC_S_CROP},");
Console.WriteLine($" {nameof(VIDIOC_TRY_FMT)} = {VIDIOC_TRY_FMT},");
Console.WriteLine($" {nameof(VIDIOC_G_PRIORITY)} = {VIDIOC_G_PRIORITY},");
Console.WriteLine($" {nameof(VIDIOC_S_PRIORITY)} = {VIDIOC_S_PRIORITY},");
Console.WriteLine($" {nameof(VIDIOC_ENUM_FRAMESIZES)} = {VIDIOC_ENUM_FRAMESIZES},");
Console.WriteLine($" {nameof(VIDIOC_ENUM_FRAMEINTERVALS)} = {VIDIOC_ENUM_FRAMEINTERVALS},");
Console.WriteLine($" {nameof(VIDIOC_PREPARE_BUF)} = {VIDIOC_PREPARE_BUF},");
Console.WriteLine($" }}");
}
}
}
Loading

0 comments on commit 040cb44

Please sign in to comment.