/// <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 (State == RTP_SourceState.Disposed) { throw new ObjectDisposedException(GetType().Name); } RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = SSRC; packet.Packets.Add(rr); RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = new[] { SSRC }; if (!string.IsNullOrEmpty(closeReason)) { bye.LeavingReason = closeReason; } packet.Packets.Add(bye); // Send packet. 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> /// <returns>Returns parsed RTCP packet.</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) { 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; throw new ArgumentException("Unknown RTCP packet type '" + type + "'."); } }
/// <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> /// <returns>Returns parsed RTCP packet.</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) { 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; throw new ArgumentException("Unknown RTCP packet type '" + type + "'."); } }
/// <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(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> /// 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[] {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 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 (State == RTP_SourceState.Disposed) { throw new ObjectDisposedException(GetType().Name); } RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); RTCP_Packet_RR rr = new RTCP_Packet_RR(); rr.SSRC = SSRC; packet.Packets.Add(rr); RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); bye.Sources = new[] {SSRC}; if (!string.IsNullOrEmpty(closeReason)) { bye.LeavingReason = closeReason; } packet.Packets.Add(bye); // Send packet. Session.SendRtcpPacket(packet); base.Close(closeReason); }