文章目录
前言
V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备也是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。
V4L2获取MJPEG视频流步骤
V4L2获取视频流大概可以分为10步,分别为:打开设备、获取摄像头支持格式(可以细分为:查看设备是否为视频采集设备、枚举帧格式、枚举分辨率、枚举某分辨率下的帧速率)、配置摄像头采集格式、申请内核缓冲区队列、把内核缓冲区队列映射到用户地址空间、开始采集、采集数据、停止采集、释放映射、关闭设备
一.打开设备
代码如下(示例):
查看设备可以使用命令 ls /dev/video*
来查看视频类设备对应的设备节点。
我的USB摄像头设备节点为:
.c文件为v4l2_video.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开视频采集设备失败");
return -1;
}
//10.关闭设备
close(fd);
return 0;
}
二.获取摄像头支持格式
获取摄像头格式的宏定义为VIDIOC_ENUM_FMT–对应存储格式的结构体struct v4l2_fmtdesc。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
}
1.查看设备是否为视频采集设备
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无视频采集设备!\n");
return -1;
}
//10.关闭设备
close(fd);
return 0;
}
2.枚举帧格式
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
//10.关闭设备
close(fd);
return 0;
}
3.枚举分辨率
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
//10.关闭设备
close(fd);
return 0;
}
4.枚举某分辨率帧速率
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//10.关闭设备
close(fd);
return 0;
}
三.配置摄像头采集格式
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//3.设置采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头捕获
vfmt.fmt.pix.width = 1920;//设置采集宽度,可根据自己摄像头支持的分辨率设置
vfmt.fmt.pix.height = 1080;//设置采集高度
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("USB摄像头获取格式失败");
}
if(vfmt.fmt.pix.width == 1920&& vfmt.fmt.pix.height == 1080&&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
}
//10.关闭设备
close(fd);
return 0;
}
四.申请内核缓冲区队列
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//3.设置采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头捕获
vfmt.fmt.pix.width = 1920;//设置采集宽度,可根据自己摄像头支持的分辨率设置
vfmt.fmt.pix.height = 1080;//设置采集高度
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("USB摄像头获取格式失败");
}
if(vfmt.fmt.pix.width == 1920&& vfmt.fmt.pix.height == 1080&&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
}
//4.申请内核空间
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //向内核申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
if(ret < 0)
{
perror("申请队列缓冲区失败");
}
//10.关闭设备
close(fd);
return 0;
}
五.把内核缓冲区队列映射到用户地址空间
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//3.设置采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头捕获
vfmt.fmt.pix.width = 1920;//设置采集宽度,可根据自己摄像头支持的分辨率设置
vfmt.fmt.pix.height = 1080;//设置采集高度
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("USB摄像头获取格式失败");
}
if(vfmt.fmt.pix.width == 1920&& vfmt.fmt.pix.height == 1080&&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
}
//4.申请内核空间
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //向内核申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
if(ret < 0)
{
perror("申请队列缓冲区失败");
}
//5.把内核缓冲区队列映射到用户地址空间
unsigned char *mptr[4];//保存映射后用户空间的首地址
unsigned int size[4];
struct v4l2_buffer mapbuffer;
//初始化type, index
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(int i=0; i<4; i++)
{
mapbuffer.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
if(ret < 0)
{
perror("查询内核空间队列失败");
}
mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, mapbuffer.m.offset);
size[i]=mapbuffer.length;
//入队
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
if(ret < 0)
{
perror("入队失败");
}
}
//10.关闭设备
close(fd);
return 0;
}
六-七.开始采集及读取帧数
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//3.设置采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头捕获
vfmt.fmt.pix.width = 1920;//设置采集宽度,可根据自己摄像头支持的分辨率设置
vfmt.fmt.pix.height = 1080;//设置采集高度
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("USB摄像头获取格式失败");
}
if(vfmt.fmt.pix.width == 1920&& vfmt.fmt.pix.height == 1080&&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
}
//4.申请内核空间
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //向内核申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
if(ret < 0)
{
perror("申请队列缓冲区失败");
}
//5.把内核缓冲区队列映射到用户地址空间
unsigned char *mptr[4];//保存映射后用户空间的首地址
unsigned int size[4];
struct v4l2_buffer mapbuffer;
//初始化type, index
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(int i=0; i<4; i++)
{
mapbuffer.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
if(ret < 0)
{
perror("查询内核空间队列失败");
}
mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, mapbuffer.m.offset);
size[i]=mapbuffer.length;
//入队
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
if(ret < 0)
{
perror("入队失败");
}
}
//6.开始采集
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if(ret < 0)
{
perror("采集失败");
}
//7.读取帧数
struct v4l2_buffer readbuffer;
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
if(ret < 0)
{
perror("读取帧数据失败");
}
// 保存保存采集到的帧数据,格式为jpg
FILE *file=fopen("my.jpg", "w+");//保存地址为当前目录。如果要保存为其他目录则可以修改代码
//例如,保存为/tmp目录 FILE *file=fopen("/tmp/my.jpg", "w+");
fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);
fclose(file);
//再次入队
ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
if(ret < 0)
{
perror("放回队列失败");
}
//10.关闭设备
close(fd);
return 0;
}
八-十.停止采集、释放映射、关闭设备
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
int main(void)
{
//1.打开设备
int fd = open("/dev/video0", O_RDWR);
if(fd < 0)
{
perror("打开设备失败");
return -1;
}
//2.获取摄像头支持的格式ioctl(文件描述符, 命令, 与命令对应的结构体)
struct v4l2_fmtdesc v4fmt;
v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获设备
int i=0;
while(1)
{
v4fmt.index = i++;
int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
if(ret < 0)
{
perror("获取摄像头格式失败");
break;
}
printf("index=%d\n", v4fmt.index);
printf("flags=%d\n", v4fmt.flags);
printf("description=%s\n", v4fmt.description);
unsigned char *p = (unsigned char *)&v4fmt.pixelformat;
printf("pixelformat=%c%c%c%c\n", p[0],p[1],p[2],p[3]);
printf("reserved=%d\n", v4fmt.reserved[0]);
}
//查看该设备是否为视频采集设备
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
perror("Error: 无USB视频采集设备!\n");
return -1;
}
// 枚举帧格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("USB摄像头支持所有格式如下:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0){
printf("v4l2_format%d:%s\n",fmtdesc.index,fmtdesc.description);
fmtdesc.index++;
}
// 枚举分辨率
struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("MJPEG格式支持所有分辨率如下:\n");
// frmsize.pixel_format = V4L2_PIX_FMT_YUYV;
frmsize.pixel_format = V4L2_PIX_FMT_MJPEG;
while(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&frmsize) == 0){
printf("frame_size<%d*%d>\n",frmsize.discrete.width,frmsize.discrete.height);
frmsize.index++;
}
// 枚举某分辨率下的帧速率
struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_MJPEG;
frmival.width = 1920;
frmival.height = 1080;
while(ioctl(fd,VIDIOC_ENUM_FRAMEINTERVALS,&frmival) == 0){
printf("frame_interval under frame_size <%d*%d> support %dfps\n",frmival.width,frmival.height,frmival.discrete.denominator / frmival.discrete.numerator);
frmival.index++;
}
//3.设置采集格式
struct v4l2_format vfmt;
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头捕获
vfmt.fmt.pix.width = 1920;//设置采集宽度,可根据自己摄像头支持的分辨率设置
vfmt.fmt.pix.height = 1080;//设置采集高度
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//设置视频采集格式
int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt);
if(ret < 0)
{
perror("设置格式失败");
}
memset(&vfmt, 0, sizeof(vfmt));
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_G_FMT, &vfmt);
if(ret < 0)
{
perror("USB摄像头获取格式失败");
}
if(vfmt.fmt.pix.width == 1920&& vfmt.fmt.pix.height == 1080&&
vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
{
printf("设置成功\n");
}else
{
printf("设置失败\n");
}
//4.申请内核空间
struct v4l2_requestbuffers reqbuffer;
reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuffer.count = 4; //向内核申请4个缓冲区
reqbuffer.memory = V4L2_MEMORY_MMAP ;//映射方式
ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer);
if(ret < 0)
{
perror("申请队列缓冲区失败");
}
//5.把内核缓冲区队列映射到用户地址空间
unsigned char *mptr[4];//保存映射后用户空间的首地址
unsigned int size[4];
struct v4l2_buffer mapbuffer;
//初始化type, index
mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(int i=0; i<4; i++)
{
mapbuffer.index = i;
ret = ioctl(fd, VIDIOC_QUERYBUF, &mapbuffer);//从内核空间中查询一个空间做映射
if(ret < 0)
{
perror("查询内核空间队列失败");
}
mptr[i] = (unsigned char *)mmap(NULL, mapbuffer.length, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, mapbuffer.m.offset);
size[i]=mapbuffer.length;
//入队
ret = ioctl(fd, VIDIOC_QBUF, &mapbuffer);
if(ret < 0)
{
perror("入队失败");
}
}
//6.开始采集
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if(ret < 0)
{
perror("采集失败");
}
//7.读取帧数
struct v4l2_buffer readbuffer;
readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_DQBUF, &readbuffer);
if(ret < 0)
{
perror("读取帧数据失败");
}
// 保存保存采集到的帧数据,格式为jpg
FILE *file=fopen("picture0.jpg", "w+");//保存地址为当前目录。如果要保存为其他目录则可以修改代码
//例如,保存为/tmp目录 FILE *file=fopen("/tmp/my.jpg", "w+");
fwrite(mptr[readbuffer.index], readbuffer.length, 1, file);
fclose(file);
//再次入队
ret = ioctl(fd, VIDIOC_QBUF, &readbuffer);
if(ret < 0)
{
perror("放回队列失败");
}
//8.停止采集
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
//9.释放映射
for(int i=0; i<4; i++){
munmap(mptr[i], size[i]);
}
//10.关闭设备
close(fd);
return 0;
}
Ubuntu中gcc编译
sudo gcc -o v4l2_video v4l2_video.c
运行生成的二进制可执行文件v4l2_video即可捕获摄像头视频流拍照,
这是我抓拍到的图片:
当然,如果要在开发板中运行,还得进行交叉编译才行。交叉编译方法可参考博文:https://blog.csdn.net/weixin_45107609/article/details/126914519
V4L2获取YUYV视频流
以上为V4L2获取MJPEG格式视频流,但是,现在大多数USB摄像头支持的是YUYV格式,在下篇博文将介绍V4L2获取YUYV视频流步骤及代码讲解。
总结
每一步都逐级附带了代码,并且都有清晰的注释,大家可以仔细看看,有什么疑问,大家可以一起交流沟通!!!