• 虚拟摄像头驱动程序彻底分析


    xawtv(虚拟摄像头)涉及的vivi驱动的系统调用。
    步骤:
    1.打开摄像头设备:open(“/dev/video0”, O_RDWR|O_LARGEFILE) = 4
    2.查询 当前driver是否合乎规范:ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbfaccd44) = 0
    3.获得摄像头所提供数据的格式:ioctl(4, VIDIOC_G_FMT or VT_SENDSIG, 0xbfaccc78) = 0
    4.for循环列举出摄像头所支持的格式:ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbfaccbec) = 0(非必须的)
    5.查询 当前driver是否合乎规范,列举性能:ioctl(4, VIDIOC_QUERYCAP
    6.获取当前使用源:ioctl(4, VIDIOC_G_INPUT, 0xbfacca2c) = 0(非必须的)
    7.列举所有的输入源:ioctl(4, VIDIOC_ENUMINPUT, 0xbfacca2c) = 0(非必须的)
    8.查询属性,比如亮度和对比度:ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0xbfacca78) = -1 EINVAL (Invalid argument)
    9.ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0x9b40998) = 0
    10.for循环查询到底有多少个输入点:ioctl(4, VIDIOC_ENUMINPUT, 0x9b40acc) = 0
    11.for循环列举标准(制式):ioctl(4, VIDIOC_ENUMSTD, 0x9b40f8c) = 0
    12.for循环列举图片数据的格式:ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0x9b4138c) = 0
    13.获取参数:ioctl(4, VIDIOC_G_PARM, 0x9b40a00) = 0
    14.for循环查询属性(比如亮度值最小值/最大值/默认值):ioctl(4, MATROXFB_TVOQUERYCTRL or VIDIOC_QUERYCTRL, 0x9b41b8c) = 0
    15.获得当前使用的标准:ioctl(4, VIDIOC_G_STD, 0xbfacce08) = 0
    16.列出当前使用的通道:ioctl(4, VIDIOC_G_INPUT, 0xbfacce1c) = 0
    17.当前所使用的属性:ioctl(4, MATROXFB_G_TVOCTRL or VIDIOC_G_CTRL, 0xbfacce14) = 0
    18.尝试能否 支持某种格式:ioctl(4, VIDIOC_TRY_FMT, 0x9b42ca4) = 0
    19.如果支持某种格式,设置摄像头使用某种格式:ioctl(4, VIDIOC_S_FMT or VT_RELDISP, 0xbfacc754) = 0
    20.请求系统分配多个缓冲区:ioctl(4, VIDIOC_REQBUFS or VT_DISALLOCATE, 0x9b42d80) = 0
    21.查询系统所分配的缓冲区:ioctl(4, VIDIOC_QUERYBUF or VT_RESIZE, 0x9b42d94) = 0
    22.for循环把缓冲区放入队列:ioctl(4, VIDIOC_QBUF, 0x9b42d94) = 0
    23.启动摄像头:ioctl(4, VIDIOC_STREAMON, 0xbfacca2c) = 0
    24.for循环设置属性:ioctl(4, MATROXFB_S_TVOCTRL or VIDIOC_S_CTRL, 0xbfacce10) = 0
    25.设置输入源:ioctl(4, VIDIOC_S_INPUT, 0xbfacce84) = 0
    26.设置标准(制式)ioctl(4, VIDIOC_S_STD, 0x9b40f90) = 0
    26.for循环
    ①查看队列中的缓冲区是否有数据,有数据就取出数据: 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

    虚拟摄像头驱动程序的数据如何获得:
    应用程序通过调用REQBUFS请求系统分配缓冲区,调用QUERYBUF查询缓冲区。调用mmap,告诉应用程序在哪个地址访问缓冲区。调用QBUF将缓冲区放入驱动程序中的某个队列中。调用STREAMON启动摄像头。调用select程序查询缓冲区是否有数据。如果驱动程序有数据之后,就会唤醒应用程序,唤醒完应用程序后,应用程序就会调用DQBUF,将缓冲区从队列中取出,获得缓冲区数据,进行处理。处理完后再将缓冲区放回队列。

    重点函数:

       1.v4l2_open打开摄像头设备,get_device_capabilities(h)获得摄像头属性
        2. v4l2_read_attr/v4l2_write_attr(读/写属性)
        3. v4l2_start_streaming(申请buf)
        4. v4l2_nextframe/v4l2_waiton
    
    • 1
    • 2
    • 3
    • 4

    摄像头驱动程序必需的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,    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    怎么写摄像头驱动程序:

    1. 分配video_device:video_device_alloc
    2. 设置
      .fops
      .ioctl_ops (里面需要设置11项)
      如果要用内核提供 的缓冲区操作函数,还需要构造一个videobuf_queue_ops
    3. 注册: video_register_device

    本文章源于韦东山嵌入式

  • 相关阅读:
    【wpf】神器Fody简化属性通知 INotifyPropertyChanged
    面试被问到HashMap 底层原理?看完这边文章绝对不慌!
    100天精通Golang(基础入门篇)——第5天: Go语言中的数据类型学习
    【MySQL】存储引擎
    阿里云OSS前端直传+net core后端签名
    TRC 链格孢菌毒素和基因毒素丨艾美捷 TRC Alternariol 9-龙胆二糖苷
    mysqldump 备份详解
    C - Matrix Reducing
    使用canvas做了一个最简单的网页版画板,5分钟学会
    Linux系统InfluxDB数据和日志目录迁移教程
  • 原文地址:https://blog.csdn.net/qq_44823010/article/details/126948075