zoukankan      html  css  js  c++  java
  • jrtplib接收数据包流程

    近来接触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;

    }

  • 相关阅读:
    addEventListener-第三个参数 useCapture
    介绍(javascript调试)
    Linux_PXE服务器_RHEL7
    Linux_PXE服务器_RHEL7
    Linux_OpenSSH远程连接
    Linux_OpenSSH远程连接
    Python基本语法_强制数据类型转换
    Python基本语法_强制数据类型转换
    Linux_NetworkManager_RHEL7
    Linux_NetworkManager_RHEL7
  • 原文地址:https://www.cnblogs.com/zhiweiyouzhishenghuo/p/5005483.html
Copyright © 2011-2022 走看看