v4l2数据获取流程
V4L2数据获取流程
整个过程相关的数据结构有如下几个:
struct v4l2_capability m_cap; /* 驱动能力 */
struct v4l2_format m_fmt; /* 数据格式 */
struct v4l2_fmtdesc m_desc_fmt; /* 格式描述 */
struct v4l2_requestbuffers m_rb; /* 请求帧缓冲 */
struct v4l2_buffer m_buf; /* 数据缓冲 */
struct v4l2_frmsizeenum m_frmsize; /* 帧大小描述 */
1. 打开设备节点
#define DEFAULT_CAMERA_PATH "/dev/video0"
m_fd = open(DEFAULT_CAMERA_PATH, O_RDWR);
if(m_fd < 0) {
perror("open failed.");
return -1;
}
2. 可使用ioctl VIDIOC_QUERYCAP 查询当前驱动能力。
memset(&m_cap, 0, sizeof(m_cap));
if(ioctl(m_fd, VIDIOC_QUERYCAP, &m_cap) < 0) {
perror("ioctl failed");
return -1;
}
if(m_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
printf("V4L2_CAP_VIDEO_CAPTURE supportted\n");
}
if(m_cap.capabilities & V4L2_CAP_STREAMING) {
printf("V4L2_CAP_STREAMING supportted\n");
}
if(m_cap.capabilities & V4L2_CAP_READWRITE) {
printf("V4L2_CAP_READWRITE supportted\n");
}
3. 设置数据格式
/* set data format */
memset(&m_fmt, 0x0, sizeof(m_fmt));
m_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
m_fmt.fmt.pix.width = 640;
m_fmt.fmt.pix.height = 480;
m_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
m_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if(ioctl(m_fd, VIDIOC_S_FMT, &m_fmt) < 0) {
perror("ioctl VIDIOC_S_FMT failed!");
return -1;
}
4. 请求帧缓冲
/* request framebuffer */
memset(&m_rb, 0x0, sizeof(m_rb));
m_rb.count = DEFAULT_BUFFER_COUNT;
m_rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
m_rb.memory = V4L2_MEMORY_MMAP;
if(ioctl(m_fd, VIDIOC_REQBUFS, &m_rb) < 0) {
perror("ioctl VIDIOC_REQBUFS failed!");
return -1;
}
5. 映射buffer到用户空间
m_rb.count
上一步请求的BUFFER数量,即v4l2_requestbuffers中定义
video_buffer
用户空间的buffer结构体,用来存放mmap相关信息,定义如下
struct video_buffer { unsigned int length; unsigned int offset; unsigned char *start; };
m_video_buffers = (struct video_buffer *)calloc(m_rb.count, sizeof(struct video_buffer));
/* map the framebuffer to userspace */
for(int index_buf = 0; index_buf < m_rb.count; index_buf++) {
/* calloc the userspace buffer */
memset(&m_buf, 0x0, sizeof(m_buf));
m_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
m_buf.memory = V4L2_MEMORY_MMAP;
m_buf.index = index_buf;
if(ioctl(m_fd, VIDIOC_QUERYBUF, &m_buf) < 0) {
perror("ioctl VIDIOC_QUERYBUF failed!");
return -1;
}
m_video_buff_size = m_buf.length;
/* map to userspace */
m_video_buffers[index_buf].start = (unsigned char *)mmap(NULL,
m_buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
m_fd,
m_buf.m.offset);
if(m_video_buffers[index_buf].start == MAP_FAILED) {
perror("map video buf failed!");
return -1;
}
/* queue the buffer */
memset(&m_buf, 0x0, sizeof(m_buf));
m_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
m_buf.memory = V4L2_MEMORY_MMAP;
m_buf.index = index_buf;
if(ioctl(m_fd, VIDIOC_QBUF, &m_buf) < 0) {
perror("ioctl VIDIOC_QBUF failed!");
return -1;
}
}
printf("v4l2_buf size: %d\n", m_buf.length);
6.启动数据流
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(m_fd, VIDIOC_STREAMON, &type) < 0) {
perror("ioctl VIDIOC_STREAMON failed!");
return -1;
}
7.获取帧数据(写入到文件)
int ret;
static int frame_count=1;
struct v4l2_buffer v4lbuffer;
/* pop fb from queue */
memset(&v4lbuffer, 0x0, sizeof(v4lbuffer));
v4lbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4lbuffer.memory = V4L2_MEMORY_MMAP;
if(ioctl(m_fd, VIDIOC_DQBUF, &v4lbuffer) < 0) {
perror("ioctl VIDIOC_DQBUF failed!");
return ret;
}
m_rb_current = v4lbuffer.index;
m_total_bytes = v4lbuffer.bytesused;
int out_fd;
unsigned char *file_base;
char out_fname[50];
sprintf(out_fname, "./abc_%04d.jpeg", frame_count);
LOG_DEBUG("***save_fbdata_to_file by mmap.");
out_fd = open(out_fname, O_RDWR | O_CREAT | O_TRUNC, 0755);
if(out_fd < 0) {
perror("open path failed!");
return out_fd;
}
/* make file length */
lseek(out_fd, m_total_bytes - 1, SEEK_END);
write(out_fd, "", 1); /* because of copy on write? */
printf("total_bytes:%d\n", m_total_bytes);
printf("current:%d\n", m_rb_current);
/* write operation here */
file_base = (unsigned char *)mmap(NULL, m_total_bytes,
PROT_READ | PROT_WRITE,
MAP_SHARED,
out_fd, 0);
if(file_base == MAP_FAILED) {
perror("mmap file failed!");
return -1;
}
LOG_DEBUG("memcpying...");
memcpy(file_base, m_video_buffers[m_rb_current].start, m_total_bytes);
/* release the resource */
munmap(file_base, m_total_bytes);
close(out_fd);
frame_count++;
/* push back fb to queue */
LOG_DEBUG("push back fb to queue");
memset(&v4lbuffer, 0x0, sizeof(v4lbuffer));
v4lbuffer.index = m_rb_current;
v4lbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4lbuffer.memory = V4L2_MEMORY_MMAP;
if(ioctl(m_fd, VIDIOC_QBUF, &v4lbuffer) < 0) {
perror("ioctl VIDIOC_QBUF failed!");
return -1;
}
//if(frame_count > 24)exit(1);
return frame_count;
8.停止数据流
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(m_fd, VIDIOC_STREAMOFF, &type) < 0) {
perror("ioctl VIDIOC_STREAMOFF failed!");
return -1;
}
最后附上main.cpp
#include <iostream>
#include <time.h>
#include <signal.h>
#include "v4l2_capturer.h"
v4l2_capturer capturer;
void capturer_sigint_handler(int signal)
{
capturer.stop();
exit(1);
}
/* TODO: listen SIGINT and call capturer.stop() when it come. */
int main(int argc, char const *argv[])
{
int frame_count=0;
std::cout << "Hello World!" << std::endl;
/* register signal handler function. */
signal(SIGINT, capturer_sigint_handler);
capturer.init();
capturer.query_supported_format_new();
capturer.start();
int i=25;
char filename[10];
while(1){
frame_count=capturer.get_frame();
if(frame_count==25*10)break;
}
/*capturer.get_frame();
capturer.save_fbdata_to_file_by_mmap("./out.jpg");*/
capturer.stop();
return 0;
}
v4l2数据获取流程的更多相关文章
- (一)V4L2学习流程
title: V4L2学习流程 date: 2019/4/23 18:00:00 toc: true --- V4L2学习流程 参考资料 关键资料,插图让人一下子就理解了 Linux摄像头驱动1--v ...
- V4L2视频采集原理
一.简介 Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版.V4L2是linux操作系统下用于采集图片.视频和音频数据的API接口,配合适当的视频采集设备 ...
- Linux实战教学笔记51:Zabbix监控平台3.2.4(三)生产环境案例
https://www.cnblogs.com/chensiqiqi/p/9162986.html 一,Zabbix生产环境监测案例概述 1.1 项目规划 [x] :主机分组 交换机 Nginx To ...
- Kinect2.0骨骼跟踪与数据平滑
Kinect v1和Kinect v2传感器的配置比较: Kinect v1 Kinect v2 颜色(Color) 分辨率(Resolution) 640×480 1920× ...
- zabbix项目实践
一,Zabbix生产环境监测案例概述 1.1 项目规划 [x] :主机分组 交换机 Nginx Tomcat MySQL Apache PHP-fpm redis(也有状态页, 自己研究) memca ...
- solr入门之多线程操作solr中索引字段的解决
涉及的问题: 建索引时有一个字段是该词语出现的次数,这个字段是放在solr里的 而我用的是多线程来进行全量导入的,这里就涉及到了多线程问题 多个线程操作同一个变量时怎样处理? 我是这样子做的 : 首 ...
- 2019年12月4日Linux开发手记
OK,经过昨天对V4L2工作流程的学习,现在已经大体了解了V4L2的工作原理,现在开始对V4L2的API的学习,目标:1.打开摄像头 2.储存图像 3.关闭摄像头,API网址:Linux Media ...
- Zabbix 监控PHP-FTPM、Tomcat、Redis应用
一.zabbix 监控 PHP-FPM应用实战Nginx+PHP-FPM是目前最流行的LNMP架构,在基于PHP开发的系统下,对这些系统性能的监控,主要是关注PHP-FPM的运行状态,那么什么是PHP ...
- zabbix生产环境案例(三)
生产环境案例(三) 链接:https://pan.baidu.com/s/1q5YwJMTcZLcS5OQ0iOu44A 提取码:8gdi 复制这段内容后打开百度网盘手机App,操作更方便哦 1. Z ...
随机推荐
- 位置式PID讲解
table { margin: auto } 一.公式拆解 \(PID\)公式展示: \[u(t)=K_p(e(t)+\frac{1}{T_t } ∫_0^te(t)dt+T_D \frac {de( ...
- [BUUCTF]PWN——bjdctf_2020_router
bjdctf_2020_router 附件 步骤: 例行检查,64位程序,开启了NX保护 本地试运行一下程序,看看大概的情况 会让我们选择,选择4.root,没什么用,但是注意了,这边选1会执行pin ...
- docker初识-docker安装、基于docker安装mysql及tomcat、基本命令
一.docker是什么 用go语言开发,开源的应用容器引擎,容器性能开销极低 二.整体架构图 Docker 包括三个基本概念: 镜像(Image):Docker 镜像(Image),就相当于是一个 r ...
- 再识requests
高级用法 本篇文档涵盖了 Requests 的一些高级特性. 会话对象 会话对象让你能够跨请求保持某些参数.它也会在同一个 Session 实例发出的所有请求之间保持 cookie, 期间使用 url ...
- python执行命令行调试工具pdb
调试 pdb pdb是基于命令行的调试工具,非常类似gnu的gdb(调试c/c++). 命令 简写命令 作用 break b 设置断点(用法,b <数字>:在第数字行设置断点....... ...
- netty系列之:小白福利!手把手教你做一个简单的代理服务器
目录 简介 代理和反向代理 netty实现代理的原理 实战 总结 简介 爱因斯坦说过:所有的伟大,都产生于简单的细节中.netty为我们提供了如此强大的eventloop.channel通过对这些简单 ...
- 【LeetCode】513. Find Bottom Left Tree Value 解题报告(Python & C++ & Java)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS DFS Date 题目地址:https:// ...
- 倍福CX5120嵌入式控制器使用教程
1.新建工程 新建TwinCAT XAE Project 2.连接设备 点击SYSTEM,再点击"Change Target..." 在弹出的"choose Targt ...
- 漫谈grpc 3:从实践到原理,带你参透 gRPC
原文链接:万字长文 | 从实践到原理,带你参透 gRPC 大家好,我是煎鱼. gRPC 在 Go 语言中大放异彩,越来越多的小伙伴在使用,最近也在公司安利了一波,希望这一篇文章能带你一览 gRPC ...
- 前后端java+vue 实现rsa 加解密与摘要签名算法
RSA 加密.解密.签名.验签.摘要,前后端java+vue联调测试通过 直接上代码 // 注意:加密密文与签名都是唯一的,不会变化.// 注意:vue 端密钥都要带pem格式.java 不要带pem ...