linux 下基於jrtplib庫的實時傳送實現

linux 下基於jrtplib庫的實時傳送實現
1、RTP 是進行實時流媒體傳輸的標準協議和關鍵技術
實時傳輸協議(Real-time Transport Protocol,PRT)是在 Internet 上處理多媒體數據流的一種網絡協議,利用它可以在一對一(unicast,單播)或者一對多(multicast,多播)的網絡環境中實現傳流媒體數據的 實時傳輸。RTP 一般使用 UDP 來進行多媒體數據的傳輸,但若是須要的話可使用 TCP 或者 ATM 等其它協議。

協議分析 :每個RTP數據報都由頭部(Header)和負載(Payload)兩個部分組成,其中頭部前 12 個字節的含義是固定的,而負載則能夠是音頻或者視頻數據。

     RTP 是目前解決流媒體實時傳輸問題的最好辦法,要在 Linux 平臺上進行實時傳送編程,能夠考慮使用一些開放源代碼的 RTP 庫,如 LIBRTP、JRTPLIB 等。JRTPLIB 是一個面向對象的 RTP 庫,它徹底遵循 RFC 1889 設計,在不少場合下是一個很是不錯的選擇。JRTPLIB 是一個用 C++ 語言實現的 RTP 庫,這個庫使用socket 機制實現網絡通信 所以能夠運行在 Windows、Linux、FreeBSD、Solaris、Unix和VxWorks 等多種操做系統上。
2、JRTPLIB 庫的使用方法及程序實現
(1)JRTPLIB  函數 的使用
a、在使用 JRTPLIB 進行實時流媒體數據傳輸以前,首先應該生成 RTPSession 類的一個實例來表示這次 RTP 會話,而後調用 Create() 方法來對其進行初始化操做。RTPSession 類的 Create() 方法只有一個參數,用來指明這次 RTP 會話所採用的端口號。
RTPSession sess;  sess.Create(5000);

b、設置恰當的時戳單元,是 RTP 會話初始化過程所要進行的另一項重要工做,這是經過調用 RTPSession 類的 SetTimestampUnit() 方法來實現的,該方法一樣也只有一個參數,表示的是以秒爲單元的時戳單元。
sess.SetTimestampUnit(1.0/8000.0);

c、當 RTP 會話成功創建起來以後,接下去就能夠開始進行流媒體數據的實時傳輸了。首先須要設置好數據發送的目標地址,RTP 協議容許同一會話存在多個目標地址,這能夠經過調用 RTPSession 類的 AddDestination()、DeleteDestination() 和 ClearDestinations() 方法來完成。例如,下面的語句表示的是讓 RTP 會話將數據發送到本地主機的 6000 端口:

unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);

d、目標地址所有指定以後,接着就能夠調用 RTPSession 類的 SendPacket() 方法,向全部的目標地址發送流媒體數據。SendPacket() 是 RTPSession 類提供的一個重載函數
對於同一個 RTP 會話來說,負載類型、標識和時戳增量一般來說都是相同的,JRTPLIB 容許將它們設置爲會話的默認參數,這是經過調用 RTPSession 類的 SetDefaultPayloadType()、SetDefaultMark() 和 SetDefaultTimeStampIncrement() 方法來完成的。爲 RTP 會話設置這些默認參數的好處是能夠簡化數據的發送,例如,若是爲 RTP 會話設置了默認參數:

sess.SetDefaultPayloadType(0);
 sess.SetDefaultMark(false);  
sess.SetDefaultTimeStampIncrement(10);



以後在進行數據發送時只需指明要發送 的數據及其長度就能夠了:

sess.SendPacket(buffer, 5);


e、對於流媒體數據的接收端,首先須要調用 RTPSession 類的 PollData() 方法來接收發送過來的 RTP 或者 RTCP 數據報。因爲同一個 RTP 會話中容許有多個參與者(源),你既能夠經過調用 RTPSession 類的 GotoFirstSource() 和 GotoNextSource() 方法來遍歷全部的源,也能夠經過調用 RTPSession 類的 GotoFirstSourceWithData() 和 GotoNextSourceWithData() 方法來遍歷那些攜帶有數據的源。在從 RTP 會話中檢測出有效的數據源以後,接下去就能夠調用 RTPSession 類的 GetNextPacket() 方法從中抽取 RTP 數據報,當接收到的 RTP 數據報處理完以後,必定要記得及時釋放。

JRTPLIB 爲 RTP 數據報定義了三種接收模式,其中每種接收模式都具體規定了哪些到達的 RTP 數據報將會被接受,而哪些到達的 RTP 數據報將會被拒絕。經過調用 RTPSession 類的 SetReceiveMode() 方法能夠設置下列這些接收模式:
? RECEIVEMODE_ALL  缺省的接收模式,全部到達的 RTP 數據報都將被接受;
? RECEIVEMODE_IGNORESOME  除了某些特定的發送者以外,全部到達的 RTP 數據報都將被接受,而被拒絕的發送者列表能夠經過調用 AddToIgnoreList()、DeleteFromIgnoreList() 和 ClearIgnoreList() 方法來進行設置;
? RECEIVEMODE_ACCEPTSOME  除了某些特定的發送者以外,全部到達的 RTP 數據報都將被拒絕,而被接受的發送者列表能夠經過調用 AddToAcceptList ()、DeleteFromAcceptList 和 ClearAcceptList () 方法來進行設置。 下面是採用第三種接收模式的程序示例。
if (sess.GotoFirstSourceWithData()) {  
 do {  
  sess.AddToAcceptList(remoteIP, allports,portbase);
         sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);

   RTPPacket *pack;        
   pack = sess.GetNextPacket();            // 處理接收到的數據    
   delete pack;   }
 while (sess.GotoNextSourceWithData());
 }


 (2)程序流程圖
發送:得到接 收端的 IP 地址和端口號        建立 RTP 會話        指定 RTP 數據接收端 設置 RTP 會話默認參數   發送流媒體數據
接收:得到用戶指定的端口號  建立RTP會話  設置接收模式  接受RTP數據  檢索RTP數據源  獲取RTP數據報  刪除RTP數據報


3、環境搭建及編譯方法
引用
(1)Toolchain的安裝
首先找到xscale-arm-toolchain.tgz文件,假設該文件包放在/tmp/下
#cd /
#tar -zxvf /tmp/xscale-arm-toolchain.tgz
再設置環境變量
#export PATH=/usr/local/arm-linux/bin:$PATH
最後檢查一下交叉編譯工具是否安裝成功
#arm-linux-g++ --version
看是否顯示arm-linux-g++的版本,若有則安裝成功。
(2)JRTPLIB 庫的交叉編譯及安裝
首先從 JRTPLIB 的網站(http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmll) 下載最新的源碼包,此處使用的是jrtplib-2.8.tar,假設下載後的源碼包放在/tmp下,執 行下面的命令對其解壓縮:
#cd /tmp
#tar -zxvf jrtplib-2.8.tar
而後要對jrtplib進行配置和編譯
#cd jrtplib-2.8
#./configure CC=arm-linux-g++ cross-compile=yes
修改Makefile文件
將連接命令ld 和ar改成arm-linux-ld和 arm-linux-ar
#make
最後再執行以下命令就能夠完成 JRTPLIB 的安裝:
#make install
(3)程序編譯
a、配置編譯環境
能夠用export來配置,也能夠用編寫Makefile的方法。這裏採用Makefile。
編寫Makefile&:
INCL = -I/usr/local/include
CFLAGS = -pipe -O2 -fno-strength-reduce
LFLAGS = /usr/local/lib/libjrtp.a -L/usr/X11R6/lib
LIBS = -LX11 -LXext /usr/local/lib/libjrtp.a
CC = arm-linux-g++

main:main.o
$(CC) $(LFLAGS) $(INCL) -o main main.o $(LIBS)
main.o:main.cpp

clean:
rm -f main
rm -f *.o

.SUFFIXES:.cpp
.cpp.o:
$(CC) -c $(CFLAGS) $(INCL) -o $@ $<         /*  $@表示目標的完整名字      */
         /* $<表示第一個依賴文件的名字 */
b、編譯
假設發送和接收程序分別放在/tmp/send和/tmp/receive目錄下
#cd /tmp/send
#make
#cd /tmp/receive
#make

4、易出錯誤及注意問題
引用
一、找不到一些標準的最 基本的一些頭文件。
 主要是由於Toolchain路徑沒安裝對,要 嚴格按照步驟安裝。
二、找不到使用的jrtplib庫中的一些頭文件。
 在 jrtplib的安裝目錄下,include路徑下不能再有別的目錄。
三、recieve函數接收數據包不能正確提出所要數據。
 因爲每個RTP數據報都由頭部(Header)和負載(Payload)兩個部分組成,若使用getrawdata()是返回整個數據包的數據,包含 傳輸媒體的類型、格式、序列號、時間戳以及是否有附加數據等信息。getpayload()函數是返回所發送的數據。二者必定要分清。
四、設置RECEIVEMODE_ACCEPTSOME  接收模式後,運行程序接收端不能接包。
 IP地址格式出了問題。iner_addr()與ntohl()函數要用對,不然參數傳不進去,接受列表中無值,固然接收不了數據包。
五、編譯經過,但測試時接收端不能接收到數據。
 多是接收機防火牆未關閉。運行:
 #iptables -F
 也多是IP地址沒有設置好。運行:
 #ifocnfig eth0  *.*.*.*  netmask *.*.*.*
六、使用jrtolib庫時,在程序中include 後最好加上庫所在的路徑。
5、程序

send:
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include "rtpsession.h"  
  4.   
  5. // 錯誤處理函數  
  6. void checkerror(int err)  
  7. {  
  8.   if (err < 0) {  
  9.     char* errstr = RTPGetErrorString(err);  
  10.     printf("Error:%s\\n", errstr);  
  11.     exit(-1);  
  12.   }  
  13. }  
  14.   
  15. int main(int argc, char** argv)  
  16. {  
  17.   RTPSession sess;  
  18.   unsigned long destip;  
  19.   int destport;  
  20.   int portbase = 6000;  
  21.   int status, index;  
  22.   char buffer[128];  
  23.   
  24.   if (argc != 3) {  
  25.     printf("Usage: ./sender destip destport\\n");  
  26.     return -1;  
  27.   }  
  28.   
  29.   // 得到接收端的IP地址和端口號  
  30.   destip = inet_addr(argv[1]);  
  31.   if (destip == INADDR_NONE) {  
  32.     printf("Bad IP address specified.\\n");  
  33.     return -1;  
  34.   }  
  35.   destip = ntohl(destip);  
  36.   destport = atoi(argv[2]);  
  37.   
  38.   // 建立RTP會話  
  39.   status = sess.Create(portbase);  
  40.   checkerror(status);  
  41.   
  42.   // 指定RTP數據接收端  
  43.   status = sess.AddDestination(destip, destport);  
  44.   checkerror(status);  
  45.   
  46.   // 設置RTP會話默認參數  
  47.   sess.SetDefaultPayloadType(0);  
  48.   sess.SetDefaultMark(false);  
  49.   sess.SetDefaultTimeStampIncrement(10);  
  50.   
  51.   // 發送流媒體數據  
  52.   index = 1;  
  53.   do {  
  54.     sprintf(buffer, "%d: RTP packet", index ++);  
  55.     sess.SendPacket(buffer, strlen(buffer));  
  56.     printf("Send packet !\\n");  
  57.   } while(1);  
  58.   
  59.   return 0;  
  60. }  





receive:
  1. #include <stdio.h>  
  2. #include "rtpsession.h"  
  3. #include "rtppacket.h"  
  4.   
  5. // 錯誤處理函數  
  6. void checkerror(int err)  
  7. {  
  8.   if (err < 0) {  
  9.     char* errstr = RTPGetErrorString(err);  
  10.     printf("Error:%s\\n", errstr);  
  11.     exit(-1);  
  12.   }  
  13. }  
  14.   
  15. int main(int argc, char** argv)  
  16. {  
  17.   RTPSession sess;  
  18.   int localport,portbase;  
  19.   int status;  
  20.   unsigned long remoteIP;  
  21.   if (argc != 4) {  
  22.     printf("Usage: ./sender localport\\n");  
  23.     return -1;  
  24.   }  
  25.   
  26.    // 得到用戶指定的端口號  
  27.      
  28.   remoteIP = inet_addr(argv[1]);  
  29.   localport = atoi(argv[2]);  
  30.   portbase = atoi(argv[3]);  
  31.   // 建立RTP會話  
  32.   status = sess.Create(localport);  
  33.   checkerror(status);  
  34.     
  35.   //RTPHeader *rtphdr;  
  36.   unsigned long timestamp1;  
  37.   unsigned char * RawData;  
  38.   unsigned char temp[30];  
  39.   int lengh ,i;  
  40.   bool allports = 1;  
  41.     
  42.   sess.AddToAcceptList(remoteIP, allports,portbase);  
  43.     
  44.      do {  
  45.  //設置接收模式  
  46.         sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);  
  47.    sess.AddToAcceptList(remoteIP, allports,portbase);  
  48.   
  49.     // 接受RTP數據  
  50.     status = sess.PollData();  
  51.   
  52.       
  53.  // 檢索RTP數據源  
  54.     if (sess.GotoFirstSourceWithData()) {  
  55.       do {  
  56.           
  57.         RTPPacket* packet;  
  58.         // 獲取RTP數據報  
  59.         while ((packet = sess.GetNextPacket()) != NULL) {  
  60.           printf("Got packet !\n");  
  61.   
  62.    timestamp1 = packet->GetTimeStamp();  
  63.    lengh=packet->GetPayloadLength();  
  64.    RawData=packet->GetPayload();  
  65.      
  66.    for(i=0;i<lengh;i++){  
  67.       temp[i]=RawData[i];  
  68.   printf("%c",temp[i]);  
  69.    }  
  70.    temp[i]='\0';  
  71.    printf("  timestamp: %d lengh=%d data:%s\n",timestamp1,lengh,&temp);  
  72.           // 刪除RTP數據報  
  73.      
  74.           delete packet;  
  75.         }  
  76.       } while (sess.GotoNextSourceWithData());  
  77.     }  
  78.   } while(1);  
  79.   
  80.   return 0;  
  81. }  
相關日誌
相關文章
相關標籤/搜索