/// <summary> /// Default constructor. /// </summary> /// <param name="sr">RTCP SR report.</param> /// <exception cref="ArgumentNullException">Is raised when <b>sr</b> is null reference.</exception> internal RTCP_Report_Sender(RTCP_Packet_SR sr) { if(sr == null){ throw new ArgumentNullException("sr"); } m_NtpTimestamp = sr.NtpTimestamp; m_RtpTimestamp = sr.RtpTimestamp; m_SenderPacketCount = sr.SenderPacketCount; m_SenderOctetCount = sr.SenderOctetCount; }
/// <summary> /// Default constructor. /// </summary> /// <param name="sr">RTCP SR report.</param> /// <exception cref="ArgumentNullException">Is raised when <b>sr</b> is null reference.</exception> internal RTCP_Report_Sender(RTCP_Packet_SR sr) { if (sr == null) { throw new ArgumentNullException("sr"); } m_NtpTimestamp = sr.NtpTimestamp; m_RtpTimestamp = sr.RtpTimestamp; m_SenderPacketCount = sr.SenderPacketCount; m_SenderOctetCount = sr.SenderOctetCount; }
/// <summary> /// Parses 1 RTCP packet from the specified buffer. /// </summary> /// <param name="buffer">Buffer which contains RTCP packet.</param> /// <param name="offset">Offset in buffer.</param> /// <param name="noException">If true and parsing failed, no exception is raised.</param> /// <returns>Returns parsed RTCP packet or null if parsing is failed and <b>noException=true</b>.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public static RTCP_Packet Parse(byte[] buffer, ref int offset, bool noException) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentException("Argument 'offset' value must be >= 0."); } /* RFC 3550 RTCP header. * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * header |V=2|P| XX | type | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ int type = buffer[offset + 1]; // SR if (type == RTCP_PacketType.SR) { RTCP_Packet_SR packet = new RTCP_Packet_SR(); packet.ParseInternal(buffer, ref offset); return(packet); } // RR else if (type == RTCP_PacketType.RR) { RTCP_Packet_RR packet = new RTCP_Packet_RR(); packet.ParseInternal(buffer, ref offset); return(packet); } // SDES else if (type == RTCP_PacketType.SDES) { RTCP_Packet_SDES packet = new RTCP_Packet_SDES(); packet.ParseInternal(buffer, ref offset); return(packet); } // BYE else if (type == RTCP_PacketType.BYE) { RTCP_Packet_BYE packet = new RTCP_Packet_BYE(); packet.ParseInternal(buffer, ref offset); return(packet); } // APP else if (type == RTCP_PacketType.APP) { RTCP_Packet_APP packet = new RTCP_Packet_APP(); packet.ParseInternal(buffer, ref offset); return(packet); } else { // We need to move offset. offset += 2; int length = buffer[offset++] << 8 | buffer[offset++]; offset += length; if (noException) { return(null); } else { throw new ArgumentException("Unknown RTCP packet type '" + type + "'."); } } }
/// <summary> /// Sends RTCP report. /// </summary> private void SendRtcp() { /* RFC 3550 6.4 Sender and Receiver Reports RTP receivers provide reception quality feedback using RTCP report packets which may take one of two forms depending upon whether or not the receiver is also a sender. The only difference between the sender report (SR) and receiver report (RR) forms, besides the packet type code, is that the sender report includes a 20-byte sender information section for use by active senders. The SR is issued if a site has sent any data packets during the interval since issuing the last report or the previous one, otherwise the RR is issued. Both the SR and RR forms include zero or more reception report blocks, one for each of the synchronization sources from which this receiver has received RTP data packets since the last report. Reports are not issued for contributing sources listed in the CSRC list. Each reception report block provides statistics about the data received from the particular source indicated in that block. Since a maximum of 31 reception report blocks will fit in an SR or RR packet, additional RR packets SHOULD be stacked after the initial SR or RR packet as needed to contain the reception reports for all sources heard during the interval since the last report. If there are too many sources to fit all the necessary RR packets into one compound RTCP packet without exceeding the MTU of the network path, then only the subset that will fit into one MTU SHOULD be included in each interval. The subsets SHOULD be selected round-robin across multiple intervals so that all sources are reported. */ bool we_sent = false; try{ m_pRtcpSource.SetLastRtcpPacket(DateTime.Now); RTCP_CompoundPacket compundPacket = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = null; // Find active send streams. List<RTP_SendStream> activeSendStreams = new List<RTP_SendStream>(); foreach(RTP_SendStream stream in this.SendStreams){ if(stream.RtcpCyclesSinceWeSent < 2){ activeSendStreams.Add(stream); we_sent = true; } // Notify stream about RTCP cycle. stream.RtcpCycle(); } #region SR(s) / RR // We are sender. if(we_sent){ // Create SR for each active send stream. for(int i=0;i<activeSendStreams.Count;i++){ RTP_SendStream sendStream = activeSendStreams[i]; RTCP_Packet_SR sr = new RTCP_Packet_SR(sendStream.Source.SSRC); sr.NtpTimestamp = RTP_Utils.DateTimeToNTP64(DateTime.Now); sr.RtpTimestamp = m_pRtpClock.RtpTimestamp; sr.SenderPacketCount = (uint)sendStream.RtpPacketsSent; sr.SenderOctetCount = (uint)sendStream.RtpBytesSent; compundPacket.Packets.Add(sr); } } // We are receiver. else{ rr = new RTCP_Packet_RR(); rr.SSRC = m_pRtcpSource.SSRC; compundPacket.Packets.Add(rr); // Report blocks added later. } #endregion #region SDES RTCP_Packet_SDES sdes = new RTCP_Packet_SDES(); // Add default SSRC. RTCP_Packet_SDES_Chunk sdesChunk = new RTCP_Packet_SDES_Chunk(m_pRtcpSource.SSRC,m_pSession.LocalParticipant.CNAME); // Add next optional SDES item, if any. (We round-robin optional items) m_pSession.LocalParticipant.AddNextOptionalSdesItem(sdesChunk); sdes.Chunks.Add(sdesChunk); // Add all active send streams SSRC -> CNAME. This enusres that all send streams will be mapped to participant. foreach(RTP_SendStream stream in activeSendStreams){ sdes.Chunks.Add(new RTCP_Packet_SDES_Chunk(stream.Source.SSRC,m_pSession.LocalParticipant.CNAME)); } compundPacket.Packets.Add(sdes); #endregion #region RR filling /* RR reporting: Report up to 31 active senders, if more senders, reoprt next with next interval. Report oldest not reported first,then ventually all sources will be reported with this algorythm. */ RTP_Source[] senders = this.Senders; DateTime[] acitveSourceRRTimes = new DateTime[senders.Length]; RTP_ReceiveStream[] activeSenders = new RTP_ReceiveStream[senders.Length]; int activeSenderCount = 0; foreach(RTP_Source sender in senders){ // Remote sender sent RTP data during last RTCP interval. if(!sender.IsLocal && sender.LastRtpPacket > m_RtcpLastTransmission){ acitveSourceRRTimes[activeSenderCount] = sender.LastRRTime; activeSenders[activeSenderCount] = ((RTP_Source_Remote)sender).Stream; activeSenderCount++; } } // Create RR is SR report and no RR created yet. if(rr == null){ rr = new RTCP_Packet_RR(); rr.SSRC = m_pRtcpSource.SSRC; compundPacket.Packets.Add(rr); } // Sort ASC. Array.Sort(acitveSourceRRTimes,activeSenders,0,activeSenderCount); // Add up to 31 oldest not reported sources to report. for(int i=1;i<31;i++){ if((activeSenderCount - i) < 0){ break; } rr.ReportBlocks.Add(activeSenders[activeSenderCount - i].CreateReceiverReport()); } #endregion // Send RTPC packet. SendRtcpPacket(compundPacket); // Timeout conflicting transport addresses, if not conflicting any more. lock(m_pConflictingEPs){ string[] keys = new string[m_pConflictingEPs.Count]; m_pConflictingEPs.Keys.CopyTo(keys,0); foreach(string key in keys){ if(m_pConflictingEPs[key].AddMinutes(3) < DateTime.Now){ m_pConflictingEPs.Remove(key); } } } // Since we must check timing out sources at least once per RTCP interval, so we // check this before sending RTCP. TimeOutSsrc(); } catch(Exception x){ if(this.IsDisposed){ return; } m_pSession.OnError(x); } m_RtcpLastTransmission = DateTime.Now; // Schedule next RTCP sending. Schedule(ComputeRtcpTransmissionInterval(m_pMembers.Count,m_pSenders.Count,m_Bandwidth * 0.25,we_sent,m_RtcpAvgPacketSize,false)); }
/// <summary> /// Parses 1 RTCP packet from the specified buffer. /// </summary> /// <param name="buffer">Buffer which contains RTCP packet.</param> /// <param name="offset">Offset in buffer.</param> /// <param name="noException">If true and parsing failed, no exception is raised.</param> /// <returns>Returns parsed RTCP packet or null if parsing is failed and <b>noException=true</b>.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public static RTCP_Packet Parse(byte[] buffer,ref int offset,bool noException) { if(buffer == null){ throw new ArgumentNullException("buffer"); } if(offset < 0){ throw new ArgumentException("Argument 'offset' value must be >= 0."); } /* RFC 3550 RTCP header. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ header |V=2|P| XX | type | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ int type = buffer[offset + 1]; // SR if(type == RTCP_PacketType.SR){ RTCP_Packet_SR packet = new RTCP_Packet_SR(); packet.ParseInternal(buffer,ref offset); return packet; } // RR else if(type == RTCP_PacketType.RR){ RTCP_Packet_RR packet = new RTCP_Packet_RR(); packet.ParseInternal(buffer,ref offset); return packet; } // SDES else if(type == RTCP_PacketType.SDES){ RTCP_Packet_SDES packet = new RTCP_Packet_SDES(); packet.ParseInternal(buffer,ref offset); return packet; } // BYE else if(type == RTCP_PacketType.BYE){ RTCP_Packet_BYE packet = new RTCP_Packet_BYE(); packet.ParseInternal(buffer,ref offset); return packet; } // APP else if(type == RTCP_PacketType.APP){ RTCP_Packet_APP packet = new RTCP_Packet_APP(); packet.ParseInternal(buffer,ref offset); return packet; } else{ // We need to move offset. offset += 2; int length = buffer[offset++] << 8 | buffer[offset++]; offset += length; if(noException){ return null; } else{ throw new ArgumentException("Unknown RTCP packet type '" + type + "'."); } } }