@[TOC]
7 攝像頭V4L2編程應(yīng)用開發(fā)
7.1 V4L2簡介
? Video for Linux two(Video4Linux2)簡稱V4L2,是V4L的改進(jìn)版。V4L2是linux操作系統(tǒng)下一套用于采集圖片、視頻和音頻數(shù)據(jù)的通用API接口,配合適當(dāng)?shù)囊曨l采集設(shè)備和相應(yīng)的驅(qū)動程序,可以實現(xiàn)圖片、視頻、音頻等的采集。V4L2像一個優(yōu)秀的快遞員,將視頻采集設(shè)備的圖像數(shù)據(jù)安全、高效的傳遞給不同需求的用戶。
? 在Linux中,一切皆文件,所有外設(shè)都被看成一種特殊的文件,稱為“設(shè)備文件”。視頻設(shè)備也不例外,也可以可以看成是設(shè)備文件,可以像訪問普通文件一樣對其進(jìn)行讀寫。V4L2驅(qū)動的攝像頭的設(shè)備文件一般是/dev/videoX(X為任意數(shù)字,要與自己的設(shè)備相對應(yīng))。
? V4L2支持三種方式來采集圖像:內(nèi)存映射方式(mmap)、直接讀取方式(read)和用戶指針。內(nèi)存映射的方式采集速度較快,一般用于連續(xù)視頻數(shù)據(jù)的采集,實際工作中的應(yīng)用概率更高;直接讀取的方式相對速度慢一些,所以常用于靜態(tài)圖片數(shù)據(jù)的采集;用戶指針使用較少,如有興趣可自行研究。由于內(nèi)存映射方式的應(yīng)用更廣泛,所以本文重點討論內(nèi)存映射方式的視頻采集。
7.2 V4L2視頻采集原理
? 在通過V4L2采集圖像之前,我們需要做的很多,但是很重要的一步是分配幀緩沖區(qū),并將分配的幀緩沖區(qū)從內(nèi)核空間映射到用戶空間,然后將申請到的幀緩沖區(qū)在視頻采集輸入隊列排隊,剩下的就是等待視頻數(shù)據(jù)的到來。但是,萬一視頻數(shù)據(jù)真的來了是怎么個流動過程呢?這個我們有必要了解一下。
? 當(dāng)啟動視頻采集后,驅(qū)動程序開始采集一幀圖像數(shù)據(jù),會把采集的圖像數(shù)據(jù)放入視頻采集輸入隊列的第一個幀緩沖區(qū),一陣圖像數(shù)據(jù)就算采集完成了。第一個幀緩沖區(qū)存滿一幀圖像數(shù)據(jù)后,驅(qū)動程序?qū)⒃搸彌_區(qū)移至視頻采集輸出隊列,等待應(yīng)用程序從輸出隊列取出,應(yīng)用程序取出圖像數(shù)據(jù)可以對圖像數(shù)據(jù)進(jìn)行處理或存儲操作,然后將幀該緩沖區(qū)放入視頻采集輸入隊列的尾部。驅(qū)動程序接下來采集下一幀數(shù)據(jù),放入第二個緩沖區(qū),同樣的幀緩沖區(qū)存滿一幀數(shù)據(jù)后,驅(qū)動程序?qū)⒃摼彌_區(qū)移至視頻采集輸出隊列,應(yīng)用程序?qū)⒃搸彌_區(qū)的圖像數(shù)據(jù)取出后又將該幀緩沖區(qū)放入視頻輸入隊列尾部,這樣循環(huán)往復(fù)就實現(xiàn)了循環(huán)采集。流程如下圖所示:
? 為了更好的理解這個過程,我們可以把“應(yīng)用程序處理數(shù)據(jù)”比喻成“西瓜加工商加工西瓜”,“V4L2驅(qū)動程序采集數(shù)據(jù)”比喻成“西瓜采集員采集西瓜”,事先“西瓜加工商”會給“西瓜采集員”準(zhǔn)備幾個空籃子,然后“西瓜采集員”守著幾個空籃子等待“瓜農(nóng)”(圖像采集設(shè)備,例如:攝像頭)將空籃子裝滿,當(dāng)“空籃子1”被“瓜農(nóng)”裝滿以后,“西瓜采集員”會將裝滿西瓜的籃子放到“西瓜加工隊列”等待“西瓜加工商”取走加工,當(dāng)“西瓜加工商”取走裝滿西瓜的籃子中的西瓜的時候,“西瓜加工商”會將空籃子放回到事先給“西瓜采集員”準(zhǔn)備好的西瓜采集隊列的尾部。當(dāng)“瓜農(nóng)”裝滿下一個空籃子的時候,“西瓜采集員”同樣的將裝滿西瓜的籃子放到“西瓜加工隊列”等待“西瓜加工商”取走加工。這樣,整個過程會持續(xù)不斷的繼續(xù)下去。
7.3 V4L2程序?qū)崿F(xiàn)流程
? 使用V4L2進(jìn)行視頻采集,一般分為5個步驟:
(1)打開設(shè)備,進(jìn)行初始化參數(shù)設(shè)置,通過V4L2接口設(shè)置視頻圖像的采集窗口、采集的點陣大小和格式;
(2)申請圖像幀緩沖,并進(jìn)行內(nèi)存映射,將這些幀緩沖區(qū)從內(nèi)核空間映射到用戶空間,便于應(yīng)用程序讀取、處理圖像數(shù)據(jù);
(3)將幀緩沖進(jìn)行入隊操作,啟動視頻采集;
(4)驅(qū)動開始視頻數(shù)據(jù)的采集,應(yīng)用程序從視頻采集輸出隊列取出幀緩沖區(qū),處理完后,將幀緩沖區(qū)重新放入視頻采集輸入隊列,循環(huán)往復(fù)采集連續(xù)的視頻數(shù)據(jù);
(5)釋放資源,停止采集工作。
? 在進(jìn)行V4L2開發(fā)中,常用的命令標(biāo)識符如下:
(1)VIDIOC_REQBUFS:分配內(nèi)存;
(2)VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數(shù)據(jù)緩存轉(zhuǎn)換成物理地址;
(3)VIDIOC_QUERYCAP:查詢驅(qū)動功能;
(4)VIDIOC_ENUM_FMT:獲取當(dāng)前驅(qū)動支持的視頻格式;
(5)VIDIOC_S_FMT:設(shè)置當(dāng)前驅(qū)動的視頻捕獲格式;
(6)VIDIOC_G_FMT:讀取當(dāng)前驅(qū)動的視頻捕獲格式;
(7)VIDIOC_TRY_FMT:驗證當(dāng)前驅(qū)動的顯示格式;
(8)VIDIOC_CROPCAP:查詢驅(qū)動的修剪功能;
(9)VIDIOC_S_CROP:設(shè)置視頻信號的邊框;
(10)VIDIOC_G_CROP:讀取視頻信號的邊框;
(11)VIDIOC_QBUF:把數(shù)據(jù)從緩存中讀取出來;
(12)VIDIOC_DQBUF:把數(shù)據(jù)放回緩存隊列;
(13)VIDIOC_STREAMOP:開始視頻顯示函數(shù);
(14)VIDIOC_STREAMOFF:結(jié)束視頻顯示函數(shù);
(15)VIDIOC_QUERYSTD:檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),例如PAL或NTSC;
這些IO調(diào)用,有些是必須的,有些是可選擇的。
具體流程如下圖所示:
7.4 V4L2程序?qū)嵗?/h2>
? V4L2的代碼主要位于video2lcd/video/v4l2.c文件中,接下來就針對上文 V4L2程序?qū)崿F(xiàn)流程和流程中使用的重要數(shù)據(jù)結(jié)構(gòu),結(jié)合v4l2.c文件中的代碼進(jìn)行說明。代碼支持內(nèi)存映射和直接讀取兩種方式,由于內(nèi)存映射方式應(yīng)用更廣泛,本文只詳細(xì)說明內(nèi)存映射方式,直接讀取方式與內(nèi)存映射方式類似,可自行研究。
7.4.1 打開設(shè)備
? 應(yīng)用程序能夠使用阻塞模式或非阻塞模式打開視頻設(shè)備,如果使用非阻塞模式調(diào)用視頻設(shè)備,即使尚未捕獲到信息,驅(qū)動依舊會把緩存(DQBUFF)里的東西返回給應(yīng)用程序。如果使用非阻塞的方式打開攝像頭設(shè)備,第2行代碼中open函數(shù)的第二個參數(shù)修改為O_RDWR | O_NONBLOCK 即可。
70 iFd = open(strDevName, O_RDWR);
71 if (iFd < 0)
72 {
73 DBG_PRINTF("can not open %s
", strDevName);
74 return -1;
75 }
7.4.2 查詢設(shè)備屬性
? 查詢設(shè)備屬性需要使用struct v4l2_capability結(jié)構(gòu)體,該結(jié)構(gòu)體描述了視頻采集設(shè)備的driver信息。
01 struct v4l2_capability
02 {
03 __u8 driver[16]; // 驅(qū)動名字
04 __u8 card[32]; // 設(shè)備名字
05 __u8 bus_info[32]; // 設(shè)備在系統(tǒng)中的位置
06 __u32 version; // 驅(qū)動版本號
07 __u32 capabilities; // 設(shè)備支持的操作
08 __u32 reserved[4]; // 保留字段
09 };
? 通過VIDIOC_QUERYCAP命令來查詢driver是否合乎規(guī)范。因為V4L2要求所有driver和device都支持這個ioctl。所以,通過VIDIOC_QUERYCAP命令是否成功來判斷當(dāng)前device和driver是否符合V4L2規(guī)范。當(dāng)然,這個命令執(zhí)行成功的同時還能夠得到設(shè)備足夠的信息,如struct v4l2_capability結(jié)構(gòu)體所示內(nèi)容。86~98行代碼檢查當(dāng)前設(shè)備是否為capture設(shè)備,并檢查使用內(nèi)存映射還是直接讀的方式獲取圖像數(shù)據(jù)。
78 iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
79 memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));
80 iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
81 if (iError) {
82 DBG_PRINTF("Error opening device %s: unable to query device.
", strDevName);
83 goto err_exit;
84 }
85
86 if (!(tV4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
87 {
88 DBG_PRINTF("%s is not a video capture device
", strDevName);
89 goto err_exit;
90 }
91
92 if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING) {
93 DBG_PRINTF("%s supports streaming i/o
", strDevName);
94 }
95
96 if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE) {
97 DBG_PRINTF("%s supports read i/o
", strDevName);
98 }
7.4.3 顯示所有支持的格式
? 顯示所有支持的格式需要用到struct v4l2_fmtdesc結(jié)構(gòu)體,該結(jié)構(gòu)體描述當(dāng)前camera支持的格式信息。
01 struct v4l2_fmtdesc
02 {
03 __u32 index; // 要查詢的格式序號,應(yīng)用程序設(shè)置
04 enum v4l2_buf_type type; // 幀類型,應(yīng)用程序設(shè)置
05 __u32 flags; // 是否為壓縮格式
06 __u8 description[32]; // 格式名稱
07 __u32 pixelformat; //所支持的格式
08 __u32 reserved[4]; // 保留
09 };
? 使用VIDIOC_ENUM_FMT命令查詢當(dāng)前camera支持的所有格式。struct v4l2_fmtdesc結(jié)構(gòu)體中index要設(shè)置,從0開始;enum v4l2_buf_type type也要設(shè)置,如果使用的是camera設(shè)備,則enum v4l2_buf_type type要設(shè)置為V4L2_BUF_TYPE_VIDEO_CAPTURE,因為camera是CAPTURE設(shè)備。結(jié)構(gòu)體中的其他內(nèi)容driver會填充。其中__u32 pixelformat參數(shù)在設(shè)置圖像幀格式時需要使用。
100 memset(&tFmtDesc, 0, sizeof(tFmtDesc));
101 tFmtDesc.index = 0;
102 tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
103 while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
104 if (isSupportThisFormat(tFmtDesc.pixelformat))
105 {
106 ptVideoDevice->iPixelFormat = tFmtDesc.pixelformat;
107 break;
108 }
109 tFmtDesc.index++;
110 }
7.4.4 設(shè)置圖像幀格式
? 設(shè)置圖像格式需要用到struct v4l2_format結(jié)構(gòu)體,該結(jié)構(gòu)體描述每幀圖像的具體格式,包括幀類型以及圖像的長、寬等信息。
01 struct v4l2_format
02 {
03 enum v4l2_buf_type type; // 幀類型,應(yīng)用程序設(shè)置
04 union fmt
05 {
06 struct v4l2_pix_format pix; // 視頻設(shè)備使用
07 structv 4l2_window win;
08 struct v4l2_vbi_format vbi;
09 struct v4l2_sliced_vbi_format sliced;
10 __u8 raw_data[200];
11 };
12 };
? struct v4l2_format結(jié)構(gòu)體需要設(shè)置enum v4l2_buf_type type和union fmt中的struct v4l2_pix_format pix。enum v4l2_buf_type type因為使用的是camera設(shè)備,camera是CAPTURE設(shè)備,所以設(shè)置成V4L2_BUF_TYPE_VIDEO_CAPTURE。struct v4l2_pix_format pix設(shè)置一幀圖像的長、寬和格式等,由于要適配LCD輸出,所以長、寬設(shè)置為LCD支持的長、寬,如124~125行所示。
119 /* set format in */
120 GetDispResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
121 memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
122 tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
123 tV4l2Fmt.fmt.pix.pixelformat = ptVideoDevice->iPixelFormat;
124 tV4l2Fmt.fmt.pix.width = iLcdWidth;
125 tV4l2Fmt.fmt.pix.height = iLcdHeigt;
126 tV4l2Fmt.fmt.pix.field = V4L2_FIELD_ANY;
127
128 /* 如果驅(qū)動程序發(fā)現(xiàn)無法某些參數(shù)(比如分辨率),
129 * 它會調(diào)整這些參數(shù), 并且返回給應(yīng)用程序
130 */
131 iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);
132 if (iError)
133 {
134 DBG_PRINTF("Unable to set format
");
135 goto err_exit;
136 }
7.4.5 申請緩沖區(qū)
? 相關(guān)結(jié)構(gòu)體如下,該結(jié)構(gòu)體描述申請的緩沖區(qū)的基本信息。
01 struct v4l2_requestbuffers
02 {
03 __u32 count; // 緩沖區(qū)內(nèi)緩沖幀的數(shù)目
04 enum v4l2_buf_type type; // 緩沖幀數(shù)據(jù)格式
05 enum v4l2_memorymemory; // 區(qū)別是內(nèi)存映射還是用戶指針方式
06 __u32 reserved[2];
07 };
? 申請一個擁有四個緩沖幀的緩沖區(qū),__u32 count為緩沖幀的數(shù)目;enum v4l2_buf_type type和前文一樣,同樣設(shè)置成V4L2_BUF_TYPE_VIDEO_CAPTURE;enum v4l2_memorymemory用來區(qū)分是內(nèi)存映射還是用戶指針,我們使用內(nèi)存映射的方式,所以設(shè)置成V4L2_MEMORY_MMAP。
140 /* request buffers */
141 memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
142 tV4l2ReqBuffs.count = NB_BUFFER;
143 tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
144 tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;
145
146 iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
147 if (iError)
148 {
149 DBG_PRINTF("Unable to allocate buffers.
");
150 goto err_exit;
151 }
7.4.6 將申請的緩沖幀從內(nèi)核空間映射到用戶空間
? 相關(guān)結(jié)構(gòu)體如下,該結(jié)構(gòu)體表示一幀圖像數(shù)據(jù)的基本信息,包含序號、緩沖幀長度和緩沖幀地址等信息。
01 struct v4l2_buffer
02 {
03 __u32 index; //buffer 序號
04 enum v4l2_buf_type type; //buffer 類型
05 __u32 byteused; //buffer 中已使用的字節(jié)數(shù)
06 __u32 flags; // 區(qū)分是MMAP 還是USERPTR
07 enum v4l2_field field;
08 struct timeval timestamp; // 獲取第一個字節(jié)時的系統(tǒng)時間
09 struct v4l2_timecode timecode;
10 __u32 sequence; // 隊列中的序號
11 enum v4l2_memory memory; //IO 方式,被應(yīng)用程序設(shè)置
12 union m
13 {
14 __u32 offset; // 緩沖幀地址,只對MMAP 有效
15 unsigned long userptr;
16 };
17 __u32 length; // 緩沖幀長度
18 __u32 input;
19 __u32 reserved;
20 };
? 將內(nèi)核空間的幀緩沖映射到用戶空間,需要兩個數(shù)據(jù)接收幀緩沖的長度和地址,我們需要自己定義一個結(jié)構(gòu)體,該結(jié)構(gòu)體位于video2lcd/include/video_manager.h文件中,其中iVideoBufMaxLen接收幀緩沖的長度,pucVideBuf接收幀緩沖地址。
16 struct VideoDevice {
17 int iFd;
18 int iPixelFormat;
19 int iWidth;
20 int iHeight;
21
22 int iVideoBufCnt;
23 int iVideoBufMaxLen;
24 int iVideoBufCurIndex;
25 unsigned char *pucVideBuf[NB_BUFFER];
26
27 /* 函數(shù) */
28 PT_VideoOpr ptOPr;
29 };
? 以下代碼使用VIDIOC_QUERYBUF命令和mmap函數(shù)將內(nèi)核空間的緩沖區(qū)映射到用戶空間。VIDIOC_QUERYBUF命令的使用需要參數(shù)struct v4l2_buffer結(jié)構(gòu)體,結(jié)構(gòu)體中的type、memory和index參數(shù)需要設(shè)置,type和memory和前文中的設(shè)置一樣,分別設(shè)置成V4L2_BUF_TYPE_VIDEO_CAPTURE和 V4L2_MEMORY_MMAP,index參數(shù)表示申請的緩沖幀的標(biāo)號,從0開始,包含申請的所有緩沖幀。
? mmap函數(shù)原形為:
01 void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);
參數(shù)具體的含義:
-
addr:映射起始地址,一般為NULL,讓內(nèi)核自動選擇;
-
length:被映射內(nèi)存塊的長度;
-
prot:標(biāo)志映射后能否被讀寫,其值為PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE;
-
flags:確定此內(nèi)存映射能否被其他進(jìn)程共享,可設(shè)置為MAP_SHARED或MAP_PRIVATE;
-
fd:設(shè)備文件句柄;
- offset:確定映射后的內(nèi)存地址
156 /* map the buffers */
157 for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
158 {
159 memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
160 tV4l2Buf.index = i;
161 tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
162 tV4l2Buf.memory = V4L2_MEMORY_MMAP;
163 iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
164 if (iError)
165 {
166 DBG_PRINTF("Unable to query buffer.
");
167 goto err_exit;
168 }
169
170 ptVideoDevice->iVideoBufMaxLen = tV4l2Buf.length;
171 ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
172 tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
173 tV4l2Buf.m.offset);
174 if (ptVideoDevice->pucVideBuf[i] == MAP_FAILED)
175 {
176 DBG_PRINTF("Unable to map buffer
");
177 goto err_exit;
178 }
179 }
7.4.7 將申請的緩沖幀放入隊列,并啟動數(shù)據(jù)流
? 184~194行代碼為使用VIDIOC_QBUF命令,將申請的緩沖幀依次放入緩沖幀輸入隊列,等待被圖像采集設(shè)備依次填滿;
181 /* Queue the buffers. */
182 for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
183 {
184 memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
185 tV4l2Buf.index = i;
186 tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
187 tV4l2Buf.memory = V4L2_MEMORY_MMAP;
188 iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
189 if (iError)
190 {
191 DBG_PRINTF("Unable to queue buffer.
");
192 goto err_exit;
193 }
194 }
7.4.8 啟動捕捉圖像數(shù)據(jù)
? 啟動捕捉圖像數(shù)據(jù)使用VIDIOC_STREAMON命令,當(dāng)該命令執(zhí)行成功后,便可以等待圖像數(shù)據(jù)的到來。
356 /**********************************************************************
357 * 函數(shù)名稱:V4l2StartDevice
358 * 功能描述:開始捕捉圖像數(shù)據(jù)
359 * 輸入?yún)?shù):ptVideoDevice
360 * 輸出參數(shù):無
361 * 返 回 值:無
362 * 修改日期 版本號 修改人 修改內(nèi)容
363 * -----------------------------------------------
364 * 2020/02/16 V1.0 zhenhua 創(chuàng)建
365 ***********************************************************************/
366 static int V4l2StartDevice(PT_VideoDevice ptVideoDevice)
367 {
368 int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
369 int iError;
370
371 iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMON, &iType);
372 if (iError)
373 {
374 DBG_PRINTF("Unable to start capture.
");
375 return -1;
376 }
377 return 0;
378 }
7.4.9 出列采集的幀緩沖,并處理圖像數(shù)據(jù),然后再將數(shù)據(jù)幀入列
? 我們可以使用VIDIOC_DQBUF命令,等待緩沖幀的到來,當(dāng)有緩沖幀被放入視頻輸出緩沖隊列,我們便可以采到一幀圖像。接收到圖像我們可以對圖像進(jìn)行操作,例如保存、壓縮或者LCD輸出等。
243 /**********************************************************************
244 * 函數(shù)名稱:V4l2GetFrameForStreaming
245 * 功能描述:從圖像數(shù)據(jù)流中獲取一幀圖像數(shù)據(jù)
246 * 輸入?yún)?shù):ptVideoDevice
247 ptVideoBuf
248 * 輸出參數(shù):無
249 * 返 回 值:無
250 * 修改日期 版本號 修改人 修改內(nèi)容
251 * -----------------------------------------------
252 * 2020/02/16 V1.0 zhenhua 創(chuàng)建
253 ***********************************************************************/
254 static int V4l2GetFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
255 {
256 struct pollfd tFds[1];
257 int iRet;
258 struct v4l2_buffer tV4l2Buf;
259
260 /* poll */
261 tFds[0].fd = ptVideoDevice->iFd;
262 tFds[0].events = POLLIN;
263
264 iRet = poll(tFds, 1, -1);
265 if (iRet <= 0)
266 {
267 DBG_PRINTF("poll error!
");
268 return -1;
269 }
270
271 /* VIDIOC_DQBUF */
272 memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
273 tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
274 tV4l2Buf.memory = V4L2_MEMORY_MMAP;
275 iRet = ioctl(ptVideoDevice->iFd, VIDIOC_DQBUF, &tV4l2Buf);
276 if (iRet < 0)
277 {
278 DBG_PRINTF("Unable to dequeue buffer.
");
279 return -1;
280 }
281 ptVideoDevice->iVideoBufCurIndex = tV4l2Buf.index;
282
283 ptVideoBuf->iPixelFormat = ptVideoDevice->iPixelFormat;
284 ptVideoBuf->tPixelDatas.iWidth = ptVideoDevice->iWidth;
285 ptVideoBuf->tPixelDatas.iHeight = ptVideoDevice->iHeight;
286 ptVideoBuf->tPixelDatas.iBpp = (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_YUYV) ? 16 :
287 (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_MJPEG) ? 0 :
288 (ptVideoDevice->iPixelFormat == V4L2_PIX_FMT_RGB565) ? 16 :
289 0;
290 ptVideoBuf->tPixelDatas.iLineBytes = ptVideoDevice->iWidth * ptVideoBuf->tPixelDatas.iBpp / 8;
291 ptVideoBuf->tPixelDatas.iTotalBytes = tV4l2Buf.bytesused;
292 ptVideoBuf->tPixelDatas.aucPixelDatas = ptVideoDevice->pucVideBuf[tV4l2Buf.index];
293 return 0;
294 }
? 當(dāng)我們從緩沖幀輸出隊列取出一個緩沖幀,取出圖像數(shù)據(jù)后我們需要將緩沖幀重新放回到視頻輸入緩沖隊列,該操作還是使用VIDIOC_QBUF命令,放回緩沖幀輸入隊列后繼續(xù)等待被填滿。
296 /**********************************************************************
297 * 函數(shù)名稱:V4l2PutFrameForStreaming
298 * 功能描述:將取出的幀緩沖重新放回圖像輸入隊列
299 * 輸入?yún)?shù):ptVideoDevice
300 ptVideoBuf
301 * 輸出參數(shù):無
302 * 返 回 值:無
303 * 修改日期 版本號 修改人 修改內(nèi)容
304 * -----------------------------------------------
305 * 2020/02/16 V1.0 zhenhua 創(chuàng)建
306 ***********************************************************************/
307 static int V4l2PutFrameForStreaming(PT_VideoDevice ptVideoDevice, PT_VideoBuf ptVideoBuf)
308 {
309 /* VIDIOC_QBUF */
310 struct v4l2_buffer tV4l2Buf;
311 int iError;
312
313 memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
314 tV4l2Buf.index = ptVideoDevice->iVideoBufCurIndex;
315 tV4l2Buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
316 tV4l2Buf.memory = V4L2_MEMORY_MMAP;
317 iError = ioctl(ptVideoDevice->iFd, VIDIOC_QBUF, &tV4l2Buf);
318 if (iError)
319 {
320 DBG_PRINTF("Unable to queue buffer.
");
321 return -1;
322 }
323 return 0;
324 }
7.4.10 停止捕捉圖像數(shù)據(jù)
? 停止采集圖像數(shù)據(jù),首先使用VIDIOC_STREAMOFF命令,關(guān)閉捕獲圖像數(shù)據(jù)。同時要注意取消內(nèi)存映射和關(guān)閉句柄,防止不必要的內(nèi)存泄漏。代碼390~407行為停止捕捉圖像數(shù)據(jù)命令;代碼227~241行為取消內(nèi)存映射和關(guān)閉句柄。
380 /**********************************************************************
381 * 函數(shù)名稱:V4l2StopDevice
382 * 功能描述:停止捕捉圖像數(shù)據(jù)
383 * 輸入?yún)?shù):ptVideoDevice
384 * 輸出參數(shù):無
385 * 返 回 值:無
386 * 修改日期 版本號 修改人 修改內(nèi)容
387 * -----------------------------------------------
388 * 2020/02/16 V1.0 zhenhua 創(chuàng)建
389 ***********************************************************************/
390 static int V4l2StopDevice(PT_VideoDevice ptVideoDevice)
391 {
392 int iType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
393 int iError;
394
395 iError = ioctl(ptVideoDevice->iFd, VIDIOC_STREAMOFF, &iType);
396 if (iError)
397 {
398 DBG_PRINTF("Unable to stop capture.
");
399 return -1;
400 }
401 return 0;
402 }
403
404 static int V4l2GetFormat(PT_VideoDevice ptVideoDevice)
405 {
406 return ptVideoDevice->iPixelFormat;
407 }
217 /**********************************************************************
218 * 函數(shù)名稱:V4l2ExitDevice
219 * 功能描述:退出采集設(shè)備,取消幀緩沖映射和關(guān)閉句柄
220 * 輸入?yún)?shù):ptVideoDevice
221 * 輸出參數(shù):無
222 * 返 回 值:無
223 * 修改日期 版本號 修改人 修改內(nèi)容
224 * -----------------------------------------------
225 * 2020/02/16 V1.0 zhenhua 創(chuàng)建
226 ***********************************************************************/
227 static int V4l2ExitDevice(PT_VideoDevice ptVideoDevice)
228 {
229 int i;
230 for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++)
231 {
232 if (ptVideoDevice->pucVideBuf[i])
233 {
234 munmap(ptVideoDevice->pucVideBuf[i], ptVideoDevice->iVideoBufMaxLen);
235 ptVideoDevice->pucVideBuf[i] = NULL;
236 }
237 }
238
239 close(ptVideoDevice->iFd);
240 return 0;
241 }
本文摘自 :https://blog.51cto.com/w