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.*;

Boris Grozev
committed
import java.util.*;

Lyubomir Marinov
committed

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

Lyubomir Marinov
committed
import javax.media.format.*;

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

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

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

Lyubomir Marinov
committed
import org.jitsi.impl.neomedia.rtp.*;
import org.jitsi.impl.neomedia.rtp.remotebitrateestimator.*;

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

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

Lyubomir Marinov
committed
/**
* Class used to compute stats concerning a MediaStream.
*
* @author Vincent Lucas
* @author Boris Grozev

hristoterezov
committed
* @author Hristo Terezov
*/
public class MediaStreamStatsImpl
implements MediaStreamStats
{
/**
* Enumeration of the direction (DOWNLOAD or UPLOAD) used for the stats.
*/
private enum StreamDirection
{
DOWNLOAD,
UPLOAD
}
/**
* 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);
* 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;
* 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)
{
return
(nbLostAndRecv == 0)
? 0
: (((double) 100 * nbLost) / nbLostAndRecv);
}
* @param nbBytes The number of bytes received.
* @param intervalMs The number of milliseconds during which
* <tt>nbBytes</tt> bytes were sent or received.
* @return the bitraterate computed in kbps (1000 bits per second)
private static double computeRateKiloBitPerSec(
return intervalMs == 0
? 0
: (nbBytes * 8.0) / intervalMs;
* Gets the <tt>JitterBufferControl</tt> of a <tt>ReceiveStream</tt>.
*
* @param receiveStream the <tt>ReceiveStream</tt> to get the
* <tt>JitterBufferControl</tt> of
* @return the <tt>JitterBufferControl</tt> of <tt>receiveStream</tt>.
public static JitterBufferControl getJitterBufferControl(
ReceiveStream receiveStream)
{
DataSource ds = receiveStream.getDataSource();
if (ds instanceof PushBufferDataSource)
{
for (PushBufferStream pbs
: ((PushBufferDataSource) ds).getStreams())
{
JitterBufferControl pqc
= (JitterBufferControl)
pbs.getControl(JitterBufferControl.class.getName());
if (pqc != null)
return pqc;
}
}
return null;
}
* The last jitter received/sent in a RTCP feedback (in RTP timestamp
* units).
private double[] jitterRTPTimestampUnits = {0, 0};
* The source data stream to analyze in order to compute the stats.
private final MediaStreamImpl mediaStreamImpl;
/**
* The last number of received/sent Bytes.
*/
private long[] nbByte = {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 download/upload lost packets.
* The last number of received/sent packets.
/**
* The last percent of discarded packets
*/
private double percentDiscarded = 0;
/**
* The last download/upload loss rate computed (in %).
*/
private double[] percentLoss = {0, 0};
/**
* The last used bandwidth computed in download/upload (in Kbit/s).
*/
private double[] rateKiloBitPerSec = {0, 0};
/**
* The <tt>RTCPReportListener</tt> which listens to {@link #rtcpReports}
* about the sending and the receiving of RTCP sender/receiver reports and
* updates this <tt>MediaStreamStats</tt> with their feedback reports.
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
private final RTCPReportListener rtcpReportListener
= new RTCPReportAdapter()
{
/**
* {@inheritDoc}
*
* Updates this <tt>MediaStreamStats</tt> with the received feedback
* (report).
*/
@Override
public void rtcpReportReceived(RTCPReport report)
{
List<?> feedbackReports = report.getFeedbackReports();
if (!feedbackReports.isEmpty())
{
updateNewReceivedFeedback(
(RTCPFeedback) feedbackReports.get(0));
}
}
/**
* {@inheritDoc}
*
* Updates this <tt>MediaStreamStats</tt> with the sent feedback
* (report).
*/
@Override
public void rtcpReportSent(RTCPReport report)
{
List<?> feedbackReports = report.getFeedbackReports();
if (!feedbackReports.isEmpty())
{
updateNewSentFeedback(
(RTCPFeedback) feedbackReports.get(0));
}
}
};
/**
* The detailed statistics about the RTCP reports sent and received by the
* associated local peer.
*/
private final RTCPReports rtcpReports = new RTCPReports();
/**
* 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;
/**
* The last time these stats have been updated.
*/
private long updateTimeMs;
/**
* 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;
/**
* Creates a new instance of stats concerning a MediaStream.
*
* @param mediaStreamImpl The MediaStreamImpl used to compute the stats.
*/
public MediaStreamStatsImpl(MediaStreamImpl mediaStreamImpl)
{
this.mediaStreamImpl = mediaStreamImpl;
getRTCPReports().addRTCPReportListener(rtcpReportListener);
updateTimeMs = System.currentTimeMillis();
* 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)
long now = System.currentTimeMillis();
long lsr = feedback.getLSR();
long dlsr = feedback.getDLSR();
int rtt = RecvSSRCInfo.getRoundTripDelay(now, lsr, dlsr);
/*
* If the RTT is greater than a minute, it may signal a bug in the
* computation. Log such occurrences in order to debug them.
*/
if ((rtt >= 65536) && logger.isInfoEnabled())
logger.info(
"Stream: " + mediaStreamImpl.getName()
+ ", RTT computation may be wrong (" + rtt
+ ">= 65536 milliseconds): now " + now + ", lsr " + lsr
+ ", dlsr " + dlsr);
* Returns the jitter average of this download stream.
* @return the last jitter average computed (in ms).
return getJitterMs(StreamDirection.DOWNLOAD);
/**
* Returns the number of lost packets for the receive streams.
* @return the number of lost packets for the receive streams.
*/
private long getDownloadNbPacketLost()
{
long downloadLost = 0;
for(ReceiveStream stream : mediaStreamImpl.getReceiveStreams())
{
downloadLost += stream.getSourceReceptionStats().getPDUlost();
}
return downloadLost;
}
* 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.
MediaDeviceSession devSession = mediaStreamImpl.getDeviceSession();
int nbLost = 0;

Lyubomir Marinov
committed
if (devSession != null)
{
for(ReceiveStream receiveStream : devSession.getReceiveStreams())
nbLost += receiveStream.getSourceReceptionStats().getPDUlost();
}
return nbLost;
* Returns the percent loss of the download stream.
* @return the last loss rate computed (in %).
public double getDownloadPercentLoss()
return percentLoss[StreamDirection.DOWNLOAD.ordinal()];
* Returns the bandwidth used by this download stream.
* @return the last used download bandwidth computed (in Kbit/s).
public double getDownloadRateKiloBitPerSec()
return rateKiloBitPerSec[StreamDirection.DOWNLOAD.ordinal()];
}
/**
* 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 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();
*
* @return the encoding used by the stream.
*/
{
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.
MediaFormat format = mediaStreamImpl.getFormat();
return (format == null) ? null : format.getRealUsedClockRateString();
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
/**
* Returns the set of <tt>PacketQueueControls</tt> found for all the
* <tt>DataSource</tt>s of all the <tt>ReceiveStream</tt>s. The set contains
* only non-null elements.
*
* @return the set of <tt>PacketQueueControls</tt> found for all the
* <tt>DataSource</tt>s of all the <tt>ReceiveStream</tt>s. The set contains
* only non-null elements.
*/
private Set<JitterBufferControl> getJitterBufferControls()
{
Set<JitterBufferControl> set = new HashSet<JitterBufferControl>();
if (mediaStreamImpl.isStarted())
{
MediaDeviceSession devSession = mediaStreamImpl.getDeviceSession();
if (devSession != null)
{
for(ReceiveStream receiveStream
: devSession.getReceiveStreams())
{
JitterBufferControl pqc
= getJitterBufferControl(receiveStream);
if(pqc != null)
set.add(pqc);
}
}
}
return set;
}
* 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
int delay = 0;
for(JitterBufferControl pqc : getJitterBufferControls())
if(pqc.getCurrentDelayMs() > delay)
delay = pqc.getCurrentDelayMs();
return delay;
* 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(JitterBufferControl pqc : getJitterBufferControls())
if(pqc.getCurrentDelayPackets() > delay)
delay = pqc.getCurrentDelayPackets();
return delay;
}
/**
* 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;
* Returns the local IP address of the MediaStream.
* @return the local IP address of the stream.
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.
InetSocketAddress mediaStreamLocalDataAddress
= mediaStreamImpl.getLocalDataAddress();
return
(mediaStreamLocalDataAddress == null)
? -1
: mediaStreamLocalDataAddress.getPort();
* 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)
StreamRTPManager rtpManager = mediaStreamImpl.queryRTPManager();
long nbBytes = 0;
if(rtpManager != null)
{
switch(streamDirection)
{
case DOWNLOAD:
nbBytes = rtpManager.getGlobalReceptionStats().getBytesRecd();
break;
case UPLOAD:
nbBytes
= rtpManager.getGlobalTransmissionStats().getBytesSent();
break;
}
}
return nbBytes;
* 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.
int nbDiscarded = 0;
for(JitterBufferControl pqc : getJitterBufferControls())
nbDiscarded =+ pqc.getDiscarded();
return nbDiscarded;
* 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.
int nbDiscardedFull = 0;
for(JitterBufferControl pqc : getJitterBufferControls())
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.
int nbDiscardedLate = 0;
for(JitterBufferControl pqc : getJitterBufferControls())
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(JitterBufferControl pqc : getJitterBufferControls())
nbDiscardedReset =+ pqc.getDiscardedReset();
return nbDiscardedReset;
}
/**
* 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.
int nbDiscardedShrink = 0;
for(JitterBufferControl pqc : getJitterBufferControls())
nbDiscardedShrink =+ pqc.getDiscardedShrink();
return nbDiscardedShrink;
}
/**
* 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;
/**
* Returns the total number of packets that are send or receive for this
* stream since the stream is created.
* @return the total number of packets.
*/
public long getNbPackets()
{
return getNbPDU(StreamDirection.DOWNLOAD)
+ getDownloadNbPacketLost()
+ uploadFeedbackNbPackets;
}
/**
* Returns the number of lost packets for that stream.
* @return the number of lost packets.
*/
public long getNbPacketsLost()
{
return nbLost[StreamDirection.UPLOAD.ordinal()]
+ getDownloadNbPacketLost();
}
/**
* 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.queryRTPManager();
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;
}
@Override
public long getNbReceivedBytes()

hristoterezov
committed
{
AbstractRTPConnector connector = mediaStreamImpl.getRTPConnector();

hristoterezov
committed
if(connector != null)
RTPConnectorInputStream<?> stream;
try
{
stream = connector.getDataInputStream();
}
catch (IOException ex)
{
// We should not enter here because we are not creating stream.
stream = null;
}
if(stream != null)
return stream.getNumberOfReceivedBytes();
return 0;
}
@Override
public long getNbSentBytes()
AbstractRTPConnector connector = mediaStreamImpl.getRTPConnector();
if(connector == null)

Lyubomir Marinov
committed
{
return 0;
}
RTPConnectorOutputStream stream = null;
try
{
stream = connector.getDataOutputStream(false);
}
catch (IOException e)
{
//We should not enter here because we are not creating output stream
}
if(stream == null)
{
return 0;

Lyubomir Marinov
committed
}
return stream.getNumberOfBytesSent();
/**
* Returns the number of packets in the first <tt>JitterBufferControl</tt>
* found via <tt>getJitterBufferControls</tt>.
*
* @return the number of packets in the first <tt>JitterBufferControl</tt>
* found via <tt>getJitterBufferControls</tt>.
*/
public int getPacketQueueCountPackets()
{
for(JitterBufferControl pqc : getJitterBufferControls())
return pqc.getCurrentPacketCount();
return 0;

Boris Grozev
committed
}

Lyubomir Marinov
committed

Boris Grozev
committed
/**
* Returns the size of the first <tt>JitterBufferControl</tt> found via
* <tt>getJitterBufferControls</tt>.

Boris Grozev
committed
*
* @return the size of the first <tt>JitterBufferControl</tt> found via
* <tt>getJitterBufferControls</tt>.

Boris Grozev
committed
*/

Boris Grozev
committed
{
for(JitterBufferControl pqc : getJitterBufferControls())
return pqc.getCurrentSizePackets();
return 0;

Boris Grozev
committed
}

Boris Grozev
committed
/**
* Returns the percent of discarded packets

Boris Grozev
committed
*
* @return the percent of discarded packets

Boris Grozev
committed
*/

Boris Grozev
committed
{

Boris Grozev
committed
}
/**
* Returns the remote IP address of the MediaStream.

Boris Grozev
committed
*
* @return the remote IP address of the stream.

Boris Grozev
committed
*/

Boris Grozev
committed
{
MediaStreamTarget mediaStreamTarget = mediaStreamImpl.getTarget();
// Gets this stream IP address endpoint. Stops if the endpoint is
// disconnected.
return
(mediaStreamTarget == null)
? null
: mediaStreamTarget.getDataAddress().getAddress()
.getHostAddress();

Boris Grozev
committed
}
/**
* Returns the remote port of the MediaStream.

Boris Grozev
committed
*
* @return the remote port of the stream.

Boris Grozev
committed
*/

Boris Grozev
committed
{
MediaStreamTarget mediaStreamTarget = mediaStreamImpl.getTarget();
// Gets this stream port endpoint. Stops if the endpoint is
// disconnected.
return
(mediaStreamTarget == null)
? -1
: mediaStreamTarget.getDataAddress().getPort();
}
* {@inheritDoc}
*/
@Override
public RTCPReports getRTCPReports()
{
return rtcpReports;
}
/**
* 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.
/**
* Returns the jitter average of this upload stream.
*
* @return the last jitter average computed (in ms).
*/
public double getUploadJitterMs()
{
return getJitterMs(StreamDirection.UPLOAD);
* Returns the percent loss of the upload stream.
* @return the last loss rate computed (in %).
*/
public double getUploadPercentLoss()
{
return percentLoss[StreamDirection.UPLOAD.ordinal()];
}
/**
* Returns the bandwidth used by this download stream.
* @return the last used upload bandwidth computed (in Kbit/s).
public double getUploadRateKiloBitPerSec()
return rateKiloBitPerSec[StreamDirection.UPLOAD.ordinal()];
}
/**
* 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()
{
MediaDeviceSession deviceSession = mediaStreamImpl.getDeviceSession();
return
(deviceSession instanceof VideoMediaDeviceSession)
? ((VideoMediaDeviceSession) deviceSession)
.getSentVideoFormat()
: 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()
{
VideoFormat format = getUploadVideoFormat();
return (format == null) ? null : format.getSize();
}
public boolean isAdaptiveBufferEnabled()
{
for(JitterBufferControl pcq : getJitterBufferControls())
if(pcq.isAdaptiveBufferEnabled())
return true;
return false;
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
/**
* Sets a specific value on {@link #rttMs}. If there is an actual difference
* between the old and the new values, notifies the (known)
* <tt>CallStatsObserver</tt>s.
*
* @param rttMs the value to set on <tt>MediaStreamStatsImpl.rttMs</tt>
*/
private void setRttMs(long rttMs)
{
if (this.rttMs != rttMs)
{
this.rttMs = rttMs;
// Notify the CallStatsObservers.
rttMs = getRttMs();
if (rttMs >= 0)
{
// RemoteBitrateEstimator is a CallStatsObserver and
// VideoMediaStream has a RemoteBitrateEstimator.
MediaStream mediaStream = this.mediaStreamImpl;
if (mediaStream instanceof VideoMediaStream)
{
RemoteBitrateEstimator remoteBitrateEstimator
= ((VideoMediaStream) mediaStream)
.getRemoteBitrateEstimator();
if (remoteBitrateEstimator instanceof CallStatsObserver)
{
((CallStatsObserver) remoteBitrateEstimator)
.onRttUpdate(rttMs);
}
}
}
}
}
* Updates the jitter stream stats with the new feedback sent.