1、初始化
在使用 JRTPLIB 进行实时流媒体数据传输之前,首先应该生成 RTPSession 类的一个实例来表示此次 RTP 会话。然后调用 Create() 方法来对其进行初始化操作
RTPSessionParams sessparams;
RTPUDPv4TransmissionParams transparams;
sessparams.SetOwnTimestampUnit(1.0/10.0); //设置时戳发送频率
sessparams.SetAcceptOwnPackets(true); //设置参数为ture,可以接收自己发送的流媒体数据
transparams.SetPortbase(portbase); //设置进行流媒体数据传输或接收的本主机端口
sess.Create(sessparams, &transparams); //创建RTP任务
2、数据发送
(1)、指定目标地址
RTPIPv4Address addr(destip, destport); //声明RTPv4Address的对象addr,并初始化(设置目标地址)
sess.AddDestination(addr); //指定目标地址
(2)发送流媒体数据
SendPacket() 是 RTPSession 类提供的一个重载函数,具有多种形式:
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() 最典型的用法是类似于下面的语句,其中第一个参数是要被发送的数据,而第二个参数则指明将要发送数据的长度,再往后依次是 RTP 负载类型、标识和时戳增量。
sess.SendPacket(buffer, 5, 0, false, 10);
(3)、设置发送数据默认参数
对于同一个 RTP 会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB 允许将它们设置为会话的默认参数,这是通过调用 RTPSession 类的SetDefaultPayloadType()、SetDefaultMark() 和 SetDefaultTimeStampIncrement() 方法来完成的。为 RTP 会话设置这些默认参数的好处是可以简化数据的发送,例如,如果为 RTP 会话设置了默认参数:
sess.SetDefaultPayloadType(0);
sess.SetDefaultMark(false);
sess.SetDefaultTimeStampIncrement(10);
之后在进行数据发送时只需指明要发送的数据及其长度就可以了:
sess.SendPacket(buffer, 5);
3、接收或发送延时
RTPTime::Wait(RTPTime(1,0)); //延时10s;
4、数据接收
(1)、接收函数
对于流媒体数据的接收端,首先需要调用 RTPSession 类的 PollData() 方法来接收发送过来的 RTP 或者 RTCP 数据报。由于同一个 RTP 会话中允许有多个参与者(源),你既可以通过调用 RTPSession 类的 GotoFirstSource() 和 GotoNextSource() 方法来遍历数据源中的所有数据,也可以通过调用 RTPSession 类的 GotoFirstSourceWithData() 和 GotoNextSourceWithData() 方法来遍历那些携带有数据的源。在从 RTP 会话中检测出有效的数据源之后,接下去就可以调用 RTPSession 类的 GetNextPacket() 方法从中抽取 RTP 数据报,当接收到的 RTP 数据报处理完之后,一定要记得及时释放。下面的代码示范了该如何对接收到的 RTP 数据报进行处理:
sess.BeginDataAccess(); //上锁,开始接收RTP数据
if (sess.GotoFirstSourceWithData()) {
do {
RTPPacket *pack;
while((pack = sess.GetNetPacket()) != NULL) {
cout << pack->GetPayloadLength() << endl; //打印本次所收到的数据包内容
// 处理接收到的数据
}
} while (sess.GotoNextSourceWithData());
}
(2)、接收设置
通过调用 RTPSession 类的 SetReceiveMode() 方法可以设置哪些到达的 RTP 数据报将会被接受或拒绝,其参数有:
RECEIVEMODE_ALL 缺省的接收模式,所有到达的 RTP 数据报都将被接受;
RECEIVEMODE_IGNORESOME 除了某些特定的发送者之外,所有到达的 RTP 数据报都将被接受,而被拒绝的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() 和 ClearIgnoreList() 方法来进行设置;
RECEIVEMODE_ACCEPTSOME 除了某些特定的发送者之外,所有到达的 RTP 数据报都将被拒绝,而被接受的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList 和 ClearAcceptList () 方法来进行设置。
5、控制信息
JRTPLIB 是一个高度封装后的 RTP 库,程序员在使用它时很多时候并不用关心 RTCP 数据报是如何被发送和接收的,因为这些都可以由 JRTPLIB 自己来完成。只要 PollData() 或者SendPacket() 方法被成功调用,JRTPLIB 就能够自动对到达的 RTCP 数据报进行处理,并且还会在需要的时候发送 RTCP 数据报,从而能够确保整个 RTP 会话过程的正确性。
而另一方面,通过调用 RTPSession 类提供的 SetLocalName()、SetLocalEMail()、SetLocalLocation()、SetLocalPhone()、SetLocalTool() 和 SetLocalNote() 方法,JRTPLIB 又允许程序员对RTP会话的控制信息进行设置。所有这些方法在调用时都带有两个参数,其中第一个参数是一个 char 型的指针,指向将要被设置的数据;而第二个参数则是一个 int 型的数值,表明该数据中的前面多少个字符将会被使用。例如下面的语句可以被用来设置控制信息中的电子邮件地址:
sess.SetLocalEMail("[email protected]",11);
通过调用 RTPSession 类提供的 EnableSendName()、EnableSendEMail()、EnableSendLocation()、EnableSendPhone()、EnableSendTool() 和 EnableSendNote() 方法,可以为当前 RTP 会话选择将被发送的控制信息。
6、以下是我根据示例写的发送端个接收端的代码
发送端:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#include "../include/jrtplib3/rtpsession.h"
#include "../include/jrtplib3/rtpudpv4transmitter.h"
#include "../include/jrtplib3/rtpsessionparams.h"
#include "../include/jrtplib3/rtpipv4address.h"
#include "../include/jrtplib3/rtperrors.h"
#include "../include/jrtplib3/rtppacket.h"
int checkerror(int err) {
if (err < 0) {
string errstr(RTPGetErrorString(err));
cout << "Error:" << errstr <<endl;
exit(-1);
}
return 0;
}
int main(void)
{
RTPSession sess;
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
int val;
char buffer[10] = "123456789";
unsigned long destip = ntohl(inet_addr("127.0.0.1"));
int destport = 6008;
int portbase = 6002;
sessparams.SetOwnTimestampUnit(1.0/10.0);
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase);
val = sess.Create(sessparams, &transparams);
checkerror(val);
RTPIPv4Address addr(destip, destport);
val = sess.AddDestination(addr);
checkerror(val);
while(1) {
val = sess.SendPacket(buffer, sizeof(buffer), 0, false, 10);
checkerror(val);
cout << "Send packet:"<< buffer << endl;
#ifndef RTP_SUPPORT_THREAD
val = sess.Poll();
checkerror(val);
#endif // RTP_SUPPORT_THREAD
RTPTime::Wait(RTPTime(1,0));
}
sess.BYEDestroy(RTPTime(10,0),0,0);
return 0;
}
接收端:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#include "../include/jrtplib3/rtpsession.h"
#include "../include/jrtplib3/rtpudpv4transmitter.h"
#include "../include/jrtplib3/rtpsessionparams.h"
#include "../include/jrtplib3/rtpipv4address.h"
#include "../include/jrtplib3/rtperrors.h"
#include "../include/jrtplib3/rtppacket.h"
int checkerror(int err) {
if (err < 0) {
string errstr(RTPGetErrorString(err));
cout << "Error:" << errstr <<endl;
exit(-1);
}
return 0;
}
int main(void)
{
RTPSession sess;
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
int val;
int localport = 6008;
sessparams.SetOwnTimestampUnit(1.0/10.0);
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(localport);
val = sess.Create(sessparams, &transparams);
checkerror(val);
while (1) {
sess.BeginDataAccess();
if (sess.GotoFirstSourceWithData()) {
do {
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL) {
cout <<"Got packet! "
<< pack ->GetExtendedSequenceNumber()
<< " from SSRC "
<< pack->GetSSRC()
<< " Length: "
<< pack->GetPayloadLength()
<< " "
<< pack->GetPayloadData()
<< " "
<< pack->GetPacketData() << endl;
sess.DeletePacket(pack);
}
} while (sess.GotoNextSourceWithData());
}
sess.EndDataAccess();
#ifndef RTP_SUPPORT_THREAD
val = sess.Poll();
checkerror(val);
#endif // RTP_SUPPORT_THREAD
// RTPTime::Wait(RTPTime(1,0));
}
sess.BYEDestroy(RTPTime(10,0),0,0);
return 0;
}