![]() |
系統(tǒng)采用的是Linux操作系統(tǒng),其下的音頻編程遵循OSS(Open Sound System)音頻接口標準,OSS是Unix/Linux平臺上統(tǒng)一的音頻接口,只要音頻處理應用程序按照OSS的API來編寫,它就可以提供源代碼級的可移植性。
Linux下的設備全部使用設備文件來管理,本系統(tǒng)使用的數(shù)字音頻設備為/dev/dsp?梢圆シ呕蜾浿茢(shù)字化的聲音,讀這個設備就相當于錄音,寫這個設備就相當于放音,它使用8位(無符號)線性編碼,其主要指標參數(shù)有:采樣速率(電話為8Kbps)、聲道數(shù)目(單聲道、立體聲)和采樣分辨率(8位、16位)。
在進行音頻的采集和播放之前,必須先打開該音頻設備并適當設置一些工作參數(shù),這些都在IP Audio類的構造函數(shù)中實現(xiàn),其中的一些參數(shù)和操作都被定義在"soundcard.h"頭文件中。
首先,要打開音頻設備。因為系統(tǒng)在通話時要同時進行錄音和放音,所以使用讀寫模式,相關代碼片斷如下:
int audio_fd;
if((audio_fd=open("/dev/dsp",O_RDWR))<0) …//錯誤處理
打開設備后,為了正常地工作,設置一些相應的工作參數(shù)。
1)先設置為全雙工工作模式,并檢查是否設置成功,代碼如下: 
設置好各個參數(shù)后,就可以進行視頻的采集和播放了,采集及錄音使用OSS提供的read()函數(shù),播放則使用對應的write()函數(shù),直接對音頻設備/dev/dsp進行操作,由于進行IP電話通話時,要進行不間斷錄音和放音,但音頻設備的輸入/輸出緩沖區(qū)的大小是有限的,必須不斷循環(huán)使用,因此采用QT/Embedded的信號和槽機制來實現(xiàn),系統(tǒng)采集完一次數(shù)據(jù)并發(fā)送出去后,給IPAudio類自身發(fā)送一個canRecord()信號,而采集函數(shù)本身是一個槽,接收到canPlay()信號后又開始下一次采集。這樣循環(huán)不斷,代碼片斷如下:
public slots; void record(){ int len; if(ioctl(audio_fd,SOUND_PCM_SYNC,0)==-1) //同步 … //錯誤處理 if(len=read(audio_fd,buf,1024))!=1024) //錄音 printf("Read wrong number of bytes %d\n",len); else{ (*(aDataSock->ds))<<buf; //發(fā)送采集的音頻數(shù)據(jù)給對話端 emit canRecord();//發(fā)送可錄音信號 } }
當系統(tǒng)接收到對話端發(fā)送過來的音頻數(shù)據(jù)時,音頻接收套接字aDataSock發(fā)送一個readyRead()數(shù)據(jù)已準備好的信號給IPAudio類的槽函數(shù)play()來播放這段音頻。套接字aDataSock初始化時的語句
connect(aDatasock,SIGNAL(readyRead()),IPAudio,SLOT(play()));
即實現(xiàn)這個功能,播放時為了避免要播放的數(shù)據(jù)太多而導致設備被阻塞,還須先檢測音頻設備的輸出緩沖區(qū)是否有足夠的空間,以使系統(tǒng)能夠正常工作。代碼如下: 
這樣,系統(tǒng)就可以實現(xiàn)無阻塞的音頻采集和播放,一個傳統(tǒng)的IP語音電話就實現(xiàn)了,系統(tǒng)退出時,用close()函數(shù)關閉音頻設備即可。
3.3 視頻采集/播放模塊設計
視頻采集和播放模塊實現(xiàn)了通過攝像頭采集本端影像視頻傳輸給對話方并接收對方的視頻數(shù)據(jù)還原成影像顯示在本端屏幕上的功能,也是本IP電話系統(tǒng)的先進之處,程序中用多的一些Video4Linux支持的專用視頻數(shù)據(jù)結構如下:
1)video_capability,包含攝像頭的基本信息,如設備名稱、支持的最大最小分辨率、信號源信息等,分別對應著結構體中成員變量name[32]、maxwidth、maxheight、minwidth、minheight、channels(信號源個數(shù))、type等;
2)video_picture,包含設備采集圖像的各種屬性,如brightness(亮度)、hue(色調(diào))、contrast(對比度)、whiteness(色度)、depth(深度)等;
3)video_mmap,用于內(nèi)存映射;
4)video_mbuf,利用mmap進行映射的幀信息,實際上是輸入到攝像頭存儲器緩沖中的幀信息,包括size(幀的大小)、frames(最大支持的幀數(shù))、offsets(每幀相對基址的偏移);
5)video_Window,包括設備采集窗口的各種參數(shù)。
視頻采集/播放模塊的基本工作流程如圖5所示。
![]() |
struct video_capability cap; struct video_window win; if(ioctl(video_fd,VIDIOCGCAP,&cap)==-1) //讀取攝像頭信息 …//錯誤處理 w=win.width=cap.maxwidth; h=win.height=cap.maxheight; frameSize=w*h; if(ioctl(video_fd,VIDIOCSWIN,&win)==-1) //設置采集窗口大小 …//錯誤處理
進行初始化設備工作后,就可以對視頻圖像進行采集了,通常有兩種方法:一種是使用read()直接讀取視頻數(shù)據(jù);另外一種是通過mmap()內(nèi)存映射來實現(xiàn),read()通過內(nèi)核緩沖區(qū)來讀取數(shù)據(jù),而mmap()通過把設備文件映射到內(nèi)存中,繞過了內(nèi)核緩沖區(qū),加速了I/O訪問,顯然比使用read()函數(shù)快。所以在系統(tǒng)實現(xiàn)中采用mmap()內(nèi)存映射方式。
利用mmap()方式對視頻進行采集時,先獲得攝像頭存儲緩沖區(qū)的幀信息,之后修改video_mmap中的設置,可以重新設置圖像幀的重新及水平分辨率、彩色顯示格式,接著把攝像頭對應的設備文件映射到內(nèi)存區(qū),代碼片斷如下:

這樣攝像頭設備所采集的內(nèi)容就映射到了內(nèi)存緩沖區(qū)pixBuf中,該映射內(nèi)容區(qū)可讀可寫并可與其他進程共享。將系統(tǒng)設置為單幀采集模式,當1幀數(shù)據(jù)采集完畢時,通過vDataSock套接字將視頻數(shù)據(jù)傳送給對方,然后發(fā)一個canSample()信號給自身再開始下一幀數(shù)據(jù)的采集,如下: 
在采集視頻數(shù)據(jù)的同時,還要顯示對方傳輸過來的視頻數(shù)據(jù),當對方的數(shù)據(jù)被接收到時,系統(tǒng)利用vDataSock的readyRead()信號告訴IPVideo將其顯示出來。IPVideo使用QT/Embedded的QImage和QPainter類來實現(xiàn)圖像數(shù)據(jù)的顯示,先初始化,為了使圖像重畫時不閃爍,設置WRepaintNoErase重畫不擦除標志,如下:
p=new QPainter(); image=new QImage((uchar*)buff,w,h,32,0,0,(QImage::Endian)0); setWFlags(getWFlags() Qt::WRepaintNoErase);
重載IPVideo的paintEvent()函數(shù),加載buff中接收過來的視頻數(shù)據(jù),并在屏幕上畫出來,代碼如下:
void paintEvent(QPaintEvent*){ image->loadFromData((uchar*)buff,frameSize); p->begin(this); p->drawImage(0,0,*image); p->end(); }
在IPVideo中增加一個槽函數(shù)show(),專門接收vDataSock的readyRead()信號,一旦接收到了,就通過vDataSock的ds將視頻數(shù)據(jù)流導入buff緩沖區(qū)中,并調(diào)用updata()函數(shù),該函數(shù)將激活paint事件,調(diào)用paintEvent()函數(shù)進行視頻的更新重畫。這樣,隨著不停地接收到對方的圖像數(shù)據(jù),就實現(xiàn)了遠端視頻的播放,雙方就能進行語音和視頻同步的IP通話了。
4 小結
本系統(tǒng)主要是針對嵌入式手持設備,可與PC或同類型的手持機進行IP視頻電話通信,擴展了傳統(tǒng)IP電話的功能,彌補了沒有圖像的缺點,并且體積小、攜帶方便、全圖形界面,操作簡單,采用無線上網(wǎng),只要網(wǎng)絡支持,可以隨時隨地使用,另外還可以做終端監(jiān)控之用,可以固定也可以移動監(jiān)控,廣泛地應用于工廠、銀行及小區(qū)等眾多場合,具有比較廣闊的市場和應用前景。







