/// <summary>
        /// Encode the packets that are in the data BufferChunk array and place the
        /// encoded result over GF16 in the checksum packets that are in
        /// the result BufferChunk array. This method encode the size on the first 2
        /// bytes of the checksum packet and the data after that. Every checksum BufferChunk
        /// has the same size, which is the size of the largest BufferChunk in the data BufferChunk
        /// array, plus one if this BufferChunk doesn't end on a 16 bits boundary, plus
        /// two to store the encoded length
        /// </summary>
        /// <param name="data">The data BufferChunk array (left part of the multiplication)</param>
        /// <param name="result"></param>
        /// <param name="encode">The encoding Vandermonde GF16 matrix (right part of the multiplication)</param>
        public static void EncodeRS(BufferChunk[] data, BufferChunk[] checksum, UInt16[,] encode)
        {
            // Get the length in byte of largest BufferChunk in the data BufferChunk array
            // (which is an array of BufferChunk that could have different sizes)
            int maxDataLength = GetMaxLength(data);

            // The checksum packet as a even number of bytes so we can stay with GF16
            // and not have to use GF8 for the last byte when length odd
            int checksumNbRowsByte = maxDataLength;

            if ((maxDataLength & 1) == 1)
            {
                checksumNbRowsByte += 1;
            }

            // Add 2 more bytes to store the length
            checksumNbRowsByte += CFec.SIZE_OVERHEAD;

            // Encode the length of the data and place that on the first 2 bytes
            // of the checksum BufferChunk
            EncodeRSLength(data, checksum, encode);

            // Start to put encoded data at index + 2, because the 2 first bytes
            // of the checksum already contain the encoded size
            for (int i = 0; i < checksum.Length; i++)
            {
                BufferChunk bc = checksum[i];
                bc.Reset(bc.Index + CFec.SIZE_OVERHEAD, checksumNbRowsByte - CFec.SIZE_OVERHEAD);

                // Reset all of the checksum packets so they have no data
                // TODO - Temporary workaround for column based approach - JVE 7/6/2004
                bc.Clear();
            }

            // Place all the packets in a 16bits boundary to avoid byte operation
            // Important: This suppose that the memory reserved for all the bc passed in param
            // to this method ends on a 16bits boundary (so at least one addition availabe byte
            // when the last item is on a even address)
            Queue paddedData = new Queue();

            AddPadding(data, 0, paddedData);

            // Encode the data and place the encoded information in the checksum BufferChunk
            EncodeRSData(data, checksum, encode, maxDataLength);

            // Earlier, we changed the boundary of all data BufferChunk
            // that was not ending on a 16 bits boundary in order to have
            // better performance during the encoding. Now we need to
            // restore the data to their original state to be clean.
            RemovePadding(paddedData);

            // Restore the BufferChunk index in the checksum checksum BufferChunk
            // to 0 to be clean. Earlier, We changed the index in order to encode
            // the data after the encoding length.
            for (int i = 0; i < checksum.Length; i++)
            {
                BufferChunk bc = checksum[i];
                bc.Reset(bc.Index - CFec.SIZE_OVERHEAD, checksumNbRowsByte);
            }
        }
        /// <summary>
        /// Decode to retrieve the length of the missing data packet(s) (null entries) in the
        /// data BufferChunk array and set the length of the packets in the recovery BufferChunk array.
        /// We have to do that because every data packet could have a different length.
        /// </summary>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to set the length of the recovered packets</param>
        private static void DecodeRSLength(BufferChunk[] data, int checksum, GF16[,] decode, BufferChunk[] recovery)
        {
            // Important! For now I assume the following in the firstMatrix:
            // - The array is always of size data + checksum
            // - #checksum not null entries = #packet lost
            // - recovery.length is exactly the number of data packet lost
            // - checksum param is # checksum packets that were used to encode


            // Get the length once to avoid having the overhead of getting it again inside a inner (performance)
            // Note that this value is also used outside of this method, so we could have had a parameter too,
            // but I prefer to get it here to avoid inconsitencies
            int dataLength = data.Length;

            int recoveryColumn = 0;

            for (int dataColumn = 0; dataColumn < dataLength - checksum; dataColumn++)
            {
                // recover only the missing column
                if (data[dataColumn] == null)
                {
                    // Inverted matrix row index
                    int imRowIndex = 0;

                    // length of the recovered buffer
                    UInt16 currentLength = 0;

                    for (int dataIndex = 0; dataIndex < dataLength; dataIndex++)
                    {
                        // Perform the core operation mult with add in GF16
                        // TODO: We could crunch the column so we avoid this test
                        if (data[dataIndex] != null)
                        {
                            UInt16 length = 0;

                            if (dataIndex < dataLength - checksum) // For the data part, we get the length of the BufferChunk
                            {
                                length = (UInt16)data[dataIndex].Length;
                            }
                            else // For the checksum part, the encoded length is inside the first 2 bytes
                            {
                                length = data[dataIndex].GetUInt16(0);
                            }

                            UInt16 currentValue = GF16.Multiply(length, decode[dataColumn, imRowIndex].Value);
                            currentLength = GF16.Add(currentLength, currentValue);

                            imRowIndex++;
                        } // if
                    }     // for column (do elementary operations)

                    BufferChunk bc = recovery[recoveryColumn];
                    bc.Reset(bc.Index, currentLength);

                    // Reset all of the recovery packets so they have no data
                    // TODO - Temporary workaround for column based approach - JVE 7/6/2004
                    bc.Clear();

                    recoveryColumn++;
                } // if
            }     // for dataColumn
        }
Example #3
0
        /// <summary>
        /// Called by RtcpSender when it is time to collect Rtcp data
        /// </summary>
        /// <returns>A CompoundPacketBuilder</returns>
        CompoundPacketBuilder RtcpSender.IRtpSession.RtcpReportIntervalReached()
        {
            // Limit the frequency of the cleanup in order to avoid participants and streams being
            // inappropriately designated as stale.  This can happen if we start up a lot of senders at once.
            // Each sender triggers an RTCP packet which without the frequency limitation could 
            // increment the stale counter to its limit and cause the participant or stream to be removed.
            bool cleanUpNow = false;
            if (DateTime.Now > this.lastCleanUp.AddSeconds(5)) {
                cleanUpNow = true;
                this.lastCleanUp = DateTime.Now;
            }

            // A stale participant is one who is not sending Rtcp data
            if (cleanUpNow)
                CheckForStaleParticipants();

            // Add participant data
            Debug.Assert(participant.SSRC != 0);
            cpb.ParticipantData(participant);

            // Add Rtp data
            if(rtpTraffic)
            {
                // A stale stream is one not sending Rtp traffic
                if (cleanUpNow)
                    CheckForStaleStreams();

                // Collect SenderReportPackets and SDESReports from each Sender
                lock(rtpSenders)
                {

                    foreach(RtpSender sender in rtpSenders.Values)
                    {
                        // this adds the SR and SDES packets
                        sender.UpdateRtcpData();
                    }
                }

                // add a sdes (source description)
                //cpb.Add_SDESReport(participant.SSRC, participant);

                // Collect ReceiverReports from each of the streams
                lock(streamsAndIPs)
                {
                    foreach(IPStreamPairHashtable.IPStreamPair ipsp in streamsAndIPs.Values)
                    {
                        if( ipsp.stream != null )
                        {
                            ipsp.stream.AddReceiverReport(cpb);
                        }
                    }
                }
            }



#if USE_APP_PACKET
            // andrew: Add an app packet that reflects the time and the current venue...
            if ((VenueName != null) && (this.groupEP != null))
            {
                string appPayload = VenueName + "#" + this.groupEP.Address.ToString();
                long time = DateTime.Now.Ticks;
                // 8  bytes for time + venue name
                int count = 8 + utf8.GetByteCount(appPayload);

                // count must be 4-byte alligned
                int mod = count % 4;
                if (mod != 0)
                {
                    count += (4 - mod);
                }

                BufferChunk buffer = new BufferChunk(count);
                buffer.Reset(0, count);
                buffer.Clear();

                buffer.SetInt64(0, time);
                buffer.SetUTF8String(8, appPayload);
                byte[] rawBytes = buffer.Buffer;

                cpb.Add_APPReport(participant.SSRC, Rtcp.APP_PACKET_NAME, Rtcp.VENUE_APP_PACKET_SUBTYPE, rawBytes);
            }
#endif
            return cpb;
        }