1 V4L2简介
video4linux2(V4L2)是Linux内核中关于视频设备的内核驱动,它为Linux中视频设备访问提供了通用接口,在Linux系统中,V4L2驱动的Video设备节点路径通常/dev/video/中的videoX
V4L2驱动对用户空间提供字符设备,主设备号为81,对于视频设备,其次设备号为0-63。除此之外,次设备号为64-127的Radio设备,次设备号为192-223的是Teletext设备,次设备号为224-255的是VBI设备
V4L2驱动的Video设备在用户空间通过各种ioctl调用进行控制,并且可以使用mmap进行内存映射
1.1 V4L2驱动主要使用的ioctl
命令值如下所示:
1---------------------------------------------------------------------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) /*查询能力*/ #define VIDIO_G_FMT _IOWR('V', 4, struct v4l2_format) /*获得格式*/ #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) /*设置格式*/ #define VIDIOC_REQBUFS _IOWR('V', 8, strut v4l2_requestbuffers) /*申请内存*/ #define VIDIOC_G_FBUF _IOW('V', 10, struct v4l2_framebuffer) /*获得Framebuffer*/ #define VIDIOC_S_BUF _IOW('V', 11, struct v4l2_framebuffer) /*设置Framebuffer*/ #define VIDIOC_OVERLAY _IOW('V', 14, int) /*设置Overlay*/ #define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) /*将内存加入队列*/ #define VIDIOC_DQBUF _IOWR('V', 17, strut v4l2_buffer) /*从队列取出内存*/ #define VIDIOC_STREAMON _IOW('V', 18, int) /*开始流*/ #define VIDIOC_STREAMOFF _IOW('V', 19, int) /*停止流*/ #define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control) /*得到控制*/ #define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control) /*设置控制*/ ---------------------------------------------------------------------
1.2 重要结构
头文件 include/linux/videodev2.h
include/media/v4l2-dev.h
V4L2驱动核心实现文件:driver/media/video/v4l2-dev.c
v4l2-dev.h中定义的video_device是V4L2驱动程序的核心数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46struct video_device { const struct v4l2_file_operations *fops; struct cdev *cdev;//字符设备 struct device *parent;//父设备 struct v4l2_device *v4l2_dev;//父v4l2_device char name[32];//名称 int vfl_type;//类型 int minor;//次设备号 /*释放回调*/ void (*release)(struct video_device *vdev); /*ioctl回调*/ const struct v4l2_ioctl_ops *ioctl_ops; } 常用的结构 参见/include/linux/videodev2.h 1)设备能力结构 struct v4l2_capability { __u8 driver[16];//驱动名 __u8 card[32];//例如Hauppauge winTV __u8 bus_info[32];//PCI总线信息 __u32 version;//内核版本 __u32 capabilities;//设备能力 __u32 reserved[4]; }; 2)数据格式结构 struct v4l2_format { enum v4l2_buf_type type;//本结构的数据类型 }; 3)像素格式结构 struct v4l2_pix_format { __u32 width;//宽度 __u32 height;//高度 } 4)请求缓冲 struct v4l2_requestbuffers { __u32 count;//缓存数量 enum v4l2_buf_type type;//数据流类型 } 5)数据流类型包括V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR enum v4l2_memory{ };
2 V4L2驱动注册
2.1 video_register_device
video4linux2驱动程序的注册drivers/media/video
video_register_device函数用来注册一个v4l驱动程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44int video_register_device(struct video_device *vdev, int type, int nr) { return __video_register_device(vdev, type, nr, 1); } 其中参数type支持的类型如下 #define VFL_TYPE_GRABBER 0//视频 #define VFL_TYPE_VBI 1//从视频消隐的时间取得信息的设备 #define VFL_TYPE_RADIO 2 //广播 #define VFL_TYPE_VTX 3//视传设备 #define VFL_TYPE_MAX 4//最大值 ----------------->返回调用 __video_register_device() __video_register_device 函数先检查设备类型,接下来 寻找一个可用的子设备号,最后注册相应的字符设备 static int __video_register_device(struct video_device *vdev, int type, int nr, int warn_if_nr_in_use) { switch (type) { case VFL_TYPE_GRABBER: minor_offset = 0; minor_cnt = 64; break; case VFL_TYPE_RADIO: minor_offset = 64; minor_cnt = 64; break; case VFL_TYPE_VTX: minor_offset = 192; minor_cnt = 32; break; case VFL_TYPE_VBI: minor_offset = 224; minor_cnt = 32; break; nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); } nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); vdev->cdev->ops = &v4l2_fops; //注册字符设备 ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); ret = device_register(&vdev->dev); //注册完毕设备信息存储在video_device数组中 mutex_lock(&videodev_lock); video_device[vdev->minor] = vdev; mutex_unlock(&videodev_lock); }
2.2 v4l2_fops接口
v4l2_fops为video4linux2设备提供了统一的应用层接口,v4l2_fops定义如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18static const struct file_operations v4l2_fops = { .owner = THIS_MODULE, .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_ioctl, .release = v4l2_release, .poll = v4l2_poll, .llseek = no_llseek, }; v4l2_fops中的成员函数最终要调用struct video_device->fops中相应的成员 struct video_device->fops是具体video4linux2摄像头驱动程序必须实现的接口 static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { return vdev->fops->read(filp, buf, sz, off); }
2.3 /drivers/media/video/samsung/fimc/s3c_fimc_core.c
驱动探测函数s3c_fimc_probe定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26static int s3c_fimc_probe(struct platform_device *dev) { ctrl = s3c_fimc_register_controller(pdev); clk_enable(ctrl->clock);//使能时钟 //注册V4L2驱动 ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id); } s3c_fimc_register_contoller函数主要用来分配资源与申请中断 static struct s3c_fimc_control *s3c_fimc_register_controller(struct platform_device *pdev) { ctrl->vd = &s3c_fimc_video_device[id]; //申请中断 ctrl->irq = platform_get_irq(pdev, 0); if(request_irq(ctrl->irq, s3c_fimc_irq, IRQF_DISABLED, ctrl->name, ctrl)) }; struct video_device s3c_fimc_video_device[S3C_FIMC_MAX_CTRLS] = { [0] = { .vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES, .fops = &s3c_fimc_fops, .ioctl_ops = &s3c_fimc_v4l2_ops, .release = s3c_fimc_vdev_release, .name = "sc3_video0", }, } s3c_fimc_v4l2_ops,是在drivers/media/video/samsung/fimc中实现的v4l2_ioctl_ops,在用户空间进行ioctl等调用时,要调用到具体实现的各个函数指针
3 V4L2 操作
3.1 s3c_fimc_open
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24static int s3c_fimc_open(struct file *filp) { struct s3c_fimc_control *ctrl; int id, ret; id =0; ctrl = &s3c_fimc.ctrl[id]; mutex_lock(&ctrl->lock); if (atomic_read(&ctrl->in_use)) { ret = -EBUSY; goto resource_busy; } else { atomic_inc(&ctrl->in_use); s3c_fimc_reset(ctrl); filp->private_data = ctrl; } mutex_unlock(&ctrl->lock); return 0; resource_busy: mutex_unlock(&ctrl->lock); return ret; } 用户空间 打开设备文件 fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
3.2 获取设备的capability,查看设备有什么功能
1)结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48struct v4l2_capability cap; ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); /include/linux/videodev2.h struct v4l2_capability { __u8 driver[16]; /* i.e. "bttv" */ __u8 card[32]; /* i.e. "Hauppauge WinTV" */ __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ __u32 version; /* should use KERNEL_VERSION() */ __u32 capabilities; /* Device capabilities */ __u32 reserved[4]; }; 驱动实现 static int s3c_fimc_v4l2_querycap(struct file *filp, void *fh, struct v4l2_capability *cap) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; strcpy(cap->driver, "Samsung FIMC Driver"); strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card)); sprintf(cap->bus_info, "FIMC AHB-bus"); cap->version = 0; cap->capabilities = (V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING); return 0; } 应用层调用 static int video_capability(int fd) { int ret = 0; /***********get the device capability********/ struct v4l2_capability cap; ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { perror("VIDIOC_QUERYCAP failed "); return ret; } printf("n****Capability informations****n"); printf("driver: %sn", cap.driver); if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) printf("Capture capability is supportedn"); if (cap.capabilities & V4L2_CAP_STREAMING) printf("Streaming capability is supportedn"); if (cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) printf("Overlay capability is supportedn"); return 0; }
3.3 选择视频输入,一个视频设备可以有多个视频输入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121结构体 struct v4l2_input input; int index; 得到INPUT ret = ioctl(fd, VIDIOC_G_INPUT, &index); input.index = index; 列举INPUT ret = ioctl(fd, VIDIOC_ENUMINPUT, &input); 设置INPUT ret = ioctl(fd, VIDIOC_S_INPUT, &index); struct v4l2_input { __u32 index; /* Which input */ __u8 name[32]; /* Label */ __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ __u32 tuner; /* Associated tuner */ v4l2_std_id std; __u32 status; __u32 capabilities; __u32 reserved[3]; }; Ioctl: VIDIOC_S_INPUT This IOCTL takes pointer to integer containing index of the input which has to be set. Application will provide the index number as an argument. 0 - Composite input, 1 - S-Video input. 驱动 static int s3c_fimc_v4l2_s_input(struct file *filp, void *fh, unsigned int i) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; if (i >= S3C_FIMC_MAX_INPUT_TYPES) return -EINVAL; ctrl->v4l2.input = &s3c_fimc_input_types[i]; if (s3c_fimc_input_types[i].type == V4L2_INPUT_TYPE_CAMERA) ctrl->in_type = PATH_IN_ITU_CAMERA; else ctrl->in_type = PATH_IN_DMA; return 0; } static struct v4l2_input s3c_fimc_input_types[] = { { .index = 0, .name = "External Camera Input", .type = V4L2_INPUT_TYPE_CAMERA, .audioset = 1, .tuner = 0, .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, }, { .index = 1, .name = "Memory Input", .type = V4L2_INPUT_TYPE_MEMORY, .audioset = 2, .tuner = 0, .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, } }; static int s3c_fimc_v4l2_enum_input(struct file *filp, void *fh, struct v4l2_input *i) { if (i->index >= S3C_FIMC_MAX_INPUT_TYPES) return -EINVAL; memcpy(i, &s3c_fimc_input_types[i->index], sizeof(struct v4l2_input)); return 0; } 应用 static int video_input(int fd) { /***********get and set the VIDIO INPUT********/ int ret = 0; struct v4l2_input input;//视频输入信息,对应命令VIDIOC_ENUMINPUT int index; index = 0; //0 - Composite input, 1 - S-Video input. ret = ioctl (fd, VIDIOC_S_INPUT, &index); if (ret < 0) { perror ("VIDIOC_S_INPUT"); return ret; } input.index = index; ret = ioctl (fd, VIDIOC_ENUMINPUT, &input); if (ret < 0){ perror ("VIDIOC_ENUMINPUT"); return ret; } printf("n****input informations****n"); printf("name of the input = %sn", input.name); return 0; }
3.4 遍历所有视频格式,查询驱动所支持的格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129结构 struct v4l2_fmtdes fmtdes; ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdes); struct v4l2_fmtdesc { __u32 index; /* Format number */ enum v4l2_buf_type type; /* buffer type */ __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ __u32 reserved[4]; }; 驱动 static int s3c_fimc_v4l2_enum_fmt_vid_cap(struct file *filp, void *fh, struct v4l2_fmtdesc *f) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; int index = f->index; if (index >= S3C_FIMC_MAX_CAPTURE_FORMATS) return -EINVAL; memset(f, 0, sizeof(*f)); memcpy(f, ctrl->v4l2.fmtdesc + index, sizeof(*f)); return 0; } #define S3C_FIMC_MAX_CAPTURE_FORMATS ARRAY_SIZE(s3c_fimc_capture_formats) const static struct v4l2_fmtdesc s3c_fimc_capture_formats[] = { { .index = 0, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .flags = FORMAT_FLAGS_PLANAR, .description = "4:2:0, planar, Y-Cb-Cr", .pixelformat = V4L2_PIX_FMT_YUV420, }, { .index = 1, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .flags = FORMAT_FLAGS_PLANAR, .description = "4:2:2, planar, Y-Cb-Cr", .pixelformat = V4L2_PIX_FMT_YUV422P, }, { .index = 2, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .flags = FORMAT_FLAGS_PACKED, .description = "4:2:2, packed, YCBYCR", .pixelformat = V4L2_PIX_FMT_YUYV, }, { .index = 3, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .flags = FORMAT_FLAGS_PACKED, .description = "4:2:2, packed, CBYCRY", .pixelformat = V4L2_PIX_FMT_UYVY, } }; const static struct v4l2_fmtdesc s3c_fimc_overlay_formats[] = { { .index = 0, .type = V4L2_BUF_TYPE_VIDEO_OVERLAY, .flags = FORMAT_FLAGS_PACKED, .description = "16 bpp RGB, le", .pixelformat = V4L2_PIX_FMT_RGB565, }, { .index = 1, .type = V4L2_BUF_TYPE_VIDEO_OVERLAY, .flags = FORMAT_FLAGS_PACKED, .description = "24 bpp RGB, le", .pixelformat = V4L2_PIX_FMT_RGB24, }, }; 应用层 static int video_fmtdesc(int fd) { /***********Format Enumeration************/ int ret = 0; struct v4l2_fmtdesc fmtdes; CLEAR(fmtdes); fmtdes.index = 0; fmtdes.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("n**********vidioc enumeration stream format informations:****n"); while (1) { ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmtdes); if (ret < 0) break; printf("{ pixelformat = %c%c%c%c, description = %s }n", (fmtdes.pixelformat & 0xFF), (fmtdes.pixelformat >> 8) & 0xFF, (fmtdes.pixelformat >> 16) & 0xFF, (fmtdes.pixelformat >> 24) & 0xFF, fmtdes.description); if (fmtdes.type == V4L2_BUF_TYPE_VIDEO_CAPTURE) printf("video capture type:n"); if (fmtdes.pixelformat == V4L2_PIX_FMT_YUYV) printf("V4L2_PIX_FMT_YUYVn"); fmtdes.index++; } return 0; }
3.5 设置视频捕获格式(重要)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151结构体 帧格式包括宽度和高度 struct v4l2_format fmt; ret = ioctl(fd, VIDIOC_S_FMT, &fmt); struct v4l2_format { enum v4l2_buf_type type;//数据流类型,必须是V4L2_BUF_TYPE_VIDEO_CAPTURE 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; }; struct v4l2_pix_format { __u32 pixelformat;//视频数据存储类型,例如是YUV4:2:2还是RGB } 驱动 static int s3c_fimc_v4l2_s_fmt_vid_cap(struct file *filp, void *fh, struct v4l2_format *f) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; ctrl->v4l2.frmbuf.fmt = f->fmt.pix; if (f->fmt.pix.priv == V4L2_FMT_IN) s3c_fimc_set_input_frame(ctrl, &f->fmt.pix); else s3c_fimc_set_output_frame(ctrl, &f->fmt.pix); return 0; } int s3c_fimc_set_input_frame(struct s3c_fimc_control *ctrl, struct v4l2_pix_format *fmt) { s3c_fimc_set_input_format(ctrl, fmt); return 0; } static void s3c_fimc_set_input_format(struct s3c_fimc_control *ctrl, struct v4l2_pix_format *fmt) { struct s3c_fimc_in_frame *frame = &ctrl->in_frame; frame->width = fmt->width; frame->height = fmt->height; switch (fmt->pixelformat) { case V4L2_PIX_FMT_RGB565: frame->format = FORMAT_RGB565; frame->planes = 1; break; case V4L2_PIX_FMT_RGB24: frame->format = FORMAT_RGB888; frame->planes = 1; break; case V4L2_PIX_FMT_NV12: frame->format = FORMAT_YCBCR420; frame->planes = 2; frame->order_2p = LSB_CBCR; break; case V4L2_PIX_FMT_NV21: frame->format = FORMAT_YCBCR420; frame->planes = 2; frame->order_2p = LSB_CRCB; break; case V4L2_PIX_FMT_NV12X: frame->format = FORMAT_YCBCR420; frame->planes = 2; frame->order_2p = MSB_CBCR; break; case V4L2_PIX_FMT_NV21X: frame->format = FORMAT_YCBCR420; frame->planes = 2; frame->order_2p = MSB_CRCB; break; case V4L2_PIX_FMT_YUV420: frame->format = FORMAT_YCBCR420; frame->planes = 3; break; case V4L2_PIX_FMT_YUYV: frame->format = FORMAT_YCBCR422; frame->planes = 1; frame->order_1p = IN_ORDER422_YCBYCR; break; case V4L2_PIX_FMT_YVYU: frame->format = FORMAT_YCBCR422; frame->planes = 1; frame->order_1p = IN_ORDER422_YCRYCB; break; case V4L2_PIX_FMT_UYVY: frame->format = FORMAT_YCBCR422; frame->planes = 1; frame->order_1p = IN_ORDER422_CBYCRY; break; case V4L2_PIX_FMT_VYUY: frame->format = FORMAT_YCBCR422; frame->planes = 1; frame->order_1p = IN_ORDER422_CRYCBY; break; case V4L2_PIX_FMT_NV16: frame->format = FORMAT_YCBCR422; frame->planes = 2; frame->order_1p = LSB_CBCR; break; case V4L2_PIX_FMT_NV61: frame->format = FORMAT_YCBCR422; frame->planes = 2; frame->order_1p = LSB_CRCB; break; case V4L2_PIX_FMT_NV16X: frame->format = FORMAT_YCBCR422; frame->planes = 2; frame->order_1p = MSB_CBCR; break; case V4L2_PIX_FMT_NV61X: frame->format = FORMAT_YCBCR422; frame->planes = 2; frame->order_1p = MSB_CRCB; break; case V4L2_PIX_FMT_YUV422P: frame->format = FORMAT_YCBCR422; frame->planes = 3; break; } } 应用层 static int video_setfmt(int fd) { /***********set Stream data format********/ int ret = 0; struct v4l2_format fmt; CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = 640; fmt.fmt.pix.height = 480; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//for PAL fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; ret = ioctl(fd, VIDIOC_S_FMT, &fmt); if (ret < 0) { perror("VIDIOC_S_FMT"); return ret; } return 0; }
3.6 视频格式查询
在v4l2中,有两种查询视频格式的方法,一个是遍历所有视频格式的
一个是查询出一种格式的
/*查询出一种格式*/
ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
/*遍历所有视频格式,查询驱动所支持的格式*/
VIDIOC_ENUM_FMT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32驱动 static int s3c_fimc_v4l2_g_fmt_vid_cap(struct file *filp, void *fh, struct v4l2_format *f) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; int size = sizeof(struct v4l2_pix_format); memset(&f->fmt.pix, 0, size); memcpy(&f->fmt.pix, &(ctrl->v4l2.frmbuf.fmt), size); return 0; } 应用 static int video_getfmt(int fd) { /***********get Stream data format********/ int ret= 0; struct v4l2_format fmt; CLEAR(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_G_FMT, &fmt); if (ret < 0) { perror("VIDIOC_G_FMT"); return ret; } printf("/n**********vidioc get stream format informations:****n"); if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) printf("8-bit YUYVV pixel formatn"); printf("Size of the buffer = %dn", fmt.fmt.pix.sizeimage); printf("Line offset = %dn", fmt.fmt.pix.bytesperline); if (fmt.fmt.pix.field == V4L2_FIELD_INTERLACED) printf("Storate format is interlaced frame formatn"); return 0; }
3.7 向驱动申请帧缓冲,内存,一般不超过5个,帧缓冲管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158结构体 struct v4l2_requestbuffers req; ret = ioctl(fd, VIDIOC_REQBUFS, &req); ret = ioctl(fd, VIDIOC_QUERYBUF, &buf);//读取缓存 struct v4l2_requestbuffers { __u32 count; enum v4l2_buf_type type; enum v4l2_memory memory; __u32 reserved[2]; }; struct v4l2_buffer { __u32 index; enum v4l2_buf_type type; __u32 bytesused; __u32 flags; enum v4l2_field field; struct timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ enum v4l2_memory memory; union { __u32 offset; unsigned long userptr; } m; __u32 length; __u32 input; __u32 reserved; }; 使用VIDIOC_REQBUFS 我们获取了req.count个缓存,下一步通过 调用VIDIOC_QUERYBUF 命令来获取这些缓存的地址,然后使用 mmap函数转换成应用程序中的绝对地址,最后把这些缓存放入 缓存队列。 The main steps that the application must perform for buffer allocation are: Allocating Memory Getting Physical Address Mapping Kernel Space Address to User Space 驱动支持 static int s3c_fimc_v4l2_reqbufs(struct file *filp, void *fh, struct v4l2_requestbuffers *b) { if (b->memory != V4L2_MEMORY_MMAP) { err("V4L2_MEMORY_MMAP is only supportedn"); return -EINVAL; } /* control user input */ if (b->count > 4) b->count = 4; else if (b->count < 1) b->count = 1; return 0; } static int s3c_fimc_v4l2_querybuf(struct file *filp, void *fh, struct v4l2_buffer *b) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; if (b->type != V4L2_BUF_TYPE_VIDEO_OVERLAY && b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (b->memory != V4L2_MEMORY_MMAP) return -EINVAL; b->length = ctrl->out_frame.buf_size; /* * NOTE: we use the m.offset as an index for multiple frames out. * Because all frames are not contiguous, we cannot use it as * original purpose. * The index value used to find out which frame user wants to mmap. */ b->m.offset = b->index * PAGE_SIZE; return 0; } static int s3c_fimc_v4l2_qbuf(struct file *filp, void *fh, struct v4l2_buffer *b) { return 0; } 应用层 static int video_mmap(int fd) { /*******step 1*****requestbuffers Allocating Memory *******/ int ret = 0; struct v4l2_requestbuffers req; CLEAR(req); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_REQBUFS, &req); if (ret < 0) { perror("VIDIOC_REQBUFS"); return ret; } if (req.count < 2) printf("insufficient buffer memoryn"); printf("Number of buffers allocated = %dn", req.count); /*******step 2*****Getting Physical Address *******/ buffers = calloc(req.count, sizeof(*buffers)); for (n_buffers = 0; n_buffers < req.count; ++n_buffers) { struct v4l2_buffer buf;//驱动中的一帧 CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; ret = ioctl(fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) { perror("VIDIOC_QUERYBUF"); return ret; } /*******step 3*****Mapping Kernel Space Address to User Space*******/ buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); //if (MAP_FAILED == buffers[n_buffers].start) //perror("mmap failed n"); } /************requestbuffers in queue***********/ for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; CLEAR(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ret = ioctl(fd, VIDIOC_QBUF, &buf);//申请的缓冲进入队列 if (ret < 0) { perror("VIDIOC_QBUF"); return ret; } } return 0; }
3.8 开始捕捉图像数据(重要)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178复制代码
结构体
复制代码enum v4l2_buf_type type;//开始捕捉图像数据 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &type); 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_PRIVATE = 0x80, }; 驱动 static int s3c_fimc_v4l2_streamon(struct file *filp, void *fh, enum v4l2_buf_type i) { struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh; if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; printk("s3c_fimc_v4l2_streamon is calledn"); if (ctrl->in_type != PATH_IN_DMA) s3c_fimc_init_camera(ctrl); ctrl->out_frame.skip_frames = 0; FSET_CAPTURE(ctrl); FSET_IRQ_NORMAL(ctrl); s3c_fimc_start_dma(ctrl); return 0; } 硬件控制寄存器的配置 应用层 static int video_streamon(int fd) { int ret = 0; /************start stream on***********/ enum v4l2_buf_type types;//开始捕捉图像数据 types = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &types); if (ret < 0) { perror("VIDIOC_STREAMON"); return ret; } return 0; }
复制代码复制代码复制代码复制代码复制代码
最后
以上就是大方汉堡最近收集整理的关于V4L2驱动程序架构1 V4L2简介2 V4L2驱动注册3 V4L2 操作的全部内容,更多相关V4L2驱动程序架构1内容请搜索靠谱客的其他文章。
发表评论 取消回复