Example #1
0
        /// <summary>
        /// This method creates as many checksum buffers as exist in the checksum array
        /// </summary>
        /// <param name="bytes">Array of buffer chunk that represents the data to encode</param>
        /// <param name="checksum">Contains 1 BufferChunk, which should be Reset() before calling</param>
        /// <returns>The checksum packet</returns>
        public void Encode(BufferChunk[] data, BufferChunk[] checksum)
        {
            ValidateObjectNotNull(data);
            ValidateObjectNotNull(checksum);
            ValidateNullData(data, 0);
            ValidateNullData(checksum, 0);
            ValidateChecksumPacketCount(checksum.Length);

            // Sort the BufferChunks from longest to shortest
            ActiveColumns(data, data.Length);

            // Add 2 bytes so the checksum packet can contain the length of the missing packet
            int         xorDataLength = activeColumns[0].Length + 2;
            BufferChunk xorData       = checksum[0];
            int         index         = xorData.Index;

            // Store the xor'd lengths of the data
            xorData += (ushort)(XORLength() ^ xorDataLength);
            xorData.Reset(index + 2, xorDataLength - 2);

            // Populate the checksum buffer (xorData)
            XORData(xorData, xorDataLength - 2);

            // Set xorData.Index back to 0
            xorData.Reset(index, xorDataLength);
        }
Example #2
0
        /// <summary>
        /// Set the payload; this version allows the payload to occupy the space
        /// reserved for packet padding.  This is used by encryption protocols.
        /// </summary>
        /// <param name="chunk"></param>
        internal void SetPaddedPayload(BufferChunk chunk)
        {
            if (chunk.Length > (MaxPayloadSize + ReservedPaddingBytes))
            {
                throw new ArgumentOutOfRangeException(string.Format(CultureInfo.CurrentCulture,
                                                                    Strings.ValueMaximumPayload, chunk.Length, MaxPayloadSize));
            }

            // Reset Buffer to just after the header because packets are re-used and so that
            // operator+ works properly when copying the payload
            buffer.Reset(0, HeaderSize);
            buffer += chunk;
        }
Example #3
0
        internal virtual void Reset()
        {
            buffer.Reset(0, HeaderSize);

            // Initialize the first byte: V==2, P==0, X==0, CC==0
            buffer[VPXCC_INDEX] = (byte)(Rtp.VERSION << 6);
        }
        /// <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);
            }
        }
Example #5
0
        /// <summary>
        /// retrieve the next bufferchunk and timestamp for the stream.  Return false if there are no more.
        /// </summary>
        public bool GetNextFrame(out BufferChunk outFrame, out long timestamp)
        {
            outFrame  = null;
            timestamp = 0;
            bool ret = false;

            if (streamEndReached)
            {
                return(false);
            }

            if (currentIndex >= indexCount)
            {
                FillBuffer();
            }

            if (currentIndex < indexCount)
            {
                int frameLength = 1 + indices[currentIndex].end - indices[currentIndex].start;

                if (frameLength > 0)
                {
                    if (indices[currentIndex].timestamp < end)
                    {
                        frame.Reset(indices[currentIndex].start - this.minOffset, frameLength);
                        outFrame  = frame;
                        timestamp = indices[currentIndex].timestamp;
                        ret       = true;
                    }
                    else
                    {
                        streamEndReached = true;
                        ret = false;
                    }
                }
                else
                {
                    Debug.Fail("Frame of length zero found.");
                    ret = false;
                }

                ++currentIndex;
                return(ret);
            }
            else
            {
                return(false);
            }
        }
Example #6
0
        /// <summary>
        /// Decoder: This method takes an array of BufferChunk in parameter with the data
        /// and checksum received and returns the missing packets. Because it
        /// is based on XOR, it can recover only from one packet lost. This method
        /// is very similar than encoder, but differ in the validation part.
        /// </summary>
        /// <param name="bytes">Array of buffer chunk that represents the packets where
        /// you might have lost a packet</param>
        /// <param name="nbChecksumPackets">Should be always set to 1</param>
        /// <returns>The missing packet</returns>
        public void Decode(BufferChunk[] data, int nbChecksumPackets, BufferChunk[] recovery)
        {
            ValidateObjectNotNull(data);
            ValidateObjectNotNull(data[data.Length - 1]); // Checksum packet can't be null
            ValidateChecksumPacketCount(nbChecksumPackets);
            ValidateNullData(data, 1);

            // Sort the BufferChunks from longest to shortest
            ActiveColumns(data, data.Length - 1);

            int xorDataLength = XORLength() ^ data[data.Length - 1].NextUInt16();

            BufferChunk xorData = recovery[0];

            xorData.Reset(xorData.Index, xorDataLength);
            XORData(xorData, xorDataLength);
        }
        /// <summary>
        /// Decode to retrieve the missing data packet(s) (null entries) in the data BufferChunk array and place the
        /// decoded result over GF16 in order in the recovery BufferChunk array with the right length (every
        /// data packet can have a different length)
        /// </summary>
        /// <param name="checksum">The number of checksum packet(s) that were used to encode</param>
        /// <param name="data">The data BufferChunk array</param>
        /// <param name="decode">The decoding GF16 matrix</param>
        /// <param name="recovery">The recovery BufferChunk array to place the recovered result</param>
        public static void DecodeRS(int checksum, BufferChunk[] data, GF16[,] decode, BufferChunk[] recovery)
        {
            // TODO: Validation discussed with Jason:
            // Check in data array if: the #data lost = # checksum and if this number = recovery.length

            // Decode the length and set it to the recovery packets
            DecodeRSLength(data, checksum, decode, recovery);

            // ... and don't forget to adjust the index of the checksum BufferChunk because
            // we don't want to take in account the length anymore
            int dataLength = data.Length;

            for (int i = dataLength - checksum; i < dataLength; i++)
            {
                BufferChunk bc = data[i];

                if (bc != null)
                {
                    bc.Reset(bc.Index + CFec.SIZE_OVERHEAD, bc.Length - CFec.SIZE_OVERHEAD);
                }
            }

            // Adjust all of the data (and recovery) to a 16bit boundary to avoid byte operations
            // Important: This assumes the provided BufferChunks have room to grow to 16bit boundary
            Queue paddedBufferChunks = new Queue();

            AddPadding(data, checksum, paddedBufferChunks);
            AddPadding(recovery, 0, paddedBufferChunks);

            // Decode the data and set the result into the recovery packets
            DecodeRSData(data, checksum, decode, recovery);

            // Reset all the packets back to their initial length
            RemovePadding(paddedBufferChunks);

            // The caller is responsible for resetting all BufferChunks so there is no need
            // to reset the checksum packets
        }
Example #8
0
 /// <summary>
 /// This method is called to return buffers to the bufferPool
 /// </summary>
 /// <param name="buffer"></param>
 private void ReturnBuffer(BufferChunk buffer)
 {
     buffer.Reset();
     bufferPool.Push(buffer);
 }
Example #9
0
        /// <summary>
        /// Send all the frames that should be sent up to this point in time.
        /// </summary>
        /// <param name="bytesSent">Number of bytes sent.</param>
        /// <param name="cumulativeLateness">Sum of temporal disparities over all frames sent.</param>
        /// <param name="firstStoredTick">The 'start' time on the first index in this whole stream.</param>
        /// <param name="sender">The RtpSender to send data on.</param>
        /// <param name="startTimeTicks">The start time of sending, in ticks.</param>
        /// <param name="timeUntilFrame">The temporal distance between the current frame and the next one to be sent, in ticks.</param>
        /// <returns>Number of frames sent.</returns>
        public int SendFrames(RtpSender sender, long timeBoundary, out long timeUntilFrame, ref long totalBytesSent, ref long cumulativeLateness)
        {
            if (this.populating)
            {
                throw new InvalidOperationException(Strings.BufferplayerSendframesError);
            }

            int framesSent = 0;

            try
            {
                while (currentIndex < indexCount && indices[currentIndex].timestamp <= timeBoundary)
                {
                    long startTimer = DateTime.Now.Ticks;

                    int frameLength = 1 + indices[currentIndex].end - indices[currentIndex].start;

                    if (frameLength > 0)
                    {
                        // Calculate how late the frame will be
                        long lateness = (timeBoundary - indices[currentIndex].timestamp) / Constants.TicksPerMs;
                        if (lateness > Constants.TicksSpent)
                        {
                            Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
                                                          "--- FRAME LATENESS OF: {0} ms", lateness));
                        }
                        cumulativeLateness += lateness;

                        // Send Frame
                        buffer.Reset(indices[currentIndex].start - indices[0].start, frameLength);
                        sender.Send(buffer);
                    }
                    else
                    {
                        // (pbristow) Why would this happen???
                        Debug.Fail("Frame of length zero found.");
                    }

                    totalBytesSent += frameLength;
                    ++framesSent;
                    ++currentIndex;

                    long takenTime = DateTime.Now.Ticks - startTimer;
                    if (takenTime > Constants.TicksSpent)
                    {
                        Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
                                                      "TIME WASTED TO SEND A FRAME: {0} ms, ID: {1}, bytes: {2}",
                                                      (takenTime / Constants.TicksPerMs), streamID, frameLength));
                    }
                }

                if (currentIndex == indexCount)
                {
                    timeUntilFrame = 0;
                }
                else
                {
                    timeUntilFrame = (indices[currentIndex].timestamp - timeBoundary);
                }
            }
            catch (ObjectDisposedException)
            {
                // Sender is disposed by Stop being called
                timeUntilFrame = long.MaxValue;
            }

            return(framesSent);
        }
 /// <summary>
 /// This method is called to return buffers to the bufferPool
 /// </summary>
 /// <param name="buffer"></param>
 private void ReturnBuffer(BufferChunk buffer)
 {
     buffer.Reset();
     bufferPool.Push(buffer);
 }
        /// <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 #12
0
        override public void Run()
        {
            BufferChunk bc = new BufferChunk(7);
            short s = 25000;
            int i = 545434;
            byte b = 56;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
                throw new TestCaseException("Short failed for " + s);
            if (bc.NextInt32() != i)
                throw new TestCaseException("Int failed for " + i);
            if (bc.NextByte() != b)
                throw new TestCaseException("Byte failed for " + b);

            bc.Reset();

            s = short.MaxValue;
            i = int.MaxValue;
            b = byte.MaxValue;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
                throw new TestCaseException("Short failed for " + s);
            if (bc.NextInt32() != i)
                throw new TestCaseException("Int failed for " + i);
            if (bc.NextByte() != b)
                throw new TestCaseException("Byte failed for " + b);

            bc.Reset();

            s = short.MinValue;
            i = int.MinValue;
            b = byte.MinValue;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
                throw new TestCaseException("Short failed for " + s);
            if (bc.NextInt32() != i)
                throw new TestCaseException("Int failed for " + i);
            if (bc.NextByte() != b)
                throw new TestCaseException("Byte failed for " + b);

        }
Example #13
0
        /// <summary>
        /// This method is called to return buffers to the bufferPool
        /// </summary>
        /// <param name="buffer"></param>
        private void ReturnBuffer(BufferChunk buffer)
        {
            buffer.Reset();

            if (null != bufferPool)
            {
                bufferPool.Push(buffer);
            }
            else
            {
                Console.WriteLine("RtpListener.ReturnBuffer() - Buffer Pool is null"); 
            }
        }
Example #14
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;
        }
Example #15
0
        override public void Run()
        {
            BufferChunk bc = new BufferChunk(7);
            short       s  = 25000;
            int         i  = 545434;
            byte        b  = 56;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
            {
                throw new TestCaseException("Short failed for " + s);
            }
            if (bc.NextInt32() != i)
            {
                throw new TestCaseException("Int failed for " + i);
            }
            if (bc.NextByte() != b)
            {
                throw new TestCaseException("Byte failed for " + b);
            }

            bc.Reset();

            s = short.MaxValue;
            i = int.MaxValue;
            b = byte.MaxValue;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
            {
                throw new TestCaseException("Short failed for " + s);
            }
            if (bc.NextInt32() != i)
            {
                throw new TestCaseException("Int failed for " + i);
            }
            if (bc.NextByte() != b)
            {
                throw new TestCaseException("Byte failed for " + b);
            }

            bc.Reset();

            s = short.MinValue;
            i = int.MinValue;
            b = byte.MinValue;

            bc += s;
            bc += i;
            bc += b;

            if (bc.NextInt16() != s)
            {
                throw new TestCaseException("Short failed for " + s);
            }
            if (bc.NextInt32() != i)
            {
                throw new TestCaseException("Int failed for " + i);
            }
            if (bc.NextByte() != b)
            {
                throw new TestCaseException("Byte failed for " + b);
            }
        }