這幾天在看關於JRTPLIB方面的東西。在網上看了不少文章,其中有很大部分使用的JRTPLIB版本在3.0以下。
在網上下載了一個JRTPLIB-3.7的庫,發現里面的函數接口做了一些修改。現奉上一篇基於JRTPLIB-3.7的網絡
語音傳送實例,希望有興趣的朋友一起參詳研究。
-------chuckGao
第一部分 JRTPLIB的編譯及安裝
這是必行的一步,在網上可以找到相關的文章,這里就不啰嗦了。不過可能有些朋友會遇到JRTPLIB
無法正常編譯的情況,出現error: 'memcpy' was not declared in this scope的錯誤。這是由於JRTPLIB編
譯中無法找到memcpy這個函數。在網上有memcpy的patch。內容如下:
diff --git a/src/rtcpcompoundpacketbuilder.cpp b/src/rtcpcompoundpacketbuilder.cpp
index 8172007..8fd4510 100644
--- a/src/rtcpcompoundpacketbuilder.cpp
+++ b/src/rtcpcompoundpacketbuilder.cpp
@@ -30,6 +30,8 @@
*/
+#include <cstring>
+
#include "rtcpcompoundpacketbuilder.h"
#include "rtcpsrpacket.h"
#include "rtcprrpacket.h"
diff --git a/src/rtppacket.cpp b/src/rtppacket.cpp
index b6d5fda..8c516c7 100644
--- a/src/rtppacket.cpp
+++ b/src/rtppacket.cpp
@@ -30,6 +30,8 @@
*/
+#include <cstring>
+
#include "rtppacket.h"
#include "rtpstructs.h"
#include "rtpdefines.h"
+代表添加,-代表刪除相應內容
第二部分 JRTPLIB編程
下面先轉載一部分網上的指南,紅色標記是JRTPLIB-3.7修了后的使用方法
linux 下基於jrtplib庫的實時傳送實現
一、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 等多種操
作系統上。
二、JRTPLIB 庫的使用方法及程序實現
(1)JRTPLIB 函數 的使用
a、在使用 JRTPLIB 進行實時流媒體數據傳輸之前,首先應該生成 RTPSession 類的一個實例來表示此次 RTP
會話,然后調用 Create() 方法來對其進行初始化操作。RTPSession 類的 Create() 方法只有一個參數,用
來指明此次 RTP 會話所采用的端口號。
RTPSession sess; sess.Create(5000);
JRTPLIB-3.7中已經修改了Create(prot)方法。新的Create方法被修改為Crea(sessparams,&transparams)。其中的兩個參數需要如下先定義:
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
sessparams.SetOwnTimestampUnit(1.0/8000.0);/*設置時間戳,1/8000表示1秒鍾采樣8000次,即錄音時的8KHz*/
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase);/*本地通訊端口*/
b、設置恰當的時戳單元,是 RTP 會話初始化過程所要進行的另外一項重要工作,這是通過調用 RTPSession
類的 SetTimestampUnit() 方法來實現的,前面已經提過。
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);
在真正的語音傳輸中,上面的buffer就是我們錄音時所得到的buffer。使用上面的函數可以簡單的發送,但無法真正的實現RTP傳輸,我們需要調用另一個接口:sess.SendPacket((void *)buffer,sizeof(buffer),0,false,8000);詳細的說明可以查看JRTPLIB的說明文檔。
e、對於流媒體數據的接收端,首先需要調用 RTPSession 類的 PollData() 方法來接收發送過來的 RTP 或者
RTCP 數據報。
JRTPLIB-3.7中修改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: %d lengh=%d/n",timestamp1,lengh);
// we don't longer need the packet, so
// we'll delete it
//Begin play
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);
(2)程序流程圖
發送:獲得接收端的 IP 地址和端口號 創建 RTP 會話 指定 RTP 數據接收端 設置 RTP 會話 默認參數 發送流媒體數據
接收:獲得用戶指定的端口號 創建RTP會話 設置接收模式 接受RTP數據 檢索RTP數據源 獲取RTP數據報 刪除RTP數據報
因為是關於JRTPLIB的文章,所以貼出的錄音和放音代碼不多。需要的朋友可以留下郵箱。