V4L2编程
一开始还以为是v412。。。 学习了两遍,查询了大量资料,最简单易懂代码最清晰的来自https://mp.weixin.qq.com/s/bbh9uY5q0CrqXDCc8rzLQA
1、简介
linux中关于视频设备的内核驱动。 V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video*下,如果只有一个视频设备,通常为/dev/video0。 V4L2在设计时,是要支持很多广泛的设备的,它们之中只有一部分在本质上是真正的视频设备。
V4L2全名是video for linux 2之前还有个老版本v4l,也就是video for linux 1.0版本。 V4L2不仅仅用于摄像头,也用于视频输出接口,收音机接口等。
我们插入USB,使用dmesg查看usb的输出信息: uvcvideo: Found UVC 1.00 device USB 2.0 Camera (05a3:9310)
搜索内核源码可以找到相关函数 cd linux-3.4.2/ cd drivers/ $ grep "Found UVC" * -nR media/video/uvc/uvc_driver.c:1848: uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s 这里就涉及到UVC这个名词了,所谓的UVC也就是usb video class类的驱动,也就是USB硬件相关的驱动,也就是应该会向上注册这个驱动程序
2、编程
v4L2是针对uvc免驱usb设备的编程框架,主要用于采集usb摄像头等,编程模式如下: 打开视频设备-》设定属性-》设定采集方式-》处理采集数据-》关闭视频设备。
demo见:D:\Github\Storage\udev\v4l2
2-1、采集方式
打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
extern int ioctl (int __fd, unsigned long int __request, …) __THROW;
/**
* 向摄像头驱动请求执行命令
*
* @param [in] fh 文件描述符
* @param [in] request 请求码
* @param [in] arg 请求参数
* @return -1 failed others success
*/
int32_t xioctl(int32_t fd, unsigned long request, void *arg)
{
int32_t r;
do {
r = ioctl(fd, request, arg);
} while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));
if (r < 0) {
CAMERA_ERROR("ioctl(%d, %lu) error(%d): %s",
fd, request, errno, strerror(errno));
}
return r;
}
通过man命令查看ioctl函数:
RETURN VALUE
Usually, on success zero is returned. A few ioctl() requests use the return value as an output parameter and return a nonnegative value on suc‐
cess. On error, -1 is returned, and errno is set appropriately.
建议使用非负数判断返回值为成功。
在进行V4L2开发中,一般会用到以下的命令标志符: https://baike.baidu.com/item/V4L2/10054109?fr=aladdin
VIDIOC_QUERYCAP:查询驱动功能 VIDIOC_QBUF:把数据放回缓存队列 VIDIOC_DQBUF:把数据从缓存中读取出来
2-2、操作流程
https://baike.baidu.com/item/V4L2/10054109?fr=aladdin
但还是没有搞清楚VIDIOC_QBUF和VIDIOC_DQBUF的作用以及具体的操作意义。
是先VIDIOC_QBUF还是先VIDIOC_DQBUF,是否真的是先VIDIOC_DQBUF后一定需要VIDIOC_QBUF????
2-3、使用demo来探究
https://blog.csdn.net/h1527820835/article/details/124366709
3、Linux下七种文件类型
文件类型标识 文件类型
- 普通文件 d 目录 l 符号链接 s(伪文件) 套接字 b(伪文件) 块设备 c(伪文件) 字符设备 p(伪文件) 管道
占用存储空间的类型:文件、目录、符号链接。符号链接记录的是路径,路径不长时存在innode里面。 其他四种:套接字、块设备、字符设备、管道是伪文件,不占用磁盘空间。
root@android:/ # ll /dev/video0
crw-rw-rw- system camera 81, 0 2023-02-08 10:33 video0
4、查看/dev/video0文件信息
linux下usb摄像头操作,离不开v4l2框架。 V4L2是Video for linux2的简称,为linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
v4l2-ctl命令查看video设备参数信息,如果提示如下未找到v4l2-ctl命令,则需要安装v4l-utils
在线部分教程:http://wiki.100ask.org/V4L2_camera_overview
# 安装V4l2工具包
$ sudo apt install v4l-utils
https://linuxtv.org/downloads/v4l-utils/
# 通过v4l2查看摄像头设备
$ sudo v4l2-ctl --list-devices
EP28WD (usb-fe380000.usb-1.3):
/dev/video0
/dev/video1
# 查看当前摄像头支持的视频压缩格式
$ sudo v4l2-ctl -d /dev/video0 --list-formats
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUYV 4:2:2
Index : 1
Type : Video Capture
Pixel Format: 'H264' (compressed)
Name : H.264
Index : 2
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : Motion-JPEG
# 查看摄像头所有参数
$ sudo v4l2-ctl -d /dev/video0 --all
# 查看/dev/video0中所有格式及分辨率
v4l2-ctl -d0 --list-formats-ext
# 查看摄像头所支持的分辨率
sudo v4l2-ctl --list-framesizes=MJPG -d /dev/video0
5、V4L2编程官网
http://v4l.videotechnology.com/dwg/ https://www.cnblogs.com/wenrenhua08/p/3937736.html
因为 V4L2 可以对多种设备编程,所以并不是所有 API 可以对所有设备编程,哪怕是同类型的设备,使用ioctl--VIDIOC_QUERYCAP 去询问支持什么功能。
struct v4l2_capability cap;
rel = ioctl(fdUsbCam, VIDIOC_QUERYCAP, &cap);
if(rel != 0)
{
perror("ioctl VIDIOC_QUERYCAP");
return -1;
}
结构体如下:
struct v4l2_capability
{
__u8 driver[16];
__u8 card[32];
__u8 bus_info[32];
__u32 version;
__u32 capabilities;
__u32 reserved[4];
};
这里面最重要的是:capabilities:
头文件 linux/videodev2.h 和 kernel 头文件 linux/videodev2.h 中都有描述:
#define V4L2_CAP_VIDEO_CAPTURE 0x00000001
#define V4L2_CAP_VIDEO_OUTPUT 0x00000002
#define V4L2_CAP_VIDEO_OVERLAY 0x00000004
#define V4L2_CAP_VBI_CAPTURE 0x00000010
#define V4L2_CAP_VBI_OUTPUT 0x00000020
#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080
#define V4L2_CAP_RDS_CAPTURE 0x00000100
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200
#define V4L2_CAP_HW_FREQ_SEEK 0x00000400
#define V4L2_CAP_RDS_OUTPUT 0x00000800
#define V4L2_CAP_TUNER 0x00010000
#define V4L2_CAP_AUDIO 0x00020000
#define V4L2_CAP_RADIO 0x00040000
#define V4L2_CAP_MODULATOR 0x00080000
#define V4L2_CAP_READWRITE 0x01000000
#define V4L2_CAP_ASYNCIO 0x02000000
#define V4L2_CAP_STREAMING 0x04000000
这里要说到 VBI,Vertical Blanking Interval 的缩写。 电视信号包括一部分非可视信号,它不传送可视信息,因此被称为ⅦI(垂直消隐期间)。VBI 可以用于传送其他信息,通常是一种专用字幕信号, 这和 Blog 重显率中所说暗合。
在这里,V4L2_CAP_VIDEO_CAPTURE 说明设备是个图像采集设备,V4L2_CAP_STREAMING 说明是个 Streaming 设备。 通常,摄像头都支持以上两个能力。
6、usb摄像头与video匹配
linux系统接上usb摄像头会出现/dev/video设备,如果接多个摄像头,通过/dev/video无法与真实摄像头一一对应。由于有的时候一个usb摄像头会产生两个或多个video(只有一个能拍照),所以通过udev的rule规则也无法固定。这时可以通过/dev/v4l/by-id/路径的软链接固定。
$ ll /dev/v4l/by-id/
lrwxrwxrwx 1 root root 12 Sep 10 14:32 usb-Generic_HDR_CAMERA_200901010001-video-index0 -> ../../video0
lrwxrwxrwx 1 root root 12 Sep 10 14:32 usb-Generic_HDR_CAMERA_200901010001-video-index1 -> ../../video1
lrwxrwxrwx 1 root root 12 Sep 10 14:24 usb-USB3.0_Camera_8M_USB3.0_Camera_8M_SITN00000000001-video-index0 -> ../../video2
lrwxrwxrwx 1 root root 12 Sep 10 14:24 usb-USB3.0_Camera_8M_USB3.0_Camera_8M_SITN00000000001-video-index1 -> ../../video3
root@android:/dev # ll /sys/class/video4linux/video*
在我使用的 ubuntu 18.04 环境下,插入一个 usb 相机,会出现两个 /dev/video*,这不是 bug 而是 v4l2 的特性,具体讨论见此文章 V4L2 issues two video devices to each camera 。 https://bugzilla.kernel.org/show_bug.cgi?id=199575
会发现有个Device Caps是不相同的。 https://github.com/torvalds/linux/commit/088ead25524583e2200aa99111bea2f66a86545a
7、继续查找一个usb摄像头2个video
udevadm info -n /dev/video0 is the "same" as udevadm info -n /dev/video1, but they get a different ATTR{index}. So for my 2 cams I ended up with the following /etc/udev/rules.d/99-cam.rules:
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="eb1a", ATTRS{idProduct}=="299f", ATTR{index}=="0", MODE="0664", GROUP="video", SYMLINK+="cams/cam1"
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="1908", ATTRS{idProduct}=="2311", ATTR{index}=="0", MODE="0664", GROUP="video", SYMLINK+="cams/cam2"
(Linux下V4L2相关头文件所在路径为/内核源码目录/include/linux/videodev2.h,V4L2相关API文档可查看链接https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/v4l2.html)
摄像头(相机)常见参数: 白平衡(自动白平衡AWB)及色温、曝光(自动曝光AE、曝光补偿EV)、亮度、对比度、饱和度、色度(色调+饱和度)、锐度(也叫清晰度)、背光补偿(也叫逆光补偿)、增益、对焦等
(注:不同摄像头开放的参数不一致,需提前确认该款摄像头的可调参数,未开放的参数是无法调整的!!!)
上述参数涵义如有不懂,可自行维基百科https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5和百度词条https://baike.baidu.com/item/科普。
8、open时需要增加写权限
如果我们希望采集这些内核空间的的数据帧的话,前提就是将内核空间相关数据映射到用户空间以方便应用程序能够直接访问这些数据帧。这样就会用到mmap操作(当然还有其他方式,由req.memory 确定使用mmap方法)。
mmap函数实现把一个文件映射到一个内存区域,从而我们可以像读写内存一样读写文件,他比单纯调用read/write也要快上许多。在某些时候我们可以把内存的内容拷贝到一个文件中实现内存备份,当然,也可以把文件的内容映射到内存来恢复某些服务。另外,mmap实现共享内存也是其主要应用之一,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。
mmap成功操作有以下要求:
1.进行文件映射的描述符必须拥有读权限,否则会产生SIGSEGV信号
2.把内存内容写入映射文件时,必须确保被写文件当前位置到文件结尾的长度不小于所写内容长度,否则产生SIGBUS信号
3.关闭文件描述符并不能保证文件内容不被修改
所以最好以读写方式打开设备文件,否则执行映射时会出现权限拒绝的现象。
9、linux下需要的摄像头软件
camorama和cheese不是很好用,无法进行切换分辨率和格式。
搜索了一个wxcam,下载地址:http://wxcam.sourceforge.net/
tar xvf wxcam-1.1.tar.gz
cd wxcam-1.1
./configure
需要安装wxWidgets库
apt install libwxgtk3.0-gtk3-dev
./configure
make
缺少<alsa/asoundlib.h>头文件
下载安装alsa-lib:https://www.alsa-project.org/files/pub/lib/
缺少<libv4l1-videodev.h>头文件
sudo apt-get install libv4l-dev
缺少<mjpegtools/jpegutils.h>头文件
apt install libmjpegtools-dev
缺少CImg.h头文件
apt install cimg-dev
缺少<xvid.h>头文件
apt install libxvidcore-dev
终于编译成功了,不容易啊!!!
软件打开会有错误提示,但不是太影响使用。 最终效果还行,虽然不是很完美,但是能做到以下几点: 1.能切换分辨率,分辨率可能不全 2.能切换指定的/dev/video,需要重新打开一下
10、ION内存管理器介绍
https://blog.csdn.net/lunhui2016/article/details/111797978
ION是google在Android4.0为了解决内存碎片化管理而引入的通用内存管理器,用来支持不同的内存分配机制,如CARVOUT(PMEM),物理连续内存(kmalloc),虚拟地址连续但物理地址不连续内存(vmalloc),IOMMU等。
内核版本:linux-4.9