最近研究mjpg-streamer時,發現這篇文章,感覺不錯,就拿過來了。
轉自:http://www.linuxidc.com/Linux/2011-03/33022.htm
其實,學習就是一個過程,剛開始最難接受,過後就容易了。。。。
首先在學習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.bytesex.org/spec/book1.htm
希望本文對大家理解linux下的視頻驅動編程有所協助。
這些其實沒什麼好講的,就是這些,你繞不過去的,這些都是精華,我當時每樣都看了起碼十五遍以上吧,並不是說你拿著它上來就讀個十五遍,而是在看中學,在學中用,在用中又學,好東西就得慢慢品,這味道就出來了,這過程就是成長的過程。當你寫的程式能夠真正採集到一張映像的時候,那麼你已經完成了萬裡長征的一小半了