diff --git a/IMX6ULL/doc_pic/13_V4L2/01_V4L2应用程序开发.md b/IMX6ULL/doc_pic/13_V4L2/01_V4L2应用程序开发.md index 5331e4c..fe7de79 100644 --- a/IMX6ULL/doc_pic/13_V4L2/01_V4L2应用程序开发.md +++ b/IMX6ULL/doc_pic/13_V4L2/01_V4L2应用程序开发.md @@ -441,11 +441,31 @@ int main(int argc, char **argv) ### 3.2 获取数据 +根据《1.2 完整的使用流程》来编写程序,步骤如下: + +* 打开设备 +* ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力 +* 枚举格式、设置格式 +* ioctl VIDIOC_REQBUFS:申请buffer +* ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射 +* ioctl VIDIOC_QBUF:把buffer放入"空闲链表" +* ioctl VIDIOC_STREAMON:启动摄像头 +* 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表" + * poll/select + * ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer + * 处理:前面使用mmap映射了每个buffer的地址,把这个buffer的数据存为文件 + * ioclt VIDIOC_QBUF:把buffer放入"空闲链表" +* ioctl VIDIOC_STREAMOFF:停止摄像头 + +![image-20230726162003001](pic/19_get_data.png) + + + ### 3.3 控制亮度 - +![image-20230726170548040](pic/20_brightness.png) diff --git a/IMX6ULL/doc_pic/13_V4L2/pic/19_get_data.png b/IMX6ULL/doc_pic/13_V4L2/pic/19_get_data.png new file mode 100644 index 0000000..bf5f69b Binary files /dev/null and b/IMX6ULL/doc_pic/13_V4L2/pic/19_get_data.png differ diff --git a/IMX6ULL/doc_pic/13_V4L2/pic/20_brightness.png b/IMX6ULL/doc_pic/13_V4L2/pic/20_brightness.png new file mode 100644 index 0000000..5038769 Binary files /dev/null and b/IMX6ULL/doc_pic/13_V4L2/pic/20_brightness.png differ diff --git a/IMX6ULL/doc_pic/13_V4L2/笔记.md b/IMX6ULL/doc_pic/13_V4L2/笔记.md index e772bea..f6d418a 100644 --- a/IMX6ULL/doc_pic/13_V4L2/笔记.md +++ b/IMX6ULL/doc_pic/13_V4L2/笔记.md @@ -46,7 +46,7 @@ sensor---串行器---GSML---->解串器---->soc(mipi接口)这一条路也 ## 3. 学习笔记 - +好文:https://zhuanlan.zhihu.com/p/613018868 Linux V4L2子系统分析(一): https://blog.csdn.net/u011037593/article/details/115415136 diff --git a/IMX6ULL/source/13_V4L2/04_video_get_data/video_test.c b/IMX6ULL/source/13_V4L2/04_video_get_data/video_test.c new file mode 100644 index 0000000..89ecab0 --- /dev/null +++ b/IMX6ULL/source/13_V4L2/04_video_get_data/video_test.c @@ -0,0 +1,237 @@ + +#include +#include +#include +#include +#include +#include +#include +#include /* for videodev2.h */ +#include +#include +#include + +/* ./video_test */ + +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 , 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; +} + diff --git a/IMX6ULL/source/13_V4L2/05_video_brightness/video_test.c b/IMX6ULL/source/13_V4L2/05_video_brightness/video_test.c new file mode 100644 index 0000000..e4c6b20 --- /dev/null +++ b/IMX6ULL/source/13_V4L2/05_video_brightness/video_test.c @@ -0,0 +1,289 @@ + +#include +#include +#include +#include +#include +#include +#include +#include /* for videodev2.h */ +#include +#include +#include +#include + +/* ./video_test */ + +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 , 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; +} + diff --git a/README.md b/README.md index 08fc870..197c270 100644 --- a/README.md +++ b/README.md @@ -784,8 +784,11 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git ```shell 03_V4L2应用程序开发_列出帧细节 + 04_V4L2应用程序开发_获取数据 +05_V4L2应用程序开发_调试 + 06_V4L2应用程序开发_控制亮度 ``` - + diff --git a/STM32MP157/doc_pic/13_V4L2/01_V4L2应用程序开发.md b/STM32MP157/doc_pic/13_V4L2/01_V4L2应用程序开发.md index 5331e4c..fe7de79 100644 --- a/STM32MP157/doc_pic/13_V4L2/01_V4L2应用程序开发.md +++ b/STM32MP157/doc_pic/13_V4L2/01_V4L2应用程序开发.md @@ -441,11 +441,31 @@ int main(int argc, char **argv) ### 3.2 获取数据 +根据《1.2 完整的使用流程》来编写程序,步骤如下: + +* 打开设备 +* ioctl VIDIOC_QUERYCAP:Query Capbility,查询能力 +* 枚举格式、设置格式 +* ioctl VIDIOC_REQBUFS:申请buffer +* ioctl VIDIOC_QUERYBUF和mmap:查询buffer信息、映射 +* ioctl VIDIOC_QBUF:把buffer放入"空闲链表" +* ioctl VIDIOC_STREAMON:启动摄像头 +* 这里是一个循环:使用poll/select监测buffer,然后从"完成链表"中取出buffer,处理后再放入"空闲链表" + * poll/select + * ioctl VIDIOC_DQBUF:从"完成链表"中取出buffer + * 处理:前面使用mmap映射了每个buffer的地址,把这个buffer的数据存为文件 + * ioclt VIDIOC_QBUF:把buffer放入"空闲链表" +* ioctl VIDIOC_STREAMOFF:停止摄像头 + +![image-20230726162003001](pic/19_get_data.png) + + + ### 3.3 控制亮度 - +![image-20230726170548040](pic/20_brightness.png) diff --git a/STM32MP157/doc_pic/13_V4L2/pic/19_get_data.png b/STM32MP157/doc_pic/13_V4L2/pic/19_get_data.png new file mode 100644 index 0000000..bf5f69b Binary files /dev/null and b/STM32MP157/doc_pic/13_V4L2/pic/19_get_data.png differ diff --git a/STM32MP157/doc_pic/13_V4L2/pic/20_brightness.png b/STM32MP157/doc_pic/13_V4L2/pic/20_brightness.png new file mode 100644 index 0000000..5038769 Binary files /dev/null and b/STM32MP157/doc_pic/13_V4L2/pic/20_brightness.png differ diff --git a/STM32MP157/doc_pic/13_V4L2/笔记.md b/STM32MP157/doc_pic/13_V4L2/笔记.md index e772bea..f6d418a 100644 --- a/STM32MP157/doc_pic/13_V4L2/笔记.md +++ b/STM32MP157/doc_pic/13_V4L2/笔记.md @@ -46,7 +46,7 @@ sensor---串行器---GSML---->解串器---->soc(mipi接口)这一条路也 ## 3. 学习笔记 - +好文:https://zhuanlan.zhihu.com/p/613018868 Linux V4L2子系统分析(一): https://blog.csdn.net/u011037593/article/details/115415136 diff --git a/STM32MP157/source/A7/13_V4L2/04_video_get_data/video_test.c b/STM32MP157/source/A7/13_V4L2/04_video_get_data/video_test.c new file mode 100644 index 0000000..89ecab0 --- /dev/null +++ b/STM32MP157/source/A7/13_V4L2/04_video_get_data/video_test.c @@ -0,0 +1,237 @@ + +#include +#include +#include +#include +#include +#include +#include +#include /* for videodev2.h */ +#include +#include +#include + +/* ./video_test */ + +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 , 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; +} + diff --git a/STM32MP157/source/A7/13_V4L2/05_video_brightness/video_test.c b/STM32MP157/source/A7/13_V4L2/05_video_brightness/video_test.c new file mode 100644 index 0000000..e4c6b20 --- /dev/null +++ b/STM32MP157/source/A7/13_V4L2/05_video_brightness/video_test.c @@ -0,0 +1,289 @@ + +#include +#include +#include +#include +#include +#include +#include +#include /* for videodev2.h */ +#include +#include +#include +#include + +/* ./video_test */ + +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 , 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; +} +