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 ac1c09619..2e9c956a6 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
@@ -527,13 +527,7 @@ public void run()
}
// REMOTE
- Component[] videos =
- videoTelephony
- .getVisualComponents(callPeer);
-
- Component video =
- ((videos == null) || (videos.length < 1)) ? null
- : videos[0];
+ Component video = videoTelephony.getVisualComponent(callPeer);
if (video != null)
videoContainer
diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
index ee0fdfea6..71b68c0cc 100644
--- a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
@@ -263,11 +263,7 @@ public void videoAdded(VideoEvent e)
*/
public void videoRemoved(VideoEvent e)
{
- if (fireVideoEvent(
- e.getType(),
- e.getVisualComponent(),
- e.getOrigin()))
- e.consume();
+ videoAdded(e);
}
};
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
index cd941ec4d..8bb68e3cf 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandler.java
@@ -6,6 +6,7 @@
*/
package net.java.sip.communicator.impl.protocol.sip;
+import java.awt.Component;
import java.net.*;
import java.util.*;
@@ -17,6 +18,7 @@
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
/**
@@ -33,7 +35,8 @@ public class CallPeerMediaHandler
/**
* Our class logger.
*/
- private Logger logger = Logger.getLogger(CallPeerMediaHandler.class);
+ private static final Logger logger
+ = Logger.getLogger(CallPeerMediaHandler.class);
/**
* A reference to the CallPeerSipImpl instance that this handler is
@@ -108,6 +111,57 @@ public class CallPeerMediaHandler
*/
private VideoMediaStream videoStream = null;
+ /**
+ * The List of VideoListeners interested in
+ * VideoEvents fired by this instance or rather its
+ * VideoMediaStream.
+ */
+ private final List videoListeners
+ = new LinkedList();
+
+ /**
+ * The neomedia VideoListener which listens to {@link #videoStream}
+ * for changes in the availability of visual Components displaying
+ * remote video and re-fires them as
+ * net.java.sip.communicator.service.protocol.event.VideoEvents
+ * originating from this instance.
+ */
+ private final net.java.sip.communicator.service.neomedia.event.VideoListener videoStreamVideoListener
+ = new net.java.sip.communicator.service.neomedia.event.VideoListener()
+ {
+ /**
+ * Notifies this neomedia VideoListener that a new visual
+ * Component displaying remote video has been added in
+ * {@link CallPeerMediaHandler#videoStream}.
+ *
+ * @param event the neomedia VideoEvent which specifies the
+ * newly-added visual Component displaying remote video
+ */
+ public void videoAdded(
+ net.java.sip.communicator.service.neomedia.event.VideoEvent event)
+ {
+ if (fireVideoEvent(
+ event.getType(),
+ event.getVisualComponent(),
+ event.getOrigin()))
+ event.consume();
+ }
+
+ /**
+ * Notifies this neomedia VideoListener that a visual
+ * Component displaying remote video has been removed from
+ * {@link CallPeerMediaHandler#videoStream}.
+ *
+ * @param event the neomedia VideoEvent which specifies the
+ * removed visual Component displaying remote video
+ */
+ public void videoRemoved(
+ net.java.sip.communicator.service.neomedia.event.VideoEvent event)
+ {
+ videoAdded(event);
+ }
+ };
+
/**
* A URL pointing to a location with call information or a call
* control web interface related to the CallPeer that we are
@@ -307,11 +361,7 @@ private void closeStream(MediaType type)
}
else
{
- if (this.videoStream != null)
- {
- videoStream.close();
- videoStream = null;
- }
+ setVideoStream(null);
if (this.videoStreamConnector != null)
{
@@ -613,7 +663,7 @@ private MediaStream configureAndStartStream(
if( stream instanceof AudioMediaStream)
this.audioStream = (AudioMediaStream)stream;
else
- this.videoStream = (VideoMediaStream)stream;
+ setVideoStream((VideoMediaStream)stream);
if ( ! stream.isStarted())
stream.start();
@@ -1178,4 +1228,172 @@ private void setCallInfoURL(URL callInfolURL)
{
this.callInfoURL = callInfolURL;
}
+
+ /**
+ * Sets the RTP media stream that this instance uses to stream video to a
+ * specific VideoMediaStream.
+ *
+ * @param videoStream the VideoMediaStream to be set as the RTP
+ * media stream that this instance uses to stream video
+ */
+ private void setVideoStream(VideoMediaStream videoStream)
+ {
+ if (this.videoStream != videoStream)
+ {
+ /*
+ * Make sure we will no longer notify the registered VideoListeners
+ * about changes in the availability of video in the old
+ * videoStream.
+ */
+ Component oldVisualComponent = null;
+
+ if (this.videoStream != null)
+ {
+ this.videoStream.removeVideoListener(videoStreamVideoListener);
+ oldVisualComponent = this.videoStream.getVisualComponent();
+
+ this.videoStream.close();
+ }
+
+ this.videoStream = videoStream;
+
+ /*
+ * Make sure we will notify the registered VideoListeners about
+ * changes in the availability of video in the new videoStream.
+ */
+ Component newVisualComponent = null;
+
+ if (this.videoStream != null)
+ {
+ this.videoStream.addVideoListener(videoStreamVideoListener);
+ newVisualComponent = this.videoStream.getVisualComponent();
+ }
+
+ /*
+ * Notify the VideoListeners in case there was a change in the
+ * availability of the visual Components displaying remote
+ * video.
+ */
+ if (oldVisualComponent != null)
+ fireVideoEvent(
+ VideoEvent.VIDEO_REMOVED,
+ oldVisualComponent,
+ VideoEvent.REMOTE);
+ if (newVisualComponent != null)
+ fireVideoEvent(
+ VideoEvent.VIDEO_REMOVED,
+ newVisualComponent,
+ VideoEvent.REMOTE);
+ }
+ }
+
+ /**
+ * Registers a specific VideoListener with this instance so that it
+ * starts receiving notifications from it about changes in the availability
+ * of visual Components displaying video.
+ *
+ * @param listener the VideoListener to be registered with this
+ * instance and to start receiving notifications from it about changes in
+ * the availability of visual Components displaying video
+ */
+ public void addVideoListener(VideoListener listener)
+ {
+ if (listener == null)
+ throw new NullPointerException("listener");
+
+ synchronized (videoListeners)
+ {
+ if (!videoListeners.contains(listener))
+ videoListeners.add(listener);
+ }
+ }
+
+ /**
+ * Notifies the VideoListeners registered with this
+ * CallPeerMediaHandler about a specific type of change in the
+ * availability of a specific visual Component depicting video.
+ *
+ * @param type the type of change as defined by VideoEvent in the
+ * availability of the specified visual Component depicting video
+ * @param visualComponent the visual Component depicting video
+ * which has been added or removed in this CallPeerMediaHandler
+ * @param origin {@link VideoEvent#LOCAL} if the origin of the video is
+ * local (e.g. it is being locally captured); {@link VideoEvent#REMOTE} if
+ * the origin of the video is remote (e.g. a remote peer is streaming it)
+ * @return true if this event and, more specifically, the visual
+ * Component it describes have been consumed and should be
+ * considered owned, referenced (which is important because
+ * Components belong to a single Container at a time);
+ * otherwise, false
+ */
+ protected boolean fireVideoEvent(
+ int type,
+ Component visualComponent,
+ int origin)
+ {
+ VideoListener[] listeners;
+
+ synchronized (videoListeners)
+ {
+ listeners
+ = videoListeners
+ .toArray(new VideoListener[videoListeners.size()]);
+ }
+
+ boolean consumed;
+
+ if (listeners.length > 0)
+ {
+ VideoEvent event
+ = new VideoEvent(this, type, visualComponent, origin);
+
+ for (VideoListener listener : listeners)
+ switch (type)
+ {
+ case VideoEvent.VIDEO_ADDED:
+ listener.videoAdded(event);
+ break;
+ case VideoEvent.VIDEO_REMOVED:
+ listener.videoRemoved(event);
+ break;
+ }
+
+ consumed = event.isConsumed();
+ }
+ else
+ consumed = false;
+ return consumed;
+ }
+
+ /**
+ * Gets the visual Component in which video from the remote peer is
+ * currently being rendered or null if there is currently no video
+ * streaming from the remote peer.
+ *
+ * @return the visual Component in which video from the remote peer
+ * is currently being rendered or null if there is currently no
+ * video streaming from the remote peer
+ */
+ public Component getVisualComponent()
+ {
+ return (videoStream == null) ? null : videoStream.getVisualComponent();
+ }
+
+ /**
+ * Unregisters a specific VideoListener from this instance so that
+ * it stops receiving notifications from it about changes in the
+ * availability of visual Components displaying video.
+ *
+ * @param listener the VideoListener to be unregistered from this
+ * instance and to stop receiving notifications from it about changes in the
+ * availability of visual Components displaying video
+ */
+ public void removeVideoListener(VideoListener listener)
+ {
+ if (listener != null)
+ synchronized (videoListeners)
+ {
+ videoListeners.remove(listener);
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
index 7d9ce2e40..2228984fd 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
@@ -8,6 +8,7 @@
import java.net.*;
import java.text.*;
+import java.util.*;
import javax.sip.*;
import javax.sip.address.*;
@@ -94,6 +95,20 @@ public class CallPeerSipImpl
*/
private final CallPeerMediaHandler mediaHandler;
+ /**
+ * The PropertyChangeListener which listens to
+ * {@link #mediaHandler} for changes in the values of its properties.
+ */
+ private PropertyChangeListener mediaHandlerPropertyChangeListener;
+
+ /**
+ * The List of PropertyChangeListeners listening to this
+ * CallPeer for changes in the values of its properties related to
+ * video.
+ */
+ private final List videoPropertyChangeListeners
+ = new LinkedList();
+
/**
* Creates a new call peer with address peerAddress.
*
@@ -391,10 +406,7 @@ public Contact getContact()
@Override
public URL getCallInfoURL()
{
- if (mediaHandler == null)
- return null;
-
- return mediaHandler.getCallInfoURL();
+ return getMediaHandler().getCallInfoURL();
}
/**
@@ -1334,10 +1346,51 @@ public boolean isLocalVideoStreaming()
*/
public void addVideoPropertyChangeListener(PropertyChangeListener listener)
{
- /**
- * @todo update to neomedia.
- getMediaCallSession().addPropertyChangeListener(listener);
- */
+ if (listener == null)
+ throw new NullPointerException("listener");
+
+ synchronized (videoPropertyChangeListeners)
+ {
+ /*
+ * The video is part of the media-related functionality and thus it
+ * is the responsibility of mediaHandler. So listen to mediaHandler
+ * for video-related property changes and re-fire them as
+ * originating from this instance.
+ */
+ if (!videoPropertyChangeListeners.contains(listener)
+ && videoPropertyChangeListeners.add(listener)
+ && (mediaHandlerPropertyChangeListener == null))
+ {
+ mediaHandlerPropertyChangeListener
+ = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ Iterable listeners;
+
+ synchronized (videoPropertyChangeListeners)
+ {
+ listeners
+ = new LinkedList(
+ videoPropertyChangeListeners);
+ }
+
+ PropertyChangeEvent thisEvent
+ = new PropertyChangeEvent(
+ this,
+ event.getPropertyName(),
+ event.getOldValue(),
+ event.getNewValue());
+
+ for (PropertyChangeListener listener : listeners)
+ listener.propertyChange(thisEvent);
+ }
+ };
+// getMediaHandler()
+// .addPropertyChangeListener(
+// mediaHandlerPropertyChangeListener);
+ }
+ }
}
/**
@@ -1353,10 +1406,28 @@ public void addVideoPropertyChangeListener(PropertyChangeListener listener)
public void removeVideoPropertyChangeListener(
PropertyChangeListener listener)
{
- /**
- * @todo update to neomedia.
- getMediaCallSession().removePropertyChangeListener(listener);
- */
+ if (listener != null)
+ synchronized (videoPropertyChangeListeners)
+ {
+ /*
+ * The video is part of the media-related functionality and thus
+ * it is the responsibility of mediaHandler. So we're listening
+ * to mediaHandler for video-related property changes and w're
+ * re-firing them as originating from this instance. Make sure
+ * that we're not listening to mediaHandler if noone is
+ * interested in video-related property changes originating from
+ * this instance.
+ */
+ if (videoPropertyChangeListeners.remove(listener)
+ && videoPropertyChangeListeners.isEmpty()
+ && (mediaHandlerPropertyChangeListener != null))
+ {
+// getMediaHandler()
+// .removePropertyChangeListener(
+// mediaHandlerPropertyChangeListener);
+ mediaHandlerPropertyChangeListener = null;
+ }
+ }
}
/**
@@ -1389,7 +1460,7 @@ public void handleAuthenticationChallenge(ClientTransaction retryTran)
* @return a reference to the CallPeerMediaHandler instance that
* this peer uses for media related tips and tricks.
*/
- private CallPeerMediaHandler getMediaHandler()
+ CallPeerMediaHandler getMediaHandler()
{
return mediaHandler;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
index 631bee7a3..fb6972314 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
@@ -270,6 +270,7 @@ public CallPeerSipImpl findCallPeer(Dialog dialog)
* @return a reference to the ProtocolProviderServiceSipImpl
* instance that created this call.
*/
+ @Override
public ProtocolProviderServiceSipImpl getProtocolProvider()
{
return (ProtocolProviderServiceSipImpl)super.getProtocolProvider();
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
index e9e6f56cb..d45f6dfed 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
@@ -51,12 +51,12 @@ public OperationSetVideoTelephonySipImpl(
}
/**
- * Delegates to the CallSession of the Call of the specified CallPeer
- * because the video is provided by the CallSession in the SIP protocol
- * implementation. Because other OperationSetVideoTelephony implementations
- * may not provide their video through the CallSession, this implementation
- * promotes itself as the provider of the video by replacing the CallSession
- * in the VideoEvents it fires.
+ * Delegates to the CallPeerMediaHandler of the specified
+ * CallPeer because the video is provided by it. Because other
+ * OperationSetVideoTelephony implementations may not provide their
+ * video through the CallPeerMediaHandler, this implementation
+ * promotes itself as the provider of the video by replacing the
+ * CallPeerMediaHandler in the VideoEvents it fires.
*
* @param peer the CallPeer that we will be registering
* listener with.
@@ -67,11 +67,10 @@ public void addVideoListener(CallPeer peer, VideoListener listener)
if (listener == null)
throw new NullPointerException("listener");
- /**
- * @todo update to neomedia.
- ((CallPeerSipImpl) peer).getMediaCallSession()
- .addVideoListener(new InternalVideoListener(this, peer, listener));
- */
+ ((CallPeerSipImpl) peer)
+ .getMediaHandler()
+ .addVideoListener(
+ new InternalVideoListener(this, peer, listener));
}
/**
@@ -136,34 +135,21 @@ public void disposeLocalVisualComponent(CallPeer peer, Component component)
}
/**
- * Delegates to the CallSession of the Call of the specified CallPeer
- * because the video is provided by the CallSession in the SIP protocol
- * implementation.
+ * Gets the visual/video Component available in this telephony for
+ * a specific CallPeer.
*
- * @param peer the peer whose visual Components we'd like to
- * retrieve.
- *
- * @return all visual Components for the specified peer.
+ * @param peer the CallPeer whose video is to be retrieved
+ * @return the visual/video Component available in this telephony
+ * for the specified peer if any; otherwise, null
*/
- public Component[] getVisualComponents(CallPeer peer)
+ public Component getVisualComponent(CallPeer peer)
{
- /**
- * @todo update to neomedia.
- CallSession callSession =((CallPeerSipImpl)peer).getMediaCallSession();
-
- return (callSession != null) ? callSession.getVisualComponents()
- : new Component[0];
- */
- return null;
+ return ((CallPeerSipImpl) peer).getMediaHandler().getVisualComponent();
}
/**
- * Delegates to the CallSession of the Call of the specified CallPeer
- * because the video is provided by the CallSession in the SIP protocol
- * implementation. Because other OperationSetVideoTelephony implementations
- * may not provide their video through the CallSession, this implementation
- * promotes itself as the provider of the video by replacing the CallSession
- * in the VideoEvents it fires.
+ * Delegates to the CallPeerMediaHandler of the specified
+ * CallPeer because the video is provided by it.
*
* @param peer the CallPeer that we'd like to unregister our
* VideoListener from.
@@ -171,15 +157,11 @@ public Component[] getVisualComponents(CallPeer peer)
*/
public void removeVideoListener(CallPeer peer, VideoListener listener)
{
- /**
- * @todo update to neomedia.
if (listener != null)
- {
- ((CallPeerSipImpl) peer).getMediaCallSession()
- .removeVideoListener(
- new InternalVideoListener(this, peer, listener));
- }
- */
+ ((CallPeerSipImpl) peer)
+ .getMediaHandler()
+ .removeVideoListener(
+ new InternalVideoListener(this, peer, listener));
}
/**
@@ -246,8 +228,9 @@ public boolean isLocalVideoStreaming(Call call)
* when the properties associated with the specified Call change
* their values
*/
- public void addPropertyChangeListener(Call call,
- PropertyChangeListener listener)
+ public void addPropertyChangeListener(
+ Call call,
+ PropertyChangeListener listener)
{
((CallSipImpl) call).addVideoPropertyChangeListener(listener);
}
@@ -264,8 +247,9 @@ public void addPropertyChangeListener(Call call,
* notified when the properties associated with the specified Call
* change their values
*/
- public void removePropertyChangeListener(Call call,
- PropertyChangeListener listener)
+ public void removePropertyChangeListener(
+ Call call,
+ PropertyChangeListener listener)
{
((CallSipImpl) call).removeVideoPropertyChangeListener(listener);
}
@@ -317,8 +301,10 @@ private static class InternalVideoListener
* that the videos in the SIP protocol implementation is managed by the
* CallSession and not by the specified telephony
*/
- public InternalVideoListener(OperationSetVideoTelephony telephony,
- CallPeer peer, VideoListener delegate)
+ public InternalVideoListener(
+ OperationSetVideoTelephony telephony,
+ CallPeer peer,
+ VideoListener delegate)
{
if (peer == null)
throw new NullPointerException("peer cannot be null");
@@ -339,6 +325,7 @@ public InternalVideoListener(OperationSetVideoTelephony telephony,
* @return true if the underlying peer, telephony operation set and
* delegate are equal to those of the other instance.
*/
+ @Override
public boolean equals(Object other)
{
if (other == this)
@@ -360,6 +347,7 @@ public boolean equals(Object other)
* @return a hashcode based on the hash codes of the wrapped telephony
* operation set and VideoListener delegate.
*/
+ @Override
public int hashCode()
{
return (telephony.hashCode() << 16) + (delegate.hashCode() >> 16);
@@ -376,8 +364,16 @@ public int hashCode()
*/
public void videoAdded(VideoEvent event)
{
- delegate.videoAdded(new VideoEvent(this, event.getType(), event
- .getVisualComponent(), event.getOrigin()));
+ VideoEvent delegateEvent
+ = new VideoEvent(
+ this,
+ event.getType(),
+ event.getVisualComponent(),
+ event.getOrigin());
+
+ delegate.videoAdded(delegateEvent);
+ if (delegateEvent.isConsumed())
+ event.consume();
}
/**
@@ -392,8 +388,16 @@ public void videoAdded(VideoEvent event)
*/
public void videoRemoved(VideoEvent event)
{
- delegate.videoAdded(new VideoEvent(this, event.getType(), event
- .getVisualComponent(), event.getOrigin()));
+ VideoEvent delegateEvent
+ = new VideoEvent(
+ this,
+ event.getType(),
+ event.getVisualComponent(),
+ event.getOrigin());
+
+ delegate.videoRemoved(delegateEvent);
+ if (delegateEvent.isConsumed())
+ event.consume();
}
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
index e947dd4e8..10f0733a0 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
@@ -13,6 +13,7 @@ Import-Package: org.apache.log4j,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.neomedia,
net.java.sip.communicator.service.neomedia.device,
+ net.java.sip.communicator.service.neomedia.event,
net.java.sip.communicator.service.neomedia.format,
net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.protocol,
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
index 7bfe45072..528f6507f 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
@@ -79,15 +79,14 @@ public Component createLocalVisualComponent(CallPeer peer,
public void disposeLocalVisualComponent(CallPeer peer, Component component);
/**
- * Gets the visual/video Components available in this telephony
- * for a specific CallPeer.
+ * Gets the visual/video Component available in this telephony for
+ * a specific CallPeer.
*
- * @param peer the CallPeer whose videos are to be retrieved
- *
- * @return an array of the visual Components available in this
- * telephony for the specified peer
+ * @param peer the CallPeer whose video is to be retrieved
+ * @return the visual/video Component available in this telephony
+ * for the specified peer if any; otherwise, null
*/
- public Component[] getVisualComponents(CallPeer peer);
+ public Component getVisualComponent(CallPeer peer);
/**
* Removes a specific VideoListener from this telephony in