如何使用JRtpLib3.5.0库中的函数和类对象和让JRTPLIB 发送/接收数据时使用同一个端口收藏

来源:互联网 发布:mac 目标磁盘模式 编辑:程序博客网 时间:2024/05/22 00:41

本文介绍了如何使用JRtpLib3.5.0库中的函数和类对象。

1RTP

1.1、初始化

生成RTPSession对象:RTPSessionSession

初始化RTPSessioSession.Create(RTPSessionParams,RTPUDPv4TransmissionParams),RTPSessionParams的关键作用就是设置时间戳,形式如下:

  RTPSessionParamsSessionParams;

 SessionParams.SetOwnTimestampUnit(1.0/8000.0)1/8000表示:当RTP会话传输8000HZ数据的时间戳)

 RTPUDPv4TransmissionParams表示传输协议采用UDPIP采用IPV4时端口参数设置。形式如下:

RTPUDPv4TransmissionParams transparams ;
transparams . SetPortbase ( 8 0 0 0 ) ;
8000表示端口号)。

注意:Create一个重载函数,有多种形式。而第二个参数的形式根据传输协议和IP地址版本来定。

Session对象创建示例:

RTPSession session;

IntStatus = session.Create(SessionParams,ransparams);

if(Status < 0)

{

   std::err<<RTPGetErrorString(Status)<<std::endl;

   exit(-1);

}

Create调用成功后,下面就要增加目的地址,使用RTPSession成员函数AddDestination,如果使用IPV4地址则其参数类型为RTPIPV4Address。程序示例:

u_i n t 8 _t  l o c a l i p [ ]={1 2 7 , 0 , 0 , 1};
RTPIPv4Address addr ( l o c a l i p , 9 0 0 0 ) ;
status = session.AddDestination ( addr ) ;
if (status < 0)
{

    std : : cerr << RTPGetErrorString ( st a t u s ) << s td : : endl;
      exit(?1);
}

 

2、数据发送

初始化后,接下来可以调用 RTPSession 类的数据发送函数 SendPacket(),向所有目的地址发送流媒体数据。这是一个重载函数,具有多种调用形式(参看 RTPSession 类对该函数的定义),例如在下面的调用形式:

int SendPacket(void *data,int len,unsigned char pt,boolmark,unsigned long timestampinc)

其中第一个参数是待发送的数据缓冲区地址,第二个参数是待发送数据的长度,后面依次是 RTP 负载类型、标识和时基增量。

对于同一个 RTP 会话双方,负载类型、标识和时戳增量相同,可以对它们提前设置,调用 RTPSession 类的SetDefaultPayloadType()SetDefaultMark()SetDefaultTimeStampIncrement()函数就可实现。在设会话双方,负载类型、标识和时戳增量,就可以使用SendPacket的简化类型:SendParket(void *data, intlen)

3、数据接收

接收实时流媒体数据,通过调用 RTPSession 类的 PollData()函数来接收收到的 RTP RTCP 数据报。同一个RTP 会话中允许有多个参与者( 源),通过调用GotoFirstSourceWithData() GotoNextSourceWithData()函数来实现源的遍历,通过 GetNextPacket()函数提取 RTP 数据包。数据包接受以RTPSession的成员函数BeginDataAccess开始、EndDataAccess结束示例:

sess.BeginDataAccess();

if(sess.GotoFirstSourceWithData())

{
   do{
            RTPPacket *pack?
            while ((pack = sess.GetNextPacket()) != NULL)
            {

                printf("Got packet !\n")? delete pack?

                 delete pack;

           }
            } while (sess.GotoNextSourceWithData())?

}

sess.EndDataAccess();

 4、数据传输结束,释放资源

RTPTime(10.0);//释放资源最多等待十秒

sess.BYEDestroy(delay, Time' s up,9);//释放占有的

 

 

 

1.开始

 

1创建一个session

为了使用这个库,你首先需要构建一个session,类型为RTPSession,然后调用create()成员函数创建,它带有一个参数,是端口号,例子:

i nclude"rtpsession.h"

             

             int main(void)

             {

                    RTPSession sess;

                    

                    sess.Create(5000);

                    return 0;

             }

 

1 错误

   在这个库中,如果条拥有错误发生,所有的函数都会返回一个int 或者 false,你可以通过调用RTPGetErrorString()函数来判断是否出错,有错,他会返回详细的错误信息,否则为0。例子:

include<stdio.h>

              include "rtpsession.h"

             

             int main(void)

             {

                    RTPSession sess;

                    int status;

                    char *msg;

                    

                    status = sess.Create(5000);

                    msg = RTPGetErrorString(status);

                    printf("%s\n",msg);

                    return 0;

             }

 

1        其他初始化

   你很可能会用到时间戳单元,设置时间戳单元调用函数RTPSession 的成员函数SetTimestampUnit(),单位是秒。

   例如:你要发送8000赫兹的采样频率,你需要设置时间戳,由于它是每秒8000次增加,所以时间戳单元为1/8000,这也是这个库的默认设置。如果为44100赫兹,则如下:

sess.SetTimestampUnit(1.0/44100.0);

 

                 发送和接收数据

 

2         设置目的地址:

     在你发送数据之前,必须指定包所要发送的目的地址。可使用的函数有AddDestination,DeleteDestination ClearDestinations。例如:

unsigned long addr =ntohl(inet_addr("127.0.0.1"));

                    sess.AddDestination(addr,5000);

既是将包发送到本机的5000端口上。Ntohl是将网络字节序转换为长整形。

 

2  发送RTP

   当已指定好包的目标地址后就可以发送了。调用成员函数SendPacket(),这是一个可以重载的函数,他有5种函数,可以指定发送的负载类型,标志符,时间戳增量,

如(其中的一种):

                    sess.SendPacket("1234567890",10,0,false,10);

参数1为发送的数据,2为数据长度,3为负载类型,4为标志符,5为时间戳增量。

 

如果你所要发送的数据的负载类型,标识符和时间戳增量是相同的,你可以使用另一种定义,

但是使用之前你必须为这些参数设置默认得值,课题通过调用成员函数SetDefaultPayloadType,SetDefaultMark SetDefaultTimeStampIncrement。这样你就可以在SendPacket()函数中不用设置后三个参数了。

sess.SetDefaultPayloadType(0);

                    sess.SetDefaultMark(false);

                    sess.SetDefaultTimeStampIncrement(10);

sess.SendPacket("1234567890",10);

 

2接收数据包

首先你需要调用函数PollData(),这个函数是处理接受到的RTPRTCP包,他会处理所有到达端口的包,因此你可以重复调用函数GotoFirstSource()和 GotoNextSource()来遍历所有的源,如果你只是对各个源所携带的数据有兴趣,你可以调用GotoFirstSourceWithData  GotoNextSourceWithData来获取接收到的各个源的数据,所有以上函数调用成功返回值为真,否则是false,当一个源正在被处理时,你可以调用函数GetNextPacket()来获取下一个RTP数据包,但是使用完必须delete掉。例子:

if(sess.GotoFirstSourceWithData())

                    {

                           do

                           {

                                  RTPPacket *pack;

                                  

                                  pack = sess.GetNextPacket();

                                  

                                  // process packet

                                  

                                  delete pack;

                                  

                           } while (sess.GotoNextSourceWithData());

                    }

       更多关于RTPPacket的信息看手册。

 

2  接受方式

 JRTPLIB  RTP 数据报定义了三种接收模式,其中每种接收模式都具体规定了哪些到达的 RTP 数据报将会被接受,而哪些到达的 RTP 数据报将会被拒绝。通过调用 RTPSession 类的 SetReceiveMode() 方法可以设置下列这些接收模式:
  RECEIVEMODE_ALL
  缺省的接收模式,所有到达的 RTP 数据报都将被接受;
  RECEIVEMODE_IGNORESOME
  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被接受,而被拒绝的发送者列表可以通过调用 AddToIgnoreList()DeleteFromIgnoreList()  ClearIgnoreList() 方法来进行设置;
  RECEIVEMODE_ACCEPTSOME
  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被拒绝,而被接受的发送者列表可以通过调用 AddToAcceptList()DeleteFromAcceptList  ClearAcceptList() 方法来进行设置。

2多播

 

251  发送到一个多播组

  要想加入一个多播组你不需要做个多的设定。只需要建立一个目的地指表或数组,RTPRTCP包就会自动多播发送了。

  唯一可能需要做的事情是设定数据包的Time ToLive(TTL) (生存时间),他决定了一个数据包的存在时间,位于IP 头部,可以是用函数SetMulticastTTL来设定。

 

25从多播网络接收数据

  你必须首先加入一个多播网络才可以重中接收数据。使用函数JoinMulticastGroup,LeaveMulticastGroupLeaveAllMulticastGroups,他们是以IP为参数的。

 

26  关于接受和发送RTCP

 

   这个库可以很简单的处理RTCP信息,只要你正常调用PollData()函数,他会自动处理收到的RTCP信息,并对rtcp信息进行统计判断是否需要发送RR,而调用SendPacket()来决定什么时候发送RTCP SR包及其信息。

 

控制消息

 

31  自己的消息

通过调用 RTPSession 类提供的SetLocalName()SetLocalEMail()SetLocalLocation()SetLocalPhone()SetLocalTool()  SetLocalNote()方法,JRTPLIB 又允许程序员对RTP会话的控制信息进行设置。所有这些方法在调用时都带有两个参数,其中第一个参数是一个 char 型的指针,指向将要被设置的数据;而第二个参数则是一个 int 型的数值,表明该数据中的前面多少个字符将会被使用。例如下面的语句可以被用来设置控制信息中的电子邮件地址: 
sess.SetLocalEMail("xiaowp@linuxgam.com",19); 
     
 RTP 会话过程中,不是所有的控制信息都需要被发送,通过调用 RTPSession 类提供的EnableSendName()EnableSendEMail()EnableSendLocation()EnableSendPhone()EnableSendTool() EnableSendNote() 方法,可以为当前 RTP 会话选择将被发送的控制信息。

 
3
其他消息

   你可以通过函数GetCurrentSourceInfo() 获得当前消息源的信息,另外你可以首先获得源的SSRC,然后调用GetSourceInfo,参数为SSRC,获得源的信息。

 

4  句柄

Youcan specify handlers for certain types of events. For example: whena

source joins the session, when a source times out, whenSSRC identifiers

collide, ...

   Formore information about the use of handlers you should take alook

atthe reference manual (manual.txt)

 

 

 

 让JRTPLIB 发送/接收数据时使用同一个端口

 

这两周在做MPEG4视频传输时,想借助STUN协议穿透内网实现P2P,但是却发现JRTPLIB在发送数据和接收数据时使用的不是同一个PORT,这就可能导致无法穿透内网,我的想法大致如下:

假设ARTP使用端口1000,则起对应的RTCP端口必定为1001,在初始化RTP信令后,JRTPLIB会随机使用另外一个端口3333来发送RTP数据,1000端口是用来接收RTP数据的,1001接收RTCP包。

假设BRTP使用端口2000,则起对应的RTCP端口必定为2001,在初始化RTP信令后,JRTPLIB会随机使用另外一个端口4444来发送RTP数据,2000端口是用来接收RTP数据的,2001接收RTCP包。

又假设AB均在NAT后,经NAT转换后,利用STUN协议,A开始向B发包,B也向A发包,由于NAT的存在,B发给A的包并不是A发出去的目的地址端口发过来的,目的端口只受数据不发数据(A将数据包发给BRTP接收端口后,NAT A就只接收B的这个RTP端口发过来的数据,对于B用来发送数据的端口而言,是不请自到的),因此NAT A会丢弃B发过来的包,同理NAT B也会丢弃A发过来的包。

这样一来,为了能UDP PUNCH HOLEAB就必须再往对方用来发送数据的端口打一个洞,以便欺骗NAT,但UDP是不可靠的,而NAT影射也有时间限制,要保证NAT的影射关系不变就的定时打洞给对方,个人感觉这种方法不是很好。

经过查看JRTPLIB源码,它的Create()函数里是这么写的

addr.sin_family = AF_INET;
addr.sin_port = htons(0); //
就是这里使用随机端口
addr.sin_addr.s_addr = htonl(0);
if (bind(sendsock,(struct sockaddr*)&addr,sizeof(struct sockaddr)) != 0)
{
return ERR_RTP_CANTBINDSOCKET;
}

socklen = sizeof(struct sockaddr_in);
if (getsockname(sendsock,(struct sockaddr*)&addr,&socklen) != 0)
{
return ERR_RTP_CANTGETSOCKETPORT;
}
sendport = ntohs(addr.sin_port);//
这里是发送数据的端口

很明显,它用的是随机端口,BIND成功后再查询这个端口的,在RTPSession里用于获取发送端口的函数GetSendPort()直接返回了这个sendport,如下:

int GetSendPort()

return sendport; 
}

为此,我只好修改JRTPLIB V2.9的源码了,在rtpconnection.cpp里,将发送数据的sendto语句里的socket都换成rtpsocket,让他使用bindrtp端口的socket即可,同时删除sendsocket,并将GetSendPort()直接返回portbase。经过修改,发送/接收数据时就只有RTPRTCP端口了,而且收发数据都通过RTP绑定的那个端口进行。

0 0
原创粉丝点击