/// <summary> /// Adds specified source to participant if participant doesn't contain the specified source. /// </summary> /// <param name="source">RTP source.</param> /// <exception cref="ArgumentNullException">Is raised when <b>source</b> is null reference.</exception> internal void EnsureSource(RTP_Source source) { if (source == null) { throw new ArgumentNullException("source"); } if (!m_pSources.Contains(source)) { m_pSources.Add(source); OnSourceAdded(source); source.Disposing += new EventHandler(delegate(object sender, EventArgs e){ if (m_pSources.Remove(source)) { OnSourceRemoved(source); // If last source removed, the participant is dead, so dispose participant. if (m_pSources.Count == 0) { OnRemoved(); Dispose(); } } }); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="source">RTP source.</param> /// <exception cref="ArgumentNullException">Is raised when <b>source</b> is null reference.</exception> public RTP_SourceEventArgs(RTP_Source source) { if(source == null){ throw new ArgumentNullException("source"); } m_pSource = source; }
/// <summary> /// Default constructor. /// </summary> /// <param name="source">RTP source.</param> /// <exception cref="ArgumentNullException">Is raised when <b>source</b> is null reference.</exception> public RTP_SourceEventArgs(RTP_Source source) { if (source == null) { throw new ArgumentNullException("source"); } m_pSource = source; }
/// <summary> /// Raises <b>SourceRemoved</b> event. /// </summary> /// <param name="source">RTP source.</param> private void OnSourceRemoved(RTP_Source source) { if (source == null) { throw new ArgumentNullException("source"); } if (this.SourceRemoved != null) { this.SourceRemoved(this, new RTP_SourceEventArgs(source)); } }
/// <summary> /// Default constructor. /// </summary> /// <param name="session">Owner RTP session.</param> /// <param name="ssrc">Onwer synchronization source.</param> /// <param name="packetSeqNo">RTP packet <b>SeqNo</b> value.</param> /// <exception cref="ArgumentNullException">Is riased when <b>session</b> or <b>ssrc</b> is null reference.</exception> internal RTP_ReceiveStream(RTP_Session session,RTP_Source ssrc,ushort packetSeqNo) { if(session == null){ throw new ArgumentNullException("session"); } if(ssrc == null){ throw new ArgumentNullException("ssrc"); } m_pSession = session; m_pSSRC = ssrc; // RFC 3550 A.1. InitSeq(packetSeqNo); m_MaxSeqNo = (ushort)(packetSeqNo - 1); m_Probation = MIN_SEQUENTIAL; }
/// <summary> /// Default constructor. /// </summary> /// <param name="session">Owner RTP session.</param> /// <param name="ssrc">Onwer synchronization source.</param> /// <param name="packetSeqNo">RTP packet <b>SeqNo</b> value.</param> /// <exception cref="ArgumentNullException">Is riased when <b>session</b> or <b>ssrc</b> is null reference.</exception> internal RTP_ReceiveStream(RTP_Session session, RTP_Source ssrc, ushort packetSeqNo) { if (session == null) { throw new ArgumentNullException("session"); } if (ssrc == null) { throw new ArgumentNullException("ssrc"); } m_pSession = session; m_pSSRC = ssrc; // RFC 3550 A.1. InitSeq(packetSeqNo); m_MaxSeqNo = (ushort)(packetSeqNo - 1); m_Probation = MIN_SEQUENTIAL; }
/// <summary> /// Adds specified source to participant if participant doesn't contain the specified source. /// </summary> /// <param name="source">RTP source.</param> /// <exception cref="ArgumentNullException">Is raised when <b>source</b> is null reference.</exception> internal void EnsureSource(RTP_Source source) { if(source == null){ throw new ArgumentNullException("source"); } if(!m_pSources.Contains(source)){ m_pSources.Add(source); OnSourceAdded(source); source.Disposing += new EventHandler(delegate(object sender,EventArgs e){ if(m_pSources.Remove(source)){ OnSourceRemoved(source); // If last source removed, the participant is dead, so dispose participant. if(m_pSources.Count == 0){ OnRemoved(); Dispose(); } } }); } }
/// <summary> /// Starts RTP session. /// </summary> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> public void Start() { if(m_IsDisposed){ throw new ObjectDisposedException(this.GetType().Name); } if(m_IsStarted){ return; } m_IsStarted = true; /* RFC 3550 6.3.2 Initialization Upon joining the session, the participant initializes tp to 0, tc to 0, senders to 0, pmembers to 1, members to 1, we_sent to false, rtcp_bw to the specified fraction of the session bandwidth, initial to true, and avg_rtcp_size to the probable size of the first RTCP packet that the application will later construct. The calculated interval T is then computed, and the first packet is scheduled for time tn = T. This means that a transmission timer is set which expires at time T. Note that an application MAY use any desired approach for implementing this timer. The participant adds its own SSRC to the member table. */ m_PMembersCount = 1; m_RtcpAvgPacketSize = 100; // Add ourself to members list. m_pRtcpSource = CreateLocalSource(); m_pMembers.Add(m_pRtcpSource.SSRC,m_pRtcpSource); // Create RTP data receiver. UDP_DataReceiver rtpDataReceiver = new UDP_DataReceiver(m_pRtpSocket); rtpDataReceiver.PacketReceived += delegate(object s1,UDP_e_PacketReceived e1){ ProcessRtp(e1.Buffer,e1.Count,e1.RemoteEP); }; // rtpDataReceiver.Error // We don't care about receiving errors here. m_pUdpDataReceivers.Add(rtpDataReceiver); rtpDataReceiver.Start(); // Create RTCP data receiver. UDP_DataReceiver rtcpDataReceiver = new UDP_DataReceiver(m_pRtcpSocket); rtcpDataReceiver.PacketReceived += delegate(object s1,UDP_e_PacketReceived e1){ ProcessRtcp(e1.Buffer,e1.Count,e1.RemoteEP); }; // rtcpDataReceiver.Error // We don't care about receiving errors here. m_pUdpDataReceivers.Add(rtcpDataReceiver); rtcpDataReceiver.Start(); // Start RTCP reporting. Schedule(ComputeRtcpTransmissionInterval(m_pMembers.Count,m_pSenders.Count,m_Bandwidth * 0.25,false,m_RtcpAvgPacketSize,true)); }
/// <summary> /// Cleans up any resources being used. /// </summary> public void Dispose() { if(m_IsDisposed){ return; } m_IsDisposed = true; foreach(UDP_DataReceiver receiver in m_pUdpDataReceivers){ receiver.Dispose(); } m_pUdpDataReceivers = null; if(m_pRtcpTimer != null){ m_pRtcpTimer.Dispose(); m_pRtcpTimer = null; } m_pSession = null; m_pLocalEP = null; m_pTargets = null; foreach(RTP_Source_Local source in m_pLocalSources.ToArray()){ source.Dispose(); } m_pLocalSources = null; m_pRtcpSource = null; foreach(RTP_Source source in m_pMembers.Values){ source.Dispose(); } m_pMembers = null; m_pSenders = null; m_pConflictingEPs = null; m_pRtpSocket.Close(); m_pRtpSocket = null; m_pRtcpSocket.Close(); m_pRtcpSocket = null; m_pUdpDataReceivers = null; OnDisposed(); this.Disposed = null; this.Closed = null; this.NewSendStream = null; this.NewReceiveStream = null; }
/// <summary> /// Does RFC 3550 6.3.5 Timing Out an SSRC. /// </summary> private void TimeOutSsrc() { /* RFC 3550 6.3.5 Timing Out an SSRC. At occasional intervals, the participant MUST check to see if any of the other participants time out. To do this, the participant computes the deterministic (without the randomization factor) calculated interval Td for a receiver, that is, with we_sent false. Any other session member who has not sent an RTP or RTCP packet since time tc - MTd (M is the timeout multiplier, and defaults to 5) is timed out. This means that its SSRC is removed from the member list, and members is updated. A similar check is performed on the sender list. Any member on the sender list who has not sent an RTP packet since time tc - 2T (within the last two RTCP report intervals) is removed from the sender list, and senders is updated. If any members time out, the reverse reconsideration algorithm described in Section 6.3.4 SHOULD be performed. The participant MUST perform this check at least once per RTCP transmission interval. */ bool membersUpdated = false; // Senders check. RTP_Source[] senders = new RTP_Source[m_pSenders.Count]; m_pSenders.Values.CopyTo(senders,0); foreach(RTP_Source sender in senders){ // Sender has not sent RTP data since last two RTCP intervals. if(sender.LastRtpPacket.AddMilliseconds(2 * m_pRtcpTimer.Interval) < DateTime.Now){ m_pSenders.Remove(sender.SSRC); // Mark source "passive". sender.SetActivePassive(false); } } int Td = ComputeRtcpTransmissionInterval(m_pMembers.Count,m_pSenders.Count,m_Bandwidth * 0.25,false,m_RtcpAvgPacketSize,false); // Members check. foreach(RTP_Source member in this.Members){ // Source timed out. if(member.LastActivity.AddSeconds(5 * Td) < DateTime.Now){ m_pMembers.Remove(member.SSRC); // Don't dispose local source, just remove only from members. if(!member.IsLocal){ member.Dispose(); } membersUpdated = true; } } if(membersUpdated){ DoReverseReconsideration(); } }
/// <summary> /// Raises <b>SourceRemoved</b> event. /// </summary> /// <param name="source">RTP source.</param> private void OnSourceRemoved(RTP_Source source) { if(source == null){ throw new ArgumentNullException("source"); } if(this.SourceRemoved != null){ this.SourceRemoved(this,new RTP_SourceEventArgs(source)); } }