最近在看 jrtplib的收包流程,看了这篇文章 jrtplib接收数据包流程 ,只是用的库老了点 V3.7 的,写的也太长了,不够简练,于是自己写一份
V3.11.1的简练点的收包流程说明,捡重点,无关的流程略过。
RTP包的接收入口函数 int RTPSession::Poll() { if ((status = rtptrans->Poll()) < 0) // 流程一, 默认调用 RTPUDPv4Transmitter::Poll() // 处理收到的buffer,转为元数据,存入容器
return ProcessPolledData(); // 流程二,将元数据解析为 RTP或RTCP包,分别处理之 }
————————————— 流程(一) 的剖析 ————————————– 说明: rtptrans是RTPSession类的成员变量,默认是Rtpudpv4transmitter类,它实现了rtp包以及rtcp包的收发工作 步骤1,分别从 rtpsock 和 rtcpsock 收数据 int RTPUDPv4Transmitter::Poll() { status = PollSocket(true); // poll RTP socket if (rtpsock != rtcpsock) // no need to poll twice when multiplexing { if (status >= 0) status = PollSocket(false); // poll RTCP socket } } 步骤2 int RTPUDPv4Transmitter::PollSocket(bool rtp) { // 收rtp或rtcp包 recvlen = recvfrom(sock,packetbuffer,RTPUDPV4TRANS_MAXPACKSIZE,0,(struct sockaddr *)&srcaddr,&fromlen); // 收到的buffer构建元数据 pack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPRAWPACKET)RTPRawPacket(datacopy,recvlen,addr,curtime,rtp,GetMemoryManager()); // 将元数据存入队列 rawpacketlist.push_back(pack); }
————————————— 流程(二) 的剖析 ————————————– 步骤1 int RTPSession::ProcessPolledData() { while ((rawpack = rtptrans->GetNextPacket()) != 0) { //内部遍历并解析元数据,得到RTPPacket包 if ((status = sources.ProcessRawPacket(rawpack,rtptrans,acceptownpackets)) < 0) } // 下面是发送rtcp相关的,没研究过 status = rtcpbuilder.BuildNextPacket(&pack) Status=rtptrans->SendRTCPData(pack->GetCompoundPacketData(),pack->GetCompoundPacketLength()) //发送rtcp包 } 步骤2,内部解析元数据,判断是RTP还是RTCP包,然后分别处理 int RTPSources::ProcessRawPacket(RTPRawPacket *rawpack ) { 步骤2.1 解析源数据, 得到RTPPacket包,如果你测试发送的是字符串,就可以看到payload属性的值了"1234456789AB" rtppack = RTPNew(GetMemoryManager(),RTPMEM_TYPE_CLASS_RTPPACKET) RTPPacket(*rawpack,GetMemoryManager()); 步骤2.2 处理RTP包 if ((status = ProcessRTPPacket(rtppack,rawpack->GetReceiveTime(),0,&stored)) < 0) 步骤2.3 处理RTCP包 status = ProcessRTCPCompoundPacket(&rtcpcomppack,rawpack->GetReceiveTime(),0); { 在函数内部,根据遍历RTCP包,根据包类型分别处理 SR, /**< An RTCP sender report. */ RR, /**< An RTCP receiver report. */ SDES, /**< An RTCP source description packet. */ BYE, /**< An RTCP bye packet. */ APP, /**< An RTCP packet containing application specific data. */ Unknown /**< The type of RTCP packet was not recognized. */ } // 注意把元数据解析成RTPpacket的函数如下 int RTPPacket::ParseRawPacket(RTPRawPacket &rawpack) } 步骤2.2 int RTPSources::ProcessRTPPacket( { // 虚函数,什么都没干,可扩展 OnRTPPacket(rtppack,receivetime,senderaddress); // 步骤2.2.1,构建 RTPInternalSourceData对象 if ((status = ObtainSourceDataInstance(ssrc,&srcdat,&created)) < 0) // 步骤2.2.2, 内部调用 RTPInternalSourceData::ProcessRTPPacket( ) 函数来处理 . RTP数据收包处理到此结束
if ((status = srcdat->ProcessRTPPacket(rtppack,receivetime,stored,this)) < 0) { //内部按序列号用容器存储,最多存32个 packetlist.push_front(rtppack); packetlist.insert(it,rtppack); } // 步骤2.2.3, 内部循环处理CSRC数组,没研究过 for (i = 0 ; i < num ; i++) { if ((status = ObtainSourceDataInstance(CSRCs[i],&csrcdat,&createdcsrc)) < 0) } } ok,到此RTPSession收RTP包后存入队列,收包流程就结束了,接下来就是处理这些收到的包了,官方示例如下:
sess.BeginDataAccess(); //同步锁 // check incoming packets // 开始遍历参与者中第一个有RTP数据的流,如果找到了,就返回tree,否则返回false。 //在接收数据时我们常用的是这套函数,因为如果没有数据要来都没用 if (sess.GotoFirstSourceWithData()) { //实际上是双层嵌套循环,第一层先遍历内部的RTPInternalSourceData对象,rcdat = sourcelist.GetCurrentElement(); do { RTPPacket *pack; // 再遍历该RTPInternalSourceData对象内部的 RTPpacket列表 while ((pack = sess.GetNextPacket()) != NULL) { // You can examine the data here // for test char pBuffer[100] ={0}; memcpy(pBuffer,pack->GetPayloadData(),pack->GetPayloadLength()); printf("Got packet -->%s \n",pBuffer); // 界面打印收到的字符串 // we don't longer need the packet, so // we'll delete it sess.DeletePacket(pack); } } while (sess.GotoNextSourceWithData()); } sess.EndDataAccess();