转自:
jrtplib 笔记(1) - CSDN博客
https://blog.csdn.net/alajl/article/details/5419489
最近要实现 non-compound rtcp(http://tools.ietf.org/html/draft-ietf-avt-rtcp-non-compound-02 )
因为我们使用的是 jrtplib 这个开源的包,这个包把 RTCP 的处理都封装好了,所以用户不用去过多的关心,但是我们要实现上述的功能,所以必须要修改这 个开源库关于 RTCP 处理部分的源代码,所以就花了一些时间,仔细的阅读了它,但是相关的文档不是很多,所以就只能靠自己去理解了啊。
在修改 RTCP 的实现时,我们有必要了解一下,原有 RTCP 的实现,所以我们就从下面开始了
在整个代码 里, RTPSession 这个对象是用户接触到最多的,在我们的实现中,我们用到了 RtpApp 这个类(这个对象是我们自己实现的,开源库是没有的) , 我们用 pollData ()轮询所有收到的数据,代码如下
void RtpApp::pollData() // 轮询所有数据
{
if(mSession == NULL)
return;
mSession->Poll();
mSession->BeginDataAccess();
if(mSession->GotoFirstSourceWithData() == false)
{
// if theres no data, sleep 1ms
mSession->EndDataAccess();
RTPTime::Wait(RTPTime(0, 1000));
}
else
{
// process the data
do
{
RTPPacket *pkt;
while ((pkt = mSession->GetNextPacket()) != NULL)
{
// 处理收到的数据
}
} while(mSession->GotoNextSourceWithData()); // 到下一个 source
mSession->EndDataAccess();
}
}
其中我们用到了 mSession->Poll(); 这个方法 用于轮询所有的数据。这个方法是 RTPSession 里对数据处理的主要方法,务必要弄明白它的原理,代码如下 :
int RTPSession::Poll() {
int status;
if (!created)
return ERR_RTP_SESSION_NOTCREATED;
if (usingpollthread)
return ERR_RTP_SESSION_USINGPOLLTHREAD;
/*
真正的对 socket 的操作,它把接受到的 RTP 和 RTCP 数据分别放到相应的队列里( awpacketlist 队列),并在 ProcessPolledData () 里处理了对应队列里的数据
*/
if ((status = rtptrans->Poll()) < 0)
return status;
return ProcessPolledData(); // 处理收到的数据,对应于 RFC3550 的逻辑部分
}
ProcessPolledData ()这个东东,里面的逻辑是相当多的,因为它处理了所有收到的 RTP 和 RTCP 包,并且根据协议对一些回复 RTCP 包的处理也在这个方法里,所以要修改 RTCP 包,那么这个方法就是切入点了。
在方法里,我们看到了一个循环,毫无疑问,这个东东就是遍历 rtptrans->Poll() 所收到的包,它放在了一个队列里( RTP 和 RTCP 都在同一个队列)
while ((rawpack = rtptrans->GetNextPacket()) != 0) {
……
}
那我们来一一分析,这个 while 循环所干的东西 :)
int RTPSession::ProcessPolledData() {
RTPRawPacket *rawpack;
int status;
SOURCES_LOCK
while ((rawpack = rtptrans->GetNextPacket()) != 0) {
sources.ClearOwnCollisionFlag();
// 清除了 SSRC 的冲突标记,因为根据 RFC3550, 如果 SSRC 冲突的话,应该要发送
//BYE 包的,所以,先上这个标记回到初始值。
// since our sources instance also uses the scheduler (analysis of incoming packets)
// we'll lock it
SCHED_LOCK
// 这东东对收到的 RTP 和 RTCP 包进行处理,例如建立 SSRC , CCRC 队列和
//SSRC 冲突标记、许多变量值的设置等等。
if ((status = sources.ProcessRawPacket(rawpack, rtptrans, acceptownpackets)) < 0) {
SCHED_UNLOCK
SOURCES_UNLOCK
RTPDelete(rawpack, GetMemoryManager());
return status;
}
SCHED_UNLOCK
// 检查是否 SSRC 产生了冲突,如果检测到的冲突,那么就应当发送 BYE 包了
if (sources.DetectedOwnCollision()) // collision handling!
{
printf("if collision/n");
bool created;
// created 的值决定了我们是否需要发送 BYE 包,如果冲突列表里,有了对应
// 的地址,那么将不发送 BYE 包,否则发送
if ((status = collisionlist.UpdateAddress(rawpack->GetSenderAddress(), rawpack->GetReceiveTime(), &created)) < 0) {
printf("if collisionlist updateAddress/n");
SOURCES_UNLOCK
RTPDelete(rawpack, GetMemoryManager());
return status;
}
// 需要生成一个新的 BYE 包
if (created)
{ // change our own SSRC
printf("if created/n");
PACKSENT_LOCK
bool hassentpackets = sentpackets;
PACKSENT_UNLOCK
// 如果我们已经用冲突的 SSRC 发送了 RTP 数据,那么就生成一个新的
//BYE 包,如果没有发送 RTP 的数据,那么这个时候发送 BYE 包是没有
// 意义的
if (hassentpackets) {
// Only send BYE packet if we've actually sent data using this
// SSRC
printf("if hassentpackets/n");
RTCPCompoundPacket *rtcpcomppack;
BUILDER_LOCK
// 生成一个新的 BYE 包
if ((status = rtcpbuilder.BuildBYEPacket(&rtcpcomppack, 0, 0, useSR_BYEifpossible)) < 0) {
printf("buildbytpacket/n");
BUILDER_UNLOCK
SOURCES_UNLOCK
RTPDelete(rawpack, GetMemoryManager());
return status;
}
BUILDER_UNLOCK
// 推入队列
byepackets.push_back(rtcpcomppack);
if (byepackets.size() == 1) // was the first packet, schedule a BYE packet (otherwise there's already one scheduled)
{
printf("bytepacketssize==1/n");
SCHED_LOCK
rtcpsched.ScheduleBYEPacket(rtcpcomppack->GetCompoundPacketLength());
SCHED_UNLOCK
}
}
// bye packet is built and scheduled, now change our SSRC
// and reset the packet count in the transmitter
BUILDER_LOCK
// 生成新的 SSRC
uint32_t newssrc = packetbuilder.CreateNewSSRC(sources);
BUILDER_UNLOCK
PACKSENT_LOCK
sentpackets = false;
PACKSENT_UNLOCK
// 删除旧的 SSRC
if ((status = sources.DeleteOwnSSRC()) < 0) {
printf("sources.DeleteOwnSSRC/n");
SOURCES_UNLOCK
RTPDelete(rawpack, GetMemoryManager());
return status;
}
// 绑定新的 SSRC
if ((status = sources.CreateOwnSSRC(newssrc)) < 0) {
printf("sources.CreateOwnSSRC/n");
SOURCES_UNLOCK
RTPDelete(rawpack, GetMemoryManager());
return status;
}
}
}
RTPDelete(rawpack, GetMemoryManager());
}//end while
SCHED_LOCK
RTPTime d = rtcpsched.CalculateDeterministicInterval(false);
SCHED_UNLOCK
RTPTime t = RTPTime::CurrentTime();
double Td = d.GetDouble();
RTPTime sendertimeout = RTPTime(Td * sendermultiplier);
RTPTime generaltimeout = RTPTime(Td * membermultiplier);
RTPTime byetimeout = RTPTime(Td * byemultiplier);
RTPTime colltimeout = RTPTime(Td * collisionmultiplier);
RTPTime notetimeout = RTPTime(Td * notemultiplier);
sources.MultipleTimeouts(t, sendertimeout, byetimeout, generaltimeout, notetimeout);
collisionlist.Timeout(t, colltimeout);
// We'll check if it's time for RTCP stuff
SCHED_LOCK
bool istime = rtcpsched.IsTime(); // 计算是否到了发送 RTCP 的时间
SCHED_UNLOCK
if (istime) {
printf("istime/n");
RTCPCompoundPacket *pack;
// we'll check if there's a bye packet to send, or just a normal packet
if (byepackets.empty()) { // 没有 BYE 包发送就发送正常的 RTCP 包
printf("byepacket empty and send RTCP packet/n");
// 是否支持 non-compound 的发送
if (allowNonCompoundRTCP == false) {
BUILDER_LOCK
printf("rtcpbuilder.BuildNextPacket with compound/n");
// 生成一个新的 RTCP 包
if ((status = rtcpbuilder.BuildNextPacket(&pack)) < 0) {
BUILDER_UNLOCK
SOURCES_UNLOCK
return status;
}
}else if (allowNonCompoundRTCP == true) {
if (firstSendRTCP) {
BUILDER_LOCK
printf("rtcpbuilder.BuildNextPacket with compound/n");
// 生成一个新的 RTCP 包,根据协议,发送 non-compound 之前,必 // 须保证有一个 compound 的包已经发送
if ((status = rtcpbuilder.BuildNextPacket(&pack)) < 0) {
BUILDER_UNLOCK
SOURCES_UNLOCK
return status;
}
}else {
BUILDER_LOCK
printf("rtcpbuilder.BuildNextPacket with non-compound/n");
// 生成一个新的 non compound RTCP 包
if ((status = rtcpbuilder.BuildNextPacketWithNonCompound(&pack)) < 0) {
BUILDER_UNLOCK
SOURCES_UNLOCK
return status;
}
}
firstSendRTCP = false;
}
BUILDER_UNLOCK
// 通过底层 socket ,发送出去咯
if ((status = rtptrans->SendRTCPData(pack->GetCompoundPacketData(), pack->GetCompoundPacketLength())) < 0) {
printf("rtptrans->SendRTCPData error!/n");
SOURCES_UNLOCK
RTPDelete(pack, GetMemoryManager());
return status;
}
PACKSENT_LOCK
sentpackets = true;
PACKSENT_UNLOCK
OnSendRTCPCompoundPacket(pack); // we'll place this after the actual send to avoid tampering
} else { // 如果 BYE 队列不为空,优先发送 BYE 包
printf("byepacket empty else/n");
pack = *(byepackets.begin());
byepackets.pop_front();
if ((status = rtptrans->SendRTCPData(pack->GetCompoundPacketData(), pack->GetCompoundPacketLength())) < 0) {
printf("rtptrans->SendRTCPData error!/n");
SOURCES_UNLOCK
RTPDelete(pack, GetMemoryManager());
return status;
}
PACKSENT_LOCK
sentpackets = true;
PACKSENT_UNLOCK
OnSendRTCPCompoundPacket(pack); // we'll place this after the actual send to avoid tampering
if (!byepackets.empty()) // more bye packets to send, schedule them
{
printf("byepackets.empty/n");
SCHED_LOCK
rtcpsched.ScheduleBYEPacket((*(byepackets.begin()))->GetCompoundPacketLength());
SCHED_UNLOCK
}
}
SCHED_LOCK
rtcpsched.AnalyseOutgoing(*pack);
SCHED_UNLOCK
RTPDelete(pack, GetMemoryManager());
}
SOURCES_UNLOCK
return 0;
}