一.查询设备能力VIDIOC_QUERYCAP
struct v4l2_capability cap;
ioctl(fd, VIDIOC_QUERYCAP, &cap)
struct v4l2_capability 结构体描述了视频采集设备的 driver 信息。
struct v4l2_capability
{
__u8 driver[16]; // 驱动名字
__u8 card[32]; // 设备名字
__u8 bus_info[32]; // 设备在系统中的位置
__u32 version; // 驱动版本号
__u32 capabilities; // 设备支持的操作
__u32 reserved[4]; // 保留字段
};
video注册的时候fops是v4l2_fops,所以会调用到v4l2_fops.unlocked_ioctl 也就是 v4l2_ioctl
v4l2_ioctl中 调用到 vdev->fops->unlocked_ioctl,vdev->fops对于vivi驱动的vivi_fops,所以unlocked_ioctl就是video_ioctl2
其中VIDIOC_QUERYCAP对应 v4l_querycap。
static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
struct file *file, void *fh, void *arg)
{
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
struct video_device *vfd = video_devdata(file);
int ret;
cap->version = LINUX_VERSION_CODE;
cap->device_caps = vfd->device_caps;
cap->capabilities = vfd->device_caps | V4L2_CAP_DEVICE_CAPS;
/*
* 对应vivi驱动的 vidioc_querycap
*/
ret = ops->vidioc_querycap(file, fh, cap);
cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
/*
* Drivers MUST fill in device_caps, so check for this and
* warn if it was forgotten.
*/
WARN(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) ||
!cap->device_caps, "Bad caps for driver %s, %x %x",
cap->driver, cap->capabilities, cap->device_caps);
cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT;
return ret;
}
vivi驱动vidioc_querycap源码如下
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct vivi_dev *dev = video_drvdata(file);
strcpy(cap->driver, "vivi");
strcpy(cap->card, "vivi");
/*
* 这里加深一下理解
* dev->v4l2_dev.name 是什么?
* vivi驱动中有这样一句代码
* snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
"%s-%03d", VIVI_MODULE_NAME, inst);
* "vivi-000" "vivi-001" 这种名字
*/
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
cap->version = VIVI_VERSION;
/*
* 驱动表示支持capture,streaming及read write
*/
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
V4L2_CAP_READWRITE;
return 0;
}
从capabilities的返回值能知道这个设备所支持的功能。
判断设备是否支持拍照功能:
if (ioctl(fd_v4l, VIDIOC_QUERYCAP, &cap) == 0)
{
printf("cap=0x%x\n", cap.capabilities);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("%s not support v4l2 capture device.\n", dev_name);
return -1;
}
}
else
{
close(fd_v4l);
return -1;
}
二.设置输入设备VIDIOC_S_INPUT
一个video设备节点可能对应多个视频源,所以需要指定输入的设备。
input.index = index; //指定输入设备
if (ioctl(fd, VIDIOC_S_INPUT, &input) < 0)
设备输入的设备为设备0:
struct v4l2_input inp;
inp.index = 0;
if (ioctl(fd_v4l, VIDIOC_S_INPUT, &inp) < 0)
{
printf("VIDIOC_S_INPUT error.!");
return -1;
}
对应vivi的vidioc_s_input
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct vivi_dev *dev = video_drvdata(file);
if (i >= NUM_INPUTS)
return -EINVAL;
dev->input = i;
precalculate_bars(dev);
precalculate_line(dev);
return 0;
}
三.获取设置支持的视频格式VIDIOC_G_FMT
struct v4l2_format {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
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;
};
其中
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_PRIVATE = 0x80,
};
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 */
};
通过VIDIOC_G_FMT获取输入设备的宽高:
if (ioctl(fd_v4l, VIDIOC_G_FMT, &fmt) < 0)
{
printf("get format failed\n");
goto fail;
}
camera_width = fmt.fmt.pix_mp.width;
camera_height = fmt.fmt.pix_mp.height;
四.设置视频格式VIDIOC_S_FMT
memset(&v4l2_fmt, 0, sizeof(struct v4l2_format));
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = width; //宽度
v4l2_fmt.fmt.pix.height = height; //高度
v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //像素格式
v4l2_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(fd, VIDIOC_S_FMT, &v4l2_fmt) < 0)
这里对应vivi驱动的vidioc_s_fmt_vid_cap
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_dev *dev = video_drvdata(file);
struct vb2_queue *q = &dev->vb_vidq;
int ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
/*
* vb2_is_streaming 判断vb2_is_streaming的值,后面的分析我们会看到这个值
* 的变化,这里认为值为0,if不满足
*/
if (vb2_is_streaming(q)) {
dprintk(dev, 1, "%s device busy\n", __func__);
return -EBUSY;
}
dev->fmt = get_format(f);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
dev->field = f->fmt.pix.field;
return 0;
}
五.获取Stream信息VIDIOC_G_PARM
struct v4l2_streamparm Stream_Parm;
memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));
Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
io_rel = ioctl(Handle, VIDIOC_G_PARM, &Stream_Parm);
struct v4l2_streamparm
{
enum v4l2_buf_type type;
union
{
struct v4l2_captureparm capture;
struct v4l2_outputparm output;
__u8 raw_data[200];
} parm;
};
因为是Camera, 所以使用capture。
struct v4l2_captureparm
{
__u32 capability; // 是否可以被timeperframe控制帧数。可以则:V4L2_CAP_TIMEPERFRAME
__u32 capturemode; // 是否为高清模式。如果是则设置为:V4L2_MODE_HIGHQUALITY。 高清模式会牺牲其它信息。通常设置为0。
struct v4l2_fract timeperframe; //帧数。
__u32 extendedmode; //定制的。如果不支持,设置为0
__u32 readbuffers;
__u32 reserved[4];
};
struct v4l2_fract {
__u32 numerator; // 分子。 例:1
__u32 denominator; // 分母。 例:30
};
六.设置Stream信息VIDIOC_S_PARM
struct v4l2_streamparm streamparm = {0};
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_G_PARM, &streamparm);
if (V4L2_CAP_TIMEPERFRAME & streamparm.parm.capture.capability)
{
streamparm.parm.capture.timeperframe.numerator = 1;
streamparm.parm.capture.timeperframe.denominator = 30;//30fps
if (0 > ioctl(fd, VIDIOC_S_PARM, &streamparm))
{
printf("ioctl error: VIDIOC_S_PARM");
return -1;
}
}