/// <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 }
/// <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; }