Newer
Older
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.neomedia;
import java.awt.*;
import java.net.*;

Boris Grozev
committed
import java.util.*;

Lyubomir Marinov
committed

Boris Grozev
committed
import javax.media.control.*;
import javax.media.protocol.*;

Lyubomir Marinov
committed
import net.sf.fmj.media.rtp.*;

Lyubomir Marinov
committed
import org.jitsi.impl.neomedia.device.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.control.*;

Lyubomir Marinov
committed
import org.jitsi.service.neomedia.format.*;
import org.jitsi.util.*;

Lyubomir Marinov
committed
/**
* Class used to compute stats concerning a MediaStream.
*
* @author Vincent Lucas
* @author Boris Grozev
*/
public class MediaStreamStatsImpl
implements MediaStreamStats
{
/**
* The <tt>Logger</tt> used by the <tt>MediaStreamImpl</tt> class and its
* instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(MediaStreamStatsImpl.class);

Lyubomir Marinov
committed
* Enumeration of the direction (DOWNLOAD or UPLOAD) used for the stats.
*/
private enum StreamDirection
{
DOWNLOAD,
UPLOAD
}
/**

Vincent Lucas
committed
* The source data stream to analyze in order to compute the stats.

Lyubomir Marinov
committed
private final MediaStreamImpl mediaStreamImpl;
/**
* The last time these stats have been updated.
*/
private long updateTimeMs;
/**
* The last number of received/sent packets.
*/
private long[] nbPackets = {0, 0};
/**
* The last number of sent packets when the last feedback has been received.
* This counter is used to compute the upload loss rate.
*/
private long uploadFeedbackNbPackets = 0;
/**
* The last number of download/upload lost packets.
*/
private long[] nbLost = {0, 0};
/**
* The total number of discarded packets
*/
private long nbDiscarded = 0;
/**
* The number of packets for which FEC data was decoded. This is only
*/
private long nbFec = 0;
/**
* The last number of received/sent Bytes.
*/
private long[] nbByte = {0, 0};
/**
* The last download/upload loss rate computed (in %).
*/
private double[] percentLoss = {0, 0};
/**
* The last percent of discarded packets
*/
private double percentDiscarded = 0;
/**
* The last used bandwidth computed in download/upload (in Kbit/s).
*/
private double[] rateKiloBitPerSec = {0, 0};
/**
* The last jitter received/sent in a RTCP feedback (in RTP timestamp
* units).
*/
private double[] jitterRTPTimestampUnits = {0, 0};
/**
* The RTT computed with the RTCP feedback (cf. RFC3550, section 6.4.1,
* subsection "delay since last SR (DLSR): 32 bits").
* -1 if the RTT has not been computed yet. Otherwise the RTT in ms.
*/
private long rttMs = -1;
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* Creates a new instance of stats concerning a MediaStream.
*
* @param mediaStreamImpl The MediaStreamImpl used to compute the stats.
*/
public MediaStreamStatsImpl(MediaStreamImpl mediaStreamImpl)
{
this.updateTimeMs = System.currentTimeMillis();
this.mediaStreamImpl = mediaStreamImpl;
}
/**
* Computes and updates information for a specific stream.
*/
public void updateStats()
{
// Gets the current time.
long currentTimeMs = System.currentTimeMillis();
// UPdates stats for the download stream.
this.updateStreamDirectionStats(
StreamDirection.DOWNLOAD,
currentTimeMs);
// UPdates stats for the upload stream.
this.updateStreamDirectionStats(
StreamDirection.UPLOAD,
currentTimeMs);
// Saves the last update values.
this.updateTimeMs = currentTimeMs;
}
/**
* Computes and updates information for a specific stream.
*
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function updates the stats.
* @param currentTimeMs The current time in ms.
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
*/
private void updateStreamDirectionStats(
StreamDirection streamDirection,
long currentTimeMs)
{
int streamDirectionIndex = streamDirection.ordinal();
// Gets the current number of packets correctly received since the
// beginning of this stream.
long newNbRecv = this.getNbPDU(streamDirection);
// Gets the number of byte received/sent since the beginning of this
// stream.
long newNbByte = this.getNbBytes(streamDirection);
// Computes the number of update steps which has not been done since
// last update.
long nbSteps = newNbRecv - this.nbPackets[streamDirectionIndex];
// Even if the remote peer does not send any packets (i.e. is
// microphone is muted), Jitsi must updates it stats. Thus, Jitsi
// computes a number of steps equivalent as if Jitsi receives a packet
// each 20ms (default value).
if(nbSteps == 0)
{
nbSteps = (currentTimeMs - this.updateTimeMs) / 20;
}
// The upload percentLoss is only computed when a new RTCP feedback is
// received. This is not the case for the download percentLoss which is
// updated for each new RTP packet received.
// Computes the loss rate for this stream.
if(streamDirection == StreamDirection.DOWNLOAD)
{
// Gets the current number of losses in download since the beginning
// of this stream.
long newNbLost =
this.getDownloadNbPDULost() - this.nbLost[streamDirectionIndex];
updateNbLoss(streamDirection, newNbLost, nbSteps + newNbLost);
long newNbDiscarded = this.getNbDiscarded() - this.nbDiscarded;
updateNbDiscarded(newNbDiscarded, nbSteps + newNbDiscarded);
}
// Computes the bandwidth used by this stream.
double newRateKiloBitPerSec =
MediaStreamStatsImpl.computeRateKiloBitPerSec(
newNbByte - this.nbByte[streamDirectionIndex],
currentTimeMs - this.updateTimeMs);
this.rateKiloBitPerSec[streamDirectionIndex] =
MediaStreamStatsImpl.computeEWMA(
nbSteps,
this.rateKiloBitPerSec[streamDirectionIndex],
newRateKiloBitPerSec);
// Saves the last update values.
this.nbPackets[streamDirectionIndex] = newNbRecv;
this.nbByte[streamDirectionIndex] = newNbByte;
updateNbFec();
}
/**
* Returns the local IP address of the MediaStream.
*
* @return the local IP address of the stream.
*/
public String getLocalIPAddress()
{

Lyubomir Marinov
committed
InetSocketAddress mediaStreamLocalDataAddress
= mediaStreamImpl.getLocalDataAddress();
return
(mediaStreamLocalDataAddress == null)
? null
: mediaStreamLocalDataAddress.getAddress().getHostAddress();
}
/**
* Returns the local port of the MediaStream.
*
* @return the local port of the stream.
*/
public int getLocalPort()
{

Lyubomir Marinov
committed
InetSocketAddress mediaStreamLocalDataAddress
= mediaStreamImpl.getLocalDataAddress();
return
(mediaStreamLocalDataAddress == null)
? -1
: mediaStreamLocalDataAddress.getPort();
}
/**
* Returns the remote IP address of the MediaStream.
*
* @return the remote IP address of the stream.
*/
public String getRemoteIPAddress()
{
MediaStreamTarget mediaStreamTarget = mediaStreamImpl.getTarget();

Lyubomir Marinov
committed
// Gets this stream IP address endpoint. Stops if the endpoint is
// disconnected.
return
(mediaStreamTarget == null)
? null
: mediaStreamTarget.getDataAddress().getAddress()
.getHostAddress();
}
/**
* Returns the remote port of the MediaStream.
*
* @return the remote port of the stream.
*/
public int getRemotePort()
{
MediaStreamTarget mediaStreamTarget = mediaStreamImpl.getTarget();

Lyubomir Marinov
committed
// Gets this stream port endpoint. Stops if the endpoint is
// disconnected.
return
(mediaStreamTarget == null)
? -1
: mediaStreamTarget.getDataAddress().getPort();
}
/**
* Returns the MediaStream enconding.
*
* @return the encoding used by the stream.
*/
public String getEncoding()
{

Lyubomir Marinov
committed
MediaFormat format = mediaStreamImpl.getFormat();
return (format == null) ? null : format.getEncoding();
}
/**
* Returns the MediaStream enconding rate (in Hz)..
*
* @return the encoding rate used by the stream.
*/
public String getEncodingClockRate()
{

Lyubomir Marinov
committed
MediaFormat format = mediaStreamImpl.getFormat();
return (format == null) ? null : format.getRealUsedClockRateString();
}
/**
* Returns the upload video format if this stream uploads a video, or null
* if not.
*
* @return the upload video format if this stream uploads a video, or null
* if not.
*/
private VideoFormat getUploadVideoFormat()
{

Lyubomir Marinov
committed
MediaDeviceSession deviceSession = mediaStreamImpl.getDeviceSession();

Lyubomir Marinov
committed
return

Lyubomir Marinov
committed
(deviceSession instanceof VideoMediaDeviceSession)
? ((VideoMediaDeviceSession) deviceSession)

Lyubomir Marinov
committed
.getSentVideoFormat()
: null;
}
/**
* Returns the download video format if this stream downloads a video, or
* null if not.
*
* @return the download video format if this stream downloads a video, or
* null if not.
*/
private VideoFormat getDownloadVideoFormat()
{

Lyubomir Marinov
committed
MediaDeviceSession deviceSession = mediaStreamImpl.getDeviceSession();

Lyubomir Marinov
committed
return

Lyubomir Marinov
committed
(deviceSession instanceof VideoMediaDeviceSession)
? ((VideoMediaDeviceSession) deviceSession)

Lyubomir Marinov
committed
.getReceivedVideoFormat()
: null;
}
/**
* Returns the upload video size if this stream uploads a video, or null if
* not.
*
* @return the upload video size if this stream uploads a video, or null if
* not.
*/
public Dimension getUploadVideoSize()
{

Lyubomir Marinov
committed
VideoFormat format = getUploadVideoFormat();
return (format == null) ? null : format.getSize();
}
/**
* Returns the download video size if this stream downloads a video, or
* null if not.
*
* @return the download video size if this stream downloads a video, or null
* if not.
*/
public Dimension getDownloadVideoSize()
{

Lyubomir Marinov
committed
VideoFormat format = getDownloadVideoFormat();
return (format == null) ? null : format.getSize();
}
/**
* Returns the percent loss of the download stream.
*
* @return the last loss rate computed (in %).
*/
public double getDownloadPercentLoss()
{
return this.percentLoss[StreamDirection.DOWNLOAD.ordinal()];
}
/**
* Returns the percent of discarded packets
*
* @return the percent of discarded packets
*/
public double getPercentDiscarded()
{
return percentDiscarded;
}
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/**
* Returns the percent loss of the upload stream.
*
* @return the last loss rate computed (in %).
*/
public double getUploadPercentLoss()
{
return this.percentLoss[StreamDirection.UPLOAD.ordinal()];
}
/**
* Returns the bandwidth used by this download stream.
*
* @return the last used download bandwidth computed (in Kbit/s).
*/
public double getDownloadRateKiloBitPerSec()
{
return this.rateKiloBitPerSec[StreamDirection.DOWNLOAD.ordinal()];
}
/**
* Returns the bandwidth used by this download stream.
*
* @return the last used upload bandwidth computed (in Kbit/s).
*/
public double getUploadRateKiloBitPerSec()
{
return this.rateKiloBitPerSec[StreamDirection.UPLOAD.ordinal()];
}
/**
* Returns the jitter average of this download stream.
*
* @return the last jitter average computed (in ms).
*/
public double getDownloadJitterMs()
{
return this.getJitterMs(StreamDirection.DOWNLOAD);
}
/**
* Returns the jitter average of this upload stream.
*
* @return the last jitter average computed (in ms).
*/
public double getUploadJitterMs()
{
return this.getJitterMs(StreamDirection.UPLOAD);
}
/**
* Returns the jitter average of this upload/download stream.
*
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function retrieve the jitter.
*
* @return the last jitter average computed (in ms).
*/
private double getJitterMs(StreamDirection streamDirection)
{

Lyubomir Marinov
committed
MediaFormat format = mediaStreamImpl.getFormat();
double clockRate;
if (format == null)
{
MediaType mediaType = mediaStreamImpl.getMediaType();

Lyubomir Marinov
committed
clockRate = MediaType.VIDEO.equals(mediaType) ? 90000 : -1;

Lyubomir Marinov
committed
}
else
clockRate = format.getClockRate();
if (clockRate <= 0)
return -1;
// RFC3550 says that concerning the RTP timestamp unit (cf. section 5.1
// RTP Fixed Header Fields, subsection timestamp: 32 bits):
// As an example, for fixed-rate audio the timestamp clock would likely
// increment by one for each sampling period.
//

Lyubomir Marinov
committed
// Thus we take the jitter in RTP timestamp units, convert it to seconds
// (/ clockRate) and finally converts it to milliseconds (* 1000).

Lyubomir Marinov
committed
return
(jitterRTPTimestampUnits[streamDirection.ordinal()] / clockRate)
* 1000.0;
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
}
/**
* Updates the jitter stream stats with the new feedback sent.
*
* @param feedback The last RTCP feedback sent by the MediaStream.
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function retrieve the jitter.
*/
private void updateJitterRTPTimestampUnits(
RTCPFeedback feedback,
StreamDirection streamDirection)
{
// Updates the download jitter in RTP timestamp units.
// There is no need to compute a jitter average, since (cf. RFC3550,
// section 6.4.1 SR: Sender Report RTCP Packet, subsection interarrival
// jitter: 32 bits) the value contained in the RTCP sender report packet
// contains a mean deviation of the jitter.
this.jitterRTPTimestampUnits[streamDirection.ordinal()] =
feedback.getJitter();
}
/**
* Updates this stream stats with the new feedback sent.
*
* @param feedback The last RTCP feedback sent by the MediaStream.
*/
public void updateNewSentFeedback(RTCPFeedback feedback)
{
updateJitterRTPTimestampUnits(feedback, StreamDirection.DOWNLOAD);
// No need to update the download loss as we have a more accurate value
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
// in the global reception stats, which are updated for each new packet
// received.
}
/**
* Updates this stream stats with the new feedback received.
*
* @param feedback The last RTCP feedback received by the MediaStream.
*/
public void updateNewReceivedFeedback(RTCPFeedback feedback)
{
StreamDirection streamDirection = StreamDirection.UPLOAD;
updateJitterRTPTimestampUnits(feedback, streamDirection);
// Updates the loss rate with the RTCP sender report feedback, since
// this is the only information source available for the upalod stream.
long uploadNewNbRecv = feedback.getXtndSeqNum();
long newNbLost =
feedback.getNumLost() - this.nbLost[streamDirection.ordinal()];
long nbSteps = uploadNewNbRecv - this.uploadFeedbackNbPackets;
updateNbLoss(streamDirection, newNbLost, nbSteps);
// Updates the upload loss counters.
this.uploadFeedbackNbPackets = uploadNewNbRecv;
// Computes RTT.
this.rttMs = computeRTTInMs(feedback);
}
/**
* Updates the number of loss for a given stream.
*
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function updates the stats.
* @param newNbLost The last update of the number of lost.
* @param nbSteps The number of elapsed steps since the last number of loss
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
* update.
*/
private void updateNbLoss(
StreamDirection streamDirection,
long newNbLost,
long nbSteps)
{
int streamDirectionIndex = streamDirection.ordinal();
double newPercentLoss = MediaStreamStatsImpl.computePercentLoss(
nbSteps,
newNbLost);
this.percentLoss[streamDirectionIndex] =
MediaStreamStatsImpl.computeEWMA(
nbSteps,
this.percentLoss[streamDirectionIndex],
newPercentLoss);
// Saves the last update number download lost value.
this.nbLost[streamDirectionIndex] += newNbLost;
}
/**
* Computes the loss rate.
*
* @param nbLostAndRecv The number of lost and received packets.
* @param nbLost The number of lost packets.
*
* @return The loss rate in percent.
*/
private static double computePercentLoss(long nbLostAndRecv, long nbLost)
{
if(nbLostAndRecv == 0)
{
return 0;
}
return ((double) 100 * nbLost) / ((double)(nbLostAndRecv));
}
/**
* Computes the bandwidth usage in Kilo bits per seconds.
*
* @param nbByteRecv The number of Byte received.
* @param callNbTimeMsSpent The time spent since the mediaStreamImpl is
* connected to the endpoint.
*
* @return the bandwidth rate computed in Kilo bits per seconds.
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
*/
private static double computeRateKiloBitPerSec(
long nbByteRecv,
long callNbTimeMsSpent)
{
if(nbByteRecv == 0)
{
return 0;
}
return (nbByteRecv * 8.0 / 1000.0) / (callNbTimeMsSpent / 1000.0);
}
/**
* Computes an Exponentially Weighted Moving Average (EWMA). Thus, the most
* recent history has a more preponderant importance in the average
* computed.
*
* @param nbStepSinceLastUpdate The number of step which has not been
* computed since last update. In our case the number of packets received
* since the last computation.
* @param lastValue The value computed during the last update.
* @param newValue The value newly computed.
*
* @return The EWMA average computed.
*/
private static double computeEWMA(
long nbStepSinceLastUpdate,
double lastValue,
double newValue)
{
// For each new packet received the EWMA moves by a 0.1 coefficient.
double EWMACoeff = 0.01 * nbStepSinceLastUpdate;
// EWMA must be <= 1.
if(EWMACoeff > 1)
{
EWMACoeff = 1.0;
}
return lastValue * (1.0 - EWMACoeff) + newValue * EWMACoeff;
}
/**
* Returns the number of Protocol Data Units (PDU) sent/received since the
* beginning of the session.
*
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function retrieve the number of sent/received
* packets.
*
* @return the number of packets sent/received for this stream.
*/
private long getNbPDU(StreamDirection streamDirection)
{

Lyubomir Marinov
committed
StreamRTPManager rtpManager = mediaStreamImpl.getRTPManager();
long nbPDU = 0;
if(rtpManager != null)
{
switch(streamDirection)
{

Lyubomir Marinov
committed
case UPLOAD:
nbPDU = rtpManager.getGlobalTransmissionStats().getRTPSent();
break;
case DOWNLOAD:
GlobalReceptionStats globalReceptionStats
= rtpManager.getGlobalReceptionStats();
nbPDU
= globalReceptionStats.getPacketsRecd()
- globalReceptionStats.getRTCPRecd();

Lyubomir Marinov
committed
break;
}
}
return nbPDU;
}
/**
* Returns the number of Protocol Data Units (PDU) lost in download since
* the beginning of the session.
*
* @return the number of packets lost for this stream.
*/
private long getDownloadNbPDULost()
{

Lyubomir Marinov
committed
MediaDeviceSession devSession = mediaStreamImpl.getDeviceSession();

Lyubomir Marinov
committed
if (devSession != null)
{
for(ReceiveStream receiveStream : devSession.getReceiveStreams())
nbLost += receiveStream.getSourceReceptionStats().getPDUlost();
}
/**

Boris Grozev
committed
* Returns the total number of Protocol Data Units (PDU) discarded by the
* FMJ packet queue since the beginning of the session. It's the sum over
* all <tt>ReceiveStream</tt>s of the <tt>MediaStream</tt>
*
* @return the number of discarded packets.
*/
public long getNbDiscarded()
{
int nbDiscarded = 0;

Boris Grozev
committed
for(PacketQueueControl pqc : getPacketQueueControls())
nbDiscarded =+ pqc.getDiscarded();
return nbDiscarded;
}

Lyubomir Marinov
committed

Boris Grozev
committed
/**
* Returns the number of Protocol Data Units (PDU) discarded by the
* FMJ packet queue since the beginning of the session due to shrinking.
* It's the sum over all <tt>ReceiveStream</tt>s of the <tt>MediaStream</tt>
*
* @return the number of discarded packets due to shrinking.
*/
public int getNbDiscardedShrink()
{
int nbDiscardedShrink = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
nbDiscardedShrink =+ pqc.getDiscardedShrink();
return nbDiscardedShrink;
}

Boris Grozev
committed
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
/**
* Returns the number of Protocol Data Units (PDU) discarded by the
* FMJ packet queue since the beginning of the session because it was full.
* It's the sum over all <tt>ReceiveStream</tt>s of the <tt>MediaStream</tt>
*
* @return the number of discarded packets because it was full.
*/
public int getNbDiscardedFull()
{
int nbDiscardedFull = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
nbDiscardedFull =+ pqc.getDiscardedFull();
return nbDiscardedFull;
}
/**
* Returns the number of Protocol Data Units (PDU) discarded by the
* FMJ packet queue since the beginning of the session because they were late.
* It's the sum over all <tt>ReceiveStream</tt>s of the <tt>MediaStream</tt>
*
* @return the number of discarded packets because they were late.
*/
public int getNbDiscardedLate()
{
int nbDiscardedLate = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
nbDiscardedLate =+ pqc.getDiscardedLate();
return nbDiscardedLate;
}
/**
* Returns the number of Protocol Data Units (PDU) discarded by the
* FMJ packet queue since the beginning of the session during resets.
* It's the sum over all <tt>ReceiveStream</tt>s of the <tt>MediaStream</tt>
*
* @return the number of discarded packets during resets.
*/
public int getNbDiscardedReset()
{
int nbDiscardedReset = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
nbDiscardedReset =+ pqc.getDiscardedReset();
return nbDiscardedReset;
}
/**
* Returns the number of sent/received bytes since the beginning of the
* session.
*
* @param streamDirection The stream direction (DOWNLOAD or UPLOAD) of the
* stream from which this function retrieve the number of sent/received
* bytes.
*
* @return the number of sent/received bytes for this stream.
*/
private long getNbBytes(StreamDirection streamDirection)
{
long nbBytes = 0;

Lyubomir Marinov
committed
StreamRTPManager rtpManager = mediaStreamImpl.getRTPManager();
if(rtpManager != null)
{
switch(streamDirection)
{

Lyubomir Marinov
committed
case DOWNLOAD:
nbBytes = rtpManager.getGlobalReceptionStats().getBytesRecd();
break;
case UPLOAD:
nbBytes
= rtpManager.getGlobalTransmissionStats().getBytesSent();
break;
}
}
return nbBytes;
}
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
/**
* Computes the RTT with the data (LSR and DLSR) contained in the last
* RTCP Sender Report (RTCP feedback). This RTT computation is based on
* RFC3550, section 6.4.1, subsection "delay since last SR (DLSR): 32
* bits".
*
* @param feedback The last RTCP feedback received by the MediaStream.
*
* @return The RTT in milliseconds, or -1 if the RTT is not computable.
*/
private long computeRTTInMs(RTCPFeedback feedback)
{
// Computes RTT.
long currentTime = System.currentTimeMillis();
long DLSR = feedback.getDLSR();
long LSR = feedback.getLSR();
// If the peer sending us the sender report has at least received on
// sender report from our side, then computes the RTT.
if(DLSR != 0 && LSR != 0)
{
long LSRs = LSR >> 16;
long LSRms = ((LSR & 0xffff) * 1000) / 0xffff;
long DLSRs = DLSR / 0xffff;
long DLSRms = ((DLSR & 0xffff) *1000) / 0xffff;
long currentTimeS = (currentTime / 1000) & 0x0000ffff;
long currentTimeMs = (currentTime % 1000);
long rttS = currentTimeS - DLSRs - LSRs;
long rttMs = currentTimeMs - DLSRms - LSRms;
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
long computedRTTms = (rttS * 1000) + rttMs;
// If the RTT is greater than a minute there might be a bug. Thus we
// log the info to see the source of this error.
if(computedRTTms > 60000 && logger.isInfoEnabled())
{
logger.info("RTT computation seems to be wrong ("
+ computedRTTms + "> 60 seconds):"
+ "\n\tcurrentTime: " + currentTime
+ " (" + Long.toHexString(currentTime) + ")"
+ "\n\tDLSR: " + DLSR
+ " (" + Long.toHexString(DLSR) + ")"
+ "\n\tLSR: " + LSR
+ " (" + Long.toHexString(LSR) + ")"
+ "\n\n\tcurrentTimeS: " + currentTimeS
+ " (" + Long.toHexString(currentTimeS) + ")"
+ "\n\tDLSRs: " + DLSRs
+ " (" + Long.toHexString(DLSRs) + ")"
+ "\n\tLSRs: " + LSRs
+ " (" + Long.toHexString(LSRs) + ")"
+ "\n\trttS: " + rttS
+ " (" + Long.toHexString(rttS) + ")"
+ "\n\n\tcurrentTimeMs: " + currentTimeMs
+ " (" + Long.toHexString(currentTimeMs) + ")"
+ "\n\tDLSRms: " + DLSRms
+ " (" + Long.toHexString(DLSRms) + ")"
+ "\n\tLSRms: " + LSRms
+ " (" + Long.toHexString(LSRms) + ")"
+ "\n\trttMs: " + rttMs
+ " (" + Long.toHexString(rttMs) + ")"
);
}
return computedRTTms;
}
// Else the RTT can not be computed yet.
return -1;
}
/**
* Returns the RTT computed with the RTCP feedback (cf. RFC3550, section
* 6.4.1, subsection "delay since last SR (DLSR): 32 bits").
*
* @return The RTT computed with the RTCP feedback. Returns -1 if the RTT
* has not been computed yet. Otherwise the RTT in ms.
*/
public long getRttMs()
{
return this.rttMs;
}
/**
* Returns the number of packets for which FEC data was decoded. Currently
* this is cumulative over all <tt>ReceiveStream</tt>s.
*
* @return the number of packets for which FEC data was decoded. Currently
* this is cumulative over all <tt>ReceiveStream</tt>s.
*
* @see org.jitsi.impl.neomedia.MediaStreamStatsImpl#updateNbFec()
*/
public long getNbFec()
{
return nbFec;
}
/**
* Updates the <tt>nbFec</tt> field with the sum of FEC-decoded packets
* over the different <tt>ReceiveStream</tt>s
*/
private void updateNbFec()
{

Lyubomir Marinov
committed
MediaDeviceSession devSession = mediaStreamImpl.getDeviceSession();
int nbFec = 0;

Lyubomir Marinov
committed
if(devSession != null)
{

Lyubomir Marinov
committed
for(ReceiveStream receiveStream : devSession.getReceiveStreams())

Boris Grozev
committed
{

Lyubomir Marinov
committed
for(FECDecoderControl fecDecoderControl

Lyubomir Marinov
committed
: devSession.getDecoderControls(
receiveStream,

Lyubomir Marinov
committed
FECDecoderControl.class))

Lyubomir Marinov
committed
{

Lyubomir Marinov
committed
nbFec += fecDecoderControl.fecPacketsDecoded();

Lyubomir Marinov
committed
}

Boris Grozev
committed
}
}
this.nbFec = nbFec;
}

Lyubomir Marinov
committed
/**
* Updates the number of discarded packets.
*
* @param newNbDiscarded The last update of the number of lost.
* @param nbSteps The number of elapsed steps since the last number of loss
* update.
*/
private void updateNbDiscarded(
long newNbDiscarded,
long nbSteps)
{
double newPercentDiscarded = MediaStreamStatsImpl.computePercentLoss(
nbSteps,
newNbDiscarded);
this.percentDiscarded =
MediaStreamStatsImpl.computeEWMA(
nbSteps,
this.percentDiscarded,
newPercentDiscarded);
// Saves the last update number download lost value.
this.nbDiscarded += newNbDiscarded;
}

Boris Grozev
committed
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
public boolean isAdaptiveBufferEnabled()
{
for(PacketQueueControl pcq : getPacketQueueControls())
if(pcq.isAdaptiveBufferEnabled())
return true;
return false;
}
/**
* Returns the delay in number of packets introduced by the jitter buffer.
* Since there might be multiple <tt>ReceiveStreams</tt>, returns the
* biggest delay found in any of them.
*
* @return the delay in number of packets introduced by the jitter buffer
*/
public int getJitterBufferDelayPackets()
{
int delay = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
if(pqc.getCurrentDelayPackets() > delay)
delay = pqc.getCurrentDelayPackets();
return delay;
}
/**
* Returns the delay in milliseconds introduced by the jitter buffer.
* Since there might be multiple <tt>ReceiveStreams</tt>, returns the
* biggest delay found in any of them.
*
* @return the delay in milliseconds introduces by the jitter buffer
*/
public int getJitterBufferDelayMs()
{
int delay = 0;
for(PacketQueueControl pqc : getPacketQueueControls())
if(pqc.getCurrentDelayMs() > delay)
delay = pqc.getCurrentDelayMs();
return delay;
}
/**
* Returns the size of the first <tt>PacketQueueControl</tt> found via
* <tt>getPacketQueueControls</tt>.
*
* @return the size of the first <tt>PacketQueueControl</tt> found via
* <tt>getPacketQueueControls</tt>.
*/
public int getPacketQueueSize()
{
for(PacketQueueControl pqc : getPacketQueueControls())
return pqc.getCurrentSizePackets();
return 0;