在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux應(yīng)用開(kāi)發(fā)【第七章】攝像頭V4L2編程應(yīng)用開(kāi)發(fā)

weidongshan ? 來(lái)源:weidongshan ? 作者:weidongshan ? 2021-12-10 19:23 ? 次閱讀

文章目錄

7 攝像頭V4L2編程應(yīng)用開(kāi)發(fā)

7.1 V4L2簡(jiǎn)介

7.2 V4L2視頻采集原理

7.3 V4L2程序?qū)崿F(xiàn)流程

7.4 V4L2程序?qū)嵗?/p>

7.4.1 打開(kāi)設(shè)備

7.4.2 查詢?cè)O(shè)備屬性

7.4.3 顯示所有支持的格式

7.4.4 設(shè)置圖像幀格式

7.4.5 申請(qǐng)緩沖區(qū)

7.4.6 將申請(qǐng)的緩沖幀從內(nèi)核空間映射到用戶空間

7.4.7 將申請(qǐng)的緩沖幀放入隊(duì)列,并啟動(dòng)數(shù)據(jù)流

7.4.8 啟動(dòng)捕捉圖像數(shù)據(jù)

7.4.9 出列采集的幀緩沖,并處理圖像數(shù)據(jù),然后再將數(shù)據(jù)幀入列

7.4.10 停止捕捉圖像數(shù)據(jù)

7 攝像頭V4L2編程應(yīng)用開(kāi)發(fā)

7.1 V4L2簡(jiǎn)介

? Video for Linux two(Video4Linux2)簡(jiǎn)稱V4L2,是V4L的改進(jìn)版。V4L2是linux操作系統(tǒng)下一套用于采集圖片、視頻和音頻數(shù)據(jù)的通用API接口,配合適當(dāng)?shù)囊曨l采集設(shè)備和相應(yīng)的驅(qū)動(dòng)程序,可以實(shí)現(xiàn)圖片、視頻、音頻等的采集。V4L2像一個(gè)優(yōu)秀的快遞員,將視頻采集設(shè)備的圖像數(shù)據(jù)安全、高效的傳遞給不同需求的用戶。

? 在Linux中,一切皆文件,所有外設(shè)都被看成一種特殊的文件,稱為“設(shè)備文件”。視頻設(shè)備也不例外,也可以可以看成是設(shè)備文件,可以像訪問(wèn)普通文件一樣對(duì)其進(jìn)行讀寫。V4L2驅(qū)動(dòng)的攝像頭的設(shè)備文件一般是/dev/videoX(X為任意數(shù)字,要與自己的設(shè)備相對(duì)應(yīng))。

? V4L2支持三種方式來(lái)采集圖像:內(nèi)存映射方式(mmap)、直接讀取方式(read)和用戶指針。內(nèi)存映射的方式采集速度較快,一般用于連續(xù)視頻數(shù)據(jù)的采集,實(shí)際工作中的應(yīng)用概率更高;直接讀取的方式相對(duì)速度慢一些,所以常用于靜態(tài)圖片數(shù)據(jù)的采集;用戶指針使用較少,如有興趣可自行研究。由于內(nèi)存映射方式的應(yīng)用更廣泛,所以本文重點(diǎn)討論內(nèi)存映射方式的視頻采集。

7.2 V4L2視頻采集原理

? 在通過(guò)V4L2采集圖像之前,我們需要做的很多,但是很重要的一步是分配幀緩沖區(qū),并將分配的幀緩沖區(qū)從內(nèi)核空間映射到用戶空間,然后將申請(qǐng)到的幀緩沖區(qū)在視頻采集輸入隊(duì)列排隊(duì),剩下的就是等待視頻數(shù)據(jù)的到來(lái)。但是,萬(wàn)一視頻數(shù)據(jù)真的來(lái)了是怎么個(gè)流動(dòng)過(guò)程呢?這個(gè)我們有必要了解一下。

? 當(dāng)啟動(dòng)視頻采集后,驅(qū)動(dòng)程序開(kāi)始采集一幀圖像數(shù)據(jù),會(huì)把采集的圖像數(shù)據(jù)放入視頻采集輸入隊(duì)列的第一個(gè)幀緩沖區(qū),一陣圖像數(shù)據(jù)就算采集完成了。第一個(gè)幀緩沖區(qū)存滿一幀圖像數(shù)據(jù)后,驅(qū)動(dòng)程序?qū)⒃搸彌_區(qū)移至視頻采集輸出隊(duì)列,等待應(yīng)用程序從輸出隊(duì)列取出,應(yīng)用程序取出圖像數(shù)據(jù)可以對(duì)圖像數(shù)據(jù)進(jìn)行處理或存儲(chǔ)操作,然后將幀該緩沖區(qū)放入視頻采集輸入隊(duì)列的尾部。驅(qū)動(dòng)程序接下來(lái)采集下一幀數(shù)據(jù),放入第二個(gè)緩沖區(qū),同樣的幀緩沖區(qū)存滿一幀數(shù)據(jù)后,驅(qū)動(dòng)程序?qū)⒃摼彌_區(qū)移至視頻采集輸出隊(duì)列,應(yīng)用程序?qū)⒃搸彌_區(qū)的圖像數(shù)據(jù)取出后又將該幀緩沖區(qū)放入視頻輸入隊(duì)列尾部,這樣循環(huán)往復(fù)就實(shí)現(xiàn)了循環(huán)采集。流程如下圖所示:

pYYBAGGzOMaAf72oAAEg9RkTlq0902.png

? 為了更好的理解這個(gè)過(guò)程,我們可以把“應(yīng)用程序處理數(shù)據(jù)”比喻成“西瓜加工商加工西瓜”,“V4L2驅(qū)動(dòng)程序采集數(shù)據(jù)”比喻成“西瓜采集員采集西瓜”,事先“西瓜加工商”會(huì)給“西瓜采集員”準(zhǔn)備幾個(gè)空籃子,然后“西瓜采集員”守著幾個(gè)空籃子等待“瓜農(nóng)”(圖像采集設(shè)備,例如:攝像頭)將空籃子裝滿,當(dāng)“空籃子1”被“瓜農(nóng)”裝滿以后,“西瓜采集員”會(huì)將裝滿西瓜的籃子放到“西瓜加工隊(duì)列”等待“西瓜加工商”取走加工,當(dāng)“西瓜加工商”取走裝滿西瓜的籃子中的西瓜的時(shí)候,“西瓜加工商”會(huì)將空籃子放回到事先給“西瓜采集員”準(zhǔn)備好的西瓜采集隊(duì)列的尾部。當(dāng)“瓜農(nóng)”裝滿下一個(gè)空籃子的時(shí)候,“西瓜采集員”同樣的將裝滿西瓜的籃子放到“西瓜加工隊(duì)列”等待“西瓜加工商”取走加工。這樣,整個(gè)過(guò)程會(huì)持續(xù)不斷的繼續(xù)下去。

poYBAGGzOMaAcV32AAD3GsjaP9E758.png

7.3 V4L2程序?qū)崿F(xiàn)流程

? 使用V4L2進(jìn)行視頻采集,一般分為5個(gè)步驟:

(1)打開(kāi)設(shè)備,進(jìn)行初始化參數(shù)設(shè)置,通過(guò)V4L2接口設(shè)置視頻圖像的采集窗口、采集的點(diǎn)陣大小和格式;

(2)申請(qǐng)圖像幀緩沖,并進(jìn)行內(nèi)存映射,將這些幀緩沖區(qū)從內(nèi)核空間映射到用戶空間,便于應(yīng)用程序讀取、處理圖像數(shù)據(jù);

(3)將幀緩沖進(jìn)行入隊(duì)操作,啟動(dòng)視頻采集;

(4)驅(qū)動(dòng)開(kāi)始視頻數(shù)據(jù)的采集,應(yīng)用程序從視頻采集輸出隊(duì)列取出幀緩沖區(qū),處理完后,將幀緩沖區(qū)重新放入視頻采集輸入隊(duì)列,循環(huán)往復(fù)采集連續(xù)的視頻數(shù)據(jù);

(5)釋放資源,停止采集工作。

? 在進(jìn)行V4L2開(kāi)發(fā)中,常用的命令標(biāo)識(shí)符如下:

(1)VIDIOC_REQBUFS:分配內(nèi)存;

(2)VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數(shù)據(jù)緩存轉(zhuǎn)換成物理地址;

(3)VIDIOC_QUERYCAP:查詢驅(qū)動(dòng)功能;

(4)VIDIOC_ENUM_FMT:獲取當(dāng)前驅(qū)動(dòng)支持的視頻格式;

(5)VIDIOC_S_FMT:設(shè)置當(dāng)前驅(qū)動(dòng)的視頻捕獲格式;

(6)VIDIOC_G_FMT:讀取當(dāng)前驅(qū)動(dòng)的視頻捕獲格式;

(7)VIDIOC_TRY_FMT:驗(yàn)證當(dāng)前驅(qū)動(dòng)的顯示格式;

(8)VIDIOC_CROPCAP:查詢驅(qū)動(dòng)的修剪功能;

(9)VIDIOC_S_CROP:設(shè)置視頻信號(hào)的邊框;

(10)VIDIOC_G_CROP:讀取視頻信號(hào)的邊框;

(11)VIDIOC_QBUF:把數(shù)據(jù)從緩存中讀取出來(lái);

(12)VIDIOC_DQBUF:把數(shù)據(jù)放回緩存隊(duì)列;

(13)VIDIOC_STREAMOP:開(kāi)始視頻顯示函數(shù);

(14)VIDIOC_STREAMOFF:結(jié)束視頻顯示函數(shù);

(15)VIDIOC_QUERYSTD:檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),例如PAL或NTSC;

這些IO調(diào)用,有些是必須的,有些是可選擇的。

具體流程如下圖所示:

pYYBAGGzOMaABTDrAAD_YsdEc08755.png

7.4 V4L2程序?qū)嵗?/strong>

? V4L2的代碼主要位于video2lcd/video/v4l2.c文件中,接下來(lái)就針對(duì)上文 V4L2程序?qū)崿F(xiàn)流程和流程中使用的重要數(shù)據(jù)結(jié)構(gòu),結(jié)合v4l2.c文件中的代碼進(jìn)行說(shuō)明。代碼支持內(nèi)存映射和直接讀取兩種方式,由于內(nèi)存映射方式應(yīng)用更廣泛,本文只詳細(xì)說(shuō)明內(nèi)存映射方式,直接讀取方式與內(nèi)存映射方式類似,可自行研究。

7.4.1 打開(kāi)設(shè)備

? 應(yīng)用程序能夠使用阻塞模式或非阻塞模式打開(kāi)視頻設(shè)備,如果使用非阻塞模式調(diào)用視頻設(shè)備,即使尚未捕獲到信息,驅(qū)動(dòng)依舊會(huì)把緩存(DQBUFF)里的東西返回給應(yīng)用程序。如果使用非阻塞的方式打開(kāi)攝像頭設(shè)備,第2行代碼中open函數(shù)的第二個(gè)參數(shù)修改為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 查詢?cè)O(shè)備屬性

? 查詢?cè)O(shè)備屬性需要使用struct v4l2_capability結(jié)構(gòu)體,該結(jié)構(gòu)體描述了視頻采集設(shè)備的driver信息。

01 struct v4l2_capability
02 {
03     __u8 driver[16];       // 驅(qū)動(dòng)名字
04     __u8 card[32];         // 設(shè)備名字
05     __u8 bus_info[32];     // 設(shè)備在系統(tǒng)中的位置
06     __u32 version;         // 驅(qū)動(dòng)版本號(hào)
07     __u32 capabilities;    // 設(shè)備支持的操作
08     __u32 reserved[4];     // 保留字段
09 };

? 通過(guò)VIDIOC_QUERYCAP命令來(lái)查詢driver是否合乎規(guī)范。因?yàn)閂4L2要求所有driver和device都支持這個(gè)ioctl。所以,通過(guò)VIDIOC_QUERYCAP命令是否成功來(lái)判斷當(dāng)前device和driver是否符合V4L2規(guī)范。當(dāng)然,這個(gè)命令執(zhí)行成功的同時(shí)還能夠得到設(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.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結(jié)構(gòu)體,該結(jié)構(gòu)體描述當(dāng)前camera支持的格式信息。

01 struct v4l2_fmtdesc
02 {
03     __u32 index;               // 要查詢的格式序號(hào),應(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開(kāi)始;enum v4l2_buf_type type也要設(shè)置,如果使用的是camera設(shè)備,則enum v4l2_buf_type type要設(shè)置為V4L2_BUF_TYPE_VIDEO_CAPTURE,因?yàn)閏amera是CAPTURE設(shè)備。結(jié)構(gòu)體中的其他內(nèi)容driver會(huì)填充。其中__u32 pixelformat參數(shù)在設(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)體描述每幀圖像的具體格式,包括幀類型以及圖像的長(zhǎng)、寬等信息。

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因?yàn)槭褂玫氖莄amera設(shè)備,camera是CAPTURE設(shè)備,所以設(shè)置成V4L2_BUF_TYPE_VIDEO_CAPTURE。struct v4l2_pix_format pix設(shè)置一幀圖像的長(zhǎng)、寬和格式等,由于要適配LCD輸出,所以長(zhǎng)、寬設(shè)置為L(zhǎng)CD支持的長(zhǎng)、寬,如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ū)動(dòng)程序發(fā)現(xiàn)無(wú)法某些參數(shù)(比如分辨率),
129      * 它會(huì)調(diào)整這些參數(shù), 并且返回給應(yīng)用程序
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 申請(qǐng)緩沖區(qū)

? 相關(guān)結(jié)構(gòu)體如下,該結(jié)構(gòu)體描述申請(qǐng)的緩沖區(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)存映射還是用戶指針?lè)绞?06     __u32 reserved[2];
07 };

? 申請(qǐng)一個(gè)擁有四個(gè)緩沖幀的緩沖區(qū),__u32 count為緩沖幀的數(shù)目;enum v4l2_buf_type type和前文一樣,同樣設(shè)置成V4L2_BUF_TYPE_VIDEO_CAPTURE;enum v4l2_memorymemory用來(lái)區(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.n");
150         goto err_exit;
151     }

7.4.6 將申請(qǐng)的緩沖幀從內(nèi)核空間映射到用戶空間

? 相關(guān)結(jié)構(gòu)體如下,該結(jié)構(gòu)體表示一幀圖像數(shù)據(jù)的基本信息,包含序號(hào)、緩沖幀長(zhǎng)度和緩沖幀地址等信息。

01 struct v4l2_buffer
02 {
03     __u32 index;                    //buffer 序號(hào)
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;       // 獲取第一個(gè)字節(jié)時(shí)的系統(tǒng)時(shí)間
09     struct v4l2_timecode timecode;
10     __u32 sequence;                 // 隊(duì)列中的序號(hào)
11     enum v4l2_memory memory;        //IO 方式,被應(yīng)用程序設(shè)置
12     union m
13     {
14         __u32 offset;               // 緩沖幀地址,只對(duì)MMAP 有效
15         unsigned long userptr;
16     };
17     __u32 length;                   // 緩沖幀長(zhǎng)度
18     __u32 input;
19     __u32 reserved;
20 };

? 將內(nèi)核空間的幀緩沖映射到用戶空間,需要兩個(gè)數(shù)據(jù)接收幀緩沖的長(zhǎng)度和地址,我們需要自己定義一個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體位于video2lcd/include/video_manager.h文件中,其中iVideoBufMaxLen接收幀緩沖的長(zhǎng)度,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ù)表示申請(qǐng)的緩沖幀的標(biāo)號(hào),從0開(kāi)始,包含申請(qǐng)的所有緩沖幀。

? mmap函數(shù)原形為:

01 void *mmap(void*addr, size_t length, int prot, int flags, int fd, off_t offset);

參數(shù)具體的含義:

addr:映射起始地址,一般為NULL,讓內(nèi)核自動(dòng)選擇;

length:被映射內(nèi)存塊的長(zhǎng)度;

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.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 將申請(qǐng)的緩沖幀放入隊(duì)列,并啟動(dòng)數(shù)據(jù)流

? 184~194行代碼為使用VIDIOC_QBUF命令,將申請(qǐng)的緩沖幀依次放入緩沖幀輸入隊(duì)列,等待被圖像采集設(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.n");
192                 goto err_exit;
193             }
194         }

7.4.8 啟動(dòng)捕捉圖像數(shù)據(jù)

? 啟動(dòng)捕捉圖像數(shù)據(jù)使用VIDIOC_STREAMON命令,當(dāng)該命令執(zhí)行成功后,便可以等待圖像數(shù)據(jù)的到來(lái)。

356 /**********************************************************************
357 * 函數(shù)名稱:V4l2StartDevice
358 * 功能描述:開(kāi)始捕捉圖像數(shù)據(jù)
359 * 輸入?yún)?shù):ptVideoDevice
360 * 輸出參數(shù):無(wú)
361 * 返 回 值:無(wú)
362 * 修改日期             版本號(hào)        修改人           修改內(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.n");
375             return -1;
376     }
377     return 0;
378 }

7.4.9 出列采集的幀緩沖,并處理圖像數(shù)據(jù),然后再將數(shù)據(jù)幀入列

? 我們可以使用VIDIOC_DQBUF命令,等待緩沖幀的到來(lái),當(dāng)有緩沖幀被放入視頻輸出緩沖隊(duì)列,我們便可以采到一幀圖像。接收到圖像我們可以對(duì)圖像進(jìn)行操作,例如保存、壓縮或者LCD輸出等。

243 /**********************************************************************
244 * 函數(shù)名稱:V4l2GetFrameForStreaming
245 * 功能描述:從圖像數(shù)據(jù)流中獲取一幀圖像數(shù)據(jù)
246 * 輸入?yún)?shù):ptVideoDevice
247             ptVideoBuf
248 * 輸出參數(shù):無(wú)
249 * 返 回 值:無(wú)
250 * 修改日期             版本號(hào)        修改人           修改內(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!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 }

? 當(dāng)我們從緩沖幀輸出隊(duì)列取出一個(gè)緩沖幀,取出圖像數(shù)據(jù)后我們需要將緩沖幀重新放回到視頻輸入緩沖隊(duì)列,該操作還是使用VIDIOC_QBUF命令,放回緩沖幀輸入隊(duì)列后繼續(xù)等待被填滿。

296 /**********************************************************************
297 * 函數(shù)名稱:V4l2PutFrameForStreaming
298 * 功能描述:將取出的幀緩沖重新放回圖像輸入隊(duì)列
299 * 輸入?yún)?shù):ptVideoDevice
300             ptVideoBuf
301 * 輸出參數(shù):無(wú)
302 * 返 回 值:無(wú)
303 * 修改日期             版本號(hào)        修改人           修改內(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.n");
321         return -1;
322     }
323     return 0;
324 }

7.4.10 停止捕捉圖像數(shù)據(jù)

? 停止采集圖像數(shù)據(jù),首先使用VIDIOC_STREAMOFF命令,關(guān)閉捕獲圖像數(shù)據(jù)。同時(shí)要注意取消內(nèi)存映射和關(guān)閉句柄,防止不必要的內(nèi)存泄漏。代碼390407行為停止捕捉圖像數(shù)據(jù)命令;代碼227241行為取消內(nèi)存映射和關(guān)閉句柄。

380 /**********************************************************************
381 * 函數(shù)名稱:V4l2StopDevice
382 * 功能描述:停止捕捉圖像數(shù)據(jù)
383 * 輸入?yún)?shù):ptVideoDevice
384 * 輸出參數(shù):無(wú)
385 * 返 回 值:無(wú)
386 * 修改日期             版本號(hào)        修改人           修改內(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.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 * 函數(shù)名稱:V4l2ExitDevice
219 * 功能描述:退出采集設(shè)備,取消幀緩沖映射和關(guān)閉句柄
220 * 輸入?yún)?shù):ptVideoDevice
221 * 輸出參數(shù):無(wú)
222 * 返 回 值:無(wú)
223 * 修改日期             版本號(hào)        修改人           修改內(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 }

審核編輯 黃昊宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11304

    瀏覽量

    209483
  • 攝像頭
    +關(guān)注

    關(guān)注

    60

    文章

    4841

    瀏覽量

    95691
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    《DNK210使用指南 -CanMV版 V1.0》第二十七 攝像頭圖像調(diào)整實(shí)驗(yàn)

    時(shí),配置攝像頭輸出畫面的對(duì)比度,對(duì)比度的值在2和-2之間切換3. 當(dāng)KEY1按鍵被按下時(shí),配置攝像頭輸出畫面的亮度,亮度的值在2和-
    發(fā)表于 10-24 09:48

    【飛凌嵌入式OK3576-C開(kāi)發(fā)板體驗(yàn)】 USB攝像頭拍照測(cè)試

    USB攝像頭拍照 將 USB 攝像頭插入開(kāi)發(fā)板,將自動(dòng)安裝 uvc 驅(qū)動(dòng) 使用命令查看usb攝像頭,已經(jīng)將USB攝像頭插入到板子上。命令:
    發(fā)表于 10-10 09:24

    《DNK210使用指南 -CanMV版 V1.0》第七章 基于CanMV的MicroPython語(yǔ)法開(kāi)發(fā)環(huán)境搭建

    第七章 基于CanMV的MicroPython語(yǔ)法開(kāi)發(fā)環(huán)境搭建 前面章節(jié)中已經(jīng)介紹了如何編譯CanMV固件,并將編譯好的CanMV固件燒錄至Kendryte K210,本章將介紹如何在Windows
    發(fā)表于 09-13 09:30

    百問(wèn)網(wǎng)全志系列開(kāi)發(fā)攝像頭V4L2編程步驟詳解

    7 攝像頭V4L2編程 7.1 V4L2簡(jiǎn)介 ?Video for Linux two(Video4Li
    發(fā)表于 08-13 09:45

    基于FPGA的攝像頭心率檢測(cè)裝置設(shè)計(jì)

    適用范圍 本系統(tǒng)適用于環(huán)境光線較為穩(wěn)定的環(huán)境,且需要使用者裸露部分皮膚。 1.4 主要技術(shù)特點(diǎn) 非接觸式心率監(jiān)測(cè),通過(guò)攝像頭實(shí)時(shí)圖像處理并得到準(zhǔn)確心率值。 2.系統(tǒng)組成及功能說(shuō)明 2.1 具體功能概述
    發(fā)表于 07-01 17:58

    esp32-s2-soala-v1.2如何獲取攝像頭描述符?

    開(kāi)發(fā)板:esp32-s2-soala-v1.2 開(kāi)發(fā)環(huán)境:WIN10 + IDF4.4 我想使用uvc攝像頭的例程,啥都沒(méi)改測(cè)試初始化是成功的,但是獲取圖片后就會(huì)卡死 應(yīng)該是
    發(fā)表于 06-27 06:48

    STM32MP135如何使用opencv-python或v4l2-ctl打開(kāi)攝像頭,并保存為圖片?

    如題,我在使用v4l2-ctl和opencv的時(shí)候,打開(kāi)攝像頭保存是可以,但是保存出來(lái)的圖片都很有問(wèn)題 比如 或者圖片上半部分是被壓扁的不知道什么色彩的圖片,下面全是灰色 我嘗試過(guò)用opencv
    發(fā)表于 05-30 06:16

    【米爾-芯馳D9360商顯板試用評(píng)測(cè)】使用ffmpeg實(shí)現(xiàn)遠(yuǎn)程視頻監(jiān)控

    (簡(jiǎn)稱V4L2),是linux中關(guān)于視頻設(shè)備的內(nèi)核驅(qū)動(dòng) -s 640*480為分辨率 -i /dev/video8為攝像頭 -f flv rtmp://192.168.3.180:10035/live為以
    發(fā)表于 04-11 13:07

    全志D1-H開(kāi)發(fā)板USB攝像頭拍照Demo

    庫(kù)如:FFmpeg和OpenCV對(duì)V4L2均支持。 本例就使用V4L2庫(kù)完成攝像頭對(duì)圖片的捕捉,并將其保存為一張圖片。 依照Tina SKD開(kāi)發(fā)架構(gòu),我們的代碼創(chuàng)建在prckage目錄
    發(fā)表于 03-04 10:48

    爆蘋果正在探索開(kāi)發(fā)攝像頭的AirPods

    據(jù)知名蘋果爆料記者馬克?古爾曼消息,蘋果正在探索開(kāi)發(fā)攝像頭的AirPods。
    的頭像 發(fā)表于 02-27 15:04 ?839次閱讀

    高清網(wǎng)絡(luò)攝像頭多媒體智能屏

    迪文全新推出的28系列智能屏,是面向屏幕實(shí)時(shí)顯示高清網(wǎng)絡(luò)攝像頭視頻畫面應(yīng)用的產(chǎn)品。該系列智能屏可通過(guò)以太網(wǎng)或WiFi連接攝像頭,可接收H.264編碼RTSP協(xié)議的攝像頭視頻,支持4
    的頭像 發(fā)表于 02-19 13:21 ?588次閱讀
    高清網(wǎng)絡(luò)<b class='flag-5'>攝像頭</b>多媒體智能屏

    使用Arduino IDE 2.0開(kāi)發(fā)ESP32攝像頭模塊

    可以看到esp32選項(xiàng)。 編譯并上傳攝像頭樣例 1. 將開(kāi)發(fā)板連接到電腦USB接口。若開(kāi)發(fā)板未集成USB串口,則需要通過(guò)一個(gè)USB串口適配器連接。 2. 點(diǎn)擊頂部工具欄的
    發(fā)表于 02-07 17:06

    雙核Cortex-A7 CPU、64位玄鐵C906 RISC-V CPU,OK113i-S開(kāi)發(fā)開(kāi)發(fā)體驗(yàn)

    HTTP長(zhǎng)連接,實(shí)現(xiàn)網(wǎng)頁(yè)視頻監(jiān)控。 功能實(shí)現(xiàn) 移植交叉編譯器arm-linux-gnueabi-gcc。 移植矢量字庫(kù)freetye。 初始化攝像頭,通過(guò)V4L2驅(qū)動(dòng)框架實(shí)現(xiàn)攝像頭
    發(fā)表于 01-16 10:07

    【飛凌OK113i-S開(kāi)發(fā)板試用】基于飛凌嵌入式OK113i網(wǎng)頁(yè)視頻監(jiān)控項(xiàng)目

    :arm-linux-gnueabi-gcc 7.3.1 USB攝像頭 OK113i開(kāi)發(fā)板實(shí)現(xiàn)功能:通過(guò)OK113i飛凌嵌入式開(kāi)發(fā)板,采用USB設(shè)備
    發(fā)表于 01-09 11:31
    主站蜘蛛池模板: 1024手机看片国产旧版你懂的 | 乱欲小说又粗又大| 四虎成人精品在永久在线观看 | 精品国产一二三区| 国模小丫大尺度啪啪人体| 欧美一级视频免费| 天天添天天射| 热re久久精品国产99热| 欧美一级二级三级视频| 天天干天天干天天干| 男人午夜| 黄色网址有那些| 国产精品久久久久久久久久影院 | 天天干夜夜拍| 男女交性视频免费| 久久狠色噜噜狠狠狠狠97| 黄色a网站| 婷婷色影院| 真实子伦视频不卡| 亚洲一区二区福利视频| 欧美黄色片免费看| 久久国产乱子伦精品免费午夜| 亚洲天堂亚洲天堂| www.男人| 天天综合网网欲色| 精品国产你懂的在线观看| 国产大片黄在线观看| 五月婷花| 五月天男人的天堂| 在线免费视频国产| brazzersvideosex欧美最| 亚洲一区毛片| 黄a级免费| 美国bj69video18| 美女视频很黄很a免费国产| 国产午夜三区视频在线| 色伊人久久| 人人干人人做| 天堂精品视频| 天天色天天爽| 亚洲成色在线综合网站|