Linux下V4L2使用流程
测试平台:vm15 ubuntu20.04
测试摄像头:小米笔记本电脑自带摄像头、优迈Q5 摄像头
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#define V4L2_PATH "/dev/video0"
#define REQBUFFER_COUNT 4
#define PIX_WIDTH 640
#define PIX_HEIGHT 480
//用户保存数据区域
typedef struct
{
unsigned char *start;
int lenth;
}UserBuffer;
/**********************************
* VIDIOC_ENUM_FMT:
* VIDIOC_QUERYCAP:
* VIDIOC_S_FMT:
* VIDIOC_G_FMT:
* VIDIOC_REQBUFS:
* VIDIOC_QUERYBUF:
* VIDIOC_QBUF:
* *******************************/
//打开设备文件
int v4l2_open(const char *pathname);
//关闭文件
int v4l2_close(const int fd);
//获取摄像头信息
int get_v4l2_info(const int fd);
//获取摄像头支持格式
int get_supported_format(const int fd);
//设置摄像头采集格式
int set_v4l2_format(const int fd);
//获取摄像头采集格式
int get_v4l2_format(const int fd);
//映射内核空间
int Mapping_kernel_space(const int fd, UserBuffer *buf);
//开始摄像头采集
int Start_cllection(const int fd);
//开始数据采集
int Data_collection(const int fd, int *Datalenth, int *index);
//停止摄像头采集
int Stop_cllection(const int fd);
//保存为jpg
int Save_data_to_JPG(const char *path, unsigned char *Data, int Datalenth);
//释放内核映射空间
int Free_kernel_mapping_space(UserBuffer *buf);
//保存至YUYV
int Save_data_to_YUYV(const char *path, unsigned char *Data, int Datalenth);
int main(int argc, char *argv[])
{
UserBuffer Buf[4];
int v4l2_fd = v4l2_open(V4L2_PATH);
if(v4l2_fd == -1)return -1;
get_v4l2_info(v4l2_fd);
get_supported_format(v4l2_fd);
set_v4l2_format(v4l2_fd);
get_v4l2_format(v4l2_fd);
Mapping_kernel_space(v4l2_fd, Buf);
Start_cllection(v4l2_fd);
int count = 0;
while(1)
{
count ++;
if(count > 30*10)break;
usleep(1000000/30);
int length = 0;
int index = 0;
Data_collection(v4l2_fd, &length, &index);
Save_data_to_YUYV("test.yuv", Buf[index].start, Buf[index].lenth);
}
Stop_cllection(v4l2_fd);
Free_kernel_mapping_space(Buf);
v4l2_close(v4l2_fd);
return 0;
}
//打开设备文件
int v4l2_open(const char *pathname)
{
int v4l2_fd = open(pathname, O_RDWR);
if(v4l2_fd < 0)
{
perror("Open v4l2");
return -1;
}
return v4l2_fd;
}
//关闭文件
int v4l2_close(const int fd)
{
int ret = close(fd);
if(ret == -1)
{
perror("Close c4l2");
return -1;
}
return 0;
}
//获取摄像头信息
int get_v4l2_info(const int fd)
{
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if(ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
perror("get_v4l2_info");
return -1;
}
puts("\n--------------------");
printf("v4l2 info:\n");
printf("Driver Name:%s\n", cap.driver);
printf("Card Name:%s\n", cap.card);
printf("Bus_info:%s\n", cap.bus_info);
printf("Version:%u.%u.%u\n", (cap.version >> 16) & 0XFF, (cap.version >> 8) & 0XFF, cap.version & 0XFF);
return 0;
}
//获取摄像头支持格式
int get_supported_format(const int fd)
{
struct v4l2_fmtdesc fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int i = 0;
puts("\n--------------------");
printf("v4l2 supported format:\n");
while(1)
{
fmt.index = i++;
if(ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1)
{
return 0;
}
printf("Description[%d]:%s\n", i, fmt.description);
unsigned char *p = (unsigned char *)&fmt.pixelformat;
printf("Pixelformat[%d]:%c%c%c%c\n", i, p[0], p[1], p[2], p[3]);
}
return 0;
}
//设置摄像头采集格式
int set_v4l2_format(const int fd)
{
struct v4l2_format vfmt;
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
vfmt.fmt.pix.width = PIX_WIDTH;//设置宽
vfmt.fmt.pix.height = PIX_HEIGHT;//设置高
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//设置视频采集格式
if(ioctl(fd, VIDIOC_S_FMT, &vfmt) == -1)
{
perror("set v4l2 format error");
return -1;
}
return 0;
}
//获取摄像头采集格式
int get_v4l2_format(const int fd)
{
struct v4l2_format vfmt;
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集
if(ioctl(fd, VIDIOC_G_FMT, &vfmt) == -1)
{
perror("get v4l2 format error");
return -1;
}
puts("\n--------------------");
printf("v4l2 format:\n");
printf("Pix:%d*%d\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height);
unsigned char *p = (unsigned char *)&vfmt.fmt.pix.pixelformat;
printf("Pixelformat:%c%c%c%c\n", p[0], p[1], p[2], p[3]);
return 0;
}
//映射内核空间
int Mapping_kernel_space(const int fd, UserBuffer *buf)
{
//申请内核空间
struct v4l2_requestbuffers reqbuffer;
memset(&reqbuffer, 0, sizeof(reqbuffer));
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = REQBUFFER_COUNT;//申请缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP;//映射方式
printf("fd = %d\n", fd);
if(ioctl(fd, VIDIOC_REQBUFS, &reqbuffer) == -1)
{
perror("Queue application failed");
return -1;
}
//映射
struct v4l2_buffer mapbuffer;
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
int i = 0;
for(i=0; i<4; i++)
{
mapbuffer.index = i;
if(ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer) == -1)
{
perror("Failed to query the kernel space queue. Procedure");
return -1;
}
buf[i].start = (unsigned char*)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, mapbuffer.m.offset);
buf[i].lenth = mapbuffer.length;
if(ioctl(fd, VIDIOC_QBUF, &mapbuffer) == -1)
{
perror("v4l2 vidioc_qbuf");
return -1;
}
}
puts("\n--------------------");
printf("v4l2 mmap successed !!!\n");
return 0;
}
//开始摄像头采集
int Start_cllection(const int fd)
{
//开启采集
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd, VIDIOC_STREAMON, &type) == -1)
{
perror("Start Error");
return -1;
}
puts("\n--------------------");
printf("Start cllection successed !!!\n");
return 0;
}
//数据采集
int Data_collection(const int fd, int *Datalenth, int *index)
{
//从队列中提取一帧数据
struct v4l2_buffer readbuffer;
memset(&readbuffer, 0, sizeof(readbuffer));
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
readbuffer.memory = V4L2_MEMORY_MMAP;
if(ioctl(fd, VIDIOC_DQBUF, &readbuffer) == -1)
{
perror("Collecthion Error");
return -1;
}
*Datalenth = readbuffer.bytesused;
*index = readbuffer.index;
//通知内核使用完毕
if(ioctl(fd, VIDIOC_QBUF, &readbuffer) == -1)
{
perror("VIDIOC_QBUF ERROR");
return -1;
}
return 0;
}
//停止摄像头采集
int Stop_cllection(const int fd)
{
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(fd, VIDIOC_STREAMOFF, &type) == -1)
{
perror("Stop cllection");
return -1;
}
puts("\n--------------------");
printf("Stop cllection successed !!!\n");
return 0;
}
//保存图片
int Save_data_to_JPG(const char *path, unsigned char *Data, int Datalenth)
{
int fd = open(path, O_RDWR|O_CREAT, 0664);
if(fd < 0)
{
perror("Open save path error");
return -1;
}
if(write(fd, Data, Datalenth)<0)
{
perror("Save ERROR");
return -1;
}
puts("\n--------------------");
printf("Save successed !!!\n");
return 0;
}
//释放内核映射空间
int Free_kernel_mapping_space(UserBuffer *buf)
{
int i = 0;
for(i=0; i<4; i++)
{
if(munmap(buf[i].start, buf[i].lenth) == -1)
{
perror("Free kernel");
return -1;
}
buf[i].start = NULL;
}
puts("\n--------------------");
printf("Free kernel successed !!!\n");
}
//保存至YUYV
int Save_data_to_YUYV(const char *path, unsigned char *Data, int Datalenth)
{
int fd = open(path, O_RDWR|O_CREAT|O_APPEND, 0666);
if(fd < 0)
{
perror("Open YUYV error");
return -1;
}
if(write(fd, Data, Datalenth) < 0)
{
perror("Save YUYV ERROR");
return -1;
}
return 0;
}