I'm trying to integrate Video4Linux in my managed application. Indeed I've declared all required structures and relative ioctl. In this question I present two ioctl
: SetFormat
and GetFormat
; while the former is working well (like the other dozen I actually use), the latter is returning me bad memory behavior.
The GetFormat
ioctl is actually performing, but as soon the application is accessing to the ioctl parameter (either debugger or my application itself), it always crashes with following stack:
System.NullReferenceException: ...
at System.String.mempy4(...) in <filename unknown>:0
at System.String.memcpy(...) in <filename unknown>:0
at Derm.Platform.Video4Linux.ControlGetFormat(...) in <...>
...
I had some investigation on p/invoking ioctl, and again, I cannot understand why my application is crashing. I suspect it's becuase structure memory layout, but I cannot explain why SetFormat
is working while GetFormat
don't (since they use the very same parameter).
I have to P/Invoke a ioctl
routine that receive/return the structure v4l2_format
:
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
__u8 raw_data[200]; /* user-defined */
} fmt;
};
struct v4l2_pix_format {
__u32 width;
__u32 height;
__u32 pixelformat;
enum v4l2_field field;
__u32 bytesperline; /* for padding, zero if unused */
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv; /* private data, depends on pixelformat */
};
I've traduced the structure v4l2_format
using the following form
[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi)]
public struct Format
{
public Format(BufferType bufferType)
{
Type = bufferType;
Pixmap = new PixmapFormat();
RawData = new byte[RawDataLength];
}
public Format(BufferType bufferType, PixmapFormat pixmapFormat)
{
Type = bufferType;
Pixmap = pixmapFormat;
RawData = new byte[RawDataLength];
}
[FieldOffset(0)]
public BufferType Type;
[FieldOffset(4)]
public PixmapFormat Pixmap;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst=RawDataLength)]
public byte[] RawData;
public const int RawDataLength = 200;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PixmapFormat
{
public PixmapFormat(uint width, uint height, PixelFormatCode pixelFormat)
{
Width = width;
Height = height;
PixelFormat = pixelFormat;
Field = Video4Linux.BufferField.Any;
BytesPerLine = 0;
SizeImage = 0;
Colorspace = Video4Linux.PixelColorspace.None;
Private = 0;
}
public UInt32 Width;
public UInt32 Height;
public PixelFormatCode PixelFormat;
public BufferField Field;
public UInt32 BytesPerLine;
public UInt32 SizeImage;
public PixelColorspace Colorspace;
public UInt32 Private;
}
And finally, here is the methods calling ioctl:
public static void ControlGetFormat(IntPtr fd, BufferType pixmapType, out PixmapFormat pixmapFormat)
{
if (fd == IntPtr.Zero)
throw new ArgumentException("invalid file descriptor", "fd");
Format format = new Format(pixmapType);
int result = IoCtrlGetFormat(fd, GetFormat.ControlCode, ref format);
if (result < 0)
ThrowExceptionOnError();
pixmapFormat = format.Pixmap;
}
private static readonly IoCtrlRequest GetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 4, typeof(Format));
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private static extern int IoCtrlGetFormat(IntPtr fd, Int32 code, ref Format argument);
public static void ControlSetFormat(IntPtr fd, BufferType pixmapType, ref PixmapFormat pixmapFormat)
{
if (fd == IntPtr.Zero)
throw new ArgumentException("invalid file descriptor", "fd");
PixmapFormat pixmapFormatCopy = pixmapFormat;
Format format = new Format(pixmapType, pixmapFormatCopy);
int result = IoCtrlSetFormat(fd, SetFormat.ControlCode, ref format);
if (result < 0)
ThrowExceptionOnError();
pixmapFormat = format.Pixmap;
}
private static readonly IoCtrlRequest SetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 5, typeof(Format));
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private static extern int IoCtrlSetFormat(IntPtr fd, Int32 code, ref Format argument);