Пример #1
0
 /// <summary>
 /// Throws an <see cref="ArgumentException"/> if the given <see cref="RtcpReport"/> is null or disposed or does not have the same <see cref="System.Type"/> which is expected as the <see cref="PayloadType"/> implemented.
 /// </summary>
 /// <param name="report">The <see cref="RtcpReport"/> to verify</param>
 internal static void VerifyPayloadType(this RtcpReport report)
 {
     if (RtcpPacket.GetImplementationForPayloadType(report.PayloadType) != report.GetType())
     {
         throw new ArgumentException("RtcpReport Implementation does not match type of report.");
     }
 }
Пример #2
0
 /// <summary>
 /// Constructs a GoodbyeReport instance from an existing RtcpPacket reference.
 /// Throws a ArgumentNullException if reference is null.
 /// Throws an ArgumentException if the <see cref="RtcpHeader.PayloadType"/> is not GoodbyeReport (203)
 /// </summary>
 /// <param name="reference">The packet containing the GoodbyeReport</param>
 public GoodbyeReport(RtcpPacket reference, bool shouldDispose)
     : this(reference.Header, reference.Payload)
 {
     if (Header.PayloadType != PayloadType)
     {
         throw new ArgumentException("Header.PayloadType is not equal to the expected type of 203.", "reference");
     }
 }
 public ApplicationSpecificReport(RtcpPacket reference, bool shouldDispose = true)
     : base(reference.Header, reference.Payload, shouldDispose)
 {
     if (Header.PayloadType != PayloadType)
     {
         throw new ArgumentException("Header.PayloadType is not equal to the expected type of 204.", "reference");
     }
 }
Пример #4
0
 public SendersReport(RtcpPacket reference, bool shouldDispose = true)
     : base(reference.Header, reference.Payload, shouldDispose)
 {
     if (Header.PayloadType != PayloadType)
     {
         throw new ArgumentException("Header.PayloadType is not equal to the expected type of 200.", "reference");
     }
     //RtcpReportExtensions.VerifyPayloadType(this);
 }
Пример #5
0
        public override bool Equals(object obj)
        {
            if (System.Object.ReferenceEquals(this, obj))
            {
                return(true);
            }

            if (false == (obj is RtcpPacket))
            {
                return(false);
            }

            RtcpPacket other = obj as RtcpPacket;

            return(other.Length == Length
                   &&
                   other.Payload == Payload
                   &&
                   other.GetHashCode() == GetHashCode());
        }
Пример #6
0
        /// <summary>
        /// Parses all RtcpPackets contained in the array using the given paramters.
        /// </summary>
        /// <param name="array">The array to parse packets from.</param>
        /// <param name="offset">The index to start parsing</param>
        /// <param name="count">The amount of bytes to use in parsing</param>
        /// <param name="version">The optional <see cref="RtcpPacket.Version"/>version of the packets</param>
        /// <param name="payloadType">The optional <see cref="RtcpPacket.PayloadType"/> of all packets</param>
        /// <param name="ssrc">The optional <see cref="RtcpPacket.SynchronizationSourceIdentifier"/> of all packets</param>
        /// <returns>A pointer to each packet found</returns>
        public static IEnumerable <RtcpPacket> GetPackets(byte[] array, int offset, int count, int version = 2, int?payloadType = null, int?ssrc = null)
        {
            //array.GetLowerBound(0) for VB, UpperBound(0) is then the index of the last element
            int lowerBound = 0, upperBound = array.Length;

            if (offset < lowerBound || offset > upperBound)
            {
                throw new ArgumentOutOfRangeException("index", "Must refer to an accessible position in the given array");
            }

            if (count <= lowerBound)
            {
                yield break;
            }

            if (count > upperBound)
            {
                throw new ArgumentOutOfRangeException("count", "Must refer to an accessible position in the given array");
            }

            //Would overflow the array
            if (count + offset > upperBound)
            {
                throw new ArgumentOutOfRangeException("index", "Count must refer to an accessible position in the given array when deleniated by index");
            }

            //While  a 32 bit value remains to be read in the vector
            while (offset + RtcpHeader.Length < upperBound && count >= RtcpHeader.Length)
            {
                //Get the header of the packet to verify if it is wanted or not
                using (var header = new RtcpHeader(new Common.MemorySegment(array, offset, count)))
                {
                    //Determine how long the header was
                    int payloadOffset = header.Size;

                    //Determine the amount of bytes in the packet NOT INCLUDING the RtcpHeader (Which may be 0)
                    int lengthInBytes = Binary.MachineWordsToBytes(header.LengthInWordsMinusOne + 1); //((lengthInWords + 1) * 4) - payloadOffset;

                    //Create a packet using the existing header and the bytes left in the packet
                    using (RtcpPacket newPacket = new RtcpPacket(header, new MemorySegment(array, offset + payloadOffset, Binary.Min(lengthInBytes - payloadOffset, count - payloadOffset))))
                    {
                        //Move the offset the length in bytes of the size of the last packet (including the header).
                        offset += newPacket.Length;

                        //Reduce the count
                        count -= newPacket.Length;

                        //Check for the optional parameters
                        if (payloadType.HasValue && payloadType.Value != header.PayloadType ||            // Check for the given payloadType if provided
                            ssrc.HasValue && ssrc.Value != header.SendersSynchronizationSourceIdentifier) //Check for the given ssrc if provided
                        {
                            //Skip the packet
                            continue;
                        }

                        //Yield the packet, disposed afterwards
                        yield return(newPacket);
                    }
                }
            }

            //Done parsing
            yield break;
        }
Пример #7
0
        /// <summary>
        /// Called for each RtcpPacket recevied in the source RtpClient
        /// </summary>
        /// <param name="stream">The listener from which the packet arrived</param>
        /// <param name="packet">The packet which arrived</param>
        internal void OnSourceRtcpPacketRecieved(object stream, RtcpPacket packet)
        {
            if (packet == null || packet.IsDisposed || m_RtpClient == null) return;

            //Should check for Goodbye and Disconnect this source

            //m_RtpClient.SendReports();

            //m_RtpClient.EnquePacket(new RtcpPacket(packet.Prepare().ToArray(), 0));

            //if (packet.PayloadType == Rtcp.SendersReport.PayloadType) // Reduced size...
            //{

            //    var sourceContext = SourceContexts.FirstOrDefault(tc => tc.RemoteSynchronizationSourceIdentifier == packet.SynchronizationSourceIdentifier);

            //    if (sourceContext == null) return;

            //    var context = m_RtpClient.GetContextByPayloadType(sourceContext.MediaDescription.MediaFormat);

            //    if (context == null) return;

            //    context.RtpTransit = sourceContext.RtpTransit;
            //    context.RtpJitter = sourceContext.RtpJitter;

                //using (Rtcp.SendersReport sr = new SendersReport(packet, false))
                //{
                //    context.NtpTimestamp = sr.NtpTimestamp;
                //    context.RtpTimestamp = sr.RtpTimestamp;

                //    if (sr.BlockCount > 0)
                //    {
                //        Rtcp.IReportBlock reportBlock = sr.First(rb => SourceContexts.Any(sc => sc.RemoteSynchronizationSourceIdentifier == rb.BlockIdentifier));

                //        if (reportBlock != null)
                //        {
                //            ReportBlock block = (ReportBlock)reportBlock;

                //            context.SequenceNumber = block.ExtendedHighestSequenceNumberReceived;
                //            context.RtpJitter = (uint)block.InterarrivalJitterEstimate;
                //        }
                //    }
                //}

            //}

            //m_RtpClient.SendReports();
        }
Пример #8
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
        }
Пример #9
0
    public void TestRtcpPacket()
    {
        Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

        //Write all Abstrractions to the console
        foreach (var abstraction in Media.Rtcp.RtcpPacket.GetImplementedAbstractions())
            Console.WriteLine(string.Format(TestingFormat, "\tFound Abstraction", "Implemented By" + abstraction.Name));

        //Write all Implementations to the console
        foreach (var implementation in Media.Rtcp.RtcpPacket.GetImplementations())
            Console.WriteLine(string.Format(TestingFormat, "\tPayloadType " + implementation.Key, "Implemented By" + implementation.Value.Name));

        //Create a RtpPacket instance
        Media.Rtcp.RtcpPacket p = new Media.Rtcp.RtcpPacket(new Media.Rtcp.RtcpHeader(0, 0, false, 0), Enumerable.Empty<byte>());

        //Check the Padding bit after modification
        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == 0, "SynchronizationSourceIdentifier should equal 0");

        //Set a values
        p.SynchronizationSourceIdentifier = 7;

        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == 7, "SynchronizationSourceIdentifier should equal 7");

        //Cache a bitValue
        bool bitValue = false;

        //Test every possible bit packed value that can be valid in the first and second octet
        for (int ibitValue = 0; ibitValue < 2; ++ibitValue)
        {
            //Make a bitValue after the 0th iteration
            if (ibitValue > 0) bitValue = Convert.ToBoolean(ibitValue);

            //Complete tested the first and second octets with the current bitValue
            //Console.WriteLine(string.Format(TestingFormat, "\tbitValue", bitValue + "\r\n"));

            //Permute every possible value within the 2 bit Version
            for (int VersionCounter = 0; VersionCounter <= Media.Common.Binary.TwoBitMaxValue; ++VersionCounter)
            {
                //Set the version
                p.Version = VersionCounter;

                //Write the version information to the console.
                //Console.Write(string.Format(TestingFormat, "\tVersionCounter", VersionCounter));
                //Console.Write(string.Format(TestingFormat, " Version", p.Version + "\r\n"));

                //Set the bit values in the first octet
                p.Padding = bitValue;

                //Check the version bits after modification
                System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                //Check the Padding bit after modification
                System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                //Permute every possible value in the 7 bit PayloadCounter
                for (int PayloadCounter = 0; PayloadCounter <= byte.MaxValue; ++PayloadCounter)
                {
                    //Set the 7 bit value in the second octet.
                    p.PayloadType = (byte)PayloadCounter;

                    //Write the value of the PayloadCounter to the console and the packet value to the Console.
                    //Console.Write(string.Format(TestingFormat, "\tPayloadCounter", PayloadCounter));
                    //Console.Write(string.Format(TestingFormat, " PayloadType", p.PayloadType + "\r\n"));

                    //Check the PayloadType
                    System.Diagnostics.Debug.Assert(p.PayloadType == PayloadCounter, payloadException.Message);

                    //Check the Padding bit after setting the PayloadType
                    System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                    //Permute every combination for a nybble
                    for (int ReportBlockCounter = byte.MinValue; ReportBlockCounter <= Media.Common.Binary.FiveBitMaxValue; ++ReportBlockCounter)
                    {
                        ///////////////Set the CC nibble in the first Octet
                        p.BlockCount = (byte)ReportBlockCounter;
                        /////////////

                        //Identify the Contributing Source Counter and the Packet's value
                        //Console.Write(string.Format(TestingFormat, "\tReportBlockCounter", ReportBlockCounter));
                        //Console.Write(string.Format(TestingFormat, " BlockCount", p.BlockCount + "\r\n"));

                        //Check the BlockCount
                        System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, reportBlockException.Message);

                        //Ensure the Version after modification
                        System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                        //Check the Padding after modification
                        System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                        ///////////////Serialize the packet
                        using (p = new Media.Rtcp.RtcpPacket(p.Prepare().ToArray(), 0))
                        {
                            /////////////

                            //Ensure the version remains after modification
                            System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                            //Ensure the Padding bit after modification
                            System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                            //Check the BlockCount after modification
                            System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, reportBlockException.Message);

                            //Check for a valid header
                            if (false == p.Header.IsValid(VersionCounter, PayloadCounter, bitValue)
                                || //Check for validation per RFC3550 A.1 when the test permits
                                false == bitValue &&
                                VersionCounter > 1 &&
                                PayloadCounter >= 200 &&
                                PayloadCounter <= 201 &&
                                false == Media.RFC3550.IsValidRtcpHeader(p.Header, VersionCounter)) throw inValidHeaderException;
                        }

                        //Perform checks with length in words set incorrectly
                    }
                }
            }

            //Console.WriteLine(string.Format(TestingFormat, "\t*****Completed an iteration wih bitValue", bitValue + "*****"));
        }
    }
Пример #10
0
    public void TestRtcpPacketExamples()
    {
        Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

        byte[] output;

        //Keep a copy of these exceptions to throw in case some error occurs.
        Exception invalidLength = new Exception("Invalid Length"), invalidData = new Exception("Invalid Data in packet"), invalidPadding = new Exception("Invalid Padding"), incompleteFalse = new Exception("Packet IsComplete is false");

        //Create a Media.RtcpPacket with only a header (results in 8 octets of 0x00 which make up the header)
        Media.Rtcp.RtcpPacket rtcpPacket = new Media.Rtcp.RtcpPacket(0, 0, 0, 0, 0, 0);

        //Prepare a sequence which contains the data in the packet including the header
        IEnumerable <byte> preparedPacket = rtcpPacket.Prepare();

        //Check for an invlaid length
        if (rtcpPacket.Payload.Count > 0 || rtcpPacket.Header.LengthInWordsMinusOne != 0 && rtcpPacket.Length != Media.Rtcp.RtcpHeader.Length || preparedPacket.Count() != Media.Rtcp.RtcpHeader.Length)
        {
            throw invalidLength;
        }

        //Check for any data in the packet binary
        if (preparedPacket.Any(o => o != default(byte)))
        {
            throw invalidData;
        }

        //Set padding in the header
        rtcpPacket.Padding = true;

        //Check for some invalid valid
        if (rtcpPacket.PaddingOctets > 0)
        {
            throw invalidPadding;
        }

        //Ensure the packet is complete
        if (rtcpPacket.IsComplete == false)
        {
            throw incompleteFalse;
        }

        //Add nothing to the payload
        rtcpPacket.AddBytesToPayload(Media.RFC3550.CreatePadding(0), 0, 0);

        //Ensure the packet is complete
        if (rtcpPacket.IsComplete == false)
        {
            throw incompleteFalse;
        }

        //Check for some invalid value
        if (rtcpPacket.PaddingOctets > 0)
        {
            throw invalidPadding;
        }

        //Make a bunch of packets with padding
        for (int paddingAmount = 1, e = byte.MaxValue; paddingAmount <= e; ++paddingAmount)
        {
            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Making Media.RtcpPacket with Padding", paddingAmount));

            //Try to make a padded packet with the given amount
            rtcpPacket = new Media.Rtcp.RtcpPacket(0, 0, paddingAmount, 0, 0, 0);

            //A a 4 bytes which are not padding related
            rtcpPacket.AddBytesToPayload(Enumerable.Repeat(default(byte), 4));

            //Check ReadPadding works after adding bytes to the payload
            if (rtcpPacket.PaddingOctets != paddingAmount)
            {
                throw invalidPadding;
            }

            //Ensure the packet is complete
            if (rtcpPacket.IsComplete == false)
            {
                throw incompleteFalse;
            }

            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Packet Length", rtcpPacket.Length));

            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Packet Padding", rtcpPacket.PaddingOctets));
        }

        //Create a new SendersReport with no blocks
        using (Media.Rtcp.RtcpReport testReport = new Media.Rtcp.SendersReport(2, 0, 7))
        {
            //The Media.RtcpData property contains all data which in the Media.RtcpPacket without padding
            if (testReport.RtcpData.Count() != 20 && testReport.Length != 20)
            {
                throw invalidLength;
            }

            output = testReport.Prepare().ToArray();//should be exactly equal to example
        }

        //Example of a Senders Report
        byte[] example = new byte[]
        {
            0x81, 0xc8, 0x00, 0x0c, 0xa3, 0x36, 0x84, 0x36, 0xd4, 0xa6, 0xaf, 0x65, 0x00, 0x00, 0x00, 0x0,
            0xcb, 0xf9, 0x44, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x36, 0x84, 0x36,
            0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00
        };

        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0);
        if (rtcpPacket.Length != example.Length)
        {
            throw new Exception("Invalid Length.");
        }

        //Make a SendersReport to access the SendersInformation and ReportBlocks, do not dispose the packet when done with the report
        using (Media.Rtcp.SendersReport sr = new Media.Rtcp.SendersReport(rtcpPacket, false))
        {
            //Check the invalid block count
            if (sr.BlockCount != 1)
            {
                throw new Exception("Invalid Block Count!");
            }
            else
            {
                Console.WriteLine(sr.BlockCount); //16, should be 1
            }
            if ((uint)sr.SynchronizationSourceIdentifier != (uint)2738258998)
            {
                throw new Exception("Invalid Senders SSRC!");
            }
            else
            {
                Console.WriteLine(sr.SynchronizationSourceIdentifier); //0xa3368436
            }
            //Ensure setting the value through a setter is correct
            sr.NtpTimestamp = sr.NtpTimestamp;//14697854519044210688
            if ((ulong)sr.NtpTimestamp != 3567693669)
            {
                throw new Exception("Invalid NtpTimestamp!");
            }
            else
            {
                Console.WriteLine(sr.NtpTimestamp);
            }

            //Timestamp
            if ((uint)sr.RtpTimestamp != 3422110928)
            {
                throw new Exception("Invalid RtpTimestamp!");
            }
            else
            {
                Console.WriteLine(sr.RtpTimestamp); //0
            }
            //Data in report (Should only be 1)
            foreach (Media.Rtcp.IReportBlock rb in sr)
            {
                //if ((uint)rb.BlockIdentifier != 2738258998) throw new Exception("Invalid BlockIdentifier");
                //else if (rb is Media.Rtcp.ReportBlock)
                //{
                Media.Rtcp.ReportBlock asReportBlock = (Media.Rtcp.ReportBlock)rb;

                if (rb.BlockIdentifier != asReportBlock.SendersSynchronizationSourceIdentifier)
                {
                    throw new Exception("Invalid SendersSynchronizationSourceIdentifier");
                }

                Console.WriteLine(asReportBlock.SendersSynchronizationSourceIdentifier); //0
                Console.WriteLine(asReportBlock.FractionsLost);                          //0
                Console.WriteLine(asReportBlock.CumulativePacketsLost);                  //0
                Console.WriteLine(asReportBlock.ExtendedHighestSequenceNumberReceived);  //0
                Console.WriteLine(asReportBlock.InterarrivalJitterEstimate);             //0
                Console.WriteLine(asReportBlock.LastSendersReportTimestamp);             //0
                //}
            }

            //Check the length to be exactly the same as the example
            if (sr.Length != example.Length)
            {
                throw new Exception("Invalid Length");
            }

            //Verify SendersReport byte for byte
            output = sr.Prepare().ToArray();//should be exactly equal to example
            for (int i = 0, e = example.Length; i < e; ++i)
            {
                if (example[i] != output[i])
                {
                    throw new Exception("Result Packet Does Not Match Example");
                }
            }
        }

        if (rtcpPacket.Header.IsDisposed || rtcpPacket.IsDisposed)
        {
            throw new Exception("Disposed the Media.RtcpPacket");
        }

        //Now the packet can be disposed
        rtcpPacket.Dispose();
        rtcpPacket = null;

        rtcpPacket = new Media.Rtcp.SendersReport(2, 0, 7);

        example = rtcpPacket.Prepare().ToArray();

        if (rtcpPacket.SynchronizationSourceIdentifier != 7)
        {
            throw new Exception("Unexpected SynchronizationSourceIdentifier");
        }

        if (rtcpPacket.BlockCount != 0)
        {
            throw new Exception("Unexpected BlockCount");
        }

        //Check the Length, 8 Byte Header, 20 Byte SendersInformation
        if (rtcpPacket.Length != Media.Rtcp.RtcpHeader.Length + Media.Rtcp.ReportBlock.ReportBlockSize)
        {
            throw new Exception("Unexpected BlockCount");
        }

        //Iterate each IReportBlock in the RtcpReport representation of the rtcpPacket instance
        foreach (Media.Rtcp.IReportBlock rb in rtcpPacket as Media.Rtcp.RtcpReport)
        {
            Console.WriteLine(rb);

            throw new Exception("Unexpected BlockCount");
        }

        //Next Sub Test

        //Create a GoodbyeReport with no SourceList, e.g. a BlockCount of 0.
        //There should be 8 bytes, 4 for the RtcpHeader and 4 for the SynchronizationSourceIdentifier
        //The LengthInWordsMinusOne should equal 1 (1 + 1 = 2, 2 * 4 = 8)
        using (var testReport = new Media.Rtcp.GoodbyeReport(2, 7))
        {
            output = testReport.Prepare().ToArray();

            if (output.Length != testReport.Length || testReport.Header.LengthInWordsMinusOne != 1 || testReport.Length != 8)
            {
                throw new Exception("Invalid Length");
            }

            if (testReport.BlockCount != 0)
            {
                throw reportBlockException;
            }

            if (output[7] != 7 || testReport.SynchronizationSourceIdentifier != 7)
            {
                throw new Exception("Invalid ssrc");
            }
        }

        //Add a Reason For Leaving

        //Should now have 4 words... Header, SSRC, Block, Reason
        using (var testReport = new Media.Rtcp.GoodbyeReport(2, 7, System.Text.Encoding.ASCII.GetBytes("v")))
        {
            output = testReport.Prepare().ToArray();

            //3
            if (output.Length != testReport.Length || testReport.Header.LengthInWordsMinusOne != 2 || testReport.Length != 12)
            {
                throw new Exception("Invalid Length");
            }

            if (testReport.BlockCount != 0)
            {
                throw reportBlockException;
            }

            if (output[7] != 7 || testReport.SynchronizationSourceIdentifier != 7)
            {
                throw new Exception("Invalid ssrc");
            }

            if (false == testReport.HasReasonForLeaving)
            {
                throw new Exception("Has no reason for leaving.");
            }

            if (System.Text.Encoding.ASCII.GetString(testReport.ReasonForLeaving.ToArray()) != "v")
            {
                throw new Exception("Does not have expected reason for leaving.");
            }
        }

        //Next Sub Test
        /////

        //Recievers Report and Source Description
        example = new byte[] { 0x81, 0xc9, 0x00, 0x07,
                               0x69, 0xf2, 0x79, 0x50,
                               0x61, 0x37, 0x94, 0x50,
                               0xff, 0xff, 0xff, 0xff,
                               0x00, 0x01, 0x00, 0x52,
                               0x00, 0x00, 0x0e, 0xbb,
                               0xce, 0xd4, 0xc8, 0xf5,
                               0x00, 0x00, 0x84, 0x28,

                               0x81, 0xca, 0x00, 0x04,
                               0x69, 0xf2, 0x79, 0x50,
                               0x01, 0x06, 0x4a, 0x61,
                               0x79, 0x2d, 0x50, 0x43,
                               0x00, 0x00, 0x00, 0x00 };


        int foundPackets = 0, foundSize = 0;

        foreach (Media.Rtcp.RtcpPacket packet in Media.Rtcp.RtcpPacket.GetPackets(example, 0, example.Length))
        {
            ++foundPackets;

            foundSize += packet.Length;
        }

        if (foundPackets != 2)
        {
            throw new Exception("Unexpected amount of packets found");
        }

        if (foundSize != example.Length)
        {
            throw new Exception("Unexpected total length of packets found");
        }

        //Or manually for some reason
        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0); // The same as foundPackets[0]
        using (Media.Rtcp.ReceiversReport rr = new Media.Rtcp.ReceiversReport(rtcpPacket, false))
        {
            Console.WriteLine(rr.SynchronizationSourceIdentifier);//1777498448

            //Check the invalid block count
            if (rr.BlockCount != 1)
            {
                throw new Exception("Invalid Block Count!");
            }
            else
            {
                Console.WriteLine(rr.BlockCount); //16, should be 1
            }
            using (var enumerator = rr.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    Console.WriteLine("Current IReportBlock Identifier: " + enumerator.Current.BlockIdentifier);//1631032400

                    //If the instance boxed in the Interface is a ReportBlock
                    if (enumerator.Current is Media.Rtcp.ReportBlock)
                    {
                        //Unbox the Interface as it's ReportBlock Instance
                        Media.Rtcp.ReportBlock asReportBlock = enumerator.Current as Media.Rtcp.ReportBlock;

                        Console.WriteLine("Found a ReportBlock");

                        //Print the instance information
                        Console.WriteLine("FractionsLost: " + asReportBlock.FractionsLost);                                                 //255/256 0xff
                        Console.WriteLine("CumulativePacketsLost: " + asReportBlock.CumulativePacketsLost);                                 //-1, 0xff,0xff,0xff
                        Console.WriteLine("ExtendedHighestSequenceNumberReceived: " + asReportBlock.ExtendedHighestSequenceNumberReceived); //65618, 00, 01, 00, 52
                        Console.WriteLine("InterarrivalJitterEstimate: " + asReportBlock.InterarrivalJitterEstimate);                       //3771
                        Console.WriteLine("LastSendersReportTimestamp: " + asReportBlock.LastSendersReportTimestamp);                       //3470000128
                    }
                    else //Not a ReportBlock
                    {
                        Console.WriteLine("Current IReportBlock TypeName: " + enumerator.Current.GetType().Name);
                        Console.WriteLine("Current IReportBlock Data: " + BitConverter.ToString(enumerator.Current.BlockData.ToArray()));
                    }
                }
            }

            //Verify RecieversReport byte for byte
            output = rr.Prepare().ToArray();//should be exactly equal to example's bytes when extension data is contained in the packet instance

            //Use to get the raw data in the packet including the header
            //.Take(rr.Length - rr.ExtensionDataOctets)

            //Or rr.ReportData which omits the header...

            //What other variations are relvent? Please submit examples, even invalid ones will be tolerated!

            //The bytes given here should reflect exactly the bytes in the example array because of how the data is formatted.

            //Yes the data is compound but there is an invalid LengthInWords in the packet which must be exactly copied when deserialized
            if (rr.HasExtensionData && false == output.SequenceEqual(example))
            {
                throw new Exception("Result Packet Does Not Match Example");
            }
            else
            {
                output = rr.Prepare(true, true, false, true).ToArray();
            }
            for (int i = 0, e = output.Length; i < e; ++i)
            {
                if (example[i] != output[i])
                {
                    throw new Exception("Result Packet Does Not Match Example @" + i);
                }
            }
        }

        if (rtcpPacket.Header.IsDisposed || rtcpPacket.IsDisposed)
        {
            throw new Exception("Disposed the Media.RtcpPacket");
        }

        //Now the packet can be disposed
        rtcpPacket.Dispose();
        rtcpPacket = null;

        //Make another packet instance from the rest of the example data.
        rtcpPacket = new Media.Rtcp.RtcpPacket(example, output.Length);

        //Create a SourceDescriptionReport from the packet instance to access the SourceDescriptionChunks
        using (Media.Rtcp.SourceDescriptionReport sourceDescription = new Media.Rtcp.SourceDescriptionReport(rtcpPacket, false))
        {
            if (false == sourceDescription.HasCName)
            {
                throw new Exception("Unexpected HasCName");
            }

            if (sourceDescription.BlockCount != 1)
            {
                throw new Exception("Unexpected BlockCount");
            }

            if (sourceDescription.Chunks.First().ChunkIdentifer != 1777498448)
            {
                throw new Exception("Chunks.ChunkIdentifer");
            }

            if (false == sourceDescription.Chunks.First().HasItems)
            {
                throw new Exception("Chunks.HasItems");
            }

            if (sourceDescription.Chunks.First().Items.First().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName)
            {
                throw new Exception("Unexpected ItemType");
            }

            if (sourceDescription.Chunks.First().Items.First().ItemLength != 6)
            {
                throw new Exception("Unexpected ItemLength");
            }

            foreach (Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk chunk in sourceDescription.GetChunkIterator())
            {
                if (chunk.ChunkIdentifer != 1777498448)
                {
                    throw new Exception("Chunks.ChunkIdentifer");
                }

                Console.WriteLine(string.Format(TestingFormat, "Chunk Identifier", chunk.ChunkIdentifer));

                //Use a SourceDescriptionItemList to access the items within the Chunk
                //This is performed auto magically when using the foreach pattern
                foreach (Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem item in chunk /*.AsEnumerable<Rtcp.SourceDescriptionItem>()*/)
                {
                    //if (item.ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName) throw new Exception("Unexpected ItemType");

                    //if (item.ItemLength != 6) throw new Exception("Unexpected ItemLength");

                    Console.WriteLine(string.Format(TestingFormat, "Item Type", item.ItemType));

                    Console.WriteLine(string.Format(TestingFormat, "Item Length", item.ItemLength));

                    Console.WriteLine(string.Format(TestingFormat, "Item Data", BitConverter.ToString(item.ItemData.ToArray())));
                }
            }

            //Verify SourceDescriptionReport byte for byte
            output = sourceDescription.Prepare().ToArray();//should be exactly equal to example
            for (int i = output.Length, e = sourceDescription.Length; i < e; ++i)
            {
                if (example[i] != output[i])
                {
                    throw new Exception("Result Packet Does Not Match Example");
                }
            }
        }

        //ApplicationSpecific - qtsi

        example = new byte[] { 0x81, 0xcc, 0x00, 0x06, 0x4e, 0xc8, 0x79, 0x50, 0x71, 0x74, 0x73, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14 };

        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0);

        //Make a ApplicationSpecificReport instance
        Media.Rtcp.ApplicationSpecificReport app = new Media.Rtcp.ApplicationSpecificReport(rtcpPacket);

        //Check the name to be equal to qtsi
        if (false == app.Name.SequenceEqual(System.Text.Encoding.UTF8.GetBytes("qtsi")))
        {
            throw new Exception("Invalid App Packet Type");
        }

        //Check the length
        if (rtcpPacket.Length != example.Length)
        {
            throw new Exception("Invalid Legnth");
        }

        //Verify ApplicationSpecificReport byte for byte
        output = rtcpPacket.Prepare().ToArray();//should be exactly equal to example
        for (int i = 0, e = example.Length; i < e; ++i)
        {
            if (example[i] != output[i])
            {
                throw new Exception("Result Packet Does Not Match Example");
            }
        }

        //Test making a packet with a known length in bytes
        Media.Rtcp.SourceDescriptionReport sd    = new Media.Rtcp.SourceDescriptionReport(2);
        byte[] sdOut = sd.Prepare().ToArray();

        //1 word when the ssrc is present but would be an invalid sdes because blockCount = 0
        if (false == sd.IsComplete || sd.Length != Media.Rtcp.RtcpHeader.Length || sd.Header.LengthInWordsMinusOne != ushort.MaxValue)
        {
            throw new Exception("Invalid Length");
        }

        //Create 9 bytes of data to add to the existing SourceDescriptionReport
        byte[] itemData = System.Text.Encoding.UTF8.GetBytes("FLABIA-PC");

        int KnownId = 0x1AB7C080;

        //Point the rtcpPacket at the SourceDescription instance
        rtcpPacket = sd;

        //Create a Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk containing a Known Identifier
        //Which Contains a Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem with the Type 'CName' containing the itemData
        //Add the Media.Rtcp.IReportBlock to the RtcpReport
        sd.Add((Media.Rtcp.IReportBlock) new Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk(KnownId,
                                                                                                       new Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName,
                                                                                                                                                                    itemData.Length, itemData, 0))); // ItemType(End) = 1, ItemLength(9) = 1, ItemData(9) = 11 Bytes in the Item, ChunkIdentifier(0x1AB7C080) = 4, 15 total bytes

        //Add an unpadded item for a 19 byte packet.
        //sd.Add(new Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk(KnownId,
        //    new Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName,
        //        itemData.Length, itemData, 0)), false); // ItemType(End) = 1, ItemLength(9) = 1, ItemData(9) = 11 Bytes in the Item, ChunkIdentifier(0x1AB7C080) = 4, 15 total bytes

        //Ensure the data is present where it is supposed to be, more data may be present to respect octet alignment
        if (false == sd.RtcpData.Skip(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.ItemHeaderSize).Take(itemData.Length).SequenceEqual(itemData))
        {
            throw new Exception("Invalid ItemData");
        }

        if (false == sd.Chunks.First().HasItems)
        {
            throw new Exception("Unexpected HasItems");
        }

        if (sd.Chunks.First().ChunkIdentifer != KnownId)
        {
            throw new Exception("Unexpected Chunks.ChunkIdentifer");
        }

        if (sd.Chunks.First().Items.Count() != 2)
        {
            throw new Exception("Unexpected Chunks.Items.Count");
        }

        if (sd.Chunks.First().Items.First().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName)
        {
            throw new Exception("Unexpected Items.ItemType");
        }

        if (sd.Chunks.First().Items.First().ItemLength != 9)
        {
            throw new Exception("Unexpected Chunks.Items.ItemLength");
        }

        if (false == sd.Chunks.First().Items.First().ItemData.SequenceEqual(itemData))
        {
            throw new Exception("Unexpected Chunks.Items.ItemData");
        }

        if (sd.Chunks.First().Items.Last().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.End)
        {
            throw new Exception("Unexpected Items.ItemType");
        }

        if (sd.Chunks.First().Items.Last().ItemLength != 1)
        {
            throw new Exception("Unexpected Chunks.Items.ItemLength");
        }

        if (false == sd.Chunks.First().Items.Last().ItemData.All(b => b == 0))
        {
            throw new Exception("Unexpected Chunks.Items.ItemData");
        }

        //
        // Header = 4 Bytes, 1 Word
        // There is a SSRC which occupies 1 Word
        //in a SourceDescription, The First Chunk is `Overlapped` in the header and the BlockIdentifier is shared with the SSRC

        //Ensure the data is present where it is supposed to be
        if (sd.SynchronizationSourceIdentifier != KnownId)
        {
            throw new Exception("Invalid SynchronizationSourceIdentifier");
        }

        //asPacket now contains 11 octets in the payload.
        //asPacket now has 1 block (1 chunk of 15 bytes)
        //asPacket is 19 octets long, 11 octets in the payload and 8 octets in the header
        //asPacket would have a LengthInWordsMinusOne of 3 because 19 / 4 = 4 - 1 = 3
        //But null octets are added (Per RFC3550 @ Page 45 [Paragraph 2] / http://tools.ietf.org/html/rfc3550#appendix-A.4)
        //19 + 1 = 20, 20 / 4 = 5, 5 - 1 = 4.
        if (false == rtcpPacket.IsComplete || rtcpPacket.Length != 20 || rtcpPacket.Header.LengthInWordsMinusOne != 4)
        {
            throw new Exception("Invalid Length");
        }
    }
Пример #11
0
    public void TestRtcpPacket()
    {
        Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

        //Write all Abstrractions to the console
        foreach (var abstraction in Media.Rtcp.RtcpPacket.GetImplementedAbstractions())
        {
            Console.WriteLine(string.Format(TestingFormat, "\tFound Abstraction", "Implemented By" + abstraction.Name));
        }

        //Write all Implementations to the console
        foreach (var implementation in Media.Rtcp.RtcpPacket.GetImplementations())
        {
            Console.WriteLine(string.Format(TestingFormat, "\tPayloadType " + implementation.Key, "Implemented By" + implementation.Value.Name));
        }

        //Create a RtpPacket instance
        Media.Rtcp.RtcpPacket p = new Media.Rtcp.RtcpPacket(new Media.Rtcp.RtcpHeader(0, 0, false, 0), Enumerable.Empty <byte>());

        //Check the Padding bit after modification
        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == 0, "SynchronizationSourceIdentifier should equal 0");

        //Set a values
        p.SynchronizationSourceIdentifier = 7;

        System.Diagnostics.Debug.Assert(p.SynchronizationSourceIdentifier == 7, "SynchronizationSourceIdentifier should equal 7");

        //Cache a bitValue
        bool bitValue = false;

        //Test every possible bit packed value that can be valid in the first and second octet
        for (int ibitValue = 0; ibitValue < 2; ++ibitValue)
        {
            //Make a bitValue after the 0th iteration
            if (ibitValue > 0)
            {
                bitValue = Convert.ToBoolean(ibitValue);
            }

            //Complete tested the first and second octets with the current bitValue
            //Console.WriteLine(string.Format(TestingFormat, "\tbitValue", bitValue + "\r\n"));

            //Permute every possible value within the 2 bit Version
            for (int VersionCounter = 0; VersionCounter <= Media.Common.Binary.TwoBitMaxValue; ++VersionCounter)
            {
                //Set the version
                p.Version = VersionCounter;

                //Write the version information to the console.
                //Console.Write(string.Format(TestingFormat, "\tVersionCounter", VersionCounter));
                //Console.Write(string.Format(TestingFormat, " Version", p.Version + "\r\n"));

                //Set the bit values in the first octet
                p.Padding = bitValue;

                //Check the version bits after modification
                System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                //Check the Padding bit after modification
                System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                //Permute every possible value in the 7 bit PayloadCounter
                for (int PayloadCounter = 0; PayloadCounter <= byte.MaxValue; ++PayloadCounter)
                {
                    //Set the 7 bit value in the second octet.
                    p.PayloadType = (byte)PayloadCounter;

                    //Write the value of the PayloadCounter to the console and the packet value to the Console.
                    //Console.Write(string.Format(TestingFormat, "\tPayloadCounter", PayloadCounter));
                    //Console.Write(string.Format(TestingFormat, " PayloadType", p.PayloadType + "\r\n"));

                    //Check the PayloadType
                    System.Diagnostics.Debug.Assert(p.PayloadType == PayloadCounter, payloadException.Message);

                    //Check the Padding bit after setting the PayloadType
                    System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                    //Permute every combination for a nybble
                    for (int ReportBlockCounter = byte.MinValue; ReportBlockCounter <= Media.Common.Binary.FiveBitMaxValue; ++ReportBlockCounter)
                    {
                        ///////////////Set the CC nibble in the first Octet
                        p.BlockCount = (byte)ReportBlockCounter;
                        /////////////

                        //Identify the Contributing Source Counter and the Packet's value
                        //Console.Write(string.Format(TestingFormat, "\tReportBlockCounter", ReportBlockCounter));
                        //Console.Write(string.Format(TestingFormat, " BlockCount", p.BlockCount + "\r\n"));

                        //Check the BlockCount
                        System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, reportBlockException.Message);

                        //Ensure the Version after modification
                        System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                        //Check the Padding after modification
                        System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                        ///////////////Serialize the packet
                        using (p = new Media.Rtcp.RtcpPacket(p.Prepare().ToArray(), 0))
                        {
                            /////////////

                            //Ensure the version remains after modification
                            System.Diagnostics.Debug.Assert(p.Version == VersionCounter, versionException.Message);

                            //Ensure the Padding bit after modification
                            System.Diagnostics.Debug.Assert(p.Padding == bitValue, paddingException.Message);

                            //Check the BlockCount after modification
                            System.Diagnostics.Debug.Assert(p.BlockCount == ReportBlockCounter, reportBlockException.Message);

                            //Check for a valid header
                            if (false == p.Header.IsValid(VersionCounter, PayloadCounter, bitValue)
                                || //Check for validation per RFC3550 A.1 when the test permits
                                false == bitValue &&
                                VersionCounter > 1 &&
                                PayloadCounter >= 200 &&
                                PayloadCounter <= 201 &&
                                false == Media.RFC3550.IsValidRtcpHeader(p.Header, VersionCounter))
                            {
                                throw inValidHeaderException;
                            }
                        }

                        //Perform checks with length in words set incorrectly
                    }
                }
            }

            //Console.WriteLine(string.Format(TestingFormat, "\t*****Completed an iteration wih bitValue", bitValue + "*****"));
        }
    }
Пример #12
0
 public virtual void EnquePacket(RtcpPacket packet)
 {
     if (IsDisposed || m_StopRequested || packet == null || packet.IsDisposed) return;
     m_OutgoingRtcpPackets.Add(packet);
 }
Пример #13
0
        static void SendFeedback(object sender, RtcpPacket received)
        {
            //Determine if RtcpPacket should have a ResponseType hash... could be created in each Type statically like PayloadTypeByte is defined and mapped.
            //E.g ResponseTypeByte = "225"
            //Either that or a CreateResponse method which returns a RtcpPacket constructed from the given

            var impl = RtcpPacket.GetImplementationForPayloadType((byte)received.PayloadType);

            if (impl != null)
            {
                //Create packet(s) which is a response for the received
                IEnumerable<RtcpPacket> packets = null;
                if (packets != null)
                {
                    if (sender is RtpClient)
                    {
                        RtpClient c = sender as RtpClient;
                        //What is needed
                        c.SendRtcpPackets(packets);
                    }
                }
            }
        }
Пример #14
0
        /// <summary>
        /// Raises the RtcpPacketHandler for Sending
        /// </summary>
        /// <param name="packet">The packet to handle</param>
        internal void OnRtcpPacketSent(RtcpPacket packet)
        {
            if (IsDisposed) return;

            RtcpPacketSent(this, packet);
        }
Пример #15
0
        //Should ensure the semantic that all callers who observe this event are aware if the packet was already handled or not.
        /// <summary>
        /// Raises the RtcpPacketHandler for Recieving
        /// </summary>
        /// <param name="packet">The packet to handle</param>
        protected internal void OnRtcpPacketReceieved(RtcpPacket packet)
        {
            if (IsDisposed || false == IncomingRtcpPacketEventsEnabled) return;

            RtcpPacketHandler action = RtcpPacketReceieved;

            if (action == null) return;

            foreach (RtcpPacketHandler handler in action.GetInvocationList())
            {
                try { handler(this, packet); }
                catch { continue; }
            }
        }
Пример #16
0
        /// <summary>
        /// Handles the logic of updating counters for the packet sent if <see cref="OutgoingRtcpPacketEventsEnabled"/> is true.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="packet"></param>OutgoingRtcpPacketEventsEnabled
        protected internal virtual void HandleOutgoingRtcpPacket(object sender, RtcpPacket packet)
        {
            if (IsDisposed || packet == null || false == HandleOutgoingRtcpPackets || false == packet.Transferred.HasValue) return;

            TransportContext transportContext = GetContextForPacket(packet);

            //if there is no context there is nothing to do.
            if (transportContext == null) return;

            unchecked
            {
                //Update the counters for the amount of bytes in the RtcpPacket including the header and any padding.
                transportContext.RtcpBytesSent += packet.Length;

                //Update the amount of packets sent
                ++transportContext.RtcpPacketsSent;

                //Mark the context as active immediately.
                transportContext.m_InactiveTime = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan;

                //Get the time the packet was sent
                DateTime sent = packet.Transferred.Value;

                //Store the last time a RtcpPacket was sent
                transportContext.m_LastRtcpOut = sent;

                //Set the time the first packet was sent.
                if (transportContext.m_FirstPacketSent == DateTime.MinValue) transportContext.m_FirstPacketSent = sent;

                //if (Logger != null) Logger.Log("Rtcp Packet Sent");
            }

            //Backoff based on ConverganceTime?
        }
Пример #17
0
        /// <summary>
        /// Parses all RtcpPackets contained in the array using the given paramters.
        /// </summary>
        /// <param name="array">The array to parse packets from.</param>
        /// <param name="offset">The index to start parsing</param>
        /// <param name="count">The amount of bytes to use in parsing</param>
        /// <param name="version">The optional <see cref="RtcpPacket.Version"/>version of the packets</param>
        /// <param name="payloadType">The optional <see cref="RtcpPacket.PayloadType"/> of all packets</param>
        /// <param name="ssrc">The optional <see cref="RtcpPacket.SynchronizationSourceIdentifier"/> of all packets</param>
        /// <returns>A pointer to each packet found</returns>
        public static IEnumerable<RtcpPacket> GetPackets(byte[] array, int offset, int count, int version = 2, int? payloadType = null, int? ssrc = null)
        {
            //array.GetLowerBound(0) for VB, UpperBound(0) is then the index of the last element
            int lowerBound = 0, upperBound = array.Length;

            if (offset < lowerBound || offset > upperBound) throw new ArgumentOutOfRangeException("index", "Must refer to an accessible position in the given array");

            if (count <= lowerBound) yield break;

            if (count > upperBound) throw new ArgumentOutOfRangeException("count", "Must refer to an accessible position in the given array");

            //Would overflow the array
            if (count + offset > upperBound) throw new ArgumentOutOfRangeException("index", "Count must refer to an accessible position in the given array when deleniated by index");

            //While  a 32 bit value remains to be read in the vector
            while (offset + RtcpHeader.Length < upperBound && count >= RtcpHeader.Length)
            {
                //Get the header of the packet to verify if it is wanted or not
                using (var header = new RtcpHeader(new Common.MemorySegment(array, offset, count)))
                {
                    //Determine how long the header was
                    int payloadOffset = header.Size;

                    //Determine the amount of bytes in the packet NOT INCLUDING the RtcpHeader (Which may be 0)
                    int lengthInBytes = Binary.MachineWordsToBytes(header.LengthInWordsMinusOne + 1); //((lengthInWords + 1) * 4) - payloadOffset;

                    //Create a packet using the existing header and the bytes left in the packet
                    using (RtcpPacket newPacket = new RtcpPacket(header, new MemorySegment(array, offset + payloadOffset, Binary.Min(lengthInBytes - payloadOffset, count - payloadOffset))))
                    {
                        //Move the offset the length in bytes of the size of the last packet (including the header).
                        offset += newPacket.Length;

                        //Reduce the count
                        count -= newPacket.Length;

                        //Check for the optional parameters
                        if (payloadType.HasValue && payloadType.Value != header.PayloadType ||  // Check for the given payloadType if provided
                            ssrc.HasValue && ssrc.Value != header.SendersSynchronizationSourceIdentifier) //Check for the given ssrc if provided
                        {
                            //Skip the packet
                            continue;
                        }

                        //Yield the packet, disposed afterwards
                        yield return newPacket;
                    }
                }
            }

            //Done parsing
            yield break;
        }
Пример #18
0
        void m_RtpClient_RecievedRtcp(object sender, RtcpPacket packet)
        {
            if (packet == null || packet.IsDisposed) return;

            //Get an implementation for the packet recieved
            var implementation = Rtcp.RtcpPacket.GetImplementationForPayloadType((byte)packet.PayloadType);

            if (implementation == null) m_Server.Logger.LogException(new Exception("Recieved Unknown PacketType: " + packet.PayloadType + " Packet Ssrc = " + packet.SynchronizationSourceIdentifier));
            else m_Server.Logger.LogException(new Exception("Recieved Rtcp PacketType: " + packet.PayloadType + " - " + implementation.Name + " Packet Ssrc = " + packet.SynchronizationSourceIdentifier));

            var context = m_RtpClient.GetContextForPacket(packet);

            if (context == null) m_Server.Logger.LogException(new Exception("Unknown Rtcp Packet Ssrc = " + packet.SynchronizationSourceIdentifier));
            else m_Server.Logger.LogException(new Exception("Rtcp Packet Ssrc = " + packet.SynchronizationSourceIdentifier + " RemoteId = " + context.RemoteSynchronizationSourceIdentifier + " LocalId = " + context.SynchronizationSourceIdentifier));

            //Crash... check bugs in compiler.. emited wrong instruction...
            //m_Server.Logger.LogException(new Exception("Recieved PacketType: " + packet.PayloadType + " - " + implementation != null ? implementation.Name : string.Empty));
        }
Пример #19
0
    public void TestRtcpPacketExamples()
    {
        Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

        byte[] output;

        //Keep a copy of these exceptions to throw in case some error occurs.
        Exception invalidLength = new Exception("Invalid Length"), invalidData = new Exception("Invalid Data in packet"), invalidPadding = new Exception("Invalid Padding"), incompleteFalse = new Exception("Packet IsComplete is false");

        //Create a Media.RtcpPacket with only a header (results in 8 octets of 0x00 which make up the header)
        Media.Rtcp.RtcpPacket rtcpPacket = new Media.Rtcp.RtcpPacket(0, 0, 0, 0, 0, 0);

        //Prepare a sequence which contains the data in the packet including the header
        IEnumerable<byte> preparedPacket = rtcpPacket.Prepare();

        //Check for an invlaid length
        if (rtcpPacket.Payload.Count > 0 || rtcpPacket.Header.LengthInWordsMinusOne != 0 && rtcpPacket.Length != Media.Rtcp.RtcpHeader.Length || preparedPacket.Count() != Media.Rtcp.RtcpHeader.Length) throw invalidLength;

        //Check for any data in the packet binary
        if (preparedPacket.Any(o => o != default(byte))) throw invalidData;

        //Set padding in the header
        rtcpPacket.Padding = true;

        //Check for some invalid valid
        if (rtcpPacket.PaddingOctets > 0) throw invalidPadding;

        //Ensure the packet is complete
        if (rtcpPacket.IsComplete == false) throw incompleteFalse;

        //Add nothing to the payload
        rtcpPacket.AddBytesToPayload(Media.RFC3550.CreatePadding(0), 0, 0);

        //Ensure the packet is complete
        if (rtcpPacket.IsComplete == false) throw incompleteFalse;

        //Check for some invalid value
        if (rtcpPacket.PaddingOctets > 0) throw invalidPadding;

        //Make a bunch of packets with padding
        for (int paddingAmount = 1, e = byte.MaxValue; paddingAmount <= e; ++paddingAmount)
        {

            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Making Media.RtcpPacket with Padding", paddingAmount));

            //Try to make a padded packet with the given amount
            rtcpPacket = new Media.Rtcp.RtcpPacket(0, 0, paddingAmount, 0, 0, 0);

            //A a 4 bytes which are not padding related
            rtcpPacket.AddBytesToPayload(Enumerable.Repeat(default(byte), 4));

            //Check ReadPadding works after adding bytes to the payload
            if (rtcpPacket.PaddingOctets != paddingAmount) throw invalidPadding;

            //Ensure the packet is complete
            if (rtcpPacket.IsComplete == false) throw incompleteFalse;

            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Packet Length", rtcpPacket.Length));

            //Write information for the test to the console
            Console.WriteLine(string.Format(TestingFormat, "Packet Padding", rtcpPacket.PaddingOctets));
        }

        //Create a new SendersReport with no blocks
        using (Media.Rtcp.RtcpReport testReport = new Media.Rtcp.SendersReport(2, 0, 7))
        {
            //The Media.RtcpData property contains all data which in the Media.RtcpPacket without padding
            if (testReport.RtcpData.Count() != 20 && testReport.Length != 20) throw invalidLength;

            output = testReport.Prepare().ToArray();//should be exactly equal to example
        }

        //Example of a Senders Report
        byte[] example = new byte[]
                         {
                            0x81,0xc8,0x00,0x0c,0xa3,0x36,0x84,0x36,0xd4,0xa6,0xaf,0x65,0x00,0x00,0x00,0x0,
                            0xcb,0xf9,0x44,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xa3,0x36,0x84,0x36,
                            0x00,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                            0x00,0x00,0x00,0x00
                         };

        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0);
        if (rtcpPacket.Length != example.Length) throw new Exception("Invalid Length.");

        //Make a SendersReport to access the SendersInformation and ReportBlocks, do not dispose the packet when done with the report
        using (Media.Rtcp.SendersReport sr = new Media.Rtcp.SendersReport(rtcpPacket, false))
        {
            //Check the invalid block count
            if (sr.BlockCount != 1) throw new Exception("Invalid Block Count!");
            else Console.WriteLine(sr.BlockCount);//16, should be 1

            if ((uint)sr.SynchronizationSourceIdentifier != (uint)2738258998) throw new Exception("Invalid Senders SSRC!");
            else Console.WriteLine(sr.SynchronizationSourceIdentifier);//0xa3368436

            //Ensure setting the value through a setter is correct
            sr.NtpTimestamp = sr.NtpTimestamp;//14697854519044210688
            if ((ulong)sr.NtpTimestamp != 3567693669) throw new Exception("Invalid NtpTimestamp!");
            else Console.WriteLine(sr.NtpTimestamp);

            //Timestamp
            if ((uint)sr.RtpTimestamp != 3422110928) throw new Exception("Invalid RtpTimestamp!");
            else Console.WriteLine(sr.RtpTimestamp);//0

            //Data in report (Should only be 1)
            foreach (Media.Rtcp.IReportBlock rb in sr)
            {
                //if ((uint)rb.BlockIdentifier != 2738258998) throw new Exception("Invalid BlockIdentifier");
                //else if (rb is Media.Rtcp.ReportBlock)
                //{
                Media.Rtcp.ReportBlock asReportBlock = (Media.Rtcp.ReportBlock)rb;

                if (rb.BlockIdentifier != asReportBlock.SendersSynchronizationSourceIdentifier) throw new Exception("Invalid SendersSynchronizationSourceIdentifier");

                Console.WriteLine(asReportBlock.SendersSynchronizationSourceIdentifier);//0
                Console.WriteLine(asReportBlock.FractionsLost);//0
                Console.WriteLine(asReportBlock.CumulativePacketsLost);//0
                Console.WriteLine(asReportBlock.ExtendedHighestSequenceNumberReceived);//0
                Console.WriteLine(asReportBlock.InterarrivalJitterEstimate);//0
                Console.WriteLine(asReportBlock.LastSendersReportTimestamp);//0
                //}

            }

            //Check the length to be exactly the same as the example
            if (sr.Length != example.Length) throw new Exception("Invalid Length");

            //Verify SendersReport byte for byte
            output = sr.Prepare().ToArray();//should be exactly equal to example
            for (int i = 0, e = example.Length; i < e; ++i) if (example[i] != output[i]) throw new Exception("Result Packet Does Not Match Example");
        }

        if (rtcpPacket.Header.IsDisposed || rtcpPacket.IsDisposed) throw new Exception("Disposed the Media.RtcpPacket");

        //Now the packet can be disposed
        rtcpPacket.Dispose();
        rtcpPacket = null;

        rtcpPacket = new Media.Rtcp.SendersReport(2, 0, 7);

        example = rtcpPacket.Prepare().ToArray();

        if (rtcpPacket.SynchronizationSourceIdentifier != 7) throw new Exception("Unexpected SynchronizationSourceIdentifier");

        if (rtcpPacket.BlockCount != 0) throw new Exception("Unexpected BlockCount");

        //Check the Length, 8 Byte Header, 20 Byte SendersInformation
        if (rtcpPacket.Length != Media.Rtcp.RtcpHeader.Length + Media.Rtcp.ReportBlock.ReportBlockSize) throw new Exception("Unexpected BlockCount");

        //Iterate each IReportBlock in the RtcpReport representation of the rtcpPacket instance
        foreach (Media.Rtcp.IReportBlock rb in rtcpPacket as Media.Rtcp.RtcpReport)
        {
            Console.WriteLine(rb);

            throw new Exception("Unexpected BlockCount");
        }

        //Next Sub Test

        //Create a GoodbyeReport with no SourceList, e.g. a BlockCount of 0.
        //There should be 8 bytes, 4 for the RtcpHeader and 4 for the SynchronizationSourceIdentifier
        //The LengthInWordsMinusOne should equal 1 (1 + 1 = 2, 2 * 4 = 8)
        using (var testReport = new Media.Rtcp.GoodbyeReport(2, 7))
        {
            output = testReport.Prepare().ToArray();

            if (output.Length != testReport.Length || testReport.Header.LengthInWordsMinusOne != 1 || testReport.Length != 8) throw new Exception("Invalid Length");

            if (testReport.BlockCount != 0) throw reportBlockException;

            if (output[7] != 7 || testReport.SynchronizationSourceIdentifier != 7) throw new Exception("Invalid ssrc");
        }

        //Add a Reason For Leaving

        //Should now have 4 words... Header, SSRC, Block, Reason
        using (var testReport = new Media.Rtcp.GoodbyeReport(2, 7, System.Text.Encoding.ASCII.GetBytes("v")))
        {
            output = testReport.Prepare().ToArray();

            //3
            if (output.Length != testReport.Length || testReport.Header.LengthInWordsMinusOne != 2 || testReport.Length != 12) throw new Exception("Invalid Length");

            if (testReport.BlockCount != 0) throw reportBlockException;

            if (output[7] != 7 || testReport.SynchronizationSourceIdentifier != 7) throw new Exception("Invalid ssrc");

            if (false == testReport.HasReasonForLeaving) throw new Exception("Has no reason for leaving.");

            if (System.Text.Encoding.ASCII.GetString(testReport.ReasonForLeaving.ToArray()) != "v") throw new Exception("Does not have expected reason for leaving.");
        }

        //Next Sub Test
        /////

        //Recievers Report and Source Description
        example = new byte[] {   0x81,0xc9,0x00,0x07,
                                   0x69,0xf2,0x79,0x50,
                                   0x61,0x37,0x94,0x50,
                                   0xff,0xff,0xff,0xff,
                                   0x00,0x01,0x00,0x52,
                                   0x00,0x00,0x0e,0xbb,
                                   0xce,0xd4,0xc8,0xf5,
                                   0x00,0x00,0x84,0x28,

                                   0x81,0xca,0x00,0x04,
                                   0x69,0xf2,0x79,0x50,
                                   0x01,0x06,0x4a,0x61,
                                   0x79,0x2d,0x50,0x43,
                                   0x00,0x00,0x00,0x00
            };

        int foundPackets = 0, foundSize = 0;

        foreach (Media.Rtcp.RtcpPacket packet in Media.Rtcp.RtcpPacket.GetPackets(example, 0, example.Length))
        {
            ++foundPackets;

            foundSize += packet.Length;
        }

        if(foundPackets != 2) throw new Exception("Unexpected amount of packets found");

        if (foundSize != example.Length) throw new Exception("Unexpected total length of packets found");

        //Or manually for some reason
        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0); // The same as foundPackets[0]
        using (Media.Rtcp.ReceiversReport rr = new Media.Rtcp.ReceiversReport(rtcpPacket, false))
        {
            Console.WriteLine(rr.SynchronizationSourceIdentifier);//1777498448

            //Check the invalid block count
            if (rr.BlockCount != 1) throw new Exception("Invalid Block Count!");
            else Console.WriteLine(rr.BlockCount);//16, should be 1

            using (var enumerator = rr.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    Console.WriteLine("Current IReportBlock Identifier: " + enumerator.Current.BlockIdentifier);//1631032400

                    //If the instance boxed in the Interface is a ReportBlock
                    if (enumerator.Current is Media.Rtcp.ReportBlock)
                    {
                        //Unbox the Interface as it's ReportBlock Instance
                        Media.Rtcp.ReportBlock asReportBlock = enumerator.Current as Media.Rtcp.ReportBlock;

                        Console.WriteLine("Found a ReportBlock");

                        //Print the instance information
                        Console.WriteLine("FractionsLost: " + asReportBlock.FractionsLost);//255/256 0xff
                        Console.WriteLine("CumulativePacketsLost: " + asReportBlock.CumulativePacketsLost);//-1, 0xff,0xff,0xff
                        Console.WriteLine("ExtendedHighestSequenceNumberReceived: " + asReportBlock.ExtendedHighestSequenceNumberReceived);//65618, 00, 01, 00, 52
                        Console.WriteLine("InterarrivalJitterEstimate: " + asReportBlock.InterarrivalJitterEstimate);//3771
                        Console.WriteLine("LastSendersReportTimestamp: " + asReportBlock.LastSendersReportTimestamp);//3470000128
                    }
                    else //Not a ReportBlock
                    {
                        Console.WriteLine("Current IReportBlock TypeName: " + enumerator.Current.GetType().Name);
                        Console.WriteLine("Current IReportBlock Data: " + BitConverter.ToString(enumerator.Current.BlockData.ToArray()));
                    }
                }
            }

            //Verify RecieversReport byte for byte
            output = rr.Prepare().ToArray();//should be exactly equal to example's bytes when extension data is contained in the packet instance

            //Use to get the raw data in the packet including the header
            //.Take(rr.Length - rr.ExtensionDataOctets)

            //Or rr.ReportData which omits the header...

            //What other variations are relvent? Please submit examples, even invalid ones will be tolerated!

            //The bytes given here should reflect exactly the bytes in the example array because of how the data is formatted.

            //Yes the data is compound but there is an invalid LengthInWords in the packet which must be exactly copied when deserialized
            if (rr.HasExtensionData && false == output.SequenceEqual(example)) throw new Exception("Result Packet Does Not Match Example");
            else output = rr.Prepare(true, true, false, true).ToArray();
            for (int i = 0, e = output.Length; i < e; ++i) if (example[i] != output[i]) throw new Exception("Result Packet Does Not Match Example @" + i);

        }

        if (rtcpPacket.Header.IsDisposed || rtcpPacket.IsDisposed) throw new Exception("Disposed the Media.RtcpPacket");

        //Now the packet can be disposed
        rtcpPacket.Dispose();
        rtcpPacket = null;

        //Make another packet instance from the rest of the example data.
        rtcpPacket = new Media.Rtcp.RtcpPacket(example, output.Length);

        //Create a SourceDescriptionReport from the packet instance to access the SourceDescriptionChunks
        using (Media.Rtcp.SourceDescriptionReport sourceDescription = new Media.Rtcp.SourceDescriptionReport(rtcpPacket, false))
        {
            if (false == sourceDescription.HasCName) throw new Exception("Unexpected HasCName");

            if (sourceDescription.BlockCount != 1) throw new Exception("Unexpected BlockCount");

            if (sourceDescription.Chunks.First().ChunkIdentifer != 1777498448) throw new Exception("Chunks.ChunkIdentifer");

            if (false == sourceDescription.Chunks.First().HasItems) throw new Exception("Chunks.HasItems");

            if (sourceDescription.Chunks.First().Items.First().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName) throw new Exception("Unexpected ItemType");

            if (sourceDescription.Chunks.First().Items.First().ItemLength != 6) throw new Exception("Unexpected ItemLength");

            foreach (Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk chunk in sourceDescription.GetChunkIterator())
            {
                if (chunk.ChunkIdentifer != 1777498448) throw new Exception("Chunks.ChunkIdentifer");

                Console.WriteLine(string.Format(TestingFormat, "Chunk Identifier", chunk.ChunkIdentifer));

                //Use a SourceDescriptionItemList to access the items within the Chunk
                //This is performed auto magically when using the foreach pattern
                foreach (Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem item in chunk /*.AsEnumerable<Rtcp.SourceDescriptionItem>()*/)
                {
                    //if (item.ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName) throw new Exception("Unexpected ItemType");

                    //if (item.ItemLength != 6) throw new Exception("Unexpected ItemLength");

                    Console.WriteLine(string.Format(TestingFormat, "Item Type", item.ItemType));

                    Console.WriteLine(string.Format(TestingFormat, "Item Length", item.ItemLength));

                    Console.WriteLine(string.Format(TestingFormat, "Item Data", BitConverter.ToString(item.ItemData.ToArray())));
                }
            }

            //Verify SourceDescriptionReport byte for byte
            output = sourceDescription.Prepare().ToArray();//should be exactly equal to example
            for (int i = output.Length, e = sourceDescription.Length; i < e; ++i) if (example[i] != output[i]) throw new Exception("Result Packet Does Not Match Example");

        }

        //ApplicationSpecific - qtsi

        example = new byte[] { 0x81, 0xcc, 0x00, 0x06, 0x4e, 0xc8, 0x79, 0x50, 0x71, 0x74, 0x73, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 0x74, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14 };

        rtcpPacket = new Media.Rtcp.RtcpPacket(example, 0);

        //Make a ApplicationSpecificReport instance
        Media.Rtcp.ApplicationSpecificReport app = new Media.Rtcp.ApplicationSpecificReport(rtcpPacket);

        //Check the name to be equal to qtsi
        if (false == app.Name.SequenceEqual(System.Text.Encoding.UTF8.GetBytes("qtsi"))) throw new Exception("Invalid App Packet Type");

        //Check the length
        if (rtcpPacket.Length != example.Length) throw new Exception("Invalid Legnth");

        //Verify ApplicationSpecificReport byte for byte
        output = rtcpPacket.Prepare().ToArray();//should be exactly equal to example
        for (int i = 0, e = example.Length; i < e; ++i) if (example[i] != output[i]) throw new Exception("Result Packet Does Not Match Example");

        //Test making a packet with a known length in bytes
        Media.Rtcp.SourceDescriptionReport sd = new Media.Rtcp.SourceDescriptionReport(2);
        byte[] sdOut = sd.Prepare().ToArray();

        //1 word when the ssrc is present but would be an invalid sdes because blockCount = 0
        if (false == sd.IsComplete || sd.Length != Media.Rtcp.RtcpHeader.Length || sd.Header.LengthInWordsMinusOne != ushort.MaxValue) throw new Exception("Invalid Length");

        //Create 9 bytes of data to add to the existing SourceDescriptionReport
        byte[] itemData = System.Text.Encoding.UTF8.GetBytes("FLABIA-PC");

        int KnownId = 0x1AB7C080;

        //Point the rtcpPacket at the SourceDescription instance
        rtcpPacket = sd;

        //Create a Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk containing a Known Identifier
        //Which Contains a Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem with the Type 'CName' containing the itemData
        //Add the Media.Rtcp.IReportBlock to the RtcpReport
        sd.Add((Media.Rtcp.IReportBlock)new Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk(KnownId,
            new Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName,
                itemData.Length, itemData, 0))); // ItemType(End) = 1, ItemLength(9) = 1, ItemData(9) = 11 Bytes in the Item, ChunkIdentifier(0x1AB7C080) = 4, 15 total bytes

        //Add an unpadded item for a 19 byte packet.
        //sd.Add(new Media.Rtcp.SourceDescriptionReport.SourceDescriptionChunk(KnownId,
        //    new Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName,
        //        itemData.Length, itemData, 0)), false); // ItemType(End) = 1, ItemLength(9) = 1, ItemData(9) = 11 Bytes in the Item, ChunkIdentifier(0x1AB7C080) = 4, 15 total bytes

        //Ensure the data is present where it is supposed to be, more data may be present to respect octet alignment
        if (false == sd.RtcpData.Skip(Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.ItemHeaderSize).Take(itemData.Length).SequenceEqual(itemData)) throw new Exception("Invalid ItemData");

        if (false == sd.Chunks.First().HasItems) throw new Exception("Unexpected HasItems");

        if (sd.Chunks.First().ChunkIdentifer != KnownId) throw new Exception("Unexpected Chunks.ChunkIdentifer");

        if (sd.Chunks.First().Items.Count() != 2) throw new Exception("Unexpected Chunks.Items.Count");

        if (sd.Chunks.First().Items.First().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.CName) throw new Exception("Unexpected Items.ItemType");

        if (sd.Chunks.First().Items.First().ItemLength != 9) throw new Exception("Unexpected Chunks.Items.ItemLength");

        if (false == sd.Chunks.First().Items.First().ItemData.SequenceEqual(itemData)) throw new Exception("Unexpected Chunks.Items.ItemData");

        if (sd.Chunks.First().Items.Last().ItemType != Media.Rtcp.SourceDescriptionReport.SourceDescriptionItem.SourceDescriptionItemType.End) throw new Exception("Unexpected Items.ItemType");

        if (sd.Chunks.First().Items.Last().ItemLength != 1) throw new Exception("Unexpected Chunks.Items.ItemLength");

        if (false == sd.Chunks.First().Items.Last().ItemData.All(b => b == 0)) throw new Exception("Unexpected Chunks.Items.ItemData");

        //
        // Header = 4 Bytes, 1 Word
        // There is a SSRC which occupies 1 Word
        //in a SourceDescription, The First Chunk is `Overlapped` in the header and the BlockIdentifier is shared with the SSRC

        //Ensure the data is present where it is supposed to be
        if (sd.SynchronizationSourceIdentifier != KnownId) throw new Exception("Invalid SynchronizationSourceIdentifier");

        //asPacket now contains 11 octets in the payload.
        //asPacket now has 1 block (1 chunk of 15 bytes)
        //asPacket is 19 octets long, 11 octets in the payload and 8 octets in the header
        //asPacket would have a LengthInWordsMinusOne of 3 because 19 / 4 = 4 - 1 = 3
        //But null octets are added (Per RFC3550 @ Page 45 [Paragraph 2] / http://tools.ietf.org/html/rfc3550#appendix-A.4)
        //19 + 1 = 20, 20 / 4 = 5, 5 - 1 = 4.
        if (false == rtcpPacket.IsComplete || rtcpPacket.Length != 20 || rtcpPacket.Header.LengthInWordsMinusOne != 4) throw new Exception("Invalid Length");
    }
Пример #20
0
        void m_RtpClient_SentRtcp(object sender, RtcpPacket packet)
        {
            if (packet == null || packet.IsDisposed) return;

            //Get an implementation for the packet recieved
            var implementation = Rtcp.RtcpPacket.GetImplementationForPayloadType((byte)packet.PayloadType);

            if (implementation == null) m_Server.Logger.LogException(new Exception("Sent Unknown PacketType: " + packet.PayloadType + " Packet Ssrc = " + packet.SynchronizationSourceIdentifier));
            else m_Server.Logger.LogException(new Exception("Sent PacketType: " + packet.PayloadType + " - " + implementation.Name + " Packet Ssrc = " + packet.SynchronizationSourceIdentifier));

            //If the context should have been synchronized then determine if a context can be found
            if (m_RtpClient.Uptime > RtpClient.DefaultReportInterval)
            {
                var context = m_RtpClient.GetContextForPacket(packet);

                if (context == null) m_Server.Logger.LogException(new Exception("Sent Unknown Packet Ssrc = " + packet.SynchronizationSourceIdentifier));
                else m_Server.Logger.LogException(new Exception("Sent Packet Ssrc = " + packet.SynchronizationSourceIdentifier + " RemoteId = " + context.RemoteSynchronizationSourceIdentifier + " LocalId = " + context.SynchronizationSourceIdentifier));
            }

            //Crash... check bugs in compiler.. emited wrong instruction...
            //m_Server.Logger.LogException(new Exception("Recieved PacketType: " + packet.PayloadType + " - " + implementation != null ? implementation.Name : string.Empty));
        }
Пример #21
0
 public SendersReport(RtcpPacket reference, bool shouldDispose)
     : base(reference.Header, reference.Payload, shouldDispose)
 {
     if (Header.PayloadType != PayloadType) throw new ArgumentException("Header.PayloadType is not equal to the expected type of 200.", "reference");
 }
Пример #22
0
 /// <summary>
 /// Selects a TransportContext by using the packet's Channel property
 /// </summary>
 /// <param name="packet"></param>
 /// <returns></returns>
 protected internal virtual TransportContext GetContextForPacket(RtcpPacket packet)
 {
     if (IsDisposed) return null;
     //Determine based on reading the packet this is where a RtcpReport class would be useful to allow reading the Ssrc without knownin the details about the type of report
     try { return GetContextBySourceId(packet.SynchronizationSourceIdentifier); }
     catch (InvalidOperationException) { return GetContextForPacket(packet); }
     catch { if (false == IsDisposed) throw; }
     return null;
 }