1. 程式人生 > >利用VC 實現區域網實時視訊傳輸

利用VC 實現區域網實時視訊傳輸

在區域網內部實時傳輸視訊已經得到廣泛應用。現在用以傳輸視訊的區域網大多數是有線區域網,因為有線區域網技術成熟,傳輸速度快,穩定性好。但是視訊資料量大,有線網路也會出現工作不穩定,引起資料堵塞,時間久了會導致嚴重的延遲現象;如果工作的環境不固定,要求移動性,那麼就要採用無線網路,如今無線網絡卡的工作隨環境的變化而變得不穩定,這樣會導致視訊傳輸的質量大幅度下降,容易引起畫面的重影、抖動、花屏等現象。本文針對不同的區域網,提出一種通用的實時視訊傳輸的解決方案,使用VC 自封裝的Windows VFW SDK軟體開發包進行二次開發,通過Divx編解碼,按照制定的傳輸策略,能夠有效地解決由於網路的區域性不穩定導致的視訊影象重影、抖動、花屏等的問題。

  區域網中實時視訊傳輸存在的問題

  為了在區域網上有效的、高質量的傳輸視訊流,需要多種技術的支援,包括視訊的壓縮、編碼技術,應用層質量控制技術等等。

  網路的頻寬是有限的,所以需要壓縮傳輸視訊影象,MPEG-4被廣泛的應用於網路環境下的實時視訊傳輸,因為MPEG-4具有:可以達到很高的壓縮比;具有靈活的編碼和解碼複雜性;基於物件的編碼方式,允許視訊、音訊物件的互動;具有很強的容錯能力等優點。本文采用Divx編解碼器對視訊進行編碼、壓縮,實際上Divx=(視訊)MPEG-4 (音訊)MP3。

  應用層質量控制技術現在採用的是RTP/RTCP協議,以確保視訊流在網路中低時延、高質量地傳輸。RTP資料傳輸協議負責音視訊資料的流化和負載,RTCP負責RTP資料報文的傳輸控制。此協議是通過客戶端(接收方)反饋網路的狀況,伺服器端(傳送方)來調整資訊採集、傳送的速度和壓縮率。但是,對於影象採集速度固定,需要軟體進行壓縮、解壓,調整採集的速度會引起採集的資料來不及壓縮而直接丟棄,調整編碼器的壓縮率需要重新設定編碼器的引數,重啟編碼器,相應的解碼器也要調整,這個過程中需要很長的時間,達不到實時的要求。所以本文沒有采用RTP/RTCP協議,而是從傳送端出發,實時判斷網路狀況,採用“停等”策略進行實時傳輸。

  網路通訊有兩種協議TCP和UDP,UDP更適合於網路環境下的視訊傳輸,但是它不提供檢錯和糾錯功能,一旦網路出現堵塞時,大量的資料報文會丟失。對於Divx編解碼技術,是以幀為單位進行編解碼的,分為關鍵幀和非關鍵幀。在傳輸過程中,由於壓縮率比較高,只要一幀中錯一位元位,將影響其它幾百甚至幾千的位元位,直接造成影象的模糊、花屏等現象。只有等到下一次關鍵幀的到來才有可能恢復影象的清晰。為了保證傳輸的正確性,自己需要在應用層制定協議。如此一來,UDP的優勢蕩然無存。所以本文選擇使用TCP來進行網路通訊。綜合使用VFW技術、流媒體技術,輔助以“停等”控制策略,較好的解決區域網中實時視訊傳輸容易引起的重影、抖動、花屏的問題。

  實時視訊傳輸實現


  為了達到視訊傳輸的實時性,總的思想是最少的傳送冗餘資訊,最大程度上傳送最新的視訊。

  區域網實時視訊傳輸採用伺服器/客戶機模式,利用VC 實現。其工作流程如圖1所示。

圖1 實時視訊傳輸工作流程

  視訊採集採用AVICap從視訊採集卡捕獲視訊影象,得到的是點陣圖型式的視訊幀,然後用Divx編碼器進行壓縮,通過Winsock實現壓縮後的視訊資料在區域網中的實時傳輸,接收完的資料交給Divx解碼器解壓,最後實現視訊顯示。

  在VC 中,採用VFW技術,客戶端通過capSetCallbackOnFrame()註冊回撥函式,當採集卡採集到一幅影象後,系統就會自動呼叫回撥函式,然後再回調函式中使用ICSeqCompressFrame()函式進行壓縮。然後再通過Winsock將壓縮後的資料傳送到伺服器端。伺服器端接收完一幀以後,交給ICDecompress()解壓,最後用SetDIBitsToDevice()將影象顯示出來。

  1、視訊幀的組建

  視訊採集的資料是點陣圖型式的視訊幀,Divx編碼器壓縮以後形成以幀為格式的Mpeg4流。Divx解碼器也是以幀的格式解壓。所以提出以幀為單位傳送視訊資料流。為了在接收端能夠方便地提取出一幀,提出如圖2所示的格式組建幀。


幀開始標誌

幀大小

幀編號

幀型別

幀資料
圖2 視訊幀格式

  完整的一幀由5個欄位組成,各個欄位的意義如下:幀開始標誌,標誌著一幀地開始,佔用4個位元組的空間。不妨設為0xffffffff。幀大小,表示整個幀的大小,包括5個欄位的大小,佔用4個位元組的空間。幀編號,表示幀的順序編號,佔用4個位元組的空間。幀型別,標誌此幀是否是關鍵幀,佔用1個位元組的空間。幀資料,存放壓縮後一幀的完整資料。

  2、視訊幀的傳送

  實時視訊傳輸為了實時,要不斷地將壓縮好的資料傳送到接受端。所以在傳送端建立一個執行緒,專門用來發送資料。同時主執行緒仍然不停的採集資料並進行壓縮。傳送執行緒的工作流程如圖3所示。

圖3 傳送執行緒工作流程

  不妨假設建立的執行緒名為sendThread,核心程式碼實現如下:

while(1)
{
 isOK=true; //準備就緒
 SuspendThread(sendThread); //掛起執行緒
 isOK=false; //執行緒正在傳送資料
 int length=frameLength; //待發資料長度
 if(length<50000) {//判斷資料是否正常
  int n=0;
  int sendCount=0;
  while(length>0) {
   n=send(sock,(char*)imageBuf sendCount,length,0); //傳送資料,
   //imageBuf是指標,指向待發資料幀
   if(n==SOCKET_ERROR) //網路出現異常,則退出執行緒
    break;
   length-=n;
   sendCount =n;
  }
 }
}


  執行緒中傳送的資料幀是按照上一節中的方法組建好的資料幀。這種方法能夠保證正在傳送的當前幀能夠完整地到達接收端。

  注意此執行緒中剛開始或者每當傳送完一幀以後,執行緒就轉到掛起狀態,等待外界喚醒。這個任務由回撥函式完成,在回撥函式中,判定如果傳送執行緒準備就緒(處於掛起狀態),則進行影象壓縮,然後喚醒執行緒傳送壓縮完的資料,否則直接跳出,等待下一次呼叫回撥函式,這種策略稱之為“停等”策略,在後面有詳細介紹。

3、視訊幀的接收

  接收端最重要的是從接受的資料流中提取出完整的一幀。方法的思想是:首先從資料流中尋找幀開始標誌,再從緊挨後面的資料中提取出幀的大小,然後再從接收緩衝區中讀入該幀剩餘的資料。再尋找下一幀的開始標誌,如此往復。圖4是接收端的工作流程。

  同樣接收端建立一個執行緒專門用來執行資料接收。不妨假設執行緒名為recThread,核心程式碼實現如下:

while(temp!=SOCKET_ERROR)
{
 if(!isStart) {//幀資料是否開始,true表示開始
  if(endNum>3) //endNum紀錄當前接收未處理的資料
   endNum=0;
  temp=recv(clisock,(char*)(recBuf endNum),1000,0);//從緩衝區讀取資料
  startPos=serchStr(temp endNum); //查詢幀開始標誌
  if(startPos!=-1) {
   isStart=true;
   endNum=temp endNum-startPos-4;
   memcpy(imageBuf,recBuf startPos 4,endNum); //儲存幀資料
  }
  else{
   memcpy(recBuf,recBuf temp endNum-3,3);//儲存最後三個位元組的資料
   endNum=3;
  }
 }
 else{
  if(endNum<4) {//判定緊跟開始標誌的資料,如果小於4表示不能獲得幀大小
   temp=recv(clisock,(char*)(recBuf),1000,0); //讀入資料
   memcpy(imageBuf endNum,recBuf,temp);//儲存資料
   endNum =temp;
   if(endNum<4)
    continue;
   frameSize= *((int*)imageBuf);//獲得幀大小
   if(frameSize<500 || frameSize>50000) {//異常處理(幀大小非法)
    isStart = false; //丟棄資料重新查詢幀開始標誌
    endNum = 0;
    continue;
   }
   frameSize-=endNum 4;
  }
  else{
   while(frameSize>0&&temp!=SOCKET_ERROR) {//獲得完整幀的剩餘資料
    temp=recv(clisock,(char*)(imageBuf endNum),frameSize,0);
    endNum =temp;
    frameSize-=temp;
   }
   if(frameSize<=0) {//幀結束置位,解壓
    isStart=false;
    endNum=0;
    deCompress();//判斷資料的有效性,呼叫ICDecompress進行解壓
   }
  }
 }
}

  以上程式執行的結果是將完整的一幀(除幀開始標誌)儲存在imageBuf中。

  4、“停等”控制策略

  如果區域網通訊速率很高,而且工作穩定,則按照以上說的方法進行實時視訊傳輸,不需要任何控制策略,就可以達到非常好的效果。但是在很多情況下,網路會出現異常,這樣會導致資料傳輸率明顯下降,造成傳送端資料積壓,等待發送的資料不能正常發出去。此時就要採取一定的策略來控制傳送端,以達到實時性的要求。

  上文傳送程式中,變數isOK是用來表示傳送端當前幀有沒有發完,如果發完則置為true,同時也表示傳送端準備就緒,可以繼續傳送資料,否則為false。那麼可以用isOK來通知視訊採集和壓縮執行緒,如果isOK為true,則可以採集視訊並且壓縮,然後喚醒傳送執行緒繼續傳送新來的幀資料,否則一直等待,直到網路可以繼續傳送資料(isOK為true)。當然,視訊採集一直不停的進行,那麼當網路發生資料堵塞時,只要不讓編碼器進行壓縮則可解決;當網路恢復正常時,繼續進行壓縮傳輸,換句話說,當網路發生堵塞時,直接拋棄等待發送的幀,保證一旦網路恢復時,傳送最新的壓縮幀。當然要保證一旦有一幀開始傳送,就要將其完全發出。

  按照這樣的“停等”策略進行實時視訊傳輸,只會帶來一個問題:當網路質量差時,接收端畫面中的移動目標會出現瞬間移動的現象。但是這種策略會保證不會出現重影,抖動,花屏等現象。

  結論

  本文提出的實時視訊傳輸方案在100M的區域網、10M區域網和11M無線區域網中進行了測試。測試時讓一個目標在鏡頭前(傳送端)移動,觀察接收端視訊的顯示。在不同的區域網中進行了多次測試,每次測試時間從10分鐘到30分鐘不等,並且改變目標的運動速度進行實驗。最後將資料彙總,得出統計結果。測試結果如表1所示。

  表1 不同區域網下的測試結果

劇烈運動

正常運動

緩慢運動

100M區域網

影象清晰,很流暢

影象清晰,很流暢

影象清晰,很流暢

10M區域網

偶爾出現停頓,丟幀率1%左右

影象清晰,人眼感覺流暢

影象清晰,很流暢

11M無線區域網

經常出現停頓,丟幀率5%-6%

經常出現停頓,丟幀率2%-3%

偶爾出現停頓,丟幀率1%左右


  其中,

  注:11M無線網絡卡是通過USB1.0介面和PC機連線的,如果採用USB2.0介面效果會更好。

  從實際測試的結果看,效果是良好的,除了出現瞬間移動外,影象能夠保持清晰,消除了由於網路質量差而導致的重影、抖動等現象,對於不同的區域網都能滿足實時傳輸的要求