mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-29 20:10:22 -04: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
 | |
| 
 |