现在的位置: 首页 > 综合 > 正文

jrtplib接收数据包流程

2012年04月26日 ⁄ 综合 ⁄ 共 7570字 ⁄ 字号 评论关闭

近来接触RTP,自然涉及到jrtplib库,阅读3.7.1代码,片余时间,做点摘记,望今后可以快速回忆起来,也希望朋友阅读,指出不当之处,奉上您们的宝贵建议,一起学习进步。
接下去源码分析笔记都是基于:#ifndef RTP_SUPPORT_THREAD ,关于后台线程处理RTP包的接收,作者用了相关的类处理,不过同样会调用到以下涉及的一些接口函数,暂时还没细看。
可能会在以后作点笔记。
PS:若需转载,请注明文章来源地址

http://openregion.blog.com/2010/05/19/jrtplib接受数据包流程

RTP包的接收入口函数

int RTPSession::Poll()

{

……

㈠ if ((status = rtptrans->Poll()) < 0)

return status;

㈡ return ProcessPolledData();

}

说明:

If you’re not using the poll thread, this function must be called regularly to process incoming data and to send RTCP data when necessary.

rtptrans是RTPSession类的成员变量,它的创建过程如下:

① RTPUDPv4TransmissionParams transparams;

② status = m_rtpSession->Create(sessparams,&transparams);

int RTPSession::Create(const RTPSessionParams &sessparams,RTPTransmitter *transmitter)

{

……

③ rtptrans = transmitter;

……

}

————————————— ㈠ ————————————–

rtptrans->Poll() (rtpudpv4transmitter.cpp):

int RTPUDPv4Transmitter::Poll()

{

……

status = PollSocket(true); // poll RTP socket

if (status >= 0)

status = PollSocket(false); // poll RTCP socket

……

}

说明:

这个文件中的Rtpudpv4transmitter类实现了rtp包以及rtcp包的收发工作。

—————————————————————————–

PollSocket函数如下(rtpudpv4transmitter.cpp):

int RTPUDPv4Transmitter::PollSocket(bool rtp)

{

……

recvlen = recvfrom(sock,packetbuffer,RTPUDPV4TRANS_MAXPACKSIZE,0,(struct sockaddr *)&srcaddr,&fromlen);

……

pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET)RTPRawPacket(datacopy,recvlen,addr,curtime,rtp,GetMemoryManager());

……

rawpacketlist.push_back(pack);

……

}

关于class RTPRawPacket 类,说明如下:

This class is used by the transmission component to store the incoming RTP and RTCP data in.

最后调用rawpacketlist.push_back(pack);  将得到的包放到队列中去。

class RTPRawPacket 类的成员变量如下:

uint8_t *packetdata;       //接收到的原始包数据内容

size_t packetdatalength; //接收到的原始包数据长度

RTPTime receivetime;    //接收到原始包的时间

RTPAddress *senderaddress; //发送原始数据包的发送方地址

bool isrtp;       //是否是rtp包

————————————— ㈡ ————————————–

ProcessPolledData()函数代码概要如下(RTPSession.cpp):

int RTPSession::ProcessPolledData()

{

……

while ((rawpack = rtptrans->GetNextPacket()) != 0)

{

……

if ((status = sources.ProcessRawPacket(rawpack,rtptrans,acceptownpackets)) < 0)

……

}

……

status = rtcpbuilder.BuildNextPacket(&pack)

……

Status=rtptrans->SendRTCPData(pack->GetCompoundPacketData(),pack->GetCompoundPacketLength()) //发送rtcp包

}

—————————————————————————–

ProcessRawPacket (RTPSources.cpp)代码概要如下:

int RTPSources::ProcessRawPacket(RTPRawPacket *rawpack,RTPTransmitter *rtptrans,bool acceptownpackets)

{

……

return ProcessRawPacket(rawpack,transmitters,num,acceptownpackets);

}

注:这里两个ProcessRawPacket函数参数不一样。

—————————————————————————–

第二个ProcessRawPacket()代码概要如下:

int RTPSources::ProcessRawPacket(RTPRawPacket *rawpack,RTPTransmitter *rtptrans[],int numtrans,bool acceptownpackets)

{

int status;

if (rawpack->IsRTP()) // RTP packet

{

RTPPacket *rtppack;

// First, we’ll see if the packet can be parsed

rtppack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPPACKET) ① RTPPacket(*rawpack,GetMemoryManager());

if (rtppack == 0)

return ERR_RTP_OUTOFMEM;

……

Status =

② ProcessRTPPacket(rtppack,rawpack->GetReceiveTime(),senderaddress,&stored)

……

}

—————————————–①————————————

RTPPacket函数根据接收到的原始包RTPRawPacket &rawpack,重新组装成生成RTPPacket 类型的rtppacket信息存储单元,代码概要如下(Rtppacket.cpp):

RTPPacket::RTPPacket(RTPRawPacket &rawpack,RTPMemoryManager *mgr) : receivetime(rawpack.GetReceiveTime()),RTPMemoryObject(mgr)

{

Clear();

error = ParseRawPacket(rawpack);

}

—————————————————————————–

※(值得一看)

ParseRawPacket代码概要如下(Rtppacket.cpp):

int RTPPacket::ParseRawPacket(RTPRawPacket &rawpack)

{

uint8_t *packetbytes;

size_t packetlen;

uint8_t payloadtype;

RTPHeader *rtpheader;

bool marker;

int csrccount;

bool hasextension;

int payloadoffset,payloadlength;

int numpadbytes;

RTPExtensionHeader *rtpextheader;

uint16_t exthdrlen;

if (!rawpack.IsRTP()) // If we didn’t receive it on the RTP port, we’ll ignore it

return ERR_RTP_PACKET_INVALIDPACKET;

// The length should be at least the size of the RTP header

packetlen = rawpack.GetDataLength();

if (packetlen < sizeof(RTPHeader))

return ERR_RTP_PACKET_INVALIDPACKET;

packetbytes = (uint8_t *)rawpack.GetData();

rtpheader = (RTPHeader *)packetbytes;

……

return 0;

}

RTPPacket类型的RTP包中,有如下成员变量:

bool hasextension,hasmarker;

int numcsrcs;

uint8_t payloadtype;       //负载类型

uint32_t extseqnr,timestamp,ssrc; //rtp包序号,时间戳,同步源信息

uint8_t *packet,*payload;      //包始址 负载数据始址

size_t packetlength,payloadlength;        //包长度 负载数据长度

uint16_t extid;

uint8_t *extension;

size_t extensionlength;

bool externalbuffer;

RTPTime receivetime;

注:此函数提取出接收到的原始数据包中的包信息,重新组装生成了RTPPacket类型的信息存储单元,供本地使用。

—————————————–②————————————

ProcessRTPPacket(RTPSources.cpp)

int RTPSources::ProcessRTPPacket(RTPPacket *rtppack,const RTPTime &receivetime,const RTPAddress *senderaddress,bool *stored)

{

……

RTPInternalSourceData *srcdat;

//virtual function,is called when an RTP packet is about to be processed.

OnRTPPacket(rtppack,receivetime,senderaddress);

ssrc = rtppack->GetSSRC();

if ((status = ※1 ObtainSourceDataInstance (ssrc,&srcdat,&created)) < 0)

……

// The packet comes from a valid source, we can process it further now

// The following function should delete rtppack itself if something goes

// wrong

if ((status = srcdat->※2 ProcessRTPPacket(rtppack,receivetime,stored)) < 0)

……

}

说明:

Srcdat:为RTPInternalSourceData类型指针。

class RTPSources类维护着一个ssrc信息的哈希表:

RTPKeyHashTable<const uint32_t, RTPInternalSourceData*, RTPSources_GetHashIndex,  RTPSOURCES_HASHSIZE> sourcelist;

列表包含参与者RTP包的SSRC信息。该类提供函数用来轮询处理每个参与者的RTP和RTCP 数据包。每次收到包都会根据该包的SSRC 把该包塞入到哈希表中,而进一步的数据处理则交给更下层的RTPInternalSourceData类。该类的设计是为了更好的管理来自不同SSRC的数据。

OnRTPPacket (rtppack, receivetime, senderaddress)

:这是Rtpsession类中的虚方法,可以在Class Rtpsession类的派生类中实现该虚函数,并在需要处理RTP包时回调该函数。

————————————※1 —————————————-

ObtainSourceDataInstance(RTPSources.cpp):

int RTPSources::ObtainSourceDataInstance(uint32_t ssrc,RTPInternalSourceData **srcdat,bool *created)

{

……

srcdat2=RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPINTERNALSOURCEDATA)RTPInternalSourceData(ssrc,RTPSources::NoProbation,GetMemoryManager());

……

if ((status = sourcelist.AddElement(ssrc,srcdat2)) < 0)

……

}

说明:

RTPInternalSourceData :由参数ssrc生成管理RTP包存储的变量srcdat。变量srcdat 是RTPInternalSourceData类型,继承自RTPSourceData,具体管理RTP数据包的存储等。

AddElement :由参数ssrc和srcdat2来生成sourcelist 的元素,并将它加入到class RTPSources类维护的哈希表sourcelist 中。

bool *created :该函数参数是【out】型,如果class RTPSources类维护的哈希表sourcelist中已经存在相同的ssrc信息,则created = false;否则,调用AddElement来添加一个ssrc信息,created = true。

—————————————※2 ————————————-

ProcessRTPPacket (RTPInternalSourceData.cpp)代码如下:

int RTPSources::ProcessRTPPacket(RTPPacket *rtppack,const RTPTime &receivetime,const RTPAddress *senderaddress,bool *stored)

{

……

stats.ProcessPacket(rtppack,receivetime,tsunit,ownssrc,&accept,applyprobation,&onprobation);

……

// Now, we can place the packet in the queue

if (packetlist.empty())

{

*stored = true;

packetlist.push_back(rtppack);

return 0;

}

……

}

说明:

变量stats 是RTPSourceStats类,管理接收到包的状态参数。对它的操作被封装在RTPSourceData的接口函数中。

packetlist :属于class RTPSourceData ,定义如下:std::list<RTPPacket *> packetlist。※2调用处的srcdat为RTPInternalSourceData类型指针,具体负责RTP包数据存储。

至此,将接收到的数据包以RTPPacket为存储单元放入了队列之中。

获取接收到的RTP包过程

for (;;)

{  #ifndef RTP_SUPPORT_THREAD      status = sess.Poll();      checkerror(status);  #endif // RTP_SUPPORT_THREAD         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();  }

该过程首先查找获得数据源(GotoFirstSourceWithData 和GotoNextSourceWithData),然后检查该源下是否有数据包(GetNextPacket):

rtpsession.cpp 文件

RTPPacket *RTPSession::GetNextPacket()

{

if (!created)

return 0;

return sources.GetNextPacket();

}

RTPSources.cpp 文件

RTPPacket *RTPSources::GetNextPacket()

{

if (!sourcelist.HasCurrentElement())

return 0;

RTPInternalSourceData *srcdat = sourcelist.GetCurrentElement();

RTPPacket *pack = srcdat->GetNextPacket();

return pack;

}

RTPSourceData.cpp 文件

inline RTPPacket *RTPSourceData::GetNextPacket()

{

if (!validated)

return 0;

RTPPacket *p;

if (packetlist.empty())

return 0;

p = *(packetlist.begin());

packetlist.pop_front();

return p;

}

抱歉!评论已关闭.