USB摄像头使用记录
文章目录
- USB摄像头使用记录
- 1.概述
- 1.1 v4l2介绍
- 2.使用
- 2.1linux应用
- 2.2linux驱动
- 3.调试
- 3.1应用调试
- 3.1.1 获取并打印摄像头参数
- 3.1.2get_data.c
- 4.平台信息
- 5.参考记录
1.概述
1.1 v4l2介绍
2.使用
2.1linux应用
2.1.1获取并打印摄像头参数
2.2linux驱动
3.调试
3.1应用调试
常用的VIDIOC命令:
1. VIDIOC_QUERYCAP (查询设备属性)2. VIDIOC_ENUM_FMT (显示所有支持的格式)3. VIDIOC_S_FMT (设置视频捕获格式)4. VIDIOC_G_FMT (获取硬件现在的视频捕获格式)5. VIDIOC_TRY_FMT (检查是否支持某种帧格式)6. VIDIOC_ENUM_FRAMESIZES (枚举设备支持的分辨率信息)7. VIDIOC_ENUM_FRAMEINTERVALS (获取设备支持的帧间隔)8. VIDIOC_S_PARM && VIDIOC_G_PARM (设置和获取流参数)9. VIDIOC_QUERYCAP (查询驱动的修剪能力)10. VIDIOC_S_CROP (设置视频信号的边框)11. VIDIOC_G_CROP (读取设备信号的边框)12. VIDIOC_REQBUFS (向设备申请缓存区)13. VIDIOC_QUERYBUF (获取缓存帧的地址、长度)14. VIDIOC_QBUF (把帧放入队列)15. VIDIOC_DQBUF (从队列中取出帧)16. VIDIOC_STREAMON && VIDIOC_STREAMOFF (启动/停止视频数据流)
原文链接:https://blog.csdn.net/mark_minge/article/details/81427489
3.1.1 获取并打印摄像头参数
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <linux/types.h>
#include <linux/videodev2.h> /* for videodev2.h */
关键结构体:
struct v4l2_fmtdesc
{u32 index; // 要查询的格式序号,应用程序设置enum v4l2_buf_type type; // 帧类型,应用程序设置u32 flags; // 是否为压缩格式u8 description[32]; // 格式名称u32 pixelformat; // 格式u32 reserved[4]; // 保留
};
struct v4l2_frmsizeenum {__u32 index; /* Frame size number */__u32 pixel_format; /* 像素格式 */__u32 type; /* type */union { /* Frame size */struct v4l2_frmsize_discrete discrete;struct v4l2_frmsize_stepwise stepwise;
};__u32 reserved[2]; /* Reserved space for future use */
};struct v4l2_frmsize_discrete {__u32 width; /* Frame width [pixel] */__u32 height; /* Frame height [pixel] */
};
.c
/** @Descripttion: * @version: * @Author: Andy* @Date: 2024-05-26 00:03:08* @LastEditors: Andy* @LastEditTime: 2024-05-26 00:06:05*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <linux/types.h> /* for videodev2.h */
#include <linux/videodev2.h>/* ./video_test </dev/video0> */int main(int argc, char **argv)
{int fd;struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum fsenum;int fmt_index = 0;int frame_index = 0;if (argc != 2){printf("Usage: %s </dev/videoX>, print format detail for video device\n", argv[0]);return -1;}/* open */fd = open(argv[1], O_RDWR);if (fd < 0){printf("can not open %s\n", argv[1]);return -1;}while (1){/* 枚举格式 */fmtdesc.index = fmt_index; // 比如从0开始fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type为"捕获"if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))break;frame_index = 0;while (1){/* 枚举这种格式所支持的帧大小 */memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0){printf("format %s,%d, framesize %d: %d x %d\n", fmtdesc.description, fmtdesc.pixelformat, frame_index, fsenum.discrete.width, fsenum.discrete.height);}else{break;}frame_index++;}fmt_index++;}return 0;
}
3.1.2get_data.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <linux/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>/* ./video_test </dev/video0> */int main(int argc, char **argv)
{int fd;struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum fsenum;int fmt_index = 0;int frame_index = 0;int i;void *bufs[32];int buf_cnt;int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;struct pollfd fds[1];char filename[32];int file_cnt = 0;if (argc != 2){printf("Usage: %s </dev/videoX>, print format detail for video device\n", argv[0]);return -1;}/* open */fd = open(argv[1], O_RDWR);if (fd < 0){printf("can not open %s\n", argv[1]);return -1;}/* 查询能力 */struct v4l2_capability cap;memset(&cap, 0, sizeof(struct v4l2_capability));if (0 == ioctl(fd, VIDIOC_QUERYCAP, &cap)){ if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {fprintf(stderr, "Error opening device %s: video capture not supported.\n",argv[1]);return -1;}if(!(cap.capabilities & V4L2_CAP_STREAMING)) {fprintf(stderr, "%s does not support streaming i/o\n", argv[1]);return -1;}}else{printf("can not get capability\n");return -1;}while (1){/* 枚举格式 */fmtdesc.index = fmt_index; // 比如从0开始fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type为"捕获"if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))break;frame_index = 0;while (1){/* 枚举这种格式所支持的帧大小 */memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0){printf("format %s,%d, framesize %d: %d x %d\n", fmtdesc.description, fmtdesc.pixelformat, frame_index, fsenum.discrete.width, fsenum.discrete.height);}else{break;}frame_index++;}fmt_index++;}/* 设置格式 */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;if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt)){printf("set format ok: %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);}else{printf("can not set format\n");return -1;}/** 申请buffer*/struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_REQBUFS, &rb)){/* 申请成功后, mmap这些buffer */buf_cnt = rb.count;for(i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){/* mmap */bufs[i] = mmap(0 /* start anywhere */ ,buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,buf.m.offset);if(bufs[i] == MAP_FAILED) {perror("Unable to map buffer");return -1;}}else{printf("can not query buffer\n");return -1;} }printf("map %d buffers ok\n", buf_cnt);}else{printf("can not request buffers\n");return -1;}/* 把所有buffer放入"空闲链表" */for(i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");return -1;}}printf("queue buffers ok\n");/* 启动摄像头 */if (0 != ioctl(fd, VIDIOC_STREAMON, &type)){perror("Unable to start capture");return -1;}printf("start capture ok\n");while (1){/* poll */memset(fds, 0, sizeof(fds));fds[0].fd = fd;fds[0].events = POLLIN;if (1 == poll(fds, 1, -1)){/* 把buffer取出队列 */struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(fd, VIDIOC_DQBUF, &buf)){perror("Unable to dequeue buffer");return -1;}/* 把buffer的数据存为文件 */sprintf(filename, "video_raw_data_%04d.jpg", file_cnt++);int fd_file = open(filename, O_RDWR | O_CREAT, 0666);if (fd_file < 0){printf("can not create file : %s\n", filename);}printf("capture to %s\n", filename);write(fd_file, bufs[buf.index], buf.bytesused);close(fd_file);/* 把buffer放入队列 */if (0 != ioctl(fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");return -1;}}}if (0 != ioctl(fd, VIDIOC_STREAMOFF, &type)){perror("Unable to stop capture");return -1;}printf("stop capture ok\n");close(fd);return 0;
}
3.1.3 ctrl_light
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <linux/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>
#include <pthread.h>/* ./video_test </dev/video0> */static void *thread_brightness_control (void *args)
{int fd = (int)args;unsigned char c;int brightness;int delta;struct v4l2_queryctrl qctrl;memset(&qctrl, 0, sizeof(qctrl));qctrl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;if (0 != ioctl(fd, VIDIOC_QUERYCTRL, &qctrl)){printf("can not query brightness\n");return NULL;}printf("brightness min = %d, max = %d\n", qctrl.minimum, qctrl.maximum);delta = (qctrl.maximum - qctrl.minimum) / 10;struct v4l2_control ctl;ctl.id = V4L2_CID_BRIGHTNESS; // V4L2_CID_BASE+0;ioctl(fd, VIDIOC_G_CTRL, &ctl);while (1){c = getchar();if (c == 'u' || c == 'U'){ctl.value += delta;}else if (c == 'd' || c == 'D'){ctl.value -= delta;}if (ctl.value > qctrl.maximum)ctl.value = qctrl.maximum;if (ctl.value < qctrl.minimum)ctl.value = qctrl.minimum;ioctl(fd, VIDIOC_S_CTRL, &ctl);}return NULL;
}int main(int argc, char **argv)
{int fd;struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum fsenum;int fmt_index = 0;int frame_index = 0;int i;void *bufs[32];int buf_cnt;int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;struct pollfd fds[1];char filename[32];int file_cnt = 0;if (argc != 2){printf("Usage: %s </dev/videoX>, print format detail for video device\n", argv[0]);return -1;}/* open */fd = open(argv[1], O_RDWR);if (fd < 0){printf("can not open %s\n", argv[1]);return -1;}/* 查询能力 */struct v4l2_capability cap;memset(&cap, 0, sizeof(struct v4l2_capability));if (0 == ioctl(fd, VIDIOC_QUERYCAP, &cap)){ if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {fprintf(stderr, "Error opening device %s: video capture not supported.\n",argv[1]);return -1;}if(!(cap.capabilities & V4L2_CAP_STREAMING)) {fprintf(stderr, "%s does not support streaming i/o\n", argv[1]);return -1;}}else{printf("can not get capability\n");return -1;}while (1){/* 枚举格式 */fmtdesc.index = fmt_index; // 比如从0开始fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 指定type为"捕获"if (0 != ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))break;frame_index = 0;while (1){/* 枚举这种格式所支持的帧大小 */memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0){printf("format %s,%d, framesize %d: %d x %d\n", fmtdesc.description, fmtdesc.pixelformat, frame_index, fsenum.discrete.width, fsenum.discrete.height);}else{break;}frame_index++;}fmt_index++;}/* 设置格式 */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;if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt)){printf("set format ok: %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);}else{printf("can not set format\n");return -1;}/** 申请buffer*/struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_REQBUFS, &rb)){/* 申请成功后, mmap这些buffer */buf_cnt = rb.count;for(i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){/* mmap */bufs[i] = mmap(0 /* start anywhere */ ,buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,buf.m.offset);if(bufs[i] == MAP_FAILED) {perror("Unable to map buffer");return -1;}}else{printf("can not query buffer\n");return -1;} }printf("map %d buffers ok\n", buf_cnt);}else{printf("can not request buffers\n");return -1;}/* 把所有buffer放入"空闲链表" */for(i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");return -1;}}printf("queue buffers ok\n");/* 启动摄像头 */if (0 != ioctl(fd, VIDIOC_STREAMON, &type)){perror("Unable to start capture");return -1;}printf("start capture ok\n");/* 创建线程用来控制亮度 */pthread_t thread;pthread_create(&thread, NULL, thread_brightness_control, (void *)fd);while (1){/* poll */memset(fds, 0, sizeof(fds));fds[0].fd = fd;fds[0].events = POLLIN;if (1 == poll(fds, 1, -1)){/* 把buffer取出队列 */struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(fd, VIDIOC_DQBUF, &buf)){perror("Unable to dequeue buffer");return -1;}/* 把buffer的数据存为文件 */sprintf(filename, "video_raw_data_%04d.jpg", file_cnt++);int fd_file = open(filename, O_RDWR | O_CREAT, 0666);if (fd_file < 0){printf("can not create file : %s\n", filename);}printf("capture to %s\n", filename);write(fd_file, bufs[buf.index], buf.bytesused);close(fd_file);/* 把buffer放入队列 */if (0 != ioctl(fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");return -1;}}}if (0 != ioctl(fd, VIDIOC_STREAMOFF, &type)){perror("Unable to stop capture");return -1;}printf("stop capture ok\n");close(fd);return 0;
}
4.平台信息
开发板:韦东山imx6ull
摄像头:韦东山usb免驱摄像头
涉及软件:
乌班图
vscode
MobaXterm
linux版本:Linux-4.9.88
5.参考记录
博客
正点原子:
【正点原子Linux连载】第二十章 V4L2摄像头应用编程-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1_v4l2驱动框架 正点原子-CSDN博客
视频
韦东山:
03_V4L2应用程序开发_列出帧细节_哔哩哔哩_bilibili