V4L是linux內(nèi)核中關(guān)于視頻設(shè)備的子系統(tǒng),為linux下的視頻驅(qū)動(dòng)提供了統(tǒng)一的接口,使應(yīng)用程序可以使用統(tǒng)一的API操作不同的視頻設(shè)備,簡(jiǎn)化視頻系統(tǒng)的開發(fā)與維護(hù)
V4L2相比與V4L有更好的擴(kuò)展性和靈活性
(一)V4L2支持設(shè)備:
V4L2可以支持多種設(shè)備,可以有以下幾種接口:
1)視頻采集接口
2)視頻輸出接口
3)直接傳輸視頻接口:將視頻采集設(shè)備上采集的信號(hào)直接輸出到視頻輸出設(shè)備上,不用經(jīng)過系統(tǒng)CPU
4)視頻間隔消隱信號(hào)接口(VBI Interface):使引用可以訪問傳輸消隱期的視頻信號(hào)
5)收音機(jī)接口:
?
(二)V4L2設(shè)備處理流程
打開V4L2設(shè)備節(jié)點(diǎn)
int fd = open( " /dev/video0 " ,O_RDWR |O_NONBLOCK);
配置設(shè)備/查詢?cè)O(shè)備屬性
int ioctl ( int fd, unsigned long int request, ... /* args */ ) ;
常見的request命令:
VIDIOC_REQBUFS:在內(nèi)核空間中分配幀緩沖區(qū)
struct v4l2_requestbuffers req; req.count = 4 ; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ioctl(fd,VIDIOC_REQBUFS, &req);
VIDIOC_QUERYBUF:將REQBUFS中分配的緩存轉(zhuǎn)換成物理地址,并將物理地址映射到用戶空間
for (n_buffers = 0 ; n_buffers < req.count; ++ n_buffers) { struct v4l2_buffer buf; memset( &buf, 0 , sizeof (buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; if (- 1 == ioctl(fd, VIDIOC_QUERYBUF, & buf)) { printf( " error in VIDIOC_QUERYBUF\n " ); return - 1 ; } buffers[n_buffers].length = buf.length; buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset); if (MAP_FAILED== buffers[n_buffers].start) return - 1 ; }
VIDIOC_QUERYCAP:查詢驅(qū)動(dòng)功能
struct v4l2_capability cap; if ( ioctl(fd,VIDIOC_QUERYCAP,&cap) == - 1 ) printf( " error\n " ); printf( " capability:\n " ); printf( " driver:%s\n " ,cap.driver); printf( " card:%s\n " ,cap.card); printf( " bus info:%s\n " ,cap.bus_info); printf( " version:%d\n " ,cap.version); printf( " capabilities:%x\n " ,cap.capabilities);
VIDIOC_ENUM_FMT:獲取當(dāng)前驅(qū)動(dòng)支持的視頻格式
struct v4l2_fmtdesc fmtdesc; fmtdesc.index = 0 ; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; printf( " fmtdesc:\n " ); while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != - 1 ) { printf( " \t%d.%s\n " ,fmtdesc.index+ 1 ,fmtdesc.description); fmtdesc.index ++ ; }
VIDIOC_G/S_FMT:讀取/設(shè)置當(dāng)前驅(qū)動(dòng)的視頻捕捉格式
struct v4l2_format format; memset( &format, 0 , sizeof ( struct v4l2_format)); format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( ioctl(fd, VIDIOC_G_FMT, &format) == - 1 ) { printf( " VIDIOC_G_FMT error\n " ); return - 1 ; } struct v4l2_pix_format pix_format; pix_format = format.fmt.pix; printf( " pix_format\n " ); printf( " width:%d\n " ,pix_format.width); printf( " height:%d\n " ,pix_format.height); printf( " bytesperline:%d\n " ,pix_format.bytesperline); printf( " sizeimage:%d\n " ,pix_format.sizeimage);
VIDIOC_TRY_FMT:驗(yàn)證當(dāng)前驅(qū)動(dòng)的顯示格式
VIDIOC_CROPCAP:查詢驅(qū)動(dòng)的修剪能力
VIDIOC_G/S_CROP:讀取/設(shè)置視頻信號(hào)的邊框
struct v4l2_cropcap cropcap; struct v4l2_crop crop; cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( 0 == ioctl(fd, VIDIOC_CROPCAP, & cropcap)) { crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; if (- 1 == ioctl(fd, VIDIOC_S_CROP, & crop)) { printf( " VIDIOC_S_CROP error\n " ); return - 1 ; } }
VIDIOC_QBUF:把緩存區(qū)放入緩存隊(duì)列
VIDIOC_DQBUF:把緩存去從緩存隊(duì)列中取出
unsigned int i; enum v4l2_buf_type type; for (i = 0 ; i< 4 ; ++ i) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; ioctl (fd,VIDIOC_QBUF, & buf); } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ioctl (fd,VIDIOC_STREAMON, & type); struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if ( ioctl (fd,VIDIOC_DQBUF, &buf)==- 1 ) { printf( " error in VIDIOC_DQBUF\n " ); return - 1 ; }
VIDIOC_STREAMON:開始視頻顯示函數(shù)
VIDIOC_STREAMOFF:結(jié)束視頻顯示函數(shù)
VIDIOC_QUERYSTD:檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),亞洲一般使用PAL制式攝像頭,歐洲一般使用NTSC攝像頭
v4l2_std_id std; int ret; do { ret = ioctl(fd,VIDIOC_QUERYSTD,& std); } while (- 1 ==ret && errno== EAGAIN); switch (std) { case V4L2_STD_NTSC: // case V4L2_STD_PAL: // }
處理v4l2視頻數(shù)據(jù)
v4l2設(shè)定了三種應(yīng)用程序與驅(qū)動(dòng)的交互方式:
1)直接讀取設(shè)備文件方式read/write
2)mmap映射方式
3)用戶指針方式
mmap方式:驅(qū)動(dòng)將內(nèi)部數(shù)據(jù)空間映射到應(yīng)用程序空間上,雙方直接在這個(gè)空間上進(jìn)行數(shù)據(jù)交換
用戶指針方式:首先由應(yīng)用程序申請(qǐng)一段緩沖區(qū),然后將緩沖區(qū)傳給驅(qū)動(dòng),驅(qū)動(dòng)將其作為緩沖區(qū),從而實(shí)現(xiàn)內(nèi)存共享
直接read/write:一般配合select使用,直接讀取設(shè)備文件的方式進(jìn)行I/O
關(guān)閉設(shè)備
調(diào)用close關(guān)閉文件描述符,如果進(jìn)行了內(nèi)存映射,關(guān)閉之前還需要munmap解除映射
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
