mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 10:05:46 -05:00
427 lines
11 KiB
C++
427 lines
11 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)
|
|
{
|
|
pmembers = 0;
|
|
byemembers = 0;
|
|
pbyemembers = 0;
|
|
avgbyepacketsize = 0;
|
|
|
|
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)
|
|
{
|
|
std::size_t packsize = headeroverhead + rtcpcomppack.GetCompoundPacketLength();
|
|
avgrtcppacksize = (std::size_t)((1.0 / 16.0) * ((double) packsize) + (15.0 / 16.0) * ((double) avgrtcppacksize));
|
|
}
|
|
else
|
|
{
|
|
if (byescheduled)
|
|
{
|
|
std::size_t packsize = headeroverhead + rtcpcomppack.GetCompoundPacketLength();
|
|
avgbyepacketsize = (std::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)
|
|
{
|
|
std::size_t packsize = headeroverhead + rtcpcomppack.GetCompoundPacketLength();
|
|
avgrtcppacksize = (std::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(std::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
|
|
|