示例#1
0
        /// <summary>
        /// Prepares the text in the stream which corresponds to the RtcpData of a Rtcp.RtcpPacket.
        /// </summary>
        /// <param name="format">The <see cref="FileFormat"/> to output.</param>
        /// <param name="packet">The <see cref="Rtcp.RtcpPacket"/> to describe</param>
        /// <returns>The text describes the packet if the <paramref name="format"/> is a text format, otherwise an empty string</returns>
        public static string ToTextualConvention(FileFormat format, Rtcp.RtcpPacket packet)
        {
            if (packet == null || packet.Payload.Count == 0 || format < FileFormat.Text || format == FileFormat.Short) return string.Empty;

            if(format == FileFormat.Unknown) return UnknownSpecifier;

            //Determine the format to use as well as the `Conventional Abbreviation`
            switch (packet.PayloadType)
            {
                case Rtcp.SourceDescriptionReport.PayloadType:
                    //Use a SourceDescriptionReport to enumerate the SourceDescriptionChunk's and SourceDescriptionItem's contained in the packet.
                    using (var sdes = new Rtcp.SourceDescriptionReport(packet, false)) //Don't dispose the packet when done.
                    {
                        return string.Format(RtcpExpressionFormat,
                            "SDES",
                            packet.SynchronizationSourceIdentifier,
                            packet.Padding ? 1.ToString() : 0.ToString(),
                            packet.BlockCount,
                            packet.Header.LengthInWordsMinusOne, ToTextualConvention(sdes));
                    }
                case Rtcp.SendersReport.PayloadType:
                    using (var sr = new Rtcp.SendersReport(packet, false))
                    {
                        return string.Format(RtcpExpressionFormat,
                            "SR",
                            packet.SynchronizationSourceIdentifier,
                            packet.Padding ? 1.ToString() : 0.ToString(),
                            packet.BlockCount,
                            packet.Header.LengthInWordsMinusOne,
                            (ToTextualConvention(sr) + (char)Common.ASCII.Space + string.Format(RtcpSendersInformationFormat,
                            //0
                                (DateTime.UtcNow - sr.NtpTime).TotalSeconds.ToString("0.000000"), //ts=
                            //1
                                sr.NtpTimestamp, //ntp=
                            //2
                                sr.SendersOctetCount, //osent=
                            //3
                                sr.SendersPacketCount))); //psent=
                    }
                case Rtcp.ReceiversReport.PayloadType:
                    using (var rr = new Rtcp.ReceiversReport(packet, false))
                    {
                        return string.Format(RtcpExpressionFormat,
                            "RR",
                            packet.SynchronizationSourceIdentifier,
                            packet.Padding ? 1.ToString() : 0.ToString(),
                            packet.BlockCount,
                            packet.Header.LengthInWordsMinusOne,
                            ToTextualConvention(rr));
                    }
                case Rtcp.GoodbyeReport.PayloadType:
                    using (var bye = new Rtcp.GoodbyeReport(packet, false))
                    {
                        return string.Format(RtcpExpressionFormat,
                            "BYE",
                            packet.SynchronizationSourceIdentifier, packet.Padding ? 1.ToString() : 0.ToString(),
                            packet.BlockCount,
                            packet.Header.LengthInWordsMinusOne, ToTextualConvention(bye));
                    }
                case Rtcp.ApplicationSpecificReport.PayloadType:
                    return string.Format(RtcpExpressionFormat, "APP", packet.SynchronizationSourceIdentifier, packet.Padding ? 1.ToString() : 0.ToString(), packet.BlockCount, packet.Header.LengthInWordsMinusOne, string.Empty);
                default:
                    //Unknown PayloadType use a Hex Representation of the PayloadType
                    return string.Format(RtcpExpressionFormat, packet.PayloadType.ToString("X"), packet.SynchronizationSourceIdentifier, packet.Padding ? 1.ToString() : 0.ToString(), packet.BlockCount, packet.Header.LengthInWordsMinusOne, string.Empty);
            }
        }
示例#2
0
        /// <summary>
        /// O( )
        /// </summary>
        public static void TestAConstructor_And_Reserialization()
        {
            //Permute every possible value in the 5 bit BlockCount
            for (byte ReportBlockCounter = byte.MinValue; ReportBlockCounter <= Media.Common.Binary.FiveBitMaxValue; ++ReportBlockCounter)
            {
                //Permute every possible value in the Padding field.
                for (byte PaddingCounter = byte.MinValue; PaddingCounter <= Media.Common.Binary.FiveBitMaxValue; ++PaddingCounter)
                {
                    //Create a random id
                    int RandomId = RFC3550.Random32(Utility.Random.Next());

                    //Create a SendersReport instance using the specified options.
                    using (Media.Rtcp.ReceiversReport p = new Rtcp.ReceiversReport(0, PaddingCounter, ReportBlockCounter, RandomId))
                    {
                        //Check IsComplete
                        System.Diagnostics.Debug.Assert(p.IsComplete, "IsComplete must be true.");

                        //Check Length
                        System.Diagnostics.Debug.Assert(p.Length == Binary.BitsPerByte + Rtcp.ReportBlock.ReportBlockSize * ReportBlockCounter + PaddingCounter, "Unexpected Length");

                        //Check SynchronizationSourceIdentifier
                        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == RandomId, "Unexpected SynchronizationSourceIdentifier");

                        //Check the BlockCount count
                        System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, "Unexpected BlockCount");

                        //Check the PaddingOctets count
                        System.Diagnostics.Debug.Assert(p.PaddingOctets == PaddingCounter, "Unexpected PaddingOctets");

                        //Check all data in the padding but not the padding octet itself.
                        System.Diagnostics.Debug.Assert(p.PaddingData.Take(PaddingCounter - 1).All(b => b == 0), "Unexpected PaddingData");

                        //Verify all IReportBlock
                        foreach (IReportBlock rb in p)
                        {
                            System.Diagnostics.Debug.Assert(rb.BlockIdentifier == 0, "Unexpected ChunkIdentifier");

                            System.Diagnostics.Debug.Assert(rb.BlockData.All(b => b == 0), "Unexpected BlockData");

                            System.Diagnostics.Debug.Assert(rb.Size == Media.Rtcp.ReportBlock.ReportBlockSize, "Unexpected Size");
                        }

                        //Serialize and Deserialize and verify again
                        using (Rtcp.ReceiversReport s = new Rtcp.ReceiversReport(new Rtcp.RtcpPacket(p.Prepare().ToArray(), 0), true))
                        {
                            //Check SynchronizationSourceIdentifier
                            System.Diagnostics.Debug.Assert(s.SynchronizationSourceIdentifier == p.SynchronizationSourceIdentifier, "Unexpected SynchronizationSourceIdentifier");

                            //Check the Payload.Count
                            System.Diagnostics.Debug.Assert(s.Payload.Count == p.Payload.Count, "Unexpected Payload Count");

                            //Check the Length,
                            System.Diagnostics.Debug.Assert(s.Length == p.Length, "Unexpected Length");

                            //Check the BlockCount count
                            System.Diagnostics.Debug.Assert(s.BlockCount == p.BlockCount, "Unexpected BlockCount");

                            //Verify all IReportBlock
                            foreach (IReportBlock rb in s)
                            {
                                System.Diagnostics.Debug.Assert(rb.BlockIdentifier == 0, "Unexpected ChunkIdentifier");

                                System.Diagnostics.Debug.Assert(rb.BlockData.All(b => b == 0), "Unexpected BlockData");

                                System.Diagnostics.Debug.Assert(rb.Size == Media.Rtcp.ReportBlock.ReportBlockSize, "Unexpected Size");
                            }

                            //Check the RtcpData
                            System.Diagnostics.Debug.Assert(p.RtcpData.SequenceEqual(s.RtcpData), "Unexpected RtcpData");

                            //Check the PaddingOctets
                            System.Diagnostics.Debug.Assert(s.PaddingOctets == p.PaddingOctets, "Unexpected PaddingOctets");

                            //Check all data in the padding but not the padding octet itself.
                            System.Diagnostics.Debug.Assert(s.PaddingData.SequenceEqual(p.PaddingData), "Unexpected PaddingData");
                        }
                    }
                }
            }
        }
        /// <summary>
        /// O( )
        /// </summary>
        public static void TestAConstructor_And_Reserialization()
        {
            //Permute every possible value in the 5 bit BlockCount
            for (byte ReportBlockCounter = byte.MinValue; ReportBlockCounter <= Media.Common.Binary.FiveBitMaxValue; ++ReportBlockCounter)
            {
                //Permute every possible value in the Padding field.
                for (byte PaddingCounter = byte.MinValue; PaddingCounter <= Media.Common.Binary.FiveBitMaxValue; ++PaddingCounter)
                {
                    //Create a random id
                    int RandomId = RFC3550.Random32(Utility.Random.Next());

                    //Create a SendersReport instance using the specified options.
                    using (Media.Rtcp.ReceiversReport p = new Rtcp.ReceiversReport(0, PaddingCounter, ReportBlockCounter, RandomId))
                    {
                        //Check IsComplete
                        System.Diagnostics.Debug.Assert(p.IsComplete, "IsComplete must be true.");

                        //Check Length
                        System.Diagnostics.Debug.Assert(p.Length == Binary.BitsPerByte + Rtcp.ReportBlock.ReportBlockSize * ReportBlockCounter + PaddingCounter, "Unexpected Length");

                        //Check SynchronizationSourceIdentifier
                        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == RandomId, "Unexpected SynchronizationSourceIdentifier");

                        //Check the BlockCount count
                        System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, "Unexpected BlockCount");

                        //Check the PaddingOctets count
                        System.Diagnostics.Debug.Assert(p.PaddingOctets == PaddingCounter, "Unexpected PaddingOctets");

                        //Check all data in the padding but not the padding octet itself.
                        System.Diagnostics.Debug.Assert(p.PaddingData.Take(PaddingCounter - 1).All(b => b == 0), "Unexpected PaddingData");

                        //Verify all IReportBlock
                        foreach (Rtcp.IReportBlock rb in p)
                        {
                            System.Diagnostics.Debug.Assert(rb.BlockIdentifier == 0, "Unexpected ChunkIdentifier");

                            System.Diagnostics.Debug.Assert(rb.BlockData.All(b => b == 0), "Unexpected BlockData");

                            System.Diagnostics.Debug.Assert(rb.Size == Media.Rtcp.ReportBlock.ReportBlockSize, "Unexpected Size");
                        }

                        //Serialize and Deserialize and verify again
                        using (Rtcp.ReceiversReport s = new Rtcp.ReceiversReport(new Rtcp.RtcpPacket(p.Prepare().ToArray(), 0), true))
                        {
                            //Check SynchronizationSourceIdentifier
                            System.Diagnostics.Debug.Assert(s.SynchronizationSourceIdentifier == p.SynchronizationSourceIdentifier, "Unexpected SynchronizationSourceIdentifier");

                            //Check the Payload.Count
                            System.Diagnostics.Debug.Assert(s.Payload.Count == p.Payload.Count, "Unexpected Payload Count");

                            //Check the Length,
                            System.Diagnostics.Debug.Assert(s.Length == p.Length, "Unexpected Length");

                            //Check the BlockCount count
                            System.Diagnostics.Debug.Assert(s.BlockCount == p.BlockCount, "Unexpected BlockCount");

                            //Verify all IReportBlock
                            foreach (Rtcp.IReportBlock rb in s)
                            {
                                System.Diagnostics.Debug.Assert(rb.BlockIdentifier == 0, "Unexpected ChunkIdentifier");

                                System.Diagnostics.Debug.Assert(rb.BlockData.All(b => b == 0), "Unexpected BlockData");

                                System.Diagnostics.Debug.Assert(rb.Size == Media.Rtcp.ReportBlock.ReportBlockSize, "Unexpected Size");
                            }

                            //Check the RtcpData
                            System.Diagnostics.Debug.Assert(p.RtcpData.SequenceEqual(s.RtcpData), "Unexpected RtcpData");

                            //Check the PaddingOctets
                            System.Diagnostics.Debug.Assert(s.PaddingOctets == p.PaddingOctets, "Unexpected PaddingOctets");

                            //Check all data in the padding but not the padding octet itself.
                            System.Diagnostics.Debug.Assert(s.PaddingData.SequenceEqual(p.PaddingData), "Unexpected PaddingData");
                        }

                    }
                }
            }
        }
示例#4
0
        //6.3.2 Initialization....
        //I will do no such thing, I will no have no table when no table is required such as be the case when no expectance is put on the identity of the recipient.
        //All such packets should be considered equal unless specifically negioated by means provided by an alternate mechanism such as SDP or the RTP-Info header and is beyond the scope of the RtpClient implementation [based on my interpretation that is.]
        //I could go on and on about this but I think we all get the point
        //6.3.3 Rtp or Rtcp
        protected internal virtual void HandleIncomingRtcpPacket(object rtpClient, RtcpPacket packet)
        {
            //Determine if the packet can be handled
            if (IsDisposed || false == RtcpEnabled || false == HandleIncomingRtcpPackets || packet == null || packet.IsDisposed) return;

            //Raise an event for the rtcp packet received
            OnRtcpPacketReceieved(packet);

            //Get a context for the packet by the identity of the receiver
            TransportContext transportContext = null;

            //Cache the ssrc of the packet's sender.
            int partyId = packet.SynchronizationSourceIdentifier;

            //See if there is a context for the remote party. (Allows 0)
            transportContext = GetContextBySourceId(partyId);

            //Only if the packet was not addressed to a unique party with the id of 0
            if (partyId != 0 &&
                transportContext == null ||
                transportContext.InDiscovery) //The remote party has not yet been identified.
            {
                //Cache the payloadType and blockCount
                int payloadType = packet.PayloadType,
                    blockCount = packet.BlockCount;

                //Check the type

                if (payloadType == Rtcp.ReceiversReport.PayloadType &&  //The packet is a RecieversReport
                    blockCount > 0)//There is at least 1 block
                {
                    //Create a wrapper around the packet to access the ReportBlocks
                    using (var rr = new Rtcp.ReceiversReport(packet, false))
                    {
                        //Iterate each contained ReportBlock
                        foreach (Rtcp.IReportBlock reportBlock in rr)
                        {
                            int blockId = reportBlock.BlockIdentifier;

                            //Attempt to obtain a context by the identifier in the report block
                            transportContext = GetContextBySourceId(blockId);

                            //If there was a context and the remote party has not yet been identified.
                            if (transportContext != null && transportContext.InDiscovery)
                            {
                                //Identify the remote party by this id.
                                transportContext.RemoteSynchronizationSourceIdentifier = partyId;

                                Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleIncomingRtcpPacket Set RemoteSynchronizationSourceIdentifier @ " + transportContext.SynchronizationSourceIdentifier + " to=" + transportContext.RemoteSynchronizationSourceIdentifier + "RR blockId=" + blockId);

                                //Stop looking for a context.
                                break;
                            }
                        }
                    }
                }
                else if (payloadType == Rtcp.GoodbyeReport.PayloadType &&
                    blockCount > 0) //The GoodbyeReport report from a remote party
                {
                    //Create a wrapper around the packet to access the source list
                    using (var gb = new Rtcp.GoodbyeReport(packet, false))
                    {
                        using (var sl = gb.GetSourceList())
                        {
                            //Iterate each party leaving
                            foreach (int party in sl)
                            {
                                //Attempt to obtain a context by the identifier in the report block
                                transportContext = GetContextBySourceId(party);

                                //If there was a context
                                if (transportContext != null &&
                                    false == transportContext.IsDisposed)
                                {
                                    //Send report now if possible.
                                    bool reportsSent = SendReports(transportContext);

                                    Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleIncomingRtcpPacket Recieved Goodbye @ " + transportContext.SynchronizationSourceIdentifier + " from=" + partyId + " reportSent=" + reportsSent);

                                    //Stop looking for a context.
                                    break;
                                }
                            }
                        }
                    }
                }
                else if (payloadType == Rtcp.SendersReport.PayloadType) //The senders report from a remote party
                {
                    //If there is a context
                    if (transportContext != null)
                    {
                        //The context is valid and still discovering a remote identity
                        if (transportContext.IsValid && transportContext.InDiscovery)
                        {
                            //Assign it
                            transportContext.RemoteSynchronizationSourceIdentifier = partyId;

                            Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleIncomingRtcpPacket Set RemoteSynchronizationSourceIdentifier @ " + transportContext.SynchronizationSourceIdentifier + " to=" + transportContext.RemoteSynchronizationSourceIdentifier + " SR=" + partyId);

                        } //If the context has been identified by an identity other than the remote party of the packet
                        else if (transportContext.RemoteSynchronizationSourceIdentifier != partyId)
                        {
                            //Attempt to obtain a context by the identity used previously
                            transportContext = GetContextBySourceId(partyId);

                            //If ther is no longer a context or the context cannot handle the packet
                            if (transportContext == null ||
                                transportContext.IsDisposed)
                            {
                                goto NoContext;
                            }

                            //If the context needs a remote identity and is still not yet valid
                            if (transportContext.InDiscovery && false == transportContext.IsValid)
                            {
                                //Assign it
                                transportContext.RemoteSynchronizationSourceIdentifier = partyId;

                                Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleIncomingRtcpPacket Set RemoteSynchronizationSourceIdentifier @ " + transportContext.SynchronizationSourceIdentifier + " to=" + transportContext.RemoteSynchronizationSourceIdentifier + " SR=" + partyId);
                            }

                        }
                    }//Validate by using the blocks of the report if possible
                    else if (blockCount > 0)
                    {
                        //Create a wrapper around the packet to access the ReportBlocks
                        using (var sr = new Rtcp.SendersReport(packet, false))
                        {
                            //Iterate each contained ReportBlock
                            foreach (Rtcp.IReportBlock reportBlock in sr)
                            {
                                int blockId = reportBlock.BlockIdentifier;

                                //Attempt to obtain a context by the identifier in the report block
                                var context = GetContextBySourceId(reportBlock.BlockIdentifier);

                                //If there was a context
                                if (context != null)
                                {
                                    //if the context found identifies the context assumed
                                    if (context.SynchronizationSourceIdentifier == transportContext.SynchronizationSourceIdentifier)
                                    {
                                        //Identify the remote party by this id.
                                        transportContext.RemoteSynchronizationSourceIdentifier = packet.SynchronizationSourceIdentifier;

                                        Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleIncomingRtcpPacket Set RemoteSynchronizationSourceIdentifier @ " + transportContext.SynchronizationSourceIdentifier + " to=" + transportContext.RemoteSynchronizationSourceIdentifier + "SR blockId=" + blockId);

                                        //Remove any reference
                                        context = null;

                                        //Stop looking for a context.
                                        break;
                                    }
                                }
                            }

                            //might not have checked anything if the list was 'incomplete'..
                        }
                    }
                }
            }

            //Handle Goodbyes with a positive blockcount but no  sourcelist...?

            NoContext:

            //If no transportContext could be found
            if (transportContext == null)
            {
                //Attempt to see if this was a rtp packet by using the RtpPayloadType
                int rtpPayloadType = packet.Header.First16Bits.RtpPayloadType;

                if (rtpPayloadType == 13 || GetContextByPayloadType(rtpPayloadType) != null)
                {
                    Media.Common.ILoggingExtensions.Log(Logger, InternalId + "HandleIncomingRtcpPacket - Incoming RtcpPacket actually was Rtp. Ssrc= " + partyId + " Type=" + rtpPayloadType + " Len=" + packet.Length);

                    //Raise an event for the 'RtpPacket' received.
                    //Todo Use the existing reference / memory of the RtcpPacket)
                    OnRtpPacketReceieved(new RtpPacket(packet.Prepare().ToArray(), 0));

                    //Don't do anything else
                    return;
                }

                //Could attempt to find the context in which this packet is trying to communicate with if we had a RemoteEndPoint indicating where the packet was received from...
                //Cannot find a context because there may be more then one context which has not yet been identified
                //Could attempt to check that there is only 1 context and then if not yet valid assign the identity...
                //if(TransportContexts.Count == 1) ...

                Media.Common.ILoggingExtensions.Log(Logger, InternalId + "HandleIncomingRtcpPacket - No Context for packet " + partyId + "@" + packet.PayloadType);

                //Don't do anything else.
                return;
            }

            //There is a transportContext

            //If there is a collision in the unique identifiers
            if (transportContext.SynchronizationSourceIdentifier == partyId)
            {
                //Handle it.
                HandleCollision(transportContext);
            }

            //Make a copy of the packet now and only refer to this copy
            RtcpPacket localPacket = packet;

            #region Unused [Packet Completion]

            //Complete the RtcpPacket if required.
            //while (!localPacket.IsComplete)
            //{
            //    //Complete the packet.
            //    int received = localPacket.CompleteFrom(transportContext.RtcpSocket, localPacket.Payload);
            //}

            #endregion

            //Last Rtcp packet was received right now now.
            transportContext.m_LastRtcpIn = packet.Created;

            //The context is active.
            transportContext.m_InactiveTime = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan;

            //Don't worry about overflow
            unchecked
            {
                //Increment packets received for the valid context.
                ++transportContext.RtcpPacketsReceived;

                //Keep track of the the bytes sent in the context
                transportContext.RtcpBytesRecieved += localPacket.Length;

                //Set the time when the first rtcp packet was recieved
                if (transportContext.m_FirstPacketReceived == DateTime.MinValue) transportContext.m_FirstPacketReceived = packet.Created;
            }

            #region Unused [Handle if packet was Goodbye]

            //bool goodBye = packet.PayloadType == Rtcp.GoodbyeReport.PayloadType;

            ////If the context is valid, AND the remote identify has a value and the packet identity is not the same then reset the state and account for the new identity
            //if (transportContext.IsValid && transportContext.RemoteSynchronizationSourceIdentifier.HasValue && localPacket.SynchronizationSourceIdentifier != transportContext.RemoteSynchronizationSourceIdentifier)
            //{
            //    //Tell the source we are no longer listening to the old identity
            //    //SendGoodbye(transportContext);

            //    //Reset state for the counters
            //    //transportContext.ResetState();

            //    //Assign the new remote ID (EVENT?)
            //    transportContext.RemoteSynchronizationSourceIdentifier = localPacket.SynchronizationSourceIdentifier;

            //    //Send reports if we can unless this is a Goodbye
            //    /*if (!goodBye) */SendReports(transportContext);
            //}

            //if (goodBye && packet.BlockCount > 0) transportContext.m_SendInterval = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan; //Then never send reports again?

            #endregion
        }