Exemple #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 (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);
        }