選擇java 進(jìn)入自由開(kāi)放的國(guó)度

          隨筆 - 49, 文章 - 3, 評(píng)論 - 154, 引用 - 1
          數(shù)據(jù)加載中……

          linux下RTP編程(使用JRTPLIB)(轉(zhuǎn))

          流媒體指的是在網(wǎng)絡(luò)中使用流技術(shù)傳輸?shù)倪B續(xù)時(shí)基媒體,其特點(diǎn)是在播放前不需要下載整個(gè)文件,而是采用邊下載邊播放的方式,它是視頻會(huì)議、IP電話等應(yīng)用場(chǎng) 合的技術(shù)基礎(chǔ)。RTP是進(jìn)行實(shí)時(shí)流媒體傳輸?shù)臉?biāo)準(zhǔn)協(xié)議和關(guān)鍵技術(shù),本文介紹如何在Linux下利用JRTPLIB進(jìn)行實(shí)時(shí)流媒體編程。
          ?
          一、流媒體簡(jiǎn)介
          ??????? 隨著Internet的日益普及,在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)已經(jīng)不再局限于文字和圖形,而是逐漸向聲音和視頻等多媒體格式過(guò)渡。目前在網(wǎng)絡(luò)上傳輸音頻/視頻 (Audio/Video,簡(jiǎn)稱(chēng)A/V)等多媒體文件時(shí),基本上只有下載和流式傳輸兩種選擇。通常說(shuō)來(lái),A/V文件占據(jù)的存儲(chǔ)空間都比較大,在帶寬受限的 網(wǎng)絡(luò)環(huán)境中下載可能要耗費(fèi)數(shù)分鐘甚至數(shù)小時(shí),所以這種處理方法的延遲很大。如果換用流式傳輸?shù)脑挘曇簟⒂跋瘛?dòng)畫(huà)等多媒體文件將由專(zhuān)門(mén)的流媒體服務(wù)器負(fù) 責(zé)向用戶連續(xù)、實(shí)時(shí)地發(fā)送,這樣用戶可以不必等到整個(gè)文件全部下載完畢,而只需要經(jīng)過(guò)幾秒鐘的啟動(dòng)延時(shí)就可以了,當(dāng)這些多媒體數(shù)據(jù)在客戶機(jī)上播放時(shí),文件 的剩余部分將繼續(xù)從流媒體服務(wù)器下載。

          ??????? 流(Streaming)是近年在Internet上出現(xiàn)的新概念,其定義非常廣泛,主要是指通過(guò)網(wǎng)絡(luò)傳輸多媒體數(shù)據(jù)的技術(shù)總稱(chēng)。流媒體包含廣義和狹義兩 種內(nèi)涵:廣義上的流媒體指的是使音頻和視頻形成穩(wěn)定和連續(xù)的傳輸流和回放流的一系列技術(shù)、方法和協(xié)議的總稱(chēng),即流媒體技術(shù);狹義上的流媒體是相對(duì)于傳統(tǒng)的 下載-回放方式而言的,指的是一種從Internet上獲取音頻和視頻等多媒體數(shù)據(jù)的新方法,它能夠支持多媒體數(shù)據(jù)流的實(shí)時(shí)傳輸和實(shí)時(shí)播放。通過(guò)運(yùn)用流媒 體技術(shù),服務(wù)器能夠向客戶機(jī)發(fā)送穩(wěn)定和連續(xù)的多媒體數(shù)據(jù)流,客戶機(jī)在接收數(shù)據(jù)的同時(shí)以一個(gè)穩(wěn)定的速率回放,而不用等數(shù)據(jù)全部下載完之后再進(jìn)行回放。

          由 于受網(wǎng)絡(luò)帶寬、計(jì)算機(jī)處理能力和協(xié)議規(guī)范等方面的限制,要想從Internet上下載大量的音頻和視頻數(shù)據(jù),無(wú)論從下載時(shí)間和存儲(chǔ)空間上來(lái)講都是不太現(xiàn)實(shí) 的,而流媒體技術(shù)的出現(xiàn)則很好地解決了這一難題。目前實(shí)現(xiàn)流媒體傳輸主要有兩種方法:順序流(progressive streaming)傳輸和實(shí)時(shí)流(realtime streaming)傳輸,它們分別適合于不同的應(yīng)用場(chǎng)合。

          順序流傳輸

          ??????? 順序流傳輸采用順序下載的方式進(jìn)行傳輸,在下載的同時(shí)用戶可以在線回放多媒體數(shù)據(jù),但給定時(shí)刻只能觀看已經(jīng)下載的部分,不能跳到尚未下載的部分,也不能在 傳輸期間根據(jù)網(wǎng)絡(luò)狀況對(duì)下載速度進(jìn)行調(diào)整。由于標(biāo)準(zhǔn)的HTTP服務(wù)器就可以發(fā)送這種形式的流媒體,而不需要其他特殊協(xié)議的支持,因此也常常被稱(chēng)作HTTP 流式傳輸。順序流式傳輸比較適合于高質(zhì)量的多媒體片段,如片頭、片尾或者廣告等。

          實(shí)時(shí)流傳輸

          ??????? 實(shí)時(shí)流式傳輸保證媒體信號(hào)帶寬能夠與當(dāng)前網(wǎng)絡(luò)狀況相匹配,從而使得流媒體數(shù)據(jù)總是被實(shí)時(shí)地傳送,因此特別適合于現(xiàn)場(chǎng)事件。實(shí)時(shí)流傳輸支持隨機(jī)訪問(wèn),即用戶 可以通過(guò)快進(jìn)或者后退操作來(lái)觀看前面或者后面的內(nèi)容。從理論上講,實(shí)時(shí)流媒體一經(jīng)播放就不會(huì)停頓,但事實(shí)上仍有可能發(fā)生周期性的暫停現(xiàn)象,尤其是在網(wǎng)絡(luò)狀 況惡化時(shí)更是如此。與順序流傳輸不同的是,實(shí)時(shí)流傳輸需要用到特定的流媒體服務(wù)器,而且還需要特定網(wǎng)絡(luò)協(xié)議的支持。

          二、流媒體協(xié)議
          實(shí) 時(shí)傳輸協(xié)議(Real-time Transport Protocol,PRT)是在Internet上處理多媒體數(shù)據(jù)流的一種網(wǎng)絡(luò)協(xié)議,利用它能夠在一對(duì)一(unicast,單播)或者一對(duì)多 (multicast,多播)的網(wǎng)絡(luò)環(huán)境中實(shí)現(xiàn)傳流媒體數(shù)據(jù)的實(shí)時(shí)傳輸。RTP通常使用UDP來(lái)進(jìn)行多媒體數(shù)據(jù)的傳輸,但如果需要的話可以使用TCP或者 ATM等其它協(xié)議,整個(gè)RTP協(xié)議由兩個(gè)密切相關(guān)的部分組成:RTP數(shù)據(jù)協(xié)議和RTP控制協(xié)議。實(shí)時(shí)流協(xié)議(Real Time Streaming Protocol,RTSP)最早由Real Networks和Netscape公司共同提出,它位于RTP和RTCP之上,其目的是希望通過(guò)IP網(wǎng)絡(luò)有效地傳輸多媒體數(shù)據(jù)。

          2.1 RTP數(shù)據(jù)協(xié)議

          RTP數(shù)據(jù)協(xié)議負(fù)責(zé)對(duì)流媒體數(shù)據(jù)進(jìn)行封包并實(shí)現(xiàn)媒體流的實(shí)時(shí)傳輸,每一個(gè)RTP數(shù)據(jù)報(bào)都由頭部(Header)和負(fù)載(Payload)兩個(gè)部分組成,其中頭部前12個(gè)字節(jié)的含義是固定的,而負(fù)載則可以是音頻或者視頻數(shù)據(jù)。RTP數(shù)據(jù)報(bào)的頭部格式如圖1所示:



          其中比較重要的幾個(gè)域及其意義如下:

          • CSRC記數(shù)(CC)   表示CSRC標(biāo)識(shí)的數(shù)目。CSRC標(biāo)識(shí)緊跟在RTP固定頭部之后,用來(lái)表示RTP數(shù)據(jù)報(bào)的來(lái)源,RTP協(xié)議允許在同一個(gè)會(huì)話中存在多個(gè)數(shù)據(jù)源,它們可以 通過(guò)RTP混合器合并為一個(gè)數(shù)據(jù)源。例如,可以產(chǎn)生一個(gè)CSRC列表來(lái)表示一個(gè)電話會(huì)議,該會(huì)議通過(guò)一個(gè) RTP混合器將所有講話者的語(yǔ)音數(shù)據(jù)組合為一個(gè)RTP數(shù)據(jù)源。
          • 負(fù)載類(lèi)型(PT)  標(biāo)明RTP負(fù)載的格式,包括所采用的編碼算法、采樣頻率、承載通道等。例如,類(lèi)型2表明該RTP數(shù)據(jù)包中承載的是用ITU G.721算法編碼的語(yǔ)音數(shù)據(jù),采樣頻率為8000Hz,并且采用單聲道。
          • 序列號(hào)  用來(lái)為接收方提供探測(cè)數(shù)據(jù)丟失的方法,但如何處理丟失的數(shù)據(jù)則是應(yīng)用程序自己的事情,RTP協(xié)議本身并不負(fù)責(zé)數(shù)據(jù)的重傳。
          • 時(shí)間戳  記錄了負(fù)載中第一個(gè)字節(jié)的采樣時(shí)間,接收方能夠時(shí)間戳能夠確定數(shù)據(jù)的到達(dá)是否受到了延遲抖動(dòng)的影響,但具體如何來(lái)補(bǔ)償延遲抖動(dòng)則是應(yīng)用程序自己的事情。
          從RTP 數(shù)據(jù)報(bào)的格式不難看出,它包含了傳輸媒體的類(lèi)型、格式、序列號(hào)、時(shí)間戳以及是否有附加數(shù)據(jù)等信息,這些都為實(shí)時(shí)的流媒體傳輸提供了相應(yīng)的基礎(chǔ)。RTP協(xié)議 的目的是提供實(shí)時(shí)數(shù)據(jù)(如交互式的音頻和視頻)的端到端傳輸服務(wù),因此在RTP中沒(méi)有連接的概念,它可以建立在底層的面向連接或面向非連接的傳輸協(xié)議之 上;RTP也不依賴于特別的網(wǎng)絡(luò)地址格式,而僅僅只需要底層傳輸協(xié)議支持組幀(Framing)和分段(Segmentation)就足夠了;另外RTP 本身還不提供任何可靠性機(jī)制,這些都要由傳輸協(xié)議或者應(yīng)用程序自己來(lái)保證。在典型的應(yīng)用場(chǎng)合下,RTP 一般是在傳輸協(xié)議之上作為應(yīng)用程序的一部分加以實(shí)現(xiàn)的,如圖2所示:


          2.2 RTCP控制協(xié)議

          RTCP 控制協(xié)議需要與RTP數(shù)據(jù)協(xié)議一起配合使用,當(dāng)應(yīng)用程序啟動(dòng)一個(gè)RTP會(huì)話時(shí)將同時(shí)占用兩個(gè)端口,分別供RTP 和RTCP使用。RTP本身并不能為按序傳輸數(shù)據(jù)包提供可靠的保證,也不提供流量控制和擁塞控制,這些都由RTCP來(lái)負(fù)責(zé)完成。通常RTCP會(huì)采用與 RTP相同的分發(fā)機(jī)制,向會(huì)話中的所有成員周期性地發(fā)送控制信息,應(yīng)用程序通過(guò)接收這些數(shù)據(jù),從中獲取會(huì)話參與者的相關(guān)資料,以及網(wǎng)絡(luò)狀況、分組丟失概率 等反饋信息,從而能夠?qū)Ψ?wù)質(zhì)量進(jìn)行控制或者對(duì)網(wǎng)絡(luò)狀況進(jìn)行診斷。

          RTCP協(xié)議的功能是通過(guò)不同的RTCP數(shù)據(jù)報(bào)來(lái)實(shí)現(xiàn)的,主要有如下幾種類(lèi)型:

          • SR  發(fā)送端報(bào)告,所謂發(fā)送端是指發(fā)出RTP數(shù)據(jù)報(bào)的應(yīng)用程序或者終端,發(fā)送端同時(shí)也可以是接收端。
          • RR  接收端報(bào)告,所謂接收端是指僅接收但不發(fā)送RTP數(shù)據(jù)報(bào)的應(yīng)用程序或者終端。
          • SDES  源描述,主要功能是作為會(huì)話成員有關(guān)標(biāo)識(shí)信息的載體,如用戶名、郵件地址、電話號(hào)碼等,此外還具有向會(huì)話成員傳達(dá)會(huì)話控制信息的功能。
          • BYE  通知離開(kāi),主要功能是指示某一個(gè)或者幾個(gè)源不再有效,即通知會(huì)話中的其他成員自己將退出會(huì)話。
          • APP  由應(yīng)用程序自己定義,解決了RTCP的擴(kuò)展性問(wèn)題,并且為協(xié)議的實(shí)現(xiàn)者提供了很大的靈活性。
          RTCP數(shù)據(jù)報(bào)攜帶有服務(wù)質(zhì)量監(jiān)控的必要信息,能夠?qū)Ψ?wù)質(zhì)量進(jìn)行動(dòng)態(tài)的調(diào)整,并能夠?qū)W(wǎng)絡(luò)擁塞進(jìn)行有效的控制。由于RTCP數(shù)據(jù)報(bào)采用的是多播方式,因此會(huì)話中的所有成員都可以通過(guò)RTCP數(shù)據(jù)報(bào)返回的控制信息,來(lái)了解其他參與者的當(dāng)前情況。

          在 一個(gè)典型的應(yīng)用場(chǎng)合下,發(fā)送媒體流的應(yīng)用程序?qū)⒅芷谛缘禺a(chǎn)生發(fā)送端報(bào)告SR,該RTCP數(shù)據(jù)報(bào)含有不同媒體流間的同步信息,以及已經(jīng)發(fā)送的數(shù)據(jù)報(bào)和字節(jié)的 計(jì)數(shù),接收端根據(jù)這些信息可以估計(jì)出實(shí)際的數(shù)據(jù)傳輸速率。另一方面,接收端會(huì)向所有已知的發(fā)送端發(fā)送接收端報(bào)告RR,該RTCP數(shù)據(jù)報(bào)含有已接收數(shù)據(jù)報(bào)的 最大序列號(hào)、丟失的數(shù)據(jù)報(bào)數(shù)目、延時(shí)抖動(dòng)和時(shí)間戳等重要信息,發(fā)送端應(yīng)用根據(jù)這些信息可以估計(jì)出往返時(shí)延,并且可以根據(jù)數(shù)據(jù)報(bào)丟失概率和時(shí)延抖動(dòng)情況動(dòng)態(tài) 調(diào)整發(fā)送速率,以改善網(wǎng)絡(luò)擁塞狀況,或者根據(jù)網(wǎng)絡(luò)狀況平滑地調(diào)整應(yīng)用程序的服務(wù)質(zhì)量。

          2.3 RTSP實(shí)時(shí)流協(xié)議

          作 為一個(gè)應(yīng)用層協(xié)議,RTSP提供了一個(gè)可供擴(kuò)展的框架,它的意義在于使得實(shí)時(shí)流媒體數(shù)據(jù)的受控和點(diǎn)播變得可能。總的說(shuō)來(lái),RTSP是一個(gè)流媒體表示協(xié)議, 主要用來(lái)控制具有實(shí)時(shí)特性的數(shù)據(jù)發(fā)送,但它本身并不傳輸數(shù)據(jù),而是必須依賴于下層傳輸協(xié)議所提供的某些服務(wù)。RTSP 可以對(duì)流媒體提供諸如播放、暫停、快進(jìn)等操作,它負(fù)責(zé)定義具體的控制消息、操作方法、狀態(tài)碼等,此外還描述了與RTP間的交互操作。

          RTSP 在制定時(shí)較多地參考了HTTP/1.1協(xié)議,甚至許多描述與HTTP/1.1完全相同。RTSP之所以特意使用與HTTP/1.1類(lèi)似的語(yǔ)法和操作,在很 大程度上是為了兼容現(xiàn)有的Web基礎(chǔ)結(jié)構(gòu),正因如此,HTTP/1.1的擴(kuò)展機(jī)制大都可以直接引入到RTSP 中。

          由RTSP 控制的媒體流集合可以用表示描述(Presentation Description)來(lái)定義,所謂表示是指流媒體服務(wù)器提供給客戶機(jī)的一個(gè)或者多個(gè)媒體流的集合,而表示描述則包含了一個(gè)表示中各個(gè)媒體流的相關(guān)信 息,如數(shù)據(jù)編碼/解碼算法、網(wǎng)絡(luò)地址、媒體流的內(nèi)容等。

          雖 然RTSP服務(wù)器同樣也使用標(biāo)識(shí)符來(lái)區(qū)別每一流連接會(huì)話(Session),但RTSP連接并沒(méi)有被綁定到傳輸層連接(如TCP等),也就是說(shuō)在整個(gè) RTSP連接期間,RTSP用戶可打開(kāi)或者關(guān)閉多個(gè)對(duì)RTSP服務(wù)器的可靠傳輸連接以發(fā)出RTSP 請(qǐng)求。此外,RTSP連接也可以基于面向無(wú)連接的傳輸協(xié)議(如UDP等)。

          RTSP協(xié)議目前支持以下操作:

          • 檢索媒體  允許用戶通過(guò)HTTP或者其它方法向媒體服務(wù)器提交一個(gè)表示描述。如表示是組播的,則表示描述就包含用于該媒體流的組播地址和端口號(hào);如果表示是單播的,為了安全在表示描述中應(yīng)該只提供目的地址。
          • 邀請(qǐng)加入  媒體服務(wù)器可以被邀請(qǐng)參加正在進(jìn)行的會(huì)議,或者在表示中回放媒體,或者在表示中錄制全部媒體或其子集,非常適合于分布式教學(xué)。
          • 添加媒體  通知用戶新加入的可利用媒體流,這對(duì)現(xiàn)場(chǎng)講座來(lái)講顯得尤其有用。與HTTP/1.1類(lèi)似,RTSP請(qǐng)求也可以交由代理、通道或者緩存來(lái)進(jìn)行處理。
          三、流媒體編程
          RTP 是目前解決流媒體實(shí)時(shí)傳輸問(wèn)題的最好辦法,如果需要在Linux平臺(tái)上進(jìn)行實(shí)時(shí)流媒體編程,可以考慮使用一些開(kāi)放源代碼的RTP庫(kù),如LIBRTP、 JRTPLIB等。JRTPLIB是一個(gè)面向?qū)ο蟮腞TP庫(kù),它完全遵循RFC 1889設(shè)計(jì),在很多場(chǎng)合下是一個(gè)非常不錯(cuò)的選擇,下面就以JRTPLIB為例,講述如何在Linux平臺(tái)上運(yùn)用RTP協(xié)議進(jìn)行實(shí)時(shí)流媒體編程。

          3.1 環(huán)境搭建

          JRTPLIB 是一個(gè)用C++語(yǔ)言實(shí)現(xiàn)的RTP庫(kù),目前已經(jīng)可以運(yùn)行在Windows、Linux、FreeBSD、 Solaris、Unix和VxWorks等多種操作系統(tǒng)上。要為L(zhǎng)inux 系統(tǒng)安裝JRTPLIB,首先從JRTPLIB的網(wǎng)站(http: //lumumba.luc.ac.be/jori/jrtplib/jrtplib.html)下載最新的源碼包,此處使用的是jrtplib- 2.7b.tar.bz2。假設(shè)下載后的源碼包保存在/usr/local/src目錄下,執(zhí)行下面的命令可以對(duì)其進(jìn)行解壓縮:



          [root@linuxgam src]# bzip2 -dc jrtplib-2.7b.tar.bz2 | tar xvf -
          接下去需要對(duì)JRTPLIB進(jìn)行配置和編譯:



          [root@linuxgam src]# cd jrtplib-2.7
          [root@linuxgam jrtplib-2.7b]# ./configure
          [root@linuxgam jrtplib-2.7b]# make
          最后再執(zhí)行如下命令就可以完成JRTPLIB的安裝:



          [root@linuxgam jrtplib-2.7b]# make install
          3.2 初始化

          在 使用JRTPLIB進(jìn)行實(shí)時(shí)流媒體數(shù)據(jù)傳輸之前,首先應(yīng)該生成RTPSession類(lèi)的一個(gè)實(shí)例來(lái)表示此次RTP會(huì)話,然后調(diào)用Create()方法來(lái)對(duì) 其進(jìn)行初始化操作。RTPSession類(lèi)的Create()方法只有一個(gè)參數(shù),用來(lái)指明此次RTP會(huì)話所采用的端口號(hào)。清單1給出了一個(gè)最簡(jiǎn)單的初始化 框架,它只是完成了RTP會(huì)話的初始化工作,還不具備任何實(shí)際的功能。



          #include "rtpsession.h"

          int main(void)
          {
          RTPSession sess;
          sess.Create(5000);
          return 0;
          }
          如 果RTP會(huì)話創(chuàng)建過(guò)程失敗,Create()方法將會(huì)返回一個(gè)負(fù)數(shù),通過(guò)它雖然可以很容易地判斷出函數(shù)調(diào)用究竟是成功的還是失敗的,但卻很難明白出錯(cuò)的原 因到底什么。JRTPLIB采用了統(tǒng)一的錯(cuò)誤處理機(jī)制,它提供的所有函數(shù)如果返回負(fù)數(shù)就表明出現(xiàn)了某種形式的錯(cuò)誤,而具體的出錯(cuò)信息則可以通過(guò)調(diào)用 RTPGetErrorString()函數(shù)得到。RTPGetErrorString()函數(shù)將錯(cuò)誤代碼作為參數(shù)傳入,然后返回該錯(cuò)誤代碼所對(duì)應(yīng)的錯(cuò)誤 信息。清單2給出了一個(gè)更加完整的初始化框架,它可以對(duì)RTP會(huì)話初始化過(guò)程中所產(chǎn)生的錯(cuò)誤進(jìn)行更好的處理:



          #include <stdio.h>
          #include "rtpsession.h"

          int main(void)
          {
          RTPSession sess;
          int status;
          char* msg;

          sess.Create(6000);
          msg = RTPGetErrorString(status);
          printf("Error String: %s\\n", msg);
          return 0;
          }
          設(shè) 置恰當(dāng)?shù)臅r(shí)戳單元,是RTP會(huì)話初始化過(guò)程所要進(jìn)行的另外一項(xiàng)重要工作,這是通過(guò)調(diào)用RTPSession類(lèi)的 SetTimestampUnit()方法來(lái)實(shí)現(xiàn)的,該方法同樣也只有一個(gè)參數(shù),表示的是以秒為單元的時(shí)戳單元。例如,當(dāng)使用RTP會(huì)話傳輸8000Hz 采樣的音頻數(shù)據(jù)時(shí),由于時(shí)戳每秒鐘將遞增8000,所以時(shí)戳單元相應(yīng)地應(yīng)該被設(shè)置成1/8000:



          sess.SetTimestampUnit(1.0/8000.0);
          3.3 數(shù)據(jù)發(fā)送

          當(dāng)RTP 會(huì)話成功建立起來(lái)之后,接下去就可以開(kāi)始進(jìn)行流媒體數(shù)據(jù)的實(shí)時(shí)傳輸了。首先需要設(shè)置好數(shù)據(jù)發(fā)送的目標(biāo)地址, RTP協(xié)議允許同一會(huì)話存在多個(gè)目標(biāo)地址,這可以通過(guò)調(diào)用RTPSession類(lèi)的AddDestination()、 DeleteDestination()和ClearDestinations()方法來(lái)完成。例如,下面的語(yǔ)句表示的是讓RTP會(huì)話將數(shù)據(jù)發(fā)送到本地主 機(jī)的6000端口:



          unsigned long addr = ntohl(inet_addr("127.0.0.1"));
          sess.AddDestination(addr, 6000);
          目標(biāo)地址全部指定之后,接著就可以調(diào)用RTPSession類(lèi)的SendPacket()方法,向所有的目標(biāo)地址發(fā)送流媒體數(shù)據(jù)。SendPacket()是RTPSession類(lèi)提供的一個(gè)重載函數(shù),它具有下列多種形式:



          int SendPacket(void *data,int len)
          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)
          SendPacket()最典型的用法是類(lèi)似于下面的語(yǔ)句,其中第一個(gè)參數(shù)是要被發(fā)送的數(shù)據(jù),而第二個(gè)參數(shù)則指明將要發(fā)送數(shù)據(jù)的長(zhǎng)度,再往后依次是RTP負(fù)載類(lèi)型、標(biāo)識(shí)和時(shí)戳增量。



          sess.SendPacket(buffer, 5, 0, false, 10);
          對(duì) 于同一個(gè)RTP會(huì)話來(lái)講,負(fù)載類(lèi)型、標(biāo)識(shí)和時(shí)戳增量通常來(lái)講都是相同的,JRTPLIB允許將它們?cè)O(shè)置為會(huì)話的默認(rèn)參數(shù),這是通過(guò)調(diào)用 RTPSession類(lèi)的SetDefaultPayloadType()、SetDefaultMark()和 SetDefaultTimeStampIncrement()方法來(lái)完成的。為RTP會(huì)話設(shè)置這些默認(rèn)參數(shù)的好處是可以簡(jiǎn)化數(shù)據(jù)的發(fā)送,例如,如果為 RTP會(huì)話設(shè)置了默認(rèn)參數(shù):



          sess.SetDefaultPayloadType(0);
          sess.SetDefaultMark(false);
          sess.SetDefaultTimeStampIncrement(10);
          之后在進(jìn)行數(shù)據(jù)發(fā)送時(shí)只需指明要發(fā)送的數(shù)據(jù)及其長(zhǎng)度就可以了:



          sess.SendPacket(buffer, 5);
          3.4 數(shù)據(jù)接收

          對(duì) 于流媒體數(shù)據(jù)的接收端,首先需要調(diào)用RTPSession類(lèi)的PollData()方法來(lái)接收發(fā)送過(guò)來(lái)的RTP或者 RTCP數(shù)據(jù)報(bào)。由于同一個(gè)RTP會(huì)話中允許有多個(gè)參與者(源),你既可以通過(guò)調(diào)用RTPSession類(lèi)的GotoFirstSource()和 GotoNextSource()方法來(lái)遍歷所有的源,也可以通過(guò)調(diào)用RTPSession類(lèi)的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法來(lái)遍歷那些攜帶有數(shù)據(jù)的源。在從RTP會(huì)話中檢測(cè)出有效的數(shù)據(jù)源之后,接下去就可以調(diào)用 RTPSession類(lèi)的GetNextPacket()方法從中抽取RTP數(shù)據(jù)報(bào),當(dāng)接收到的RTP數(shù)據(jù)報(bào)處理完之后,一定要記得及時(shí)釋放。下面的代碼 示范了該如何對(duì)接收到的RTP數(shù)據(jù)報(bào)進(jìn)行處理:



          if (sess.GotoFirstSourceWithData()) {
          do {
          RTPPacket *pack;
          pack = sess.GetNextPacket();
          // 處理接收到的數(shù)據(jù)
          delete pack;
          } while (sess.GotoNextSourceWithData());
          }
          JRTPLIB為RTP數(shù)據(jù)報(bào)定義了三種接收模式,其中每種接收模式都具體規(guī)定了哪些到達(dá)的RTP數(shù)據(jù)報(bào)將會(huì)被接受,而哪些到達(dá)的RTP數(shù)據(jù)報(bào)將會(huì)被拒絕。通過(guò)調(diào)用RTPSession類(lèi)的SetReceiveMode()方法可以設(shè)置下列這些接收模式:

          • RECEIVEMODE_ALL  缺省的接收模式,所有到達(dá)的RTP數(shù)據(jù)報(bào)都將被接受;
          • RECEIVEMODE_IGNORESOME  除了某些特定的發(fā)送者之外,所有到達(dá)的RTP數(shù)據(jù)報(bào)都將被接受,而被拒絕的發(fā)送者列表可以通過(guò)調(diào)用AddToIgnoreList()、DeleteFromIgnoreList()和ClearIgnoreList()方法來(lái)進(jìn)行設(shè)置;
          • RECEIVEMODE_ACCEPTSOME  除了某些特定的發(fā)送者之外,所有到達(dá)的RTP數(shù)據(jù)報(bào)都將被拒絕,而被接受的發(fā)送者列表可以通過(guò)調(diào)用AddToAcceptList ()、DeleteFromAcceptList和ClearAcceptList ()方法來(lái)進(jìn)行設(shè)置。
          3.5 控制信息

          JRTPLIB 是一個(gè)高度封裝后的RTP庫(kù),程序員在使用它時(shí)很多時(shí)候并不用關(guān)心RTCP數(shù)據(jù)報(bào)是如何被發(fā)送和接收的,因?yàn)檫@些都可以由JRTPLIB自己來(lái)完成。只要 PollData()或者SendPacket()方法被成功調(diào)用,JRTPLIB就能夠自動(dòng)對(duì)到達(dá)的 RTCP數(shù)據(jù)報(bào)進(jìn)行處理,并且還會(huì)在需要的時(shí)候發(fā)送RTCP數(shù)據(jù)報(bào),從而能夠確保整個(gè)RTP會(huì)話過(guò)程的正確性。

          而 另一方面,通過(guò)調(diào)用RTPSession類(lèi)提供的SetLocalName()、SetLocalEMail()、 SetLocalLocation()、SetLocalPhone()、SetLocalTool()和SetLocalNote()方法, JRTPLIB又允許程序員對(duì)RTP會(huì)話的控制信息進(jìn)行設(shè)置。所有這些方法在調(diào)用時(shí)都帶有兩個(gè)參數(shù),其中第一個(gè)參數(shù)是一個(gè)char型的指針,指向?qū)⒁辉O(shè) 置的數(shù)據(jù);而第二個(gè)參數(shù)則是一個(gè)int型的數(shù)值,表明該數(shù)據(jù)中的前面多少個(gè)字符將會(huì)被使用。例如下面的語(yǔ)句可以被用來(lái)設(shè)置控制信息中的電子郵件地址:



          sess.SetLocalEMail("
          xiaowp@linuxgam.com

          ",19);
          在RTP 會(huì)話過(guò)程中,不是所有的控制信息都需要被發(fā)送,通過(guò)調(diào)用RTPSession類(lèi)提供的 EnableSendName()、EnableSendEMail()、EnableSendLocation()、EnableSendPhone ()、EnableSendTool()和EnableSendNote()方法,可以為當(dāng)前RTP會(huì)話選擇將被發(fā)送的控制信息。

          3.6 實(shí)際應(yīng)用

          最后通過(guò)一個(gè)簡(jiǎn)單的流媒體發(fā)送-接收實(shí)例,介紹如何利用JRTPLIB來(lái)進(jìn)行實(shí)時(shí)流媒體的編程。清單3給出了數(shù)據(jù)發(fā)送端的完整代碼,它負(fù)責(zé)向用戶指定的IP地址和端口,不斷地發(fā)送RTP數(shù)據(jù)包:



          #include <stdio.h>
          #include <string.h>
          #include "rtpsession.h"

          // 錯(cuò)誤處理函數(shù)
          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地址和端口號(hào)
          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會(huì)話
          status = sess.Create(portbase);
          checkerror(status);

          // 指定RTP數(shù)據(jù)接收端
          status = sess.AddDestination(destip, destport);
          checkerror(status);

          // 設(shè)置RTP會(huì)話默認(rèn)參數(shù)
          sess.SetDefaultPayloadType(0);
          sess.SetDefaultMark(false);
          sess.SetDefaultTimeStampIncrement(10);

          // 發(fā)送流媒體數(shù)據(jù)
          index = 1;
          do {
          sprintf(buffer, "%d: RTP packet", index ++);
          sess.SendPacket(buffer, strlen(buffer));
          printf("Send packet !\\n");
          } while(1);

          return 0;
          }
          清單4則給出了數(shù)據(jù)接收端的完整代碼,它負(fù)責(zé)從指定的端口不斷地讀取RTP數(shù)據(jù)包:



          #include <stdio.h>
          #include "rtpsession.h"
          #include "rtppacket.h"

          // 錯(cuò)誤處理函數(shù)
          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;
          }

          // 獲得用戶指定的端口號(hào)
          localport = atoi(argv[1]);

          // 創(chuàng)建RTP會(huì)話
          status = sess.Create(localport);
          checkerror(status);

          do {
          // 接受RTP數(shù)據(jù)
          status = sess.PollData();
          // 檢索RTP數(shù)據(jù)源
          if (sess.GotoFirstSourceWithData()) {
          do {
          RTPPacket* packet;
          // 獲取RTP數(shù)據(jù)報(bào)
          while ((packet = sess.GetNextPacket()) != NULL) {
          printf("Got packet !\\n");
          // 刪除RTP數(shù)據(jù)報(bào)
          delete packet;
          }
          } while (sess.GotoNextSourceWithData());
          }
          } while(1);

          return 0;
          }
          本文源碼 下載

          四、小結(jié)
          隨 著多媒體數(shù)據(jù)在Internet上所承擔(dān)的作用變得越來(lái)越重要,需要實(shí)時(shí)傳輸音頻和視頻等多媒體數(shù)據(jù)的場(chǎng)合也將變得越來(lái)越多,如IP電話、視頻點(diǎn)播、在線 會(huì)議等。RTP是用來(lái)在Internet上進(jìn)行實(shí)時(shí)流媒體傳輸?shù)囊环N協(xié)議,目前已經(jīng)被廣泛地應(yīng)用在各種場(chǎng)合,JRTPLIB是一個(gè)面向?qū)ο蟮腞TP封裝 庫(kù),利用它可以很方便地完成Linux平臺(tái)上的實(shí)時(shí)流媒體編程。


          • 1. 在JRTPLIB的網(wǎng)站http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.html上,可以下載到JRTPLIB最新的源碼包,并且還能找到一些與RTP相關(guān)的資源。

          • 2. 顧淑珍等編著,寬帶增值服務(wù)開(kāi)發(fā)實(shí)例,北京:機(jī)械工業(yè)出版社,2002

          • 3. 黃永峰等編著,IP網(wǎng)絡(luò)多媒體通信技術(shù),北京:人民郵電出版社,2003

          注:本文作者:肖文鵬? ??? 摘自:developerWorks 中國(guó)?

          posted on 2006-03-30 21:37 soochow_hhb 以java論成敗 以架構(gòu)論英雄 閱讀(4166) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): Reading

          評(píng)論

          # re: linux下RTP編程(使用JRTPLIB)(轉(zhuǎn))  回復(fù)  更多評(píng)論   

          能說(shuō)說(shuō)自己寫(xiě)的收發(fā)代碼怎么編嗎?
          我的一用 g++ -g file.cpp -o file就出一大堆錯(cuò)
          2008-09-11 14:52 | juzixiangchang
          主站蜘蛛池模板: 大余县| 潞城市| 常山县| 尚志市| 海口市| 梅河口市| 兖州市| 璧山县| 德格县| 延川县| 武隆县| 邛崃市| 辉南县| 黑河市| 基隆市| 贵阳市| 讷河市| 塔城市| 钟祥市| 介休市| 谷城县| 贵德县| 达州市| 阿合奇县| 巴东县| 富蕴县| 永丰县| 宜城市| 通海县| 甘孜| 天津市| 龙泉市| 三门县| 吉安市| 莲花县| 中阳县| 白城市| 海淀区| 江永县| 牡丹江市| 共和县|