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");
    }
Example #2
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");
        }
    }