程式流程
發送:獲得接收端的 IP 位址和端口号 建立 RTP 會話 指定 RTP 資料接收端 設定 RTP 會話 預設參數 發送流媒體資料
接收:獲得使用者指定的端口号 建立RTP會話 設定接收模式 接受RTP資料 檢索RTP資料源 擷取RTP資料報 删除RTP資料報
1.初始化
I、在使用 JRTPLIB 進行實時流媒體資料傳輸之前,首先應該生成 RTPSession 類的一個執行個體來表示此次 RTP會話,然後調用 Create() 方法來對其進行初始化操作。RTPSession 類的 Create() 方法隻有一個參數,用來指明此次 RTP 會話所采用的端口号。
RTPSession sess;
sess.Create(5000);
JRTPLIB-3.11中已經修改了Create(prot)方法。新的Create方法被修改為Create(sessparams,&transparams)。其中的兩個參數需要如下先定義:
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
sessparams.SetOwnTimestampUnit(1.0/8000.0);/*設定時間戳,1/8000表示1秒鐘采樣8000次,即錄音時的8KHz*/
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase);/*本地通訊端口*/
II、設定恰當的時戳單元,是 RTP 會話初始化過程所要進行的另外一項重要工作,這是通過調用 RTPSession類的 SetTimestampUnit() 方法來實作的,前面已經提過。
2.資料發送
I、當 RTP 會話成功建立起來之後,接下去就可以開始進行流媒體資料的實時傳輸了。首先需要設定好資料發送的目标位址,RTP 協定允許同一會話存在多個目标位址,這可以通過調用 RTPSession 類的AddDestination()、DeleteDestination() 和 ClearDestinations() 方法來完成。例如,下面的語句表示的是讓 RTP 會話将資料發送到本地主機的 6000 端口:
unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);
II、目标位址全部指定之後,接着就可以調用 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);
在真正的語音傳輸中,上面的buffer就是我們錄音時所得到的buffer。使用上面的函數可以簡單的發送,但無法真正的實作RTP傳輸,我們需要調用另一個接口:sess.SendPacket((void *)buffer,sizeof(buffer),0,false,8000);詳細的說明可以檢視JRTPLIB的說明文檔。
3.資料接收
對于流媒體資料的接收端,首先需要調用 RTPSession 類的 PollData() 方法來接收發送過來的 RTP 或者RTCP 資料報。
JRTPLIB-3.11中修改PollData()方法為Poll(),使用都一樣
由于同一個 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());
}
完整的代碼中,首先需調用Poll()方法接收RTP資料報,然後在BeginDataAccess()和EndDataAccess()之間進行資料接收的操作。此時,我們設定程式一直do-while等待并處理資料
do{
#ifndef RTP_SUPPORT_THREAD
error_status = sess_client.Poll();
checkerror(error_status);
#endif // RTP_SUPPORT_THREAD
sess_client.BeginDataAccess();
// check incoming packets
if (sess_client.GotoFirstSourceWithData())
{
printf("Begin play/n");
do
{
RTPPacket *pack;
while ((pack = sess_client.GetNextPacket()) != NULL)
{
// You can examine the data here
printf("Got packet !/n");
timestamp1 = pack->GetTimestamp();
lengh=pack->GetPayloadLength();
RawData=pack->GetPayloadData(); //得到資料
printf(" timestamp: %dlengh=%d/n",timestamp1,lengh);
int fd = open("/dev/dsp", O_RDWR);
int status = write(fd, RawData,lengh );
printf("Play bytes:%d/n",status);
if (status != lengh)
perror("wrote wrong number of bytes");
status = ioctl(fd, SOUND_PCM_SYNC, 0);
if (status == -1)
perror("SOUND_PCM_SYNC ioctl failed");
printf("Play end/n");
close(fd);
sess_client.DeletePacket(pack);
}
} while (sess_client.GotoNextSourceWithData());
//return 0;
}
sess_client.EndDataAccess();
}while(1);
說明 : jrtp-3.x 中有兩種資料接收方式:
第一種是用 jthread 庫提供的線程自動在背景執行對資料的接收。
第二種是使用者自己調用 RTPSession 中的 Poll 方法。
如果采取第一種方法則要安裝 jthread 庫,則安裝 jthread-1.x.tar.gz ,而且 jthread-1.x 必須先與 jrtp-3.x 的安裝。因為在 jrtp-3.x 的 configure 中,會查找系統是否有編譯了 jthread 庫,如果有,那麼編譯的 jrtp 庫會開啟對 jthread 的支援。是以如果先編譯jrtp 在編譯 jthread ,編譯出來的 jrtp 是沒有開啟對 jthread 的支援的。如果采用第二種方法,那麼可以不用編譯 jthread 庫,而直接編譯 jrtp 庫。
可以加入環境變量 export LD_LIBRARY_PATH=/XXX/lib,避免将所有lib都放入/usr下
./ example