コード例 #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);
        }
コード例 #2
0
        /// <summary>
        /// Parses RTP compound packet.
        /// </summary>
        /// <param name="buffer">Data buffer..</param>
        /// <param name="count">Number of bytes in the <b>buffer</b>.</param>
        /// <returns>Returns parsed RTP packet.</returns>
        public static RTCP_CompoundPacket Parse(byte[] buffer, int count)
        {
            /* Compound packet stucture:
             *   Encryption prefix
             *      If and only if the compound packet is to be encrypted, it is prefixed by a
             *      random 32-bit quantity redrawn for every compound packet transmitted.
             *
             *   SR or RR
             *      The first RTCP packet in the compound packet must always be a report
             *      packet to facilitate header validation as described in Appendix A.2.
             *      This is true even if no data has been sent nor received, in which case an
             *      empty RR is sent, and even if the only other RTCP packet in the compound packet is a BYE.
             *
             *   Additional RRs
             *      If the number of sources for which reception statistics are being reported
             *      exceeds 31, the number that will fit into one SR or RR packet, then additional
             *      RR packets should follow the initial report packet.
             *
             *   SDES
             *      An SDES packet containing a CNAME item must be included in each compound RTCP packet.
             *      Other source description items may optionally be included if required by a particular
             *      application, subject to bandwidth constraints (see Section 6.2.2).
             *
             *   BYE or APP
             *      Other RTCP packet types, including those yet to be defined, may follow in any order,
             *      except that BYE should be the last packet sent with a given SSRC/CSRC.
             *      Packet types may appear more than once.
             */

            int offset = 0;

            RTCP_CompoundPacket packet = new RTCP_CompoundPacket();

            while (offset < count)
            {
                try{
                    packet.m_pPackets.Add(RTCP_Packet.Parse(buffer, ref offset));
                }
                catch {
                    // Skip invalid or unknown packets.
                }
            }

            return(packet);
        }
コード例 #3
0
ファイル: RTP_Source_Local.cs プロジェクト: dioptre/nkd
        /// <summary>
        /// Sends specified application packet to the RTP session target(s).
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <param name="packet">Is raised when <b>packet</b> is null reference.</param>
        public void SendApplicationPacket(RTCP_Packet_APP packet)
        {
            if(this.State == RTP_SourceState.Disposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(packet == null){
                throw new ArgumentNullException("packet");
            }

            packet.Source = this.SSRC;

            RTCP_CompoundPacket p = new RTCP_CompoundPacket();
            RTCP_Packet_RR rr = new RTCP_Packet_RR();
            rr.SSRC = this.SSRC;
            p.Packets.Add(packet);

            // Send APP packet.
            this.Session.SendRtcpPacket(p);
        }
コード例 #4
0
ファイル: RTCP_CompoundPacket.cs プロジェクト: dioptre/nkd
        /// <summary>
        /// Parses RTP compound packet.
        /// </summary>
        /// <param name="buffer">Data buffer..</param>
        /// <param name="count">Number of bytes in the <b>buffer</b>.</param>
        /// <returns>Returns parsed RTP packet.</returns>
        public static RTCP_CompoundPacket Parse(byte[] buffer,int count)
        {
            /* Compound packet stucture:
                 Encryption prefix
                    If and only if the compound packet is to be encrypted, it is prefixed by a 
                    random 32-bit quantity redrawn for every compound packet transmitted. 

                 SR or RR
                    The first RTCP packet in the compound packet must always be a report 
                    packet to facilitate header validation as described in Appendix A.2. 
                    This is true even if no data has been sent nor received, in which case an 
                    empty RR is sent, and even if the only other RTCP packet in the compound packet is a BYE. 

                 Additional RRs
                    If the number of sources for which reception statistics are being reported 
                    exceeds 31, the number that will fit into one SR or RR packet, then additional 
                    RR packets should follow the initial report packet. 

                 SDES
                    An SDES packet containing a CNAME item must be included in each compound RTCP packet. 
                    Other source description items may optionally be included if required by a particular 
                    application, subject to bandwidth constraints (see Section 6.2.2). 

                 BYE or APP
                    Other RTCP packet types, including those yet to be defined, may follow in any order, 
                    except that BYE should be the last packet sent with a given SSRC/CSRC. 
                    Packet types may appear more than once. 
            */

            int offset = 0;

            RTCP_CompoundPacket packet = new RTCP_CompoundPacket();
            while(offset < count){
                RTCP_Packet p = RTCP_Packet.Parse(buffer,ref offset,true);
                if(p != null){
                    packet.m_pPackets.Add(p);
                }
            }

            return packet;
        }
コード例 #5
0
        /// <summary>
        /// Sends specified application packet to the RTP session target(s).
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <param name="packet">Is raised when <b>packet</b> is null reference.</param>
        public void SendApplicationPacket(RTCP_Packet_APP packet)
        {
            if (this.State == RTP_SourceState.Disposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (packet == null)
            {
                throw new ArgumentNullException("packet");
            }

            packet.Source = this.SSRC;

            RTCP_CompoundPacket p  = new RTCP_CompoundPacket();
            RTCP_Packet_RR      rr = new RTCP_Packet_RR();

            rr.SSRC = this.SSRC;
            p.Packets.Add(packet);

            // Send APP packet.
            this.Session.SendRtcpPacket(p);
        }
コード例 #6
0
ファイル: RTP_Source_Local.cs プロジェクト: dioptre/nkd
        /// <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);
        }
コード例 #7
0
ファイル: RTP_Session.cs プロジェクト: CivilPol/LumiSoft.Net
        /// <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;
        }
コード例 #8
0
ファイル: RTP_Session.cs プロジェクト: CivilPol/LumiSoft.Net
        /// <summary>
        /// Sends specified RTCP packet to the session remote party.
        /// </summary>
        /// <param name="packet">RTCP compound packet.</param>
        /// <returns>Returns packet size in bytes.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>packet</b> is null reference.</exception>
        internal int SendRtcpPacket(RTCP_CompoundPacket packet)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(packet == null){
                throw new ArgumentNullException("packet");
            }
                        
            byte[] packetBytes = packet.ToByte();

            // Send packet to each remote target.
            foreach(RTP_Address target in this.Targets){
                try{
                    m_pRtcpSocket.SendTo(packetBytes,packetBytes.Length,SocketFlags.None,target.RtcpEP);

                    m_RtcpPacketsSent++;
                    m_RtcpBytesSent += packetBytes.Length;
                    // RFC requires IP header counted too, we just don't do it.
                    m_RtcpAvgPacketSize = (1/16) * packetBytes.Length + (15/16) * m_RtcpAvgPacketSize;
                }
                catch{
                    m_RtcpFailedTransmissions++;
                }
            }

            return packetBytes.Length;
        }
コード例 #9
0
ファイル: RTP_Session.cs プロジェクト: CivilPol/LumiSoft.Net
        /// <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();
        }
コード例 #10
0
ファイル: RTP_Session.cs プロジェクト: CivilPol/LumiSoft.Net
        /// <summary>
        /// Sends RTCP report.
        /// </summary>
        private void SendRtcp()
        {
            /* RFC 3550 6.4 Sender and Receiver Reports
                RTP receivers provide reception quality feedback using RTCP report
                packets which may take one of two forms depending upon whether or not
                the receiver is also a sender.  The only difference between the
                sender report (SR) and receiver report (RR) forms, besides the packet
                type code, is that the sender report includes a 20-byte sender
                information section for use by active senders.  The SR is issued if a
                site has sent any data packets during the interval since issuing the
                last report or the previous one, otherwise the RR is issued.

                Both the SR and RR forms include zero or more reception report
                blocks, one for each of the synchronization sources from which this
                receiver has received RTP data packets since the last report.
                Reports are not issued for contributing sources listed in the CSRC
                list.  Each reception report block provides statistics about the data
                received from the particular source indicated in that block.  Since a
                maximum of 31 reception report blocks will fit in an SR or RR packet,
                additional RR packets SHOULD be stacked after the initial SR or RR
                packet as needed to contain the reception reports for all sources
                heard during the interval since the last report.  If there are too
                many sources to fit all the necessary RR packets into one compound
                RTCP packet without exceeding the MTU of the network path, then only
                the subset that will fit into one MTU SHOULD be included in each
                interval.  The subsets SHOULD be selected round-robin across multiple
                intervals so that all sources are reported.                
            */

            bool we_sent = false;

            try{
                m_pRtcpSource.SetLastRtcpPacket(DateTime.Now);
                                
                RTCP_CompoundPacket compundPacket = new RTCP_CompoundPacket();

                RTCP_Packet_RR rr = null;

                // Find active send streams.
                List<RTP_SendStream> activeSendStreams = new List<RTP_SendStream>();
                foreach(RTP_SendStream stream in this.SendStreams){
                    if(stream.RtcpCyclesSinceWeSent < 2){
                        activeSendStreams.Add(stream);
                        we_sent = true;
                    }
                    // Notify stream about RTCP cycle.
                    stream.RtcpCycle();
                }

                #region SR(s) / RR

                // We are sender.
                if(we_sent){
                    // Create SR for each active send stream.
                    for(int i=0;i<activeSendStreams.Count;i++){
                        RTP_SendStream sendStream = activeSendStreams[i];

                        RTCP_Packet_SR sr = new RTCP_Packet_SR(sendStream.Source.SSRC);
                        sr.NtpTimestamp      = RTP_Utils.DateTimeToNTP64(DateTime.Now);
                        sr.RtpTimestamp      = m_pRtpClock.RtpTimestamp;
                        sr.SenderPacketCount = (uint)sendStream.RtpPacketsSent;
                        sr.SenderOctetCount  = (uint)sendStream.RtpBytesSent;

                        compundPacket.Packets.Add(sr);
                    }
                }
                // We are receiver.
                else{
                    rr = new RTCP_Packet_RR();
                    rr.SSRC = m_pRtcpSource.SSRC;
                    compundPacket.Packets.Add(rr);

                    // Report blocks added later.                
                }

                #endregion

                #region SDES

                RTCP_Packet_SDES sdes = new RTCP_Packet_SDES();
                // Add default SSRC.
                RTCP_Packet_SDES_Chunk sdesChunk = new RTCP_Packet_SDES_Chunk(m_pRtcpSource.SSRC,m_pSession.LocalParticipant.CNAME);
                // Add next optional SDES item, if any. (We round-robin optional items)
                m_pSession.LocalParticipant.AddNextOptionalSdesItem(sdesChunk);
                sdes.Chunks.Add(sdesChunk);   
                // Add all active send streams SSRC -> CNAME. This enusres that all send streams will be mapped to participant.
                foreach(RTP_SendStream stream in activeSendStreams){
                    sdes.Chunks.Add(new RTCP_Packet_SDES_Chunk(stream.Source.SSRC,m_pSession.LocalParticipant.CNAME));
                }
                compundPacket.Packets.Add(sdes);

                #endregion

                #region RR filling

                /* RR reporting:
                    Report up to 31 active senders, if more senders, reoprt next with next interval.
                    Report oldest not reported first,then ventually all sources will be reported with this algorythm.
                */
                RTP_Source[]        senders             = this.Senders;
                DateTime[]          acitveSourceRRTimes = new DateTime[senders.Length];
                RTP_ReceiveStream[] activeSenders       = new RTP_ReceiveStream[senders.Length];
                int                 activeSenderCount   = 0;
                foreach(RTP_Source sender in senders){
                    // Remote sender sent RTP data during last RTCP interval.
                    if(!sender.IsLocal && sender.LastRtpPacket > m_RtcpLastTransmission){
                        acitveSourceRRTimes[activeSenderCount] = sender.LastRRTime;
                        activeSenders[activeSenderCount]       = ((RTP_Source_Remote)sender).Stream;
                        activeSenderCount++;
                    }
                }                
                // Create RR is SR report and no RR created yet.
                if(rr == null){
                    rr = new RTCP_Packet_RR();
                    rr.SSRC = m_pRtcpSource.SSRC;
                    compundPacket.Packets.Add(rr);
                }
                // Sort ASC.
                Array.Sort(acitveSourceRRTimes,activeSenders,0,activeSenderCount);
                // Add up to 31 oldest not reported sources to report.
                for(int i=1;i<31;i++){
                    if((activeSenderCount - i) < 0){
                        break;
                    }
                    rr.ReportBlocks.Add(activeSenders[activeSenderCount - i].CreateReceiverReport());
                }

                #endregion

                // Send RTPC packet.
                SendRtcpPacket(compundPacket);

                // Timeout conflicting transport addresses, if not conflicting any more.
                lock(m_pConflictingEPs){
                    string[] keys = new string[m_pConflictingEPs.Count];
                    m_pConflictingEPs.Keys.CopyTo(keys,0);
                    foreach(string key in keys){
                        if(m_pConflictingEPs[key].AddMinutes(3) < DateTime.Now){
                            m_pConflictingEPs.Remove(key);
                        }
                    }
                }

                // Since we must check timing out sources at least once per RTCP interval, so we
                // check this before sending RTCP.
                TimeOutSsrc();
            }
            catch(Exception x){
                if(this.IsDisposed){
                    return;
                }

                m_pSession.OnError(x);
            }

            m_RtcpLastTransmission = DateTime.Now;

            // Schedule next RTCP sending.
            Schedule(ComputeRtcpTransmissionInterval(m_pMembers.Count,m_pSenders.Count,m_Bandwidth * 0.25,we_sent,m_RtcpAvgPacketSize,false));
        }