3

マネージド アプリケーションに Video4Linux を統合しようとしています。実際、必要なすべての構造と相対ioctlを宣言しました。この質問では、次の 2 つを提示ioctlSetFormatますGetFormat。前者は(私が実際に使用している他のダースと同様に)うまく機能していますが、後者はメモリ動作が悪いです。

ioctl は実際にはGetFormat実行されていますが、アプリケーションが ioctl パラメーター (デバッガーまたはアプリケーション自体) にアクセスするとすぐに、次のスタックで常にクラッシュします。

System.NullReferenceException: ...
at System.String.mempy4(...) in <filename unknown>:0
at System.String.memcpy(...) in <filename unknown>:0
at Derm.Platform.Video4Linux.ControlGetFormat(...) in <...>
...

p/invoking ioctlについて調査しましたが、アプリケーションがクラッシュする理由がわかりません。構造メモリ レイアウトが原因であると思われますが、機能しないのに機能する理由SetFormatを説明できGetFormatません (まったく同じパラメーターを使用するため)。

ioctl構造体を受け取る/返すルーチンを P/Invoke する必要があり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 */
};

v4l2_format次のフォームを使用して構造を変換しました

[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;
}

最後に、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);
4

1 に答える 1

4

この問題は、構造体のサイズを実際のサイズ (私の場合は204 ) に強制し、配列フィールドを削除することで解決されました ( David HeffernanRawDataの提案による)。

それはそう:

[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi, Size = 204)]
public struct Format
{
    public Format(BufferType bufferType)
    {
        Type = bufferType;
        Pixmap = new PixmapFormat();
    }

    public Format(BufferType bufferType, PixmapFormat pixmapFormat)
    {
        Type = bufferType;
        Pixmap = pixmapFormat;
    }

    [FieldOffset(0)]
    public BufferType Type;

    [FieldOffset(4)]
    public PixmapFormat Pixmap;
}

もちろん、Marshal.SizeOf(typeof(Format)) == 204

于 2012-06-11T20:29:14.227 に答える