文章目錄
7 攝像頭V4L2編程應用開發
7.1 V4L2簡介
7.2 V4L2視頻采集原理
7.3 V4L2程序實現流程
7.4 V4L2程序實例
7.4.1 打開設備
7.4.2 查詢設備屬性
7.4.3 顯示所有支持的格式
7.4.4 設置圖像幀格式
7.4.5 申請緩沖區
7.4.6 將申請的緩沖幀從內核空間映射到用戶空間
7.4.7 將申請的緩沖幀放入隊列,并啟動數據流
7.4.8 啟動捕捉圖像數據
7.4.9 出列采集的幀緩沖,并處理圖像數據,然后再將數據幀入列
7.4.10 停止捕捉圖像數據
7 攝像頭V4L2編程應用開發
7.1 V4L2簡介
? Video for Linux two(Video4Linux2)簡稱V4L2,是V4L的改進版。V4L2是linux操作系統下一套用于采集圖片、視頻和音頻數據的通用API接口,配合適當的視頻采集設備和相應的驅動程序,可以實現圖片、視頻、音頻等的采集。V4L2像一個優秀的快遞員,將視頻采集設備的圖像數據安全、高效的傳遞給不同需求的用戶。
? 在Linux中,一切皆文件,所有外設都被看成一種特殊的文件,稱為“設備文件”。視頻設備也不例外,也可以可以看成是設備文件,可以像訪問普通文件一樣對其進行讀寫。V4L2驅動的攝像頭的設備文件一般是/dev/videoX(X為任意數字,要與自己的設備相對應)。
? V4L2支持三種方式來采集圖像:內存映射方式(mmap)、直接讀取方式(read)和用戶指針。內存映射的方式采集速度較快,一般用于連續視頻數據的采集,實際工作中的應用概率更高;直接讀取的方式相對速度慢一些,所以常用于靜態圖片數據的采集;用戶指針使用較少,如有興趣可自行研究。由于內存映射方式的應用更廣泛,所以本文重點討論內存映射方式的視頻采集。
7.2 V4L2視頻采集原理
? 在通過V4L2采集圖像之前,我們需要做的很多,但是很重要的一步是分配幀緩沖區,并將分配的幀緩沖區從內核空間映射到用戶空間,然后將申請到的幀緩沖區在視頻采集輸入隊列排隊,剩下的就是等待視頻數據的到來。但是,萬一視頻數據真的來了是怎么個流動過程呢?這個我們有必要了解一下。
? 當啟動視頻采集后,驅動程序開始采集一幀圖像數據,會把采集的圖像數據放入視頻采集輸入隊列的第一個幀緩沖區,一陣圖像數據就算采集完成了。第一個幀緩沖區存滿一幀圖像數據后,驅動程序將該幀緩沖區移至視頻采集輸出隊列,等待應用程序從輸出隊列取出,應用程序取出圖像數據可以對圖像數據進行處理或存儲操作,然后將幀該緩沖區放入視頻采集輸入隊列的尾部。驅動程序接下來采集下一幀數據,放入第二個緩沖區,同樣的幀緩沖區存滿一幀數據后,驅動程序將該緩沖區移至視頻采集輸出隊列,應用程序將該幀緩沖區的圖像數據取出后又將該幀緩沖區放入視頻輸入隊列尾部,這樣循環往復就實現了循環采集。流程如下圖所示:
? 為了更好的理解這個過程,我們可以把“應用程序處理數據”比喻成“西瓜加工商加工西瓜”,“V4L2驅動程序采集數據”比喻成“西瓜采集員采集西瓜”,事先“西瓜加工商”會給“西瓜采集員”準備幾個空籃子,然后“西瓜采集員”守著幾個空籃子等待“瓜農”(圖像采集設備,例如:攝像頭)將空籃子裝滿,當“空籃子1”被“瓜農”裝滿以后,“西瓜采集員”會將裝滿西瓜的籃子放到“西瓜加工隊列”等待“西瓜加工商”取走加工,當“西瓜加工商”取走裝滿西瓜的籃子中的西瓜的時候,“西瓜加工商”會將空籃子放回到事先給“西瓜采集員”準備好的西瓜采集隊列的尾部。當“瓜農”裝滿下一個空籃子的時候,“西瓜采集員”同樣的將裝滿西瓜的籃子放到“西瓜加工隊列”等待“西瓜加工商”取走加工。這樣,整個過程會持續不斷的繼續下去。
7.3 V4L2程序實現流程
? 使用V4L2進行視頻采集,一般分為5個步驟:
(1)打開設備,進行初始化參數設置,通過V4L2接口設置視頻圖像的采集窗口、采集的點陣大小和格式;
(2)申請圖像幀緩沖,并進行內存映射,將這些幀緩沖區從內核空間映射到用戶空間,便于應用程序讀取、處理圖像數據;
(3)將幀緩沖進行入隊操作,啟動視頻采集;
(4)驅動開始視頻數據的采集,應用程序從視頻采集輸出隊列取出幀緩沖區,處理完后,將幀緩沖區重新放入視頻采集輸入隊列,循環往復采集連續的視頻數據;
(5)釋放資源,停止采集工作。
? 在進行V4L2開發中,常用的命令標識符如下:
(1)VIDIOC_REQBUFS:分配內存;
(2)VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數據緩存轉換成物理地址;
(3)VIDIOC_QUERYCAP:查詢驅動功能;
(4)VIDIOC_ENUM_FMT:獲取當前驅動支持的視頻格式;
(5)VIDIOC_S_FMT:設置當前驅動的視頻捕獲格式;
(6)VIDIOC_G_FMT:讀取當前驅動的視頻捕獲格式;
(7)VIDIOC_TRY_FMT:驗證當前驅動的顯示格式;
(8)VIDIOC_CROPCAP:查詢驅動的修剪功能;
(9)VIDIOC_S_CROP:設置視頻信號的邊框;
(10)VIDIOC_G_CROP:讀取視頻信號的邊框;
(11)VIDIOC_QBUF:把數據從緩存中讀取出來;
(12)VIDIOC_DQBUF:把數據放回緩存隊列;
(13)VIDIOC_STREAMOP:開始視頻顯示函數;
(14)VIDIOC_STREAMOFF:結束視頻顯示函數;
(15)VIDIOC_QUERYSTD:檢查當前視頻設備支持的標準,例如PAL或NTSC;
這些IO調用,有些是必須的,有些是可選擇的。
具體流程如下圖所示:
7.4 V4L2程序實例
? V4L2的代碼主要位于video2lcd/video/v4l2.c文件中,接下來就針對上文 V4L2程序實現流程和流程中使用的重要數據結構,結合v4l2.c文件中的代碼進行說明。代碼支持內存映射和直接讀取兩種方式,由于內存映射方式應用更廣泛,本文只詳細說明內存映射方式,直接讀取方式與內存映射方式類似,可自行研究。
7.4.1 打開設備
? 應用程序能夠使用阻塞模式或非阻塞模式打開視頻設備,如果使用非阻塞模式調用視頻設備,即使尚未捕獲到信息,驅動依舊會把緩存(DQBUFF)里的東西返回給應用程序。如果使用非阻塞的方式打開攝像頭設備,第2行代碼中open函數的第二個參數修改為O_RDWR | O_NONBLOCK 即可。
70 iFd = open(strDevName, O_RDWR); 71 if (iFd < 0) 72 { 73 DBG_PRINTF("can not open %sn", strDevName); 74 return -1; 75 }
7.4.2 查詢設備屬性
? 查詢設備屬性需要使用struct v4l2_capability結構體,該結構體描述了視頻采集設備的driver信息。
01 struct v4l2_capability 02 { 03 __u8 driver[16]; // 驅動名字 04 __u8 card[32]; // 設備名字 05 __u8 bus_info[32]; // 設備在系統中的位置 06 __u32 version; // 驅動版本號 07 __u32 capabilities; // 設備支持的操作 08 __u32 reserved[4]; // 保留字段 09 };
? 通過VIDIOC_QUERYCAP命令來查詢driver是否合乎規范。因為V4L2要求所有driver和device都支持這個ioctl。所以,通過VIDIOC_QUERYCAP命令是否成功來判斷當前device和driver是否符合V4L2規范。當然,這個命令執行成功的同時還能夠得到設備足夠的信息,如struct v4l2_capability結構體所示內容。86~98行代碼檢查當前設備是否為capture設備,并檢查使用內存映射還是直接讀的方式獲取圖像數據。
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.n", 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 devicen", strDevName); 89 goto err_exit; 90 } 91 92 if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING) { 93 DBG_PRINTF("%s supports streaming i/on", strDevName); 94 } 95 96 if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE) { 97 DBG_PRINTF("%s supports read i/on", strDevName); 98 }
7.4.3 顯示所有支持的格式
? 顯示所有支持的格式需要用到struct v4l2_fmtdesc結構體,該結構體描述當前camera支持的格式信息。
01 struct v4l2_fmtdesc 02 { 03 __u32 index; // 要查詢的格式序號,應用程序設置 04 enum v4l2_buf_type type; // 幀類型,應用程序設置 05 __u32 flags; // 是否為壓縮格式 06 __u8 description[32]; // 格式名稱 07 __u32 pixelformat; //所支持的格式 08 __u32 reserved[4]; // 保留 09 };
? 使用VIDIOC_ENUM_FMT命令查詢當前camera支持的所有格式。struct v4l2_fmtdesc結構體中index要設置,從0開始;enum v4l2_buf_type type也要設置,如果使用的是camera設備,則enum v4l2_buf_type type要設置為V4L2_BUF_TYPE_VIDEO_CAPTURE,因為camera是CAPTURE設備。結構體中的其他內容driver會填充。其中__u32 pixelformat參數在設置圖像幀格式時需要使用。
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 設置圖像幀格式
? 設置圖像格式需要用到struct v4l2_format結構體,該結構體描述每幀圖像的具體格式,包括幀類型以及圖像的長、寬等信息。
01 struct v4l2_format 02 { 03 enum v4l2_buf_type type; // 幀類型,應用程序設置 04 union fmt 05 { 06 struct v4l2_pix_format pix; // 視頻設備使用 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結構體需要設置enum v4l2_buf_type type和union fmt中的struct v4l2_pix_format pix。enum v4l2_buf_type type因為使用的是camera設備,camera是CAPTURE設備,所以設置成V4L2_BUF_TYPE_VIDEO_CAPTURE。struct v4l2_pix_format pix設置一幀圖像的長、寬和格式等,由于要適配LCD輸出,所以長、寬設置為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 /* 如果驅動程序發現無法某些參數(比如分辨率), 129 * 它會調整這些參數, 并且返回給應用程序 130 */ 131 iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt); 132 if (iError) 133 { 134 DBG_PRINTF("Unable to set formatn"); 135 goto err_exit; 136 }
7.4.5 申請緩沖區
? 相關結構體如下,該結構體描述申請的緩沖區的基本信息。
01 struct v4l2_requestbuffers 02 { 03 __u32 count; // 緩沖區內緩沖幀的數目 04 enum v4l2_buf_type type; // 緩沖幀數據格式 05 enum v4l2_memorymemory; // 區別是內存映射還是用戶指針方式 06 __u32 reserved[2]; 07 };
? 申請一個擁有四個緩沖幀的緩沖區,__u32 count為緩沖幀的數目;enum v4l2_buf_type type和前文一樣,同樣設置成V4L2_BUF_TYPE_VIDEO_CAPTURE;enum v4l2_memorymemory用來區分是內存映射還是用戶指針,我們使用內存映射的方式,所以設置成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.n"); 150 goto err_exit; 151 }
7.4.6 將申請的緩沖幀從內核空間映射到用戶空間
? 相關結構體如下,該結構體表示一幀圖像數據的基本信息,包含序號、緩沖幀長度和緩沖幀地址等信息。
01 struct v4l2_buffer 02 { 03 __u32 index; //buffer 序號 04 enum v4l2_buf_type type; //buffer 類型 05 __u32 byteused; //buffer 中已使用的字節數 06 __u32 flags; // 區分是MMAP 還是USERPTR 07 enum v4l2_field field; 08 struct timeval timestamp; // 獲取第一個字節時的系統時間 09 struct v4l2_timecode timecode; 10 __u32 sequence; // 隊列中的序號 11 enum v4l2_memory memory; //IO 方式,被應用程序設置 12 union m 13 { 14 __u32 offset; // 緩沖幀地址,只對MMAP 有效 15 unsigned long userptr; 16 }; 17 __u32 length; // 緩沖幀長度 18 __u32 input; 19 __u32 reserved; 20 };
? 將內核空間的幀緩沖映射到用戶空間,需要兩個數據接收幀緩沖的長度和地址,我們需要自己定義一個結構體,該結構體位于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 /* 函數 */ 28 PT_VideoOpr ptOPr; 29 };
? 以下代碼使用VIDIOC_QUERYBUF命令和mmap函數將內核空間的緩沖區映射到用戶空間。VIDIOC_QUERYBUF命令的使用需要參數struct v4l2_buffer結構體,結構體中的type、memory和index參數需要設置,type和memory和前文中的設置一樣,分別設置成V4L2_BUF_TYPE_VIDEO_CAPTURE和 V4L2_MEMORY_MMAP,index參數表示申請的緩沖幀的標號,從0開始,包含申請的所有緩沖幀。
? mmap函數原形為:
01 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:確定映射后的內存地址
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.n"); 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 buffern"); 177 goto err_exit; 178 } 179 }
7.4.7 將申請的緩沖幀放入隊列,并啟動數據流
? 184~194行代碼為使用VIDIOC_QBUF命令,將申請的緩沖幀依次放入緩沖幀輸入隊列,等待被圖像采集設備依次填滿;
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.n"); 192 goto err_exit; 193 } 194 }
7.4.8 啟動捕捉圖像數據
? 啟動捕捉圖像數據使用VIDIOC_STREAMON命令,當該命令執行成功后,便可以等待圖像數據的到來。
356 /********************************************************************** 357 * 函數名稱:V4l2StartDevice 358 * 功能描述:開始捕捉圖像數據 359 * 輸入參數:ptVideoDevice 360 * 輸出參數:無 361 * 返 回 值:無 362 * 修改日期 版本號 修改人 修改內容 363 * ----------------------------------------------- 364 * 2020/02/16 V1.0 zhenhua 創建 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.n"); 375 return -1; 376 } 377 return 0; 378 }
7.4.9 出列采集的幀緩沖,并處理圖像數據,然后再將數據幀入列
? 我們可以使用VIDIOC_DQBUF命令,等待緩沖幀的到來,當有緩沖幀被放入視頻輸出緩沖隊列,我們便可以采到一幀圖像。接收到圖像我們可以對圖像進行操作,例如保存、壓縮或者LCD輸出等。
243 /********************************************************************** 244 * 函數名稱:V4l2GetFrameForStreaming 245 * 功能描述:從圖像數據流中獲取一幀圖像數據 246 * 輸入參數:ptVideoDevice 247 ptVideoBuf 248 * 輸出參數:無 249 * 返 回 值:無 250 * 修改日期 版本號 修改人 修改內容 251 * ----------------------------------------------- 252 * 2020/02/16 V1.0 zhenhua 創建 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!n"); 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.n"); 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 }
? 當我們從緩沖幀輸出隊列取出一個緩沖幀,取出圖像數據后我們需要將緩沖幀重新放回到視頻輸入緩沖隊列,該操作還是使用VIDIOC_QBUF命令,放回緩沖幀輸入隊列后繼續等待被填滿。
296 /********************************************************************** 297 * 函數名稱:V4l2PutFrameForStreaming 298 * 功能描述:將取出的幀緩沖重新放回圖像輸入隊列 299 * 輸入參數:ptVideoDevice 300 ptVideoBuf 301 * 輸出參數:無 302 * 返 回 值:無 303 * 修改日期 版本號 修改人 修改內容 304 * ----------------------------------------------- 305 * 2020/02/16 V1.0 zhenhua 創建 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.n"); 321 return -1; 322 } 323 return 0; 324 }
7.4.10 停止捕捉圖像數據
? 停止采集圖像數據,首先使用VIDIOC_STREAMOFF命令,關閉捕獲圖像數據。同時要注意取消內存映射和關閉句柄,防止不必要的內存泄漏。代碼390407行為停止捕捉圖像數據命令;代碼227241行為取消內存映射和關閉句柄。
380 /********************************************************************** 381 * 函數名稱:V4l2StopDevice 382 * 功能描述:停止捕捉圖像數據 383 * 輸入參數:ptVideoDevice 384 * 輸出參數:無 385 * 返 回 值:無 386 * 修改日期 版本號 修改人 修改內容 387 * ----------------------------------------------- 388 * 2020/02/16 V1.0 zhenhua 創建 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.n"); 399 return -1; 400 } 401 return 0; 402 } 403 404 static int V4l2GetFormat(PT_VideoDevice ptVideoDevice) 405 { 406 return ptVideoDevice->iPixelFormat; 407 } 217 /********************************************************************** 218 * 函數名稱:V4l2ExitDevice 219 * 功能描述:退出采集設備,取消幀緩沖映射和關閉句柄 220 * 輸入參數:ptVideoDevice 221 * 輸出參數:無 222 * 返 回 值:無 223 * 修改日期 版本號 修改人 修改內容 224 * ----------------------------------------------- 225 * 2020/02/16 V1.0 zhenhua 創建 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 } 審核編輯 黃昊宇
-
Linux
+關注
關注
87文章
11225瀏覽量
208914 -
攝像頭
+關注
關注
59文章
4807瀏覽量
95418
發布評論請先 登錄
相關推薦
評論