V4L2应用程序开发(1)

参考资料:韦东山第三期
 
v4l2应用程序开发分为两个部分,数据采集流程和控制流程两个部分
 
数据采集流程:
分为空闲链表和完成链表
 
驱动程序周而复始地做如下事情:
  • 从硬件采集到数据
  • 把"空闲链表"取出buffer,把数据存入buffer
  • 把含有数据的buffer放入"完成链表"
APP也会周而复始地做如下事情:
  • 监测"完成链表",等待它含有buffer
  • 从"完成链表"中取出buffer
  • 处理数据
  • 把buffer放入"空闲链表"
链表操作示意图:
0
摄像头的应用操作流程,如下:
  • open:打开设备节点/dev/videoX
  • ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力,比如
    • 确认它是否是"捕获设备",因为有些节点是输出设备
    • 确认它是否支持mmap操作,还是仅支持read/write操作
  • ioctl VIDIOC_ENUM_FMT:枚举它支持的格式
  • ioctl VIDIOC_S_FMT:在上面枚举出来的格式里,选择一个来设置格式
  • ioctl VIDIOC_REQBUFS:申请buffer,APP可以申请很多个buffer,但是驱动程序不一定能申请到,requery申请
  • ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射
    • 如果申请到了N个buffer,这个ioctl就应该执行N次
    • 执行mmap后,APP就可以直接读写这些buffer
  • ioctl VIDIOC_QBUF:把buffer放入"空闲链表"
    • 如果申请到了N个buffer,这个ioctl就应该执行N次
  • ioctl VIDIOC_STREAMON:启动摄像头
  • 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表"
    • poll/select
    • ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer
    • 处理:前面使用mmap映射了每个buffer的地址,处理时就可以直接使用地址来访问buffer
    • ioclt VIDIOC_QBUF:把buffer放入"空闲链表"
  • ioctl VIDIOC_STREAMOFF:停止摄像头
 
控制流程:
应用程序接口

数据格式的枚举、获得和设置:

1、数据格式

枚举格式的时候,只是返回支持的pixelformat和描述
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;  // 比如从0开始
fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type为"捕获"
ioctl(vd->fd, VIDIOC_ENUM_FMT, &fmtdesc);

// uapi/linux/videodev2.h
 struct v4l2_fmtdesc {
         __u32               index;             /* Format number      */
         __u32               type;              /* enum v4l2_buf_type */
         __u32               flags;
         __u8                description[32];   /* Description string */
         __u32               pixelformat;       /* Format fourcc      */
         __u32               reserved[4];
 };

 enum v4l2_buf_type {
         V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,            // 摄像头一般就为捕获设备
         V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
         V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
         V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
         V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
         V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
         V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
 #if 1
         /* Experimental */
         V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
 #endif
         V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
         V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE  = 10,
         V4L2_BUF_TYPE_SDR_CAPTURE          = 11,
         /* Deprecated, do not use */
         V4L2_BUF_TYPE_PRIVATE              = 0x80,
 };

 

2、获取当前摄像头使用的格式

获取当前格式更加详细的信息,就需要使用VIDIOC_G_FRT
struct v4l2_format currentFormat;
memset(&currentFormat, 0, sizeof(struct v4l2_format));
currentFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(vd->fd, VIDIOC_G_FMT, &currentFormat);

#if 0
struct v4l2_format {
    __u32     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 */
        struct v4l2_sdr_format        sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
        __u8    raw_data[200];                   /* user-defined */
    } fmt;
};

/*
 *    V I D E O   I M A G E   F O R M A T
 */
struct v4l2_pix_format {
    __u32                 width;
    __u32            height;
    __u32            pixelformat;
    __u32            field;        /* enum v4l2_field */
    __u32                bytesperline;    /* for padding, zero if unused */
    __u32                  sizeimage;
    __u32            colorspace;    /* enum v4l2_colorspace */
    __u32            priv;        /* private data, depends on pixelformat */
    __u32            flags;        /* format flags (V4L2_PIX_FMT_FLAG_*) */
    __u32            ycbcr_enc;    /* enum v4l2_ycbcr_encoding */
    __u32            quantization;    /* enum v4l2_quantization */
    __u32            xfer_func;    /* enum v4l2_xfer_func */
};
#endif

 

3、设置当前格式

使用VIDIOC_S_FMT
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(struct v4l2_format));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1024;
fmt.fmt.pix.height = 768;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
int ret = ioctl(vd->fd, VIDIOC_S_FMT, &fmt);
 

选择输入源:

int value;
ioctl(h->fd,VIDIOC_G_INPUT,&value);  // 读到的value从0开始, 0表示第1个input源

int value = 0;  // 0表示第1个input源
ioctl(h->fd,VIDIOC_S_INPUT,&value)
 

其他参数:

如果每一参数都提供一系列的ioctl cmd,那使用起来很不方便。
对于这些参数,APP使用对应ID来选中它,然后使用VIDIOC_QUERYCTRL、VIDIOC_G_CTRL、VIDIOC_S_CTRL来操作它。
不同参数的ID值不同。以亮度Brightness为例,有如下调用方法:

1、查询

struct v4l2_queryctrl   qctrl;
memset(&qctrl, 0, sizeof(qctrl));
qctrl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
ioctl(fd, VIDIOC_QUERYCTRL, &qctrl);

/*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
struct v4l2_queryctrl {
    __u32             id;
    __u32             type;    /* enum v4l2_ctrl_type */
    __u8             name[32];    /* Whatever */
    __s32             minimum;    /* Note signedness */
    __s32             maximum;
    __s32             step;
    __s32             default_value;
    __u32                flags;
    __u32             reserved[2];
};

 

2、获得当前值

struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
ioctl(h->fd, VIDIOC_G_CTRL, &c);


/*
 *    C O N T R O L S
 */
struct v4l2_control {
    __u32             id;
    __s32             value;
};

 

3、设置

struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;
c.value = 99;
ioctl(h->fd, VIDIOC_S_CTRL, &c);
 
 

理解接口:

0
SU:select unit,选择输入源
PU:processiong unit,用于调整亮度、对比度、色度等
EU:encoding unit,对采集到的数据进行个性化处理的功能
0
ID会传递给PU
app->驱动->(id, val)->硬件
 

操作方法:

使用ioctl操作设备节点/dev/video0时,不同的ioctl操作可能是video control接口,或者video streaming接口
跟视频流相关的操作,比如VIDIOC_ENUM_FMT,VIDIOC_G_FMT,VIDIOC_S_FMT,VIDIOC_STREAMON,VIDIOC_STREAMOFF,是操作video streaming的接口
 
其他的接口,大多是video control接口
从驱动和硬件角度来看,要操作video control接口,需要指明:
1、entity:要操作的是那个terminal或者unit,比如PU
2、control selector:要操作entity里面的那个控制项,比如亮度
3、控制项里面的哪些位:比如camera terminal里的CT_PANTILT_RELATIVE_CONTROL控制项对应的32位数据,其中前16位对应PAN控制(左右转动),后16位对应TILE控制(上下转动)
但是应用程序不关心这些,使用一个ID来指定entity、control selector、哪些位:
struct v4l2_control c;
c.id = V4L2_CID_BRIGHTNESS;    // V4L2_CID_BASE+0;
c.value = 99;
ioctl(h->fd, VIDIOC_S_CTRL, &c);

/*
 *    C O N T R O L S
 */
struct v4l2_control {
    __u32             id;
    __s32             value;
};

 

支持V4L2_CAP_STREAMING就可以用mmap函数,不支持的话就只能用V4L2_CAP_READWRITE了

 

 
posted @ 2024-03-26 17:17  lethe1203  阅读(55)  评论(0编辑  收藏  举报