利用VC++實現局域網實時傳輸

本文針對不同的局域網,提出一種通用的實時視頻傳輸的解決方案。在使用Divx編解碼的基礎上,提出了從壓縮、組幀、發送到接收、解壓整個流程的思想,具體實施方案和VC++實現核心源代碼以及傳輸控制策略,有效地保證了高質量的實時視頻傳輸。

關鍵詞客戶/服務器;實時視頻傳輸;Divx

引言

在局域網內部實時傳輸視頻已經得到廣泛應用。現在用以傳輸視頻的局域網大多數是有線局域網,因為有線局域網技術成熟,傳輸速度快,穩定性好。但是視頻數據量大,有線網絡也會出現工作不穩定,引起數據堵塞,時間久了會導致嚴重的延遲現象;如果工作的環境不固定,要求移動性,那么就要采用無線網絡,如今無線網卡的工作隨環境的變化而變得不穩定,這樣會導致視頻傳輸的質量大幅度下降,容易引起畫面的重影、抖動、花屏等現象。本文針對不同的局域網,提出一種通用的實時視頻傳輸的解決方案,使用VC++自封裝的WindowsVFWSDK軟件開發包進行二次開發,通過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所示。

視頻采集采用AVICap從視頻采集卡捕獲視頻圖像,得到的是位圖型式的視頻幀,然后用Divx編碼器進行壓縮,通過Winsock實現壓縮后的視頻數據在局域網中的實時傳輸,接收完的數據交給Divx解碼器解壓,最后實現視頻顯示。

在VC++中,采用VFW技術,客戶端通過capSetCallbackOnFrame()注冊回調函數,當采集卡采集到一幅圖像后,系統就會自動調用回調函數,然后再回調函數中使用ICSeqCompressFrame()函數進行壓縮。然后再通過Winsock將壓縮后的數據發送到服務器端。服務器端接收完一幀以后,交給ICDecompress()解壓,最后用SetDIBitsToDevice()將圖像顯示出來。

1、視頻幀的組建

視頻采集的數據是位圖型式的視頻幀,Divx編碼器壓縮以后形成以幀為格式的Mpeg4流。Divx解碼器也是以幀的格式解壓。所以提出以幀為單位發送視頻數據流。為了在接收端能夠方便地提取出一幀,提出如所示的格式組建幀。

幀開始標志

幀大小

幀編號

幀類型

幀數據

視頻幀格式

完整的一幀由5個字段組成,各個字段的意義幀開始標志,標志著一幀地開始,占用4個字節的空間。不妨設為0xffffffff。幀大小,表示整個幀的大小,包括5個字段的大小,占用4個字節的空間。幀編號,表示幀的順序編號,占用4個字節的空間。幀類型,標志此幀是否是關鍵幀,占用1個字節的空間。幀數據,存放壓縮后一幀的完整數據。

2、視頻幀的發送

實時視頻傳輸為了實時,要不斷地將壓縮好的數據發送到接受端。所以在發送端創建一個線程,專門用來發送數據。同時主線程仍然不停的采集數據并進行壓縮。發送線程的工作流程如圖3所示。

不妨假設創建的線程名為sendThread,核心代碼實現

while(1)

{

isOK=true;//準備就緒

SuspendThread(sendThread);//掛起線程

isOK=false;//線程正在發送數據

intlength=frameLength;//待發數據長度

if(length<50000){//判斷數據是否正常

intn=0;

intsendCount=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<500frameSize>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接口效果會更好。

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254209.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254209.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254209.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ASP.NET Web API之消息[攔截]處理(轉)

出處&#xff1a;http://www.cnblogs.com/Leo_wl/p/3238719.html 標題相當難取&#xff0c;內容也許和您想的不一樣&#xff0c;而且網上已經有很多這方面的資料了&#xff0c;我不過是在實踐過程中作下記錄。廢話少說&#xff0c;直接開始。 Exception 當服務端拋出未處理異常…

無人駕駛遇見人工智能 百度將推有“大腦”的汽車

在日前舉行的中國云計算大會&#xff0c;百度高級副總裁、技術戰略委員會主席王勁表示&#xff0c;百度將在今年下半年推出無人駕駛汽車。不過&#xff0c;百度自己并不會造車&#xff0c;它將與第三方汽車廠商合作制造。據介紹&#xff0c;百度將利用現有的大數據、地圖、人工…

AdlinkMotionCardLibrary函數C++

#include "stdafx.h" #include "AdlinkMotionCardLibrary.h"extern "C" _declspec(dllexport) bool _stdcall MotionCardIni(I32& BoardId_InBits, I32 Mode) { try{//mode0&#xff1a;&#xff1a; 系統指定卡號 mode1&#xff1a;&am…

查看表的結構

describe 表名轉載于:https://www.cnblogs.com/dengyg200891/p/5966565.html

定制一個網絡文件系統

定制一個網絡文件系統【把pc上的文件系統掛接到開發板上面】 1、修改exports文件【PC上】一定要修改&#xff0c;否則不會成功 vi /etc/exports 修改為 /空格* 并保存 2、設置開發板上的IP地址 ifconfig eth0 192.168.0.11 up 3、設置PC上的IP地址 ifconfig et…

創建Hbase Hive外部表報錯: Unable to determine ZooKeeper ensemble

創建HBase的Hive外部表1: create external table ttt(rowkey string,info map<string,string>)STORED BY org.apache.hadoop.hive.hbase.HBaseStorageHandler WITH SERDEPROPERTIES ("hbase.columns.mapping" ":key,info:") TBLPROPERTIES ("h…

死磕算法之快速排序

版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。博客源地址為zhixiang.org.cn https://blog.csdn.net/myFirstCN/article/details/80851021 學習更多算法系列請參考文章&#xff1a;死磕算法之匯總篇 快速排序是一個運用了分治法和遞歸算法的排序方…

九點標定進行仿射變換halcon仿真代碼

篩選出來的點得坐標已經顯示在PxRow、PxColunm里邊 * Image Acquisition 01: Code generated by Image Acquisition 01 read_image (Image, C:/Users/Administrator/Desktop/標定板圖片.png) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHand…

用SQL語句添加刪除修改字段_常用SQL

1.增加字段 alter table docdsp add dspcodechar(200)2.刪除字段 ALTER TABLE table_NAME DROP COLUMNcolumn_NAME3.修改字段類型 ALTER TABLE table_name ALTER COLUMNcolumn_name new_data_type4.sp_rename 改名 EXEC sp_rename [dbo].[Table_1].[fi…

DAVINCI開發原理之三----達芬奇編解碼引擎Codec Engine(CE)

DaVinci是DSP和ARM 雙核架構的SOC芯片。對芯片與外界的交互通過ARM端的Montavista Linux和相關驅動與應用程序來管理&#xff0c; DSP端只處理編解碼相關的算法。DSP和ARM之間的通訊和交互是通過引擎(Engine)和服務器(Server)來完成的。1. 編解碼引擎(Codec Engine) a. 核心引…

Windows操作系統安全加固

本文檔旨在指導系統管理人員或安全檢查人員進行Windows操作系統的安全合規性檢查和配置。 1. 賬戶管理和認證授權 1.1 賬戶 默認賬戶安全 禁用Guest賬戶。禁用或刪除其他無用賬戶&#xff08;建議先禁用賬戶三個月&#xff0c;待確認沒有問題后刪除。&#xff09;操作步驟 打開…

ios修改了coredata數據結構后,更新安裝會閃退

如果iOS App 使用到CoreData&#xff0c;并且在上一個版本上有數據庫更新&#xff08;新增表、字段等操作&#xff09;&#xff0c;那在覆蓋安裝程序時就要進行CoreData數據庫的遷移&#xff0c;具體操作如下&#xff1a; 1.選中你的mydata.xcdatamodeld文件&#xff0c;選擇菜…

TI DAVINCI開發原理(總共5部分)

2011-06-03 11:14:17| 分類&#xff1a; TI 達芬奇視頻處 | 標簽&#xff1a; |字號大中小訂閱 DAVINCI開發原理之一----ARM端開發環境的建立(DVEVM) 1. 對DAVINCI平臺&#xff0c;TI在硬件上給予雙核架構強有力的支撐&#xff0c;在DSP端用DSP/BIOS來支持音視頻算法的運行…

數據庫代碼寫法

1.創建數據庫create database test2; 2.刪除數據庫drop database test2; 3.創建表 create table ceshi (ids int auto_increment primary key,uid varchar(20),name varchar(20),class varchar(20),foreign key (class) references class(code) ); create table class (code …

random庫的使用

有關Python中random標準庫的使用 Python中關于隨機值的部分&#xff0c;借助的是根據當前的隨機種子&#xff0c;通過梅森旋轉算法&#xff0c;生成一段隨機序列。 基本隨機函數 random.seed(aNone)初始化給定的隨機種子&#xff0c;默認值為當前的系統時間。 random.random()生…

ThinkPHP--欄目增刪改查ADSF

<?php /*** 欄目發布*/ //V層&#xff0c;action/name值 action " :U( Admin/Cat/Cateadd )";/*** 添加欄目數據* C層&#xff0c;寫相應的方法進行數據添加*/ public function add(){if(!IS_POST){$this->display();}else{//var_dump($_POST);$catModelD…

模擬查找晶元的位置

通過模板匹配找到所有模板位置&#xff0c;并且當單擊某個模板時&#xff0c;選中某個模板 read_image (Image, C:/Users/22967/Desktop/晶圓找位置/0.bmp) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)* draw_cir…

JavaScript常用函數之Eval()使用

eval() 功能&#xff1a;首先解釋Javascript代碼 然后執行它 用法&#xff1a;Eval&#xff08;codeString&#xff09; codeString是包含有javascript語句的字符串&#xff0c;在eval之后使用Javascript引擎編譯。即&#xff1a;eval函數可以把一個字符串當作一個javascript表…

初探數位dp

前言&#xff1a;這是蒟蒻第一次寫算法系列&#xff0c;請諸位大佬原諒文筆與排版。 一、導入 在刷題的時候&#xff0c;我們有時會見到這樣一類問題&#xff1a;在區間$[l,r]$內&#xff0c;共有多少個整數滿足某種條件。如果$l$和$r$間的差很小&#xff0c;我們可以考慮暴力枚…

Java演示手機發送短信驗證碼功能實現

我們這里采用阿里大于的短信API 第一步&#xff1a;登陸阿里大于&#xff0c;下載阿里大于的SDK a、在阿里大于上創建自己的應用 b、點擊配置管理中的驗證碼&#xff0c;先添加簽名&#xff0c;再配置短信模板 第二步&#xff1a;解壓相關SDK&#xff0c;第一個為jar包&#xf…