基於V4L2的視頻驅動開發(2)

三、            V4L2 API 及數據結構

V4L2 是 V4L 的升級版本,爲 linux 下視頻設備程序提供了一套接口規範。包括一套數據結構和底層 V4L2 驅動接口。

1 、常用的結構體在內核目錄 include/linux/videodev2.h 中定義

   struct v4l2_requestbuffers // 申請幀緩衝,對應命令 VIDIOC_REQBUFS 
   struct v4l2_capability // 視頻設備的功能,對應命令 VIDIOC_QUERYCAP 
   struct v4l2_input   // 視頻輸入信息,對應命令 VIDIOC_ENUMINPUT
   struct v4l2_standard // 視頻的制式,比如PAL ,NTSC ,對應命令 VIDIOC_ENUMSTD 
   struct v4l2_format     // 幀的格式,對應命令VIDIOC_G_FMT 、VIDIOC_S_FMT 等 
   struct v4l2_buffer   // 驅動 中的一幀圖像緩存,對應命令VIDIOC_QUERYBUF

   struct v4l2_crop   // 視頻信號矩形邊框

      v4l2_std_id    // 視頻制式

2 、常用的 IOCTL 接口命令也在 include/linux/videodev2.h 中定義

 

VIDIOC_REQBUFS // 分配內存  

VIDIOC_QUERYBUF // 把 VIDIOC_REQBUFS 中分配的數據緩存轉換成物理地址

VIDIOC_QUERYCAP // 查詢驅動功能

VIDIOC_ENUM_FMT // 獲取當前驅動支持的視頻格式

VIDIOC_S_FMT // 設置當前驅動的頻捕獲格式

VIDIOC_G_FMT // 讀取當前驅動的頻捕獲格式

VIDIOC_TRY_FMT // 驗證當前驅動的顯示格式

VIDIOC_CROPCAP // 查詢驅動的修剪能力

VIDIOC_S_CROP // 設置視頻信號的矩形邊框

VIDIOC_G_CROP // 讀取視頻信號的矩形邊框

VIDIOC_QBUF // 把數據從緩存中讀取出來

VIDIOC_DQBUF // 把數據放回緩存隊列

VIDIOC_STREAMON // 開始視頻顯示函數

VIDIOC_STREAMOFF // 結束視頻顯示函數

VIDIOC_QUERYSTD // 檢查當前視頻設備支持的標準,例如 PAL 或 NTSC 。

 

 

3 、操作流程

V4L2 提供了很多訪問接口,你可以根據具體需要選擇操作方法。需要注意的是,很少有驅動完全實現了所有的接口功能。所以在使用時需要參考驅動源碼,或仔細閱讀驅動提供者的使用說明。

下面列舉出一種操作的流程,供參考。

( 1 )打開設備文件

int fd = open(Devicename,mode);

    Devicename : /dev/video0 、 /dev/video1 ……

     Mode : O_RDWR [| O_NONBLOCK]

       如果使用非阻塞模式調用視頻設備,則當沒有可用的視頻數據時,不會阻塞,而立刻返回。

( 2 )取得設備的 capability

struct v4l2_capability capability ;

              int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);     

看看設備具有什麼功能,比如是否具有視頻輸入特性。

( 3 )選擇視頻輸入

struct v4l2_input input ;

……初始化 input

int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);     

一個視頻設備可以有多個視頻輸入。如果只有一路輸入,這個功能可以沒有。

( 4 )檢測視頻支持的制式

            v4l2_std_id std;

            do {

                   ret = ioctl(fd, VIDIOC_QUERYSTD, &std);

            } while (ret == -1 && errno == EAGAIN);

            switch (std) {

                 case V4L2_STD_NTSC: 

                       //……

         case V4L2_STD_PAL:

             //……

}

( 5 )設置視頻捕獲格式

struct v4l2_format fmt;

fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;

fmt.fmt.pix.height = height;

fmt.fmt.pix.width = width;

fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

ret = ioctl(fd, VIDIOC_S_FMT, &fmt);

if(ret) {

perror("VIDIOC_S_FMT/n");

close(fd);

return -1;

}

( 6 )向驅動申請幀緩存

     struct v4l2_requestbuffers  req;

if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {

           return -1;

}

       v4l2_requestbuffers 結構中定義了緩存的數量,驅動會據此申請對應數量的視頻緩存。多個緩存可以用於建立 FIFO ,來提高視頻採集的效率。

( 7 )獲取每個緩存的信息,並 mmap 到用戶空間

typedef struct VideoBuffer {

    void   *start;

    size_t  length;

} VideoBuffer;

                                                              

VideoBuffer*       buffers = calloc( req.count, sizeof(*buffers) );

struct v4l2_buffer    buf;

 

for (numBufs = 0; numBufs < req.count; numBufs++) {// 映射所有的緩存

    memset( &buf, 0, sizeof(buf) );

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    buf.memory = V4L2_MEMORY_MMAP;

    buf.index = numBufs;

      if (ioctl( fd, VIDIOC_QUERYBUF, &buf) == -1) {// 獲取到對應 index 的緩存信息,此處主要利用 length 信息及offset 信息來完成後面的 mmap 操作。

        return -1;

    }

 

    buffers[numBufs].length = buf.length;

    // 轉換成相對地址

    buffers[numBufs].start = mmap(NULL, buf.length,

        PROT_READ | PROT_WRITE,

        MAP_SHARED,

        fd, buf.m.offset);

 

    if (buffers[numBufs].start == MAP_FAILED) {

        return -1;

    }

 

( 8 )開始採集視頻

int buf_type= V4L2_BUF_TYPE_VIDEO_CAPTURE ;

int ret = ioctl(fd, VIDIOC_STREAMON, &buf_type);

 

( 9 )取出 FIFO 緩存中已經採樣的幀緩存

struct v4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=0;// 此值由下面的 ioctl 返回

if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)

{

    return -1;

}

根據返回的 buf.index 找到對應的 mmap 映射好的緩存,取出視頻數據。

( 10 )將剛剛處理完的緩衝重新入隊列尾,這樣可以循環採集

if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {

    return -1;

}

( 11 )停止視頻的採集

int ret = ioctl(fd, VIDIOC_STREAMOFF, &buf_type);

( 12 )關閉視頻設備

close(fd);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章