视频监控—根据虚拟驱动vivi的使用过程彻底分析摄像头驱动
- 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 参考资料:Video for Linux Two - Driver Writer’s Guide、《Android驱动开发权威指南》
- 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
- 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-3
一、虚拟驱动vivi测试
- 在Ubuntu虚拟机上安装xawtv,执行
uname -a
确实ubuntu的内核版本; - 去www.kernel.org下载同版本的内核,解压后把
/drivers/media/video
目录取出,修改它的Makefile为:
KERN_DIR = /usr/src/linux-headers-"内核版本"-generic
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += vivi.o
obj-m += videobuf-core.o
obj-m += videobuf-vmalloc.o
obj-m += v4l2-common.o
- 执行
make
进行编译 - 加载驱动
sudo modprobe vivi
、sudo rmmod vivi
、sudo insmod ./vivi.ko
- 查看对应的设备节点
ls /dev/video*
- 执行
xawtv -c /dev/videoX
,启动测试vivi
二、根据log分析摄像头驱动
执行,打印出vivi使用时的xawtv.log
,从中彻底分析摄像头驱动。
由于log信息过多,只挑选出对应部分,由于vivi对应的设备节点为/dev/event0
,在日志搜索/dev/event0
,得到以下有用信息。
open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccd44) = 0
ioctl(4, VIDIOC_G_FMT or VT_SENDSIG, 0xbfaccc78) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, 0xc02c564a, 0xbfaccb58) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, 0xc02c564a, 0xbfaccb58) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, 0xc02c564a, 0xbfaccb58) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccb84) = 0
ioctl(4, VIDIOC_G_INPUT, 0xbfacca2c) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0xbfacca2c) = 0
fstat64(4, {st_mode=S_IFCHR|0660, st_rdev=makedev(81, 0), ...}) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0xbfacca78) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0x9b40acc) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0x9b40b18) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0x9b40b64) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0x9b40bb0) = 0
ioctl(4, VIDIOC_ENUMINPUT, 0x9b40bfc) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_ENUMSTD, 0x9b40f8c) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b40fcc) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b4100c) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b4104c) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b4108c) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b410cc) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b4110c) = 0
ioctl(4, VIDIOC_ENUMSTD, 0x9b4114c) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4138c) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b413cc) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4140c) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4144c) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4148c) = 0
ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b414cc) = 0
ioctl(4, VIDIOC_G_PARM, 0x9b40a00) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41b8c) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41bd0) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c14) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c58) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41c9c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ce0) = 0
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d24) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41d68) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41dac) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41df0) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e34) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41e78) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41ebc) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f00) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f44) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41f88) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41fcc) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42010) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42054) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42098) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b420dc) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42120) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42164) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421a8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b421ec) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42230) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42274) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422b8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b422fc) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42340) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42384) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b423c8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4240c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42450) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42494) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b424d8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4251c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42560) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425a4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b425e8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4262c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42670) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426b4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b426f8) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4273c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42780) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b427c4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42808) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4284c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42890) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b428d4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42918) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b4295c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429a0) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b429e4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a28) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42a6c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42ab0) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42af4) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b38) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42b7c) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42bc0) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c04) = -1 EINVAL (Invalid argument)
ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b42c48) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_G_STD, 0xbfacce08) = 0
ioctl(4, VIDIOC_G_INPUT, 0xbfacce1c) = 0
ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce24) = 0
ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4) = 0
ioctl(4, VIDIOC_S_FMT or VT_RELDISP, 0xbfacc754) = 0
ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4) = 0
ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42d94) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42dd8) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_STREAMON, 0xbfacca2c) = 0
ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
ioctl(4, VIDIOC_S_INPUT, 0xbfacce84) = 0
ioctl(4, VIDIOC_S_STD, 0x9b40f90) = 0
select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979})
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42dd8) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfaccd00) = 0
ioctl(4, VIDIOC_STREAMOFF, 0xbfacc53c) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc48c) = 0
ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4) = -1 EINVAL (Invalid argument)
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_TRY_FMT, 0xbfacc000) = 0
ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0xbfacc70c) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) = 0
ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0xbfacc61c) = 0
ioctl(4, VIDIOC_QBUF, 0xbfacc61c) = 0
ioctl(4, VIDIOC_QBUF, 0xbfacc61c) = 0
ioctl(4, VIDIOC_STREAMON, 0xbfacc65c) = 0
ioctl(4, VIDIOC_DQBUF, 0xbfacc6c8) = 0
ioctl(4, VIDIOC_QBUF, 0xbfacc61c) = 0
1、提取有用的信息,得到关键函数
下面分析的源码linux版本为linux-2.6.31.14
- 以下为使用到的
ioctl code for video
#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability) //获取设备能力
#define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc) //获取设备支持的帧格式
#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format) //获取当前设备帧格式
#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format) //设置当前设备帧格式
#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers)//申请内核空间缓冲区
#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer) //获取内核空间单帧缓冲区信息
#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer) //将单帧缓冲区放入采集入队队列
#define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer) //从采集出队队列取出单帧缓冲区
#define VIDIOC_STREAMON _IOW('V', 18, int) //开始视频流采集
#define VIDIOC_STREAMOFF _IOW('V', 19, int) //关闭视频流采集
根据上述log中提取到的信息的宏定义,去xawtv源码中进行搜索,得到以下信息:
/*----- 1~7 在v4l2_open() ------*/
1. open
2. ioctl(4, VIDIOC_QUERYCAP
/*----- 3~7 在get_device_capabilities() ------*/
3. for()
ioctl(4, VIDIOC_ENUMINPUT // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
4. for()
ioctl(4, VIDIOC_ENUMSTD // 列举标准(制式), 不是必需的
5. for()
ioctl(4, VIDIOC_ENUM_FMT // 列举格式
6. ioctl(4, VIDIOC_G_PARM
7. for()
ioctl(4, VIDIOC_QUERYCTRL // 查询属性(比如说亮度值最小值、最大值、默认值)
/*----- 8~10 在v4l2_read_attr() ------*/
8. ioctl(4, VIDIOC_G_STD // 获得当前使用的标准(制式), 不是必需的
9. ioctl(4, VIDIOC_G_INPUT
10. ioctl(4, VIDIOC_G_CTRL // 获得当前属性, 比如亮度是多少
11. ioctl(4, VIDIOC_TRY_FMT // 试试能否支持某种格式
12. ioctl(4, VIDIOC_S_FMT // 设置摄像头使用某种格式
/*----- 13~16 在v4l2_start_streaming() ------*/
13. ioctl(4, VIDIOC_REQBUFS // 请求系统分配缓冲区
14. for()
ioctl(4, VIDIOC_QUERYBUF // 查询所分配的缓冲区
mmap
15. for ()
ioctl(4, VIDIOC_QBUF // 把缓冲区放入队列
16. ioctl(4, VIDIOC_STREAMON // 启动摄像头
/*----- 在v4l2_write_attr() ------*/
17. for ()
ioctl(4, VIDIOC_S_CTRL // 设置属性
ioctl(4, VIDIOC_S_INPUT // 设置输入源
ioctl(4, VIDIOC_S_STD / 设置标准(制式), 不是必需的
/*----- 在v4l2_nextframe()/v4l2_waiton() ------*/
18. v4l2_queue_all
v4l2_waiton
for ()
{
select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979})
ioctl(4, VIDIOC_DQBUF // de-queue, 把缓冲区从队列中取出
// 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据
ioctl(4, VIDIOC_QBUF // 把缓冲区放入队列
}
- 得到测试vivi时xawtv的几个关键函数:
1. v4l2_open()
2. v4l2_read_attr()/v4l2_write_attr()
3. v4l2_start_streaming()
4. v4l2_nextframe()/v4l2_waiton()
2、分析vivi.c
源码
2.1 关键ioctl
在vivi.c
中,我们看到有如下vivi_ioctl_ops
结构体:
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_querycap = vidioc_querycap, //获取设备能力
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, //获取设备支持的帧格式
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, //获取当前设备帧格式
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, //设置当前设备帧格式
.vidioc_reqbufs = vidioc_reqbufs, //申请内核空间缓冲区
.vidioc_querybuf = vidioc_querybuf, //获取内核空间单帧缓冲区信息
.vidioc_qbuf = vidioc_qbuf, //将单帧缓冲区放入采集入队队列
.vidioc_dqbuf = vidioc_dqbuf, //从采集出队队列取出单帧缓冲区
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon, //开始视频流采集
.vidioc_streamoff = vidioc_streamoff, //关闭视频流采集
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
经过测试,实际提取出11个关键的ioctl
// 表示它是一个摄像头设备
.vidioc_querycap = vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
// 启动/停止
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
2.2 分析数据的获取过程
vivi进行数据获取时,根据log的信息,数据的获取过程如下,在linux内核源码中v4l2-ioctl.c
文件中找到对应的操作项。
/*----- 13~16 在v4l2_start_streaming() ------*/
13. ioctl(4, VIDIOC_REQBUFS // 请求系统分配缓冲区
14. for()
ioctl(4, VIDIOC_QUERYBUF // 查询所分配的缓冲区
mmap
15. for ()
ioctl(4, VIDIOC_QBUF // 把缓冲区放入队列
16. ioctl(4, VIDIOC_STREAMON // 启动摄像头
/*----- 在v4l2_write_attr() ------*/
17. for ()
ioctl(4, VIDIOC_S_CTRL // 设置属性
ioctl(4, VIDIOC_S_INPUT // 设置输入源
ioctl(4, VIDIOC_S_STD / 设置标准(制式), 不是必需的
/*----- 在v4l2_nextframe()/v4l2_waiton() ------*/
18. v4l2_queue_all
v4l2_waiton
for ()
{
select(5, [4], NULL, NULL, {5, 0}) = 1 (in [4], left {4, 985979})
ioctl(4, VIDIOC_DQBUF // de-queue, 把缓冲区从队列中取出
// 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据
ioctl(4, VIDIOC_QBUF // 把缓冲区放入队列
}
2.2.1 请求系统分配缓冲区VIDIOC_REQBUFS
通过追踪源码可知,其最终调用的是vivi.c的vidioc_reqbufs()
,此时的vidioc_reqbufs()
分配的并不是真正的缓存,只是缓冲区的头部信息。
2.2.2 查询与所分配的缓冲区VIDIOC_QUERYBUF
通过追踪源码可知,其最终调用的是vivi.c的vidioc_querybuf()
,此时进行的是查询2.2.1中分配的缓冲区的状态
具体的状态信息为:数据格式、大小、每一行长度、高度
2.2.3 映射缓冲区mmap
此时才是正在的分配缓存,通过2.2.1头部信息的中指针指向缓存(调用到才分配内存)
2.2.4 把缓冲区放入队列VIDIOC_QBUF
把缓冲区放入队列中,采用的是尾插法。
2.2.5 启动摄像头VIDIOC_STREAMON
2.2.6 用select查询是否有数据
其采用的是poll机制,即硬件有数据时,程序才被唤醒
那么谁来唤醒呢?由于vivi.c
是一个虚拟驱动,其数据产生通过内核线程在一定时间内调用函数,产生数据来唤醒进程
2.2.7 有数据后从队列里取出缓冲区VIDIOC_DQBUF
对于队列中的那么多缓冲区,APP如何知道哪一个缓冲区有数据?
通过调用vidioc_dqbuf()
来获取有数据的缓存区,处理完该缓存区的数据后,从队列中删除该缓存区,返回数据给APP。
2.2.8 应用程序读取数据
根据2.2.6的VIDIOC_DQBUF
所得到缓冲区状态,知道是哪一个缓冲区有数据,去读对应的地址(该地址来自前面的mmap),之后返回2.2.4把缓冲区重新放入队列的尾部,进行下一缓冲区数据的读取。
3、总结
对于其缓冲区的数据处理过程如下: