近來接觸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;
}