其實(shí),我剛開始一直都不知道怎么寫驅(qū)動,什么都不懂的,只知道我需要在做項目的過程中學(xué)習(xí),所以,我就自己找了一個關(guān)于編寫Linux下的視頻采集監(jiān)控項目做,然后上學(xué)期剛開學(xué)的時候聽師兄說,跟院長做項目,沒做出來也沒關(guān)系,所以直接退出博士的團(tuán)隊,投靠了院長的門下,呵呵,說到這里其實(shí)并不是我太見風(fēng)使駝了,而是····老是讓我做單片機(jī)的東東,我嫌沒意思,他也知道我一開始就要學(xué)嵌入式,所以,最后,我想了一下,既然不能幫他做項目,那留著也沒啥意思,自己滾蛋吧(呵呵,不過現(xiàn)在似乎馬上就快要繼續(xù)幫博士做項目了),所以找了院長做導(dǎo)師。。。進(jìn)了他的實(shí)驗室。廢話完,上菜(唉,誰是我的菜?。?br />其實(shí),學(xué)習(xí)就是一個過程,剛開始最難接受,過后就容易了。。。。
首先在學(xué)習(xí)v4l2之前,你得起碼先有個攝像頭,不然沒得玩
另外,最好給自己指定一個計劃,有針對性的學(xué)習(xí),定下時間,這樣學(xué)習(xí)才有緊迫感
v4l2架構(gòu)入門其實(shí)并不是很難,高級的我還沒資格說,想當(dāng)初我剛開始看的時候,也是感覺超級難啊,因為沒有抓住體系,對整體沒有認(rèn)識,所以我就花了兩天時間天天研究那幾篇文章和程序
程序?qū)龠@兩篇文章最為經(jīng)典了:
http://www.linuxidc.com/Linux/2011-03/33020.htm
http://www.linuxidc.com/Linux/2011-03/33021.htm
基礎(chǔ)知識,我就直接貼出來算了 ?,//后是我添加的
基于Linux視頻驅(qū)動接口V4L2視頻采集編程44253105764
Linux系統(tǒng)中,視頻設(shè)備被當(dāng)作一個設(shè)備文件來看待,設(shè)備文件存放在 /dev目錄下,完整路徑的設(shè)備文件名為: /dev/video0 .
視頻采集基本步驟流程如下: 打開視頻設(shè)備,設(shè)置視頻設(shè)備屬性及采集方式、視頻數(shù)據(jù)處理,關(guān)閉視頻設(shè)備,如下圖所示:
一、打開視頻設(shè)備
打開視頻設(shè)備非常簡單,在V4L2中,視頻設(shè)備被看做一個文件。使用open函數(shù)打開這個設(shè)備:
1. ? ? 用非阻塞模式打開攝像頭設(shè)備
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);
2. ? ? 如果用阻塞模式打開攝像頭設(shè)備,上述代碼變?yōu)椋?
cameraFd = open("/dev/video0", O_RDWR);
關(guān)于阻塞模式和非阻塞模式
應(yīng)用程序能夠使用阻塞模式或非阻塞模式打開視頻設(shè)備,如果使用非阻塞模式調(diào)用視頻設(shè)備,即使尚未捕獲到信息,驅(qū)動依舊會把緩存(DQBUFF)里的東西返回給應(yīng)用程序。
?
二、Linux視頻設(shè)備驅(qū)動常用控制命令使用說明
?
設(shè)置視頻設(shè)備屬性通過ioctl來進(jìn)行設(shè)置,ioctl有三個參數(shù),分別是fd, cmd,和parameter,表示設(shè)備描述符,控制命令和控制命令參數(shù)。
?
Linux 視頻設(shè)備驅(qū)動接口V4L2支持的常用控制命令如下:
?
1. 控制命令 VIDIOC_ENUM_FMT ? ? ?//ENUM什么意思?自己查查去
功能: 獲取當(dāng)前視頻設(shè)備支持的視頻格式 。
參數(shù)說明:參數(shù)類型為V4L2的視頻格式描述符類型 struct v4l2_fmtdesc
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;struct v4l2_fmtdesc 結(jié)構(gòu)體中的 .pixelformat和 .description 成員返回當(dāng)前視頻設(shè)備所支持的視頻格式;
使用舉例:
-------------------------------------------------------------------------------------------------
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各代表什么意思
功能: 查詢視頻設(shè)備的功能 ;
參數(shù)說明:參數(shù)類型為V4L2的能力描述類型struct v4l2_capability ;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;函數(shù)執(zhí)行成功后,struct v4l2_capability 結(jié)構(gòu)體變量中的返回當(dāng)前視頻設(shè)備所支持的功能;例如支持視頻捕獲功能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 ;
}
------------------------------------------------------------------------------------------------------
執(zhí)行完VIDIOC_QUERYCAP命令后,cap變量中包含了該視頻設(shè)備的能力信息,程序中通過檢查cap中的設(shè)備能力信息來判斷設(shè)備是否支持某項功能。
3. 控制命令VIDIOC_S_FMT ? ? ? ? ? //直接告訴你,s是set的意思
功能: 設(shè)置視頻設(shè)備的視頻數(shù)據(jù)格式,例如設(shè)置視頻圖像數(shù)據(jù)的長、寬,圖像格式(JPEG、YUYV格式);
參數(shù)說明:參數(shù)類型為V4L2的視頻數(shù)據(jù)格式類型 ? ?struct v4l2_format ?;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 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);
-----------------------------------------------------------------------------------------------------------
注意:如果該視頻設(shè)備驅(qū)動不支持你所設(shè)定的圖像格式,視頻驅(qū)動會重新修改struct v4l2_format結(jié)構(gòu)體變量的值為該視頻設(shè)備所支持的圖像格式,所以在程序設(shè)計中,設(shè)定完所有的視頻格式后,要獲取實(shí)際的視頻格式,要重新讀取struct v4l2_format結(jié)構(gòu)體變量。
?
4. 控制命令VIDIOC_REQBUFS ? ? ? ? ? ?//我不問了,你自己問自己吧
功能: 請求V4L2驅(qū)動分配視頻緩沖區(qū)(申請V4L2視頻驅(qū)動分配內(nèi)存),V4L2是視頻設(shè)備的驅(qū)動層,位于內(nèi)核空間,所以通過VIDIOC_REQBUFS控制命令字申請的內(nèi)存位于內(nèi)核空間,應(yīng)用程序不能直接訪問,需要通過調(diào)用mmap內(nèi)存映射函數(shù)把內(nèi)核空間內(nèi)存映射到用戶空間后,應(yīng)用程序通過訪問用戶空間地址來訪問內(nèi)核空間。
參數(shù)說明:參數(shù)類型為V4L2的申請緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)體類型struct v4l2_requestbuffers ?;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;V4L2驅(qū)動層分配好了視頻緩沖區(qū);
使用舉例:
-----------------------------------------------------------------------------------------------------
struct v4l2_requestbuffers ?tV4L2_reqbuf;
memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));
?
tV4L2_reqbuf.count = 1; ? ?//申請緩沖區(qū)的個數(shù)
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值返回實(shí)際申請成功的視頻緩沖區(qū)數(shù)目;
5. 控制命令VIDIOC_QUERYBUF
功能: 查詢已經(jīng)分配的V4L2的視頻緩沖區(qū)的相關(guān)信息,包括視頻緩沖區(qū)的使用狀態(tài)、在內(nèi)核空間的偏移地址、緩沖區(qū)長度等。在應(yīng)用程序設(shè)計中通過調(diào)VIDIOC_QUERYBUF來獲取內(nèi)核空間的視頻緩沖區(qū)信息,然后調(diào)用函數(shù)mmap把內(nèi)核空間地址映射到用戶空間,這樣應(yīng)用程序才能夠訪問位于內(nèi)核空間的視頻緩沖區(qū)。
參數(shù)說明:參數(shù)類型為V4L2緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)類型 ? ?struct v4l2_buffer ?;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;struct v4l2_buffer結(jié)構(gòu)體變量中保存了指令的緩沖區(qū)的相關(guān)信息;
一般情況下,應(yīng)用程序中調(diào)用VIDIOC_QUERYBUF取得了內(nèi)核緩沖區(qū)信息后,緊接著調(diào)用mmap函數(shù)把內(nèi)核空間地址映射到用戶空間,方便用戶空間應(yīng)用程序的訪問。
使用舉例:
-------------------------------------------------------------------------------------------------------
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; ?// 要獲取內(nèi)核視頻緩沖區(qū)的信息編號
iret = ioctl(fd_usbcam, VIDIOC_QUERYBUF, &tV4L2buf);
// 把內(nèi)核空間緩沖區(qū)映射到用戶空間緩沖區(qū)
AppBufLength ?= tV4L2buf.length;
AppBufStartAddr = mmap(NULL /* start anywhere */ ,?
? ? ? ? ? ? ? ? ? ? ? ?tV4L2buf.length,?
? ? ? ? ? ? ? ? ? ? ? ?PROT_READ | PROT_WRITE /* access privilege */ ,?
? ? ? ? ? ? ? ? ? ? ? ?MAP_SHARED /* recommended */ ,?
? ? ? ? ? ? ? ? ? ? ? ?fd_usbcam, tV4L2buf.m.offset);?
-------------------------------------------------------------------------------------------------------
上述代碼在通過調(diào)用VIDIOC_QUERYBUF取得內(nèi)核空間的緩沖區(qū)信息后,接著調(diào)用mmap函數(shù)把內(nèi)核空間緩沖區(qū)映射到用戶空間;關(guān)于mmap函數(shù)的用法,請讀者查詢相關(guān)資料;
?
6. 控制命令VIDIOC_QBUF
功能: 投放一個空的視頻緩沖區(qū)到視頻緩沖區(qū)輸入隊列中 ;
參數(shù)說明:參數(shù)類型為V4L2緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)類型 ? ?struct v4l2_buffer ;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;函數(shù)執(zhí)行成功后,指令(指定)的視頻緩沖區(qū)進(jìn)入視頻輸入隊列,在啟動視頻設(shè)備拍攝圖像時,相應(yīng)的視頻數(shù)據(jù)被保存到視頻輸入隊列相應(yīng)的視頻緩沖區(qū)中。
使用舉例:
-------------------------------------------------------------------------------------------------------
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; //指令(指定)要投放到視頻輸入隊列中的內(nèi)核空間視頻緩沖區(qū)的編號;
?
iret = ioctl(fd_usbcam, VIDIOC_QBUF, &tV4L2buf);
----------------------------------------------------------------------------------------------------
7. 控制命令VIDIOC_STREAMON
功能: 啟動視頻采集命令,應(yīng)用程序調(diào)用VIDIOC_STREAMON啟動視頻采集命令后,視頻設(shè)備驅(qū)動程序開始采集視頻數(shù)據(jù),并把采集到的視頻數(shù)據(jù)保存到視頻驅(qū)動的視頻緩沖區(qū)中。
參數(shù)說明:參數(shù)類型為V4L2的視頻緩沖區(qū)類型 enum v4l2_buf_type ;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;函數(shù)執(zhí)行成功后,視頻設(shè)備驅(qū)動程序開始采集視頻數(shù)據(jù),此時應(yīng)用程序一般通過調(diào)用select函數(shù)來判斷一幀視頻數(shù)據(jù)是否采集完成,當(dāng)視頻設(shè)備驅(qū)動完成一幀視頻數(shù)據(jù)采集并保存到視頻緩沖區(qū)中時,select函數(shù)返回,應(yīng)用程序接著可以讀取視頻數(shù)據(jù);否則select函數(shù)阻塞直到視頻數(shù)據(jù)采集完成。Select函數(shù)的使用請讀者參考相關(guān)資料。
使用舉例:
----------------------------------------------------------------------------------------------------------
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是刪除的意思
功能: 從視頻緩沖區(qū)的輸出隊列中取得一個已經(jīng)保存有一幀視頻數(shù)據(jù)的視頻緩沖區(qū);
參數(shù)說明:參數(shù)類型為V4L2緩沖區(qū)數(shù)據(jù)結(jié)構(gòu)類型 ? ?struct v4l2_buffer ;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;函數(shù)執(zhí)行成功后,相應(yīng)的內(nèi)核視頻緩沖區(qū)中保存有當(dāng)前拍攝到的視頻數(shù)據(jù),應(yīng)用程序可以通過訪問用戶空間來讀取該視頻數(shù)據(jù)。(前面已經(jīng)通過調(diào)用函數(shù)mmap做了用戶空間和內(nèi)核空間的內(nèi)存映射).
使用舉例:
----------------------------------------------------------------------------------------------------------
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命令結(jié)果 使從隊列刪除的緩沖幀信息 傳給了此tVL2buf。V4L2_buffer結(jié)構(gòu)體的作用就相當(dāng)于申請的緩沖幀的代理,找緩沖幀的都要先問問它,通過它來聯(lián)系緩沖幀,起了中間橋梁的作用。
----------------------------------------------------------------------------------------------------------
?
9. 控制命令VIDIOC_STREAMOFF
功能: 停止視頻采集命令,應(yīng)用程序調(diào)用VIDIOC_ STREAMOFF停止視頻采集命令后,視頻設(shè)備驅(qū)動程序不在采集視頻數(shù)據(jù)。
參數(shù)說明:參數(shù)類型為V4L2的視頻緩沖區(qū)類型 enum v4l2_buf_type ;
返回值說明: 執(zhí)行成功時,函數(shù)返回值為 0;函數(shù)執(zhí)行成功后,視頻設(shè)備停止采集視頻數(shù)據(jù)。
使用舉例:
----------------------------------------------------------------------------------------------------------
enum v4l2_buf_type ?v4l2type;?
v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ?
iret = ioctl(fd_usbcam, VIDIOC_STREAMOFF, &v4l2type);
-----------------------------------------------------------------------------------------------------------
以上就是Linux 視頻設(shè)備驅(qū)動V4L2最常用的控制命令使用說明,通過使用以上控制命令,可以完成一幅視頻數(shù)據(jù)的采集過程。V4L2更多的控制命令使用說明請參考:http://v4l2spec.byte***.org/spec/book1.htm
希望本文對大家理解linux下的視頻驅(qū)動編程有所幫助。
*******************************我是分割線,你看不見我********************************************
?
這些其實(shí)沒什么好講的,就是這些,你繞不過去的,這些都是精華,我當(dāng)時每樣都看了起碼十五遍以上吧,并不是說你拿著它上來就讀個十五遍,而是在看中學(xué),在學(xué)中用,在用中又學(xué),好東西就得慢慢品,這味道就出來了,這過程就是成長的過程。當(dāng)你寫的程序能夠真正采集到一張圖像的時候,那么你已經(jīng)完成了萬里長征的一小半了
我前段時間上傳了一個也是基于v4l2視頻架構(gòu)的采集程序,是別人寫的,我注釋了一下,上面搞定了可以去下一下看看,另外,在學(xué)習(xí)的過程中,我還從網(wǎng)上找了許多關(guān)于這方面的碩士論文,現(xiàn)在回頭一看,那也能叫碩士論文??沒啥含量,純粹就是瞎胡鬧,東西估計都沒做出來,就自己在那兒吹,有些抄別人的抄錯了都不知道,,還好我從沒想過要考研,現(xiàn)在上大學(xué)我都覺得沒意思還考研??可笑,可笑。。。