毕设系列之Linux V4L2(图形图像采集篇)
PS:要转载请注明出处,本人版权所有。
PS: 这个只是基于《我自己》的理解,
如果和你的原则及想法相冲突,请谅解,勿喷。
前置说明
本文作为本人csdn blog的主站的备份。(BlogID=034)
本文发布于 2017-07-02 14:12:38,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=034)
环境说明
Ubuntu 16.04 LTS
前言
无
V4L2
V4L2 介绍
虽然介绍Linux V4L2的文章已经满大街了,但是这里我也还要讲一些基本的东西。
- v4l2 是Video for Linux 2的简称。
- v4l2 不仅仅支持图像类设备,还支持音频等设备类型。
- 能够使用v4l2的前提是Linux内核已经识别并注册了了相关的设备,常见的就是/dev/videox类似的设备文件存在。(如果是做图像类的采集,说白了你得有个摄像头,这个摄像头还得被Linux 内核识别)
- 从上文可知,一个设备要被内核识别必须要由驱动才行,而对于我们常用的图像采集来说,就是要有一个摄像头驱动才行。对于这个问题,linux内核工作者根据UVC这个标准开发了一个通用的驱动程序,并且整合到linux内核中。只要是支持UVC这个通用标准的摄像头芯片,就能够使用这个驱动。(非图像类也有类似的存在,具体这里不做多余阐述)
- 一般来说,常用的Linux平台是的内核是已经编译进去了UVC驱动的。如果是自己移植的嵌入式Linux 内核,请在配置内核选项时候打开UVC驱动的选项。如下图:
V4L2框架简介
V4L2这个框架的原理简述(这里我们只需要关注几个结构体就可以,要深入,去看内核源代码)
- struct v4l2_device v4l2框架中的根节点,主要是用来管理和遍历其它子节点。
- struct v4l2_subdev v4l2框架中的子节点,在v4l2_device下可以有多个v4l2_subdev存在,这里主要是区分设备类型,如图像或者音频等等。
- struct video_device 具体设备的结构体,并在/dev/下创建相关的设备文件。
- struct v4l2_buffer 为设备数据交换提供空间。
原理(这里以USB摄像头为例):当一个设备接入内核,首先根据USB标准协议对USB进行初始化,最终完成USB设备信息探测。根据USB设备信息,内核给其分配相应的驱动。这里内核知道我们的USB设备是一个图像设备,这时内核开始初始化v4l2_subdev结构体,并且类型设置为图形图像设备。当初始化好后,内核继续初始化一个video_device结构体,并插入到v4l2_subdev的管理链表中。这里的初始化过程中,就要涉及摄像头驱动的加载,设备文件的创建等等。到这里,整个注册环节就结束了,意味着我们可以使用v4l2框架来操作我们的设备。操作的话,就是各种ops的调用就ok了。(这里涉及到usb设备驱动的初始化和字符型设备驱动等等相关知识,需要更多,自行查阅资料)
V4L2的使用
V4L2的使用(没啥可讲的,各种资料烂大街了,我直接贴源代码)
ym_v4l2.c文件
/*
FileName:m_v4l2.c
Version:1.5
Description:
Created On: 2017-2-21
Modified date:2017-3-14
Author:Sky
*/
#include <ym_v4l2.h>
int yInitMV4l2(const char * pathname, yMV4L2 * mv4l2){
//mv4l2 = mvl;
//request alloc IMG_BUFF_NUM DATA_BUF size mem
if ( (mv4l2->img_buf = (IMG_BUF *)calloc(IMG_BUFF_NUM, sizeof(IMG_BUF))) == NULL){
printf("calloc failed\n");
return -1;
}
if ( (mv4l2->camera_fd = open(pathname, O_RDWR | O_NONBLOCK)) < 0){//open video device
perror("Open video device faild");
return -1;
}
return 0;
}
int yIoctlV4l2(enum yV4l2Cmd cmd,...){
va_list arg;
va_start(arg,cmd);
yMV4L2 *mv4l2;
mv4l2 = va_arg(arg,yMV4L2 *);
va_end(arg);
switch(cmd){
case yVIDIOC_QUERYCAP:
{
if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYCAP, &mv4l2->cap) < 0){
perror("QUERY VIDEO CAP FAILED");
return -1;
}
printf("DriverName:%s/nCard Name:%s/nBusinfo:%s/nDriverVersion:%u.%u.%u\n",mv4l2->cap.driver,mv4l2->cap.card,mv4l2->cap.bus_info,(mv4l2->cap.version>>16)&0XFF,(mv4l2->cap.version>>8)&0xFF,mv4l2->cap.version&0xFF);
if ( !(mv4l2->cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE) ){
printf("The device is not a video capture\n");
return -1;
}
if ( !(mv4l2->cap.capabilities & V4L2_CAP_STREAMING) ){
printf("The device can not support streaming i/o\n");
return -1;
}
break;
}
case yVIDIOC_ENUM_FMT:
{
CLEAR_MEM(mv4l2->desc_fmt);
mv4l2->desc_fmt.index = 0;
mv4l2->desc_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while( ioctl(mv4l2->camera_fd, VIDIOC_ENUM_FMT,&mv4l2->desc_fmt) == 0 ){
printf("index : %d, format:%s \n", mv4l2->desc_fmt.index,mv4l2->desc_fmt.description);
mv4l2->desc_fmt.index++;
}
break;
}
case yVIDIOC_S_FMT:
{
// set data format for dev
mv4l2->stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->stream_fmt.fmt.pix.width = IMAGE_WIDTH;
mv4l2->stream_fmt.fmt.pix.height = IMAGE_HEIGHT;
mv4l2->stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if ( ioctl(mv4l2->camera_fd, VIDIOC_S_FMT, &mv4l2->stream_fmt) ){
perror("Set data format failed");
return -1;
}
break;
}
case yVIDIOC_G_FMT:
{
CLEAR_MEM(mv4l2->stream_fmt);
mv4l2->stream_fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(mv4l2->camera_fd,VIDIOC_G_FMT,&mv4l2->stream_fmt);
printf("Currentdata format information: width:%d height:%d\n",mv4l2->stream_fmt.fmt.pix.width,mv4l2->stream_fmt.fmt.pix.height);
break;
}
case yVIDIOC_REQBUFS:
{
//bzero(&reqbuf, sizeof(reqbuf));
CLEAR_MEM(mv4l2->reqbuf);
mv4l2->reqbuf.count = IMG_BUFF_NUM;
mv4l2->reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->reqbuf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_REQBUFS, &mv4l2->reqbuf) < 0 ){
perror("ioctl REQBUFS failed");
return -1;
}
break;
}
case yVIDIOC_STREAMON:
{
mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMON,&mv4l2->type)< 0){
perror("Failed to ioctl:VIDIOC_STREAMON");
return -1;
}
break;
}
case yVIDIOC_STREAMOFF:
{
mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMOFF,&mv4l2->type)< 0){
perror("Failed to ioctl:VIDIOC_STREAMOFF");
return -1;
}
break;
}
case yVIDIOC_S_PARM:
{
CLEAR_MEM(mv4l2->stream_parm);
mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->stream_parm.parm.capture.timeperframe.numerator = 1;
mv4l2->stream_parm.parm.capture.timeperframe.denominator = 10;
if ( ioctl(mv4l2->camera_fd, VIDIOC_S_PARM, &mv4l2->stream_parm) < 0){
perror("Failed to ioctl:VIDIOC_S_PARM");
return -1;
}
break;
}
case yVIDIOC_G_PARM:
{
CLEAR_MEM(mv4l2->stream_parm);
mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( ioctl(mv4l2->camera_fd, VIDIOC_G_PARM, &mv4l2->stream_parm) < 0){
perror("Failed to ioctl:VIDIOC_G_PARM");
return -1;
}
if ( mv4l2->stream_parm.parm.capture.capability == V4L2_CAP_TIMEPERFRAME ){
printf("This Video Support Set Fps,Now-Fps is : %d\n",mv4l2->stream_parm.parm.capture.timeperframe.denominator);
}
else{
printf("This Video Un-Support Set Fps\n");
}
break;
}
case yVIDIOC_DQBUF:
{
//bzero(&normal_buf, sizeof(normal_buf));
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_DQBUF, &mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_DQBUF");
return -1;
}
break;
}
case yVIDIOC_QBUF:
{
if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF, &mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QBUF");
return -1;
}
break;
}
case yMMAPTOVEDIOBUF:
{
for ( int i=0; i < IMG_BUFF_NUM; i++ ){
//bzero(&normal_buf, sizeof(normal_buf));
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
mv4l2->normal_buf.index = i;
//get kernel cache information
if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYBUF,&mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QUERYBUF");
return -1;
}
mv4l2->img_buf[i].len = mv4l2->normal_buf.length;
mv4l2->img_buf[i].start = mmap(NULL,mv4l2->normal_buf.length,
PROT_READ|PROT_WRITE,
MAP_SHARED,mv4l2->camera_fd,
mv4l2->normal_buf.m.offset);
if ( MAP_FAILED == mv4l2->img_buf[i].start){
perror("Failed to mmap");
return -1;
}
}
break;
}
case yPUTVEDIOALLBUFTOQUEUE:
{
//bzero(&normal_buf, sizeof(normal_buf));
for ( int i = 0; i < IMG_BUFF_NUM;i++){
CLEAR_MEM(mv4l2->normal_buf);
mv4l2->normal_buf.index = i;
mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF,&mv4l2->normal_buf) < 0){
perror("Failed to ioctl:VIDIOC_QBUF");
return -1;
}
}
break;
}
case yUNMMAPTOVEDIOBUF:
{
for (int i = 0; i < IMG_BUFF_NUM; i++){
if ( munmap(mv4l2->img_buf[i].start,mv4l2->img_buf[i].len) < 0){
perror("Failed to munmap");
return -1;
}
}
break;
}
default :
{
return -1;
break;
}
}
return 0;
}
int yDestroyMV4l2(yMV4L2 *mv4l2){
//StopStream();
yIoctlV4l2(yVIDIOC_STREAMOFF,mv4l2);
//UnMMapToVedioBUf(mv4l2);
yIoctlV4l2(yUNMMAPTOVEDIOBUF,mv4l2);
close(mv4l2->camera_fd);
free(mv4l2->img_buf);
return 0;
}
ym_v4l2.h
/*
FileName:ym_v4l2.h
Version:1.5
Description:
Created On: 2017-2-21
Modified date:2017-3-14
Author:Sky
*/
#ifndef _YM_V4L2_H
#define _YM_V4L2_H
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
#include <ym_v4l2_config.h>
//open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//memset
#include <string.h>
//v4l2
#include <linux/videodev2.h>
//errno
#include <errno.h>
//perror
#include <stdio.h>
//calloc,free
#include <stdlib.h>
//close
#include <unistd.h>
//ioctl
#include <sys/ioctl.h>
//mmap,munmap
#include <sys/mman.h>
#include <stdarg.h>
#define CLEAR_MEM(x) memset(&(x),0,sizeof(x))
enum yV4l2Cmd{
yVIDIOC_QUERYCAP = 0,//Get Camera Capability
yVIDIOC_ENUM_FMT = 1,//Get Camera all support-format
yVIDIOC_S_FMT = 2,//Set Img Format
yVIDIOC_G_FMT = 3,//Get Img Format
yVIDIOC_REQBUFS = 4,//Req Video buf
yVIDIOC_STREAMON = 5,//Start stream
yVIDIOC_STREAMOFF = 6,//Stop stream
yVIDIOC_S_PARM = 7,//Set Fps info
yVIDIOC_G_PARM = 8,//Get Fps info
yVIDIOC_DQBUF = 9,//delete buf from out queue
yVIDIOC_QBUF = 10,//put buf to in queue
yMMAPTOVEDIOBUF = 11,
yUNMMAPTOVEDIOBUF = 12,
yPUTVEDIOALLBUFTOQUEUE = 13,
};
typedef struct {
void * start;
long len;
} IMG_BUF;
typedef struct ymv4l2{
int camera_fd;//camara file descriptor
IMG_BUF *img_buf;//img buf head
struct v4l2_buffer normal_buf;
struct v4l2_fmtdesc desc_fmt;
struct v4l2_capability cap;
struct v4l2_format stream_fmt;
struct v4l2_requestbuffers reqbuf;
struct v4l2_streamparm stream_parm;
enum v4l2_buf_type type;
}yMV4L2;
int yInitMV4l2(const char * pathname, yMV4L2 * mvl);
int yDestroyMV4l2(yMV4L2 * mvl);
int yIoctlV4l2(enum yV4l2Cmd cmd,...);
//int OpenCamera(const char * pathname);
//int GetCapability(void);
//int GetAllSupportFormat(void);
//int SetFrameInfo(void);//to set fps
//int GetFrameInfo(void);
//int SetImgFormat(void);
//int GetImgFormat(void);
//int RequestVedioBuf(void);
//int MMapToVedioBuf(void);
//int PutVedioBufToQueue(void);
//int UnMMapToVedioBUf(void);
//int StartStream(void);
//int StopStream(void);
//int ConfigV4l2(void);
//int GetVedioBufFromQueue(void);
//int PutVedioBufToQueue(void);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
#endif
后记
无
参考文献
- 无
打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
PS: 请尊重原创,不喜勿喷。
PS: 要转载请注明出处,本人版权所有。
PS: 有问题请留言,看到后我会第一时间回复。
毕设系列之Linux V4L2(图形图像采集篇)的更多相关文章
- v4l2驱动编写篇【转】
转自:http://blog.csdn.net/michaelcao1980/article/details/53008418 大部分所需的信息都在这里.作为一个驱动作者,当挖掘头文件的时候,你可能也 ...
- 【Linux开发】Linux V4L2驱动架构解析与开发导引
Linux V4L2驱动架构解析与开发导引 Andrew按:众所周知,linux中可以采用灵活的多层次的驱动架构来对接口进行统一与抽象,最低层次的驱动总是直接面向硬件的,而最高层次的驱动在linux中 ...
- jvm系列(七):jvm调优-工具篇
16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化.工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗 ...
- 12.Linux软件安装 (一步一步学习大数据系列之 Linux)
1.如何上传安装包到服务器 有三种方式: 1.1使用图形化工具,如: filezilla 如何使用FileZilla上传和下载文件 1.2使用 sftp 工具: 在 windows下使用CRT 软件 ...
- python在linux制作图形界面(snack)
snack是一个用于在linux制作图形界面(GUI)的模块,该模块由c编写,而且redhat的系统都自带这个模块. 1.获取模块 虽然redhat系统会自带这个模块,但是直接去import snac ...
- linux指令大全(完整篇)(转)
http://blog.chinaunix.net/uid-9681606-id-1998590.html linux指令大全(完整篇)(转) 2009-03-17 01:21:46 分类: ...
- 1 weekend110的Linux带图形系统安装 + 网络配置 + 静态IP设置
一.weekend110的Linux带图形系统安装 二.网络配置 明明是配置好的啊,只能说是域名出现问题了, 出现ping:unknow host www.baidu.com的问题解决 解决Ubunt ...
- TCP/IP协议栈源码图解分析系列10:linux内核协议栈中对于socket相关API的实现
题记:本系列文章的目的是抛开书本从Linux内核源代码的角度详细分析TCP/IP协议栈内核相关技术 轻松搞定TCP/IP协议栈,原创文章欢迎交流, byhankswang@gmail.com linu ...
- .NET 并行(多核)编程系列之六 Task基础部分完结篇
原文:.NET 并行(多核)编程系列之六 Task基础部分完结篇 .NET 并行(多核)编程系列之六 Task基础部分完结篇 前言:之前的文章介绍了了并行编程的一些基本的,也注重的讲述了Task的一些 ...
- sed修炼系列(一):花拳绣腿之入门篇
本文为花拳绣腿招式入门篇,主要目的是入门,为看懂sed修炼系列(二):武功心法做准备.虽然是入门篇,只介绍了基本工作机制以及一些选项和命令,但其中仍然包括了很多sed的工作机制细节.对比网上各sed相 ...
随机推荐
- ch583/ch582/ch573/ch571 central(主机)程序
本程序是在CH582m上运行的, 一.主从连接 主机这里可以根据从机的MAC地址进行连接.static uint8_t PeerAddrDef[B_ADDR_LEN] = {0x02, 0x02, 0 ...
- Adoquery.Refresh 慎用。。。。非常严重,会带来各种问题。
adoquery.refresh 各种问题,根本启不到刷新的作用.完全不刷新的节奏. 修改成已经打印后,如果用adoquery.refresh的话,这两个订单 并不会被刷新掉,惨吧......
- TDD学习笔记(二)单元测试
单元测试 定义 单元测试最早来源于Kent Beck,他在开发SmallTalk中引入了这个概念,随着软件工程学的不断发展,使得单元测试已经成为软件编程中一项非常有用的实践. 在维基百科中," ...
- JS实现一个布隆过滤器
之前专门聊过令牌桶算法,而类似的方案还有布隆过滤器.它一般用于高效地查找一个元素是否在一个集合中. 用js实现如下所示: class BloomFilter { constructor(size, h ...
- NVME(学习笔记三)—PMR
PMR(Persistent Memory Region)持久性内存区域 NVM Express在2019年完成了NVMe 1.4规范的制定,新的NVMe协议带来了大量的全新特性,尤其在纠错.强化性能 ...
- 使用CNN实现MNIST数据集分类
1 MNIST数据集和CNN网络配置 关于MNIST数据集的说明及配置见使用TensorFlow实现MNIST数据集分类 CNN网络参数配置如下: 原始数据:输入为[28,28],输出为[1,10] ...
- Js中fetch方法
Js中fetch方法 fetch()方法定义在Window对象以及WorkerGlobalScope对象上,用于发起获取资源的请求,其返回一个Promise对象,这个Promise对象会在请求响应后被 ...
- 文心一言 VS 讯飞星火 VS chatgpt (200)-- 算法导论15.2 4题
四.用go语言,对输入链长度为 n 的矩阵链乘法问题,描述其子问题图:它包含多少个顶点?包含多少条边?这些边分别连接哪些顶点? 文心一言: 矩阵链乘法问题是一个经典的动态规划问题,其中给定一个矩阵链, ...
- Dubbo使用APISIX作为网关
为什么使用网关 Dubbo服务本身没有暴露HTTP接口,客户端(如:Web,APP)无法直接调用其提供的方法. 而APISIX可以通过dubbo-proxy插件为Dubbo服务提供外部访问的HTTP接 ...
- 文件的拓展及文件函数,定义函数及函数参数---day09
1.文件的拓展模式 utf-8 编码格式下,默认一个中文三个字节,一个英文或符号占用一个字节 read() 功能:读取字符的个数(里面的参数代表字符个数) seek() 功能:调整指针的位置(里面的参 ...