v4l2驅動編寫篇【轉】

轉自:http://blog.csdn.net/michaelcao1980/article/details/53008418html

大部分所需的信息都在這裏。做爲一個驅動做者,當挖掘頭文件的時候,你可能也得看看include/media/v4l2-dev.h,它定義了許多你未來要打交道的結構體。
一個視頻驅動極可能要有處理PCI總線,或USB總線的部分。這裏咱們不會花什麼時間還接觸這些東西。一般會有一個內部一I2C接口,咱們在這一系列的後續文章中會接觸到它。而後還有一個V4L2的子系統接口。這個子系統是圍繞video_device這個結構體創建的,它表明的是一個V4L2設備。講解進入這個結構體的一切,將會是這個系列中幾篇文章的主題。這裏咱們先有一個概覽。
video_device結構體的name字段是這一類設備的名字,它會出如今內核日誌和sysfs中出現。這個名字一般與驅動的名字相同。
所表示的設備有兩個字段來描述。第一個字段(type)彷佛是從V4L1的API中遺留下來的,它能夠下列四個值之一:node


  筆者最近有機會寫了一個攝像頭的驅動,是「One laptop per child」項目的中攝像頭專用的。這個驅動使用了爲此目的而設計的內核API:the Video4Linux2 API。在寫這個驅動的過程當中,筆者發現了一個驚人的問題:這個API的文檔工做作得並非很好,而用戶層的文檔則寫的,實際上,至關不錯。爲了補救如今的情況,LWN將在將來的內個月裏寫一系列文章,告訴你們如何寫V4L2接口的驅動。
V4L2有一段歷史了。大約在1998的秋天,它的光芒第一次出如今Bill Dirks的眼中。通過長足的發展,它於2002年11月,發佈2.5.46 時,融入了內核主幹之中。然而直到今天,仍有一部份內核驅不支持新的API,這種新舊API的轉換工做仍在進行。同時,V4L2 API也在發展,並在2.6.18版本中進行了一些重大的改變。支持V4L2的應用依舊相對較少。
V4L2在設計時,是要支持不少普遍的設備的,它們之中只有一部分在本質上是真正的視頻設備:
linux

•video capture interface (影像捕獲接口)從調諧器或是攝像頭上獲取視頻數據。對不少人來說,影像捕獲(video capture) 是V4L2的基本應用。因爲筆者在這方面的經驗是強項,這一系列文章也趨於強調捕獲API,但V4L2不止這些。
•video output interface (視頻輸出接口)容許應用使用PC的外設,讓其提供視頻圖像。有多是經過電視信號的形式。
•捕獲接口還有一個變體,存在於video overlay interface(視頻覆蓋接口) 之中。它的工做是方便視頻顯示設備直接從捕獲設備上獲取數據。視頻數據直接從捕獲設備傳到顯示設備,無需通過CPU。
•VBI interfaces (Vertical blanking interval interface,垂直消隱接口)提供垂直消隱期的數據接入。這個接口包括raw和sliced兩種接口,其分別在於硬件中處理的VBI數據量。(爲何要在消隱期間接入數據呢?看這裏 ) 
•radio interface (廣播接口) 用於從AM或FM調諧器中得到音頻數據。 
也可能出現其它種類的設備。V4L2 API中還有一些關於編譯碼和效果設備的stub,他們都用來轉換視頻數據流。然而這塊的東西還沒有完成肯定,更不說應用了。還有「teletext」 和」radio data system」的接口,他們目前在V4L1 API中實現。他們沒有移動到V4L2的API中來,並且目前也沒有這方面的計劃。
視頻驅動與其餘驅動不一樣之處,在於它的配置方式多種多樣。所以大部分V4L2驅動都有一些特定的代碼,好讓應用能夠知道給定的設備有什麼功能,並配置設備,使其定期望的方式工做。V4L2的API定義了幾十個回調函數,用來配置如調諧頻率、窗口和裁剪、幀速率、視頻壓縮、圖像參數(亮度、對比度…)、視頻標準、視頻格式等參數。這一系列文章的很大部分都要用來考察這些配置的過程。
而後,還有一個小任務,就是有效地在視頻頻率下進行I/O操做。V4L2定義了三種方法來在用戶空間和外設之間移動視頻數據,其中有些會比較複雜。視頻I/O和視頻緩衝層,將會分紅兩篇文章來寫,它們是用來處量一些共性的任務的。
隨後的文章每幾周發一篇,共會加入到下面的列表中。
android

 

v4l2驅動編寫篇二--註冊和打開 [複製連接] 
原文網址:http://lwn.net/Articles/204545/ 
  這篇文章是LWN寫V4L2接口的設備驅動系列文章的第二篇。沒看過介紹篇的,也許能夠從那篇開始看。這一期文章將關注Video for Linux驅動的整體結構和設備註冊過程。
開始以前,有必要提一點,那就是對於搞視頻驅動的人來講,有兩份資料是很是有價值的。
ios

•TheV4L2 API Specification . (V4L2 API說明)這份文檔涵蓋了用戶空間視角下的API,但在很大程度上,V4L2驅動直接實現的就是那些API。因此大部分結構體是相同的,並且V4L2調用的語義也表述很很明瞭。打印一份出來(能夠考慮去掉自由文本協議的文本內容,以保護樹木[前面是做者原文,節省紙張就是保護樹木嘛 ]),放在容易夠到的地方。
•內核代碼中的vivi驅動,即drivers/media/video/vivi.c.這是一個虛擬驅動。它能夠用來測試,卻不使用任何實際硬件。這樣,它就成一個教人如何寫V4L2驅動的很是好的實例。 
首先,每一個V4L2驅動都要包含一個必須的頭文件:
程序員

 

•VFL_TYPE_GRABBER 代表是一個圖像採集設備–包括攝像頭、調諧器,諸如此類。
•VFL_TYPE_VBI 表明的設備是從視頻消隱的時間段取得信息的設備。
•VFL_TYPE_RADIO 表明無線電設備。
•VFL_TYPE_VTX 表明視傳設備。 
若是你的設備支持上面提到的不僅一種功能,那就要爲每一個功能註冊一個V4L2設備。然而在V4L2中,註冊的每一個設備均可以用做它實際支持的各類模式(就是說,你要爲一個物理設備建立很少個設備節點,但你卻能夠調用任意一個設備節點,來實現這個物理設備支持的任意功能)。實質上的問題是,在V4L2中,你實際上只需一個設備,註冊多個V4l2設備只是爲了與V4l1兼容。 
第二個字段是type2,它以掩碼的形式對設備的功能提供了更詳盡的描述。它能夠包含如下值:web

/* These defines are V4L1 specific and should not be used with the V4L2 API!
   They will be removed from this header in the future. */
spring

•VID_TYPE_CAPTURE 它能夠捕獲視頻數據 
•VID_TYPE_TUNER 它能夠接收不一樣的頻率 
•VID_TYPE_TELETEXT 它能夠抓取字幕 
•VID_TYPE_OVERLAY 它能夠將視頻數據直接覆蓋到顯示設備的幀緩衝區 
•VID_TYPE_CHROMAKEY 一種特殊的覆蓋能力,覆蓋的僅是幀緩衝區中像素值爲某特定值的區域 
•VID_TYPE_CLIPPING 它能夠剪輯覆蓋數據 
•VID_TYPE_FRAMERAM 它使用幀緩衝區中的存儲器 
•VID_TYPE_SCALES 它能夠縮放視頻數據 
•VID_TYPE_MONOCHROME 這個是一個純灰度設備 
•VID_TYPE_SUBCAPTURE 它能夠捕獲圖像的子區域 
•VID_TYPE_MPEG_DECODER 它支持mpeg碼流解碼 
•VID_TYPE_MPEG_ENCODER 它支持編碼mpeg碼流 
•VID_TYPE_MJPEG_DECODER 它支持mjpeg解碼 
•VID_TYPE_MJPEG_ENCODER 它支持mjpeg編碼 
V4L2驅動還要初始化的一個字段是minor,它是你想要的子設備號。一般這個值都設爲-1,這樣會讓video4linux子系統在註冊時自動分配一個子設備號。 
在video_device結構體中,還有三組不一樣的函數指針集。第一組只包含一個函數,那就是 release(),若是驅動沒有release()函數,內核就會抱怨(筆者發現一個件有趣的事,就是這個抱怨涉及到冒犯一篇LWN文章的做者)。 release()函數很重要:因爲多種緣由,對video_device的引用能夠在最後一個應用關閉文件描述符後很長一段時間依然保持。它們甚至能夠在設備己經註銷後依然保持。所以,在release()函數調用前,釋放這個結構體是不安全的。因此這個函數一般要包含一個簡單的kfree()調用。 
video_device的 file_operations結構體包含都是常規的函數指針。視頻設備一般都包括open()和release()函數。注意:這裏所說的 release函數並不是上面所講到的同名的release()函數,這個release() 函數只要設備關閉就要調用。一般都還要有read()和write()函數,這取決於設備的功能是輸入仍是輸出。然而咱們要注意的是,對於視頻流設備而言,傳輸數據還有別的方法。多數處理視頻流數據的設備還須要實現poll()和mmap();並且每一個V4L2設備都要有ioctl()函數,可是也能夠使用V4L2子系統的video_ioctl2(); 
第三組函數存在於video_device結構體自己裏面,它們是V4L2 API的核心。這組函數有幾十個,處理不一樣的設備配置操做、流輸入輸出和其餘操做。 
最後,從一開始就要知道的一個字段就是debug.能夠把它設成是V4L2_DEBUG_IOCTL或V4L2_DEBUG_IOCTL_ARG(或是兩個都設,這是個掩碼),能夠生成不少的調試信息,它們能夠幫助一個迷糊的程序員找到毛病,知道爲何驅動和應用誰也不知道對方在說什麼。
視頻設備註冊 
一旦video_device己經配置好,就能夠下面的函數註冊了:shell

1
 int video_register_device(struct video_device *vfd, int type, int nr);編程


這裏vfd是設備的結構體(video_device),type的值與它的type字段值相同,nr也是同樣,想要的子設備號(爲-1則註冊時自動分配)。返回值當爲0,若返加的是負的
出錯碼,則代表出錯了,和一般同樣,咱們要知道,設備一旦註冊,它的函數可能就會當即調用,因此不到一切準備就緒,不要調用video_register_device();
設備的註銷方法爲:

1
 void video_unregister_device(struct video_device *vfd);


請繼續關注本系列的下篇文章,咱們將會看看這些函數的具體實現。
open() 和 release()每一個V4L2設備都須要open()函數,其原型也與常規的相同。
int (*open)(struct inode *inode, struct file *filp);

open()函數要作的第一件事是經過給定的inode找到內部設備,這是經過找到inode中存存儲的子設備號來完成的。這裏還能夠實現必定數量的初始化,若是有關閉電源選項的話,這個時間剛好能夠用來開啓硬件電源。
V4L2規範還定義了一些相關的慣例。其一是:根據其設計,文件描述符能夠在給定的任什麼時候間重複打開。這樣設定的目的是當一個應用在顯示(或是產生)視頻信號時,另外一個應用能夠改變控制值。因此,雖然某些操做是獨佔性質的(特別是數據讀、寫等),可是設備整體自己是要支持描述符複用的。
另外一個值得一提的慣例是:open()函數,整體上講,不能夠改變硬件中現行的操做參數。有些時候可能會有這樣的狀況:經過命令行程序,根據一組參數(分辨率,視頻格式等)來改變攝像頭配置,而後運行一個徹底不一樣的程序來,好比說,從攝像頭獲取上幀圖像。若是攝像頭在設置在中途改變了,這種模式就很差用。因此除非應用明確表示要改變設置(這種狀況固然不包括在open函數中)V4L2驅動要儘可能保持設定不變。
release()函數作一些必要清理工做。由於文件描述符能夠重複打開,因此release函數中減少引用計數,並在完全退出以前作檢查。若是關閉的文件描述符是用來傳輸數據的,release函數極可能要關掉DMA,並作一些其餘的清理工做。
本系列的下一篇文章咱們將進入查詢設備功能和設定系統模式的冗長過程之中,請續斷關注。

 

v4l2驅動編寫篇三--基本I/O處理 
若是有人在video for linux API規範上花了我時間的話,他確定已經注意到了一個問題,那就是V4L2大量使用了ioctl接口。視頻硬件有大量的可操做旋鈕,可能比其它任何處設都要多。視頻流要與許多參數相聯繫,並且有很大一部分處理要經過硬件進行。不使用硬件有良好支持模式可能致使表現很差,甚至根本沒有表現。因此咱們不得不揭露硬件的許多特性,而對最終應用表現得怪異一點。
傳統上來說,視頻驅動中包含的ioctl()函數通常會長得像一部小說,而函數所獲得的結論也每每比小說更使人滿意來,他們每每在中間拖了不少(這句話徹底不明白什麼意思)。因此V4L2的API在2.6.18版本的內核開始作出了改變。冗長的ioctl函數被替換成了一個大的回調函數的集合,每一個回調函數實現本身的ioctl函數。實際上,在2.6.19-rc3中,有79個這樣的回調函數。而幸運的是,多數驅動並不需實現全部的回調函數,甚至都不是大部分回調函數。
在ioctl()函數中發生的事情都放到了drivers/media/video/videodev.c裏面。這部分代碼處理數據在內核和用戶空間之間的傳輸並把ioctl調用發送給驅動。要使用它的話,只要把video_device中的video_ioctl2()作爲ioctl()來調用就好了。實際上,多數驅動也要把它當成unlocked_ioctl()來用。Video4Linux2層的鎖能夠對其進行處理,並且驅動也應該在合適的地方加鎖。(這一段沒看明白,亂寫的)

 

你的驅動第一個可能要實現的回調函數是:

int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);

這個函數處理VIDIOC_QUERYCAP ioctl(), 只是簡單問問「你是誰?你能幹什麼?」實現它是V4L2驅動的責任。在這個函數中,和全部其餘V4L2回調函數同樣, 參數priv是file->private_data域的內容;一般的實現是在open()的時候把它指向驅動中表示設備的內部結構。

 

驅動應該負責填充cap結構而且返回「0或者負的錯誤碼」值。若是成功返回,則V4L2層會負責把回覆拷貝到用戶空間。

 

v4l2_capability結構(定義在<linux/videodev2.h>中)是這樣的:

struct v4l2_capability {
 __u8 driver[16]; /* i.e. "bttv" */
 __u8 card[32]; /* i.e. "Hauppauge WinTV" */
 __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
 __u32   version;        /* should use KERNEL_VERSION() */
 __u32 capabilities; /* Device capabilities */
 __u32 reserved[4];
};

 

其中driver域應該被填充設備驅動的名字,card域應該被填充這個設備的硬件描述信息。並非全部的驅動都消耗精力去處理bus_info域,這些驅動一般使用下面這個方法:

   springf(cap->bus_info, "PCI:%s", pci_name(&my_dev));

 

version域用來保存驅動的版本號。capabilities域是一個位掩碼用來描述驅動能作的不一樣的事情:

/* Values for 'capabilities' field */
#define V4L2_CAP_VIDEO_CAPTURE  0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT  0x00000002  /* Is a video output device */
#define V4L2_CAP_VIDEO_OVERLAY  0x00000004  /* Can do video overlay */
#define V4L2_CAP_VBI_CAPTURE  0x00000010  /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT  0x00000020  /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040  /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080  /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE  0x00000100  /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200  /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK  0x00000400  /* Can do hardware frequency seek  */

#define V4L2_CAP_TUNER   0x00010000  /* has a tuner */
#define V4L2_CAP_AUDIO   0x00020000  /* has audio support */
#define V4L2_CAP_RADIO   0x00040000  /* is a radio device */

#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */

 

最後一個域(reserved)是保留的。V4L2規則要求要求reserved被置爲0, 可是, 由於video_ioctl2()設置了整個的結構體爲0,因此這個就不用咱們操心了。

 

能夠在「vivi」這個驅動中找到一個典型的應用:

static int vidioc_querycap(struct file *file, void  *priv,
     struct v4l2_capability *cap)
{
 struct vivi_fh  *fh  = priv;
 struct vivi_dev *dev = fh->dev;

 strcpy(cap->driver, "vivi");
 strcpy(cap->card, "vivi");
 strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
 cap->version = VIVI_VERSION;
 cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
    V4L2_CAP_STREAMING     |
    V4L2_CAP_READWRITE;
 return 0;
}

考慮到這個回調函數的出現,咱們但願應用程序使用它,避免要求設備完成它們不可能完成的功能。然而,在你編程的有限經驗中,應用程序不會花太多的精力來關注VIDIOC_QUERYCAP調用。

 

另外一個可選而又不常被是實現的回調函數是:

     int (*vidioc_log_status)       (struct file *file, void *fh);

這個函數用來實現VIDIOC_LOG_STATUS調用,做爲視頻應用程序編寫者的調試助手。當調用時,它應該打印藐視驅動及其硬件的當前狀態信息。這個信息應該足夠充分以便幫助迷糊的應用程序開發者弄明白爲何視頻顯示一片空白。

 

 

下一部分開始剩下的77個回調函數。特別的,咱們要開始看看與硬件協商一組操做模式的過程。

 

v4l2驅動編寫篇第四--輸入輸出


輸入和輸出
這是不按期發佈的關於寫視頻驅動程序的LWN系統文章的第四篇.沒有看過介紹篇的,也許想從這裏開始.本週的文章介紹的是應用程序如何肯定在特定適配器上哪些輸入和輸出可用,而且它們之間作出選擇。

在不少狀況下,視頻適配器並不能提供不少的輸入輸出選項.好比說攝像頭控制器,可能只是提供攝像頭,而沒什麼別的功能.然而,在一些其餘的狀況下,事情將變得很複雜.一個電視卡可能對應板上不用的接頭有不一樣的輸入.他甚至可能有能夠獨立發揮其功能的多路調諧器.有時,那些輸入會有不一樣的特性;有些調諧器能夠支持比其餘的更普遍的視頻標準.對於輸出來講,也有一樣的問題.

很明顯,若想一個應用能夠有效地利用視頻適配器,它必須有能力找到可用的輸入和輸出,並且他必須能找到他想操做的那一個.爲此,Video4Linux2 API提供三種不一樣的ioctl()調用來處理輸入,相應地有三個來處理輸出.

這三個(對於硬件支持的每個功能)驅動都要支持.雖然如此,對於簡單的硬件而言,代碼仍是很簡單的.驅動也要提供一此啓動時的默認值.然而,驅動不該該作的是,在應用退出時重置輸入輸出信息.對於其餘視頻參數,在屢次打開之間,參數應維持不變.

 

視頻標準

  在咱們進入輸入輸出的細節以前,咱們應該先了解一下視頻標準 .這些標準描述的是視頻爲進行傳輸而作出的格式轉換–分辨率,幀頻率等.這些標準一般是由每個國家的監管部門制定的。如今世界上使標準主要的有三個:NTSC(主要是北美使用),PAL(主要是歐洲,非洲和中國),和SECAM(法,俄和非洲部分地區).然而這在標準在國家之間都有變化,並且有些設備比其餘設備能更加靈活,能與更多的標準變種協同工做.

V4L2使用v4l2_std_id來表明視頻標準,它是一個64位的掩碼。每一個標準變種在掩碼中就是一位。因此標準NTSC就是V4L2_STD_NTSC_M, 值爲0x1000,而日本的變種就是V4L2_STD_NTSC_M_JP
(0x2000)。若是一個設備能夠處理因此有NTSC變種,它就能夠設爲V4L2_STD_NTSC,它能夠全部相關位置位。對PAL和SECAM標準,也存在一組相似的位集。Seethis page for a complete list.

對於用戶空間而言,V4L2提供一個ioctl()命令(VIDIOC_ENUMSTD),它容許應用查詢設備實現了哪些標準。驅動卻無需直接回答查詢,而是將video_device結構體的tvnorm字段設置爲它所支持的全部標準。而後V4L2層會嚮應用輸出所支持的標準。VIDIOC_G_STD命令,能夠用來查詢如今哪一種標準是激活的,它也是在V4L2層經過返回video_device結構的current_norm字段來處理的,驅動程序應在啓動時,初始化current_norm來反應現實狀況。有些應用即便他並無設置過標準,發現標準沒有設置也會感到困惑。

當某個應用想要申請某個標準的時候,會發出一個VIDIOC_S_STD調用,該調用經過下面的函數傳到驅動:

  int (*vidioc_s_std) (struct file *file, void *private_data,                         v4l2_std_id std);


驅動要對硬件編程,以使用給定的標準,並返回0(或是負的出錯編碼).V4L2層須要把current_norm設爲新的值。

應用可能想要知道硬件所看到的是何種信號,答案能夠經過VIDIOC_QUERYSTD找到,它到了驅動裏面就是:

    int (*vidioc_querystd) (struct file *file, void *private_data,                            v4l2_std_id *std);


驅動要儘量地在這個字段填寫詳細信息。若是硬件沒有提供足夠的信息,std字段就會暗示任何可能出現的標準。

這裏還有一點值得一提:因此的視頻設備必須支持(或是聲明支持)至少一種視頻標準。視頻標準對於攝像頭來講沒什麼意義,它不與任何監管制度綁定。可是也不存一個標準說「我是個攝像頭,我什麼都能作」,因此V4L2層有不少攝像頭聲明能夠返回PAL或NTSC數據(實際只是如些聲明而己)。

 

輸入

  視頻捕獲的應用首先要經過VIDIOC_ENUMINPUT
命令來枚舉全部可用的輸入。在V4L2層,這個調用會轉換成調用一個驅動中對應的回調函數:

  int (*vidioc_enum_input)(struct file *file, void *private_data,                             struct v4l2_input *input);


在這個調用中,file 對就的是打開的視頻設備。private_data是驅動的私有字段,input字段是真正的傳遞的信息,它有以下幾個值得關注的字段:

•__u32 index:應用關注的輸入的索引號; 這是唯一一個用戶空間設定的字段. 驅動要分配索引號給輸入,從0開始,依次往上增長.想要知道因此可用的輸入的應用會調用VIDIOC_ENUMINPUT
調用過索引號從0開始,並開始遞增。 一旦返回EINVAL,應用就知道,輸入己經用光了.只要有輸入,輸入索引號0就必定要存在的.
•__u8 name[32]: 輸入的名字,由驅動設定.簡單起見,能夠設爲」Camera」,諸如此類;若是卡上有多個輸入,名稱就要與接口的打印相符合.
•__u32 type:輸入的類型,如今只有兩個值可選:V4L2_INPUT_TYPE_TUNER
和V4L2_INPUT_TYPE_CAMERA.
•__u32 audioset:描述哪一個音頻輸入能夠與些視頻輸入相關聯. 音頻輸入與視頻輸入同樣經過索引號枚舉 (咱們會在另外一篇文章中關注音頻),但並不是因此的音頻與視頻的組合都是可用的.這個字段是一個掩碼,表明對於當前枚舉出的視頻而言,哪些音頻輸入是能夠與之關聯的.若是沒有音頻輸入能夠與之關聯,或是隻有一個可選,那麼就能夠簡單地把這個字段置0.
•__u32 tuner: 若是輸入是一個調諧器 (type
字段置爲V4L2_INPUT_TYPE_TUNER), 這個字段就是會包含一個相應的調諧設備的索引號.枚舉和調諧器的控制也將在將來的文章中講述.
•v4l2_std_id std: 描述設備支持哪一個或哪些視頻標準.
•__u32 status: 給出輸入的狀態. 全整的標識符集合能夠在V4L2的文檔中找到(即這裏 
);簡而言之,status
中設置的每一位都表明一個問題. 這些問題包括沒有電源,沒有信號,沒有同頻鎖,或 the presence of Macrovision(這個是什麼意思?沒查到)或是其餘一些不幸的問題.
•__u32 reserved[4]:保留字段,驅動應該將其置0. 
一般驅動會設置上面因此的字段,並返回0。若是索引值超出支持的輸入範圍,應該返回-EINVAL.這個調用裏可能出現的錯誤很少。

當應用想改變現行的輸入時,驅動會收到一個對回調函數vidioc_s_input()的調用。

int (*vidioc_s_input) (struct file *file, void *private_data,                           unsigned int index);


index的值與上面講到的意義相同 – 它相來肯定哪一個輸入是相要的.驅動要對硬件編輯,選擇那個輸入並返回0.也有可能要返回-EINVAL
(索引號不正確時) 或-EIO
(硬件有問題). 即便只有一路輸入,驅動也要實現這個回調函數.

還有另外一個回調函數,指示哪個輸入是激活狀態的:

  int (*vidioc_g_input) (struct file *file, void *private_data,                           unsigned int *index);

 


這裏驅動把index值設爲對應的輸入的索引號.

 

 

輸出

  枚舉和選擇輸出的過程與輸入的是十分類似的。因此這裏的描述就從簡。輸入枚舉的回調函數是這樣的:

  int (*vidioc_enumoutput) (struct file *file, void *private_data                                  struct v4l2_output *output);


v4l2_output
結構的字段是:

•__u32 index:相關輸入的索引號.其工做方式與輸入的索引號相同:它從0開始遞增。
•__u8 name[32]: 輸出的名字.
•__u32 type: 輸入的類型.支持的輸出類型爲:V4L2_OUTPUT_TYPE_MODULATOR
用於模擬電視調製器,V4L2_OUTPUT_TYPE_ANALOG
用於基本模擬視頻輸出,和V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY
用於模擬VGA覆蓋設備.
•__u32 audioset: 能與這個視頻協同工做的音頻集.
•__u32 modulator: 與此設備相關的調製器 (對於類型爲V4L2_OUTPUT_TYPE_MODULATOR 的設備而言).
•v4l2_std_id std:輸出所支持的視頻標準.
•__u32 reserved[4]: 保留字段,要設置爲0. 
也有用於得到和設定現行輸入設置的回調函數;他們與輸入的具體對稱性:

    int (*vidioc_g_output) (struct file *file, void *private_data,    unsigned int *index);   

    int (*vidioc_s_output) (struct file *file, void *private_data,     unsigned int index);


即使只有一個輸出,設備驅動也要定義全部上述的三個回調函數.

有了這些函數以後,V4L2應用就能夠知道有哪些輸入和輸入,並在它們間進行選擇.然而選譯這些輸入輸出中所傳輸的是什麼視頻數據流則是一件更加複雜的事情.本系列的下一期文章,咱們將關注視頻數據流的格式,以及如何與用戶空間協定數據格式.

 

v4l2文檔第五A--顏色與格式 
  顏色與格式這是不按期發佈的關於寫視頻驅動程序的LWN系統文章的第五篇.沒有看過介紹篇的,也許想從這裏 開始.

 


  應用在能夠使視頻設備工做以前,它必須與驅動達成了解,知道視頻數據是何種格式的。這種協商將是一個很是複雜的過程,其緣由有二:一、視頻硬件所支持的視頻格互不相同。二、在內核的格式轉換是使人難以接受的。因此應用在找出一種硬件支持的格式,並作出一種你們均可以接受的配置。這篇文章將會講述格式的基本描述方式;下期文章則會講述V4L2驅動與應用協商格式時所實現的API。


色域 
  色域在廣義上來說,就是系統在描述色彩時所使用的座標。V4L2規範中定義了好幾個,但只有兩個使用最爲普遍。它們是:

   •V4L2_COLORSPACE_SRGB.多數開發者所熟悉的[red,green,blue]數組包含在這個色域之中。它爲每一種顏色提供了一個簡單的強度值,把它們混合在一塊兒,從而產生了一種普遍的顏色的效果。表示RGB值的方法有不少,咱們在下面將會有所介紹。 
這個色域也包含YUV和YCbCr的表示方法,這個表示方法最先是爲了早期的彩色電視信號能夠在黑白電視中的播放,因此Y(或說亮度)值只是一個簡單的亮度值,單獨播放時能夠產生灰度圖像。U和V(或Cb和Cr)色度值描述的是色彩中藍色和紅色的份量。綠色能夠經過從亮度中減去這些份量而獲得。YUV和RGB之間的轉換並不簡單,可是咱們有一些成形的公式 可選。
注意:YUV和YCbCr並不是完成同樣,雖然有時他們的名字會替代使用。

   •V4L2_COLORSPACE_SMPTE170M 這個是NTSC或PAL等電視信號的模擬色彩表示方法,電視調諧器一般產生的色域都屬於這個色域。 
還存在不少其餘的色域,他們多數都是電視相關標準的變種。點擊查看V4L2規範 中的詳細列表。

 

密集存儲和平面存儲

  如汝所見,像素值是以數組的方式表示的,一般由RGB或YUV值組成。要把這數組組織成圖像,一般有兩種經常使用的方法。

  •Packed 格式把一個像素的全部值存領教在一塊兒.
  •Planar 格式把每個份量單獨存儲成一個陣列,這樣在YUV格式中,全部Y值都連續地一塊兒存儲在一個陣列中,U值存儲在另外一箇中,V值存在第三個中.這些平面經常都存儲在一個緩衝區中,但並不必定非要這樣. 
緊密型存儲方式可能使用更爲普遍,特別是RGB格式,但這兩種存儲方式均可以由硬件產生並由應用程序請求。若是設備能夠產生緊密型和平面型兩種,那麼驅動就要讓兩種都在用戶空間可見。


四字符碼(four Charactor Code:FourCC)

V4L2 API中表示色彩格式採用的是廣受好評的四字符碼(fourcc)機制。這些編碼都是32位的值,由四個ASCII碼產生。如此一來,它就有一個優勢就是,易於傳遞,對人可讀。當一個色彩格式讀做,例如,」RGB4″就沒有必要去查表了。
  注意:四字符碼在不少不一樣的設定中都會使用,有些仍是早於linux的.Mplayer中內部使用它們,然而,fourcc只是說明一種編碼機制,並不說明使用何種編碼。Mplayer有一個轉換函數,用於在它本身的fourcc碼和v4l2用的fourcc碼之間作出轉換。

RGB格式

在下面的格式描述中,字節是按存儲順序列出的。在小端模式下,LSByte在前面。每字節的LSbit在右側。每種色域中,輕陰影位是最高有效的。

 

Name

fourcc

Byte 0

Byte 1

Byte 2

Byte 3

V4L2_PIX_FORMAT_RGB332

RGB1

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB444

R444

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB555

RGB0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB565

RGBP

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB555X

RGBQ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB565X

RGBR

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_BGR24

BGR3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB24

RGB3

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_BGR32

BGR4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_RGB32

RGB4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V4L2_PIX_FORMAT_SBGGR8

BA81

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

當使用有空位(上圖中灰色部分)的格式 , 應用能夠使用空位做alpha(透明度)值.
上面的最後一個格式是」Bayer(人名好像是)」格式,此格式與多數攝像機感光器所獲得的真實數據很是接近。每一個像素都有綠色份量,但藍和紅只是隔一個像素纔有份量。本質上講,綠色帶有更重的強度信息,而藍紅色則在丟失時以相隔像素的內插值替換。這種模式咱們交在YUV格式中再次見到。

YUV格式

YUV的緊密型模式在下面首先展現,看錶的關鍵處以下:

  •  

 

 

 

 

 

 

 

 

  • = Y (intensity) 
  •  

 

 

 

 

 

 

 

 

  • = U (Cb) 
  •  

 

 

 

 

 

 

 

 

  • = V (Cr)

 

 

 

也有幾中平面型的YUV格式在用,但把它們全畫出來並無什麼大的幫助,因此咱們只是在下面舉一下例子,經常使用」YUV 4:2:2″(V4L2_PIX_FMT_YUV422, fourcc422P)格式使用三組陣列,一幅4X4的圖片將以下表示:

Y plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

U plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

V plane:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

對於Bayer格式, YUV 4:2:2 每隔一個Y值有一個U和一個V值,展現圖像須要之內插值替換的丟失的值。其餘的平面YUV格式有:


•V4L2_PIX_FMT_YUV420: YUV 4:2:0格式,每四個Y值纔有一個U值一個V值.U和V都要在水平和垂直兩個方向上都之內插值替換.平面是以Y-U-V的順序存儲的,與上面的例子一致. 
•V4L2_PIX_FMT_YVU420: 與YUV 4:2:0格式相似,只是U,V值調換了位置. 
•V4L2_PIX_FMT_YUV410: 每16個Y值纔有一個U值和V值.陣列的順序是 Y-U-V. 
•V4L2_PIX_FMT_YVU410: 每16個Y 值纔有一個U值和V值.陣列的順序是Y-V-U.

 

其餘格式

還有一些可能對驅動有用的格式以下:

•V4L2_PIX_FMT_JPEG: 一種定義模糊的JPEG流;更多信息請看這裏 . 
•V4L2_PIX_FMT_MPEG: MPEG流.還有一些MPEG流格式的變種;將來的文章中將討論流的控制.

[p=21, null, left]還有一些其餘的混雜的格式,其中一些仍是有傳利保護的;這裏 有一張列表.
格式的描述[p=21, null, left]如今咱們己經瞭解了顏色的格式,下面將要看看下V4L2 API中是如何描述圖像格式的了。這裏的主要的結構體是struct v4l2_pix_format (定義於<linux/videodev2.h>),它包含以下字段:


•__u32 width: 圖片寬度,以像素爲單位. 
•__u32 height:圖片高度,以像素爲單位. 
•__u32 pixelformat: 描述圖片格式的四字符碼. 
•enum v4l2_field field:不少圖片的源會使數據交錯 -先傳輸奇數行,而後是偶到行.真正的攝像頭設備是不會作數據的交錯的。 V4L2 API 容許應用使用不少種方式交錯字段.經常使用的值爲V4L2_FIELD_NONE (字段不交錯),V4l2_FIELD_TOP (只交錯頂部字面),或V4L2_FIELD_ANY (無所謂). 詳情見這裏 . 
•__u32 bytesperline: 相臨掃描行之間的字節數.這包括各類設備可能會加入的填充字節.對於平面格式,這個值描述的是最大的 (Y) 平面. 
•__u32 sizeimage: 存儲圖片所需的緩衝區的大小. 
•enum v4l2_colorspace colorspace: 使用的色域. 
加到一塊兒,這些參數以合理而完整的方式描述了視頻數據緩衝區。應用能夠填充 v4l2_pix_format 請求用戶空間開發者所能想到的幾乎任何格式. 然而,在驅動層面上,驅動開發者則要限制在硬件所能支持的格式上. 因此每個 V4L2 應用都必須經歷一個與驅動協定的過程,以便於使用一個硬件支持而且能滿足應用須要的圖像格式。下一期文章,咱們將從驅動角度,描述這種協定是什麼進行的。

 

v4l2驅動編寫篇第五B--格式的協定

 
 這是不按期發佈的關於寫視頻驅動程序的LWN系統文章的一篇續篇.介紹篇 包含了對整個系統的描述,而且包含對本篇的上一篇的連接,在上一集,咱們關注了V4L2 API是如何描述視頻格式的:圖片的大小,和像素在其內部的表示方式。這篇文章將完成對這個問題的討論,它將描述如就硬件所支持的實際視頻格與應用達到協議。
如咱們在上一篇中所見,在存儲器中表示圖像有不少種方法。市場幾乎找不到能夠處理全部V4L2所理解的視頻格式的設備。驅動不該支持底層硬件不懂的視頻格式。實際上在內核中進行格式的轉換是使人難以接受的。因此驅動必須能夠應用選擇一個硬件能夠支持的格式。
第一步就是簡單的容許應用查詢所支持的格式。VIDIOC_ENUM_FMT ioctl()就是爲此目的而提供的。在驅動內部這個調用會轉化爲這樣一個回調函數(若是查詢的是視頻捕獲設備)。
  int (*vidioc_enum_fmt_cap)(struct file *file, void *private_data,
                               struct v4l2_fmtdesc *f);

這個回返調函數要求視頻捕獲設備描述其支持的格式。應用會傳遞一個v4l2_fmtdesc 結構體:
   struct v4l2_fmtdesc
    {
        __u32                    index;
        enum v4l2_buf_type  type;
        __u32               flags;
        __u8                    description[32];
        __u32                    pixelformat;
        __u32                    reserved[4];
    };

應用會設置index 和type 字段.index 是用來肯定格式的一個簡單的整型 數;與其餘V4L2所使用的索引(indexes)同樣,這個也是從0開始遞增,至最大容許的值爲止.應用能夠經過一直遞增索引值(index)直到返回 EINVAL的方式枚舉全部支持的格式.type 字段描述的是數據流類型 ;對於視頻捕獲設備來講(攝像頭或調諧器就是V4L2_BUF_TYPE_VIDEO_CAPTURE.
若是index 對就某個支持的格式,驅動應該填寫結構體的其餘字段.pixelformat字段應該是描述視頻表現方式的fourcc編碼,而 description 是對這個格式的一種簡短的字符串描述.flags 字段只定義了一個值即V4L2_FMT_FLAG_COMPRESSED,它表示是一個壓縮了的視頻格式.
上述的函數是視頻捕獲函數;只有當type 字段的是值是V4L2_BUF_TYPE_VIDEO_CAPTURE時纔會調用. VIDIOC_ENUM_FMT 調用將根據type字段的值的不一樣解釋成不一樣的回調函數。
    /* V4L2_BUF_TYPE_VIDEO_OUTPUT */ 
    int (*vidioc_enum_fmt_video_output)(file, private_date, f);

    /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ 
    int (*vidioc_enum_fmt_overlay)(file, private_date, f);

    /* V4L2_BUF_TYPE_VBI_CAPTURE */ 
    int (*vidioc_enum_fmt_vbi)(file, private_date, f);

    /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ */
    int (*vidioc_enum_fmt_vbi_capture)(file, private_date, f);

    /* V4L2_BUF_TYPE_VBI_OUTPUT */ 
    /* V4L2_BUF_TYPE_SLICED_VBI_OUTPUT */ 
    int (*vidioc_enum_fmt_vbi_output)(file, private_date, f);

    /* V4L2_BUF_TYPE_VIDEO_PRIVATE */ 
    int (*vidioc_enum_fmt_type_private)(file, private_date, f);

參數相似對於因此的調用都是同樣. 對於以V4L2_BUF_TYPE_PRIVATE開頭的解碼器,驅動能夠支持特殊的緩衝類型是沒有意義的。, 但在應用端卻須要一個清楚的認識. 對這篇文章的目的而言,咱們更加關心的是視頻捕獲和輸出設備; 其餘的視頻設備咱們會在未來某期的文章中講述.
應用能夠經過調用VIDIOC_G_FMT知道硬件如今的配置如何。這種狀況下傳遞的參數是一個v4l2_format 結構體:
    struct v4l2_format
    {
        enum v4l2_buf_type type;
        union
        {
                struct v4l2_pix_format                pix;
                struct v4l2_window                win;
                struct v4l2_vbi_format                vbi;
                struct v4l2_sliced_vbi_format        sliced;
                __u8        raw_data[200];
        } fmt;
    };

一樣,type 描述的是緩衝區類型;V4L2層會根據type的不一樣,將調用解釋成不一樣的驅動的回調函數. 對於視頻捕獲設備而言,這個回調函數就是:
  int (*vidioc_g_fmt_cap)(struct file *file, void *private_data,
                                struct v4l2_format *f);

對於視頻捕獲(和輸出)設備, 聯合體中pix 字段是咱們關注的重點. 這是咱們在上一期中見過的v4l2_pix_format 結構體;驅動應該用如今的硬件設置填充那個結構體而且返回。這個調用一般不會失敗,除非是硬件出現了很是嚴重的問題。
其餘的回調函數還有:
    int (*vidioc_s_fmt_overlay)(file, private_data, f);
    int (*vidioc_s_fmt_video_output)(file, private_data, f);
    int (*vidioc_s_fmt_vbi)(file, private_data, f);
    int (*vidioc_s_fmt_vbi_output)(file, private_data, f);
    int (*vidioc_s_fmt_vbi_capture)(file, private_data, f);
    int (*vidioc_s_fmt_type_private)(file, private_data, f);

vidioc_s_fmt_video_output()與捕獲接口同樣使用相同的方式使用同一個pix字段。
多數應用都想最終對硬件進行配置以使其爲應用提供一種符合其目的的格式。改變視頻格有兩個接口。第一個是VIDIOC_TRY_FMT 調用,它在V4L2驅動中轉化爲下面的回調函數:
    int (*vidioc_try_fmt_cap)(struct file *file, void *private_data,
                              struct v4l2_format *f);
    int (*vidioc_try_fmt_video_output)(struct file *file, void *private_data,
                                             struct v4l2_format *f);
    /* And so on for the other buffer types */

要處理這個調用,驅動會查看請求的視頻格式,而後判定硬件是否支持這個格式。若是應用請求的格式是不能支持的,就會返回-EINVAL.因此,例如,一個描述了一個不支持格的fourcc編碼或者請求了一個隔行掃描的視頻,而設備只支持逐行掃描的就會失敗。在另外一方面,驅動能夠調整size字段,以與硬件支持的圖像大小相適應。普便的作法是可能的話就將大小調小。因此一個只能處理VGA分辨率的設備驅動會根據狀況相應地調整width和height參數而成功返回。v4l2_format 結構體會在調用後複製給用戶空間;驅動應該更新這個結構體以反映改變的參數,這樣應用才能夠知道它真正獲得就是什麼。
VIDIOC_TRY_FMT 這個處理對於驅動來講是可選的,可是不推薦忽略這個功能.若是提供了的話,這個函數能夠在任什麼時候候調用,甚至時設備正在工做的時候。它不能夠對實質上的硬件參數作任何改變,只是讓應用知道均可以作什麼的一種方式。
若是應用要真正的改變硬件的格式,它使用VIDIOC_S_FMT 調用,它如下面的方式到達驅動:
    int (*vidioc_s_fmt_cap)(struct file *file, void *private_data,
                                struct v4l2_format *f);
    int (*vidioc_s_fmt_video_output)(struct file *file, void *private_data,
                                         struct v4l2_format *f);

與VIDIOC_TRY_FMT不一樣,這個調用是不能隨時調用的.若是硬件正在工做,或者有流緩衝器己經開闢了(將來另外一篇文章的),改變格式會帶來無盡的麻煩。想一想會發生什麼,好比說,一個新的格式比如今使的緩衝區大的時候。因此驅動要一直保證硬件是空閒的,若是不空閒就對請求返回失敗 (-EBUSY).
格式的改變應該是原子的 – 它或者改變因此的參數以實現請求不然就必須一個也不改變.一樣,驅動在必要時是能夠改變圖像的大小的,一般的回調函數格式與下面的差很少:
    int my_s_fmt_cap(struct file *file, void *private,
                     struct v4l2_format *f)
    {
        struct mydev *dev = (struct mydev *) private;
        int ret;

        if (hardware_busy(mydev))
            return -EBUSY;
        ret = my_try_fmt_cap(file, private, f);
        if (ret != 0)
            return ret;
        return tweak_hardware(mydev, &f->fmt.pix);
    }

使用VIDIOC_TRY_FMT 句柄能夠避免代碼重寫並且能夠避免任何沒有先實現那個函數的藉口. 若是」try」函數成功返回,結果格式就己知而且能夠直接編程進硬件。
還有不少其餘調用也用影響視頻I/O的完成方式。未來的文章將會討論他們中的一部分。支持設置格式就足以讓應用開始傳輸圖像了,並且那也這個結構體的最終目的.因此下一篇文章,(但願會在此次以後的時間不會過久)咱們會來關注對視頻數據的讀和寫的支持。

 

v4l2驅動編寫篇第六A--基本的幀輸入輸出 
基本的幀輸入輸出
關於視頻驅動的這一系列文章己經更新了好幾期,可是咱們尚未傳輸過一幀的視頻數據。雖然在這一點上,咱們己經瞭解了足夠多的關於格式協定方面的細節,咱們能夠看一下視頻幀是如何在應用和設備之間傳輸的了。

V4L2 API定義了三種不一樣的傳輸視頻幀的方法,如今有兩種是能夠實現的:

read() 和write() 系統調用這種普通的方法. 根據硬件和驅動的不一樣,這種方法可能會很是慢 -但也不是必定會那樣.
將幀直接以視頻流的方法送到應用能夠訪問的緩衝區. 視頻流這際上是傳輸視頻數據的最有效的方法;這種接口還容許在圖像幀中附帶一些其餘信息. 視頻流的方法有兩種變種,其分別在於緩衝的開闢是在用戶空間仍是內核空間。
Video4Linux2 API規範提供一種異頻的輸入輸出機制用於幀的傳輸。然而這種模式尚未實現,所以不能使用。
這一篇將關注的是簡單的read()和write()接口,視頻流的方式將在下一期來說解。

read() 和 write()
Video4Linux2 規範並無規定要實現read()和write(),然而不少簡單的應用但願這種系統調用可用,因此可能的話,驅動的做者應該使之工做。若是驅動沒有實現這些系統調用,它應該在保證V4L2_CAP_READWRITE置位,來回應VIDIOC_QUERYCAP調用。然而以筆者的經驗,多數的應用在使用調用以前,根本就不會是費心查看調用是否可用。

驅動的read()和/或write()方法必須存在相關的video_device結構中的fops字段裏。注意:V4L2規範要求實現這些方法,從而也提供poll()操做。

在一下視頻捕獲設備上實現read()操做是很是直接的:驅動告訴硬件開始捕獲幀,發送一幀到用戶空間緩衝,而後關停硬件並返回。若是可能的話,驅動應該安排DMA操做直接將數據傳送到目的緩衝區,但這種方式只有在控制器能夠處理分散/彙集I/O的時候可能。不然,驅動應該在內核裏啓用幀緩衝區。一樣,寫操做也是儘量直接傳到設備,不然啓用幀緩衝區。

不那麼簡單的操做也是能夠的。例如筆者的」cafe」驅動會在read()操做後讓攝像頭控制器工做在一種投機的狀態,在一秒鐘的下一部分,攝像頭中的後續幀將會存儲在內核的緩衝區中,若是應用發出了另外一個讀的調用,它將會更快的反應,無續再次啓動硬件。通過必定數目的幀都沒有讀的話,控制器就會被放回空閒的狀態。同理,寫操做時,也會廷時幾十毫秒,意在幫助應用與硬件幀同步。

流參數
VIDIOC_G_PARM 和VIDIOC_S_PARM ioctl()系統調用會調整一些read(),write()專用的參數,其中一些更加普便。它看起來像是一個設置沒有明顯歸屬的雜項的調用。咱們在這裏就瞭解一下,雖然有些參數同時會影響流輸入輸出的參數。

支持這些調用的Video4Linux2驅動提供以下兩個方法:

int (*vidioc_g_parm) (struct file *file, void *private_data,
                              struct v4l2_streamparm *parms);
    int (*vidioc_s_parm) (struct file *file, void *private_data,
                          struct v4l2_streamparm *parms);
v4l2_streamparm結構包含下面的聯合,這一系列文章的讀者到如今應該對它們己經很熟悉了。

struct v4l2_streamparm
    {
        enum v4l2_buf_type type;
        union
        {
                struct v4l2_captureparm        capture;
                struct v4l2_outputparm        output;
                __u8 raw_data[200];
        } parm;
    };
type字段描述的是在涉及的操做的類型。對於視頻捕獲設備,應該爲V4L2_BUF_TYPE_VIDEO_CAPTURE。對於輸出設備應該爲 V4L2_BUF_TYPE_VIDEO_OUTPUT。它的值也能夠是V4L2_BUF_TYPE_PRIVATE,在這種狀況下,raw_data字段用來傳遞一些私有的,不可移植的,甚至是不鼓勵的數據給內核 。

對於捕獲設備而言,parm.capture字段是要關注的內容,這個結構體以下:

struct v4l2_captureparm
    {
        __u32                   capability;
        __u32                   capturemode;
        struct v4l2_fract  timeperframe;
        __u32                   extendedmode;
        __u32              readbuffers;
        __u32                   reserved[4];
    };
capability字段是一組功能標籤。目前爲止已經定義的只有一個V4L2_CAP_TIMEPERFRAME,它表明能夠改變幀頻率。 capturemode也是一個只定義了一個標籤的字段:V4L2_MODE_HIGHQUALITY,這個標籤意在使硬件在高清模式下工做,實現單幀的捕獲。這個模式能夠作出任何的犧牲(包括支持的格式,曝光時間等),以達到設備能夠處理的最佳圖片質量。

timeperframe字段用於指定想要使用的幀頻率,它又是一個結構體:

    struct v4l2_fract {
        __u32   numerator;
        __u32   denominator;
    };
numerator 和denominator所描述的係數給出的是成功的幀之間的時間間隔。另外一個驅動相關的字段是:extendedmode,它在API中沒有明確的意義。readbuffers字段是read()操做被調用時內核應爲輸入的幀準備的緩衝區數量。

對於輸出設備,其結構體以下:

struct v4l2_outputparm
    {
        __u32                   capability;
        __u32                   outputmode;
        struct v4l2_fract  timeperframe;
        __u32                   extendedmode;
        __u32              writebuffers;
        __u32                   reserved[4];
    };
capability, timeperframe, 和 extendedmode字段與捕獲設備中的意義相同。outputmode 和writebuffers與capturemode 和readbuffers對應相同。

當應用想要查詢如今的參數時,它會發出一個VIDIOC_G_PARM調用,於是調用驅動的vidioc_g_parm()方法。驅動應該提供如今的設置,不用的話確保將extendedmode設爲0,而且把reserved字段永遠設爲0.

設置參數將調用vidioc_s_parm()。在這種狀況下,驅動將參數設爲與應用所提供的參數儘量近的值,並調整v4l2_streamparm結構體以反應現行使用的值 。例如,應用能夠會請求一個比硬件所能提供的更高的幀頻率,在這種狀況下,幀頻率會設爲最高,並把imeperframe設爲這個最高的幀頻率。

若是應用提供timeperframe爲0,the driver should program the nominal frame rate associated with the current video norm.(這句不懂)。若是readbuffers 或writebuffers是0,驅動應返回現行設置而不是刪除緩衝區。

到如今爲止,咱們已經能夠寫一個支持read()和write()方式幀傳輸的簡單的驅動了。然而多數正式的應用要使用流輸入輸出方式:流的方式使高性能變得更簡單;幀能夠打包帶上附加信息如幀序號,請繼續關注本系列的下篇文章,咱們將討論如何在視頻驅動中實現流API.
v4l2驅動編寫篇第六B--流輸入輸出 
在本系列文章的上一期中,咱們討論瞭如何經過read()和write()的方式實現視頻幀的傳輸,這樣的實現能夠完成基本的工做,卻並非普便上用來實現視頻輸入輸出你們偏心的方法。爲了實現最高的性能和最好的信息傳輸,視頻驅動應該支持V4L2 流輸入輸出。

使用read()和write()方法,每一幀都要經過I/O操做在用戶和內核空間之間拷貝數據。然而,當使用流輸入輸出的方式時,這種狀況就不會發生。替代的方案是用戶與內核空間之間交換緩衝區的指針,這些緩衝區將被映射到應用的地址空間,這也就使零幀複製數成爲可能。有兩種流輸入輸出緩衝區:

    * 內存映謝緩衝區(memory-mapped buffers) (typeV4L2_MEMORY_MMAP) 是在內核空間開闢的;應用經過themmap()系統調用將其映謝到地址空間。這些緩衝區能夠是大而相臨DMA緩衝區,經過vmalloc()建立的虛擬緩衝區,或者(若是硬件支持的話)直接在設備的輸入輸出存儲器中開闢的。
    * 用戶空間緩衝區 (V4L2_MEMORY_USERPTR) 是在用戶空間的應用中開闢的.很明顯,在這種狀況下,是不須要mmap()調用的,但驅動在有效地支持用戶空間緩衝區上的工做將會更難一些。

注意:驅動支持流輸入輸出的方式並不是必需,即使作了實現,驅動也沒必要兩種緩衝區類型都作處理。一個靈活的驅動能夠支持更多的應用。在實際應用中,彷佛多數應用都是使用內存映射緩衝區的。同時使用兩種緩衝區是不可能的。

如今,咱們將要探索一下支持流輸入輸出的衆多而邋遢的細節。任何Video4Linux2驅動的做者都要了解這部分API。然而值得指出的是,有一個更高層次的API,它可以幫助驅動做者寫流驅動。當底層設備能夠支持分散/彙集I/O的時候,這一層(稱爲video-buf)能夠使事情變得容易。關於 video-buf API咱們將在未來的某期討論。

支持流輸入輸出的驅動應該通知應用這一事實,方法是在vidioc_querycap()方法中設置V4L2_CAP_STREAMING標籤。注意:並無辦法來描述支持的是哪種緩衝區,那是後話。
v4l2_buffer結構體

當流輸入輸出是有效的,幀是以v4l2_buffer的形式在應用和驅動之間傳輸的。這個結構體是一個複雜的--畜生?--,要用很長的時間才能描述完。一個很好的開頭要你們知道一個緩衝區能夠有三種基本的狀態:

    * 在驅動的傳入隊列中.若是驅動不用它作什麼有用事的話,應用就能夠把緩衝區放在這個隊列裏。對於一個視頻捕獲設備還講,傳入隊列中的緩衝區是空的,等待驅動向其內填入視頻數據。對於輸入設備來說,這些緩衝區內是要送入設備的幀數據。
    * 在驅動的傳出隊列中.這些緩衝區已經通過驅動的處理,正等待應用來認領。對於捕獲設備而言,傳出緩衝區內是新的幀數據;對出輸出設備而言,這個緩衝區是空的。
    * 不在上述兩個隊列裏.在這種狀態時,緩衝區是由用戶空間擁有的,驅動沒法訪問。這是應用能夠對緩衝區進行操做的惟一的時間。咱們稱其爲用戶空間狀態。

這些狀態和形成他們之間傳輸的操做都放在一塊兒,在下圖中示出:

實際上的v4l2_buffer結構體以下:

    struct v4l2_buffer
    {
        __u32                        index;
        enum v4l2_buf_type      type;
        __u32                        bytesused;
        __u32                        flags;
        enum v4l2_field                field;
        struct timeval                timestamp;
        struct v4l2_timecode        timecode;
        __u32                        sequence;

        /* memory location */
        enum v4l2_memory        memory;
        union {
                __u32           offset;
                unsigned long   userptr;
        } m;
        __u32                        length;
        __u32                        input;
        __u32                        reserved;
    };

index 字段是鑑別緩衝區的序號;它只在內存映射緩衝區中使用。與其它能夠在V4L2接口中枚舉的對相同樣,內存映射緩衝區的index從0開始,依次遞增。 type字段描述的是緩衝區的類型 ,一般是V4L2_BUF_TYPE_VIDEO_CAPTURE 或V4L2_BUF_TYPE_VIDEO_OUTPUT.

緩衝區的大小是論長度給定的,單位爲byte.緩衝區中的圖像數據大小能夠在bytesused字段中找到。很明顯,bytesused<=length.對於捕獲設備而言,驅動會設置bytesused; 對輸出設備而言,應用必須設置這個字段。

field字段描述的是圖像存在緩衝區的那一個區域。這些區域在這系統文章中的part5a 中能夠找到。

timestamp(時間戳)字段,對於輸入設備來講,表明幀捕獲的時間.對輸出設備來講,在沒有到達時間戳所表明的時間前,驅動不能夠把幀發送出去;時間戳值爲0表明越快越好。驅動會把時間戳設爲幀的第一個字節傳送到設備的時間,或者說是驅動所能達到的最接近的時間。timecode 字段能夠用來存放時間編碼,對於視頻編輯類的應用是很是有用的。關於時間編碼詳情見表 .

驅動對傳過設備的幀維護了一個遞增的計數; 每一幀傳送時,它都會在sequence字段中存入現行序號。對於輸入設備來說,應用能夠觀察這一字段來檢測幀。

memory 字段表示的是緩衝是內存映射的仍是用戶空間的。對於內存映謝的緩衝區,m.offset 描述的是緩衝區的位置. 規範將它描述爲「從設備存儲器開發的緩衝區偏移」,但其實質倒是一個 magic cookie,應用能夠將其傳給mmap(),以肯定那一個緩衝區被映射了。然而對於用戶空間緩衝區而言,m.userptr是緩衝區的用戶空間地址。

input 字段能夠用來快速切換捕獲設備的輸入 – 固然,這要得設備支持幀與幀音的快速切換纔來。reserved字段應置0.

最後,還有幾個標籤訂義:

    * V4L2_BUF_FLAG_MAPPED 暗示緩衝區己映射到用戶空間。它只應用於內存映射緩衝區。

    * V4L2_BUF_FLAG_QUEUED: the buffer is in the driver’s incoming queue.
    * V4L2_BUF_FLAG_DONE: 緩衝區在驅動的傳出隊列.
    * V4L2_BUF_FLAG_KEYFRAME: 緩衝區包含一個關鍵幀,它在壓縮流中是很是有用的.
    * V4L2_BUF_FLAG_PFRAME 和V4L2_BUF_FLAG_BFRAME 也是應用於壓縮流中;他們表明的是預測的或者說是差分的幀.
    * V4L2_BUF_FLAG_TIMECODE: timecode 字段有效.
    * V4L2_BUF_FLAG_INPUT: input字段有效.

緩衝區設定

一旦流應用已經完成了基本的設置,它將轉去執行組織I/O緩衝區的任務。第一步就是使用VIDIOC_REQBUFS ioctl()來創建一組緩衝區,它將由V4L2轉換成對驅動的vidioc_reqbufs()方法的調用。

    int (*vidioc_reqbufs) (struct file *file, void *private_data,
                           struct v4l2_requestbuffers *req);

咱們要關注的因此內容都在v4l2_requestbuffers結構體中,以下所示:

    struct v4l2_requestbuffers
    {
        __u32                        count;
        enum v4l2_buf_type      type;
        enum v4l2_memory        memory;
        __u32                        reserved[2];
    };

type 字段描述的是完成的I/O操做的類型。一般它的值要麼是視頻得到設備的V4L2_BUF_TYPE_VIDEO_CAPTURE ,要麼是輸出設備的V4L2_BUF_TYPE_VIDEO_OUTPUT.也有其它的類型,但在這裏咱們不予討論。

若是應用想要使用內存映謝的緩衝區,它將會把memory字段置爲V4L2_MEMORY_MMAP,count置爲它想要使用的緩衝區的數目。若是驅動不支持內存映射,它就該返回-EINVAL.不然它將在內部開闢請求的緩衝區並返回0.返回以後,應用就會認爲緩衝區是存在的,因此任何能夠失敗的任務都在在這個階段進行處理 (好比說內存開闢)。

注意:驅動並不必定要開闢與請求的同樣數目的緩衝區.在不少狀況下,只有一個緩衝區數的最小值有意義。若是應用請求的比最小值小,它能夠能實際需要的要多一些。以筆者的經驗,mplayer要用兩個緩衝區,若是用戶空間速度慢下來的話,這將很容易超支(於是丟失幀)。經過強制一個大一點的最小緩衝區數(經過模塊能數能夠調整),cafe_ccic驅動能夠使流輸入輸出通道更增強壯。count 字段應設爲方法返回前實際開闢的緩衝區數。

應用能夠通設置count字段爲0的方式來釋放掉全部已存在的緩衝區。在這種狀況下,驅動必須在釋放緩衝前中止全部的DMA操做,不然會發生很是嚴重的事情。若是緩衝區已映射到用戶空間,則釋放緩衝區是不可能的。

相反,若是用的是用戶空間緩衝區,則有意義的字段只有緩衝區的type和只有V4L2_MEMORY_USERPTR這個值可用的memory 字段。應用不須要指定它想用的緩衝區的數目。由於內存是在用戶空間開闢的,驅動無須操心。若是驅動支持用戶空間緩衝區,它只須注意應用會使用這一特性,返回0就能夠了,不然一般的-EINVAL 返回值會被調用到.

VIDIOC_REQBUFS 命令是應用得知驅動支持的流輸入輸出緩衝區類型的惟一方法。
將緩衝區映射到用戶空間

若是使用了用戶空間,在應用向傳入隊列放置緩衝區以前,驅動看不到任何緩衝區相關的調用。內存映射緩衝區須要更多的設置。應用一般會查看每個開闢了的緩衝區,並將其映射到地址空間。第一站是VIDIOC_QUERYBUF命令,它將轉換成驅動中的vidioc_querybuf() 方法:

    int (*vidioc_querybuf)(struct file *file, void *private_data,
                           struct v4l2_buffer *buf);

進入這個方法時,buf 字段中要設置的字段有type (在緩衝區開闢時,它將被檢查是否與給定的類型相同)和index,它們能夠肯定一個特定的緩衝區。驅動要保證index有意義,並添充buf中的其他字段。一般來講,驅動內部存儲着一個v4l2_buffer結構體的數組, 因此vidioc_querybuf()方法的核心只是一個結構體的賦值。

應用訪問內存映射緩衝區的惟一方法就是將其映射到它們的地址空間,因此vidioc_querybuf() 調用後面一般會跟着一個驅動的mmap()方法 -這個方法,你們要記住,是存儲在相關設備的video_device結構體中的fops字段中的。 設備如何處理mmap() 將依賴於內核中緩衝區是如何設置的。若是緩衝區能夠在remap_pfn_range() 或remap_vmalloc_range()以前映射,那就應該在這個時間來作.對於內核空間的緩衝區,頁也能夠在頁錯誤時經過常規的使用 nopage()方法的方式單獨被映射,對於須要的人來講,在Linux Device Drivers 能夠找到一個關於handlingmmap()的一個很好的討論。

mmap() 被調用時,傳遞的VMA結構應該含有vm_pgoff字段中的某個緩衝區的地址-固然是通過PAGE_SHIFT右移過的。特別是,它應該是你的驅動對於 VIDIOC_QUERYBUF調用返回值的楄移。請您遍歷緩衝區列表,並確保傳入地址匹配其中之一。視頻驅動程序不該該是一個可讓惡意程序映射內存的任意區域手段。

你所提供的偏移值可幾乎全部的東西.有些驅動只是返回(index<<PAGE_SHIFT),意思是說傳入的vm_pgoff字段應該正好是緩衝區索引。有一件事你不能夠作的就是把緩衝區的內核實際地址存儲到offset字段,把內核地址泄露給用戶空間永遠也不是一個好注意。

當用戶空間映射緩衝區時,驅動應該在相關的v4l2_buffer結構體中調置V4L2_BUF_FLAG_MAPPED標籤.它也必須設定open() 和close() VMA 操做,這樣它才能夠跟蹤映謝了緩衝區的進程數。只要緩衝區在哪裏映射了,它就不能夠在內核裏釋放掉。若是一個或多個緩衝區的映謝計算降爲0,驅動就應該中止正在進行的輸入輸出,由於沒有進程要用它。
流輸入輸出

到現爲止,咱們己經看了不少設置,卻沒有傳輸過一幀的數據,咱們離這步己經很近了,但在些以前還有一個步驟要作。當應用經過 VIDIOC_REQBUFS得到了緩衝區後,那個緩衝區都處於用戶空間狀態。若是他們是用戶空間緩衝區,他們甚至還並不真的存在。在應用能夠開始流的輸入輸出以前,它必須至少將一個緩衝區放到驅動傳入隊列,對出輸出設備,那些緩衝區固然還要先添完有效的幀數據。

要把一個緩衝區加入隊列,應用首先要發出一個VIDIOC_QBUF ioctl()調用,這個調用V4L2會映射爲對驅動的vidioc_qbuf()方法的調用。

    int (*vidioc_qbuf) (struct file *file, void *private_data,
                        struct v4l2_buffer *buf);

對於內存映射緩衝而言,仍是那樣,只有buf的type和 index字段有效. 驅動只能進行一些明顯的檢查(type 和index 有意義,緩衝區尚未在驅動的隊列裏,緩衝區己映射等。),把緩衝區放在傳入隊列裏 (設置V4L2_BUF_FLAG_QUEUED 標籤),並返回.

在這點上,用戶空間緩衝區可能會更加複雜,由於驅動可能歷來沒看過緩衝區的樣子。使用這個方法時,容許應用在每次向隊列傳入緩衝區時,傳遞不一樣的地址,因此驅動不能提早作任何的設置。若是你的驅動經過內核空間緩衝區將幀送回,它只須記錄一下應用提供的用戶空間地址就能夠了。然而若是你正嘗試將經過 DMA直接將數據傳送到用戶空間,那將會很是的具備挑戰性。

要想把數據直接傳送到用戶空間,驅必須先fault in緩衝區的因此的頁,並將其鎖定。get_user_pages() 能夠作這件事.注意這個函數會在開闢很大的內存空間和硬盤I/O-它可能會卡住很長時間。你得注意要保證重要的驅動功能不能在 get_user_pages()時中止,由於它可能中止很長時間等待許多視頻幀經過。

下面就是要告訴設備把圖像數據傳到用戶空間緩衝區(或是相反的方向)了。緩衝區在物理上不是相臨的,相反,它會被分散成不少不少單獨的4096字節的頁(在大部分架構上如此)。很明顯,設備得能夠實現分散/彙集DMA操做才行。若是設備當即傳輸一個完整的視頻幀,它就須要接受一個包含不少頁的分散列表(scatterlist)。一個 16位格式的VGA解決方案的圖像須要150個頁,隨着圖像大小的增長,分散列表的大小也會增長.V4L2規範說:

    若是硬件須要,驅動要與物理內存交換內存頁,以產生相臨的內存區。這對內核子系統的虛擬內存中的應用來講是透明的。

然而,筆者卻不推薦驅動做者嘗試這種深層的虛擬內存技巧。有一個更有前途的方法就是要求用戶空間緩衝區分配成大的tlb頁,可是如今的驅動不那麼作。

若是你的設備轉輸的是小圖像(如USB攝像頭),直接從DMA到用戶空間的設定就簡單些。在任何狀況下,不得不支持到用戶空間緩衝區的直接I/O 時,驅動做者都應該(1)肯定的確值得這麼大的麻煩,由於應用更趨向於使用內存映射緩衝區。(2)使用video_buf屋,它能夠幫你解決一些痛苦的難題 。

一旦流輸入輸出開始,驅動就要從它的傳入隊列裏抓取緩衝區,讓設備更快地實現轉送請求,而後把緩衝區移動到傳出隊列。轉輸開始時,緩衝區標籤也要相應調整。像序號和時間戳這樣的字段必需在這個時候添充。最後,應用會在傳出隊列中認領緩衝區,讓它變回爲用戶空間狀態。這是VIDIOC_DQBUF的工做,它最終變爲以下調用:

    int (*vidioc_dqbuf) (struct file *file, void *private_data,
                         struct v4l2_buffer *buf);

這裏,驅動會從傳出隊列中移除第一個緩衝區,把相關的信息存入buf.一般,傳出隊列是空的,這個調用會處於阻塞狀態直到有緩衝區可用。然而V4L2是用來處理非阻塞I/O的,因此若是視頻設備是以O_NONBLOCK方式打開的,在隊列爲空的狀況下驅動就該返回-EAGAIN .固然,這個要求也暗示驅動必須爲流I/O支持poll();

剩下最後的一個步驟實際上就是告訴設備開始流輸入輸出操做。這個任務的 Video4Linux2驅動方法是:

    int (*vidioc_streamon) (struct file *file, void *private_data,
                            enum v4l2_buf_type type);
    int (*vidioc_streamoff)(struct file *file, void *private_data,
                                enum v4l2_buf_type type);

對vidioc_streamon()的調用應該在檢查類型有意義以後讓設備開始工做。如查須要的話,驅動能夠請求等傳入隊列中有必定數目的緩衝區後再開始流的轉輸.

當應用關閉時,它應發出一個對vidioc_streamoff()的調用,這個調用要中止設備。驅動還應從傳入還傳出隊列是移除全部的緩衝區,使它們都處於用戶空間狀態。固然,驅動必須準備好,應用可能在沒有停流轉輸的狀況下關閉設備。

v4l2驅動編寫篇第七--控制方法 
剛剛完成了這一系列文章的第六部分,咱們如今知道如何設置視頻設備,並來回傳輸幀了。然而,有一個衆所周知的事實,那就是用戶永遠也不會滿意,不會知足於能從攝像頭上看到視頻,他們立刻就會問我可不能夠調參數啊?像亮度、對比度等等。這些參數能夠視頻應用中調整,有時也的確會這樣作,可是當硬件支持時,在硬件中進行調整有其優點。好比說亮度調整,若是不這樣作的話,可能會丟失動態範圍,可是基於硬件的調整能夠完整保持傳感器能夠傳遞的動態範圍。很明顯,基於硬件的調整也可讓主機的處理器壓力輕些。

現代的硬件中一般均可以在運行的時候調整不少參數。然而如今,在不一樣設備之間這些參數差異很大。簡單的亮度調整能夠直觀地設置一個寄存器,也能夠須要處理一個沒有頭緒的矩陣變換。最好是能儘量多的把諸多細節對應用隱藏,但能隱藏到什麼程序卻受到不少限制。一個過於抽象的接口會使硬件的控制沒法發揮其極限。

V4L2的控制接口試圖使事情儘量地簡單,同時還能徹底發揮硬件的功能。它開始於定義一個標準控制名字的集合,包括 V4L2_CID_BRIGHTNESS,V4L2_CID_CONTRAST,V4L2_CID_SATURATION,還有許多其餘的。對於白平衡、水平,垂直鏡像等特性,還提供了一些布爾型的控制。定義的控制ID值的完整列表請見the V4L2 API spec 。還有一個驅動程序特定的控制,但這些,顯然,通常只能由專用的應用程序使用。私有的控制從V4L2_CID_PRIVATE_BASE開始日後都是。

一種典型的作法,V4L2 API提供一種機制可讓應用能夠枚舉可用的控制操做。爲此,他們要發出最終要實現爲驅動中的vidioc_queryctrl()方法的ioctl()調用。

   int (*vidioc_queryctrl)(struct file *file, void *private_data,
                            struct v4l2_queryctrl *qc);

驅動一般會用所關心的控制信息來添充qc結構體,或是當控制操做不支持時返回EINVAL,這個結構體有不少個字段:

    struct v4l2_queryctrl
    {
        __u32                     id;
        enum v4l2_ctrl_type  type;
        __u8                     name[32];
        __s32                     minimum;
        __s32                     maximum;
        __s32                     step;
        __s32                     default_value;
        __u32                flags;
        __u32                     reserved[2];
    };

被查詢的控制操做將會經過id傳送。做爲一個特殊的狀況,應用能夠經過設定V4L2_CTRL_FLAG_NEXT_CTRL位的方式傳遞控制id.當這種狀況發生時,驅動會返回關於下一個所支持的控制id的信息,這比應用給出的ID要高。不管在何種狀況下,id都應設爲實際上被描述的控制操做的id.

其餘全部字段都由驅動設定,用來描述所選的控制操做。控制的數據類型在type字段中給定。這能夠是V4L2_CTRL_TYPE_INTEGER、 V4L2_CTRL_TYPE_BOOLEAN、V4L2_CTRL_TYPE_MENU (針對一組固定的擇項) 或V4L2_CTRL_TYPE_BUTTON (針對一些設定時會忽略任何給出的值的控制操做).name字段用來描述控制操做;它能夠在展示給用戶的應用接口中使用。 對於整型的控制來講(僅針對這種控制),minimum和maximum 描述的是控制所實現的值的範圍,step 給出的是此範圍下的粒度大小。default_value顧名思義就是默認值 – 僅管他只對整型,布爾型和菜單控制適用。驅動只應在初始化時將控制參數設爲默認。至於其它設備參數,他們應該從open()到close()保持不變。結果,default_value 極可能不是如今的控制參數值。

不可避免地,還有一組值進一步描述控制操做。V4L2_CTRL_FLAG_DISABLED 意爲控制操做不可用,就用應該忽略它。V4L2_CTRL_FLAG_GRABBED 意爲控制暫進不可變,可能會是由於另外一個應用正在使用它。V4L2_CTRL_FLAG_READ_ONLY 意爲能夠查看,但不能夠改變的控制操做。V4L2_CTRL_FLAG_UPDATE意爲調整這個參數能夠會對其餘控制操做形成影響。 V4L2_CTRL_FLAG_INACTIVE 意爲與當前設備配置無關的控制操做。V4L2_CTRL_FLAG_SLIDER 意在暗示應用在表現這個操做的時候能夠使用相似於滾動條的接口。

應用能夠只是查詢幾個特別編程過的控制操做,或者他們也想枚舉整個集合。對後而來說,他們會從開始V4L2_CID_BASE 至V4L2_CID_LASTP1結束,過程當中可能會用到V4L2_CTRL_FLAG_NEXT_CTRL標籤.對於菜單型的諸多控制操做 (type=V4L2_CTRL_TYPE_MENU)而言, 應用極可能想要枚舉可能的值,相關的回調函數是:

int (*vidioc_querymenu)(struct file *file, void *private_data,
                            struct v4l2_querymenu *qm);

v4l2_querymenu 結構體以下:

    struct v4l2_querymenu
    {
        __u32                id;
        __u32                index;
        __u8                name[32];
        __u32                reserved;
    };

在輸入中,id 是相關的菜單控制操做的ID值,index 某特定菜單ID值的索引值.索引值從0開始,依次遞增到vidioc_queryctrl()返回的最大值.驅動會填充菜單項的name字段。reserved字段恆設爲0.

一旦應用知道了可用的控制操做,它就很能夠開始查詢並改變其值。這種狀況下相關的結構體是:

    struct v4l2_control
    {
        __u32 id;
        __s32 value;
    };

要查詢某一給定控制操做,應用應將id字段設爲對應的控制的ID,併發出一個調用,這個調最終實現爲:

    int (*vidioc_g_ctrl)(struct file *file, void *private_data,
                             struct v4l2_control *ctrl);

驅動應將值設爲當前控制的設定,還要保證它知道這個特定的控制操做並在應用試圖查詢不存在的控制操做時返回EINVAL,試圖訪問按鍵控制時也應返回EINVAL.

一個試圖改變控制操做的請求實現爲:

    int (*vidioc_s_ctrl)(struct file *file, void *private_data,
                         struct v4l2_control *ctrl);

驅動應該驗證id,保證其值在容許的區間。若是一切都沒有問題的話,就將新值寫入硬件。

最後, 值得注意的是,還有一個單獨的擴展控制接口 也爲V4L2所支持.這個API是一組至關複雜的控制操做。實際上,它的主要應用就是 MPEG編解碼的參數。擴展控制能夠分門歸類, 並且支持64位整型值。其接口與常規的控制接口相似。詳見API規範。

 
0
0
 
 
 
 
查看評論

  暫無評論

 
發表評論
  • 用 戶 名:
  • u011749143
  • 評論內容:
  • 插入代碼
      
* 以上用戶言論只表明其我的觀點,不表明CSDN網站的觀點或立場
 
 
 
 
    我的資料
 
 
    • 訪問:342371次
    • 積分:3967
    • 等級:
    • 排名:第8158名
    • 原創:50篇
    • 轉載:129篇
    • 譯文:1篇
    • 評論:36條
    文章存檔
    最新評論
 
 
 
相關文章
相關標籤/搜索