乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      徹底分析虛擬視頻驅(qū)動(dòng)vivi(三)

       印度阿三17 2019-03-28

      在Ubuntu系統(tǒng)中接上usb攝像頭設(shè)備時(shí),系統(tǒng)會(huì)自動(dòng)安裝對(duì)應(yīng)的usb設(shè)備驅(qū)動(dòng)程序。
      我們現(xiàn)在要使用自己編譯的vivi驅(qū)動(dòng),該怎么辦呢?
        1.先安裝系統(tǒng)自帶的vivi驅(qū)動(dòng)和它所依賴的所有驅(qū)動(dòng):sudo modprobe vivi ;
        2.卸載原有的vivi驅(qū)動(dòng) : sudo rmmod vivi ;
        3.裝載自己的驅(qū)動(dòng) :sudo insmod ./vivi.ko ;
      然后 ls /dev/video* ,可以看到有一個(gè)video設(shè)備節(jié)點(diǎn) /dev/video0 ,即對(duì)應(yīng)的是vivi虛擬出來(lái)的視頻設(shè)備。
      我們可以直接閱讀xawtv源碼,從main函數(shù)開(kāi)始一路分析它調(diào)用vivi驅(qū)動(dòng)的過(guò)程,但是這個(gè)過(guò)程會(huì)非常漫長(zhǎng),因?yàn)樗苏{(diào)用vivi驅(qū)動(dòng)之外
      ,還會(huì)做許多其他的準(zhǔn)備工作。我們可以通過(guò)strace 這個(gè)命令來(lái)跟蹤調(diào)用過(guò)程。
      本文目的:

      (通過(guò)追蹤應(yīng)用程序xawtv調(diào)用驅(qū)動(dòng)vivi的過(guò)程,使之生成對(duì)應(yīng)的TXT文件,在文件中搜索 /dev/video*字段,得到一系列函數(shù),再打開(kāi)xawtv源碼,得到vivi驅(qū)動(dòng)必須的系統(tǒng)調(diào)用,進(jìn)而分析驅(qū)動(dòng)框架)

      一、xawtv所涉及的vivi驅(qū)動(dòng)的系統(tǒng)調(diào)用

      使用方法 :執(zhí)行 strace -o xawtv.txt xawtv ,生成了調(diào)用過(guò)程xawtv.txt

      搜索 /dev/video0,得到如下:

      open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
      ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0x95b8998) = -1 EINVAL (Invalid argument)
      close(4) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?= 0
      open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
      .....
      發(fā)現(xiàn)打開(kāi)了兩次,open成功之后得到file_fd =4,后面有一大堆ioctl,把所有的ioctl列舉出來(lái),即可得到ioctl的過(guò)程:

      open("/dev/video0", O_RDWR|O_LARGEFILE) = 4
      ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbff6c704) = 0
      ioctl(4, VIDIOC_G_FMT or VT_SENDSIG, 0xbff6c638) = 0
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, 0xc02c564a, 0xbff6c518)        = -1 EINVAL (Invalid argument)
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, 0xc02c564a, 0xbff6c518)        = -1 EINVAL (Invalid argument)
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, 0xc02c564a, 0xbff6c518)        = -1 EINVAL (Invalid argument)
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = 0
      ioctl(4, VIDIOC_ENUM_FMT or VT_SETMODE, 0xbff6c5ac) = -1 EINVAL (Invalid argument)
      ioctl(4, VIDIOC_QUERYCAP or VT_OPENQRY, 0xbff6c544) = 0
      ioctl(4, VIDIOC_G_INPUT, 0xbff6c3ec)    = 0
      ioctl(4, VIDIOC_ENUMINPUT, 0xbff6c3ec)  = 0
      View Code

      xawtv涉及的vivi驅(qū)動(dòng)的系統(tǒng)調(diào)用:

      // 1~7都是在v4l2_open里調(diào)用
      1. open
      2. ioctl(4, VIDIOC_QUERYCAP
      
      // 3~7 都是在get_device_capabilities里調(diào)用
      3. for()
              ioctl(4, VIDIOC_ENUMINPUT   // 列舉輸入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
      4. for()
              ioctl(4, VIDIOC_ENUMSTD     // 列舉標(biāo)準(zhǔn)(制式), 不是必需的
      5. for()        
              ioctl(4, VIDIOC_ENUM_FMT    // 列舉格式
      
      6. ioctl(4, VIDIOC_G_PARM
      7. for()
              ioctl(4, VIDIOC_QUERYCTRL   // 查詢屬性(比如說(shuō)亮度值最小值、最大值、默認(rèn)值)
      
      // 8~10都是通過(guò)v4l2_read_attr來(lái)調(diào)用的        
      8.  ioctl(4, VIDIOC_G_STD            // 獲得當(dāng)前使用的標(biāo)準(zhǔn)(制式), 不是必需的
      9.  ioctl(4, VIDIOC_G_INPUT 
      10. ioctl(4, VIDIOC_G_CTRL           // 獲得當(dāng)前屬性, 比如亮度是多少
      
      11. ioctl(4, VIDIOC_TRY_FMT          // 試試能否支持某種格式
      12. ioctl(4, VIDIOC_S_FMT            // 設(shè)置攝像頭使用某種格式
      
      
      // 13~16在v4l2_start_streaming
      13. ioctl(4, VIDIOC_REQBUFS          // 請(qǐng)求系統(tǒng)分配緩沖區(qū)
      14. for()
              ioctl(4, VIDIOC_QUERYBUF         // 查詢所分配的緩沖區(qū)
              mmap        
      15. for ()
              ioctl(4, VIDIOC_QBUF             // 把緩沖區(qū)放入隊(duì)列        
      16. ioctl(4, VIDIOC_STREAMON             // 啟動(dòng)攝像頭
      
      
      // 17里都是通過(guò)v4l2_write_attr來(lái)調(diào)用的
      17. for ()
              ioctl(4, VIDIOC_S_CTRL           // 設(shè)置屬性
          ioctl(4, VIDIOC_S_INPUT              // 設(shè)置輸入源
          ioctl(4, VIDIOC_S_STD                // 設(shè)置標(biāo)準(zhǔn)(制式), 不是必需的
      
      // 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, 把緩沖區(qū)從隊(duì)列中取出
                  // 處理, 之以已經(jīng)通過(guò)mmap獲得了緩沖區(qū)的地址, 就可以直接訪問(wèn)數(shù)據(jù)        
                  ioctl(4, VIDIOC_QBUF                 // 把緩沖區(qū)放入隊(duì)列
              }

      ?

      由上可知xawtv的幾大函數(shù):

        1. v4l2_open

        2. v4l2_read_attr/v4l2_write_attr

        3. v4l2_start_streaming

        4. v4l2_nextframe/v4l2_waiton

      攝像頭驅(qū)動(dòng)程序必需的11個(gè)ioctl:

      ?

      // 表示它是一個(gè)攝像頭設(shè)備
          .vidioc_querycap      = vidioc_querycap,
      
          /* 用于列舉、獲得、測(cè)試、設(shè)置攝像頭的數(shù)據(jù)的格式 */
          .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,
      
          /* 緩沖區(qū)操作: 申請(qǐng)/查詢/放入隊(duì)列/取出隊(duì)列 */
          .vidioc_reqbufs       = vidioc_reqbufs,
          .vidioc_querybuf      = vidioc_querybuf,
          .vidioc_qbuf          = vidioc_qbuf,
          .vidioc_dqbuf         = vidioc_dqbuf,
      
          // 啟動(dòng)/停止
          .vidioc_streamon      = vidioc_streamon,
          .vidioc_streamoff     = vidioc_streamoff,    

      ?

      分析數(shù)據(jù)的獲取過(guò)程:

      1. 請(qǐng)求分配緩沖區(qū): ioctl(4, VIDIOC_REQBUFS          // 請(qǐng)求系統(tǒng)分配緩沖區(qū)
                              videobuf_reqbufs(隊(duì)列, v4l2_requestbuffers) // 隊(duì)列在open函數(shù)用videobuf_queue_vmalloc_init初始化
                              // 注意:這個(gè)IOCTL只是分配緩沖區(qū)的頭部信息,真正的緩存還沒(méi)有分配呢
                     //在驅(qū)動(dòng)程序有一條原則,這些資源只有在我們用到的時(shí)候才進(jìn)行分配
      2. 查詢映射緩沖區(qū):
      ioctl(4, VIDIOC_QUERYBUF         // 查詢所分配的緩沖區(qū)
              videobuf_querybuf        // 獲得緩沖區(qū)的數(shù)據(jù)格式、每一行長(zhǎng)度、高度、緩沖區(qū)使用狀態(tài)、在內(nèi)核空間的偏移地址、緩沖區(qū)長(zhǎng)度等            
      mmap(參數(shù)里有"大小")   // 在這里才分配緩存
              v4l2_mmap
                  vivi_mmap
                      videobuf_mmap_mapper
                          videobuf-vmalloc.c里的__videobuf_mmap_mapper
                                  mem->vmalloc = vmalloc_user(pages);   // 在這里才給緩沖區(qū)分配空間
      
      3. 把緩沖區(qū)放入隊(duì)列:
      ioctl(4, VIDIOC_QBUF             // 把緩沖區(qū)放入隊(duì)列        
          videobuf_qbuf
              q->ops->buf_prepare(q, buf, field);  // 調(diào)用驅(qū)動(dòng)程序提供的函數(shù)做些預(yù)處理
              list_add_tail(&buf->stream, &q->stream);  // 把緩沖區(qū)放入隊(duì)列的尾部
              q->ops->buf_queue(q, buf);           // 調(diào)用驅(qū)動(dòng)程序提供的"入隊(duì)列函數(shù)"
              
      
      4. 啟動(dòng)攝像頭
      ioctl(4, VIDIOC_STREAMON
          videobuf_streamon
              q->streaming = 1;
              
      
      5. 用select查詢是否有數(shù)據(jù)
                // 驅(qū)動(dòng)程序里必定有: 產(chǎn)生數(shù)據(jù)、喚醒進(jìn)程
                v4l2_poll
                      vdev->fops->poll
                          vivi_poll   
                              videobuf_poll_stream
                                  // 從隊(duì)列的頭部獲得緩沖區(qū)
                                  buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
                                  
                                  // 如果沒(méi)有數(shù)據(jù)則休眠,在buf->done這里進(jìn)行休眠                            
                                  poll_wait(file, &buf->done, wait);
      
          誰(shuí)來(lái)產(chǎn)生數(shù)據(jù)、誰(shuí)來(lái)喚醒它?
          內(nèi)核線程vivi_thread每30MS執(zhí)行一次,它調(diào)用
          vivi_thread_tick
              vivi_fillbuff(fh, buf);  // 構(gòu)造數(shù)據(jù) 
              wake_up(&buf->vb.done);  // 喚醒進(jìn)程
                
      6. 有數(shù)據(jù)后從隊(duì)列里取出緩沖區(qū)
      // 有那么多緩沖區(qū),APP如何知道哪一個(gè)緩沖區(qū)有數(shù)據(jù)?調(diào)用VIDIOC_DQBUF
      ioctl(4, VIDIOC_DQBUF 
          vidioc_dqbuf   
              // 在隊(duì)列里獲得有數(shù)據(jù)的緩沖區(qū)
              retval = stream_next_buffer(q, &buf, nonblocking);
              
              // 把它從隊(duì)列中刪掉
              list_del(&buf->stream);
              
              // 把這個(gè)緩沖區(qū)的狀態(tài)返回給APP
              videobuf_status(q, b, buf, q->type);
              
      7. 應(yīng)用程序根據(jù)VIDIOC_DQBUF所得到緩沖區(qū)狀態(tài),知道是哪一個(gè)緩沖區(qū)有數(shù)據(jù)
         就去讀對(duì)應(yīng)的地址(該地址來(lái)自前面的mmap)
      

      ?

      總結(jié)數(shù)據(jù)獲取過(guò)程:(圖片來(lái)自:https://blog.csdn.net/ljmiaw/article/details/72801456)


      怎么寫(xiě)攝像頭驅(qū)動(dòng)程序:
      1. 分配video_device:video_device_alloc
      2. 設(shè)置
        .fops
        .ioctl_ops (里面需要設(shè)置11項(xiàng))
        如果要用內(nèi)核提供的緩沖區(qū)操作函數(shù),還需要構(gòu)造一個(gè)videobuf_queue_ops
      3. 注冊(cè): video_register_device

      ?

      ?

      ?

      怎么寫(xiě)攝像頭驅(qū)動(dòng)程序:

      1. 分配video_device:video_device_alloc

      2. 設(shè)置 .fops .ioctl_ops (里面需要設(shè)置11項(xiàng)) 如果要用內(nèi)核提供的緩沖區(qū)操作函數(shù),還需要構(gòu)造一個(gè)videobuf_queue_ops

      3. 注冊(cè): video_register_device

      ?

      來(lái)源:http://www./content-4-151051.html

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多