利用VC++實現區域網路即時視頻傳輸

來源:互聯網
上載者:User
  • 摘要 本文針對不同的區域網路,提出一種通用的即時視頻傳輸的解決方案。在使用Divx編解碼的基礎上,提出了從壓縮、組幀、發送到接收、解壓整個流程的思想,具體實施方案和VC++實現核心原始碼以及傳輸控制策略,有效地保證了高品質的即時視頻傳輸。
  關鍵詞 客戶/伺服器;即時視頻傳輸;Divx

  引言

  在區域網路內部即時傳輸視頻已經得到廣泛應用。現在用以傳輸視頻的區域網路大多數是有線區域網路,因為有線區域網路技術成熟,傳輸速度快,穩定性好。但是視頻資料量大,有線網路也會出現工作不穩定,引起資料堵塞,時間久了會導致嚴重的延遲現象;如果工作的環境不固定,要求移動性,那麼就要採用無線網路,如今無線網卡的工作隨環境的變化而變得不穩定,這樣會導致視頻傳輸的品質大幅度下降,容易引起畫面的重影、抖動、花屏等現象。本文針對不同的區域網路,提出一種通用的即時視頻傳輸的解決方案,使用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介面效果會更好。

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.