Linux系統中,視頻設備被看成一個設備文件來看待,設備文件存放在 /dev目錄下,完整路徑的設備文件名爲: /dev/video0 .html
視頻採集基本步驟流程以下: 打開視頻設備,設置視頻設備屬性及採集方式、視頻數據處理,關閉視頻設備,以下圖所示:linux
1、打開視頻設備ios
打開視頻設備很是簡單,在V4L2中,視頻設備被看作一個文件。使用open函數打開這個設備:編程
1. 用非阻塞模式打開攝像頭設備
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);緩存
2. 若是用阻塞模式打開攝像頭設備,上述代碼變爲:
cameraFd = open("/dev/video0", O_RDWR);數據結構
關於阻塞模式和非阻塞模式app
應用程序可以使用阻塞模式或非阻塞模式打開視頻設備,若是使用非阻塞模式調用視頻設備,即便還沒有捕獲到信息,驅動依舊會把緩存(DQBUFF)裏的東西返回給應用程序。less
2、Linux視頻設備驅動經常使用控制命令使用說明ide
設置視頻設備屬性經過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
V4L2(Video For Linux Two) 是內核提供給應用程序訪問音、視頻驅動的統一接口。
打開設備-> 檢查和設置設備屬性-> 設置幀格式-> 設置一種輸入輸出方法(緩衝 區管理)-> 循環獲取數據-> 關閉設備。
#include <fcntl.h> int open(const char *device_name, int flags); #include <unistd.h> int clo se(int fd);
例:
int fd=open(「/dev/video0」,O_RDWR); // 打開設備 close(fd); // 關閉設備
注意:V4L2 的相關定義包含在頭文件<linux/videodev2.h> 中.
相關函數:
int ioctl(int fd, int request, struct v4l2_capability *argp);
相關結構體:
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 // 是否支持圖像獲取
例:顯示設備信息
struct v4l2_capability cap; ioctl(fd,VIDIOC_QUERYCAP,&cap); printf(「Driver Name:%s\nCard Name:%s\nBus info:%s\nDriver Version:%u.%u.%u\n」,cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF);
制式包括PAL,NTSC,幀的格式個包括寬度和高度等。
相關函數:
int ioctl(int fd, int request, struct v4l2_fmtdesc *argp); int ioctl(int fd, int request, struct v4l2_format *argp);
相關結構體:
v4l2_cropcap 結構體用來設置攝像頭的捕捉能力,在捕捉上視頻時應先先設置
v4l2_cropcap 的 type 域,再經過 VIDIO_CROPCAP 操做命令獲取設備捕捉能力的參數,保存於 v4l2_cropcap 結構體中,包括 bounds(最大捕捉方框的左上角座標和寬高),defrect
(默認捕捉方框的左上角座標和寬高)等。
v4l2_format 結構體用來設置攝像頭的視頻制式、幀格式等,在設置這個參數時應先填 好 v4l2_format 的各個域,如 type(傳輸流類型),fmt.pix.width(寬),
fmt.pix.heigth(高),fmt.pix.field(採樣區域,如隔行採樣),fmt.pix.pixelformat(採
樣類型,如 YUV4:2:2),而後經過 VIDIO_S_FMT 操做命令設置視頻捕捉格式。以下圖所示:
相關函數:
int ioctl(int fd, int request, struct v4l2_fmtdesc *argp);
相關結構體:
1 struct v4l2_fmtdesc 2 3 { 4 5 u32 index; // 要查詢的格式序號,應用程序設置 6 7 enum v4l2_buf_type type; // 幀類型,應用程序設置 8 9 u32 flags; // 是否爲壓縮格式 10 11 u8 description[32]; // 格式名稱 12 13 u32 pixelformat; // 格式 14 15 u32 reserved[4]; // 保留 16 17 };
例:顯示全部支持的格式
1 struct v4l2_fmtdesc fmtdesc; fmtdesc.index=0; fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("Support format:\n"); 2 3 while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) 4 5 { 6 7 printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description); 8 9 fmtdesc.index++; 10 11 }
檢查是否支持某種格式:VIDIOC_TRY_FMT
相關函數:
int ioctl(int fd, int request, struct v4l2_format *argp);
相關結構體:
1 struct v4l2_format 2 3 { 4 5 enum v4l2_buf_type type; // 幀類型,應用程序設置 6 7 union fmt 8 9 { 10 11 struct v4l2_pix_format pix; // 視頻設備使用 12 13 struct v4l2_window win; 14 15 struct v4l2_vbi_format vbi; 16 17 struct v4l2_sliced_vbi_format sliced; 18 19 u8 raw_data[200]; 20 21 }; 22 23 }; 24 25 struct v4l2_pix_format 26 27 { 28 29 u32 width; // 幀寬,單位像素 30 31 u32 height; // 幀高,單位像素 32 33 u32 pixelformat; // 幀格式 34 35 enum v4l2_field field; 36 37 u32 bytesperline; 38 39 u32 sizeimage; 40 41 enum v4l2_colorspace colorspace; 42 43 u32 priv; 44 45 };
例:顯示當前幀的相關信息
1 struct v4l2_format fmt; fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl(fd, VIDIOC_G_FMT, &fmt); 2 3 printf(「Current data format information:\n\twidth:%d\n\theight:%d\n」, 4 5 fmt.fmt.pix.width,fmt.fmt.pix.height); 6 7 struct v4l2_fmtdesc fmtdesc; fmtdesc.index=0; fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1) 8 9 { 10 11 if(fmtdesc.pixelformat & fmt.fmt.pix.pixelformat) 12 13 { 14 15 printf(「\tformat:%s\n」,fmtdesc.description); 16 17 break; 18 19 } 20 21 fmtdesc.index++; 22 23 }
例:檢查是否支持某種幀格式
struct v4l2_format fmt; fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32; if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1) if(errno==EINVAL) printf(「not support format RGB32!\n」);
相關函數:
int ioctl(int fd, int request, struct v4l2_cropcap *argp); int ioctl(int fd, int request, struct v4l2_crop *argp); int ioctl(int fd, int request, const struct v4l2_crop *argp);
相關結構體:
Cropping 和 scaling 主要指的是圖像的取景範圍及圖片的比例縮放的支持。Crop 就 是把獲得的數據做必定的裁剪和伸縮,裁剪能夠只取樣咱們能夠獲得的圖像大小的一部分, 剪裁的主要參數是位置、長度、寬度。而 scale 的設置是經過 VIDIOC_G_FMT 和 VIDIOC_S_FMT 來得到和設置當前的 image 的長度,寬度來實現的。看下圖
咱們能夠假設 bounds 是 sensor 最大能捕捉到的圖像範圍,而 defrect 是設備默認 的最大取樣範圍,這個能夠經過 VIDIOC_CROPCAP 的 ioctl 來得到設備的 crap 相關的屬 性 v4l2_cropcap,其中的 bounds 就是這個 bounds,其實就是上限。每一個設備都有個默 認的取樣範圍,就是 defrect,就是 default rect 的意思,它比 bounds 要小一些。這 個範圍也是經過 VIDIOC_CROPCAP 的 ioctl 來得到的 v4l2_cropcap 結構中的 defrect 來表示的,咱們能夠經過 VIDIOC_G_CROP 和 VIDIOC_S_CROP 來獲取和設置設備當前的 crop 設置。
相關函數:
int ioctl(int fd, int request, struct v4l2_cropcap *argp);
相關結構體:
1 struct v4l2_cropcap 2 3 { 4 5 enum v4l2_buf_type type; // 數據流的類型,應用程序設置 6 7 struct v4l2_rect bounds; // 這是 camera 的鏡頭能捕捉到的窗口大小的侷限 8 9 struct v4l2_rect defrect; // 定義默認窗口大小,包括起點位置及長,寬的大小,大小以像素爲單位 10 11 struct v4l2_fract pixelaspect; // 定義了圖片的寬高比 12 13 };
相關函數:
int ioctl(int fd, int request, struct v4l2_crop *argp); int ioctl(int fd, int request, const struct v4l2_crop *argp);
相關結構體:
1 struct v4l2_crop 2 3 { 4 5 enum v4l2_buf_type type;// 應用程序設置 6 7 struct v4l2_rect c; 8 9 }
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.
相關函數:
int ioctl(int fd, int request, struct v4l2_input *argp);
相關結構體:
1 struct v4l2_input { 2 __u32 index; /* Which input */ 3 __u8 name[32]; /* Label */ 4 __u32 type; /* Type of input */ 5 __u32 audioset; /* Associated audios (bitfield) */ 6 __u32 tuner; /* Associated tuner */ 7 v4l2_std_id std; 8 __u32 status; 9 __u32 reserved[4]; 10 };
咱們能夠經過VIDIOC_ENUMINPUT and VIDIOC_ENUMOUTPUT 分別列舉一個input或者 output的信息,咱們使用一個v4l2_input結構體來存放查詢結果,這個結構體中有一個 index域用來指定你索要查詢的是第幾個input/ouput,若是你所查詢的這個input是當前正 在使用的,那麼在v4l2_input還會包含一些當前的狀態信息,若是所 查詢的input/output 不存在,那麼回返回EINVAL錯誤,因此,咱們經過循環查找,直到返回錯誤來遍歷全部的 input/output. VIDIOC_G_INPUT and VIDIOC_G_OUTPUT 返回當前的video input和output 的index.
例: 列舉當前輸入視頻所支持的視頻格式
1 struct v4l2_input input; 2 3 struct v4l2_standard standard; 4 5 memset (&input, 0, sizeof (input)); 6 7 //首先得到當前輸入的 index,注意只是 index,要得到具體的信息,就的調用列舉操做 8 9 if (-1 == ioctl (fd, VIDIOC_G_INPUT, &input.index)) { 10 11 perror (」VIDIOC_G_INPUT」); 12 13 exit (EXIT_FAILURE); 14 15 } 16 17 //調用列舉操做,得到 input.index 對應的輸入的具體信息 18 19 if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) { 20 21 perror (」VIDIOC_ENUM_INPUT」); 22 23 exit (EXIT_FAILURE); 24 25 } 26 27 printf (」Current input %s supports:\n」, input.name); memset (&standard, 0, sizeof (standard)); standard.index = 0; 28 29 //列舉全部的所支持的 standard,若是 standard.id 與當前 input 的 input.std 有共同的 30 31 bit flag,意味着當前的輸入支持這個 standard,這樣將全部驅動所支持的 standard 列舉一個 32 33 遍,就能夠找到該輸入所支持的全部 standard 了。 34 35 while (0 == ioctl (fd, VIDIOC_ENUMSTD, &standard)) { 36 37 if (standard.id & input.std) 38 39 printf (」%s\n」, standard.name); 40 41 standard.index++; 42 43 } 44 45 /* EINVAL indicates the end of the enumeration, which cannot be empty unless this device falls under the USB exception. */ 46 47 if (errno != EINVAL || standard.index == 0) { 48 49 perror (」VIDIOC_ENUMSTD」); 50 51 exit (EXIT_FAILURE); 52 53 }
相關函數:
v4l2_std_id std_id; //這個就是個64bit得數 int ioctl(int fd, int request, struct v4l2_standard *argp);
相關結構體:
1 typedef u64 v4l2_std_id; 2 3 struct v4l2_standard { 4 5 u32 index; 6 7 v4l2_std_id id; 8 9 u8 name[24]; 10 11 struct v4l2_fract frameperiod; /* Frames, not fields */ 12 13 u32 framelines; 14 15 u32 reserved[4]; 16 17 };
固然世界上如今有多個視頻標準,如NTSC和PAL,他們又細分爲好多種,那麼咱們的設 備輸入/輸出究竟支持什麼樣的標準呢?咱們的當前在使用的輸入和輸出正在使用的是哪 個標準呢?咱們怎麼設置咱們的某個輸入輸出使用的標準呢?這都是有方法的。
查詢咱們的輸入支持什麼標準,首先就得找到當前的這個輸入的index,而後查出它的 屬性,在其屬性裏面能夠獲得該輸入所支持的標準,將它所支持的各個標準與全部的標準 的信息進行比較,就能夠獲知所支持的各個標準的屬性。一個輸入所支持的標準應該是一 個集合,而這個集合是用bit與的方式用一個64位數字表示。所以咱們所查到的是一個數字。
Example: Information about the current video standard v4l2_std_id std_id; //這個就是個64bit得數
1 struct v4l2_standard standard; 2 3 // VIDIOC_G_STD就是得到當前輸入使用的standard,不過這裏只是獲得了該標準的id 4 5 // 即flag,尚未獲得其具體的屬性信息,具體的屬性信息要經過列舉操做來獲得。 6 7 if (-1 == ioctl (fd, VIDIOC_G_STD, &std_id)) { //得到了當前輸入使用的standard 8 9 // Note when VIDIOC_ENUMSTD always returns EINVAL this is no video device 10 11 // or it falls under the USB exception, and VIDIOC_G_STD returning EINVAL 12 13 // is no error. 14 15 perror (」VIDIOC_G_STD」); 16 17 exit (EXIT_FAILURE); 18 19 } 20 21 memset (&standard, 0, sizeof (standard)); 22 23 standard.index = 0; //從第一個開始列舉 24 25 // VIDIOC_ENUMSTD用來列舉所支持的全部的video標準的信息,不過要先給standard 26 27 // 結構的index域制定一個數值,所列舉的標 準的信息屬性包含在standard裏面, 28 29 // 若是咱們所列舉的標準和std_id有共同的bit,那麼就意味着這個標準就是當前輸 30 31 // 入所使用的標準,這樣咱們就獲得了當前輸入使用的標準的屬性信息 32 33 while (0 == ioctl (fd, VIDIOC_ENUMSTD, &standard)) { 34 35 if (standard.id & std_id) { 36 37 printf (」Current video standard: %s\n」, standard.name); 38 39 exit (EXIT_SUCCESS); 40 41 } 42 43 standard.index++; 44 45 } 46 47 /* EINVAL indicates the end of the enumeration, which cannot be empty unless this device falls under the USB exception. */ 48 49 if (errno == EINVAL || standard.index == 0) { 50 51 perror (」VIDIOC_ENUMSTD」); 52 53 exit (EXIT_FAILURE); 54 55 }
應用程序和設備有三種交換數據的方法,直接 read/write、內存映射(memory mapping)
和用戶指針。這裏只討論內存映射(memory mapping)。
相關函數:
int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);
相關結構體:
1 struct v4l2_requestbuffers 2 3 { 4 5 u32 count; // 緩衝區內緩衝幀的數目 6 7 enum v4l2_buf_type type; // 緩衝幀數據格式 8 9 enum v4l2_memory memory; // 區別是內存映射仍是用戶指針方式 10 11 u32 reserved[2]; 12 13 };
注:enum v4l2_memoy
{
V4L2_MEMORY_MMAP, V4L2_MEMORY_USERPTR
};
//count,type,memory 都要應用程序設置
例:申請一個擁有四個緩衝幀的緩衝區
1 struct v4l2_requestbuffers req; 2 3 req.count=4; req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; 4 5 req.memory=V4L2_MEMORY_MMAP; 6 7 ioctl(fd,VIDIOC_REQBUFS,&req);
9.2 獲取緩衝幀的地址,長度:VIDIOC_QUERYBUF
相關函數:
int ioctl(int fd, int request, struct v4l2_buffer *argp);
相關結構體:
1 struct v4l2_buffer 2 3 { 4 5 u32 index; //buffer 序號 6 7 enum v4l2_buf_type type; //buffer 類型 8 9 u32 byteused; //buffer 中已使用的字節數 10 11 u32 flags; // 區分是MMAP 仍是USERPTR 12 13 enum v4l2_field field; 14 15 struct timeval timestamp; // 獲取第一個字節時的系統時間 16 17 struct v4l2_timecode timecode; 18 19 u32 sequence; // 隊列中的序號 20 21 enum v4l2_memory memory; //IO 方式,被應用程序設置 22 23 union m 24 25 { 26 27 u32 offset; // 緩衝幀地址,只對MMAP 有效 28 29 unsigned long userptr; 30 31 }; 32 33 u32 length; // 緩衝幀長度 34 35 u32 input; 36 37 u32 reserved; 38 39 };
9.3 內存映射MMAP 及定義一個結構體來映射每一個緩衝幀。 相關結構體:
1 struct buffer 2 3 { 4 5 void* start; 6 7 unsigned int length; 8 9 }*buffers;
相關函數:
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
//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 爲映射後的內存長度
例:將四個已申請到的緩衝幀映射到應用程序,用buffers 指針記錄。
1 buffers = (buffer*)calloc (req.count, sizeof (*buffers)); 2 3 if (!buffers) { 4 5 // 映射 6 7 fprintf (stderr, "Out of memory/n"); 8 9 exit (EXIT_FAILURE); 10 11 } 12 13 for (unsigned int n_buffers = 0; n_buffers < req.count; ++n_buffers) 14 15 { 16 17 struct v4l2_buffer buf; 18 19 memset(&buf,0,sizeof(buf)); 20 21 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 22 23 buf.memory = V4L2_MEMORY_MMAP; 24 25 buf.index = n_buffers; 26 27 // 查詢序號爲n_buffers 的緩衝區,獲得其起始物理地址和大小 28 29 if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) 30 31 exit(-1); 32 33 buffers[n_buffers].length = buf.length; 34 35 // 映射內存 36 37 buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset); 38 39 if (MAP_FAILED == buffers[n_buffers].start) 40 41 exit(-1); 42 43 }
int ioctl(int fd, int request, const int *argp);
//argp 爲流類型指針,如V4L2_BUF_TYPE_VIDEO_CAPTURE.
10.2 在開始以前,還應當把緩衝幀放入緩衝隊列:
VIDIOC_QBUF// 把幀放入隊列
VIDIOC_DQBUF// 從隊列中取出幀
int ioctl(int fd, int request, struct v4l2_buffer *argp);
例:把四個緩衝幀放入隊列,並啓動數據流
1 unsigned int i; 2 3 enum v4l2_buf_type type; 4 5 for (i = 0; i < 4; ++i) // 將緩衝幀放入隊列 6 7 { 8 9 struct v4l2_buffer buf; 10 11 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 12 13 buf.memory = V4L2_MEMORY_MMAP; 14 15 buf.index = i; 16 17 ioctl (fd, VIDIOC_QBUF, &buf); 18 19 } 20 21 type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 22 23 ioctl (fd, VIDIOC_STREAMON, &type); 24 25 // 這有個問題,這些buf 看起來和前面申請的buf 沒什麼關係,爲何呢?
例:獲取一幀並處理
1 struct v4l2_buffer buf; CLEAR (buf); 2 3 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 4 5 buf.memory = V4L2_MEMORY_MMAP; 6 7 ioctl (fd, VIDIOC_DQBUF, &buf); // 從緩衝區取出一個緩衝幀 8 9 process_image (buffers[buf.index.]start); // 10 11 ioctl (fdVIDIOC_QBUF&buf); //