利用V4L2架构 实现从笔记本摄像头拍摄一张照片写入本地 一

一下内容实现环境:

Ubuntu 12.04+libjpeg62-dev,采用vi编写,gcc/g++编译,当然一下程序均使用Makefile统一管理。

首先第一点:           我们得明白我们的V4L2架构的基本信息:Video4linux2(简称 V4L2),是 linux 中关于视频设备的内核驱动。在 Linux 中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0 下,以下用到的结构提也都定义在/usr/include/linux/videodev2.h头文件下。

我们的步骤:

1. 打开设备文件。 int fd=open(”/dev/video0′′,O_RDWR);
2. 取得设备的 capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_
QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括 PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 开始视频的采集。VIDIOC_STREAMON
6. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
7. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
8. 停止视频的采集。VIDIOC_STREAMOFF
9. 关闭视频设备。close(fd)

第一步:首先是要查询你笔记本的相关的摄像头信息,这里我把它做成了一个模块,用于查询摄像头的相关信息,定义一个文件:query.h 摄像头查询模块接口文件,定义三个方法:query_cap(int fd);查询摄像头参数及驱动信息,

query_fps(int fd);VIDIOC_G_PARM查询帧率信息,

query_fmt(int fd);使用VIDIOC_ENUM_FMT查询摄像头信息

下面我们分别实现这三个方法:

/**
* @brief query_cap
* 查询摄像头参数及驱动信息:打印struct v4l2_capability
* @param fd 摄像头文件描述符
*
*/
void query_cap(int fd)
{

Cap cap;
CLEAR(cap);
if(ioctl(fd,VIDIOC_QUERYCAP,&cap) < 0) //查询摄像头的功能
{
perror(“query_cap()”); //也是出错处理
exit(EXIT_FAILURE);
}
printf(“===============Camera Info===============\n”);
printf(“Driver: %s \n”,cap.driver);
printf(“Card: %s \n”,cap.card);
printf(“BusInfo: %s \n”,cap.bus_info);
printf(“Version: %d \n”,cap.version);
printf(“Capabilities: 0x%x \n”,cap.capabilities);
//printf(“Device Cap: 0x%x \n”,cap.device_caps);
//printf(“Reserved: 0x%x \n”,cap.reserved);
}

 
/**
* @brief query_fps
* 使用VIDIOC_G_PARM查询帧率信息
* @param fd
*/
void query_fps(int fd)
{
Fps fps;
CLEAR(fps);
fps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置类型
//ioctl(fd,VIDIOC_G_PARM,&fps);
if(ioctl(fd,VIDIOC_G_PARM,&fps) < 0)
{
fprintf(stderr,”Usage:query_fps:%s error\n”,strerror(errno));
exit(EXIT_FAILURE);
}
printf(“=======================Camera Fps==============\n”);
printf(“Type:%d \n”,fps.type);
printf(“Capture->capability:%d \n”,fps.parm.capture.capability);
printf(“Capture->capturemode:%d \n”,fps.parm.capture.capturemode); //1为高清模式
printf(“Capture->captureparm->timeperframe->numerator:%d \n”,\
fps.parm.capture.timeperframe.numerator); //默认为1
printf(“Capture->captureparm->timeperframe->denominator:%d \n”,\
fps.parm.capture.timeperframe.denominator); //帧率
printf(“Capture->extendedmode:%d \n”,fps.parm.capture.extendedmode);
printf(“Capture->readbuffer:%d \n”,fps.parm.capture.readbuffers);

//printf(“Capture->outputparm->\n”,fps.parm.output.);
}

 

 

/**
* @brief query_fmt
* 使用VIDIOC_ENUM_FMT查询摄像头信息
* @param fd
*/
void query_fmt(int fd)
{
Fmtd fmtd;
CLEAR(fmtd);
fmtd.index = 0;
fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf(“================Fmtd Info================\n”);
while(-1 != ioctl(fd,VIDIOC_ENUM_FMT,&fmtd))
{
printf(“query fmtd %d %s\n”,fmtd.index,fmtd.description);
fmtd.index++;
}
}

实现以上三个函数之后我们就可以编写一个测试函数了:cam_test.c

int main(int argc,char *argv[])
{

int fd = cam_open(argc,argv); //打开摄像头
//query_cap(fd);
//query_fps(fd);
//query_fmt(fd);

cam_close(fd);
return 0;

}

还有就是以上使用的结构体都预先定义在文件:common.h

#ifndef __COMMON_H_
#define __COMMON_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/videodev2.h>
#include <jpeglib.h>

#define CLEAR(x) memset(&x,0,sizeof(x))
typedef enum
{
MJPEG = V4L2_PIX_FMT_MJPEG,
YUYV = V4L2_PIX_FMT_YUYV
}fmt_t;
#define WIDTH 640
#define HEIGHT 480

typedef struct v4l2_capability Cap; //
typedef struct v4l2_format Fmt; //
typedef struct v4l2_fmtdesc Fmtd; //
typedef struct v4l2_streamparm Fps; //
typedef struct v4l2_requestbuffers ReqBuf; //
typedef struct v4l2_buffer Vbuf; //

#endif