/*
打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法(缓冲 区管理)-> 循环获取数据-> 关闭设备。
https://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVNAME "/dev/video0"
// void signal_hander(int sig)
// {
// printf("----------signal_hander---------\n");
// //goto lable;
// }
int main()
{
//signal(SIGINT,signal_hander);
//1. 打开设备
int fd=open(DEVNAME,O_RDWR);
if(fd < 0) {
printf("open dev fail\n");
return -1;
}
//2.查询设备属性: VIDIOC_QUERYCAP
struct v4l2_capability cap;
int request;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if(ret < 0) {
printf("ioctl fail\n");
}
printf("Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n"
,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF);
//3.设置视频的制式和帧格式(制式包括PAL,NTSC,帧的格式个包括宽度和高度等)。
//3.1查询并显示所有支持的格式: VIDIOC_ENUM_FMT
/*struct v4l2_fmtdesc
{
u32 index; // 要查询的格式序号,应用程序设置
enum v4l2_buf_type type; // 帧类型,应用程序设置
u32 flags; // 是否为压缩格式
u8 description[32]; // 格式名称
u32 pixelformat; // 格式
u32 reserved[4]; // 保留
};
*/
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
{
printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
//3.2配置摄像头采集格式
//查看或设置当前格式: VIDIOC_G_FMT, VIDIOC_S_FMT
/*
struct v4l2_format
{
enum v4l2_buf_type type; // 帧类型,应用程序设置
union fmt
{
struct v4l2_pix_format pix; // 视频设备使用
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
u8 raw_data[200];
};
};
struct v4l2_pix_format
{
u32 width; // 帧宽,单位像素
u32 height; // 帧高,单位像素
u32 pixelformat; // 帧格式
enum v4l2_field field;
u32 bytesperline;
u32 sizeimage;
enum v4l2_colorspace colorspace;
u32 priv;
};
*/
struct v4l2_format vfmt;
vfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置类型摄像头采集
#if 0
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //根据摄像头设置格式
vfmt.fmt.pix.width = 1280;
vfmt.fmt.pix.height = 720;
#else
//vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264; //根据摄像头设置格式
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //根据摄像头设置格式
//vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; //根据摄像头设置格式
// vfmt.fmt.pix.width = 640;
// vfmt.fmt.pix.height = 480;
#endif
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
printf("---------设置类型摄像头采集失败--------\n");
return -1;
}
printf("Current data format information:\n\twidth:%d\n\theight:%d\n", vfmt.fmt.pix.width,vfmt.fmt.pix.height);
#if 1
{
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
if(fmtdesc.pixelformat & vfmt.fmt.pix.pixelformat)
{
printf("\tformat:%s\n",fmtdesc.description);
break;
}
fmtdesc.index++;
}
}
#endif
#if 0
//3.3检查是否支持某种帧格式
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1) {
if(errno==EINVAL) {
printf("not support format RGB32!\n");
}
}
#endif
//4.图像的缩放 VIDIOC_CROPCAP
/*
int ioctl(int fd, int request, struct v4l2_cropcap *argp);
int ioctl(int fd, int request, struct v4l2_crop *argp);
int ioctl(int fd, int request, const struct v4l2_crop *argp);
*/
//5.VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 用来查询和选则当前的 input,一个 video 设备 节点可能对应多个视频源,
//比如 saf7113 可以最多支持四路 cvbs 输入,如果上层想在四 个cvbs视频输入间切换,那么就要调用 ioctl(fd, VIDIOC_S_INPUT, &input) 来切换。
//VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回当前的 video input和output的index.
//6. 申请和管理缓冲区
//4.申请内核空间
//应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping) 和用户指针。
//向设备申请缓冲区 VIDIOC_REQBUFS
/*相关函数:int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
相关结构体:
struct v4l2_requestbuffers
{
u32 count; // 缓冲区内缓冲帧的数目
enum v4l2_buf_type type; // 缓冲帧数据格式
enum v4l2_memory memory; // 区别是内存映射还是用户指针方式
u32 reserved[2];
};
enum v4l2_memory
{
V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR
};
//count,type,memory 都要应用程序设置
*/
//6.1 向设备申请缓冲区 VIDIOC_REQBUFS
//申请一个拥有四个缓冲帧的缓冲区
struct v4l2_requestbuffers req;
req.count=4; //申请4个缓冲区
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; // 缓冲帧数据格式
req.memory=V4L2_MEMORY_MMAP; //内存映射
if(ioctl(fd,VIDIOC_REQBUFS,&req) < 0)
{
printf("-----申请队列空间失败-VIDIOC_REQBUFS-fail------\n");
}
//获取缓冲帧的地址,长度:VIDIOC_QUERYBUF
// struct buffer
// {
// void* start;
// unsigned int length;
// }*buffers;
// buffers = (struct buffer*)calloc (req.count, sizeof (*buffers));
// if (!buffers) {
// // 映射
// printf("Out of memory\n");
// return -1;
// }
//把内核的缓冲区队列映射到用户空间
//获取缓冲帧的地址,长度:VIDIOC_QUERYBUF
//相关函数:int ioctl(int fd, int request, struct v4l2_buffer *argp);
/*相关结构体:
struct v4l2_buffer
{
u32 index; //buffer 序号
enum v4l2_buf_type type; //buffer 类型
u32 byteused; //buffer 中已使用的字节数
u32 flags; // 区分是MMAP 还是USERPTR
enum v4l2_field field;
struct timeval timestamp; // 获取第一个字节时的系统时间
struct v4l2_timecode timecode;
u32 sequence; // 队列中的序号
enum v4l2_memory memory; //IO 方式,被应用程序设置
union m
{
u32 offset; // 缓冲帧地址,只对MMAP 有效
unsigned long userptr;
};
u32 length; // 缓冲帧长度
u32 input;
u32 reserved;
};
*/
unsigned char *mptr[4]; //保护映射后用户空间的首地址
unsigned int size[4];
struct v4l2_buffer mapbuffer;
for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers)
{
memset(&mapbuffer,0,sizeof(mapbuffer)); //清空
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置类型摄像头采集
mapbuffer.memory = V4L2_MEMORY_MMAP; //内存映射 IO 方式,被应用程序设置
//mapbuffer.memory = V4L2_MEMORY_USERPTR;
mapbuffer.index = n_buffers; //buffer 序号
// 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &mapbuffer)){
printf("----查询内核空间队列失败--VIDIOC_QUERYBUF-fail------\n");
return -1;
}
size[n_buffers] = mapbuffer.length;
// 映射内存
mptr[n_buffers] =(unsigned char *)mmap (NULL,mapbuffer.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, mapbuffer.m.offset);
//VIDIOC_QBUF// 把帧放入队列
//VIDIOC_DQBUF// 从队列中取出帧
if(ioctl (fd, VIDIOC_QBUF, &mapbuffer) < 0) {// 将缓冲帧放入队列
printf("----------帧放入队列失败------------\n");
return -1;
}
}
//7.缓冲区处理好之后,就可以开始获取数据了
//启动 或 停止数据流 VIDIOC_STREAMON, VIDIOC_STREAMOFF
//例:把四个缓冲帧放入队列,并启动数据流
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl (fd, VIDIOC_STREAMON, &type) < 0) {
printf("-----------开启失败-------\n");
return -1;
}
//int flag = 1;
#if 01
FILE *file = fopen("my.yuv","w+");
#else
FILE *file = fopen("my.h264","w+");
#endif
while (1)
{
//8.采集数据
//从队列中提取一帧数据
struct v4l2_buffer readbuffer;
memset(&readbuffer,0,sizeof(readbuffer));
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
readbuffer.memory = V4L2_MEMORY_MMAP;
printf("--------------1-----------\n");
ret = ioctl (fd, VIDIOC_DQBUF, &readbuffer); // 从缓冲区取出一个缓冲帧
if(ret < 0)
{
perror("提取数据失败");
}
printf("--------------2-----------\n");
//把一帧数据写入文件
//if(1 == flag) {
fwrite(mptr[readbuffer.index],readbuffer.length,1,file);
fflush(file);
//fclose(file);
//flag = -1;
//}
//通知内核已经使用完毕
ret = ioctl (fd,VIDIOC_QBUF,&readbuffer);
if(ret < 0)
{
perror("放回队列失败");
}
}
//9.停止采集
ret = ioctl (fd,VIDIOC_STREAMOFF,&type);
//10.释放映射
for(int i=0; i<4; i++)
{
munmap(mptr[i], size[i]);// 断开映射
}
//11.关闭设备
close(fd);
printf("------------end------------\n");
return 0;
}
#查看dev/video设备信息
v4l2-ctl --list-formats -d /dev/FPV
打开相机获取yuv
ffmpeg -f video4linux2 -s 1920x1080 -pix_fmt yuyv422 -i /dev/video0 out.yuv
window获取相机列表
ffmpeg -list_devices true -f dshow -i dummy
window播放相机设备
ffplay -f dshow -i video=“Integrated Camera”
linux播放相机设备
ffmpeg -f v4l2 -i video=/dev/video0
查看摄像头所支持的分辨率
v4l2-ctl --list-framesizes=MJPG -d /dev/video0
查看摄像头支持的视频参数
sudo v4l2-ctl --all --list-formats-ext
#查看相机当前支持格式
v4l2-ctl --list-formats -d /dev/video0 --all
v4l2参数意义
https://blog.csdn.net/liujun3512159/article/details/123946141
相关v4l2链接:
https://blog.csdn.net/abcamus/article/details/52940412
https://blog.csdn.net/wishfly/article/details/50856799
https://blog.csdn.net/wyc1522510/article/details/121959665
https://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html