dynamicRTPPayloadType
: dynamicRTPPayloadTypes.entrySet())
{
@SuppressWarnings("unchecked")
MediaFormatImpl extends Format> mediaFormatImpl
= (MediaFormatImpl extends Format>)
dynamicRTPPayloadType.getValue();
rtpManager.addFormat(
mediaFormatImpl.getFormat(),
dynamicRTPPayloadType.getKey());
}
}
}
/**
* Notifies this MediaStream implementation that its
* RTPConnector instance has changed from a specific old value to a
* specific new value. Allows extenders to override and perform additional
* processing after this MediaStream has changed its
* RTPConnector instance.
*
* @param oldValue the RTPConnector of this MediaStream
* implementation before it got changed to newValue
* @param newValue the current RTPConnector of this
* MediaStream which replaced oldValue
*/
protected void rtpConnectorChanged(
AbstractRTPConnector oldValue,
AbstractRTPConnector newValue)
{
srtpControl.setConnector(newValue);
if (newValue != null)
{
/*
* Register the transform engines that we will be using in this
* stream.
*/
if(newValue instanceof RTPTransformUDPConnector)
((RTPTransformUDPConnector)newValue)
.setEngine(createTransformEngineChain());
else if(newValue instanceof RTPTransformTCPConnector)
((RTPTransformTCPConnector)newValue)
.setEngine(createTransformEngineChain());
if (rtpConnectorTarget != null)
doSetTarget(rtpConnectorTarget);
}
}
/**
* Sets the StreamConnector to be used by this instance for sending
* and receiving media.
*
* @param connector the StreamConnector to be used by this instance
* for sending and receiving media
*/
public void setConnector(StreamConnector connector)
{
if (connector == null)
throw new NullPointerException("connector");
if (rtpConnector != null)
{
// Is the StreamConnector really changing?
if (rtpConnector.getConnector() == connector)
return;
}
AbstractRTPConnector oldValue = rtpConnector;
if(connector.getProtocol() == StreamConnector.Protocol.UDP)
{
rtpConnector
= new RTPTransformUDPConnector(connector)
{
@Override
protected TransformUDPOutputStream createDataOutputStream()
throws IOException
{
TransformUDPOutputStream dataOutputStream
= super.createDataOutputStream();
if (dataOutputStream != null)
{
configureDataOutputStream(dataOutputStream);
}
return dataOutputStream;
}
@Override
protected TransformUDPInputStream createDataInputStream()
throws IOException
{
TransformUDPInputStream dataInputStream
= super.createDataInputStream();
if (dataInputStream != null)
{
configureDataInputStream(dataInputStream);
}
return dataInputStream;
}
};
}
else if(connector.getProtocol() == StreamConnector.Protocol.TCP)
{
rtpConnector
= new RTPTransformTCPConnector(connector)
{
@Override
protected TransformTCPOutputStream createDataOutputStream()
throws IOException
{
TransformTCPOutputStream dataOutputStream
= super.createDataOutputStream();
if (dataOutputStream != null)
configureDataOutputStream(dataOutputStream);
return dataOutputStream;
}
@Override
protected TransformTCPInputStream createDataInputStream()
throws IOException
{
TransformTCPInputStream dataInputStream
= super.createDataInputStream();
if (dataInputStream != null)
configureDataInputStream(dataInputStream);
return dataInputStream;
}
};
}
rtpConnectorChanged(oldValue, rtpConnector);
}
/**
* Sets the MediaDevice that this stream should use to play back
* and capture media.
*
* Note: Also resets any previous direction set with
* {@link #setDirection(MediaDirection)} to the direction of the specified
* MediaDevice.
*
*
* @param device the MediaDevice that this stream should use to
* play back and capture media
* @see MediaStream#setDevice(MediaDevice)
*/
public void setDevice(MediaDevice device)
{
if (device == null)
throw new NullPointerException("device");
// Require AbstractMediaDevice for MediaDeviceSession support.
AbstractMediaDevice abstractMediaDevice = (AbstractMediaDevice) device;
if ((deviceSession == null) || (deviceSession.getDevice() != device))
{
MediaDeviceSession oldValue = deviceSession;
MediaDirection startedDirection;
if (deviceSession != null)
{
startedDirection = deviceSession.getStartedDirection();
deviceSession.removePropertyChangeListener(
deviceSessionPropertyChangeListener);
// keep player active
deviceSession.setDisposePlayerOnClose(false);
deviceSession.close();
deviceSession = null;
}
else
{
startedDirection = MediaDirection.INACTIVE;
}
if(oldValue != null)
{
// transfer the rendering session objects (JMF player, ...)
// to the new MediaDeviceSession. So we do not have a
// reinitialization of the receive stream if we just change our
// device (switch from camera to desktop streaming).
deviceSession = abstractMediaDevice.createSession(oldValue);
}
else
{
deviceSession = abstractMediaDevice.createSession();
}
deviceSession.addPropertyChangeListener(
deviceSessionPropertyChangeListener);
/*
* Setting a new device resets any previously-set direction.
* Otherwise, we risk not being able to set a new device if it is
* mandatory for the new device to fully cover any previously-set
* direction.
*/
direction = null;
MediaDeviceSession newValue = deviceSession;
deviceSessionChanged(oldValue, newValue);
if (deviceSession != null)
{
deviceSession.setMute(mute);
deviceSession.start(startedDirection);
synchronized (receiveStreamSyncRoot)
{
if (receiveStream != null)
deviceSession.setReceiveStream(receiveStream);
}
}
}
}
/**
* Sets the direction in which media in this MediaStream is to be
* streamed. If this MediaStream is not currently started, calls to
* {@link #start()} later on will start it only in the specified
* direction. If it is currently started in a direction different
* than the specified, directions other than the specified will be stopped.
*
* @param direction the MediaDirection in which this
* MediaStream is to stream media when it is started
* @see MediaStream#setDirection(MediaDirection)
*/
public void setDirection(MediaDirection direction)
{
if (direction == null)
throw new NullPointerException("direction");
if(this.direction == direction)
return;
if(logger.isTraceEnabled())
logger.trace("Changing direction of stream " + hashCode()
+ " from:" + this.direction + " to:" + direction);
/*
* Make sure that the specified direction is in accord with the
* direction of the MediaDevice of this instance.
*/
MediaDeviceSession deviceSession = getDeviceSession();
MediaDirection deviceDirection
= (deviceSession == null)
? MediaDirection.INACTIVE
: deviceSession.getDevice().getDirection();
if (!deviceDirection.and(direction).equals(direction))
throw new IllegalArgumentException("direction");
this.direction = direction;
switch (this.direction)
{
case INACTIVE:
stop(MediaDirection.SENDRECV);
return;
case RECVONLY:
stop(MediaDirection.SENDONLY);
break;
case SENDONLY:
stop(MediaDirection.RECVONLY);
break;
case SENDRECV:
break;
default:
// Don't know what it may be (in the future) so ignore it.
return;
}
if (started)
start(this.direction);
}
/**
* Sets the MediaFormat that this MediaStream should
* transmit in.
*
* @param format the MediaFormat that this MediaStream
* should transmit in
* @see MediaStream#setFormat(MediaFormat)
*/
public void setFormat(MediaFormat format)
{
if (getDeviceSession() != null &&
getDeviceSession().getFormat() != null &&
getDeviceSession().getFormat().equals(format))
return;
if (logger.isTraceEnabled())
logger.trace(
"Changing format of stream " + hashCode()
+ " from: " + getDeviceSession().getFormat()
+ " to: " + format);
setAdvancedAttributes(format.getAdvancedAttributes());
handleAttributes(format, format.getAdvancedAttributes());
handleAttributes(format, format.getFormatParameters());
getDeviceSession().setFormat(format);
}
/**
* Handles attributes contained in MediaFormat.
*
* @param format the MediaFormat to handle the attributes of
* @param attrs the attributes Map to handle
*/
protected void handleAttributes(
MediaFormat format,
Map attrs)
{
}
/**
* Causes this MediaStream to stop transmitting the media being fed
* from this stream's MediaDevice and transmit "silence" instead.
* "Silence" for video is understood as video data which is not the captured
* video data and may represent, for example, a black image.
*
* @param mute true to have this MediaStream transmit
* "silence" instead of the actual media data that it captures from its
* MediaDevice; false to transmit actual media data
* captured from the MediaDevice of this MediaStream
* @see MediaStream#setMute(boolean)
*/
public void setMute(boolean mute)
{
if (this.mute != mute)
{
if(logger.isTraceEnabled())
logger.trace((mute? "Muting" : "Unmuting")
+ " stream with hashcode " + hashCode());
this.mute = mute;
MediaDeviceSession deviceSession = getDeviceSession();
if (deviceSession != null)
deviceSession.setMute(this.mute);
}
}
/**
* Returns the target of this MediaStream to which it is to send
* and from which it is to receive data (e.g. RTP) and control data (e.g.
* RTCP).
*
* @return the MediaStreamTarget describing the data
* (e.g. RTP) and the control data (e.g. RTCP) locations to which this
* MediaStream is to send and from which it is to receive
* @see MediaStream#setTarget(MediaStreamTarget)
*/
public MediaStreamTarget getTarget()
{
return rtpConnectorTarget;
}
/**
* Sets the target of this MediaStream to which it is to send and
* from which it is to receive data (e.g. RTP) and control data (e.g. RTCP).
*
* @param target the MediaStreamTarget describing the data
* (e.g. RTP) and the control data (e.g. RTCP) locations to which this
* MediaStream is to send and from which it is to receive
* @see MediaStream#setTarget(MediaStreamTarget)
*/
public void setTarget(MediaStreamTarget target)
{
// Short-circuit if setting the same target.
if (target == null)
{
if (rtpConnectorTarget == null)
return;
}
else if (target.equals(rtpConnectorTarget))
return;
doSetTarget(target);
}
/**
* Starts capturing media from this stream's MediaDevice and then
* streaming it through the local StreamConnector toward the
* stream's target address and port. Also puts the MediaStream in a
* listening state which make it play all media received from the
* StreamConnector on the stream's MediaDevice.
*
* @see MediaStream#start()
*/
public void start()
{
start(getDirection());
started = true;
}
/**
* Starts the processing of media in this instance in a specific direction.
*
* @param direction a MediaDirection value which represents the
* direction of the processing of media to be started. For example,
* {@link MediaDirection#SENDRECV} to start both capture and playback of
* media in this instance or {@link MediaDirection#SENDONLY} to only start
* the capture of media in this instance
*/
private void start(MediaDirection direction)
{
if (direction == null)
throw new NullPointerException("direction");
if (direction.allowsSending()
&& ((startedDirection == null)
|| !startedDirection.allowsSending()))
{
startSendStreams();
getDeviceSession().start(MediaDirection.SENDONLY);
if (MediaDirection.RECVONLY.equals(startedDirection))
startedDirection = MediaDirection.SENDRECV;
else if (startedDirection == null)
startedDirection = MediaDirection.SENDONLY;
}
if (direction.allowsReceiving()
&& ((startedDirection == null)
|| !startedDirection.allowsReceiving()))
{
startReceiveStreams();
getDeviceSession().start(MediaDirection.RECVONLY);
if (MediaDirection.SENDONLY.equals(startedDirection))
startedDirection = MediaDirection.SENDRECV;
else if (startedDirection == null)
startedDirection = MediaDirection.RECVONLY;
}
}
/**
* Starts the ReceiveStreams that this instance is receiving from
* its remote peer. By design, a MediaStream instance is associated
* with a single ReceiveStream at a time. However, the
* ReceiveStreams are created by RTPManager and it tracks
* multiple ReceiveStreams. In practice, the RTPManager of
* this MediaStreamImpl will have a single ReceiveStream
* in its list.
*/
@SuppressWarnings("unchecked")
private void startReceiveStreams()
{
RTPManager rtpManager = getRTPManager();
List receiveStreams;
try
{
receiveStreams = rtpManager.getReceiveStreams();
}
catch (Exception ex)
{
/*
* It appears that in early call states when there are no streams, a
* NullPointerException could be thrown. Make sure we handle it
* gracefully.
*/
if (logger.isTraceEnabled())
logger.trace("Failed to retrieve receive streams", ex);
receiveStreams = null;
}
if (receiveStreams != null)
{
// receiveStreams coming from rtp manager can be empty
// we do not receive any rtcp from other side,
// than we use local stored receive stream
if(receiveStreams.size() == 0)
{
if(receiveStream != null)
{
try
{
DataSource receiveStreamDataSource
= receiveStream.getDataSource();
/*
* For an unknown reason, the stream DataSource can be null
* at the end of the Call after re-INVITEs have been
* handled.
*/
if (receiveStreamDataSource != null)
receiveStreamDataSource.start();
}
catch (IOException ioex)
{
logger.warn(
"Failed to start stream " + receiveStream,
ioex);
}
}
}
else
{
for (ReceiveStream receiveStream : receiveStreams)
{
try
{
DataSource receiveStreamDataSource
= receiveStream.getDataSource();
/*
* For an unknown reason, the stream DataSource can be
* null at the end of the Call after re-INVITEs have
* been handled.
*/
if (receiveStreamDataSource != null)
receiveStreamDataSource.start();
}
catch (IOException ioex)
{
logger.warn(
"Failed to start stream " + receiveStream,
ioex);
}
}
}
}
}
/**
* Starts the SendStreams of the RTPManager of this
* MediaStreamImpl.
*/
private void startSendStreams()
{
/*
* Until it's clear that the SendStreams are required (i.e. we've
* negotiated to send), they will not be created. Otherwise, their
* creation isn't only illogical but also causes the CaptureDevice to
* be used.
*/
if (!sendStreamsAreCreated)
createSendStreams();
RTPManager rtpManager = getRTPManager();
@SuppressWarnings("unchecked")
Iterable sendStreams = rtpManager.getSendStreams();
if (sendStreams != null)
{
for (SendStream sendStream : sendStreams)
{
try
{
// TODO Are we sure we want to connect here?
sendStream.getDataSource().connect();
sendStream.start();
sendStream.getDataSource().start();
if (logger.isTraceEnabled())
{
logger.trace(
"Started SendStream with hashCode "
+ sendStream.hashCode());
}
}
catch (IOException ioe)
{
logger
.warn("Failed to start stream " + sendStream, ioe);
}
}
}
}
/**
* Stops all streaming and capturing in this MediaStream and closes
* and releases all open/allocated devices/resources. Has no effect if this
* MediaStream is already closed and is simply ignored.
*
* @see MediaStream#stop()
*/
public void stop()
{
stop(MediaDirection.SENDRECV);
started = false;
}
/**
* Stops the processing of media in this instance in a specific direction.
*
* @param direction a MediaDirection value which represents the
* direction of the processing of media to be stopped. For example,
* {@link MediaDirection#SENDRECV} to stop both capture and playback of
* media in this instance or {@link MediaDirection#SENDONLY} to only stop
* the capture of media in this instance
*/
private void stop(MediaDirection direction)
{
if (direction == null)
throw new NullPointerException("direction");
if (rtpManager == null)
return;
if ((MediaDirection.SENDRECV.equals(direction)
|| MediaDirection.SENDONLY.equals(direction))
&& (MediaDirection.SENDRECV.equals(startedDirection)
|| MediaDirection.SENDONLY.equals(startedDirection)))
{
stopSendStreams(false);
if (deviceSession != null)
deviceSession.stop(MediaDirection.SENDONLY);
if (MediaDirection.SENDRECV.equals(startedDirection))
startedDirection = MediaDirection.RECVONLY;
else if (MediaDirection.SENDONLY.equals(startedDirection))
startedDirection = null;
}
if ((MediaDirection.SENDRECV.equals(direction)
|| MediaDirection.RECVONLY.equals(direction))
&& (MediaDirection.SENDRECV.equals(startedDirection)
|| MediaDirection.RECVONLY.equals(startedDirection)))
{
stopReceiveStreams();
if (deviceSession != null)
deviceSession.stop(MediaDirection.RECVONLY);
if (MediaDirection.SENDRECV.equals(startedDirection))
startedDirection = MediaDirection.SENDONLY;
else if (MediaDirection.RECVONLY.equals(startedDirection))
startedDirection = null;
}
}
/**
* Stops the ReceiveStreams that this instance is receiving from
* its remote peer. By design, a MediaStream instance is associated
* with a single ReceiveStream at a time. However, the
* ReceiveStreams are created by RTPManager and it tracks
* multiple ReceiveStreams. In practice, the RTPManager of
* this MediaStreamImpl will have a single ReceiveStream
* in its list.
*/
@SuppressWarnings("unchecked")
private void stopReceiveStreams()
{
List receiveStreams;
try
{
receiveStreams = rtpManager.getReceiveStreams();
}
catch (Exception ex)
{
/*
* It appears that in early call states when there are no streams, a
* NullPointerException could be thrown. Make sure we handle it
* gracefully.
*/
if (logger.isTraceEnabled())
logger.trace("Failed to retrieve receive streams", ex);
receiveStreams = null;
}
if (receiveStreams != null)
{
// receiveStreams coming from rtp manager can be empty
// we do not receive any rtcp from other side,
// than we use local stored receive stream
if(receiveStreams.size() == 0)
{
if(receiveStream != null)
{
try
{
if(logger.isTraceEnabled())
logger.trace("Stopping receive stream with hashcode "
+ receiveStream.hashCode());
DataSource receiveStreamDataSource
= receiveStream.getDataSource();
/*
* For an unknown reason, the stream DataSource can be
* null at the end of the Call after re-INVITEs have
* been handled.
*/
if (receiveStreamDataSource != null)
receiveStreamDataSource.stop();
}
catch (IOException ioex)
{
logger.warn("Failed to stop stream "
+ receiveStream, ioex);
}
}
}
else
{
for (ReceiveStream receiveStream : receiveStreams)
{
try
{
if(logger.isTraceEnabled())
logger.trace("Stopping receive stream with hashcode "
+ receiveStream.hashCode());
DataSource receiveStreamDataSource
= receiveStream.getDataSource();
/*
* For an unknown reason, the stream DataSource can be null
* at the end of the Call after re-INVITEs have been
* handled.
*/
if (receiveStreamDataSource != null)
receiveStreamDataSource.stop();
}
catch (IOException ioex)
{
logger.warn("Failed to stop stream " + receiveStream, ioex);
}
}
}
}
}
/**
* Stops the SendStreams that this instance is sending to its
* remote peer and optionally closes them.
*
* @param close true to close the SendStreams that this
* instance is sending to its remote peer after stopping them;
* false to only stop them
* @return the SendStreams which were stopped
*/
private Iterable stopSendStreams(boolean close)
{
if (rtpManager == null)
return null;
@SuppressWarnings("unchecked")
Iterable sendStreams = rtpManager.getSendStreams();
Iterable stoppedSendStreams
= stopSendStreams(sendStreams, close);
if (close)
sendStreamsAreCreated = false;
return stoppedSendStreams;
}
/**
* Stops specific SendStreams and optionally closes them.
*
* @param sendStreams the SendStreams to be stopped and optionally
* closed
* @param close true to close the specified SendStreams
* after stopping them; false to only stop them
* @return the stopped SendStreams
*/
private Iterable stopSendStreams(
Iterable sendStreams,
boolean close)
{
if (sendStreams == null)
return null;
for (SendStream sendStream : sendStreams)
try
{
if(logger.isTraceEnabled())
logger.trace("Stopping send stream with hashcode "
+ sendStream.hashCode());
sendStream.getDataSource().stop();
sendStream.stop();
if (close)
try
{
sendStream.close();
}
catch (NullPointerException npe)
{
/*
* Sometimes com.sun.media.rtp.RTCPTransmitter#bye() may
* throw NullPointerException but it does not seem to be
* guaranteed because it does not happen while debugging
* and stopping at a breakpoint on SendStream#close().
* One of the cases in which it appears upon call
* hang-up is if we do not close the "old" SendStreams
* upon reinvite(s). Though we are now closing such
* SendStreams, ignore the exception here just in case
* because we already ignore IOExceptions.
*/
logger
.error(
"Failed to close stream " + sendStream,
npe);
}
}
catch (IOException ioe)
{
logger.warn("Failed to stop stream " + sendStream, ioe);
}
return sendStreams;
}
/**
* Returns a human-readable representation of a specific DataSource
* instance in the form of a String value.
*
* @param dataSource the DataSource to return a human-readable
* representation of
* @return a String value which gives a human-readable
* representation of the specified dataSource
*/
public static String toString(DataSource dataSource)
{
StringBuffer str = new StringBuffer();
str.append(dataSource.getClass().getSimpleName());
str.append(" with hashCode ");
str.append(dataSource.hashCode());
MediaLocator locator = dataSource.getLocator();
if (locator != null)
{
str.append(" and locator ");
str.append(locator);
}
return str.toString();
}
/**
* Notifies this ReceiveStreamListener that the RTPManager
* it is registered with has generated an event related to a ReceiveStream.
*
* @param event the ReceiveStreamEvent which specifies the
* ReceiveStream that is the cause of the event and the very type
* of the event
* @see ReceiveStreamListener#update(ReceiveStreamEvent)
*/
public void update(ReceiveStreamEvent event)
{
if (event instanceof NewReceiveStreamEvent)
{
ReceiveStream receiveStream = event.getReceiveStream();
if (receiveStream != null)
{
long receiveStreamSSRC = receiveStream.getSSRC();
if (logger.isTraceEnabled())
{
logger.trace(
"Received new ReceiveStream with ssrc "
+ receiveStreamSSRC);
}
setRemoteSourceID(receiveStreamSSRC);
synchronized (receiveStreamSyncRoot)
{
if (this.receiveStream != receiveStream)
{
this.receiveStream = receiveStream;
MediaDeviceSession deviceSession = getDeviceSession();
if (deviceSession != null)
deviceSession.setReceiveStream(this.receiveStream);
}
}
}
}
else if (event instanceof TimeoutEvent)
{
ReceiveStream receiveStream = event.getReceiveStream();
/*
* If we recreate streams, we will already have restarted
* zrtpControl. But when on the other end someone recreates his
* streams, we will receive a ByeEvent (which extends TimeoutEvent)
* and then we must also restart our ZRTP. This happens, for
* example, when we are already in a call and the remote peer
* converts his side of the call into a conference call.
*/
/*
if(!zrtpRestarted)
restartZrtpControl();
*/
if (receiveStream != null)
{
synchronized (receiveStreamSyncRoot)
{
if (this.receiveStream == receiveStream)
{
this.receiveStream = null;
MediaDeviceSession deviceSession = getDeviceSession();
if (deviceSession != null)
deviceSession.setReceiveStream(null);
}
}
}
}
}
/**
* Notifies this SendStreamListener that the RTPManager it
* is registered with has generated an event related to a SendStream.
*
* @param event the SendStreamEvent which specifies the
* SendStream that is the cause of the event and the very type of
* the event
* @see SendStreamListener#update(SendStreamEvent)
*/
public void update(SendStreamEvent event)
{
if ((event instanceof NewSendStreamEvent)
&& (event.getSendStream().getSSRC() != this.localSourceID))
setLocalSourceID(event.getSendStream().getSSRC());
}
/**
* Notifies this SessionListener that the RTPManager it is
* registered with has generated an event which pertains to the session as a
* whole and does not belong to a ReceiveStream or a
* SendStream or a remote participant necessarily.
*
* @param event the SessionEvent which specifies the source and the
* very type of the event
* @see SessionListener#update(SessionEvent)
*/
public void update(SessionEvent event)
{
// TODO Auto-generated method stub
}
/**
* Method called back in the RemoteListener to notify
* listener of all RTP Remote Events.RemoteEvents are one of
* ReceiverReportEvent, SenderReportEvent or RemoteCollisionEvent
*
* @param remoteEvent the event
*/
public void update(RemoteEvent remoteEvent)
{
if(!logger.isInfoEnabled())
return;
if(remoteEvent instanceof SenderReportEvent)
{
numberOfReceivedSenderReports++;
SenderReport report =
((SenderReportEvent)remoteEvent).getReport();
if(report.getFeedbackReports().size() > 0)
{
Feedback feedback =
(Feedback)report.getFeedbackReports().get(0);
long remoteJitter = feedback.getJitter();
if(remoteJitter < minRemoteInterArrivalJitter
||minRemoteInterArrivalJitter == -1)
minRemoteInterArrivalJitter = remoteJitter;
if(maxRemoteInterArrivalJitter < remoteJitter)
maxRemoteInterArrivalJitter = remoteJitter;
// As sender reports are received on every 5 seconds
// print every 4th packet, on every 20 seconds
if(numberOfReceivedSenderReports%4 != 1)
return;
StringBuilder buff
= new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX);
MediaFormat format = getFormat();
buff.append("Received a report for ")
.append(
(format == null)
? ""
: format.getMediaType().toString())
.append(" stream SSRC:")
.append(getLocalSourceID())
.append(" [packet count:")
.append(report.getSenderPacketCount())
.append(", bytes:").append(report.getSenderByteCount())
.append(", interarrival jitter:")
.append(remoteJitter)
.append(", lost packets:").append(feedback.getNumLost())
.append(", time since previous report:")
.append((int)(feedback.getDLSR()/65.536))
.append("ms ]");
logger.info(buff);
}
}
}
/**
* Sets the local SSRC identifier and fires the corresponding
* PropertyChangeEvent.
*
* @param ssrc the SSRC identifier that this stream will be using in
* outgoing RTP packets from now on.
*/
protected void setLocalSourceID(long ssrc)
{
Long oldValue = this.localSourceID;
this.localSourceID = ssrc;
firePropertyChange(PNAME_LOCAL_SSRC, oldValue, ssrc);
}
/**
* Sets the remote SSRC identifier and fires the corresponding
* PropertyChangeEvent.
*
* @param ssrc the SSRC identifier that this stream will be using in
* outgoing RTP packets from now on.
*/
protected void setRemoteSourceID(long ssrc)
{
Long oldValue = this.remoteSourceID;
this.remoteSourceID = ssrc;
firePropertyChange(PNAME_REMOTE_SSRC, oldValue, ssrc);
}
/**
* Returns the list of CSRC identifiers for all parties currently known
* to contribute to the media that this stream is sending toward its remote
* counter part. In other words, the method returns the list of CSRC IDs
* that this stream will include in outgoing RTP packets. This method will
* return an null in case this stream is not part of a mixed
* conference call.
*
* @return a long[] array of CSRC IDs representing parties that are
* currently known to contribute to the media that this stream is sending
* or an null in case this MediaStream is not part of a
* conference call.
*/
public long[] getLocalContributingSourceIDs()
{
return localContributingSourceIDList;
}
/**
* Returns the List of CSRC identifiers representing the parties
* contributing to the stream that we are receiving from this
* MediaStream's remote party.
*
* @return a List of CSRC identifiers representing the parties
* contributing to the stream that we are receiving from this
* MediaStream's remote party.
*/
public long[] getRemoteContributingSourceIDs()
{
long[] remoteSsrcList = getDeviceSession().getRemoteSSRCList();
// TODO implement
return remoteSsrcList;
}
/**
* Used to set the priority of the receive/send streams. Underling
* implementations can override this and return different than
* current default value.
*
* @return the priority for the current thread.
*/
protected int getPriority()
{
return Thread.currentThread().getPriority();
}
/**
* Prints all statistics available for rtpManager.
*
* @param rtpManager the RTP manager that we'd like to print statistics for.
*/
private void printFlowStatistics(RTPManager rtpManager)
{
try
{
if(!logger.isInfoEnabled())
return;
//print flow statistics.
GlobalTransmissionStats s = rtpManager.getGlobalTransmissionStats();
StringBuilder buff =
new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX);
buff.append("call stats for outgoing ")
.append(getFormat() != null ? getFormat().getMediaType() : "")
.append(" stream SSRC:")
.append(getLocalSourceID())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("bytes sent: ").append(s.getBytesSent())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("RTP sent: ").append(s.getRTPSent())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("remote reported min interarrival jitter : ")
.append(minRemoteInterArrivalJitter)
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("remote reported max interarrival jitter : ")
.append(maxRemoteInterArrivalJitter)
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("local collisions: ").append(s.getLocalColls())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("remote collisions: ").append(s.getRemoteColls())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("RTCP sent: ").append(s.getRTCPSent())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("transmit failed: ").append(s.getTransmitFailed());
logger.info(buff);
GlobalReceptionStats rs = rtpManager.getGlobalReceptionStats();
buff = new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX);
buff.append("call stats for incoming ")
.append(getFormat().getMediaType()).append(" stream SSRC:")
.append(getRemoteSourceID())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("packets received: ").append(rs.getPacketsRecd())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("bytes received: ").append(rs.getBytesRecd())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("packets lost: ").append(statisticsEngine.getLost())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("min interarrival jitter : ")
.append(statisticsEngine.getMinInterArrivalJitter())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("max interarrival jitter : ")
.append(statisticsEngine.getMaxInterArrivalJitter())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("RTCPs received: ").append(rs.getRTCPRecd())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("bad RTCP packets: ").append(rs.getBadRTCPPkts())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("bad RTP packets: ").append(rs.getBadRTPkts())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("local collisions: ").append(rs.getLocalColls())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("malformed BYEs: ").append(rs.getMalformedBye())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("malformed RRs: ").append(rs.getMalformedRR())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("malformed SDESs: ").append(rs.getMalformedSDES())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("malformed SRs: ").append(rs.getMalformedSR())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("packets looped: ").append(rs.getPacketsLooped())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("remote collisions: ").append(rs.getRemoteColls())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("SRRs received: ").append(rs.getSRRecd())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("transmit failed: ").append(rs.getTransmitFailed())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("unknown types: ").append(rs.getUnknownTypes());
logger.info(buff);
}
catch(Throwable t)
{
logger.error("Error writing statistics", t);
}
}
}