예제 #1
0
        public RTCPReportSampler(Guid rtpStreamId, uint syncSource, IPEndPoint remoteEndPoint, UInt16 startSequenceNumber, DateTime startTime, long bytesReceived)
        {
            m_rtpStreamId    = rtpStreamId;
            m_syncSource     = syncSource;
            m_remoteEndPoint = remoteEndPoint;

            m_windowStartSeqNum      = startSequenceNumber;
            m_windowLastSeqNum       = startSequenceNumber;
            m_windowSecondLastSeqNum = startSequenceNumber;

            m_lastSampleTime = DateTime.Now;

            logger.Debug("New RTCP report created for " + syncSource + " for stream from " + IPSocket.GetSocketString(remoteEndPoint) + ", start seq num=" + startSequenceNumber + ".");
            //resultsLogger.Info("StartTime,StartTimestamp,EndTime,EndTimestamp,Duration(ms),StartSeqNum,EndSeqNum,TotalPackets,TotalBytes,TransmissionRate(bps),Drops,Duplicates");

            RTPReceiveRecord measurement = new RTPReceiveRecord(startTime, startSequenceNumber, bytesReceived, 0, true, false);

            m_rcvdSeqNums.Add(startSequenceNumber, measurement);
        }
예제 #2
0
        /// <summary>
        /// All times passed into this method should already be UTC.
        /// </summary>
        private RTCPReport Sample(UInt16 sampleStartSequenceNumber, UInt16 sampleEndSequenceNumber, TimeSpan sampleDuration)
        {
            try
            {
                RTCPReport sample = new RTCPReport(m_rtpStreamId, m_syncSource, m_remoteEndPoint);
                sample.ReportNumber    = m_reportNumber;
                sample.SampleStartTime = DateTime.MinValue;
                //sample.SampleEndTime = DateTime.MinValue;
                sample.StartSequenceNumber = sampleStartSequenceNumber;
                sample.Duration            = Convert.ToUInt64(sampleDuration.TotalMilliseconds);

                double jitterTotal = 0;
                //double transitTotal = 0;

                int endSequence = (sampleEndSequenceNumber < sampleStartSequenceNumber) ? sampleEndSequenceNumber + UInt16.MaxValue + 1 : sampleEndSequenceNumber;

                // logger.Debug("Sampling range " + sampleStartSequenceNumber + " to " + endSequence);

                for (int index = sampleStartSequenceNumber; index <= endSequence; index++)
                {
                    UInt16 testSeqNum = (index > UInt16.MaxValue) ? Convert.ToUInt16((index % UInt16.MaxValue) - 1) : Convert.ToUInt16(index);
                    //logger.Debug("Sampling " + testSeqNum + ".");

                    if (m_rcvdSeqNums.ContainsKey(testSeqNum))
                    {
                        RTPReceiveRecord measurement = m_rcvdSeqNums[testSeqNum];

                        //sample.SampleEndTime = measurement.SendTime;

                        if (sample.SampleStartTime == DateTime.MinValue)
                        {
                            sample.SampleStartTime = measurement.ReceiveTime;
                        }

                        sample.SampleEndTime     = measurement.ReceiveTime;
                        sample.EndSequenceNumber = measurement.SequenceNumber;
                        sample.TotalPackets++;
                        sample.BytesReceived += (uint)measurement.RTPBytes;

                        if (measurement.Duplicates > 0)
                        {
                            logger.Debug("Duplicates for " + testSeqNum + " number " + measurement.Duplicates);
                            sample.Duplicates += (uint)measurement.Duplicates;
                        }

                        if (!measurement.InSequence)
                        {
                            logger.Debug("OutOfOrder for " + testSeqNum);
                            sample.OutOfOrder++;
                        }
                        else
                        {
                            // It is possible for the jitter to be negative as an average measurement is being used for the transit time and
                            // some transits could be slightly less than the average.
                            //double jitter = Math.Abs(measurement.ReceiveTime.Subtract(measurement.SendTime).TotalMilliseconds - averageTransitTime);

                            jitterTotal += measurement.Jitter;
                            //transitTotal += measurement.AverageTransit;

                            if (measurement.Jitter > sample.JitterMaximum)
                            {
                                //logger.Debug("Jitter max set to " + measurement.Jitter + " for sequence number " + measurement.SequenceNumber);
                                sample.JitterMaximum = (uint)measurement.Jitter;
                            }
                        }

                        if (measurement.JitterBufferDiscard)
                        {
                            logger.Debug("Jitter discard for " + testSeqNum);
                            sample.JitterDiscards++;
                        }

                        // Remove the measurement from the buffer.
                        // Remove the RTP measurements now that they have been sampled.
                        lock (m_rcvdSeqNums)
                        {
                            //logger.Debug("Removing " + index);
                            m_rcvdSeqNums.Remove(testSeqNum);
                        }
                    }
                    else
                    {
                        logger.Debug("Packet drop for " + index);
                        sample.PacketsLost++;
                    }
                }

                // Calculate the average jitter.
                if (sample.TotalPackets > 0)
                {
                    sample.JitterAverage = Convert.ToUInt32(jitterTotal / sample.TotalPackets);
                }

                // Calculate the average transit.
                //if (sample.TotalPackets > 0)
                //{
                //    sample.AverageTransitTime = Convert.ToUInt32(transitTotal / sample.TotalPackets);
                //}

                // Calculate the transmission rate.
                double packetRate = 0;
                if (sampleDuration.TotalMilliseconds > 0)
                {
                    sample.TransmissionRate = Convert.ToUInt32(sample.BytesReceived * 8 / sampleDuration.TotalSeconds);
                    packetRate = sample.TotalPackets / sampleDuration.TotalSeconds;
                }

                string rtcpReport = String.Format(RTCP_FORMAT_STRING, new object[] {
                    sample.SyncSource,
                    sample.SampleStartTime.ToString("HH:mm:ss:fff"),
                    sample.SampleEndTime.ToString("HH:mm:ss:fff"),
                    sampleDuration.TotalMilliseconds.ToString("0"),
                    sample.StartSequenceNumber.ToString(),
                    sample.EndSequenceNumber.ToString(),
                    sample.TotalPackets.ToString(),
                    sample.JitterMaximum.ToString("0"),
                    sample.JitterAverage.ToString("0.##"),
                    sample.AverageTransitTime.ToString("0.##"),
                    packetRate.ToString("0.##"),
                    sample.BytesReceived.ToString(),
                    sample.TransmissionRate.ToString("0.##"),
                    sample.PacketsLost.ToString(),
                    sample.JitterDiscards.ToString(),
                    sample.Duplicates.ToString(),
                    sample.OutOfOrder.ToString()
                });

                logger.Info(rtcpReport);

                //logger.Info("start=" + sample.SampleStartTime.ToString("HH:mm:ss:fff") + ",end=" + sample.SampleEndTime.ToString("HH:mm:ss:fff") + ",dur=" + sampleDuration.TotalMilliseconds.ToString("0") + "ms" +
                //    ",seqnnumstart=" + sample.StartSequenceNumber + ",seqnumend=" + sample.EndSequenceNumber + ",pktstotal=" + sample.TotalPackets + "p,pktrate=" + packetRate.ToString("0.##") + "pps,bytestotal=" + sample.BytesReceived + "B,bw=" + sample.TransmissionRate.ToString("0.##") + "Kbps,jitteravg=" +
                //    sample.JitterAverage.ToString("0.##") + ",jittermax=" + sample.JitterMaximum.ToString("0.##") + ",pktslost=" + sample.PacketsLost + ",jitterdiscards=" + sample.JitterDiscards + ",duplicates=" + sample.Duplicates + ",outoforder=" + sample.OutOfOrder);

                return(sample);
            }
            catch (Exception excp)
            {
                logger.Error("Exception Sample. " + excp.Message);
                return(null);
            }
            finally
            {
                m_reportNumber++;
            }
        }
예제 #3
0
        /// <summary>
        /// A sample is taken if the last RTP measurement recorded is 4x the sample time since the last last smaple was taken.
        /// The 4x is needed in order to be able to take into account late arriving packets as out of order rather then as drops.
        /// </summary>
        /// <param name="sequenceNumber"></param>
        private RTCPReport CheckForAvailableSample(UInt16 sequenceNumber)
        {
            try
            {
                //logger.Debug("Check for available sample " + sequenceNumber + ".");
                RTCPReport sample = null;

                RTPReceiveRecord measurement = m_rcvdSeqNums[sequenceNumber];

                //logger.Debug("window start seq num=" + m_windowStartSeqNum);
                RTPReceiveRecord startSampleMeasuerment = m_rcvdSeqNums[m_windowStartSeqNum];

                UInt16   endSampleSeqNum         = 0;
                UInt16   endSampleSeqNumMinusOne = 0;
                int      samplesAvailable        = 0;
                DateTime sampleCutOffTime;

                bool sampleAvailable = false;

                // Determine whether a sample of the RTP stream measurements should be taken.
                if (DateTime.Now.Subtract(m_lastSampleTime).TotalMilliseconds > (4 * ReportSampleDuration))
                {
                    // Make the sample a random size between N and 2N
                    int randomElement  = Crypto.GetRandomInt(ReportSampleDuration, 2 * ReportSampleDuration);
                    int sampleDuration = ReportSampleDuration + randomElement;
                    sampleCutOffTime = m_lastSampleTime.AddMilliseconds(sampleDuration);

                    //logger.Debug("Sample duration=" + sampleDuration + "ms, cut off time=" + sampleCutOffTime.ToString("HH:mm:ss:fff") + ".");

                    // Get the list of RTP measurements from last time a sample was taken up to the last receive within the window.
                    int endSeqNum = (sequenceNumber < m_windowStartSeqNum) ? sequenceNumber + UInt16.MaxValue + 1: sequenceNumber;
                    //logger.Debug("Checking for sample from " + m_windowStartSeqNum + " to " + endSeqNum + ".");
                    for (int seqNum = m_windowStartSeqNum; seqNum <= endSeqNum; seqNum++)
                    {
                        UInt16 testSeqNum = (seqNum > UInt16.MaxValue) ? Convert.ToUInt16((seqNum % UInt16.MaxValue) - 1) : Convert.ToUInt16(seqNum);

                        if (m_rcvdSeqNums.ContainsKey(testSeqNum))
                        {
                            //logger.Debug(testSeqNum + " " + m_rcvdSeqNums[testSeqNum].ReceiveTime.ToString("ss:fff") + "<" + sampleCutOffTime.ToString("ss:fff") + ".");

                            if (m_rcvdSeqNums[testSeqNum].ReceiveTime < sampleCutOffTime)
                            {
                                samplesAvailable++;
                                endSampleSeqNum = testSeqNum;
                            }
                            else
                            {
                                endSampleSeqNumMinusOne = endSampleSeqNum;
                                endSampleSeqNum         = testSeqNum;
                                sampleAvailable         = true;
                                break;
                            }
                        }
                    }
                }

                /*if (m_rcvdSeqNums.Count > 200)
                 * {
                 *  endSampleSeqNum = m_windowSecondLastSeqNum;
                 *  sampleAvailable = true;
                 * }*/

                if (sampleAvailable)
                {
                    //logger.Debug(samplesAvailable + " ready for RTCP sampling, start seq num=" + m_windowStartSeqNum + " to " + endSampleSeqNumMinusOne + ".");

                    TimeSpan measurementsSampleDuration = m_rcvdSeqNums[endSampleSeqNumMinusOne].ReceiveTime.Subtract(m_rcvdSeqNums[m_windowStartSeqNum].ReceiveTime);
                    //logger.Debug("Sample available start seq num=" + m_windowStartSeqNum + " end seq num=" + endSampleSeqNumMinusOne + ", " + measurementsSampleDuration.TotalMilliseconds.ToString("0") + ".");

                    sample = Sample(m_windowStartSeqNum, endSampleSeqNumMinusOne, measurementsSampleDuration);

                    m_windowStartSeqNum = endSampleSeqNum;
                    m_lastSampleTime    = m_rcvdSeqNums[endSampleSeqNum].ReceiveTime;
                }

                return(sample);
            }
            catch (Exception excp)
            {
                logger.Error("Exception CheckForAvailableSample. " + excp.Message);
                return(null);
            }
        }
예제 #4
0
        /// <summary>
        /// Records the arrival of a new RTP packet and periodically samples the mesaurements to record the characteristics of the RTP stream.
        /// </summary>
        /// <param name="sequenceNumber">RTP header sequence number, monotonically increasing in RTP stream.</param>
        /// <param name="sendTime">The remote time at which the RTP packet was sent.</param>
        /// <param name="receiveTime">The local time at which the RTP listener received the packet.</param>
        /// <param name="bytesReceived">Number of bytes received.</param>
        public void RecordRTPReceive(DateTime receiveTime, UInt16 sequenceNumber, long bytesReceived, uint jitter)
        {
            try
            {
                //logger.Debug("RecordRTPReceive " + sequenceNumber + ".");

                if (m_rcvdSeqNums.ContainsKey(sequenceNumber))
                {
                    logger.Debug("duplicate " + sequenceNumber + ".");
                    m_rcvdSeqNums[sequenceNumber].Duplicates = m_rcvdSeqNums[sequenceNumber].Duplicates + 1;
                }
                //else if(sequenceNumber < m_windowStartSeqNum)
                //{
                //    OutsideWindow++;
                //}
                else
                {
                    bool inSequence = (m_windowLastSeqNum != UInt16.MaxValue) ? sequenceNumber == m_windowLastSeqNum + 1 : sequenceNumber == 0;
                    //bool inSequence = (Math.Abs(sequenceNumber - m_windowLastSeqNum) > (UInt16.MaxValue / 2)) ? sequenceNumber == m_windowLastSeqNum + 1 : sequenceNumber == 0;

                    //logger.Debug(sequenceNumber + " in sequence=" + inSequence + ".");

                    /*int startJitterBufferSeq = (m_windowLastSeqNum - JitterBufferSamples >= 0) ? m_windowLastSeqNum - JitterBufferSamples : m_windowLastSeqNum  + 65535 - JitterBufferSamples;
                     * int endJitterBufferSeq = (m_windowLastSeqNum + JitterBufferSamples <= 65535) ? m_windowLastSeqNum + JitterBufferSamples : m_windowLastSeqNum + JitterBufferSamples - 65535;
                     * bool jitterDiscard = false;
                     *
                     * if(endJitterBufferSeq > startJitterBufferSeq)
                     * {
                     *  jitterDiscard = !(sequenceNumber >= startJitterBufferSeq && sequenceNumber <= endJitterBufferSeq);
                     * }
                     * else
                     * {
                     *  jitterDiscard = !(sequenceNumber >= startJitterBufferSeq || sequenceNumber <= endJitterBufferSeq);
                     * }*/

                    m_windowSecondLastSeqNum = m_windowLastSeqNum;
                    m_windowLastSeqNum       = sequenceNumber;

                    // Add measurement to buffer.

                    /*DateTime utcSendTime = sendTime.ToUniversalTime();
                     * DateTime utcReceiveTime = receiveTime.ToUniversalTime();
                     * double transitTime = (utcSendTime < utcReceiveTime) ? utcReceiveTime.Subtract(utcSendTime).TotalMilliseconds : utcSendTime.Subtract(utcReceiveTime).TotalMilliseconds;
                     */

                    //if (m_latestInterArrivalTimes.Count > TRANSITTIMES_QUEUE_LENGTH)
                    //{
                    //    m_latestInterArrivalTimes.Dequeue();
                    //}
                    //m_latestInterArrivalTimes.Enqueue(interArrivalMilliseconds);
                    //int avgArrivalTime = GetAverageTransitMilliseconds();

                    //logger.Debug("Avg transit time=" + avgTransitTime + "ms, " + sequenceNumber);

                    //double jitterAbs = Math.Abs(interArrivalMilliseconds - avgArrivalTime);
                    //logger.Debug("jitterAbs=" + jitterAbs + "ms, " + sequenceNumber);
                    //int jitter = 0;
                    //if (jitterAbs > 1)
                    //{
                    //    jitter = Convert.ToInt32(jitterAbs);
                    //}

                    bool jitterDiscard = false;
                    if (jitter >= JitterBufferMilliseconds)
                    {
                        logger.Debug("jitter discard " + sequenceNumber + ".");
                        jitterDiscard = true;
                    }

                    //logger.Debug("jitter=" + jitter + "ms, avg transit=" + avgArrivalTime);

                    //resultsLogger.Info(sequenceNumber + "," + bytesReceived + "," + avgTransitTime + "," + jitter + "," + inSequence + "," + jitterDiscard + ",[" + startJitterBufferSeq + "<=" + sequenceNumber + "<=" + endJitterBufferSeq + "]");

                    //RTPReceiveRecord measurement = new RTPReceiveRecord(localSendTime, utcReceiveTime, sequenceNumber, bytesReceived);
                    RTPReceiveRecord measurement = new RTPReceiveRecord(receiveTime, sequenceNumber, bytesReceived, jitter, inSequence, jitterDiscard);

                    //logger.Debug("adding measurement for " + measurement.SequenceNumber + ".");
                    lock (m_rcvdSeqNums)
                    {
                        if (m_rcvdSeqNums.ContainsKey(sequenceNumber))
                        {
                            logger.Warn("RecordRTPReceive having to remove measurement for " + sequenceNumber + " in order to accomodate new RTP measurement.");
                            m_rcvdSeqNums.Remove(sequenceNumber);
                        }

                        m_rcvdSeqNums.Add(sequenceNumber, measurement);
                    }
                }

                //return CheckForAvailableSample(sequenceNumber);
            }
            catch (Exception excp)
            {
                logger.Error("Exception RecordRTPReceive for " + sequenceNumber + ". " + excp.Message);
            }
        }