256 lines
7.9 KiB
C
Executable File
256 lines
7.9 KiB
C
Executable File
/* RTCP code taken directly from the most recent RTP specification:
|
|
* RFC 3550
|
|
* Implementation
|
|
*/
|
|
|
|
#include "rtcp_from_spec.h"
|
|
|
|
/*****
|
|
|
|
A.7 Computing the RTCP Transmission Interval
|
|
|
|
The following functions implement the RTCP transmission and reception
|
|
rules described in Section 6.2. These rules are coded in several
|
|
functions:
|
|
|
|
o rtcp_interval() computes the deterministic calculated
|
|
interval, measured in seconds. The parameters are defined in
|
|
Section 6.3.
|
|
|
|
o OnExpire() is called when the RTCP transmission timer expires.
|
|
|
|
o OnReceive() is called whenever an RTCP packet is received.
|
|
|
|
Both OnExpire() and OnReceive() have event e as an argument. This is
|
|
the next scheduled event for that participant, either an RTCP report
|
|
or a BYE packet. It is assumed that the following functions are
|
|
available:
|
|
|
|
o Schedule(time t, event e) schedules an event e to occur at
|
|
time t. When time t arrives, the function OnExpire is called
|
|
with e as an argument.
|
|
|
|
o Reschedule(time t, event e) reschedules a previously scheduled
|
|
event e for time t.
|
|
|
|
o SendRTCPReport(event e) sends an RTCP report.
|
|
|
|
o SendBYEPacket(event e) sends a BYE packet.
|
|
|
|
o TypeOfEvent(event e) returns EVENT_BYE if the event being
|
|
processed is for a BYE packet to be sent, else it returns
|
|
EVENT_REPORT.
|
|
|
|
o PacketType(p) returns PACKET_RTCP_REPORT if packet p is an
|
|
RTCP report (not BYE), PACKET_BYE if its a BYE RTCP packet,
|
|
and PACKET_RTP if its a regular RTP data packet.
|
|
|
|
o ReceivedPacketSize() and SentPacketSize() return the size of
|
|
the referenced packet in octets.
|
|
|
|
o NewMember(p) returns a 1 if the participant who sent packet p
|
|
is not currently in the member list, 0 otherwise. Note this
|
|
function is not sufficient for a complete implementation
|
|
because each CSRC identifier in an RTP packet and each SSRC in
|
|
a BYE packet should be processed.
|
|
|
|
o NewSender(p) returns a 1 if the participant who sent packet p
|
|
is not currently in the sender sublist of the member list, 0
|
|
otherwise.
|
|
|
|
o AddMember() and RemoveMember() to add and remove participants
|
|
from the member list.
|
|
|
|
o AddSender() and RemoveSender() to add and remove participants
|
|
from the sender sublist of the member list.
|
|
*****/
|
|
|
|
double rtcp_interval(int members, int senders, double rtcp_bw, int we_sent,
|
|
double avg_rtcp_size, int initial) {
|
|
/*
|
|
* Minimum average time between RTCP packets from this site (in
|
|
* seconds). This time prevents the reports from `clumping' when
|
|
* sessions are small and the law of large numbers isn't helping
|
|
* to smooth out the traffic. It also keeps the report interval
|
|
* from becoming ridiculously small during transient outages like
|
|
* a network partition.
|
|
*/
|
|
double const RTCP_MIN_TIME = 5.;
|
|
/*
|
|
* Fraction of the RTCP bandwidth to be shared among active
|
|
* senders. (This fraction was chosen so that in a typical
|
|
* session with one or two active senders, the computed report
|
|
* time would be roughly equal to the minimum report time so that
|
|
* we don't unnecessarily slow down receiver reports.) The
|
|
* receiver fraction must be 1 - the sender fraction.
|
|
*/
|
|
double const RTCP_SENDER_BW_FRACTION = 0.25;
|
|
double const RTCP_RCVR_BW_FRACTION = (1 - RTCP_SENDER_BW_FRACTION);
|
|
/*
|
|
* To compensate for "unconditional reconsideration" converging to a
|
|
* value below the intended average.
|
|
*/
|
|
double const COMPENSATION = 2.71828 - 1.5;
|
|
|
|
double t; /* interval */
|
|
double rtcp_min_time = RTCP_MIN_TIME;
|
|
int n; /* no. of members for computation */
|
|
|
|
/*
|
|
* Very first call at application start-up uses half the min
|
|
* delay for quicker notification while still allowing some time
|
|
* before reporting for randomization and to learn about other
|
|
* sources so the report interval will converge to the correct
|
|
* interval more quickly.
|
|
*/
|
|
if (initial) {
|
|
rtcp_min_time /= 2;
|
|
}
|
|
|
|
/*
|
|
* If there were active senders, give them at least a minimum
|
|
* share of the RTCP bandwidth. Otherwise all participants share
|
|
* the RTCP bandwidth equally.
|
|
*/
|
|
n = members;
|
|
if (senders > 0 && senders < members * RTCP_SENDER_BW_FRACTION) {
|
|
if (we_sent) {
|
|
rtcp_bw *= RTCP_SENDER_BW_FRACTION;
|
|
n = senders;
|
|
} else {
|
|
rtcp_bw *= RTCP_RCVR_BW_FRACTION;
|
|
n -= senders;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The effective number of sites times the average packet size is
|
|
* the total number of octets sent when each site sends a report.
|
|
* Dividing this by the effective bandwidth gives the time
|
|
* interval over which those packets must be sent in order to
|
|
* meet the bandwidth target, with a minimum enforced. In that
|
|
* time interval we send one report so this time is also our
|
|
* average time between reports.
|
|
*/
|
|
t = avg_rtcp_size * n / rtcp_bw;
|
|
if (t < rtcp_min_time)
|
|
t = rtcp_min_time;
|
|
|
|
/*
|
|
* To avoid traffic bursts from unintended synchronization with
|
|
* other sites, we then pick our actual next report interval as a
|
|
* random number uniformly distributed between 0.5*t and 1.5*t.
|
|
*/
|
|
t = t * (drand48() + 0.5);
|
|
t = t / COMPENSATION;
|
|
return t;
|
|
}
|
|
|
|
void OnExpire(event e, int members, int senders, double rtcp_bw, int we_sent,
|
|
double *avg_rtcp_size, int *initial, time_tp tc, time_tp *tp,
|
|
int *pmembers) {
|
|
/* This function is responsible for deciding whether to send
|
|
* an RTCP report or BYE packet now, or to reschedule transmission.
|
|
* It is also responsible for updating the pmembers, initial, tp,
|
|
* and avg_rtcp_size state variables. This function should be called
|
|
* upon expiration of the event timer used by Schedule(). */
|
|
|
|
double t; /* Interval */
|
|
double tn; /* Next transmit time */
|
|
|
|
/* In the case of a BYE, we use "unconditional reconsideration" to
|
|
* reschedule the transmission of the BYE if necessary */
|
|
|
|
if (TypeOfEvent(e) == EVENT_BYE) {
|
|
t = rtcp_interval(members, senders, rtcp_bw, we_sent, *avg_rtcp_size,
|
|
*initial);
|
|
tn = *tp + t;
|
|
if (tn <= tc) {
|
|
SendBYEPacket(e);
|
|
exit(1);
|
|
} else {
|
|
Schedule(tn, e);
|
|
}
|
|
|
|
} else if (TypeOfEvent(e) == EVENT_REPORT) {
|
|
t = rtcp_interval(members, senders, rtcp_bw, we_sent, *avg_rtcp_size,
|
|
*initial);
|
|
tn = *tp + t;
|
|
|
|
if (tn <= tc) {
|
|
SendRTCPReport(e);
|
|
*avg_rtcp_size = (1. / 16.) * SentPacketSize(e)
|
|
+ (15. / 16.) * (*avg_rtcp_size);
|
|
*tp = tc;
|
|
|
|
/* We must redraw the interval. Don't reuse the
|
|
one computed above, since its not actually
|
|
distributed the same, as we are conditioned
|
|
on it being small enough to cause a packet to
|
|
be sent */
|
|
|
|
t = rtcp_interval(members, senders, rtcp_bw, we_sent,
|
|
*avg_rtcp_size, *initial);
|
|
|
|
Schedule(t + tc, e);
|
|
*initial = 0;
|
|
} else {
|
|
Schedule(tn, e);
|
|
}
|
|
*pmembers = members;
|
|
}
|
|
}
|
|
|
|
void OnReceive(packet p, event e, int *members, int *pmembers, int *senders,
|
|
double *avg_rtcp_size, double *tp, double tc, double tn) {
|
|
/* What we do depends on whether we have left the group, and
|
|
* are waiting to send a BYE (TypeOfEvent(e) == EVENT_BYE) or
|
|
* an RTCP report. p represents the packet that was just received. */
|
|
|
|
if (PacketType(p) == PACKET_RTCP_REPORT) {
|
|
if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
|
|
AddMember(p);
|
|
*members += 1;
|
|
}
|
|
*avg_rtcp_size = (1. / 16.) * ReceivedPacketSize(p)
|
|
+ (15. / 16.) * (*avg_rtcp_size);
|
|
} else if (PacketType(p) == PACKET_RTP) {
|
|
if (NewMember(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
|
|
AddMember(p);
|
|
*members += 1;
|
|
}
|
|
if (NewSender(p) && (TypeOfEvent(e) == EVENT_REPORT)) {
|
|
AddSender(p);
|
|
*senders += 1;
|
|
}
|
|
} else if (PacketType(p) == PACKET_BYE) {
|
|
*avg_rtcp_size = (1. / 16.) * ReceivedPacketSize(p)
|
|
+ (15. / 16.) * (*avg_rtcp_size);
|
|
|
|
if (TypeOfEvent(e) == EVENT_REPORT) {
|
|
if (NewSender(p) == FALSE) {
|
|
RemoveSender(p);
|
|
*senders -= 1;
|
|
}
|
|
|
|
if (NewMember(p) == FALSE) {
|
|
RemoveMember(p);
|
|
*members -= 1;
|
|
}
|
|
|
|
if (*members < *pmembers) {
|
|
tn = tc + (((double) *members) / (*pmembers)) * (tn - tc);
|
|
*tp = tc - (((double) *members) / (*pmembers)) * (tc - *tp);
|
|
|
|
/* Reschedule the next report for time tn */
|
|
|
|
Reschedule(tn, e);
|
|
*pmembers = *members;
|
|
}
|
|
|
|
} else if (TypeOfEvent(e) == EVENT_BYE) {
|
|
*members += 1;
|
|
}
|
|
}
|
|
}
|