diff --git a/lib/installer-exclude/libjitsi.jar b/lib/installer-exclude/libjitsi.jar
index ec632cdba..0ea822b6c 100644
Binary files a/lib/installer-exclude/libjitsi.jar and b/lib/installer-exclude/libjitsi.jar differ
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
index 9ee762b83..901b04893 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
@@ -722,6 +722,7 @@ private void doUpdateViewFromModelInEventDispatchThread()
boolean isConference = isConference();
boolean isVideo = CallManager.isVideoStreaming(callConference);
CallPeer callPeer = null;
+ boolean validateAndRepaint = false;
if (callPanel != null)
{
@@ -770,6 +771,7 @@ private void doUpdateViewFromModelInEventDispatchThread()
if (removeCallPanel)
{
remove(callPanel);
+ validateAndRepaint = true;
try
{
((CallRenderer) callPanel).dispose();
@@ -814,15 +816,39 @@ private void doUpdateViewFromModelInEventDispatchThread()
}
}
if (callPanel != null)
+ {
add(callPanel, BorderLayout.CENTER);
+ validateAndRepaint = true;
+ }
}
- /*
- * The center of this view is occupied by callPanel and we have just
- * updated it. The bottom of this view is dedicated to settingsPanel so
- * we have to update it as well.
- */
- updateSettingsPanelInEventDispatchThread(false);
+ try
+ {
+ /*
+ * The center of this view is occupied by callPanel and we have just
+ * updated it. The bottom of this view is dedicated to settingsPanel
+ * so we have to update it as well.
+ */
+ updateSettingsPanelInEventDispatchThread(false);
+ }
+ finally
+ {
+ /*
+ * It seems that AWT/Swing does not validate and/or repaint this
+ * Container (enough) and, consequently, its display may not update
+ * itself with an up-to-date drawing of the current callPanel.
+ */
+ if (validateAndRepaint)
+ {
+ if (isDisplayable())
+ {
+ validate();
+ repaint();
+ }
+ else
+ doLayout();
+ }
+ }
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
index 60f418b0e..d8a2c3023 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
@@ -1161,9 +1161,6 @@ private void updateViewFromModelInEventDispatchThread()
}
this.localVideo = localVideo;
}
-
- center.validate();
- center.repaint();
}
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler2.java b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler2.java
index 8cbb1ed7d..1fc5bbdde 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler2.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler2.java
@@ -96,17 +96,17 @@ protected void callConferenceCallsPropertyChange(PropertyChangeEvent ev)
}
/**
- * Notifies this instance about a change in the value of the
- * videoSsrc property of a ConferenceMember. Changing the
- * value in question means that a visual Component displaying video
- * may be associated or dissociated with the ConferenceMember.
+ * Notifies this instance about a change in the value of a video-related
+ * property of a ConferenceMember. Changing such a value means that
+ * a visual Component displaying video may be associated or
+ * dissociated with the ConferenceMember.
*
* @param ev a PropertyChangeEvent which specifies the
- * ConferenceMember whose videoSsrc property value changed
- * and the old and new values of the property in question
+ * ConferenceMember whose video-related property value changed, the
+ * name of the property whose value changed, and the old and new values of
+ * the property in question
*/
- protected void conferenceMemberVideoSsrcPropertyChange(
- PropertyChangeEvent ev)
+ protected void conferenceMemberVideoPropertyChange(PropertyChangeEvent ev)
{
notifyObservers(ev);
}
@@ -488,10 +488,12 @@ public void propertyChange(PropertyChangeEvent ev)
}
}
else if (ConferenceMember.VIDEO_SSRC_PROPERTY_NAME.equals(
- propertyName))
+ propertyName)
+ || ConferenceMember.VIDEO_STATUS_PROPERTY_NAME.equals(
+ propertyName))
{
if (ev.getSource() instanceof ConferenceMember)
- conferenceMemberVideoSsrcPropertyChange(ev);
+ conferenceMemberVideoPropertyChange(ev);
}
else if (OperationSetVideoTelephony.LOCAL_VIDEO_STREAMING.equals(
propertyName))
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
index d2f97bed2..ce249893a 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/BasicConferenceCallPanel.java
@@ -326,14 +326,16 @@ protected void updateViewFromModel()
/**
* Updates the ConferenceCallPeerRenderer which is to depict a
- * specific CallPeer.
+ * specific CallPeer. Invoked by
+ * {@link #updateViewFromModelInEventDispatchThread()} in the AWT event
+ * dispatching thread.
*
* @param callPeer the CallPeer whose depicting
* ConferenceCallPeerPanel is to be updated. The null
* value is used to indicate the local peer.
* @see #updateViewFromModel(ConferenceCallPeerRenderer, CallPeer)
*/
- private void updateViewFromModel(CallPeer callPeer)
+ protected void updateViewFromModel(CallPeer callPeer)
{
ConferenceCallPeerRenderer oldCallPeerPanel
= callPeerPanels.get(callPeer);
@@ -399,7 +401,7 @@ protected abstract ConferenceCallPeerRenderer updateViewFromModel(
*/
protected void updateViewFromModelInEventDispatchThread()
{
- /* Update the view of the local peer. */
+ /* Update the view of the local peer/user. */
updateViewFromModel(null);
List callPeers = callConference.getCallPeers();
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
index fedcb500c..c03cddae0 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
@@ -66,6 +66,13 @@ public void update(Observable o, Object arg)
*/
private final VideoContainer videoContainer;
+ /**
+ * The set of visual Components displaying video streaming between
+ * the local peer/user and the remote peers which are depicted by this
+ * instance.
+ */
+ private final Set videos = new HashSet();
+
/**
* Initializes a new VideoConferenceCallPanel instance which is to
* be used by a specific CallPanel to depict a specific
@@ -573,7 +580,28 @@ else if (cmcParticipant == conferenceMember)
{
if (cmc.getParticipant() == conferenceMember)
{
- if (cmc.toBeRemoved)
+ /*
+ * It is possible to have a ConferenceMember who is
+ * sending video but we just do not have the SSRC of
+ * that video to associate the video with the
+ * ConferenceMember. In such a case, we may be depicting
+ * the ConferenceMember twice: once with video without a
+ * ConferenceMember and once with a ConferenceMember
+ * without video. This will surely be the case at the
+ * time of this writing with non-focus participants in a
+ * telephony conference hosted on a Jitsi VideoBridge.
+ * Such a display is undesirable. If the
+ * conferenceMember is known to send video, we will not
+ * display it until we associated it with a video. This
+ * way, if a ConferenceMember is not sending video, we
+ * will depict it and we can be sure that no video
+ * without a ConferenceMember association will be
+ * depicting it a second time.
+ */
+ if (cmc.toBeRemoved
+ && !conferenceMember
+ .getVideoStatus()
+ .allowsSending())
{
cmc.setVideo(null);
cmc.toBeRemoved = false;
@@ -613,7 +641,6 @@ else if (cmcParticipant == conferenceMember)
}
}
- @Override
protected ConferenceCallPeerRenderer updateViewFromModel(
ConferenceCallPeerRenderer callPeerPanel,
CallPeer callPeer)
@@ -748,6 +775,96 @@ protected ConferenceCallPeerRenderer updateViewFromModel(
return callPeerPanel;
}
+ @Override
+ protected void updateViewFromModelInEventDispatchThread()
+ {
+ /*
+ * Determine the set of visual Components displaying video streaming
+ * between the local peer/user and the remote peers which are to be
+ * depicted by this instance.
+ */
+ Component localVideo = null;
+ Set videos = new HashSet();
+
+ for (Call call : callConference.getCalls())
+ {
+ OperationSetVideoTelephony videoTelephony
+ = call.getProtocolProvider().getOperationSet(
+ OperationSetVideoTelephony.class);
+
+ if (videoTelephony == null)
+ continue;
+
+ Iterator extends CallPeer> callPeerIter = call.getCallPeers();
+
+ while (callPeerIter.hasNext())
+ {
+ CallPeer callPeer = callPeerIter.next();
+
+ if (uiVideoHandler.isLocalVideoVisible()
+ && (localVideo == null))
+ {
+ try
+ {
+ localVideo
+ = videoTelephony.getLocalVisualComponent(callPeer);
+ }
+ catch (OperationFailedException ofe)
+ {
+ /*
+ * We'll just try to get the local video through another
+ * CallPeer then.
+ */
+ }
+ if (localVideo != null)
+ videos.add(localVideo);
+ }
+
+ List callPeerRemoteVideos
+ = videoTelephony.getVisualComponents(callPeer);
+
+ videos.addAll(callPeerRemoteVideos);
+ }
+ }
+
+ /*
+ * Remove the Components of this view which are no longer present in the
+ * model.
+ */
+ Iterator thisVideoIter = this.videos.iterator();
+
+ while (thisVideoIter.hasNext())
+ {
+ Component thisVideo = thisVideoIter.next();
+
+ if (!videos.contains(thisVideo))
+ {
+ thisVideoIter.remove();
+ videoContainer.remove(thisVideo);
+ }
+
+ /*
+ * If a video is known to be depicted by this view and is still
+ * present in the model, then we could remove it from the set of
+ * videos present in the model in order to prevent going through the
+ * procedure of adding it to this view. However, we choose to play
+ * on the safe side.
+ */
+ }
+
+ /*
+ * Add the Components of the model which are not depicted by this view.
+ */
+ for (Component video : videos)
+ {
+ if (!UIVideoHandler2.isAncestor(videoContainer, video))
+ {
+ this.videos.add(video);
+ videoContainer.add(video, VideoLayout.CENTER_REMOTE);
+ }
+ }
+ }
+
@Override
protected void viewForModelAdded(
ConferenceCallPeerRenderer callPeerPanel,
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
index df8d308de..511cc4399 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
@@ -164,7 +164,7 @@ private List getMedia(
= new MediaPacketExtension(Long.toString(i));
long srcId
= remote
- ? stream.getRemoteSourceID()
+ ? getRemoteSourceID(callPeer, mediaType)
: stream.getLocalSourceID();
if (srcId != -1)
@@ -172,7 +172,10 @@ private List getMedia(
ext.setType(mediaType.toString());
- MediaDirection direction = stream.getDirection();
+ MediaDirection direction
+ = remote
+ ? getRemoteDirection(callPeer, mediaType)
+ : stream.getDirection();
if (direction == null)
direction = MediaDirection.INACTIVE;
@@ -290,7 +293,7 @@ private IQ getConferenceInfo(CallPeerJabberImpl callPeer, int version)
iq.setEntity(getBasicTelephony().getProtocolProvider().getOurJID());
iq.setVersion(version);
iq.setState(StateType.full);
- iq.setSID(callPeer.getSID());
+ iq.setSID(callPeerSID);
// conference-description
iq.addExtension(new DescriptionPacketExtension());
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyConferencingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyConferencingSipImpl.java
index bf562a160..39ec45cf4 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyConferencingSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyConferencingSipImpl.java
@@ -453,7 +453,7 @@ private void getMediaXML(
long srcId
= remote
- ? stream.getRemoteSourceID()
+ ? getRemoteSourceID(callPeer, mediaType)
: stream.getLocalSourceID();
if (srcId != -1)
@@ -465,7 +465,10 @@ private void getMediaXML(
append(xml, "", ELEMENT_SRC_ID, ">");
}
- MediaDirection direction = stream.getDirection();
+ MediaDirection direction
+ = remote
+ ? getRemoteDirection(callPeer, mediaType)
+ : stream.getDirection();
if (direction == null)
direction = MediaDirection.INACTIVE;
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java b/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
index a3e423ea4..072e2eb3c 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
+++ b/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
@@ -6,6 +6,9 @@
*/
package net.java.sip.communicator.service.protocol;
+import java.util.*;
+
+import org.jitsi.service.neomedia.*;
import org.jitsi.util.event.*;
/**
@@ -75,6 +78,12 @@ public class AbstractConferenceMember
*/
private long audioSsrc = -1;
+ /**
+ * The status in both directions of the audio RTP stream from the point of
+ * view of this ConferenceMember.
+ */
+ private MediaDirection audioStatus = MediaDirection.INACTIVE;
+
/**
* The CallPeer which is the conference focus of this
* ConferenceMember.
@@ -98,6 +107,12 @@ public class AbstractConferenceMember
*/
private long videoSsrc = -1;
+ /**
+ * The status in both directions of the video RTP stream from the point of
+ * view of this ConferenceMember.
+ */
+ private MediaDirection videoStatus = MediaDirection.INACTIVE;
+
/**
* Creates an instance of AbstractConferenceMember by specifying
* the corresponding conferenceFocusCallPeer, to which this member
@@ -142,6 +157,14 @@ public long getAudioSsrc()
return audioSsrc;
}
+ /**
+ * {@inheritDoc}
+ */
+ public MediaDirection getAudioStatus()
+ {
+ return audioStatus;
+ }
+
/**
* {@inheritDoc}
*
@@ -191,6 +214,56 @@ public long getVideoSsrc()
return videoSsrc;
}
+ /**
+ * {@inheritDoc}
+ */
+ public MediaDirection getVideoStatus()
+ {
+ return videoStatus;
+ }
+
+ private static long parseMediaSssrc(Object value)
+ {
+ long ssrc;
+
+ if (value == null)
+ ssrc = -1;
+ else if (value instanceof Long)
+ ssrc = ((Long) value).longValue();
+ else
+ {
+ String str = value.toString();
+
+ if ((str == null) || (str.length() == 0))
+ ssrc = -1;
+ else
+ ssrc = Long.parseLong(str);
+ }
+
+ return ssrc;
+ }
+
+ private static MediaDirection parseMediaStatus(Object value)
+ {
+ MediaDirection status;
+
+ if (value == null)
+ status = MediaDirection.INACTIVE;
+ else if (value instanceof MediaDirection)
+ status = (MediaDirection) value;
+ else
+ {
+ String str = value.toString();
+
+ if ((str == null) || (str.length() == 0))
+ status = MediaDirection.INACTIVE;
+ else
+ status = MediaDirection.parseString(str);
+ }
+
+ return status;
+ }
+
/**
* Sets the audio SSRC identifier of this member.
*
@@ -198,7 +271,41 @@ public long getVideoSsrc()
*/
public void setAudioSsrc(long ssrc)
{
- this.audioSsrc = ssrc;
+ if (this.audioSsrc != ssrc)
+ {
+ long oldValue = this.audioSsrc;
+
+ this.audioSsrc = ssrc;
+
+ firePropertyChange(
+ AUDIO_SSRC_PROPERTY_NAME,
+ oldValue, this.audioSsrc);
+ }
+ }
+
+ /**
+ * Sets the status in both directions of the audio RTP stream from the point
+ * of view of this ConferenceMember.
+ *
+ * @param status the status in both directions of the audio RTP stream from
+ * the point of view of this ConferenceMember. If null,
+ * the method executes as if {@link MediaDirection#INACTIVE}. was specified.
+ */
+ public void setAudioStatus(MediaDirection status)
+ {
+ if (status == null)
+ status = MediaDirection.INACTIVE;
+
+ if (this.audioStatus != status)
+ {
+ MediaDirection oldValue = this.audioStatus;
+
+ this.audioStatus = status;
+
+ firePropertyChange(
+ AUDIO_STATUS_PROPERTY_NAME,
+ oldValue, this.audioStatus);
+ }
}
/**
@@ -258,6 +365,60 @@ else if (PENDING.equalsIgnoreCase(endpointStatus))
setState(state);
}
+ public boolean setProperties(Map properties)
+ {
+ boolean changed = false;
+
+ for (Map.Entry entry : properties.entrySet())
+ {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ if (AUDIO_SSRC_PROPERTY_NAME.equals(key))
+ {
+ long ssrc = parseMediaSssrc(value);
+
+ if (getAudioSsrc() != ssrc)
+ {
+ setAudioSsrc(ssrc);
+ changed = true;
+ }
+ }
+ else if (AUDIO_STATUS_PROPERTY_NAME.equals(key))
+ {
+ MediaDirection status = parseMediaStatus(value);
+
+ if (!getAudioStatus().equals(status))
+ {
+ setAudioStatus(status);
+ changed = true;
+ }
+ }
+ else if (VIDEO_SSRC_PROPERTY_NAME.equals(key))
+ {
+ long ssrc = parseMediaSssrc(value);
+
+ if (getVideoSsrc() != ssrc)
+ {
+ setVideoSsrc(ssrc);
+ changed = true;
+ }
+ }
+ else if (VIDEO_STATUS_PROPERTY_NAME.equals(key))
+ {
+ MediaDirection status = parseMediaStatus(value);
+
+ if (!getVideoStatus().equals(status))
+ {
+ setVideoStatus(status);
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+ }
+
/**
* Sets the state of the device and signaling session of this
* ConferenceMember in the conference and fires a new
@@ -298,4 +459,29 @@ public void setVideoSsrc(long ssrc)
oldValue, this.videoSsrc);
}
}
+
+ /**
+ * Sets the status in both directions of the video RTP stream from the point
+ * of view of this ConferenceMember.
+ *
+ * @param status the status in both directions of the video RTP stream from
+ * the point of view of this ConferenceMember. If null,
+ * the method executes as if {@link MediaDirection#INACTIVE}. was specified.
+ */
+ public void setVideoStatus(MediaDirection status)
+ {
+ if (status == null)
+ status = MediaDirection.INACTIVE;
+
+ if (this.videoStatus != status)
+ {
+ MediaDirection oldValue = this.videoStatus;
+
+ this.videoStatus = status;
+
+ firePropertyChange(
+ VIDEO_STATUS_PROPERTY_NAME,
+ oldValue, this.videoStatus);
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/service/protocol/ConferenceMember.java b/src/net/java/sip/communicator/service/protocol/ConferenceMember.java
index a5f22aafd..ad7cf67e4 100644
--- a/src/net/java/sip/communicator/service/protocol/ConferenceMember.java
+++ b/src/net/java/sip/communicator/service/protocol/ConferenceMember.java
@@ -8,6 +8,8 @@
import java.beans.*;
+import org.jitsi.service.neomedia.*;
+
/**
* Represents a member and its details in a telephony conference managed by a
* CallPeer in its role as a conference focus.
@@ -16,6 +18,20 @@
*/
public interface ConferenceMember
{
+ /**
+ * The name of the property of ConferenceMember which specifies the
+ * SSRC of the audio content/RTP stream sent by the respective
+ * ConferenceMember in the conference.
+ */
+ public static final String AUDIO_SSRC_PROPERTY_NAME = "audioSsrc";
+
+ /**
+ * The name of the property of ConferenceMember which specifies the
+ * status of the audio RTP stream from the point of view of the
+ * ConferenceMember.
+ */
+ public static final String AUDIO_STATUS_PROPERTY_NAME = "audioStatus";
+
/**
* The name of the property of ConferenceMember which specifies the
* user-friendly display name of the respective ConferenceMember in
@@ -37,6 +53,13 @@ public interface ConferenceMember
*/
public static final String VIDEO_SSRC_PROPERTY_NAME = "videoSsrc";
+ /**
+ * The name of the property of ConferenceMember which specifies the
+ * status of the video RTP stream from the point of view of the
+ * ConferenceMember.
+ */
+ public static final String VIDEO_STATUS_PROPERTY_NAME = "videoStatus";
+
/**
* Adds a specific PropertyChangeListener to the list of
* listeners interested in and notified about changes in the values of the
@@ -75,6 +98,16 @@ public interface ConferenceMember
*/
public long getAudioSsrc();
+ /**
+ * Gets the status in both directions of the audio RTP stream from the point
+ * of view of this ConferenceMember.
+ *
+ * @return a MediaDIrection which represents the status in both
+ * directions of the audio RTP stream from the point of view of this
+ * ConferenceMember
+ */
+ public MediaDirection getAudioStatus();
+
/**
* Gets the CallPeer which is the conference focus of this
* ConferenceMember.
@@ -115,6 +148,16 @@ public interface ConferenceMember
*/
public long getVideoSsrc();
+ /**
+ * Gets the status in both directions of the video RTP stream from the point
+ * of view of this ConferenceMember.
+ *
+ * @return a MediaDIrection which represents the status in both
+ * directions of the video RTP stream from the point of view of this
+ * ConferenceMember
+ */
+ public MediaDirection getVideoStatus();
+
/**
* Removes a specific PropertyChangeListener from the list of
* listeners interested in and notified about changes in the values of the
diff --git a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java
index b09a8249b..3b3114497 100644
--- a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java
+++ b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetTelephonyConferencing.java
@@ -397,23 +397,12 @@ public OperationSetBasicTelephonyT getBasicTelephony()
return basicTelephony;
}
- /**
- * Reads the text content of the src-id XML element of a
- * media XML element of a specific endpoint XML element.
- *
- * @param endpoint an XML Node which represents the
- * endpoint XML element from which to get the text content of a
- * src-id XML element of a media XML element
- * @param mediaType the type of the media to get the src-id of
- * @return the text content of the src-id XML element of the
- * media XML element of the specified endpoint XML element
- * if any; otherwise, null
- */
- private String getEndpointMediaSrcId(Node endpoint, MediaType mediaType)
+ private void getEndpointMediaProperties(
+ Node endpoint,
+ Map properties)
{
NodeList endpointChildList = endpoint.getChildNodes();
int endpoingChildCount = endpointChildList.getLength();
- String mediaTypeStr = mediaType.toString();
for (int endpointChildIndex = 0;
endpointChildIndex < endpoingChildCount;
@@ -426,6 +415,7 @@ private String getEndpointMediaSrcId(Node endpoint, MediaType mediaType)
NodeList mediaChildList = endpointChild.getChildNodes();
int mediaChildCount = mediaChildList.getLength();
String srcId = null;
+ String status = null;
String type = null;
for (int mediaChildIndex = 0;
@@ -436,24 +426,33 @@ private String getEndpointMediaSrcId(Node endpoint, MediaType mediaType)
String mediaChildName = mediaChild.getNodeName();
if (ELEMENT_SRC_ID.equals(mediaChildName))
- {
srcId = mediaChild.getTextContent();
- if (mediaTypeStr.equalsIgnoreCase(type))
- return srcId;
- }
+ else if (ELEMENT_STATUS.equals(mediaChildName))
+ status = mediaChild.getTextContent();
else if (ELEMENT_TYPE.equals(mediaChildName))
- {
type = mediaChild.getTextContent();
- if ((srcId != null)
- && mediaTypeStr.equalsIgnoreCase(type))
- {
- return srcId;
- }
- }
+ }
+
+ if (MediaType.AUDIO.toString().equalsIgnoreCase(type))
+ {
+ properties.put(
+ ConferenceMember.AUDIO_SSRC_PROPERTY_NAME,
+ srcId);
+ properties.put(
+ ConferenceMember.AUDIO_STATUS_PROPERTY_NAME,
+ status);
+ }
+ else if (MediaType.VIDEO.toString().equalsIgnoreCase(type))
+ {
+ properties.put(
+ ConferenceMember.VIDEO_SSRC_PROPERTY_NAME,
+ srcId);
+ properties.put(
+ ConferenceMember.VIDEO_STATUS_PROPERTY_NAME,
+ status);
}
}
}
- return null;
}
/**
@@ -483,6 +482,110 @@ private String getEndpointStatus(Node endpoint)
return null;
}
+ /**
+ * Gets the MediaDirection of the media RTP stream of a specific
+ * CallPeer with a specific MediaType from the point of
+ * view of the remote peer.
+ *
+ * @param callPeer
+ * @param mediaType
+ * @return the MediaDirection of the media RTP stream of a specific
+ * CallPeer with a specific MediaType from the point of
+ * view of the remote peer
+ */
+ protected MediaDirection getRemoteDirection(
+ MediaAwareCallPeer,?,?> callPeer,
+ MediaType mediaType)
+ {
+ MediaStream stream = callPeer.getMediaHandler().getStream(mediaType);
+ MediaDirection remoteDirection;
+
+ if (stream != null)
+ {
+ remoteDirection = stream.getDirection();
+ if (remoteDirection != null)
+ remoteDirection = remoteDirection.getReverseDirection();
+ }
+ else
+ remoteDirection = null;
+ return null;
+ }
+
+ /**
+ * Gets the remote SSRC to be reported in the conference-info XML for a
+ * specific CallPeer's media of a specific MediaType.
+ *
+ * @param callPeer the CallPeer whose remote SSRC for the media of
+ * the specified mediaType is to be returned
+ * @param mediaType the MediaType of the specified
+ * callPeer's media whose remote SSRC is to be returned
+ * @return the remote SSRC to be reported in the conference-info XML for the
+ * specified callPeer's media of the specified mediaType
+ */
+ protected long getRemoteSourceID(
+ MediaAwareCallPeer,?,?> callPeer,
+ MediaType mediaType)
+ {
+ MediaStream stream = callPeer.getMediaHandler().getStream(mediaType);
+ long remoteSourceID;
+
+ if (stream != null)
+ {
+ remoteSourceID = stream.getRemoteSourceID();
+ if (remoteSourceID != -1)
+ {
+ /*
+ * TODO Technically, we are detecting conflicts within a Call
+ * while we should be detecting them within the whole
+ * CallConference.
+ */
+ MediaAwareCall,?,?> call = callPeer.getCall();
+
+ if (call != null)
+ {
+ for (MediaAwareCallPeer,?,?> aCallPeer
+ : call.getCallPeerList())
+ {
+ if (aCallPeer != callPeer)
+ {
+ MediaStream aStream
+ = aCallPeer.getMediaHandler().getStream(
+ mediaType);
+
+ /*
+ * When the Jitsi VideoBridge server-side technology
+ * is utilized by the local peer/user to host a
+ * telephony conference, one and the same
+ * MediaStream instance will be shared by the
+ * CallPeers. This will definitely lead to a
+ * conflict.
+ */
+ if (aStream == stream)
+ {
+ remoteSourceID = -1;
+ break;
+ }
+ else
+ {
+ long aRemoteSourceID
+ = stream.getRemoteSourceID();
+
+ if (aRemoteSourceID == remoteSourceID)
+ {
+ remoteSourceID = -1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ remoteSourceID = -1;
+ return remoteSourceID;
+ }
+
/**
* Notifies this CallListener that a specific incoming
* Call has been received.
@@ -703,6 +806,8 @@ private void setConferenceInfoDocument(
{
NodeList userList = usersList.item(0).getChildNodes();
int userCount = userList.getLength();
+ Map conferenceMemberProperties
+ = new HashMap();
for (int userIndex = 0; userIndex < userCount; userIndex++)
{
@@ -759,9 +864,19 @@ private void setConferenceInfoDocument(
int userChildCount = userChildList.getLength();
String displayName = null;
String endpointStatus = null;
- String audioSsrc = null;
- String videoSsrc = null;
+ conferenceMemberProperties.put(
+ ConferenceMember.AUDIO_SSRC_PROPERTY_NAME,
+ null);
+ conferenceMemberProperties.put(
+ ConferenceMember.AUDIO_STATUS_PROPERTY_NAME,
+ null);
+ conferenceMemberProperties.put(
+ ConferenceMember.VIDEO_SSRC_PROPERTY_NAME,
+ null);
+ conferenceMemberProperties.put(
+ ConferenceMember.VIDEO_STATUS_PROPERTY_NAME,
+ null);
for (int userChildIndex = 0;
userChildIndex < userChildCount;
userChildIndex++)
@@ -774,39 +889,17 @@ private void setConferenceInfoDocument(
else if (ELEMENT_ENDPOINT.equals(userChildName))
{
endpointStatus = getEndpointStatus(userChild);
- audioSsrc
- = getEndpointMediaSrcId(
- userChild,
- MediaType.AUDIO);
- videoSsrc
- = getEndpointMediaSrcId(
- userChild,
- MediaType.VIDEO);
+ getEndpointMediaProperties(
+ userChild,
+ conferenceMemberProperties);
}
}
existingConferenceMember.setDisplayName(displayName);
existingConferenceMember.setEndpointStatus(endpointStatus);
- if (audioSsrc != null)
- {
- long newSsrc = Long.parseLong(audioSsrc);
-
- if (existingConferenceMember.getAudioSsrc() != newSsrc)
- {
- changed = true;
- existingConferenceMember.setAudioSsrc(newSsrc);
- }
- }
- if (videoSsrc != null)
- {
- long newSsrc = Long.parseLong(videoSsrc);
-
- if (existingConferenceMember.getVideoSsrc() != newSsrc)
- {
- changed = true;
- existingConferenceMember.setVideoSsrc(newSsrc);
- }
- }
+ changed
+ = existingConferenceMember.setProperties(
+ conferenceMemberProperties);
if (addConferenceMember)
callPeer.addConferenceMember(existingConferenceMember);