summaryrefslogtreecommitdiffstats
path: root/src/rtcpscheduler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rtcpscheduler.cpp')
-rw-r--r--src/rtcpscheduler.cpp416
1 files changed, 416 insertions, 0 deletions
diff --git a/src/rtcpscheduler.cpp b/src/rtcpscheduler.cpp
new file mode 100644
index 0000000..fa8510f
--- /dev/null
+++ b/src/rtcpscheduler.cpp
@@ -0,0 +1,416 @@
+/*
+
+ This file is a part of JRTPLIB
+ Copyright (c) 1999-2007 Jori Liesenborgs
+
+ Contact: jori.liesenborgs@gmail.com
+
+ This library was developed at the "Expertisecentrum Digitale 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"
+
+#include "rtpdebug.h"
+
+#define RTCPSCHED_MININTERVAL 1.0
+
+RTCPSchedulerParams::RTCPSchedulerParams() : mininterval(RTCP_DEFAULTMININTERVAL)
+{
+ bandwidth = 1000; // TODO What is a good value here?
+ senderfraction = RTCP_DEFAULTSENDERFRACTION;
+ usehalfatstartup = RTCP_DEFAULTHALFATSTARTUP;
+ immediatebye = RTCP_DEFAULTIMMEDIATEBYE;
+#if (defined(WIN32) || defined(_WIN32_WCE))
+ timeinit.Dummy();
+#endif // WIN32 || _WIN32_WCE
+}
+
+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) : sources(s),nextrtcptime(0,0),prevrtcptime(0,0)
+{
+ Reset();
+}
+
+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);
+}
+