Linux下v4l2采集编程笔记
来源:互联网 发布:读圣经软件 编辑:程序博客网 时间:2024/05/17 04:08
Linux下v4l2采集编程笔记
采集图片
1打开视频设备文件
STATUS open_dev()
{
int fd;
fd = open("/dev/video0",O_RDWR);
if(fd<0)
{
perror("open video device faild:");
exit(-1);
}
else
printf("open :sucess!\n");
return fd;
}
2获取设备相关信息:
在结构体:v4l2_capability中
附type定义:
定义一个结构体变量cap
通过ioctl()函数:
SYNOPSIS
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
第二个参数从上面填写;比如我们要获取的是设备的信息,结构体是v4l2_cappability那么应该是 VIDIOC_QUERYCAP
代码:
/*
* 获取设备相关属性*
*
*/
void get_dev_info(int fd)
{
struct v4l2_capability cap;
int ret;
ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(-1 == ret )
{
perror("v4l2_capability ioctl faild:");
exit(-1);
}
/*成功绑定,获取到信息*/
printf("---------------------------------\n");
printf("采集摄像头相关信息:\n");
printf("驱 动:%s\n",cap.driver);
printf("设 备:%s\n",cap.card);
printf("接口信息:%s\n",cap.bus_info);
printf("版 本:%x\n",cap.version);
printf("capabilities: 0x%08X\n",cap.capabilities);
//printf("reserved:%d\n",cap.reserved);
printf("---------------------------------\n");
}
3设置视频的制式
Viodeodev.h 629-729行:
/*
* A N A L O G V I D E O S T A N D A R D模拟视频标准
*/
typedef __u64 v4l2_std_id;
/* one bit for each */
#define V4L2_STD_PAL_B ((v4l2_std_id)0x00000001)
#define V4L2_STD_PAL_B1 ((v4l2_std_id)0x00000002)
#define V4L2_STD_PAL_G ((v4l2_std_id)0x00000004)
#define V4L2_STD_PAL_H ((v4l2_std_id)0x00000008)
#define V4L2_STD_PAL_I ((v4l2_std_id)0x00000010)
#define V4L2_STD_PAL_D ((v4l2_std_id)0x00000020)
#define V4L2_STD_PAL_D1 ((v4l2_std_id)0x00000040)
#define V4L2_STD_PAL_K ((v4l2_std_id)0x00000080)
#define V4L2_STD_PAL_M ((v4l2_std_id)0x00000100)
#define V4L2_STD_PAL_N ((v4l2_std_id)0x00000200)
#define V4L2_STD_PAL_Nc ((v4l2_std_id)0x00000400)
#define V4L2_STD_PAL_60 ((v4l2_std_id)0x00000800)
#define V4L2_STD_NTSC_M ((v4l2_std_id)0x00001000)
#define V4L2_STD_NTSC_M_JP ((v4l2_std_id)0x00002000)
#define V4L2_STD_NTSC_443 ((v4l2_std_id)0x00004000)
#define V4L2_STD_NTSC_M_KR ((v4l2_std_id)0x00008000)
#define V4L2_STD_SECAM_B ((v4l2_std_id)0x00010000)
#define V4L2_STD_SECAM_D ((v4l2_std_id)0x00020000)
#define V4L2_STD_SECAM_G ((v4l2_std_id)0x00040000)
#define V4L2_STD_SECAM_H ((v4l2_std_id)0x00080000)
#define V4L2_STD_SECAM_K ((v4l2_std_id)0x00100000)
#define V4L2_STD_SECAM_K1 ((v4l2_std_id)0x00200000)
#define V4L2_STD_SECAM_L ((v4l2_std_id)0x00400000)
#define V4L2_STD_SECAM_LC ((v4l2_std_id)0x00800000)
/* ATSC/HDTV */
#define V4L2_STD_ATSC_8_VSB ((v4l2_std_id)0x01000000)
#define V4L2_STD_ATSC_16_VSB ((v4l2_std_id)0x02000000)
/* FIXME:
Although std_id is 64 bits, there is an issue on PPC32 architecture that
makes switch(__u64) to break. So, there's a hack on v4l2-common.c rounding
this value to 32 bits.
As, currently, the max value is for V4L2_STD_ATSC_16_VSB (30 bits wide),
it should work fine. However, if needed to add more than two standards,
v4l2-common.c should be fixed.
*/
/* some merged standards */
#define V4L2_STD_MN (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)
#define V4L2_STD_B (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)
#define V4L2_STD_GH (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)
#define V4L2_STD_DK (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)
/* 一些常见的需要的东西 */
#define V4L2_STD_PAL_BG (V4L2_STD_PAL_B |\
V4L2_STD_PAL_B1 |\
V4L2_STD_PAL_G)
#define V4L2_STD_PAL_DK (V4L2_STD_PAL_D |\
V4L2_STD_PAL_D1 |\
V4L2_STD_PAL_K)
#define V4L2_STD_PAL (V4L2_STD_PAL_BG |\
V4L2_STD_PAL_DK |\
V4L2_STD_PAL_H |\
V4L2_STD_PAL_I)
#define V4L2_STD_NTSC (V4L2_STD_NTSC_M |\
V4L2_STD_NTSC_M_JP |\
V4L2_STD_NTSC_M_KR)
#define V4L2_STD_SECAM_DK (V4L2_STD_SECAM_D |\
V4L2_STD_SECAM_K |\
V4L2_STD_SECAM_K1)
#define V4L2_STD_SECAM (V4L2_STD_SECAM_B |\
V4L2_STD_SECAM_G |\
V4L2_STD_SECAM_H |\
V4L2_STD_SECAM_DK |\
V4L2_STD_SECAM_L |\
V4L2_STD_SECAM_LC)
#define V4L2_STD_525_60 (V4L2_STD_PAL_M |\
V4L2_STD_PAL_60 |\
V4L2_STD_NTSC |\
V4L2_STD_NTSC_443)
#define V4L2_STD_625_50 (V4L2_STD_PAL |\
V4L2_STD_PAL_N |\
V4L2_STD_PAL_Nc |\
V4L2_STD_SECAM)
#define V4L2_STD_ATSC (V4L2_STD_ATSC_8_VSB |\
V4L2_STD_ATSC_16_VSB)
#define V4L2_STD_UNKNOWN 0
#define V4L2_STD_ALL (V4L2_STD_525_60 |\
V4L2_STD_625_50)
struct v4l2_standard {
__u32 index;
v4l2_std_id id;
__u8 name[24];
struct v4l2_fract frameperiod; /* Frames, not fields */
__u32 framelines;
__u32 reserved[4];
};
根据上图选择模拟视频标准制式;我们这里选择V4L2_STD_PAL;
通过自定义类型定义一个变量 stdid= V4L2_STD_PAL
Ioctl();设置制式;
4设置帧格式
/*
v4l2_format 结构如下:
struct v4l2_format
{
enum v4l2_buf_type type; // 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
union
{
struct v4l2_pix_format pix;
struct v4l2_window win;
struct v4l2_vbi_format vbi;
__u8 raw_data[200];
} fmt;
};
struct v4l2_pix_format
{
__u32 width; // 宽,必须是16 的倍数
__u32 height; // 高,必须是16 的倍数
__u32 pixelformat; // 视频数据存储类型,例如是YUV 4 :2 :2 还是RGB
enum v4l2_field field;
__u32 bytesperline;
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv;
};
*/
/*
*设置视频帧格式 帧的格式个包括宽度和高度等。
*
*/
void set_frame_format(int fd)
{
int ret;
struct v4l2_fmtdesc fmtdesc;
memset(&fmtdesc,0,sizeof(fmtdesc));
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while((ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)) == 0)
{
fmtdesc.index ++ ;
/* printf("{pixelformat = %c%c%c%c},description = '%s'\n",
fmtdesc.pixelformat & 0xff,(fmtdesc.pixelformat >> 8)&0xff,
(fmtdesc.pixelformat >> 16) & 0xff,(fmtdesc.pixelformat >> 24)&0xff,
fmtdesc.description);
*/
}
struct v4l2_format fmt;
//以下初始化结构体变量fmt
memset(&fmt , 0 , sizeof(fmt));//清0
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat =V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if((ioctl(fd,VIDIOC_S_FMT,&fmt))<0)
{
perror("设置帧格式:");
//return -1;
}
else
printf("设置帧格式成功(JPEG)......\n");
}
5申请帧缓冲
01551 :#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers)
/*
*向驱动申请帧缓冲
*
*/
void get_frame_format(int fd)
{
int ret;
struct v4l2_requestbuffers req;
memset(&req , 0 ,sizeof(req));
req.count = 10;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd,VIDIOC_REQBUFS,&req);
if(ret<0)
{
printf("get video sys faild\n");
//return -1;
}
n_buffer = req.count;//传给全局变量,申请内存的时候需要用到呢
usr_buf = calloc(req.count,sizeof(*usr_buf));
printf("n_buf:%d\n",n_buffer);
}
6申请物理内存并映射到申请的设备内存中
那么应用程序直接操作物理内存就等译操作申请到的设备内存
01552:#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer)
映射内存
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
/*
*申请内存(物理)
*
*///
STATUS mmap_memory(int fd)
{
int ret;
int i;
memset(&buf , 0 ,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
for(i = 0; i < n_buffer ; i++)
{
buf.index = i; //下标
ret = ioctl(fd , VIDIOC_QUERYBUF ,&buf);
if(-1 == ret)
{
printf("set video sys faild\n");
return FALSE;
}
//内存映射:
usr_buf[i].length = buf.length;
usr_buf[i].start = mmap(NULL , buf.length , PROT_READ|PROT_WRITE, MAP_SHARED , fd , buf.m.offset);
if (usr_buf[i].start == MAP_FAILED)
{
perror("buffers error\n");
return FALSE;
}
if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
{
printf("VIDIOC_QBUF error\n");
return FALSE;
}
}//end of for
return TRUE;
}
7开始采集视频:
01558:#define VIDIOC_STREAMON _IOW('V', 18, int)
//代码:
void mmap_memory(int fd)
{
int ret;
int i;
memset(&buf , 0 ,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
for(i = 0; i < n_buffer ; i++)
{
buf.index = i; //下标
ret = ioctl(fd , VIDIOC_QUERYBUF ,&buf);
if(-1 == ret)
{
printf("set video sys faild\n");
//return -1;
}
//内存映射:
usr_buf[i].length = buf.length;
usr_buf[i].start = mmap(NULL , buf.length , PROT_READ|PROT_WRITE, MAP_SHARED , fd , buf.m.offset);
if (usr_buf[i].start == MAP_FAILED)
{
perror("buffers error\n");
//return -1;
}
//映射内存后要进行一次入列的操作
if (ioctl (fd, VIDIOC_QBUF, &buf) < 0)
{
printf("VIDIOC_QBUF error\n");
//return -1;
}
}//end of for
printf("1111index = %d\n",buf.index);
}
8保存图像:
int save_image(void *addr,int length)
{
FILE *fp;
int num=0;
char picture_name[20];
sprintf(picture_name,"picture%d.jpg",pic_num++);
fp = fopen(picture_name,"w");
if(NULL == fp)
{
perror("Open picture_name faild!");
exit(-1);
}
fwrite(addr,length,1,fp);
usleep(100);
fclose(fp);
return 0;
}
放进队列
/*
*入列
*/
STATUS into_query(int fd)
{
if(-1 == (ioctl(fd,VIDIOC_QBUF,&buf)))
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
return FALSE;
}
取出队列
/*
*
*出队列取得已采集数据的帧缓冲,取得原始采集数据
*
*/
STATUS get_query(int fd)
{
int ret;
if(-1 == (ioctl(fd,VIDIOC_DQBUF,&buf)))
{
perror("Fail to ioctl 'VIDIOC_DQBUF'");
return FALSE;
}
return TRUE;
}
注意:保存的时候要通过出列,保存,入列的过程;这里通过封装一个函数实现
过程:先出列 保存 然后再如入列
映射内存的完成后已经进行了入列的处理一次了!!!!
//读取一帧图片保存:
void read_framt(int fd)
{
// 出列
printf("初始下标:index= %d\n",buf.index);//4
if(-1 == ioctl(fd,VIDIOC_DQBUF,&buf))
{
perror("Fail to ioctl 'VIDIOC_DQBUF'");
exit(EXIT_FAILURE);
}
//assert(buf.index < n_buffer);
//保存
save_image(usr_buf[buf.index].start,usr_buf[buf.index].length);
//入列
printf("保存下标为 :index= %d\n",buf.index);
if(-1 == ioctl(fd,VIDIOC_QBUF,&buf))
{
perror("Fail to ioctl 'VIDIOC_QBUF'");
exit(EXIT_FAILURE);
}
}
9关闭流:
void set_off_stream(int fd)
{
int ret;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
ret = ioctl(fd, VIDIOC_STREAMOFF ,&type);
if (ret == -1)
{
perror("streamoff error\n");
//return -1;
}
}
10关闭映射释放申请的内存
void uninit_camer_device()
{
unsigned int i;
for(i = 0;i < n_buffer;i ++)
{
if(-1 == munmap(usr_buf[i].start,usr_buf[i].length))
{
exit(EXIT_FAILURE);
}
}
free(usr_buf);
}
11关闭设备:
close_dev(dev_fd);
捕获图片简单过程图
有关保存的时候出列的操作分析:
我们假设我们的申请的设备缓冲区count为5:
那么也就是说我们的换冲队列的大小也是5帧大小
在映射的时候已经进行了一次的入列操作,那么我们的下标默认开始是顶部了也就是4:
分析过程流程图:
采集视频流格式:
压缩:YUYV-àjpeg
1设置帧格式的参数:
改为fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
2安装jpeg库
·准备:需要安装第三方库jpeg;在开发板上运行的话需要交叉编译jpeg库;
·测试:pc 安装jpeg库 移植jpeg库到板子
3添加头文件:#include <jpeglib.h>
4修改makefile,因为使用了第三方库
5压缩函数如下:(在读取帧的函数中可以读取 一样的顺序出列---处理压缩(保存)---入列)
/*
*压缩 YUYV到 JPEG
*/
//绿色的为测试保存在当前目录的文件,实际上我们需要通过第二个参数的buf指针指向的申请的缓冲区保存;可以是连续帧得以形成录像
int compress_yuyv_to_jpeg(BUFTYPE *frame_buf, unsigned char *buffer, int size)
//frame_buf是指采集的流,buffer是指需要保存的位置;size是大小;
{
unsigned char *line_buffer ,*yuyv ,*row_pointer[1];
FILE *fp;//这里测试保存在当前目录的文件流指针
/*1申请并初始化jpeg压缩对象,同时要指定错误处理器*/
struct jpeg_compress_struct cinfo; //定义一个压缩信息结构体
struct jpeg_error_mgr jerr; //声明错误处理器,并赋值给 cinfo.err域
cinfo.err = jpeg_std_error(&jerr);
//创建压缩对象,并初始化
jpeg_create_compress(&cinfo);
/*2指定输出数据的传输位置,传输数据大小 */
//jpeg_mem_dest(&cinfo,&buffer,&size);// buffer 保存处理后的数据 size大小
/*2 指定压缩后的图像所存放的目标文件,注意,目标文件应以二进制模式打开 */
fp = fopen("test.jpeg","wb");
if(NULL ==fp)
{
perror(" 2" );
return 0;
}
jpeg_stdio_dest(&cinfo,fp);
/*设置压缩参数*/
cinfo.image_height = PIX_HEIGHT;
cinfo.image_width = PIX_WIDTH;
cinfo.input_components = 3; //3//3表示3原色 1表示灰色
cinfo.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像
jpeg_set_defaults(&cinfo); //将设置的参数加入到默认参数中;
jpeg_set_quality(&cinfo,80,TRUE);//设置压缩质量,压缩比在0-100间;true表示压缩 false表示不压缩
/*4 开始压缩*/
jpeg_start_compress(&cinfo,TRUE);
//JSAMPARRAY row_pointer[1];//一行位图
int z;
z = 0;
//对每一行进行压缩
line_buffer = calloc(PIX_WIDTH*3,1);//每一行字节数
yuyv = frame_buf->start;
while(cinfo.next_scanline < cinfo.image_height)
{
int x;
unsigned char *ptr = line_buffer;//吧yuyv数据赋值给ptr指针
printf("compress start ......\n");
/*YUYV转RGB格一行一行处理*/
for(x = 0 ;x < PIX_WIDTH ; x++)
{
int r,g,b;
int y,u,v;
if ( !z )
y = yuyv[0] << 8;
else
y = yuyv[2] <<8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
r = (y + (359 * v)) >>8;
g = (y - (88 * u) -(183 * v)) >>8;
b = (y+(454* u )) >> 8;
*(ptr ++) = (r > 255) ? 255 : ((r<0) ? 0 : r);
*(ptr ++) = (g > 255) ? 255 : ((g<0) ? 0 : g);
*(ptr ++) = (b > 255) ? 255 : ((b<0) ? 0 : b);
if (z ++)
{
z = 0;
yuyv +=4;
}
}
row_pointer[0] = line_buffer;
jpeg_write_scanlines(&cinfo , row_pointer , 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
free(line_buffer);
return 0;
}
- Linux下v4l2采集编程笔记
- v4l2视频采集笔记
- Linux下的V4L2编程
- Linux下V4L2编程小结
- Linux下V4L2编程小结
- Linux下Camera编程--V4L2
- linux 视频采集v4l2
- Linux V4L2摄像头采集
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- 基于Linux视频驱动接口V4L2视频采集编程
- linux下usb camera图像采集(V4l2)
- 嵌入式LINUX环境下视频采集知识-V4L2,原理
- MYSQL查询某字段中以逗号分隔的字符串的方法
- svg translate 操作
- ORACLE常用监控语句(未完待续)
- cocos2d-x按钮CCControlButton的用法
- struts hibernate spring easyui 报修系统
- Linux下v4l2采集编程笔记
- 利用vbs设置Java环境变量
- ubuntu中使用SQuirreL连接sql server
- ASIHTTPRequest类库简介和使用说明
- Linux on android(ubuntu12.04)在三星i9250和i9300上的实验
- div +css 纵向导航
- HDU 3118 Arbiter(枚举)
- 准程序员必须知道的内存那点事儿----指针域数组的对比
- Phpcms V9列表分页自定义页码文字方法