虚拟摄像头vivi驱动分析:
1.通过分析xawtv这个应用程序所涉及的系统调用来间接分析vivi的驱动程序。我们用运行时显示的信息作为切入点:strace -o xawtv.log xawtv 将运行xawtv后的打印信息保存入xawtv.log中,并且自动运行xawtv。
2.xawtv的几大函数:
1.v4l2_open
2.v4l2_read_atrr/v4l2_write_attr
3.v4l2_start_streaming
4.v4l2_nextframe/v4l2_waiton
3.整体分析:xawtv调用一系列的ioctl函数已经相应的参数发送到驱动程序,vivi驱动对参数进行分析和处理,下面进行具体说明。
4.首先要说的是xawtv查询驱动中摄像头的属性capabilities,驱动中已经设置其为cap->capabilities=V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;//表示此驱动支持摄像头采集(CAPTURE),同时支持应用层调用ioctl函数进行的QBUF,DQBUF等涉及队列的数据操作,以及支持应用层调用read,write接口进行的数据操作这两种摄像头数据读取方式。
5.第二个ioctl命令是ENUM_INPUT 设置输入源。在xawtv界面中可以设置输入源,Video source:Camera0,camer1...。为使看见其他输入源的效果,可让vivi驱动中vivi.c文件中的vidioc_streamoff函数都返回0,即修改为:videobuf_streamoff(&fh->vb_vidq); return 0;即可。然后重新编译并加载驱动即可。然后点击应用程序的其他输入源也能显示了。总之,当我们的摄像头只有一个输入源时,用于选择输入源的操作函数可以省略,有VIDIOC_ENUMINPUT,VIDIOC_G_INPUT,VIDIOC_S_INPUT这三个ioctl命令。
6.VIDIOC_ENUMSTD和VIDIOC_G_STD这些涉及标准制式的操作可以省略,底层中file_operations中的.tvnorms=V4L2_STD_525_60;
.current_norm=V4L2_STD_NTSC_M;//这些用于列举、设置、获得TV制式的操作可以省略,对图像显示没有影响。
7.用于列举、获得、测试、设置摄像头所提供的数据的格式的操作有:
.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,
这些操作是必不可少的,最好不要去掉。
8.缓冲区操作:申请、查询、放入队列、取出队列这些操作当然是必不可少的,这里就不说了。
9.查询、获得、设置属性操作来对亮度、对比度、白平衡这些摄像头数据进行操作。有vidioc_queryctrl,vidioc_g_ctrl,vidioc_s_ctrl这三个。
这些可以省略,省略后只是不能修改属性了。
10.启动摄像头命令vidioc_streamon,关闭摄像头命令vidioc_streamoff当然是必不可少的。
11.继续分析数据的获取过程:
1.请求分配缓冲区:ioctl(4,VIDIOC_REQBUFS); videobuf_reqbufs(队列,v4l2_requestbuffers);//第二个参数表示要分配多少个缓冲区,一般至少有2个缓冲区。队列在open函数里用videobuf_vmalloc_init函数中进行了初始化。这个IOCTL只是分配了缓冲区的头部信息,真正的缓存还没有分配。
2.查询映射缓冲区:应用层调用ioctl(4,VIDIOC_QUERYBUF);//获得缓冲区的数据格式、大小、每一行长度、高度信息,但是实际上驱动还并未分配内存。
应用层调用mmap函数后,驱动调用vivi_mmap函数,给缓冲区分配空间,这时才有实际的缓冲区。
3.把缓冲区放入队列:ioctl(4,VIDIOC_QBUF)。其中调用函数list_add_tail把缓冲区放入队列的尾部。
4.启动摄像头:ioctl(4,VIDIOC_STREAMON)
5.用select查询是否有数据,没有则阻塞。驱动中有产生数据,唤醒进程。应用层从队列的头部获得缓冲区,如果没有数据则休眠。同时,驱动中的vivi_thread_tick函数中会创建一个内核线程,构造虚拟摄像头数据,每隔30毫秒唤醒进程,应用程序调用IOCTL的vidioc_dqbuf来。
6.有数据后从队列中取出缓冲区: ioctl(4,VIDIOC_DQBUF)。
在队列里获得有数据的缓冲区,把它从队列中删掉,然后把这个缓冲区的状态返回给APP。应用程序根据VIDIOC_DQBUF所得到缓冲区状态,知道是哪一个缓冲区有数据,就去读对应的地址(该地址来自前面的mmap)。队列的处理总是从头开始,用完之后再放入尾部。
7.怎样写摄像头驱动:
1.分配video_device结构体
2.设置
.fops
.ioctl_ops(里面有11项)
如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops结构体。
3.注册结构体