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

Camera config management #30

Merged
merged 22 commits into from
Oct 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2b32b47
Explicit types and better documentation
Speykious Apr 23, 2022
67312c7
Add `AvailableVideoInputOptions` field
Speykious Apr 23, 2022
19090c5
Code to get available video input options on dshow
Speykious Apr 23, 2022
69e79d9
Forgor one `var`
Speykious Apr 23, 2022
494aab3
Actually not just one
Speykious Apr 23, 2022
03ffca9
Move dshow enumerate device logic to `DShowUtils`
Speykious Apr 23, 2022
93ac939
Move `DShowUtils` up
Speykious Apr 23, 2022
82a28b2
Add V4l2 Interop
Speykious Jun 12, 2022
5ab1aaf
Make `getAvailableOptions` private
Speykious Jul 18, 2022
1cf4736
Change order of VideoSettings according to RawVideoSettings
Speykious Jul 19, 2022
31228bf
Add more video settings
Speykious Jul 19, 2022
03408f9
Add some videodev2 structs
Speykious Jul 19, 2022
bfac235
Fix potentially bad `IntPtr` comparison
Speykious Jul 19, 2022
3e13cba
Fill device options on Linux
Speykious Jul 19, 2022
f21a1ab
Get number from direct C call
Speykious Jul 22, 2022
076edfd
Finish `VideoInputOptions` enumeration for Linux
Speykious Jul 22, 2022
efa43b6
Enumerate video input options in ASCII example
Speykious Jul 22, 2022
be3e70f
Fix wrong key for DShow `vcodec` option
Speykious Sep 26, 2022
c86af38
Enhance VideoInputOption on Windows: get VCodec (h264 and mjpeg) or I…
ChristopheI Sep 27, 2022
8d31400
Formatting and print to stderr upon unhandled media type
Speykious Oct 28, 2022
1a7b3be
Use boolean to simplify the API
Speykious Oct 29, 2022
a5fe069
Fix new case of input format name inconsistency
Speykious Oct 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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