linux v4l2编程
参考文档:https://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.htmlv4l2 : video for linux api two version //也就是linux系统下视频设备驱动好后,应用程序怎样调用相关的视频设备的编程接口视频设备: 摄像头, 硬件编解码设备, 图形加速等。
参考文档:
https://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html
v4l2 : video for linux api two version //也就是linux系统下视频设备驱动好后,应用程序怎样调用相关的视频设备的编程接口
视频设备: 摄像头, 硬件编解码设备, 图形加速等。
设备文件: /dev/video* //有这些设备文件就是表示相应的视频设备已经驱动好,应用程序就可以使用v4l2的编程接口来实现视频设备的调用.
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
ioctl函数用于把硬件的配置参数传给相应的设备驱动,让设备驱动根据指定的参数来配置硬件.
也可用于从设备驱动里获取出硬件的现用配置参数.
如ioctl用于配置摄像头的数据格式,所用的分辨率等参数的配置
函数返回值, 0表示配置成功, -1表示失败.
参数: fd表示打开设备文件得到文件描述符
request表示让设备驱动具体的操作行为 //如配置分辨率,配置数据格式等
…表示是可选的第三个参数,request是配置参数用时, 第三个参数应为准备好的配置参数变量的地址
V4L2里常用的ioctl介绍:
int ioctl(int fd, VIDIOC_QUERYCAP, struct v4l2_capability *argp); //可用于判断是否一个摄像头设备及所支持的操作接口
int ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *argp); //可查询摄像头支持的图像格式
int ioctl(int fd, VIDIOC_ENUM_FRAMESIZES, struct v4l2_frmsizeenum *argp); //可查询摄像头在某种图像格式下所能支持的图形分辨率
int ioctl(int fd, VIDIOC_ENUMINPUT, struct v4l2_input *argp); //查询摄像头的选择。如前置/后置摄像头
int ioctl(int fd, VIDIOC_S_FMT, struct v4l2_format *argp); //设置摄像头的图形格式
int ioctl(int fd, VIDIOC_S_INPUT, int *argp); //设置使用前置/后置摄像头
int ioctl(int fd, VIDIOC_S_CTRL, struct v4l2_control *argp); //设置摄像头的亮度,对比度等信息
int ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *argp); //设置摄像头驱动申请图像缓冲区
int ioctl(int fd, VIDIOC_QBUF/VIDIOC_DQBUF, struct v4l2_buffer *argp); //把指定的图像缓冲区加入/退出图像采集队列
int ioctl(int fd, VIDIOC_STREAMON/VIDIOC_STREAMOFF , const int *argp); //启动/停止图像采集
以上结构体及其的成员的含议可在文档或/usr/include/linux/videodev2.h查看
usb摄像常用的图像格式:
V4L2_PIX_FMT_YUYV V4L2_PIX_FMT_MJPG V4L2_PIX_FMT_JPEG
V4L2_PIX_FMT_MJPG/V4L2_PIX_FMT_JPEG其实取出来就是一张完整的jpg图像.
查询是否一个摄像头设备的代码:
int fd, i;
fd = open("/dev/video0", O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
{
perror("get capability failed");
return -1;
}
printf("driver : %s\n", cap.driver);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("not a camera\n");
return -2;
}
获取摄像头设备支持的图像格式:
int fd, i;
fd = open("/dev/video0", O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
struct v4l2_fmtdesc fmtdesc;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕捉类型
for (i = 0; ; i++)
{
fmtdesc.index = i; //获取摄像头所支持的第几种格式
if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0)
break;
printf("%s\n", fmtdesc.description);
}
获取摄像头设备支持的图像格式的图像分辨率:
int fd, i, j;
fd = open("/dev/video0", O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
struct v4l2_fmtdesc fmtdesc;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕捉类型
struct v4l2_frmsizeenum frmsize;
for (i = 0; ; i++)
{
fmtdesc.index = i; //获取摄像头所支持的第几种格式
if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0)
break;
printf("%s\n", fmtdesc.description);
//每种格式下又会支持几种不同的分辨率
frmsize.pixel_format = fmtdesc.pixelformat; //把获取出来的格式传给frmsize`
for (j = 0; ; j++)
{
frmsize.index = j;
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) < 0)
break;
printf("w = %d, h = %d\n", frmsize.discrete.width, frmsize.discrete.height);
}
}
获取摄像头图像数据的步骤流程:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#define W 640
#define H 480
#define FMT V4L2_PIX_FMT_YUYV // YUV 4:2:2
#define COUNT 3
int main(void)
{
int fd, i;
char *data[COUNT];
// 1. 打开摄像头的设备文件
fd = open("/dev/video0", O_RDWR);
if (fd < 0)
{
perror("open");
return -1;
}
// 2. 检查是否是一个摄像头的设备文件
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0)
{
perror("get capability failed");
return -1;
}
printf("driver : %s\n", cap.driver);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("no a camera\n");
return -2;
}
// 3. 设置摄像头的数据格式, 分辨率
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = W;
fmt.fmt.pix.height = H;
fmt.fmt.pix.pixelformat = FMT;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
{
perror("set format");
return -1;
}
// 4. 让驱动申请出存放图像数据的缓冲区, 每个缓冲区存放一帧图像
// 三个缓冲区
struct v4l2_requestbuffers bufs;
bufs.count = COUNT;
bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufs.memory = V4L2_MEMORY_MMAP; //指定申请出来的缓冲区用于mmap映射到用户进程使用
if (ioctl(fd, VIDIOC_REQBUFS, &bufs) < 0)
{
perror("request bufs");
return -2;
}
//5.查询缓冲区的状态。 前面让驱动申请出3个缓冲区, 实际上是分配出存放三个图像数据的连续的一块内存区域. 所以需要查出每个缓冲区的开始地址,大小等信息
struct v4l2_buffer buffer;
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;
for (i = 0; i < COUNT ; i++)
{
buffer.index = i; //指定要查询的是第几个缓冲区
if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0)
{
perror("query buf");
return -2;
}
printf("%d: len = %d, offset = %d\n", i, buffer.length, buffer.m.offset);
// 6. 把驱动里的缓冲区映射到用户进程来使用
data[i] = mmap(NULL, buffer.length, PROT_READ, MAP_SHARED, fd, buffer.m.offset);
if (MAP_FAILED == data[i])
{
perror("mmap failed");
return -1;
}
// 7. 把每个缓冲区加入图像数据的采集队列. 只有在队列里的缓冲区才会被驱动填充新的图像数据.
// 当取缓冲区的数据时,需让缓冲区退出采集队列,数据取好后, 再重新加入采集队列
if (ioctl(fd, VIDIOC_QBUF, &buffer) < 0)
{
perror("qbuf");
return -2;
}
}
//8. 让驱动开始采集图像数据, 采集到的数据将会填入缓冲区里
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
{
perror("stream on");
return -3;
}
// 9. 取出缓冲区里的图像数据。先让一个已采好数据的缓冲区退出采集队列
// 取出10张图像
struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
int dst_fd;
char file_name[100];
//如果是MJPG/JPEG的图像格式,直接存起来就是一张jpg图像了.
for (i = 0; i < 10; i++)
{
if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0)
{
perror("dqbuf");
return -3;
}
sprintf(file_name, "my%02d.yuyv", i);
dst_fd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
write(dst_fd, data[buf.index], buf.bytesused);
close(dst_fd);
//重新加入采集队列
ioctl(fd, VIDIOC_QBUF, &buf);
}
return 0;
}
更多推荐
所有评论(0)