jrtplib提供的SendPacket()函数不能实现发送自定义的数据包序号,只能从一个随机数开始依次相加。
如果想要实现发送自定义的数据包序号则需要对源码进行修改。
修改方法如下:
第一步:打开源码的rtpsession.cpp,在文件中加入这么一段代码,重载SendPacket函数。
int RTPSession::SendPacket(const void *data,size_t len,
uint8_t pt,bool mark,uint32_t timestampinc,uint16_t seqnum)
{
int status;
if (!created)
return ERR_RTP_SESSION_NOTCREATED;
BUILDER_LOCK
if ((status = packetbuilder.BuildPacket(data,len,pt,mark,timestampinc,seqnum)) < 0)
{
BUILDER_UNLOCK
return status;
}
if ((status = SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0)
{
BUILDER_UNLOCK
return status;
}
BUILDER_UNLOCK
SOURCES_LOCK
sources.SentRTPPacket();
SOURCES_UNLOCK
PACKSENT_LOCK
sentpackets = true;
PACKSENT_UNLOCK
return 0;
}
在rtpsession.h文件中将其进行声明:
/** Sends the RTP packet with payload \c data which has length \c len.
* It will use payload type \c pt, marker \c mark and after the packet has been built, the
* timestamp will be incremented by \c timestampinc
* and the sequence number will be set to \c seqnum.
*/
int SendPacket(const void *data, size_t len,
uint8_t pt, bool mark, uint32_t timestampinc, uint16_t seqnum);
第二步:重载int RTPPacketBuilder::BuildPacket函数
源码中的SendPacket函数中调用的代码是这样的
if ((status = packetbuilder.BuildPacket(data,len,pt,mark,timestampinc)) < 0)
{
BUILDER_UNLOCK
return status;
}
其中BuildPacket参数没有数据包序号这一项,我们需要重载它,让它能够传递数据包序号。
从第一步的代码中可以看出我们对它进行了修改:
if ((status = packetbuilder.BuildPacket(data,len,pt,mark,timestampinc,seqnum)) < 0)
{
BUILDER_UNLOCK
return status;
}
传递的参数数量发生了变话,多出了一个seqnum。
重载方法:在rtppacketbuilder.cpp中加入以下代码,并在rtppacketbuider.h中声明它,与第一步方法相同,就不再赘述了。
int RTPPacketBuilder::BuildPacket(const void *data,size_t len,
uint8_t pt,bool mark,uint32_t timestampinc,uint16_t seqnum)
{
if (!init)
return ERR_RTP_PACKBUILD_NOTINIT;
return PrivateBuildPacket(data,len,seqnum,pt,mark,timestampinc,false);
}
其中的PrivateBuildPacket函数也是重载过的,否则不可以传递seqnum这个参数。
第三步 重载PrivateBuildPacket函数
在在rtppacketbuilder.cpp中加入以下代码,并在rtppacketbuider.h中声明它,与前两步方法相同,就不再赘述了。
int RTPPacketBuilder::PrivateBuildPacket(const void *data,size_t len,uint16_t seqnum,
uint8_t pt,bool mark,uint32_t timestampinc,bool gotextension,
uint16_t hdrextID,const void *hdrextdata,size_t numhdrextwords)
{
// RTPPacket p(pt,data,len,seqnum,timestamp,ssrc,mark,numcsrcs,csrcs,gotextension,hdrextID,
// (uint16_t)numhdrextwords,hdrextdata,buffer,maxpacksize,GetMemoryManager());
RTPPacket p(pt,data,len,seqnum,timestamp,ssrc,mark,numcsrcs,csrcs,gotextension,hdrextID,
(uint16_t)numhdrextwords,hdrextdata,buffer,maxpacksize,GetMemoryManager());
int status = p.GetCreationError();
if (status < 0)
return status;
packetlength = p.GetPacketLength();
if (numpackets == 0) // first packet
{
lastwallclocktime = RTPTime::CurrentTime();
lastrtptimestamp = timestamp;
prevrtptimestamp = timestamp;
}
else if (timestamp != prevrtptimestamp)
{
lastwallclocktime = RTPTime::CurrentTime();
lastrtptimestamp = timestamp;
prevrtptimestamp = timestamp;
}
numpayloadbytes += (uint32_t)p.GetPayloadLength();
numpackets++;
timestamp += timestampinc;
//seqnr++;
return 0;
}
到此为止,对源码的修改就结束了,下面我们来进行测试。
将源码给的例程进行修改来调用我们自己重载的sendPacket函数
代码如下:
/*
Here's a small IPv4 example: it asks for a portbase and a destination and
starts sending packets to that destination.
*/
#include "rtpsession.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtperrors.h"
#include "rtplibraryversion.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace jrtplib;
//
// This function checks if there was a RTP error. If so, it displays an error
// message and exists.
//
void checkerror(int rtperr)
{
if (rtperr < 0)
{
std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;
exit(-1);
}
}
//
// The main routine
//
int main(void)
{
#ifdef RTP_SOCKETTYPE_WINSOCK
WSADATA dat;
WSAStartup(MAKEWORD(2,2),&dat);
#endif // RTP_SOCKETTYPE_WINSOCK
RTPSession sess;
uint16_t portbase,destport;
uint32_t destip;
std::string ipstr;
int status,i,num;
std::cout << "Using version " << RTPLibraryVersion::GetVersion().GetVersionString() << std::endl;
// First, we'll ask for the necessary information
std::cout << "Enter local portbase:" << std::endl;
std::cin >> portbase;
std::cout << std::endl;
std::cout << "Enter the destination IP address" << std::endl;
std::cin >> ipstr;
destip = inet_addr(ipstr.c_str());
if (destip == INADDR_NONE)
{
std::cerr << "Bad IP address specified" << std::endl;
return -1;
}
// The inet_addr function returns a value in network byte order, but
// we need the IP address in host byte order, so we use a call to
// ntohl
destip = ntohl(destip);
std::cout << "Enter the destination port" << std::endl;
std::cin >> destport;
std::cout << std::endl;
std::cout << "Number of packets you wish to be sent:" << std::endl;
std::cin >> num;
// Now, we'll create a RTP session, set the destination, send some
// packets and poll for incoming data.
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
// IMPORTANT: The local timestamp unit MUST be set, otherwise
// RTCP Sender Report info will be calculated wrong
// In this case, we'll be sending 10 samples each second, so we'll
// put the timestamp unit to (1.0/10.0)
sessparams.SetOwnTimestampUnit(1.0/10.0);
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase);
status = sess.Create(sessparams,&transparams);
checkerror(status);
RTPIPv4Address addr(destip,destport);
status = sess.AddDestination(addr);
checkerror(status);
for (i = 1 ; i <= num ; i++)
{
printf("\nSending packet %d/%d\n",i,num);
// send the packet
status = sess.SendPacket((void *)"1234567890",10,0,false,10,0xffff);
checkerror(status);
status = sess.SendPacket((void *)"1234567890",10,0,false,10);
checkerror(status);
sess.BeginDataAccess();
// check incoming packets
if (sess.GotoFirstSourceWithData())
{
do
{
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL)
{
// You can examine the data here
printf("Got packet !\n");
// we don't longer need the packet, so
// we'll delete it
sess.DeletePacket(pack);
}
} while (sess.GotoNextSourceWithData());
}
sess.EndDataAccess();
#ifndef RTP_SUPPORT_THREAD
status = sess.Poll();
checkerror(status);
#endif // RTP_SUPPORT_THREAD
RTPTime::Wait(RTPTime(1,0));
}
sess.BYEDestroy(RTPTime(10,0),0,0);
#ifdef RTP_SOCKETTYPE_WINSOCK
WSACleanup();
#endif // RTP_SOCKETTYPE_WINSOCK
return 0;
}
在网络调试助手中我们可以看到:
程序一次发送两个数据包,一个是调用我们自己修改的SendPacket函数,第二次是发送代码中原本的SendPacket函数,从数据中可以看到第一行的FF FF是我们自定义的数据包序号,而第二行是jrtplib给的数据包序号。下面的数据也是,我们自定义的数据包序号固定不变,jrtplib发送的数据包序号会自动+1。
到此我们就完成了给出自定义数据包序号的接口。