Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        /// <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 + "'.");
                }
            }
        }
Beispiel #3
0
        /// <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;
        }
Beispiel #4
0
        /// <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();
        }
Beispiel #5
0
        /// <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 + "'.");
                }
            }
        }
Beispiel #6
0
        /// <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);
        }