V4L是 Video for Linux的縮寫,它是Linux 核心中關於視頻裝置的子系統,它為linux 下的視頻驅動提供了統一的介面,使得應用程式可以使用統一的API 函數操作不同的視頻裝置,極大地簡化了視頻系統的開發和維護。 由於早期的 V4L 有很多缺陷,Bill Dirks 等人對其進行了重新設計,並取名為Video for Linux 2(V4L2使用),最早出現於Linux2.5.x 版本。V4L2 相比於V4L 有更好的擴充性和靈活性,並且支援的硬體裝置更多。 因此在應用程式V4L編程實際是指v4l2,我們這個系列的以V4L2為主,但由於曆史的原因,V4L2一般相容V4L.所以很多程式可以用V4L介面.
1.V4L支援裝置
V4L2(video for linux) 可以支援多種裝置,它可以有以下幾種介面:
1. 視頻採集介面(video capture interface):這種應用的裝置可以是高頻頭或者網路攝影機.V4L2的最初設計就是應用於這種功能的.下面也是著重講解這種應用.
2. 視頻輸出介面(video output interface):可以驅動電腦的外圍視頻映像裝置--像可以輸齣電視訊號格式的裝置.
3. 直接傳輸視頻介面(video overlay interface):它的主要工作是把從視頻採集裝置採集過來的訊號直接輸出到輸出裝置之上,而不用經過系統的CPU.
4. 視頻間隔消隱訊號介面(VBI interface):它可以使應用可以訪問傳輸消隱期的視頻訊號.
5. 收音機介面(radio interface):可用來處理從AM或FM高頻頭裝置接收來的音頻流.
2.V4L處理基本流程
跟一般裝置處理一樣,大體上V4L處理有四個流程.
2.1 開啟V4L裝置結點
一般V4L裝置結點名是 /dev/videoN.如第一個V4L裝置是/dev/video0.
int fd = open("/dev/video0",O_RDWR |O_NONBLOCK);
2.2配置裝置/查詢裝置屬性
主要通過ioctl來操作,象V4L2 常見的的命令有
格式
int ioctl (int __fd, unsigned long int __request, .../*args*/) ;
__request是V4L2一些ioctl命令,常見如下.
VIDIOC_REQBUFS:分配記憶體 VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的資料緩衝轉換成物理地址 VIDIOC_QUERYCAP:查詢驅動功能 VIDIOC_ENUM_FMT:擷取當前驅動支援的視頻格式 VIDIOC_S_FMT:設定當前驅動的頻捕獲格式 VIDIOC_G_FMT:讀取當前驅動的頻捕獲格式 VIDIOC_TRY_FMT:驗證當前驅動的顯示格式 VIDIOC_CROPCAP:查詢驅動的修剪能力 VIDIOC_S_CROP:設定視頻訊號的邊框 VIDIOC_G_CROP:讀取視頻訊號的邊框 VIDIOC_QBUF:把資料從緩衝中讀取出來 VIDIOC_DQBUF:把資料放回緩衝隊列 VIDIOC_STREAMON:開始視頻顯示函數 VIDIOC_STREAMOFF:結束視頻顯示函數 VIDIOC_QUERYSTD:檢查當前視頻裝置支援的標準,例如PAL或NTSC。
完整的IOCTL命令參見http://v4l2spec.bytesex.org/spec/r7624.htm
2.3 處理V4L視頻資料
在V4L裝置中,有的裝置從硬體取出,送到應用程式處理,比如網路攝影機硬體取得視頻資料後,通過V4L介面把視頻資料發送應用程式, 比如顯示螢幕或儲存成為檔案.
有的裝置是從應用發往硬體處理,如電視介面.
在V4L介面,設定了三種應用程式與驅動的互動方式,分別是
直接讀取裝置檔案方式(read/write)、使用者指標方式(userptr)以及mmap 映射方式。
1)mmap方式,驅動將內部資料空間映射到應用程式空間上,雙方直接在這個空間進行資料交換,是效果最高的方法,這也是最常用的方式之一 2)
直接讀取裝置檔案方式 直接調用 read()、write()函數進行資料的讀入和輸出,該方法一般配合select()使用。
3)使用者指標方式 首先由應用程式申請一段緩衝區,然後將緩衝區傳給驅動,驅動將其作為緩衝區,從而實現了記憶體共用。這一方法用的較少.
2.4 關閉裝置 調用close();如果是記憶體映射方式,在關閉前還需要調用munmap解除映射.
3.V4L兩個版本區別
1.標頭檔不一樣 V4L使用#include <linux/videodevice.h>
V4L2使用 #include <linux/videodevice2.h>
2.IOCTL命令編號 ,V4L使用 VIDIOCXXXX的形式,而V4L2使用VIDIOC_XXXX 或 VIDIOC_G_XXXX形式.
如V4L中取裝置屬性命令是VIDIOCGCAP,而V4L2對應的是VIDIOC_QUERYCAP.
3.兩者資料結構不一樣,V4L以Video_為首碼,而V4L以v4l2_為首碼.如裝置屬性
V4l1--> struct video_capability video_cap
V4l2-->struct v4l2_capability
4.檢測V4L裝置版本 在V4L2中,規定必須實現 VIDIOC_QUERYCAP命令,而V4L1,規定必須實現VIDIOCGCAP,用這個方法可以判斷裝置的版本.參見如下代碼.
/* * Author: Andrew Huang <bluedrum@163.com> * detectd v4l2 device version * */#include <stdio.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <linux/videodev2.h>#include <linux/videodev.h>/* 0 -- 不是v4l裝置 1 -- v4l 裝置 2 -- v4l2 裝置*/int test_v4l_version(int fd){ int ret = 0; char dummy[256]; if (-1 != ioctl(fd,VIDIOC_QUERYCAP,dummy)) { ret = 2; } else if (-1 != ioctl(fd,VIDIOCGCAP,dummy)) { ret = 1; } return ret;}int main(int argc,char * argv[]){ char dev_name[64] = "/dev/video2"; int cam_fd =-1; if(argc>1) { strncpy(dev_name,argv[1],sizeof(dev_name)-1); } printf("open device %s\n",dev_name); cam_fd = open(dev_name,O_RDWR|O_NONBLOCK);if(cam_fd == -1) { printf("open failure \n"); return -1; } switch(test_v4l_version(cam_fd)) { case 0: printf("%s:fd %d isn't v4l deivce\n",dev_name,cam_fd); return -1; break; case 1: printf("\n### video4linux device info [%s] ###\n",dev_name); return -2; break; case 2: printf("\n### v4l2 device info [%s] ###\n",dev_name); break; } close(cam_fd); return 0; }
作者:
Andrew Huang bluedrum@163.com 轉自: http://blog.chinaunix.net/uid-20587912-id-405264.html PS: 錯誤:expected expression before ‘struct’ 解決:#include<sys/ioctl.h>