一、流媒體簡介 隨著Internet 的日益普及,在網絡上傳輸的數據已經不再局限于文字和圖形,而是逐漸向聲音和視頻等多媒體格式過渡。目前在網絡上傳輸音頻/視頻(Audio /Video,簡稱A/V)等多媒體文件時,基本上只有下載和流式傳輸兩種選擇。通常說來,A/V文件占據的存儲空間都比較大,在帶寬受限的網絡環(huán)境中下 載可能要耗費數分鐘甚至數小時,所以這種處理方法的延遲很大。如果換用流式傳輸的話,聲音、影像、動畫等多媒體文件將由專門的流媒體服務器負責向用戶連 續(xù)、實時地發(fā)送,這樣用戶可以不必等到整個文件全部下載完畢,而只需要經過幾秒鐘的啟動延時就可以了,當這些多媒體數據在客戶機上播放時,文件的剩余部分 將繼續(xù)從流媒體服務器下載。 流(Streaming)是近年在Internet上出現的新概念,其定義非常廣泛,主要是指通過網絡傳輸 多媒體數據的技術總稱。流媒體包含廣義和狹義兩種內涵:廣義上的流媒體指的是使音頻和視頻形成穩(wěn)定和連續(xù)的傳輸流和回放流的一系列技術、方法和協(xié)議的總 稱,即流媒體技術;狹義上的流媒體是相對于傳統(tǒng)的下載-回放方式而言的,指的是一種從Internet上獲取音頻和視頻等多媒體數據的新方法,它能夠支持 多媒體數據流的實時傳輸和實時播放。通過運用流媒體技術,服務器能夠向客戶機發(fā)送穩(wěn)定和連續(xù)的多媒體數據流,客戶機在接收數據的同時以一個穩(wěn)定的速率回 放,而不用等數據全部下載完之后再進行回放。 由于受網絡帶寬、計算機處理能力和協(xié)議規(guī)范等方面的限制,要想從Internet上下載大 量的音頻和視頻數據,無論從下載時間和存儲空間上來講都是不太現實的,而流媒體技術的出現則很好地解決了這一難題。目前實現流媒體傳輸主要有兩種方法:順 序流(progressive streaming)傳輸和實時流(realtime streaming)傳輸,它們分別適合于不同的應用場合。 順序流傳輸 順 序流傳輸采用順序下載的方式進行傳輸,在下載的同時用戶可以在線回放多媒體數據,但給定時刻只能觀看已經下載的部分,不能跳到尚未下載的部分,也不能在傳 輸期間根據網絡狀況對下載速度進行調整。由于標準的HTTP服務器就可以發(fā)送這種形式的流媒體,而不需要其他特殊協(xié)議的支持,因此也常常被稱作 HTTP流式傳輸。順序流式傳輸比較適合于高質量的多媒體片段,如片頭、片尾或者廣告等。 實時流傳輸 實時流式傳輸保 證媒體信號帶寬能夠與當前網絡狀況相匹配,從而使得流媒體數據總是被實時地傳送,因此特別適合于現場事件。實時流傳輸支持隨機訪問,即用戶可以通過快進或 者后退操作來觀看前面或者后面的內容。從理論上講,實時流媒體一經播放就不會停頓,但事實上仍有可能發(fā)生周期性的暫?,F象,尤其是在網絡狀況惡化時更是如 此。與順序流傳輸不同的是,實時流傳輸需要用到特定的流媒體服務器,而且還需要特定網絡協(xié)議的支持。 二、流媒體協(xié)議 實時傳輸協(xié)議(Real-time Transport Protocol,RTP)是 在Internet上處理多媒體數據流的一種網絡協(xié)議,利用它能夠在一對一(unicast,單播)或者一對多(multicast,多播)的網絡環(huán)境中 實現傳流媒體數據的實時傳輸。RTP通常使用UDP來進行多媒體數據的傳輸,但如果需要的話可以使用TCP或者 ATM等其它協(xié)議,整個RTP協(xié)議由兩個密切相關的部分組成:RTP數據協(xié)議和RTCP控制協(xié)議。實時流協(xié)議(Real Time Streaming Protocol,RTSP)最早由Real Networks和Netscape公司共同提出,它位于RTP和RTCP之上,其目的是希望通過IP網絡有效地傳輸多媒體數據。 2.1 RTP數據協(xié)議 RTP數據協(xié)議負責對流媒體數據進行封包并實現媒體流的實時傳輸,每一個RTP數據報都由頭部(Header)和負載(Payload)兩個部分組成,其中頭部前12個字節(jié)的含義是固定的,而負載則可以是音頻或者視頻數據。RTP數據報的頭部格式如圖1所示:
其中比較重要的幾個域及其意義如下:
2.2 RTCP控制協(xié)議 RTCP 控制協(xié)議需要與RTP數據協(xié)議一起配合使用,當應用程序啟動一個RTP會話時將同時占用兩個端口,分別供RTP和RTCP使用。RTP本身并不能為按序傳 輸數據包提供可靠的保證,也不提供流量控制和擁塞控制,這些都由RTCP來負責完成。通常RTCP會采用與RTP相同的分發(fā)機制,向會話中的所有成員周期 性地發(fā)送控制信息,應用程序通過接收這些數據,從中獲取會話參與者的相關資料,以及網絡狀況、分組丟失概率等反饋信息,從而能夠對服務質量進行控制或者對 網絡狀況進行診斷。 RTCP協(xié)議的功能是通過不同的RTCP數據報來實現的,主要有如下幾種類型:
在 一個典型的應用場合下,發(fā)送媒體流的應用程序將周期性地產生發(fā)送端報告SR,該RTCP數據報含有不同媒體流間的同步信息,以及已經發(fā)送的數據報和字節(jié)的 計數,接收端根據這些信息可以估計出實際的數據傳輸速率。另一方面,接收端會向所有已知的發(fā)送端發(fā)送接收端報告RR,該RTCP數據報含有已接收數據報的 最大序列號、丟失的數據報數目、延時抖動和時間戳等重要信息,發(fā)送端應用根據這些信息可以估計出往返時延,并且可以根據數據報丟失概率和時延抖動情況動態(tài) 調整發(fā)送速率,以改善網絡擁塞狀況,或者根據網絡狀況平滑地調整應用程序的服務質量。 2.3 RTSP實時流協(xié)議 作為一個應用層協(xié)議,RTSP提供了一個可供擴展的框架,它的意義在于使得實時流媒體數據的受控和點播變得可能。總的說來,RTSP是一個流媒體表示協(xié)議,主要用來控制具有實時特性的數據發(fā)送,但它本身并不傳輸數據,而是必須依賴于下層傳輸協(xié)議所提供的某些服務。RTSP可以對流媒體提供諸如播放、暫停、快進等操作,它負責定義具體的控制消息、操作方法、狀態(tài)碼等,此外還描述了與RTP間的交互操作。 RTSP 在制定時較多地參考了 HTTP/1.1協(xié)議,甚至許多描述與HTTP/1.1完全相同。RTSP之所以特意使用與HTTP/1.1類似的語法和操作,在很大程度上是為了兼容現 有的Web基礎結構,正因如此,HTTP/1.1的擴展機制大都可以直接引入到RTSP中。 由 RTSP控制的媒體流集合可以用表示描述(Presentation Description)來定義,所謂表示是指流媒體服務器提供給客戶機的一個或者多個媒體流的集合,而表示描述則包含了一個表示中各個媒體流的相關信 息,如數據編碼/解碼算法、網絡地址、媒體流的內容等。 雖然RTSP服務器同樣也使用標識符來區(qū)別每一流連接會話 (Session),但 RTSP連接并沒有被綁定到傳輸層連接(如TCP等),也就是說在整個RTSP連接期間,RTSP用戶可打開或者關閉多個對RTSP服務器的可靠傳輸連接 以發(fā)出RTSP 請求。此外,RTSP連接也可以基于面向無連接的傳輸協(xié)議(如UDP等)。 RTSP協(xié)議目前支持以下操作:
RTP 是目前解決流媒體實時傳輸問題的最好辦法,如果需要在Linux平臺上進行實時流媒體編程,可以考慮使用一些開放源代碼的RTP庫,如LIBRTP、 JRTPLIB等。 JRTPLIB是一個面向對象的RTP庫,它完全遵循RFC 1889設計,在很多場合下是一個非常不錯的選擇,下面就以JRTPLIB為例,講述如何在Linux平臺上運用RTP協(xié)議進行實時流媒體編程。 3.1 環(huán)境搭建 JRTPLIB 是一個用C++語言實現的RTP庫,目前已經可以運行在Windows、Linux、FreeBSD、Solaris、Unix和 VxWorks等多種操作系統(tǒng)上。要為Linux 系統(tǒng)安裝JRTPLIB,首先從JRTPLIB的網站(http: //lumumba./jori/jrtplib/jrtplib.html)下載最新的源碼包,此處使用的是jrtplib- 2.7b.tar.bz2。假設下載后的源碼包保存在/usr/local/src目錄下,執(zhí)行下面的命令可以對其進行解壓縮:
[root@linuxgam jrtplib-2.7b]# ./configure [root@linuxgam jrtplib-2.7b]# make
在 使用JRTPLIB進行實時流媒體數據傳輸之前,首先應該生成RTPSession類的一個實例來表示此次RTP會話,然后調用Create() 方法來對其進行初始化操作。RTPSession類的Create()方法只有一個參數,用來指明此次RTP會話所采用的端口號。清單1給出了一個最簡單 的初始化框架,它只是完成了RTP會話的初始化工作,還不具備任何實際的功能。 代碼清單1:initial.cpp
int main(void) { RTPSession sess; sess.Create(5000); return 0; }
代碼清單2:framework.cpp
#i nclude "rtpsession.h" int main(void) { RTPSession sess; int status; char* msg; sess.Create(6000); //此處是不是應該為status = sess.Create(6000); msg = RTPGetErrorString(status); printf("Error String: %s\\n", msg); return 0; }
當RTP 會話成功建立起來之后,接下去就可以開始進行流媒體數據的實時傳輸了。首先需要設置好數據發(fā)送的目標地址,RTP協(xié)議允許同一會話存在多個目標地址,這可 以通過調用RTPSession類的AddDestination()、DeleteDestination()和 ClearDestinations()方法來完成。例如,下面的語句表示的是讓RTP會話將數據發(fā)送到本地主機的6000端口:
sess.AddDestination(addr, 6000);
int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc) int SendPacket(void *data,int len,unsigned short hdrextID,void *hdrextdata,int numhdrextwords) int SendPacket(void *data,int len,unsigned char pt,bool mark,unsigned long timestampinc,unsigned short hdrextID, void *hdrextdata,int numhdrextwords)
sess.SetDefaultMark(false); sess.SetDefaultTimeStampIncrement(10);
對 于流媒體數據的接收端,首先需要調用RTPSession類的PollData()方法來接收發(fā)送過來的RTP或者RTCP數據報。由于同一個 RTP會話中允許有多個參與者(源),你既可以通過調用RTPSession類的GotoFirstSource()和GotoNextSource() 方法來遍歷所有的源,也可以通過調用RTPSession類的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法來遍歷那些攜帶有數據的源。在從RTP會話中檢測出有效的數據源之后,接下去就可以調用 RTPSession類的GetNextPacket()方法從中抽取RTP數據報,當接收到的RTP數據報處理完之后,一定要記得及時釋放。下面的代碼 示范了該如何對接收到的RTP數據報進行處理:
{ do { RTPPacket *pack; pack = sess.GetNextPacket(); // 處理接收到的數據 delete pack; } while (sess.GotoNextSourceWithData()); }
。RECEIVEMODE_ALL 缺省的接收模式,所有到達的RTP數據報都將被接受; JRTPLIB 是一個高度封裝后的RTP庫,程序員在使用它時很多時候并不用關心RTCP數據報是如何被發(fā)送和接收的,因為這些都可以由 JRTPLIB自己來完成。只要PollData()或者SendPacket()方法被成功調用,JRTPLIB就能夠自動對到達的RTCP數據報進行 處理,并且還會在需要的時候發(fā)送RTCP數據報,從而能夠確保整個RTP會話過程的正確性。 而另一方面,通過調用RTPSession 類提供的SetLocalName()、SetLocalEMail()、 SetLocalLocation()、SetLocalPhone()、SetLocalTool()和SetLocalNote()方法, JRTPLIB又允許程序員對RTP會話的控制信息進行設置。所有這些方法在調用時都帶有兩個參數,其中第一個參數是一個char型的指針,指向將要被設 置的數據;而第二個參數則是一個int型的數值,表明該數據中的前面多少個字符將會被使用。例如下面的語句可以被用來設置控制信息中的電子郵件地址:
3.6 實際應用 最后通過一個簡單的流媒體發(fā)送-接收實例,介紹如何利用JRTPLIB來進行實時流媒體的編程。清單3給出了數據發(fā)送端的完整代碼,它負責向用戶指定的IP地址和端口,不斷地發(fā)送RTP數據包: 代碼清單3:sender.cpp
#i nclude #i nclude "rtpsession.h" // 錯誤處理函數 void checkerror(int err) { if (err < 0) { char* errstr = RTPGetErrorString(err); printf("Error:%s\\n", errstr); exit(-1); } } int main(int argc, char** argv) { RTPSession sess; unsigned long destip; int destport; int portbase = 6000; int status, index; char buffer[128]; if (argc != 3) { printf("Usage: ./sender destip destport\\n"); return -1; } // 獲得接收端的IP地址和端口號 destip = inet_addr(argv[1]); if (destip == INADDR_NONE) { printf("Bad IP address specified.\\n"); return -1; } destip = ntohl(destip); destport = atoi(argv[2]); // 創(chuàng)建RTP會話 status = sess.Create(portbase); checkerror(status); // 指定RTP數據接收端 status = sess.AddDestination(destip, destport); checkerror(status); // 設置RTP會話默認參數 sess.SetDefaultPayloadType(0); sess.SetDefaultMark(false); sess.SetDefaultTimeStampIncrement(10); // 發(fā)送流媒體數據 index = 1; do { sprintf(buffer, "%d: RTP packet", index ++); sess.SendPacket(buffer, strlen(buffer)); printf("Send packet !\\n"); } while(1); return 0; }
代碼清單4:receiver.cpp
#i nclude "rtpsession.h" #i nclude "rtppacket.h" // 錯誤處理函數 void checkerror(int err) { if (err < 0) { char* errstr = RTPGetErrorString(err); printf("Error:%s\\n", errstr); exit(-1); } } int main(int argc, char** argv) { RTPSession sess; int localport; int status; if (argc != 2) { printf("Usage: ./sender localport\\n"); return -1; } // 獲得用戶指定的端口號 localport = atoi(argv[1]); // 創(chuàng)建RTP會話 status = sess.Create(localport); checkerror(status); do { // 接受RTP數據 status = sess.PollData(); // 檢索RTP數據源 if (sess.GotoFirstSourceWithData()) { do { RTPPacket* packet; // 獲取RTP數據報 while ((packet = sess.GetNextPacket()) != NULL) { printf("Got packet !\\n"); // 刪除RTP數據報 delete packet; } } while (sess.GotoNextSourceWithData()); } } while(1); return 0; }
四、小結 隨 著多媒體數據在 Internet上所承擔的作用變得越來越重要,需要實時傳輸音頻和視頻等多媒體數據的場合也將變得越來越多,如IP電話、視頻點播、在線會議等。RTP 是用來在Internet上進行實時流媒體傳輸的一種協(xié)議,目前已經被廣泛地應用在各種場合,JRTPLIB是一個面向對象的RTP封裝庫,利用它可以很 方便地完成Linux平臺上的實時流媒體編程。 五、參考資源 1. 在JRTPLIB的網站http://lumumba./jori/jrtplib/jrtplib.html上,可以下載到JRTPLIB最新的源碼包,并且還能找到一些與RTP相關的資源。 2. 顧淑珍等編著,寬帶增值服務開發(fā)實例,北京:機械工業(yè)出版社,2002 3. 黃永峰等編著,IP網絡多媒體通信技術,北京:人民郵電出版社,2003
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/ipromiseu/archive/2009/09/08/4531613.aspx |
|