| /* 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; |
| } |
| } |
| } |