mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-09 01:56:05 -05:00
420 lines
10 KiB
C++
420 lines
10 KiB
C++
|
/*
|
||
|
|
||
|
This file is a part of JRTPLIB
|
||
|
Copyright (c) 1999-2017 Jori Liesenborgs
|
||
|
|
||
|
Contact: jori.liesenborgs@gmail.com
|
||
|
|
||
|
This library was developed at the Expertise Centre for Digital Media
|
||
|
(http://www.edm.uhasselt.be), a research center of the Hasselt University
|
||
|
(http://www.uhasselt.be). The library is based upon work done for
|
||
|
my thesis at the School for Knowledge Technology (Belgium/The Netherlands).
|
||
|
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
copy of this software and associated documentation files (the "Software"),
|
||
|
to deal in the Software without restriction, including without limitation
|
||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||
|
Software is furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included
|
||
|
in all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||
|
IN THE SOFTWARE.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "rtcpscheduler.h"
|
||
|
#include "rtpsources.h"
|
||
|
#include "rtpdefines.h"
|
||
|
#include "rtcppacket.h"
|
||
|
#include "rtppacket.h"
|
||
|
#include "rtcpcompoundpacket.h"
|
||
|
#include "rtpsourcedata.h"
|
||
|
|
||
|
#define RTCPSCHED_MININTERVAL 1.0
|
||
|
|
||
|
namespace qrtplib
|
||
|
{
|
||
|
|
||
|
RTCPSchedulerParams::RTCPSchedulerParams() : mininterval(RTCP_DEFAULTMININTERVAL)
|
||
|
{
|
||
|
bandwidth = 1000; // TODO What is a good value here?
|
||
|
senderfraction = RTCP_DEFAULTSENDERFRACTION;
|
||
|
usehalfatstartup = RTCP_DEFAULTHALFATSTARTUP;
|
||
|
immediatebye = RTCP_DEFAULTIMMEDIATEBYE;
|
||
|
timeinit.Dummy();
|
||
|
}
|
||
|
|
||
|
RTCPSchedulerParams::~RTCPSchedulerParams()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
int RTCPSchedulerParams::SetRTCPBandwidth(double bw)
|
||
|
{
|
||
|
if (bw < 0.0)
|
||
|
return ERR_RTP_SCHEDPARAMS_INVALIDBANDWIDTH;
|
||
|
bandwidth = bw;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int RTCPSchedulerParams::SetSenderBandwidthFraction(double fraction)
|
||
|
{
|
||
|
if (fraction < 0.0 || fraction > 1.0)
|
||
|
return ERR_RTP_SCHEDPARAMS_BADFRACTION;
|
||
|
senderfraction = fraction;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int RTCPSchedulerParams::SetMinimumTransmissionInterval(const RTPTime &t)
|
||
|
{
|
||
|
double t2 = t.GetDouble();
|
||
|
|
||
|
if (t2 < RTCPSCHED_MININTERVAL)
|
||
|
return ERR_RTP_SCHEDPARAMS_BADMINIMUMINTERVAL;
|
||
|
|
||
|
mininterval = t;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
RTCPScheduler::RTCPScheduler(RTPSources &s, RTPRandom &r) : sources(s),nextrtcptime(0,0),prevrtcptime(0,0),rtprand(r)
|
||
|
{
|
||
|
Reset();
|
||
|
|
||
|
//std::cout << (void *)(&rtprand) << std::endl;
|
||
|
}
|
||
|
|
||
|
RTCPScheduler::~RTCPScheduler()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::Reset()
|
||
|
{
|
||
|
headeroverhead = 0; // user has to set this to an appropriate value
|
||
|
hassentrtcp = false;
|
||
|
firstcall = true;
|
||
|
avgrtcppacksize = 1000; // TODO: what is a good value for this?
|
||
|
byescheduled = false;
|
||
|
sendbyenow = false;
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::AnalyseIncoming(RTCPCompoundPacket &rtcpcomppack)
|
||
|
{
|
||
|
bool isbye = false;
|
||
|
RTCPPacket *p;
|
||
|
|
||
|
rtcpcomppack.GotoFirstPacket();
|
||
|
while (!isbye && ((p = rtcpcomppack.GetNextPacket()) != 0))
|
||
|
{
|
||
|
if (p->GetPacketType() == RTCPPacket::BYE)
|
||
|
isbye = true;
|
||
|
}
|
||
|
|
||
|
if (!isbye)
|
||
|
{
|
||
|
size_t packsize = headeroverhead+rtcpcomppack.GetCompoundPacketLength();
|
||
|
avgrtcppacksize = (size_t)((1.0/16.0)*((double)packsize)+(15.0/16.0)*((double)avgrtcppacksize));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (byescheduled)
|
||
|
{
|
||
|
size_t packsize = headeroverhead+rtcpcomppack.GetCompoundPacketLength();
|
||
|
avgbyepacketsize = (size_t)((1.0/16.0)*((double)packsize)+(15.0/16.0)*((double)avgbyepacketsize));
|
||
|
byemembers++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::AnalyseOutgoing(RTCPCompoundPacket &rtcpcomppack)
|
||
|
{
|
||
|
bool isbye = false;
|
||
|
RTCPPacket *p;
|
||
|
|
||
|
rtcpcomppack.GotoFirstPacket();
|
||
|
while (!isbye && ((p = rtcpcomppack.GetNextPacket()) != 0))
|
||
|
{
|
||
|
if (p->GetPacketType() == RTCPPacket::BYE)
|
||
|
isbye = true;
|
||
|
}
|
||
|
|
||
|
if (!isbye)
|
||
|
{
|
||
|
size_t packsize = headeroverhead+rtcpcomppack.GetCompoundPacketLength();
|
||
|
avgrtcppacksize = (size_t)((1.0/16.0)*((double)packsize)+(15.0/16.0)*((double)avgrtcppacksize));
|
||
|
}
|
||
|
|
||
|
hassentrtcp = true;
|
||
|
}
|
||
|
|
||
|
RTPTime RTCPScheduler::GetTransmissionDelay()
|
||
|
{
|
||
|
if (firstcall)
|
||
|
{
|
||
|
firstcall = false;
|
||
|
prevrtcptime = RTPTime::CurrentTime();
|
||
|
pmembers = sources.GetActiveMemberCount();
|
||
|
CalculateNextRTCPTime();
|
||
|
}
|
||
|
|
||
|
RTPTime curtime = RTPTime::CurrentTime();
|
||
|
|
||
|
if (curtime > nextrtcptime) // packet should be sent
|
||
|
return RTPTime(0,0);
|
||
|
|
||
|
RTPTime diff = nextrtcptime;
|
||
|
diff -= curtime;
|
||
|
|
||
|
return diff;
|
||
|
}
|
||
|
|
||
|
bool RTCPScheduler::IsTime()
|
||
|
{
|
||
|
if (firstcall)
|
||
|
{
|
||
|
firstcall = false;
|
||
|
prevrtcptime = RTPTime::CurrentTime();
|
||
|
pmembers = sources.GetActiveMemberCount();
|
||
|
CalculateNextRTCPTime();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
RTPTime currenttime = RTPTime::CurrentTime();
|
||
|
|
||
|
// // TODO: for debugging
|
||
|
// double diff = nextrtcptime.GetDouble() - currenttime.GetDouble();
|
||
|
//
|
||
|
// std::cout << "Delay till next RTCP interval: " << diff << std::endl;
|
||
|
|
||
|
if (currenttime < nextrtcptime) // timer has not yet expired
|
||
|
return false;
|
||
|
|
||
|
RTPTime checktime(0,0);
|
||
|
|
||
|
if (!byescheduled)
|
||
|
{
|
||
|
bool aresender = false;
|
||
|
RTPSourceData *srcdat;
|
||
|
|
||
|
if ((srcdat = sources.GetOwnSourceInfo()) != 0)
|
||
|
aresender = srcdat->IsSender();
|
||
|
|
||
|
checktime = CalculateTransmissionInterval(aresender);
|
||
|
}
|
||
|
else
|
||
|
checktime = CalculateBYETransmissionInterval();
|
||
|
|
||
|
// std::cout << "Calculated checktime: " << checktime.GetDouble() << std::endl;
|
||
|
|
||
|
checktime += prevrtcptime;
|
||
|
|
||
|
if (checktime <= currenttime) // Okay
|
||
|
{
|
||
|
byescheduled = false;
|
||
|
prevrtcptime = currenttime;
|
||
|
pmembers = sources.GetActiveMemberCount();
|
||
|
CalculateNextRTCPTime();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// std::cout << "New delay: " << nextrtcptime.GetDouble() - currenttime.GetDouble() << std::endl;
|
||
|
|
||
|
nextrtcptime = checktime;
|
||
|
pmembers = sources.GetActiveMemberCount();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::CalculateNextRTCPTime()
|
||
|
{
|
||
|
bool aresender = false;
|
||
|
RTPSourceData *srcdat;
|
||
|
|
||
|
if ((srcdat = sources.GetOwnSourceInfo()) != 0)
|
||
|
aresender = srcdat->IsSender();
|
||
|
|
||
|
nextrtcptime = RTPTime::CurrentTime();
|
||
|
nextrtcptime += CalculateTransmissionInterval(aresender);
|
||
|
}
|
||
|
|
||
|
RTPTime RTCPScheduler::CalculateDeterministicInterval(bool sender /* = false */)
|
||
|
{
|
||
|
int numsenders = sources.GetSenderCount();
|
||
|
int numtotal = sources.GetActiveMemberCount();
|
||
|
|
||
|
// std::cout << "CalculateDeterministicInterval" << std::endl;
|
||
|
// std::cout << " numsenders: " << numsenders << std::endl;
|
||
|
// std::cout << " numtotal: " << numtotal << std::endl;
|
||
|
|
||
|
// Try to avoid division by zero:
|
||
|
if (numtotal == 0)
|
||
|
numtotal++;
|
||
|
|
||
|
double sfraction = ((double)numsenders)/((double)numtotal);
|
||
|
double C,n;
|
||
|
|
||
|
if (sfraction <= schedparams.GetSenderBandwidthFraction())
|
||
|
{
|
||
|
if (sender)
|
||
|
{
|
||
|
C = ((double)avgrtcppacksize)/(schedparams.GetSenderBandwidthFraction()*schedparams.GetRTCPBandwidth());
|
||
|
n = (double)numsenders;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
C = ((double)avgrtcppacksize)/((1.0-schedparams.GetSenderBandwidthFraction())*schedparams.GetRTCPBandwidth());
|
||
|
n = (double)(numtotal-numsenders);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
C = ((double)avgrtcppacksize)/schedparams.GetRTCPBandwidth();
|
||
|
n = (double)numtotal;
|
||
|
}
|
||
|
|
||
|
RTPTime Tmin = schedparams.GetMinimumTransmissionInterval();
|
||
|
double tmin = Tmin.GetDouble();
|
||
|
|
||
|
if (!hassentrtcp && schedparams.GetUseHalfAtStartup())
|
||
|
tmin /= 2.0;
|
||
|
|
||
|
double ntimesC = n*C;
|
||
|
double Td = (tmin>ntimesC)?tmin:ntimesC;
|
||
|
|
||
|
// TODO: for debugging
|
||
|
// std::cout << " Td: " << Td << std::endl;
|
||
|
|
||
|
return RTPTime(Td);
|
||
|
}
|
||
|
|
||
|
RTPTime RTCPScheduler::CalculateTransmissionInterval(bool sender)
|
||
|
{
|
||
|
RTPTime Td = CalculateDeterministicInterval(sender);
|
||
|
double td,mul,T;
|
||
|
|
||
|
// std::cout << "CalculateTransmissionInterval" << std::endl;
|
||
|
|
||
|
td = Td.GetDouble();
|
||
|
mul = rtprand.GetRandomDouble()+0.5; // gives random value between 0.5 and 1.5
|
||
|
T = (td*mul)/1.21828; // see RFC 3550 p 30
|
||
|
|
||
|
// std::cout << " Td: " << td << std::endl;
|
||
|
// std::cout << " mul: " << mul << std::endl;
|
||
|
// std::cout << " T: " << T << std::endl;
|
||
|
|
||
|
return RTPTime(T);
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::PerformReverseReconsideration()
|
||
|
{
|
||
|
if (firstcall)
|
||
|
return;
|
||
|
|
||
|
double diff1,diff2;
|
||
|
int members = sources.GetActiveMemberCount();
|
||
|
|
||
|
RTPTime tc = RTPTime::CurrentTime();
|
||
|
RTPTime tn_min_tc = nextrtcptime;
|
||
|
|
||
|
if (tn_min_tc > tc)
|
||
|
tn_min_tc -= tc;
|
||
|
else
|
||
|
tn_min_tc = RTPTime(0,0);
|
||
|
|
||
|
// std::cout << "+tn_min_tc0 " << nextrtcptime.GetDouble()-tc.GetDouble() << std::endl;
|
||
|
// std::cout << "-tn_min_tc0 " << -nextrtcptime.GetDouble()+tc.GetDouble() << std::endl;
|
||
|
// std::cout << "tn_min_tc " << tn_min_tc.GetDouble() << std::endl;
|
||
|
|
||
|
RTPTime tc_min_tp = tc;
|
||
|
|
||
|
if (tc_min_tp > prevrtcptime)
|
||
|
tc_min_tp -= prevrtcptime;
|
||
|
else
|
||
|
tc_min_tp = 0;
|
||
|
|
||
|
if (pmembers == 0) // avoid division by zero
|
||
|
pmembers++;
|
||
|
|
||
|
diff1 = (((double)members)/((double)pmembers))*tn_min_tc.GetDouble();
|
||
|
diff2 = (((double)members)/((double)pmembers))*tc_min_tp.GetDouble();
|
||
|
|
||
|
nextrtcptime = tc;
|
||
|
prevrtcptime = tc;
|
||
|
nextrtcptime += RTPTime(diff1);
|
||
|
prevrtcptime -= RTPTime(diff2);
|
||
|
|
||
|
pmembers = members;
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::ScheduleBYEPacket(size_t packetsize)
|
||
|
{
|
||
|
if (byescheduled)
|
||
|
return;
|
||
|
|
||
|
if (firstcall)
|
||
|
{
|
||
|
firstcall = false;
|
||
|
pmembers = sources.GetActiveMemberCount();
|
||
|
}
|
||
|
|
||
|
byescheduled = true;
|
||
|
avgbyepacketsize = packetsize+headeroverhead;
|
||
|
|
||
|
// For now, we will always use the BYE backoff algorithm as described in rfc 3550 p 33
|
||
|
|
||
|
byemembers = 1;
|
||
|
pbyemembers = 1;
|
||
|
|
||
|
if (schedparams.GetRequestImmediateBYE() && sources.GetActiveMemberCount() < 50) // p 34 (top)
|
||
|
sendbyenow = true;
|
||
|
else
|
||
|
sendbyenow = false;
|
||
|
|
||
|
prevrtcptime = RTPTime::CurrentTime();
|
||
|
nextrtcptime = prevrtcptime;
|
||
|
nextrtcptime += CalculateBYETransmissionInterval();
|
||
|
}
|
||
|
|
||
|
void RTCPScheduler::ActiveMemberDecrease()
|
||
|
{
|
||
|
if (sources.GetActiveMemberCount() < pmembers)
|
||
|
PerformReverseReconsideration();
|
||
|
}
|
||
|
|
||
|
RTPTime RTCPScheduler::CalculateBYETransmissionInterval()
|
||
|
{
|
||
|
if (!byescheduled)
|
||
|
return RTPTime(0,0);
|
||
|
|
||
|
if (sendbyenow)
|
||
|
return RTPTime(0,0);
|
||
|
|
||
|
double C,n;
|
||
|
|
||
|
C = ((double)avgbyepacketsize)/((1.0-schedparams.GetSenderBandwidthFraction())*schedparams.GetRTCPBandwidth());
|
||
|
n = (double)byemembers;
|
||
|
|
||
|
RTPTime Tmin = schedparams.GetMinimumTransmissionInterval();
|
||
|
double tmin = Tmin.GetDouble();
|
||
|
|
||
|
if (schedparams.GetUseHalfAtStartup())
|
||
|
tmin /= 2.0;
|
||
|
|
||
|
double ntimesC = n*C;
|
||
|
double Td = (tmin>ntimesC)?tmin:ntimesC;
|
||
|
|
||
|
double mul = rtprand.GetRandomDouble()+0.5; // gives random value between 0.5 and 1.5
|
||
|
double T = (Td*mul)/1.21828; // see RFC 3550 p 30
|
||
|
|
||
|
return RTPTime(T);
|
||
|
}
|
||
|
|
||
|
} // end namespace
|
||
|
|