v4l2介绍,点击打开链接
下面是我的Demo源码
camera.h
#ifndef _CAMERA_H_
#define _CAMERA_H_
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <asm/types.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
typedef struct _ReqBufInfo
{
void *start;
unsigned int length;
}ReqBufInfo;
typedef enum _PixelFormat
{
PIX_FMT_MJPEG,
PIX_FMT_YUYV
}PixelFormat;
class Camera
{
public:
Camera(const char* dev_name = ""); //! 需要操作的设备名
~Camera();
int openCamera();
int closeCamera();
int camera_query_cap(); //! 查询设备信息
int camera_query_support_format(); //! 查询支持的帧格式类型
int enum_frame_sizes(const int pix_fmt); //! 枚举帧大小
int enum_frame_intervals(int pix_fmt, const int width, const int height); //! 枚举帧间隔
int camera_set_format(const int width, const int height, const int format_t); //! 设置帧格式类型
int camera_get_format(struct v4l2_format &format); //! 获取当前设置的帧格式类型
int camera_set_streamparm(const int fps); //! 设置是视频流属性
int camera_get_streamparm(struct v4l2_streamparm &streamparm); //! 获取视频流属性
int camera_req_buf(const int req_buffer_count); //! 向内核申请帧缓冲
int camera_mmap(const int mmap_buffer_count); //! 把内核空间地址映射到用户空间
int camera_all_put_queue(const int buffer_count); //! 将buffer放入视频输入队列
int camera_start_capture(); //! 开启视频流
int camera_stop_capture(); //! 关闭视频流
int camera_unmmap(const int buffer_count); //! 解除映射关系
int camera_is_read_ready(const int sec, const int usec); //! 测试视频缓存是否可读
int camera_get_a_frame(unsigned char **data, int &datalen); //! 获取一帧数据
private:
int m_fd;
int m_is_open;
char* m_name;
ReqBufInfo *m_req_buf_info;
};
#endif
camera.cpp
#include "camera.h"
static int xioctl(int fd, int request, void *arg)
{
int ret = 0;
while ((ret = ioctl(fd, request, arg)) == -1 &&
errno == EINTR);
return ret;
}
Camera::Camera(const char* dev_name) : m_name(new char[strlen(dev_name)+1])
{
strcpy(m_name, dev_name);
}
Camera::~Camera()
{
if(m_req_buf_info != NULL)
{
free(m_req_buf_info);
m_req_buf_info = NULL;
}
delete [] m_name;
}
int Camera::openCamera()
{
m_fd = open(m_name, O_RDWR);
if(m_fd < 0)
{
perror("open Camera");
return -1;
}
return 0;
}
int Camera::closeCamera()
{
int ret = -1;
ret = close(m_fd);
if(ret < 0)
{
perror("colse Camera");
}
return ret;
}
int Camera::camera_query_cap()
{
struct v4l2_capability cap;
memset(&cap, 0, sizeof(struct v4l2_capability));
if(xioctl(m_fd, VIDIOC_QUERYCAP, &cap) == -1) //! 查询设备信息
{
perror("query capabilities failed");
return -1;
}
//! 打印信息
printf("driver name:\t\t%s\n", cap.driver); //! 驱动名称
printf("dev name:\t\t%s\n", cap.card); //! 设备名字
printf("buf info:\t%s\n", cap.bus_info); //! 设备在系统中的位置
printf("dirve vereion \t%d\n",cap.version); //! 查询驱动版本号
printf("capabilities\t%x\n", cap.capabilities);
if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) //! 是否支持图像获取
{
printf("capabilities:\tsupport capture\n");
}
if((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) //! 是否支持流操作
{
printf("capabilities:\tsupport srteaming\n");
}
return 0;
}
int Camera::camera_query_support_format() //! 查询支持的帧格式类型
{
int ret;
struct v4l2_fmtdesc fmt;
memset(&fmt, 0, sizeof(struct v4l2_fmtdesc));
fmt.index = 0; //! 要查询的格式序号
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //! 帧类型
printf("camera format: \n");
while((ret = xioctl(m_fd, VIDIOC_ENUM_FMT, &fmt)) == 0) //! 循环打印出所有支持的格式
{
fmt.index++;
printf("{ pixelformat = '%c%c%c%c', description = '%s'}\n",
fmt.pixelformat & 0xFF,
(fmt.pixelformat >> 8) & 0xFF,
(fmt.pixelformat >> 16) & 0xFF,
(fmt.pixelformat >> 24) & 0xFF,
fmt.description);
ret = enum_frame_sizes(fmt.pixelformat);
if(ret != 0)
{
printf("Unable to enumerate frame sizes.\n");
}
}
if(errno != EINVAL)
{
printf("ERROR enumerating frame formats: %d\n",errno);
return errno;
}
return 0;
}
int Camera::enum_frame_sizes(const int pix_fmt) //! 枚举帧大小
{
int ret;
struct v4l2_frmsizeenum fsize;
memset(&fsize, 0, sizeof(struct v4l2_frmsizeenum));
fsize.index = 0; //! 要查询所支持的帧大小的对应序号
fsize.pixel_format = pix_fmt; //! 根据他支持的格式去查询支持帧的大小
while((ret = xioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &fsize)) == 0)
{
if(fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) //! 网上查询说UVC驱动固定的这种类型
{
printf("{ discrete: width = %u, height = %u}\n",
fsize.discrete.width, fsize.discrete.height);
ret = enum_frame_intervals(pix_fmt, fsize.discrete.width, //! 枚举帧间隔
fsize.discrete.height);
if(ret != 0)
{
printf("Unable to enumerate frame sizes.\n");
}
}
else if (fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)
{
printf("{ continuous: min { width = %u, height = %u } .. "
"max { width = %u, height = %u } }\n",
fsize.stepwise.min_width, fsize.stepwise.min_height,
fsize.stepwise.max_width, fsize.stepwise.max_height);
printf(" Refusing to enumerate frame intervals.\n");
break;
}
else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
{
printf("{ stepwise: min { width = %u, height = %u } .. "
"max { width = %u, height = %u } / "
"stepsize { width = %u, height = %u } }\n",
fsize.stepwise.min_width, fsize.stepwise.min_height,
fsize.stepwise.max_width, fsize.stepwise.max_height,
fsize.stepwise.step_width, fsize.stepwise.step_height);
printf(" Refusing to enumerate frame intervals.\n");
break;
}
fsize.index++;
}
if(ret != 0 && errno != EINVAL)
{
printf("ERROR enumerating frame size: %d\n", errno);
return errno;
}
return 0;
}
int Camera::enum_frame_intervals(int pix_fmt, const int width, const int height) //! 枚举帧间隔
{
int ret;
struct v4l2_frmivalenum fival;
memset(&fival, 0, sizeof(struct v4l2_frmivalenum));
fival.index = 0; //! 设置对应序号
fival.pixel_format = pix_fmt; //! 设置对应要查询的格式
fival.width = width; //! 设置宽度
fival.height = height; //! 设置高度
printf("\tTime interval between frame");
while((ret = xioctl(m_fd, VIDIOC_ENUM_FRAMEINTERVALS, &fival)) == 0) //! 根据设置内容轮询帧间隔
{
if(fival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
{
printf("%u/%u, ",fival.discrete.numerator, fival.discrete.denominator);
}
else if (fival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
{
printf("{min { %u/%u } .. max { %u/%u } }, ",
fival.stepwise.min.numerator, fival.stepwise.min.numerator,
fival.stepwise.max.denominator, fival.stepwise.max.denominator);
break;
}
else if (fival.type == V4L2_FRMIVAL_TYPE_STEPWISE)
{
printf("{min { %u/%u } .. max { %u/%u } / ""stepsize { %u/%u } }, ",
fival.stepwise.min.numerator, fival.stepwise.min.denominator,
fival.stepwise.max.numerator, fival.stepwise.max.denominator,
fival.stepwise.step.numerator, fival.stepwise.step.denominator);
break;
}
fival.index++;
}
printf("\n\n");
if(ret != 0 && errno != EINVAL)
{
printf("ERROR enumerating frame intervals: %d\n",errno);
return errno;
}
return 0;
}
int Camera::camera_set_format(const int width, const int height, const int format_t) //! 设置帧格式类型
{
struct v4l2_format format;
memset(&format, 0, sizeof(struct v4l2_format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = width;
format.fmt.pix.height = height;
switch(format_t)
{
case PIX_FMT_MJPEG:
format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
break;
case PIX_FMT_YUYV:
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
break;
default:
printf("unsupported video format\n");
return -1;
}
if(xioctl(m_fd, VIDIOC_S_FMT, &format) == -1)
{
perror("set format failed");
return -1;
}
return 0;
}
int Camera::camera_get_format(struct v4l2_format &format) //! 获取当前设置的帧格式类型
{
memset(&format, 0, sizeof(struct v4l2_format));
if(xioctl(m_fd, VIDIOC_G_FMT, &format) == -1)
{
perror("get format failed\n");
return -1;
}
return 0;
}
int Camera::camera_set_streamparm(const int fps) //! 设置是视频流属性
{
struct v4l2_streamparm streamparm;
memset(&streamparm, 0, sizeof(struct v4l2_streamparm));
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
streamparm.parm.capture.timeperframe.numerator = 1;
streamparm.parm.capture.timeperframe.denominator = fps;
if(xioctl(m_fd, VIDIOC_S_PARM, &streamparm) == -1)
{
perror("set fps failed\n");
return -1;
}
return 0;
}
int Camera::camera_get_streamparm(struct v4l2_streamparm &streamparm) //! 获取视频流属性
{
memset(&streamparm, 0, sizeof(struct v4l2_streamparm));
streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(xioctl(m_fd, VIDIOC_G_PARM, &streamparm) == -1)
{
perror("get streamparm failed\n");
return -1;
}
}
int Camera::camera_req_buf(const int req_buffer_count) //! 向内核申请帧缓冲
{
struct v4l2_requestbuffers req;
memset(&req, 0, sizeof(struct v4l2_requestbuffers));
req.count = req_buffer_count;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if(req.count < 2)
{
printf("insufficient buffer memory\n");
return -1;
}
if(xioctl(m_fd, VIDIOC_REQBUFS, &req) == -1)
{
perror("request buffer failed");
return -1;
}
return 0;
}
int Camera::camera_mmap(const int mmap_buffer_count) //! 把内核空间地址映射到用户空间
{
struct v4l2_buffer buf;
m_req_buf_info = (ReqBufInfo *)calloc(mmap_buffer_count, sizeof(ReqBufInfo)); //! 开辟出与内核申请到的缓存大小一直的空间
for(int i = 0; i < mmap_buffer_count; i++) //! 开始循环映射
{
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if(xioctl(m_fd, VIDIOC_QUERYBUF, &buf) == -1)
{
perror("query buffer failed");
return -1;
}
m_req_buf_info[i].length = buf.length;
m_req_buf_info[i].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
m_fd,
buf.m.offset);
if(m_req_buf_info[i].start == MAP_FAILED)
{
perror("mmap buffers failed");
return -1;
}
}
return 0;
}
int Camera::camera_all_put_queue(const int buffer_count) //! 将buffer放入视频输入队列
{
struct v4l2_buffer buf;
for(int i = 0; i < buffer_count; i++) //! 循环将所有缓存块放入
{
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if(xioctl(m_fd, VIDIOC_QBUF, &buf) == -1)
{
perror("queue buffer failed");
return -1;
}
}
return 0;
}
int Camera::camera_start_capture() //! 开启视频流
{
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(xioctl(m_fd, VIDIOC_STREAMON, &type) == -1)
{
perror("stream on failed");
return -1;
}
return 0;
}
int Camera::camera_stop_capture() //! 关闭视频流
{
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(xioctl(m_fd, VIDIOC_STREAMOFF, &type) == -1)
{
perror("stream off failed");
return -1;
}
return 0;
}
int Camera::camera_unmmap(const int buffer_count) //! 解除映射关系
{
for(int i = 0; i < buffer_count; i++)
{
if(munmap(m_req_buf_info[i].start, m_req_buf_info[i].length) == -1)
{
perror("mnumap failed");
return -1;
}
}
return 0;
}
int Camera::camera_is_read_ready(const int sec, const int usec) //! 测试视频缓存是否可读
{
int sel_ret;
fd_set my_set;
FD_ZERO(&my_set);
FD_SET(m_fd, &my_set);
struct timeval tv;
tv.tv_sec = sec;
tv.tv_usec = usec;
sel_ret = select(m_fd + 1, &my_set, NULL, NULL, &tv); //! 阻塞等待一定时间
if(sel_ret == -1)
{
if(errno == EINTR)
{
return 0;
}
return -1;
}
if(sel_ret == 0)
{
printf("select time out\n");
return 0;
}
return 1;
}
int Camera::camera_get_a_frame(unsigned char **data, int &datalen) //! 获取一帧数据
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if(xioctl(m_fd, VIDIOC_DQBUF, &buf))
{
perror("de-queue buffers failed");
return -1;
}
*data = (unsigned char*)m_req_buf_info[buf.index].start;
datalen = m_req_buf_info[buf.index].length;
if(xioctl(m_fd, VIDIOC_QBUF, &buf) == -1)
{
perror("queue buffers failed");
return -1;
}
return 0;
}
main.cpp
#include <stdio.h>
#include "camera.h"
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Invalid argument\n");
exit(EXIT_FAILURE);
}
char *dev_name = argv[1];
Camera *my_camera = new Camera(dev_name);
if(my_camera->openCamera() == -1)
{
exit(EXIT_FAILURE);
}
my_camera->camera_query_cap(); //! 查询
my_camera->camera_query_support_format();
if(my_camera->camera_set_format(640, 480, PIX_FMT_MJPEG) < 0) //! 设置
{
exit(EXIT_FAILURE);
}
if(my_camera->camera_set_streamparm(30) == -1)
{
exit(-1);
}
if( my_camera->camera_req_buf(4) == -1)
{
exit(EXIT_FAILURE);
}
if(my_camera->camera_mmap(4) == -1)
{
exit(-1);
}
if(my_camera->camera_all_put_queue(4) == -1)
{
exit(-1);
}
if(my_camera->camera_start_capture() == -1)
{
exit(-1);
}
int len = 0;
unsigned char *mjpeg;
if(my_camera->camera_is_read_ready(5,0) > 0)
{
printf("start get jpeg\n");
my_camera->camera_get_a_frame(&mjpeg, len);
int file = open("./temp.jpg",O_CREAT | O_RDWR);
if(file < 0)
{
printf("file is error\n");
my_camera->camera_unmmap(4);
my_camera->closeCamera();
exit(-1);
}
write(file,mjpeg,len); //! 数据写入文件
my_camera->camera_unmmap(4);
printf("write is succeed\n");
close(file);
}
my_camera->closeCamera();
delete my_camera;
return 0;
}
实现效果是打开摄像头采一幅图,保存成文件。如有错误请指正,一起学习