一篇不錯的v4l2


http://blog.chinaunix.net/uid-26851094-id-3356224.html


其實,我剛開始一直都不知道怎麼寫驅動,什麼都不懂的,只知道我需要在做項目的過程中學習,所以,我就自己找了一個關於編寫Linux下的視頻採集監控項目做,然後上學期剛開學的時候聽師兄說,跟院長做項目,沒做出來也沒關係,所以直接退出博士的團隊,投靠了院長的門下,呵呵,說到這裏其實並不是我太見風使駝了,而是····老是讓我做單片機的東東,我嫌沒意思,他也知道我一開始就要學嵌入式,所以,最後,我想了一下,既然不能幫他做項目,那留着也沒啥意思,自己滾蛋吧(呵呵,不過現在似乎馬上就快要繼續幫博士做項目了),所以找了院長做導師。。。進了他的實驗室。廢話完,上菜(唉,誰是我的菜啊)

其實,學習就是一個過程,剛開始最難接受,過後就容易了。。。。

首先在學習v4l2之前,你得起碼先有個攝像頭,不然沒得玩

另外,最好給自己指定一個計劃,有針對性的學習,定下時間,這樣學習纔有緊迫感

v4l2架構入門其實並不是很難,高級的我還沒資格說,想當初我剛開始看的時候,也是感覺超級難啊,因爲沒有抓住體系,對整體沒有認識,所以我就花了兩天時間天天研究那幾篇文章和程序

程序屬這兩篇文章最爲經典了:

http://www.linuxidc.com/Linux/2011-03/33020.htm

http://www.linuxidc.com/Linux/2011-03/33021.htm

基礎知識,我就直接貼出來算了  ,//後是我添加的

基於Linux視頻驅動接口V4L2視頻採集編程44253105764


Linux系統中,視頻設備被當作一個設備文件來看待,設備文件存放在 /dev目錄下,完整路徑的設備文件名爲: /dev/video0 .

視頻採集基本步驟流程如下: 打開視頻設備,設置視頻設備屬性及採集方式、視頻數據處理,關閉視頻設備,如下圖所示:

一、打開視頻設備

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

1.     用非阻塞模式打開攝像頭設備
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);

2.     如果用阻塞模式打開攝像頭設備,上述代碼變爲:
cameraFd = open("/dev/video0", O_RDWR);

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

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

 

二、Linux視頻設備驅動常用控制命令使用說明

 

設置視頻設備屬性通過ioctl來進行設置,ioctl有三個參數,分別是fd, cmd,和parameter,表示設備描述符,控制命令和控制命令參數。

 

Linux 視頻設備驅動接口V4L2支持的常用控制命令如下:

 

1. 控制命令 VIDIOC_ENUM_FMT      //ENUM什麼意思?自己查查去

功能: 獲取當前視頻設備支持的視頻格式 。

參數說明:參數類型爲V4L2的視頻格式描述符類型 struct v4l2_fmtdesc

返回值說明: 執行成功時,函數返回值爲 0;struct v4l2_fmtdesc 結構體中的 .pixelformat和 .description 成員返回當前視頻設備所支持的視頻格式;

使用舉例:

-------------------------------------------------------------------------------------------------

struct v4l2_fmtdesc fmt;

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

       fmt.index = 0;

       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

       while ((ret = ioctl(dev, VIDIOC_ENUM_FMT, &fmt)) == 0) {

              fmt.index++;

              printf("{ pixelformat = ''%c%c%c%c'', description = ''%s'' }\n",

                            fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF,

                            (fmt.pixelformat >> 16) & 0xFF, (fmt.pixelformat >> 24) & 0xFF,

                            fmt.description);

       }

-------------------------------------------------------------------------------------------------------

 

2. 控制命令VIDIOC_QUERYCAP        //query 和cap各代表什麼意思

功能: 查詢視頻設備的功能 ;

參數說明:參數類型爲V4L2的能力描述類型struct v4l2_capability ;

返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,struct v4l2_capability 結構體變量中的返回當前視頻設備所支持的功能;例如支持視頻捕獲功能V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING等。

使用舉例:

-------------------------------------------------------------------------------------------------------

struct v4l2_capability cap; 

iret = ioctl(fd_usbcam, VIDIOC_QUERYCAP, &cap);

if(iret < 0)

{

              printf("get vidieo capability error,error code: %d \n", errno);

              return ;

}

------------------------------------------------------------------------------------------------------

執行完VIDIOC_QUERYCAP命令後,cap變量中包含了該視頻設備的能力信息,程序中通過檢查cap中的設備能力信息來判斷設備是否支持某項功能。
3. 控制命令VIDIOC_S_FMT           //直接告訴你,s是set的意思

功能: 設置視頻設備的視頻數據格式,例如設置視頻圖像數據的長、寬,圖像格式(JPEG、YUYV格式);

參數說明:參數類型爲V4L2的視頻數據格式類型    struct v4l2_format  ;

返回值說明: 執行成功時,函數返回值爲 0;

使用舉例:

----------------------------------------------------------------------------------------------------------

       struct v4l2_format tv4l2_format; 

tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

   tv4l2_format.fmt.pix.width = img_width; 

   tv4l2_format.fmt.pix.height = img_height; 

   tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 

   tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED; 

 

       iret = ioctl(fd_usbcam, VIDIOC_S_FMT, &tv4l2_format);

-----------------------------------------------------------------------------------------------------------

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

 

4. 控制命令VIDIOC_REQBUFS            //我不問了,你自己問自己吧

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

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

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

使用舉例:

-----------------------------------------------------------------------------------------------------

struct v4l2_requestbuffers  tV4L2_reqbuf;

memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));

 

tV4L2_reqbuf.count = 1;    //申請緩衝區的個數

tV4L2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

tV4L2_reqbuf.memory = V4L2_MEMORY_MMAP;

 

iret = ioctl(fd_usbcam, VIDIOC_REQBUFS, &tV4L2_reqbuf);

-----------------------------------------------------------------------------------------------------

注意:VIDIOC_REQBUFS會修改tV4L2_reqbuf的count值,tV4L2_reqbuf的count值返回實際申請成功的視頻緩衝區數目;
5. 控制命令VIDIOC_QUERYBUF

功能: 查詢已經分配的V4L2的視頻緩衝區的相關信息,包括視頻緩衝區的使用狀態、在內核空間的偏移地址、緩衝區長度等。在應用程序設計中通過調VIDIOC_QUERYBUF來獲取內核空間的視頻緩衝區信息,然後調用函數mmap把內核空間地址映射到用戶空間,這樣應用程序才能夠訪問位於內核空間的視頻緩衝區。

參數說明:參數類型爲V4L2緩衝區數據結構類型    struct v4l2_buffer  ;

返回值說明: 執行成功時,函數返回值爲 0;struct v4l2_buffer結構體變量中保存了指令的緩衝區的相關信息;

一般情況下,應用程序中調用VIDIOC_QUERYBUF取得了內核緩衝區信息後,緊接着調用mmap函數把內核空間地址映射到用戶空間,方便用戶空間應用程序的訪問。

使用舉例:

-------------------------------------------------------------------------------------------------------

struct v4l2_buffer tV4L2buf; 

memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));

 

tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

tV4L2buf.memory = V4L2_MEMORY_MMAP; 

tV4L2buf.index = i;  // 要獲取內核視頻緩衝區的信息編號

iret = ioctl(fd_usbcam, VIDIOC_QUERYBUF, &tV4L2buf);

// 把內核空間緩衝區映射到用戶空間緩衝區

AppBufLength  = tV4L2buf.length;

AppBufStartAddr = mmap(NULL /* start anywhere */ , 

                       tV4L2buf.length, 

                       PROT_READ | PROT_WRITE /* access privilege */ , 

                       MAP_SHARED /* recommended */ , 

                       fd_usbcam, tV4L2buf.m.offset); 

-------------------------------------------------------------------------------------------------------

上述代碼在通過調用VIDIOC_QUERYBUF取得內核空間的緩衝區信息後,接着調用mmap函數把內核空間緩衝區映射到用戶空間;關於mmap函數的用法,請讀者查詢相關資料;

 

6. 控制命令VIDIOC_QBUF

功能: 投放一個空的視頻緩衝區到視頻緩衝區輸入隊列中 ;

參數說明:參數類型爲V4L2緩衝區數據結構類型    struct v4l2_buffer ;

返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,指令(指定)的視頻緩衝區進入視頻輸入隊列,在啓動視頻設備拍攝圖像時,相應的視頻數據被保存到視頻輸入隊列相應的視頻緩衝區中。

使用舉例:

-------------------------------------------------------------------------------------------------------

struct v4l2_buffer tV4L2buf; 

memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));

 

tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

tV4L2buf.memory = V4L2_MEMORY_MMAP; 

tV4L2buf.index = i; //指令(指定)要投放到視頻輸入隊列中的內核空間視頻緩衝區的編號;

 

iret = ioctl(fd_usbcam, VIDIOC_QBUF, &tV4L2buf);

----------------------------------------------------------------------------------------------------
7. 控制命令VIDIOC_STREAMON

功能: 啓動視頻採集命令,應用程序調用VIDIOC_STREAMON啓動視頻採集命令後,視頻設備驅動程序開始採集視頻數據,並把採集到的視頻數據保存到視頻驅動的視頻緩衝區中。

參數說明:參數類型爲V4L2的視頻緩衝區類型 enum v4l2_buf_type ;

返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,視頻設備驅動程序開始採集視頻數據,此時應用程序一般通過調用select函數來判斷一幀視頻數據是否採集完成,當視頻設備驅動完成一幀視頻數據採集並保存到視頻緩衝區中時,select函數返回,應用程序接着可以讀取視頻數據;否則select函數阻塞直到視頻數據採集完成。Select函數的使用請讀者參考相關資料。

使用舉例:

----------------------------------------------------------------------------------------------------------

enum v4l2_buf_type v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

fd_set    fds ; 

struct timeval   tv; 

iret = ioctl(fd_usbcam, VIDIOC_STREAMON, &v4l2type);

 

FD_ZERO(&fds); 

FD_SET(fd_usbcam,  &fds); 

tv.tv_sec = 2;       /* Timeout. */ 

tv.tv_usec = 0; 

 

iret = select(fd_usbcam+ 1, &fds, NULL, NULL, &tv); 

----------------------------------------------------------------------------------------------------------

 

8. 控制命令VIDIOC_DQBUF        //第二個D是刪除的意思

功能: 從視頻緩衝區的輸出隊列中取得一個已經保存有一幀視頻數據的視頻緩衝區;

參數說明:參數類型爲V4L2緩衝區數據結構類型    struct v4l2_buffer ;

返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,相應的內核視頻緩衝區中保存有當前拍攝到的視頻數據,應用程序可以通過訪問用戶空間來讀取該視頻數據。(前面已經通過調用函數mmap做了用戶空間和內核空間的內存映射).

使用舉例:

----------------------------------------------------------------------------------------------------------

struct v4l2_buffer tV4L2buf; 

memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));

 

tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

tV4L2buf.memory = V4L2_MEMORY_MMAP; 

 

iret = ioctl(fd_usbcam, VIDIOC_DQBUF, &tV4L2buf);

Sasoritattoo註釋:VIDIOC_DQBUF命令結果 使從隊列刪除的緩衝幀信息 傳給了此tVL2buf。V4L2_buffer結構體的作用就相當於申請的緩衝幀的代理,找緩衝幀的都要先問問它,通過它來聯繫緩衝幀,起了中間橋樑的作用。

----------------------------------------------------------------------------------------------------------

 

9. 控制命令VIDIOC_STREAMOFF

功能: 停止視頻採集命令,應用程序調用VIDIOC_ STREAMOFF停止視頻採集命令後,視頻設備驅動程序不在採集視頻數據。

參數說明:參數類型爲V4L2的視頻緩衝區類型 enum v4l2_buf_type ;

返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,視頻設備停止採集視頻數據。

使用舉例:

----------------------------------------------------------------------------------------------------------

enum v4l2_buf_type  v4l2type; 

v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

iret = ioctl(fd_usbcam, VIDIOC_STREAMOFF, &v4l2type);

-----------------------------------------------------------------------------------------------------------

以上就是Linux 視頻設備驅動V4L2最常用的控制命令使用說明,通過使用以上控制命令,可以完成一幅視頻數據的採集過程。V4L2更多的控制命令使用說明請參考:http://v4l2spec.byte***.org/spec/book1.htm

希望本文對大家理解linux下的視頻驅動編程有所幫助。

*******************************我是分割線,你看不見我********************************************

 

這些其實沒什麼好講的,就是這些,你繞不過去的,這些都是精華,我當時每樣都看了起碼十五遍以上吧,並不是說你拿着它上來就讀個十五遍,而是在看中學,在學中用,在用中又學,好東西就得慢慢品,這味道就出來了,這過程就是成長的過程。當你寫的程序能夠真正採集到一張圖像的時候,那麼你已經完成了萬里長征的一小半了


我前段時間上傳了一個也是基於v4l2視頻架構的採集程序,是別人寫的,我註釋了一下,上面搞定了可以去下一下看看,另外,在學習的過程中,我還從網上找了許多關於這方面的碩士論文,現在回頭一看,那也能叫碩士論文??沒啥含量,純粹就是瞎胡鬧,東西估計都沒做出來,就自己在那兒吹,有些抄別人的抄錯了都不知道,,還好我從沒想過要考研,現在上大學我都覺得沒意思還考研??可笑,可笑。。。



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