前言.寫這篇文章的想法和思路
由於畢業設計的關系,本人要做一下在linux系統中視頻的相關工作比如采集和傳輸。由於本人是菜鳥一個,所以是需要上網搜一搜看大家都是如何做的,當然開始都是理不出一個頭緒,但是很多文章都提到了video4linux(v4l),所以我覺得工作的展開可以先從這裡開始。看了網上的一些文章,其中比較重要的也是比較知名的吧,有戴小鼠寫的《基於Video4Linux 的USB 攝像頭圖像采集實現》,有陳俊宏寫的《video stream 初探》的一系列共六篇文章,也找了一些英文的資料,看到過《video4linux programming》但是這篇文章偏重於視頻設備在linux中的驅動實現,所以對像我這種低端的只是使用v4l相關系統調用的人來說有些幫助但幫助不大,《Video4Linux Kernel API Reference》詳細介紹了v4l中各個重要的結構體的作用。另外順著陳俊宏的文章,找到了一個叫EffecTV的軟件,其中的有關v4l的源碼部分也很值得一看,在後的文章裡也會介紹。翻看了網上的很多文章,多半是使用陳俊宏介紹的相關代碼,或者是EffecTV中的,大家都是這麼用而且也都用的不錯。
基於Video4Linux 的USB 攝像頭圖像采集實現 PDF下載
免費下載地址在 http://linux.linuxidc.com/
用戶名與密碼都是www.linuxidc.com
具體下載目錄在 /2012年資料/12月/31日/Video4Linux(V4L)使用攝像頭的實例基礎教程與體會/基於Video4Linux 的USB 攝像頭圖像采集實現
-------------------------------------------------------------------------------
我寫這個文章一是想為自己的畢業論文積累些素材,二是我想可能會給今後想要了解v4l相關使用知識的人提供一個學習的路線,因為上一段中提到的幾篇文章無論誰讀起來肯定都會對他有很大的幫助,三是希望我也寫篇文章給想學習的人一點幫助,哪怕只有一點點。
文章就分成三個大部分吧:
第一個部分介紹一些v4l的基本概念和基本方法,利用系統API完成一系列函數以方便後續應用程序的開發和使用。
第二個部分一些說明如何使用v4l,用一個示例程序說明。
第三個部分想簡單說一說對獲取和處理圖像相關問題的思路。在這一章可能會談一談我的一些理解和體會。其實網絡上的資料很多,我只是稍微整理一下而已。
我的感覺linux內核和驅動開發的那些程序員很厲害因為他們留給我們一個很容易使用的接口而使底層復雜的工作對我們很透明,讀過上述我提到的文章後會覺得使用v4l是相對容易的(我希望如果有人讀了我的文章也會有這種感覺),相對復雜的是采集到圖像數據後我們應該怎麼辦,我想這也可能是很多人當然也包括我所不是特別清晰和明確的。所以我想在第三個部分裡做一些對采集到圖像數據後相關問題的探討,當然我的水平有限,請您指出文中的錯誤方法和對概念的錯誤理解,我非常願意共同學習和進步。
1.video4linux基礎相關
1.1 v4l的介紹與一些基礎知識的介紹
I.首先說明一下video4linux(v4l)。
它是一些視頻系統,視頻軟件,音頻軟件的基礎,經常使用在需要采集圖像的場合,如視頻監控,webcam,可視電話,經常應用在embedded linux中是linux嵌入式開發中經常使用的系統接口。它是linux內核提供給用戶空間的編程接口,各種的視頻和音頻設備開發相應的驅動程序後,就可以通過v4l提供的系統API來控制視頻和音頻設備,也就是說v4l分為兩層,底層為音視頻設備在內核中的驅動,上層為系統提供的API,而對於我們來說需要的就是使用這些系統的API。
II.Linux系統中的文件操作
有關Linux系統中的文件操作不屬於本文的內容。但是還是要了解相關系統調用的作用和使用方法。其中包括open(),read(),close(),ioctl(),mmap()。詳細的使用不作說明。在Linux系統中各種設備(當然包括視頻設備)也都是用文件的形式來使用的。他們存在與dev目錄下,所以本質上說,在Linux中各種外設的使用(如果它們已經正確的被驅動),與文件操作本質上是沒有什麼區別的。
1.2 建立一套簡單的v4l函數庫
這一節將一邊介紹v4l的使用方法,一邊建立一套簡單的函數,應該說是一套很基本的函數,它完成很基本的夠能但足夠展示如何使用v4l。這些函數可以用來被其他程序使用,封裝基本的v4l功能。本文只介紹一些和攝像頭相關的編程方法,並且是最基礎和最簡單的,所以一些內容並沒有介紹,一些與其他視頻設備(如視頻采集卡)和音頻設備有關的內容也沒有介紹,本人也不是很理解這方面的內容。
這裡先給出接下來將要開發出來函數的一個總覽。
相關結構體和函數的定義我們就放到一個名為v4l.h的文件中,相關函數的編寫就放在一個名為v4l.c的文件中把。
對於這個函數庫共有如下的定義(也就是大體v4l.h中的內容):
#ifndef _V4L_H_
#define _V4L_H_
#include <sys/types.h>
#include <linux/videodev.h> //使用v4l必須包含的頭文件
這個頭文件可以在/usr/include/linux下找到,裡面包含了對v4l各種結構的定義,以及各種ioctl的使用方法,所以在下文中有關v4l的相關結構體並不做詳細的介紹,可以參看此文件就會得到你想要的內容。
下面是定義的結構體,和相關函數,突然給出這麼多的代碼很唐突,不過隨著一點點解釋條理就會很清晰了。
struct _v4l_struct
{
int fd;//保存打開視頻文件的設備描述符
struct video_capability capability;//該結構及下面的結構為v4l所定義可在上述頭文件中找到
struct video_picture picture;
struct video_mmap mmap;
struct video_mbuf mbuf;
unsigned char *map;//用於指向圖像數據的指針
int frame_current;
int frame_using[VIDEO_MAXFRAME];//這兩個變量用於雙緩沖在後面介紹。
};
typedef struct _v4l_struct v4l_device;
//上面的定義的結構體,有的文中章有定義channel的變量,但對於攝像頭來說設置這個變量意義不大通常只有一個channel,本文不是為了寫出一個大而全且成熟的函數庫,只是為了介紹如何使用v4l,再加上本人水平也有限,能夠給讀者一個路線我就很知足了,所以並沒有設置這個變量同時與channel相關的函數也沒有給出。
extern int v4l_open(char *, v4l_device *);
extern int v4l_close(v4l_device *);
extern int v4l_get_capability(v4l_device *);
extern int v4l_get_picture(v4l_device *);
extern int v4l_get_mbuf(v4l_device *);
extern int v4l_set_picture(v4l_device *, int, int, int, int, int,);
extern int v4l_grab_picture(v4l_device *, unsigned int);
extern int v4l_mmap_init(v4l_device *);
extern int v4l_grab_init(v4l_device *, int, int);
extern int v4l_grab_frame(v4l_device *, int);
extern int v4l_grab_sync(v4l_device *);
上述函數會在下文中逐漸完成,功能也會逐漸介紹,雖然現在看起來沒什麼感覺只能從函數名上依稀體會它的功能,或許看起來很煩,不過看完下文就會好了。
前面已經說過使用v4l視頻編程的流程和對文件操作並沒有什麼本質的不同,大概的流程如下:
1.打開視頻設備(通常是/dev/video0)
2.獲得設備信息。
3.根據需要更改設備的相關設置。
4.獲得采集到的圖像數據(在這裡v4l提供了兩種方式,直接通過打開的設備讀取數據,使用mmap內存映射的方式獲取數據)。
5.對采集到的數據進行操作(如顯示到屏幕,圖像處理,存儲成圖片文件)。
6.關閉視頻設備。
知道了流程之後,我們就需要根據流程完成相應的函數。
那麼我們首先完成第1步打開視頻設備,需要完成int v4l_open(char *, v4l_device *);
具體的函數如下
#define DEFAULT_DEVICE “/dev/video0”
int v4l_open(char *dev , v4l_device *vd)
{
if(!dev)dev= DEFAULT_DEVICE;
if((vd-fd=open(dev,O_RDWR))<0){perror(“v4l_open:”);return -1;}
if(v4l_get_capability(vd))return -1;
if(v4l_get_picture(vd))return -1;//這兩個函數就是即將要完成的獲取設備信息的函數
return 0
}