/// <summary> /// Closes this source, sends BYE to remote party. /// </summary> /// <param name="closeReason">Stream closing reason text what is reported to the remote party. Value null means not specified.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> internal override void Close(string closeReason) { if (this.State == RTP_SourceState.Disposed) { throw new ObjectDisposedException(this.GetType().Name); } RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = this.SSRC; packet.Packets.Add(rr); RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = new uint[] { this.SSRC }; if (!string.IsNullOrEmpty(closeReason)) { bye.LeavingReason = closeReason; } packet.Packets.Add(bye); // Send packet. this.Session.SendRtcpPacket(packet); base.Close(closeReason); }
/// <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> /// Gets or creates source. This method also does RFC 3550 8.2 "Collision Resolution and Loop Detection". /// </summary> /// <param name="rtcp_rtp">If true <b>src</b> is RTCP identifier, otherwise RTP identifier.</param> /// <param name="src">Source SSRC or CSRC identifier.</param> /// <param name="cname">RTCP SDES chunk CNAME. Must be passed only if <b>src</b> if from RTCP SDES chunk.</param> /// <param name="packetEP">Packet sender end point.</param> /// <returns>Returns specified source. Returns null if source has "collision or loop".</returns> /// <exception cref="ArgumentNullException">Is raised when <b>packetEP</b> is null reference.</exception> private RTP_Source_Remote GetOrCreateSource(bool rtcp_rtp,uint src,string cname,IPEndPoint packetEP) { if(packetEP == null){ throw new ArgumentNullException("packetEP"); } /* RFC 3550 8.2. if(SSRC or CSRC identifier is not found in the source identifier table){ create a new entry storing the data or control source transport address, the SSRC or CSRC and other state; } else if(table entry was created on receipt of a control packet and this is the first data packet or vice versa){ store the source transport address from this packet; } else if(source transport address from the packet does not match the one saved in the table entry for this identifier){ // An identifier collision or a loop is indicated if(source identifier is not the participant's own){ // OPTIONAL error counter step if(source identifier is from an RTCP SDES chunk containing a CNAME item that differs from the CNAME in the table entry){ count a third-party collision; } else{ count a third-party loop; } abort processing of data packet or control element; // MAY choose a different policy to keep new source } // A collision or loop of the participant's own packets else if(source transport address is found in the list of conflicting data or control source transport addresses){ // OPTIONAL error counter step if(source identifier is not from an RTCP SDES chunk containing a CNAME item or CNAME is the participant's own){ count occurrence of own traffic looped; } mark current time in conflicting address list entry; abort processing of data packet or control element; } // New collision, change SSRC identifier else{ log occurrence of a collision; create a new entry in the conflicting data or control source transport address list and mark current time; send an RTCP BYE packet with the old SSRC identifier; choose a new SSRC identifier; create a new entry in the source identifier table with the old SSRC plus the source transport address from the data or control packet being processed; } } */ RTP_Source source = null; lock(m_pMembers){ m_pMembers.TryGetValue(src,out source); // SSRC or CSRC identifier is not found in the source identifier table. if(source == null){ source = new RTP_Source_Remote(this,src); if(rtcp_rtp){ source.SetRtcpEP(packetEP); } else{ source.SetRtpEP(packetEP); } m_pMembers.Add(src,source); } // Table entry was created on receipt of a control packet and this is the first data packet or vice versa. else if((rtcp_rtp ? source.RtcpEP : source.RtpEP) == null){ if(rtcp_rtp){ source.SetRtcpEP(packetEP); } else{ source.SetRtpEP(packetEP); } } // Source transport address from the packet does not match the one saved in the table entry for this identifier. else if(!packetEP.Equals((rtcp_rtp ? source.RtcpEP : source.RtpEP))){ // Source identifier is not the participant's own. if(!source.IsLocal){ if(cname != null && cname != source.CName){ m_RemoteCollisions++; } else{ m_RemotePacketsLooped++; } return null; } // A collision or loop of the participant's own packets. else if(m_pConflictingEPs.ContainsKey(packetEP.ToString())){ if(cname == null || cname == source.CName){ m_LocalPacketsLooped++; } m_pConflictingEPs[packetEP.ToString()] = DateTime.Now; return null; } // New collision, change SSRC identifier. else{ m_LocalCollisions++; m_pConflictingEPs.Add(packetEP.ToString(),DateTime.Now); // Remove SSRC from members,senders. Choose new SSRC, CNAME new and BYE old. m_pMembers.Remove(source.SSRC); m_pSenders.Remove(source.SSRC); uint oldSSRC = source.SSRC; source.GenerateNewSSRC(); // Ensure that new SSRC is not in use, if so repaeat while not conflicting SSRC. while(m_pMembers.ContainsKey(source.SSRC)){ source.GenerateNewSSRC(); } m_pMembers.Add(source.SSRC,source); RTCP_CompoundPacket compoundPacket = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = m_pRtcpSource.SSRC; compoundPacket.Packets.Add(rr); RTCP_Packet_SDES sdes = new RTCP_Packet_SDES(); RTCP_Packet_SDES_Chunk sdes_chunk = new RTCP_Packet_SDES_Chunk(source.SSRC,m_pSession.LocalParticipant.CNAME); sdes.Chunks.Add(sdes_chunk); compoundPacket.Packets.Add(sdes); RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = new uint[]{oldSSRC}; bye.LeavingReason = "Collision, changing SSRC."; compoundPacket.Packets.Add(bye); SendRtcpPacket(compoundPacket); //---------------------------------------------------------------------- // Add new source to members, it's not conflicting any more, we changed SSRC. source = new RTP_Source_Remote(this,src); if(rtcp_rtp){ source.SetRtcpEP(packetEP); } else{ source.SetRtpEP(packetEP); } m_pMembers.Add(src,source); } } } return (RTP_Source_Remote)source; }
/// <summary> /// Closes RTP session, sends BYE with optional reason text to remote targets. /// </summary> /// <param name="closeReason">Close reason. Value null means not specified.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> public void Close(string closeReason) { if(m_IsDisposed){ throw new ObjectDisposedException(this.GetType().Name); } // Generate BYE packet(s). RTCP_CompoundPacket compundPacket = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = m_pRtcpSource.SSRC; compundPacket.Packets.Add(rr); int sourcesProcessed = 0; while(sourcesProcessed < m_pLocalSources.Count){ uint[] sources = new uint[Math.Min(m_pLocalSources.Count - sourcesProcessed,31)]; for(int i=0;i<sources.Length;i++){ sources[i] = m_pLocalSources[sourcesProcessed].SSRC; sourcesProcessed++; } RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = sources; compundPacket.Packets.Add(bye); } // Send BYE. SendRtcpPacket(compundPacket); OnClosed(); Dispose(); }
/// <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> /// Closes this source, sends BYE to remote party. /// </summary> /// <param name="closeReason">Stream closing reason text what is reported to the remote party. Value null means not specified.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> internal override void Close(string closeReason) { if(this.State == RTP_SourceState.Disposed){ throw new ObjectDisposedException(this.GetType().Name); } RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = this.SSRC; packet.Packets.Add(rr); RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = new uint[]{this.SSRC}; if(!string.IsNullOrEmpty(closeReason)){ bye.LeavingReason = closeReason; } packet.Packets.Add(bye); // Send packet. this.Session.SendRtcpPacket(packet); base.Close(closeReason); }