v4l2的學習建議和流程解析


  v4l2,一開始聽到這個名詞的時候,以為又是一個很難很難的模塊,涉及到視頻的處理,后來在網上各種找資料后,才發現其實v4l2已經分裝好了驅動程序,只要我們根據需要調用相應的接口和函數,從而實現視頻的獲取和處理。只要認真的看幾篇文章就對v4l2有一定的了解了,由於是第一次接觸,網上的資料良莠不齊,難得可以找到幾篇自己感覺很不錯的。記錄下來:(沒必要看太多,很多都是一樣的意思)

http://www.embedu.org/Column/Column320.htm   這篇是不錯的介紹,很討厭有彈窗

http://www.cnblogs.com/emouse/archive/2013/03/04/2943243.html  這個可以作為第一篇來看,博主整理的不錯

http://blog.chinaunix.net/uid-11765716-id-2855735.html    這篇也比較詳細

http://blog.csdn.net/ddddwant/article/details/8475211   這篇提到的問題和我遇到的一樣,花屏了,內存沒有讀取好

http://my.oschina.net/u/1024767/blog/210801#OSC_h2_14    對capture.c文件的解讀

http://blog.csdn.net/g_salamander/article/details/8107692    對各個結構體有比較好的說明

 

一、Video for Linux two

  v4l2為linux下視頻設備程序提供了一套接口規范。包括一套數據結構和底層V4L2驅動接口。只能在linux下使用。它使程序有發現設備和操作設備的能力。它主要是用一系列的回調函數來實現這些功能。像設置攝像頭的頻率、幀頻、視頻壓縮格式和圖像參數等等。當然也可以用於其他多媒體的開發,如音頻等。

  在Linux下,所有外設都被看成一種特殊的文件,成為“設備文件”,可以象訪問普通文件一樣對其進行讀寫。一般來說,采用V4L2驅動的攝像頭設備文是/dev/v4l/video0。為了通用,可以建立一個到/dev/video0的鏈接。V4L2支持兩種方式來采集圖像:內存映射方式(mmap)和直接讀取方式(read)。V4L2在include/linux/videodev.h文件中定義了一些重要的數據結構,在采集圖像的過程中,就是通過對這些數據的操作來獲得最終的圖像數據。Linux系統V4L2的能力可在Linux內核編譯階段配置,默認情況下都有此開發接口。V4L2從Linux 2.5.x版本的內核中開始出現。

  V4L2規范中不僅定義了通用API元素(Common API Elements),圖像的格式(Image Formats),輸入/輸出方法(Input/Output),還定義了Linux內核驅動處理視頻信息的一系列接口(Interfaces),這些接口主要有:

  視頻采集接口——Video Capture Interface;

  視頻輸出接口—— Video Output Interface;

  視頻覆蓋/預覽接口——Video Overlay Interface;

  視頻輸出覆蓋接口——Video Output Overlay Interface;

  編解碼接口——Codec Interface。

二、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        //視頻制式

常用結構體的內容:

struct v4l2_capability

{

u8 driver[16]; // 驅動名字

u8 card[32]; // 設備名字

u8 bus_info[32]; // 設備在系統中的位置

u32 version; // 驅動版本號

u32 capabilities; // 設備支持的操作

u32 reserved[4]; // 保留字段

};

  其中域 capabilities 代表設備支持的操作模式,常見的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一個視頻捕捉設備並且具有數據流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。

struct v4l2_format {  
    enum v4l2_buf_type type;  
    union {  
        struct v4l2_pix_format         pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */  
        struct v4l2_window             win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */  
        struct v4l2_vbi_format         vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */  
        struct v4l2_sliced_vbi_format  sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */  
        __u8   raw_data[200];                   /* user-defined */  
    } fmt;  
};  

enum v4l2_buf_type {  
    V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,  
    V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,  
    V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,  
    ...  
    V4L2_BUF_TYPE_PRIVATE              = 0x80,  
};  
  
struct v4l2_pix_format {  
    __u32                   width;  
    __u32                   height;  
    __u32                   pixelformat;  
    enum v4l2_field         field;  
    __u32                   bytesperline;   /* for padding, zero if unused */  
    __u32                   sizeimage;  
    enum v4l2_colorspace    colorspace;  
    __u32                   priv;           /* private data, depends on pixelformat */  
};  

  常見的捕獲模式為 V4L2_BUF_TYPE_VIDEO_CAPTURE 即視頻捕捉模式,在此模式下 fmt 聯合體采用域 v4l2_pix_format:其中 width 為視頻的寬、height 為視頻的高、pixelformat 為視頻數據格式(常見的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、bytesperline 為一行圖像占用的字節數、sizeimage 則為圖像占用的總字節數、colorspace 指定設備的顏色空間。

 

struct v4l2_requestbuffers 與 VIDIOC_REQBUFS ,VIDIOC_REQBUFS 命令通過結構 v4l2_requestbuffers 請求驅動申請一片連續的內存用於緩存視頻信息:

struct v4l2_requestbuffers { 
    __u32                   count; 
    enum v4l2_buf_type      type; 
    enum v4l2_memory        memory; 
    __u32                   reserved[2]; 
}; 
enum v4l2_memory { 
    V4L2_MEMORY_MMAP             = 1, 
    V4L2_MEMORY_USERPTR          = 2, 
    V4L2_MEMORY_OVERLAY          = 3, 
}; 

count 指定根據圖像占用空間大小申請的緩存區個數,type 為視頻捕獲模式,memory 為內存區的使用方式。

struct v4l2_buffer { 
    __u32   index; 
    enum v4l2_buf_type    type; 
    __u32    bytesused; 
    __u32    flags; 
    enum v4l2_field  field; 
    struct timeval    timestamp; 
    struct v4l2_timecode   timecode; 
    __u32     sequence; 
 
    /* memory location */ 
    enum v4l2_memory    memory; 
    union { 
            __u32   offset; 
            unsigned long   userptr; 
    } m; 
    __u32    length; 
    __u32    input; 
    __u32    reserved; 
}; 

  index 為緩存編號

  type 為視頻捕獲模式

  bytesused 為緩存已使用空間大小

  flags 為緩存當前狀態(常見值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分別代表當前緩存已經映射、緩存可以采集數據、緩存可以提取數據)

  timestamp 為時間戳

  sequence為緩存序號

  memory 為緩存使用方式

  offset 為當前緩存與內存區起始地址的偏移

  length 為緩存大小

  reserved 一般用於傳遞物理地址值。

  另外 VIDIOC_QBUF 和 VIDIOC_DQBUF 命令都采用結構 v4l2_buffer 與驅動通信:VIDIOC_QBUF 命令向驅動傳遞應用程序已經處理完的緩存,即將緩存加入空閑可捕獲視頻的隊列,傳遞的主要參數為 index;VIDIOC_DQBUF 命令向驅動獲取已經存放有視頻數據的緩存,v4l2_buffer 的各個域幾乎都會被更新,但主要的參數也是 index,應用程序會根據 index 確定可用數據的起始地址和范圍。

 

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。

三、調用v4l2的工作流程

  打開設備-> 檢查和設置設備屬性-> 設置幀格式-> 設置一種輸入輸出方法(緩沖 區管理)-> 循環獲取數據-> 關閉設備。

 

(1)打開設備文件

  打開視頻設備非常簡單,在V4L2中,視頻設備被看做一個文件。使用open函數打開這個設備:

  1. 用非阻塞模式打開攝像頭設備

  int cameraFd;

  cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);

  2. 如果用阻塞模式打開攝像頭設備,上述代碼變為:

  cameraFd = open("/dev/video0", O_RDWR);

  關於阻塞模式和非阻塞模式

  應用程序能夠使用阻塞模式或非阻塞模式打開視頻設備,如果使用非阻塞模式調用視頻設備,即使尚未捕獲到信息,驅動依舊會把緩存(DQBUFF)里的東西返回給應用程序。

(2)取得設備的capability

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

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

    struct v4l2_capability cap;
   
    memset(&cap, 0, sizeof(cap));
    /* 獲取設備支持的操作 */
    if(ioctl(dev->fd, VIDIOC_QUERYCAP, &cap) < 0){
        if(EINVAL == errno){   /*EINVAL為返回的錯誤值*/
            printf(stderr,"%s is no V4L2 device\n", dev->dev);
            return TFAIL;
        }
        else
        {
            printf(stderr,"%s is not V4L2 device,unknow error\n", dev->dev);
            return TFAIL;
        }
    }
    //獲取成功,檢查是否有視頻捕獲功能
    if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
        printf(stderr, "%s is no video capture device\n",dev->dev);
        return TFAIL;
    }
    /* streaming I/O ioctls */
    if(!(cap.capabilities & V4L2_CAP_STREAMING)){
        printf(stderr, "%s does not support streaming i/o\n",dev->dev);
        return TFAIL;
    }

(3)選擇視頻輸入

          struct v4l2_input input;
                  ……初始化input
                  int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);

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

  VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 用來查詢和選則當前的 input,一個 video 設備節點可能對應多個視頻源,比如 saf7113 可以最多支持四路 cvbs 輸入,如果上層想在四個cvbs視頻輸入間切換,那么就要調用 ioctl(fd, VIDIOC_S_INPUT, &input) 來切換。VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回當前的 video input和output的index.

struct v4l2_input {
__u32 index; /* Which input *
/__u8 name[32]; /* Label */
__u32 type; /* Type of input */
__u32 audioset; /* Associated audios (bitfield) */
__u32 tuner; /* Associated tuner */
v4l2_std_id std;
__u32 status;
__u32 reserved[4];
};

(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)設置視頻捕獲格式

  v4l2_format 結構體用來設置攝像頭的視頻制式、幀格式等,在設置這個參數時應先填 好 v4l2_format 的各個域,如 type(傳輸流類型),fmt.pix.width(寬),fmt.pix.heigth(高),fmt.pix.field(采樣區域,如隔行采樣),fmt.pix.pixelformat(采樣類型,如 YUV4:2:2),然后通過 VIDIO_S_FMT 操作命令設置視頻捕捉格式。

    struct v4l2_format fmt; 
    memset(&fmt, 0, sizeof(fmt));
    fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width		= g_display_width;
    fmt.fmt.pix.height		= g_display_height;
    fmt.fmt.pix.pixelformat = g_fmt;
    fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
    /* 設置設備捕獲視頻的格式 */
    if(ioctl(dev->fd, VIDIOC_S_FMT, &fmt) < 0)
    {
        printf(stderr, "%s iformat not supported \n",dev->dev);
        close(dev->fd);
        return TFAIL;
    }

  注意:如果該視頻設備驅動不支持你所設定的圖像格式,視頻驅動會重新修改struct v4l2_format結構體變量的值為該視頻設備所支持的圖像格式,所以在程序設計中,設定完所有的視頻格式后,要獲取實際的視頻格式,要重新讀取struct v4l2_format結構體變量。 

(6)向驅動申請幀緩存

  一般不超過5個,CAP_BUF_NUM = 4

    struct v4l2_requestbuffers req;
/* 申請設備的緩存區 */
    memset(&req, 0, sizeof(req));
    req.count = CAP_BUF_NUM;  //申請一個擁有四個緩沖幀的緩沖區
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (ioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0)
    {
        if (EINVAL == errno)
        {
            printf(stderr, "%s does not support "
                     "memory mapping\n", dev->dev);
            return TFAIL;
        }
        else
        {
            printf(stderr, "%s does not support "
                     "memory mapping, unknow error\n", dev->dev);
            return TFAIL;
        }
    }
    if (req.count < 2)
    {
        printf(stderr, "Insufficient buffer memory on %s\n",
                 dev->dev);
        return TFAIL;
    }

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

功能: 請求V4L2驅動分配視頻緩沖區(申請V4L2視頻驅動分配內存),V4L2是視頻設備的驅動層,位於內核空間,所以通過VIDIOC_REQBUFS控制命令字申請的內存位於內核空間,應用程序不能直接訪問,需要通過調用mmap內存映射函數把內核空間內存映射到用戶空間后,應用程序通過訪問用戶空間地址來訪問內核空間。

參數說明:參數類型為V4L2的申請緩沖區數據結構體類型struct v4l2_requestbuffers  ;

返回值說明: 執行成功時,函數返回值為 0;V4L2驅動層分配好了視頻緩沖區;

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

應用程序和設備有三種交換數據的方法,直接 read/write、內存映射(memory mapping)和用戶指針。這里只討論內存映射(memory mapping)。

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;
                          }
//addr 映射起始地址,一般為NULL ,讓內核自動選擇
//length 被映射內存塊的長度
//prot 標志映射后能否被讀寫,其值為PROT_EXEC,PROT_READ,PROT_WRITE, PROT_NONE
//flags 確定此內存映射能否被其他進程共享,MAP_SHARED,MAP_PRIVATE
//fd,offset, 確定被映射的內存地址 返回成功映射后的地址,不成功返回MAP_FAILED ((void*)-1)
int munmap(void *addr, size_t length);// 斷開映射
//addr 為映射后的地址,length 為映射后的內存長度

(8)開始采集視頻 (在緩沖區處理好之后就可以獲得視頻了 )

在開始之前,還應當把緩沖幀放入緩沖隊列,應用程序和設備有三種交換數據的方法,直接 read/write內存映射(memory mapping)和用戶指針。這里只討論內存映射(memory mapping)。

 //把四個緩沖幀放入隊列
    for (i = 0; i < CAP_BUF_NUM; i++)
    {
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        buf.m.offset = dev->buffer[i].offset;
        /* 將空閑的內存加入可捕獲視頻的隊列 */
        if(ioctl(dev->fd, VIDIOC_QBUF, &buf) < 0)
        {
            printf("ERROR: VIDIOC_QBUF[%s], FUNC[%s], LINE[%d]\n", dev->dev, __FUNCTION__, __LINE__);
            return TFAIL;
        }
    }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    /* 打開設備視頻流 */
    if(ioctl(dev->fd, VIDIOC_STREAMON, &type) < 0)
    {
        printf("ERROR: VIDIOC_STREAMON[%s], FUNC[%s], LINE[%d]\n", dev->dev, __FUNCTION__, __LINE__);
        return TFAIL;
    }

  前期初始化完成后,只是解決了一幀視頻數據的格式和大小問題,而連續視頻幀數據的采集需要用幀緩沖區隊列的方式來解決,即要通過驅動程序在內存中申請幾個幀緩沖區來存放視頻數據。

  應用程序通過API接口提供的方法(VIDIOC_REQBUFS)申請若干個視頻數據的幀緩沖區,申請幀緩沖區數量一般不低於3個,每個幀緩沖區存放一幀視頻數據,這些幀緩沖區在內核空間。

  應用程序通過API接口提供的查詢方法(VIDIOC_QUERYBUF)查詢到幀緩沖區在內核空間的長度和偏移量地址。

  應用程序再通過內存映射方法(mmap),將申請到的內核空間幀緩沖區的地址映射到用戶空間地址,這樣就可以直接處理幀緩沖區的數據。

  (1)將幀緩沖區在視頻輸入隊列排隊,並啟動視頻采集

  在驅動程序處理視頻的過程中,定義了兩個隊列:視頻采集輸入隊列(incoming queues)和視頻采集輸出隊列(outgoing queues),前者是等待驅動存放視頻數據的隊列,后者是驅動程序已經放入了視頻數據的隊列。如圖2所示。

  應用程序需要將上述幀緩沖區在視頻采集輸入隊列排隊(VIDIOC_QBUF),然后可啟動視頻采集。

  (2)循環往復,采集連續的視頻數據

  啟動視頻采集后,驅動程序開始采集一幀數據,把采集的數據放入視頻采集輸入隊列的第一個幀緩沖區,一幀數據采集完成,也就是第一個幀緩沖區存滿一幀數據后,驅動程序將該幀緩沖區移至視頻采集輸出隊列,等待應用程序從輸出隊列取出。驅動程序接下來采集下一幀數據,放入第二個幀緩沖區,同樣幀緩沖區存滿下一幀數據后,被放入視頻采集輸出隊列。

  應用程序從視頻采集輸出隊列中取出含有視頻數據的幀緩沖區,處理幀緩沖區中的視頻數據,如存儲或壓縮。

  最后,應用程序將處理完數據的幀緩沖區重新放入視頻采集輸入隊列,這樣可以循環采集,如圖1所示。

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

struct v4l2_buffer capture_buf;
memset(&capture_buf, 0, sizeof(capture_buf));
capture_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
capture_buf.memory = V4L2_MEMORY_MMAP;
/* 將已經捕獲好視頻的內存拉出已捕獲視頻的隊列 */
if (ioctl(dev.fd, VIDIOC_DQBUF, &capture_buf) < 0)
 {
       printf("ERROR: VIDIOC_DQBUF[%s], FUNC[%s], LINE[%d]\n", dev, __FUNCTION__, __LINE__);
       return TFAIL;
       }
}

image_data_handle(buffer[capture_buf.index].start, capture_buf.bytesused);

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

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

(11)停止視頻的采集,解除映射

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

  munmap(buffer[j].start, buffer[j].length);

(12)關閉視頻設備

          close(fd);

 

  最后這個是一般mmap形式的使用流程,還有使用read/write方式的內存讀寫流程,具體的可以參考官方的capture.c這個文檔,程序的流程很清楚,也有相關的博文有寫到。

文字描述版流程:

    (1)打開視頻設備文件。int fd=open("/dev/video0",O_RDWR);

  (2)查詢視頻設備的能力,比如是否具有視頻輸入,或者音頻輸入輸出等。ioctl(fd_v4l, VIDIOC_QUERYCAP, &cap)

  (3)設置視頻采集的參數

  設置視頻的制式,制式包括PAL/NTSC,使用ioctl(fd_v4l, VIDIOC_S_STD, &std_id)

  設置視頻圖像的采集窗口的大小,使用ioctl(fd_v4l, VIDIOC_S_CROP, &crop)

  設置視頻幀格式,包括幀的點陣格式,寬度和高度等,使用ioctl(fd_v4l, VIDIOC_S_FMT, &fmt)

  設置視頻的幀率,使用ioctl(fd_v4l, VIDIOC_S_PARM, &parm)

  設置視頻的旋轉方式,使用ioctl(fd_v4l, VIDIOC_S_CTRL, &ctrl)

  (4)向驅動申請視頻流數據的幀緩沖區

  請求/申請若干個幀緩沖區,一般為不少於3個,使用ioctl(fd_v4l, VIDIOC_REQBUFS, &req)

  查詢幀緩沖區在內核空間中的長度和偏移量 ioctl(fd_v4l, VIDIOC_QUERYBUF, &buf)

  (5)應用程序通過內存映射,將幀緩沖區的地址映射到用戶空間,這樣就可以直接操作采集到的幀了,而不必去復制。

  buffers[i].start = mmap (NULL, buffers[i].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd_v4l, buffers[i].offset);

  (6)將申請到的幀緩沖全部放入視頻采集輸出隊列,以便存放采集的數據。ioctl (fd_v4l, VIDIOC_QBUF, &buf)

  (7)開始視頻流數據的采集。 ioctl (fd_v4l, VIDIOC_STREAMON, &type)

  (8) 驅動將采集到的一幀視頻數據存入輸入隊列第一個幀緩沖區,存完后將該幀緩沖區移至視頻采集輸出隊列。

  (9)應用程序從視頻采集輸出隊列中取出已含有采集數據的幀緩沖區。ioctl (fd_v4l, VIDIOC_DQBUF, &buf) ,應用程序處理該幀緩沖區的原始視頻數據。

  (10)處理完后,應用程序的將該幀緩沖區重新排入輸入隊列,這樣便可以循環采集數據。ioctl (fd_v4l, VIDIOC_QBUF, &buf)

  重復上述步驟8到10,直到停止采集數據。

  (11)停止視頻的采集。ioctl (fd_v4l, VIDIOC_STREAMOFF, &type)

  (12)釋放申請的視頻幀緩沖區unmap,關閉視頻設備文件close(fd_v4l)。

  以上的程序流程,包含了視頻設備采集連續的視頻數據的邏輯關系。而在實際運用中,往往還要加入對視頻數據進行處理(如壓縮編碼)的工作,否則,視頻流數據量相當大,需要很大的存儲空間和傳輸帶寬。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM