aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2012-04-02 06:29:32 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2012-04-02 06:29:32 +0000
commit6eae7b0183c65e16ccbbe097b726a9fee4c4e66a (patch)
treeaf778cef410f272b25bf32abfbc123f41fc1dea4 /src/net/java/sip
parentdd3fc88660d5ff9b02d68f208c94b6ea69cfffdb (diff)
downloadjitsi-6eae7b0183c65e16ccbbe097b726a9fee4c4e66a.zip
jitsi-6eae7b0183c65e16ccbbe097b726a9fee4c4e66a.tar.gz
jitsi-6eae7b0183c65e16ccbbe097b726a9fee4c4e66a.tar.bz2
Adds (experimental) support for the cobri Jabber extension.
Diffstat (limited to 'src/net/java/sip')
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java1
-rw-r--r--src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf5
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java124
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java17
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java1
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java2
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java393
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java324
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriStreamConnector.java61
-rw-r--r--src/net/java/sip/communicator/service/neomedia/StreamConnectorDelegate.java88
-rw-r--r--src/net/java/sip/communicator/service/neomedia/StreamConnectorFactory.java22
-rw-r--r--src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java2
-rw-r--r--src/net/java/sip/communicator/service/notification/LogMessageNotificationAction.java116
-rw-r--r--src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java1
-rw-r--r--src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java4
-rw-r--r--src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java2
-rw-r--r--src/net/java/sip/communicator/service/protocol/event/SizeChangeVideoEvent.java86
-rw-r--r--src/net/java/sip/communicator/service/protocol/event/VideoEvent.java177
-rw-r--r--src/net/java/sip/communicator/service/protocol/event/VideoListener.java49
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java2
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java1085
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/MediaHandler.java984
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/TransportManager.java44
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/protocol.media.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/util/event/PropertyChangeNotifier.java (renamed from src/net/java/sip/communicator/util/PropertyChangeNotifier.java)4
-rw-r--r--src/net/java/sip/communicator/util/event/SizeChangeVideoEvent.java (renamed from src/net/java/sip/communicator/service/neomedia/event/SizeChangeVideoEvent.java)14
-rw-r--r--src/net/java/sip/communicator/util/event/VideoEvent.java (renamed from src/net/java/sip/communicator/service/neomedia/event/VideoEvent.java)12
-rw-r--r--src/net/java/sip/communicator/util/event/VideoListener.java (renamed from src/net/java/sip/communicator/service/neomedia/event/VideoListener.java)4
-rw-r--r--src/net/java/sip/communicator/util/event/VideoNotifierSupport.java (renamed from src/net/java/sip/communicator/service/neomedia/event/VideoNotifierSupport.java)2
-rw-r--r--src/net/java/sip/communicator/util/util.manifest.mf9
33 files changed, 2288 insertions, 1354 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java
index 1889c7d..d55d2b2 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/UIVideoHandler.java
@@ -19,6 +19,7 @@ import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
import net.java.sip.communicator.util.swing.*;
/**
diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
index 9bdaaca..bf871c6 100644
--- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
+++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
@@ -37,12 +37,13 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.replacement,
net.java.sip.communicator.service.replacement.smilies,
net.java.sip.communicator.util,
+ net.java.sip.communicator.util.event,
+ net.java.sip.communicator.util.skin,
+ net.java.sip.communicator.util.swing.transparent,
net.java.sip.communicator.util.swing,
net.java.sip.communicator.util.swing.border,
net.java.sip.communicator.util.swing.event,
net.java.sip.communicator.util.swing.plaf,
- net.java.sip.communicator.util.skin,
- net.java.sip.communicator.util.swing.transparent,
javax.accessibility,
javax.imageio,
javax.swing,
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
index 63da32a..0570b14 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
@@ -30,7 +30,9 @@ import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
+import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
import net.java.sip.communicator.util.swing.*;
/**
@@ -611,7 +613,11 @@ public class MediaServiceImpl
encodingConfiguration.registerCustomPackages();
encodingConfiguration.registerCustomCodecs();
- if(!OSUtils.IS_ANDROID)
+ /*
+ * The neomedia bundle is generic enough to be used in a headless
+ * GraphicsEnvironment so prevent a HeadlessException.
+ */
+ if(!OSUtils.IS_ANDROID && !GraphicsEnvironment.isHeadless())
{
try
{
@@ -620,55 +626,55 @@ public class MediaServiceImpl
DeviceConfigurationComboBoxModel.AUDIO,
false);
- audioConfigDialog = new SIPCommDialog()
- {
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void close(boolean isEscaped)
+ audioConfigDialog
+ = new SIPCommDialog()
{
- setVisible(false);
- }
- };
+ /** Serial version UID. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override
+ protected void close(boolean escaped)
+ {
+ setVisible(false);
+ }
+ };
TransparentPanel mainPanel
= new TransparentPanel(new BorderLayout(20, 5));
-
TransparentPanel fieldsPanel
= new TransparentPanel(new BorderLayout(10, 5));
mainPanel.setBorder(
- BorderFactory.createEmptyBorder(20, 20, 20, 20));
+ BorderFactory.createEmptyBorder(20, 20, 20, 20));
TransparentPanel btnPanel
= new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+ ResourceManagementService resources
+ = NeomediaActivator.getResources();
+ JButton btn
+ = new JButton(resources.getI18NString("service.gui.CLOSE"));
- JButton btn = new JButton(NeomediaActivator.getResources().
- getI18NString("service.gui.CLOSE"));
-
- btn.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent evt)
- {
- audioConfigDialog.setVisible(false);
- }
- });
+ btn.addActionListener(
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent evt)
+ {
+ audioConfigDialog.setVisible(false);
+ }
+ });
btnPanel.add(btn);
JTextArea infoTextArea = new JTextArea();
+
infoTextArea.setOpaque(false);
infoTextArea.setEditable(false);
infoTextArea.setWrapStyleWord(true);
infoTextArea.setLineWrap(true);
- infoTextArea.setText(NeomediaActivator.getResources()
- .getI18NString(
- "impl.media.configform.AUDIO_DEVICE_CONNECTED_REMOVED"));
+ infoTextArea.setText(
+ resources.getI18NString(
+ "impl.media.configform"
+ + ".AUDIO_DEVICE_CONNECTED_REMOVED"));
fieldsPanel.add(infoTextArea, BorderLayout.NORTH);
fieldsPanel.add(panel, BorderLayout.CENTER);
@@ -676,24 +682,30 @@ public class MediaServiceImpl
TransparentPanel iconPanel
= new TransparentPanel(new BorderLayout());
- iconPanel.add(new JLabel(NeomediaActivator.getResources()
- .getImage("plugin.mediaconfig.AUDIO_ICON_64x64")),
- BorderLayout.NORTH);
+
+ iconPanel.add(
+ new JLabel(
+ resources.getImage(
+ "plugin.mediaconfig.AUDIO_ICON_64x64")),
+ BorderLayout.NORTH);
mainPanel.add(iconPanel,BorderLayout.WEST);
mainPanel.add(fieldsPanel, BorderLayout.CENTER);
- audioConfigDialog.setTitle(NeomediaActivator.getResources()
- .getI18NString("impl.media.configform.AUDIO_DEVICE_CONFIG"));
+ audioConfigDialog.setTitle(
+ resources.getI18NString(
+ "impl.media.configform.AUDIO_DEVICE_CONFIG"));
audioConfigDialog.add(mainPanel);
audioConfigDialog.validate();
audioConfigDialog.pack();
PortAudioDeviceChangedCallbacks.addDeviceChangedCallback(this);
}
- catch(Throwable e)
+ catch(Throwable t)
{
- logger.info("Cannot create audio configuration panel", e);
+ logger.info("Failed to create audio configuration panel", t);
+ if (t instanceof ThreadDeath)
+ throw (ThreadDeath) t;
}
}
}
@@ -710,10 +722,11 @@ public class MediaServiceImpl
}
/**
- * Creates <tt>ZrtpControl</tt> used to control all zrtp options
- * on particular stream.
+ * Initializes a new <tt>ZrtpControl</tt> instance which is to control all
+ * ZRTP options.
*
- * @return ZrtpControl instance.
+ * @return a new <tt>ZrtpControl</tt> instance which is to control all ZRTP
+ * options
*/
public ZrtpControl createZrtpControl()
{
@@ -721,9 +734,11 @@ public class MediaServiceImpl
}
/**
- * Creates <tt>SDesControl</tt> used to control all SDes options.
+ * Initializes a new <tt>SDesControl</tt> instance which is to control all
+ * SDes options.
*
- * @return SDesControl instance.
+ * @return a new <tt>SDesControl</tt> instance which is to control all SDes
+ * options
*/
public SDesControl createSDesControl()
{
@@ -778,13 +793,10 @@ public class MediaServiceImpl
ScreenDevice screens[] = ScreenDeviceImpl.getAvailableScreenDevice();
List<ScreenDevice> screenList;
- if (screens != null)
- {
- screenList = new ArrayList<ScreenDevice>(screens.length);
- screenList.addAll(Arrays.asList(screens));
- }
+ if ((screens != null) && (screens.length != 0))
+ screenList = new ArrayList<ScreenDevice>(Arrays.asList(screens));
else
- screenList = new ArrayList<ScreenDevice>();
+ screenList = Collections.emptyList();
return screenList;
}
@@ -800,16 +812,15 @@ public class MediaServiceImpl
int height = 0;
ScreenDevice best = null;
- for (ScreenDevice sc : screens)
+ for (ScreenDevice screen : screens)
{
- java.awt.Dimension res = sc.getSize();
+ java.awt.Dimension res = screen.getSize();
- if ((res != null)
- && ((width < res.width) || (height < res.height)))
+ if ((res != null) && ((width < res.width) || (height < res.height)))
{
width = res.width;
height = res.height;
- best = sc;
+ best = screen;
}
}
return best;
@@ -1318,8 +1329,9 @@ public class MediaServiceImpl
MediaDeviceImpl dev = (MediaDeviceImpl)mediaDevice;
CaptureDeviceInfo devInfo = dev.getCaptureDeviceInfo();
- return (devInfo != null
- && devInfo.getName().startsWith("Partial desktop streaming"));
+ return
+ (devInfo != null)
+ && devInfo.getName().startsWith("Partial desktop streaming");
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
index cc961d8..ba63c1a 100644
--- a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java
@@ -24,9 +24,9 @@ import net.java.sip.communicator.service.neomedia.control.*;
import net.java.sip.communicator.service.neomedia.control.KeyFrameControl; // disambiguation
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
-import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* Extends <tt>MediaStreamImpl</tt> in order to provide an implementation of
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java
index c3f51bd..2f63316 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java
@@ -23,6 +23,7 @@ import net.java.sip.communicator.impl.neomedia.portaudio.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* This class aims to provide a simple configuration interface for JMF. It
@@ -33,7 +34,6 @@ import net.java.sip.communicator.util.*;
* @author Emil Ivov
* @author Lyubomir Marinov
*/
-@SuppressWarnings("unchecked")
public class DeviceConfiguration
extends PropertyChangeNotifier
implements PropertyChangeListener,
@@ -365,6 +365,7 @@ public class DeviceConfiguration
*/
private CaptureDeviceInfo extractConfiguredVideoCaptureDevice(Format format)
{
+ @SuppressWarnings("unchecked")
List<CaptureDeviceInfo> videoCaptureDevices
= CaptureDeviceManager.getDeviceList(format);
CaptureDeviceInfo videoCaptureDevice = null;
@@ -426,9 +427,10 @@ public class DeviceConfiguration
*/
public CaptureDeviceInfo[] getAvailableAudioCaptureDevices()
{
- Vector<CaptureDeviceInfo> audioCaptureDevices =
- CaptureDeviceManager.getDeviceList(new AudioFormat(
- AudioFormat.LINEAR, -1, 16, -1));
+ @SuppressWarnings("unchecked")
+ Vector<CaptureDeviceInfo> audioCaptureDevices
+ = CaptureDeviceManager.getDeviceList(
+ new AudioFormat(AudioFormat.LINEAR, -1, 16, -1));
return audioCaptureDevices.toArray(NO_CAPTURE_DEVICES);
}
@@ -508,8 +510,9 @@ public class DeviceConfiguration
for (Format format : formats)
{
- Vector<CaptureDeviceInfo> captureDeviceInfos =
- CaptureDeviceManager.getDeviceList(format);
+ @SuppressWarnings("unchecked")
+ Vector<CaptureDeviceInfo> captureDeviceInfos
+ = CaptureDeviceManager.getDeviceList(format);
if(useCase != MediaUseCase.ANY)
{
@@ -1080,6 +1083,7 @@ public class DeviceConfiguration
*/
private void registerCustomRenderers()
{
+ @SuppressWarnings("unchecked")
Vector<String> renderers
= PlugInManager.getPlugInList(null, null, PlugInManager.RENDERER);
boolean commit = false;
@@ -1116,6 +1120,7 @@ public class DeviceConfiguration
* are considered preferred.
*/
int pluginType = PlugInManager.RENDERER;
+ @SuppressWarnings("unchecked")
Vector<String> plugins
= PlugInManager.getPlugInList(null, null, pluginType);
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
index a95b4f4..2e67910 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
@@ -23,6 +23,7 @@ import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* Represents the use of a specific <tt>MediaDevice</tt> by a
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
index efd0e2a..4f95b7d 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
@@ -26,10 +26,10 @@ import net.java.sip.communicator.impl.neomedia.transform.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.control.*;
import net.java.sip.communicator.service.neomedia.control.KeyFrameControl; // disambiguation
-import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* Extends <tt>MediaDeviceSession</tt> to add video-specific functionality.
diff --git a/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf b/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf
index 116ba5e..6ffdc19 100644
--- a/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf
+++ b/src/net/java/sip/communicator/impl/neomedia/neomedia.manifest.mf
@@ -35,6 +35,7 @@ Import-Package: org.bouncycastle.crypto,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
+ net.java.sip.communicator.util.event,
net.java.sip.communicator.util.swing,
quicktime,
quicktime.qd,
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
index 62b4c86..8e12c1b 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
@@ -6,11 +6,10 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
+import java.lang.ref.*;
import java.util.*;
-import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smackx.packet.*;
-
+import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.neomedia.*;
@@ -19,11 +18,17 @@ import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smackx.packet.*;
+
/**
* A Jabber implementation of the <tt>Call</tt> abstract class encapsulating
* Jabber jingle sessions.
*
* @author Emil Ivov
+ * @author Lyubomir Marinov
*/
public class CallJabberImpl
extends MediaAwareCall<
@@ -38,12 +43,29 @@ public class CallJabberImpl
private static final Logger logger = Logger.getLogger(CallJabberImpl.class);
/**
- * Indicates if the <tt>CallPeer</tt> will support </tt>inputevt</tt>
+ * Indicates if the <tt>CallPeer</tt> will support <tt>inputevt</tt>
* extension (i.e. will be able to be remote-controlled).
*/
private boolean localInputEvtAware = false;
/**
+ * The Jitsi VideoBridge conference which the local peer represented by this
+ * instance is a focus of.
+ */
+ private CobriConferenceIQ cobri;
+
+ /**
+ * The shared <tt>CallPeerMediaHandler</tt> state which is to be used by the
+ * <tt>CallPeer</tt>s of this <tt>Call</tt> which use {@link #cobri}.
+ */
+ private MediaHandler cobriMediaHandler;
+
+ private final List<WeakReference<CobriStreamConnector>>
+ cobriStreamConnectors
+ = new ArrayList<WeakReference<CobriStreamConnector>>(
+ MediaType.values().length);
+
+ /**
* Initializes a new <tt>CallJabberImpl</tt> instance belonging to
* <tt>sourceProvider</tt> and associated with the jingle session with the
* specified <tt>jingleSID</tt>. If the new instance corresponds to an
@@ -54,7 +76,7 @@ public class CallJabberImpl
* instance in the context of which this call has been created.
*/
protected CallJabberImpl(
- OperationSetBasicTelephonyJabberImpl parentOpSet)
+ OperationSetBasicTelephonyJabberImpl parentOpSet)
{
super(parentOpSet);
@@ -102,8 +124,8 @@ public class CallJabberImpl
if (remoteParty == null)
remoteParty = jingleIQ.getFrom();
- CallPeerJabberImpl callPeer = new CallPeerJabberImpl(remoteParty, this,
- jingleIQ);
+ CallPeerJabberImpl callPeer
+ = new CallPeerJabberImpl(remoteParty, this, jingleIQ);
addCallPeer(callPeer);
@@ -130,9 +152,8 @@ public class CallJabberImpl
= getProtocolProvider();
basicTelephony
= (OperationSetBasicTelephonyJabberImpl)
- protocolProvider
- .getOperationSet(
- OperationSetBasicTelephony.class);
+ protocolProvider.getOperationSet(
+ OperationSetBasicTelephony.class);
CallJabberImpl attendantCall
= basicTelephony
.getActiveCallsRepository()
@@ -140,9 +161,7 @@ public class CallJabberImpl
if (attendantCall != null)
{
- attendant
- = attendantCall.getPeer(sid);
-
+ attendant = attendantCall.getPeer(sid);
if ((attendant != null)
&& basicTelephony
.getFullCalleeURI(attendant.getAddress())
@@ -170,14 +189,18 @@ public class CallJabberImpl
CoinPacketExtension coin
= (CoinPacketExtension)
- jingleIQ.getExtension(
- CoinPacketExtension.ELEMENT_NAME,
- CoinPacketExtension.NAMESPACE);
+ jingleIQ.getExtension(
+ CoinPacketExtension.ELEMENT_NAME,
+ CoinPacketExtension.NAMESPACE);
if(coin != null)
{
- boolean b = (Boolean.parseBoolean((String)
- coin.getAttribute(CoinPacketExtension.ISFOCUS_ATTR_NAME)));
+ boolean b
+ = Boolean.parseBoolean(
+ (String)
+ coin.getAttribute(
+ CoinPacketExtension.ISFOCUS_ATTR_NAME));
+
callPeer.setConferenceFocus(b);
}
@@ -223,7 +246,8 @@ public class CallJabberImpl
}
catch(Exception e)
{
- logger.info("Exception occurred while answer transferred call",
+ logger.info(
+ "Exception occurred while answer transferred call",
e);
callPeer = null;
}
@@ -235,8 +259,10 @@ public class CallJabberImpl
}
catch(OperationFailedException e)
{
- logger.error("Failed to hang up on attendant as part of " +
- "session transfer", e);
+ logger.error(
+ "Failed to hang up on attendant as part of session"
+ + " transfer",
+ e);
}
return callPeer;
@@ -245,27 +271,24 @@ public class CallJabberImpl
/* see if offer contains audio and video so that we can propose
* option to the user (i.e. answer with video if it is a video call...)
*/
- List<ContentPacketExtension> offer =
- callPeer.getSessionIQ().getContentList();
- Map<MediaType, MediaDirection> directions = new HashMap<MediaType,
- MediaDirection>();
+ List<ContentPacketExtension> offer
+ = callPeer.getSessionIQ().getContentList();
+ Map<MediaType, MediaDirection> directions
+ = new HashMap<MediaType, MediaDirection>();
directions.put(MediaType.AUDIO, MediaDirection.INACTIVE);
directions.put(MediaType.VIDEO, MediaDirection.INACTIVE);
for(ContentPacketExtension c : offer)
{
- MediaDirection remoteDirection = JingleUtils.getDirection(
- c, callPeer.isInitiator());
+ String contentName = c.getName();
+ MediaDirection remoteDirection
+ = JingleUtils.getDirection(c, callPeer.isInitiator());
- if(c.getName().equals(MediaType.AUDIO.toString()))
- {
+ if(MediaType.AUDIO.toString().equals(contentName))
directions.put(MediaType.AUDIO, remoteDirection);
- }
- else if(c.getName().equals(MediaType.VIDEO.toString()))
- {
+ else if(MediaType.VIDEO.toString().equals(contentName))
directions.put(MediaType.VIDEO, remoteDirection);
- }
}
// if this was the first peer we added in this call then the call is
@@ -325,9 +348,8 @@ public class CallJabberImpl
CallEvent event = new CallEvent(this, CallEvent.CALL_INITIATED);
AbstractOperationSetTelephonyConferencing<?,?,?,?,?> opSet
= (AbstractOperationSetTelephonyConferencing<?,?,?,?,?>)
- getProtocolProvider()
- .getOperationSet(
- OperationSetTelephonyConferencing.class);
+ getProtocolProvider().getOperationSet(
+ OperationSetTelephonyConferencing.class);
if (opSet != null)
opSet.outgoingCallCreated(event);
@@ -435,10 +457,14 @@ public class CallJabberImpl
}
/**
- * Notified when a call are added to a <tt>CallGroup</tt>.
+ * Notifies this instance that a specific <tt>Call</tt> has been added to a
+ * <tt>CallGroup</tt>.
*
- * @param evt event
+ * @param evt a <tt>CallGroupEvent</tt> which specifies the <tt>Call</tt>
+ * which has been added to a <tt>CallGroup</tt>
+ * @see MediaAwareCall#callAdded(CallGroupEvent)
*/
+ @Override
public void callAdded(CallGroupEvent evt)
{
Iterator<CallPeerJabberImpl> peers = getCallPeers();
@@ -447,6 +473,7 @@ public class CallJabberImpl
while(peers.hasNext())
{
setConferenceFocus(true);
+
CallPeerJabberImpl callPeer = peers.next();
if(callPeer.getState() == CallPeerState.CONNECTED)
@@ -455,4 +482,294 @@ public class CallJabberImpl
super.callAdded(evt);
}
+
+ /**
+ * Closes a specific <tt>CobriStreamConnector</tt> which is associated with
+ * a <tt>MediaStream</tt> of a specific <tt>MediaType</tt> upon request from
+ * a specific <tt>CallPeer</tt>.
+ *
+ * @param peer the <tt>CallPeer</tt> which requests the closing of the
+ * specified <tt>cobriStreamConnector</tt>
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> with
+ * which the specified <tt>cobriStreamConnector</tt> is associated
+ * @param cobriStreamConnector the <tt>CobriStreamConnector</tt> to close on
+ * behalf of the specified <tt>peer</tt>
+ */
+ public void closeCobriStreamConnector(
+ CallPeerJabberImpl peer,
+ MediaType mediaType,
+ CobriStreamConnector cobriStreamConnector)
+ {
+ cobriStreamConnector.close();
+ }
+
+ /**
+ * Allocates cobri (conference) channels for asp ecific <tt>MediaType</tt>
+ * to be used by a specific <tt>CallPeer</tt>.
+ *
+ * @param peer the <tt>CallPeer</tt> which is to use the allocated cobri
+ * (conference) channels
+ * @param mediaTypes the <tt>MediaType</tt>s for which cobri (conference)
+ * channels are to be allocated
+ * @return a <tt>CobriConferenceIQ</tt> which describes the allocated cobri
+ * (conference) channels for the specified <tt>mediaTypes</tt> which are to
+ * be used by the specified <tt>peer</tt>; otherwise, <tt>null</tt>
+ */
+ public CobriConferenceIQ createCobriChannels(
+ CallPeerJabberImpl peer,
+ Iterable<MediaType> mediaTypes)
+ {
+ if (!isConferenceFocus())
+ return null;
+
+ /*
+ * For a cobri conference to work properly, all CallPeers in the
+ * conference must share one and the same CallPeerMediaHandler state
+ * i.e. they must use a single set of MediaStreams as if there was a
+ * single CallPeerMediaHandler.
+ */
+ CallPeerMediaHandler<?> peerMediaHandler = peer.getMediaHandler();
+
+ if (peerMediaHandler.getMediaHandler() != cobriMediaHandler)
+ {
+ for (MediaType mediaType : MediaType.values())
+ if (peerMediaHandler.getStream(mediaType) != null)
+ return null;
+ }
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ String jitsiVideoBridge
+ = (cobri == null)
+ ? protocolProvider.getJitsiVideoBridge()
+ : cobri.getFrom();
+
+ if ((jitsiVideoBridge == null) || (jitsiVideoBridge.length() == 0))
+ return null;
+
+ CobriConferenceIQ conferenceRequest = new CobriConferenceIQ();
+
+ if (cobri != null)
+ conferenceRequest.setID(cobri.getID());
+
+ for (MediaType mediaType : mediaTypes)
+ {
+ String contentName = mediaType.toString();
+ CobriConferenceIQ.Content contentRequest
+ = new CobriConferenceIQ.Content(contentName);
+
+ conferenceRequest.addContent(contentRequest);
+
+ boolean requestLocalChannel = true;
+
+ if (cobri != null)
+ {
+ CobriConferenceIQ.Content content
+ = cobri.getContent(contentName);
+
+ if ((content != null) && (content.getChannelCount() > 0))
+ requestLocalChannel = false;
+ }
+ if (requestLocalChannel)
+ {
+ CobriConferenceIQ.Channel localChannelRequest
+ = new CobriConferenceIQ.Channel();
+
+ contentRequest.addChannel(localChannelRequest);
+ }
+
+ CobriConferenceIQ.Channel remoteChannelRequest
+ = new CobriConferenceIQ.Channel();
+
+ contentRequest.addChannel(remoteChannelRequest);
+ }
+
+ XMPPConnection connection = protocolProvider.getConnection();
+ PacketCollector packetCollector
+ = connection.createPacketCollector(
+ new PacketIDFilter(conferenceRequest.getPacketID()));
+
+ conferenceRequest.setTo(jitsiVideoBridge);
+ conferenceRequest.setType(IQ.Type.GET);
+ connection.sendPacket(conferenceRequest);
+
+ Packet response
+ = packetCollector.nextResult(
+ SmackConfiguration.getPacketReplyTimeout());
+
+ packetCollector.cancel();
+
+ if ((response == null)
+ || (response.getError() != null)
+ || !(response instanceof CobriConferenceIQ))
+ return null;
+
+ CobriConferenceIQ conferenceResponse = (CobriConferenceIQ) response;
+ String conferenceResponseID = conferenceResponse.getID();
+
+ /*
+ * Update the complete VideoBridgeConferenceIQ
+ * representation maintained by this instance with the
+ * information given by the (current) response.
+ */
+ if (cobri == null)
+ {
+ cobri = conferenceResponse;
+ }
+ else
+ {
+ String cobriID = cobri.getID();
+
+ if (cobriID == null)
+ cobri.setID(conferenceResponseID);
+ else if (!cobriID.equals(conferenceResponseID))
+ throw new IllegalStateException("conference.id");
+
+ for (CobriConferenceIQ.Content contentResponse
+ : conferenceResponse.getContents())
+ {
+ CobriConferenceIQ.Content content
+ = cobri.getOrCreateContent(contentResponse.getName());
+
+ for (CobriConferenceIQ.Channel channelResponse
+ : contentResponse.getChannels())
+ content.addChannel(channelResponse);
+ }
+ }
+
+ /*
+ * Formulate the result to be returned to the caller which
+ * is a subset of the whole conference information kept by
+ * this CallJabberImpl and includes the remote channels
+ * explicitly requested by the method caller and their
+ * respective local channels.
+ */
+ CobriConferenceIQ conferenceResult = new CobriConferenceIQ();
+
+ conferenceResult.setID(conferenceResponseID);
+
+ for (MediaType mediaType : mediaTypes)
+ {
+ CobriConferenceIQ.Content contentResponse
+ = conferenceResponse.getContent(mediaType.toString());
+
+ if (contentResponse != null)
+ {
+ String contentName = contentResponse.getName();
+ CobriConferenceIQ.Content contentResult
+ = new CobriConferenceIQ.Content(contentName);
+
+ conferenceResult.addContent(contentResult);
+
+ /*
+ * The local channel may have been allocated in a
+ * previous method call as part of the allocation of
+ * the first remote channel in the respective
+ * content. Anyway, the current method caller still
+ * needs to know about it.
+ */
+ CobriConferenceIQ.Content content
+ = cobri.getContent(contentName);
+ CobriConferenceIQ.Channel localChannel = null;
+
+ if ((content != null) && (content.getChannelCount() > 0))
+ {
+ localChannel = content.getChannel(0);
+ contentResult.addChannel(localChannel);
+ }
+
+ String localChannelID
+ = (localChannel == null) ? null : localChannel.getID();
+
+ for (CobriConferenceIQ.Channel channelResponse
+ : contentResponse.getChannels())
+ {
+ if ((localChannelID == null)
+ || !localChannelID.equals(channelResponse.getID()))
+ contentResult.addChannel(channelResponse);
+ }
+ }
+ }
+
+ /*
+ * The specified CallPeer will participate in the cobri conference
+ * organized by this Call so it must use the shared CallPeerMediaHandler
+ * state of all CallPeers in the same cobri conference.
+ */
+ if (cobriMediaHandler == null)
+ cobriMediaHandler = new MediaHandler();
+ peerMediaHandler.setMediaHandler(cobriMediaHandler);
+
+ return conferenceResult;
+ }
+
+ /**
+ * Initializes a <tt>CobriStreamConnector</tt> on behalf of a specific
+ * <tt>CallPeer</tt> to be used in association with a specific
+ * <tt>CobriConferenceIQ.Channel</tt> of a specific <tt>MediaType</tt>.
+ *
+ * @param peer the <tt>CallPeer</tt> which requests the initialization of a
+ * <tt>CobriStreamConnector</tt>
+ * @param mediaType the <tt>MediaType</tt> of the stream which is to use the
+ * initialized <tt>CobriStreamConnector</tt> for RTP and RTCP traffic
+ * @param channel the <tt>CobriConferenceIQ.Channel</tt> to which RTP and
+ * RTCP traffic is to be sent and from which such traffic is to be received
+ * via the initialized <tt>CobriStreamConnector</tt>
+ * @param factory a <tt>StreamConnectorFactory</tt> implementation which is
+ * to allocate the sockets to be used for RTP and RTCP traffic
+ * @return a <tt>CobriStreamConnector</tt> to be used for RTP and RTCP
+ * traffic associated with the specified <tt>channel</tt>
+ */
+ public CobriStreamConnector createCobriStreamConnector(
+ CallPeerJabberImpl peer,
+ MediaType mediaType,
+ CobriConferenceIQ.Channel channel,
+ StreamConnectorFactory factory)
+ {
+ String channelID = channel.getID();
+
+ if (channelID == null)
+ throw new IllegalArgumentException("channel");
+
+ if (cobri == null)
+ throw new IllegalStateException("cobri");
+
+ CobriConferenceIQ.Content content
+ = cobri.getContent(mediaType.toString());
+
+ if (content == null)
+ throw new IllegalArgumentException("mediaType");
+ if ((content.getChannelCount() < 1)
+ || !channelID.equals((channel = content.getChannel(0)).getID()))
+ throw new IllegalArgumentException("channel");
+
+ CobriStreamConnector cobriStreamConnector;
+
+ synchronized (cobriStreamConnectors)
+ {
+ int index = mediaType.ordinal();
+ WeakReference<CobriStreamConnector> weakReference
+ = cobriStreamConnectors.get(index);
+
+ cobriStreamConnector
+ = (weakReference == null) ? null : weakReference.get();
+ if (cobriStreamConnector == null)
+ {
+ StreamConnector streamConnector
+ = factory.createStreamConnector();
+
+ if (streamConnector != null)
+ {
+ cobriStreamConnector
+ = new CobriStreamConnector(streamConnector);
+ cobriStreamConnectors.set(
+ index,
+ new WeakReference<CobriStreamConnector>(
+ cobriStreamConnector));
+ }
+ }
+ }
+
+ return cobriStreamConnector;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
index 3c5d0c9..a21d9e1 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
@@ -6,8 +6,10 @@
*/
package net.java.sip.communicator.impl.protocol.jabber;
+import java.net.*;
import java.util.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.cobri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
import net.java.sip.communicator.service.neomedia.*;
@@ -38,6 +40,15 @@ public class RawUdpTransportManager
= new LinkedList<Iterable<ContentPacketExtension>>();
/**
+ * The information pertaining to the Jisti VideoBridge conference which the
+ * local peer represented by this instance is a focus of. It gives a view of
+ * the whole Jitsi VideoBridge conference managed by the associated
+ * <tt>CallJabberImpl</tt> which provides information specific to this
+ * <tt>RawUdpTransportManager</tt> only.
+ */
+ private CobriConferenceIQ cobri;
+
+ /**
* Creates a new instance of this transport manager, binding it to the
* specified peer.
*
@@ -50,9 +61,90 @@ public class RawUdpTransportManager
}
/**
+ * Closes a specific <tt>StreamConnector</tt> associated with a specific
+ * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to
+ * the specified <tt>streamConnector</tt>, it remains.
+ *
+ * @param mediaType the <tt>MediaType</tt> associated with the specified
+ * <tt>streamConnector</tt>
+ * @param streamConnector the <tt>StreamConnector</tt> to be closed
+ */
+ @Override
+ protected void closeStreamConnector(
+ MediaType mediaType,
+ StreamConnector streamConnector)
+ {
+ if (streamConnector instanceof CobriStreamConnector)
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+ CallJabberImpl call = peer.getCall();
+
+ call.closeCobriStreamConnector(
+ peer,
+ mediaType,
+ (CobriStreamConnector) streamConnector);
+ }
+ else
+ super.closeStreamConnector(mediaType, streamConnector);
+ }
+
+ /**
+ * Creates a media <tt>StreamConnector</tt> for a stream of a specific
+ * <tt>MediaType</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the stream for which a
+ * <tt>StreamConnector</tt> is to be created
+ * @return a <tt>StreamConnector</tt> for the stream of the specified
+ * <tt>mediaType</tt>
+ * @throws OperationFailedException if the binding of the sockets fails
+ */
+ @Override
+ protected StreamConnector createStreamConnector(final MediaType mediaType)
+ throws OperationFailedException
+ {
+ CobriConferenceIQ.Channel channel = getCobriChannel(mediaType, true);
+
+ if (channel != null)
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+ CallJabberImpl call = peer.getCall();
+ StreamConnector streamConnector
+ = call.createCobriStreamConnector(
+ peer,
+ mediaType,
+ channel,
+ new StreamConnectorFactory()
+ {
+ public StreamConnector createStreamConnector()
+ {
+ try
+ {
+ return
+ RawUdpTransportManager
+ .super
+ .createStreamConnector(
+ mediaType);
+ }
+ catch (OperationFailedException ofe)
+ {
+ return null;
+ }
+ }
+ });
+
+ if (streamConnector != null)
+ return streamConnector;
+ }
+
+ return super.createStreamConnector(mediaType);
+ }
+
+ /**
* Creates a raw UDP transport element according to the specified stream
* <tt>connector</tt>.
*
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
+ * uses the specified <tt>connector</tt>
* @param connector the connector that we'd like to describe within the
* transport element.
*
@@ -60,34 +152,60 @@ public class RawUdpTransportManager
* RTCP candidates of the specified {@link StreamConnector}.
*/
private RawUdpTransportPacketExtension createTransport(
- StreamConnector connector)
+ MediaType mediaType,
+ StreamConnector connector)
{
+ CobriConferenceIQ.Channel channel = getCobriChannel(mediaType, false);
+
RawUdpTransportPacketExtension ourTransport
= new RawUdpTransportPacketExtension();
+ int generation = getCurrentGeneration();
// create and add candidates that correspond to the stream connector
// RTP
CandidatePacketExtension rtpCand = new CandidatePacketExtension();
+
rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
- rtpCand.setGeneration(getCurrentGeneration());
+ rtpCand.setGeneration(generation);
rtpCand.setID(getNextID());
- rtpCand.setIP(connector.getDataSocket().getLocalAddress()
- .getHostAddress());
- rtpCand.setPort(connector.getDataSocket().getLocalPort());
rtpCand.setType(CandidateType.host);
+ if (channel == null)
+ {
+ DatagramSocket dataSocket = connector.getDataSocket();
+
+ rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress());
+ rtpCand.setPort(dataSocket.getLocalPort());
+ }
+ else
+ {
+ rtpCand.setIP(channel.getHost());
+ rtpCand.setPort(channel.getRTPPort());
+ }
+
ourTransport.addCandidate(rtpCand);
// RTCP
CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
+
rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
- rtcpCand.setGeneration(getCurrentGeneration());
+ rtcpCand.setGeneration(generation);
rtcpCand.setID(getNextID());
- rtcpCand.setIP(connector.getControlSocket().getLocalAddress()
- .getHostAddress());
- rtcpCand.setPort(connector.getControlSocket().getLocalPort());
rtcpCand.setType(CandidateType.host);
+ if (channel == null)
+ {
+ DatagramSocket controlSocket = connector.getControlSocket();
+
+ rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress());
+ rtcpCand.setPort(controlSocket.getLocalPort());
+ }
+ else
+ {
+ rtcpCand.setIP(channel.getHost());
+ rtcpCand.setPort(channel.getRTCPPort());
+ }
+
ourTransport.addCandidate(rtcpCand);
return ourTransport;
@@ -121,7 +239,26 @@ public class RawUdpTransportManager
if (mediaType.equals(contentMediaType))
{
- streamTarget = JingleUtils.extractDefaultTarget(content);
+ CobriConferenceIQ.Channel channel
+ = getCobriChannel(mediaType, true);
+
+ if (channel == null)
+ {
+ streamTarget
+ = JingleUtils.extractDefaultTarget(content);
+ }
+ else
+ {
+ streamTarget
+ = new MediaStreamTarget(
+ new InetSocketAddress(
+ channel.getHost(),
+ channel.getRTPPort()),
+ new InetSocketAddress(
+ channel.getHost(),
+ channel.getRTPPort()));
+ }
+
break;
}
}
@@ -130,6 +267,45 @@ public class RawUdpTransportManager
}
/**
+ * Gets the {@link CobriConferenceIQ.Channel} which belongs to a content
+ * associated with a specific <tt>MediaType</tt> and is to be either locally
+ * or remotely used.
+ *
+ * @param mediaType the <tt>MediaType</tt> associated with the content which
+ * contains the <tt>CobriConferenceIQ.Channel</tt> to get
+ * @param local <tt>true</tt> if the <tt>CobriConferenceIQ.Channel</tt>
+ * which is to be used locally is to be returned or <tt>false</tt> for the
+ * one which is to be used remotely
+ * @return the <tt>CobriConferenceIQ.Channel</tt> which belongs to a content
+ * associated with the specified <tt>mediaType</tt> and which is to be used
+ * in accord with the specified <tt>local</tt> indicator if such a channel
+ * exists; otherwise, <tt>null</tt>
+ */
+ private CobriConferenceIQ.Channel getCobriChannel(
+ MediaType mediaType,
+ boolean local)
+ {
+ CobriConferenceIQ.Channel channel = null;
+
+ if (cobri != null)
+ {
+ CobriConferenceIQ.Content content
+ = cobri.getContent(mediaType.toString());
+
+ if (content != null)
+ {
+ List<CobriConferenceIQ.Channel> channels
+ = content.getChannels();
+
+ if (channels.size() == 2)
+ channel = channels.get(local ? 0 : 1);
+ }
+ }
+
+ return channel;
+ }
+
+ /**
* Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
* XML namespace of the Jingle transport implemented by this
* <tt>TransportManagerJabberImpl</tt>.
@@ -210,31 +386,12 @@ public class RawUdpTransportManager
* {@link #wrapupCandidateHarvest()}.
* @throws OperationFailedException in case we fail allocating ports
*/
- public void startCandidateHarvest(List<ContentPacketExtension> ourOffer,
- TransportInfoSender transportInfoSender)
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> ourOffer,
+ TransportInfoSender transportInfoSender)
throws OperationFailedException
{
- for(ContentPacketExtension content : ourOffer)
- {
- RtpDescriptionPacketExtension rtpDesc
- = content.getFirstChildOfType(
- RtpDescriptionPacketExtension.class);
-
- StreamConnector connector
- = getStreamConnector(
- MediaType.parseString( rtpDesc.getMedia()));
-
- RawUdpTransportPacketExtension ourTransport
- = createTransport(connector);
-
- //now add our transport to our offer
- ContentPacketExtension cpExt
- = findContentByName(ourOffer, content.getName());
-
- cpExt.addChildExtension(ourTransport);
- }
-
- this.local = ourOffer;
+ startCandidateHarvest(null, ourOffer, transportInfoSender);
}
/**
@@ -269,23 +426,106 @@ public class RawUdpTransportManager
TransportInfoSender transportInfoSender)
throws OperationFailedException
{
- for(ContentPacketExtension content : theirOffer)
+ CallPeerJabberImpl peer = getCallPeer();
+ CallJabberImpl call = peer.getCall();
+ List<ContentPacketExtension> cpes
+ = (theirOffer == null) ? ourAnswer : theirOffer;
+
+ /*
+ * If Jitsi VideoBridge is to be used, determine which channels are to
+ * be allocated and attempt to allocate them now.
+ */
+ if (call.isConferenceFocus())
+ {
+ List<MediaType> mediaTypes = new ArrayList<MediaType>();
+
+ for (ContentPacketExtension cpe : cpes)
+ {
+ RtpDescriptionPacketExtension rtpDesc
+ = cpe.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+ MediaType mediaType = MediaType.parseString(rtpDesc.getMedia());
+
+ /*
+ * The existence of a content for the mediaType and regardless
+ * of the existence of channels in it signals that a channel
+ * allocation request has already been sent for that mediaType.
+ */
+ if ((cobri == null)
+ || (cobri.getContent(mediaType.toString()) == null))
+ {
+ if (!mediaTypes.contains(mediaType))
+ mediaTypes.add(mediaType);
+ }
+ }
+ if (mediaTypes.size() != 0)
+ {
+ /*
+ * We are about to request the channel allocations for
+ * mediaTypes. Regardless of the response, we do not want to
+ * repeat these requests.
+ */
+ if (cobri == null)
+ cobri = new CobriConferenceIQ();
+ for (MediaType mediaType : mediaTypes)
+ cobri.getOrCreateContent(mediaType.toString());
+
+ CobriConferenceIQ conferenceResult
+ = call.createCobriChannels(peer, mediaTypes);
+
+ if (conferenceResult != null)
+ {
+ String videoBridgeID = cobri.getID();
+ String conferenceResultID = conferenceResult.getID();
+
+ if (videoBridgeID == null)
+ cobri.setID(conferenceResultID);
+ else if (!videoBridgeID.equals(conferenceResultID))
+ throw new IllegalStateException("conference.id");
+
+ for (CobriConferenceIQ.Content contentResult
+ : conferenceResult.getContents())
+ {
+ CobriConferenceIQ.Content content
+ = cobri.getOrCreateContent(
+ contentResult.getName());
+
+ for (CobriConferenceIQ.Channel channelResult
+ : contentResult.getChannels())
+ {
+ if (content.getChannel(channelResult.getID())
+ == null)
+ content.addChannel(channelResult);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * RawUdpTransportManager#startCandidateHarvest(
+ * List<ContentPacketExtension>, TransportInfoSender) delegates here
+ * because the implementations are pretty much identical and it's just
+ * that there's no theirOffer and ourAnswer is in fact our offer to
+ * which their answer is expected.
+ */
+ for (ContentPacketExtension cpe : cpes)
{
RtpDescriptionPacketExtension rtpDesc
- = content.getFirstChildOfType(
+ = cpe.getFirstChildOfType(
RtpDescriptionPacketExtension.class);
- StreamConnector connector
- = getStreamConnector(MediaType.parseString(rtpDesc.getMedia()));
+ MediaType mediaType = MediaType.parseString(rtpDesc.getMedia());
+ StreamConnector connector = getStreamConnector(mediaType);
RawUdpTransportPacketExtension ourTransport
- = createTransport(connector);
+ = createTransport(mediaType, connector);
//now add our transport to our answer
- ContentPacketExtension cpExt
- = findContentByName(ourAnswer, content.getName());
+ ContentPacketExtension ourCpe
+ = findContentByName(ourAnswer, cpe.getName());
//it might be that we decided not to reply to this content
- if(cpExt != null)
- cpExt.addChildExtension(ourTransport);
+ if (ourCpe != null)
+ ourCpe.addChildExtension(ourTransport);
}
this.local = ourAnswer;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriStreamConnector.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriStreamConnector.java
new file mode 100644
index 0000000..288da0e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/cobri/CobriStreamConnector.java
@@ -0,0 +1,61 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.cobri;
+
+import net.java.sip.communicator.service.neomedia.*;
+
+/**
+ * Implements a <tt>StreamConnector</tt> which allows sharing a specific
+ * <tt>StreamConnector</tt> instance among multiple <tt>TransportManager</tt>s
+ * for the purposes of the Jitsi VideoBridge.
+ *
+ * @author Lyubomir Marinov
+ */
+public class CobriStreamConnector
+ extends StreamConnectorDelegate<StreamConnector>
+{
+ /**
+ * Initializes a new <tt>CobriStreamConnector</tt> instance which is to
+ * share a specific <tt>StreamConnector</tt> instance among multiple
+ * <tt>TransportManager</tt>s for the purposes of the Jitsi VideoBridge.
+ *
+ * @param streamConnector the <tt>StreamConnector</tt> instance to be shared
+ * by the new instance among multiple <tt>TransportManager</tt>s for the
+ * purposes of the Jitsi VideoBridge
+ */
+ public CobriStreamConnector(StreamConnector streamConnector)
+ {
+ super(streamConnector);
+ }
+
+ @Override
+ public void close()
+ {
+ /*
+ * Do not close the shared StreamConnector because it is not clear
+ * whether no TransportManager is using it.
+ */
+ }
+
+ @Override
+ protected void finalize()
+ throws Throwable
+ {
+ try
+ {
+ /*
+ * Close the shared StreamConnector because it is clear that no
+ * TrasportManager is using it.
+ */
+ super.close();
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/service/neomedia/StreamConnectorDelegate.java b/src/net/java/sip/communicator/service/neomedia/StreamConnectorDelegate.java
new file mode 100644
index 0000000..1119231
--- /dev/null
+++ b/src/net/java/sip/communicator/service/neomedia/StreamConnectorDelegate.java
@@ -0,0 +1,88 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.neomedia;
+
+import java.net.*;
+
+/**
+ * Implements a {@link StreamConnector} which wraps a specific
+ * <tt>StreamConnector</tt> instance.
+ *
+ * @param <T> the very type of the <tt>StreamConnector</tt> wrapped by
+ * <tt>StreamConnectorDelegate</tt>
+ *
+ * @author Lyubomir Marinov
+ */
+public class StreamConnectorDelegate<T extends StreamConnector>
+ implements StreamConnector
+{
+ /**
+ * The <tt>StreamConnector</tt> wrapped by this instance.
+ */
+ protected final T streamConnector;
+
+ /**
+ * Initializes a new <tt>StreamConnectorDelegate</tt> which is to wrap a
+ * specific <tt>StreamConnector</tt>.
+ *
+ * @param streamConnector the <tt>StreamConnector</tt> to be wrapped by the
+ * new instance
+ */
+ public StreamConnectorDelegate(T streamConnector)
+ {
+ if (streamConnector == null)
+ throw new NullPointerException("streamConnector");
+
+ this.streamConnector = streamConnector;
+ }
+
+ /**
+ * Releases the resources allocated by this instance in the course of its
+ * execution and prepares it to be garbage collected. Calls
+ * {@link StreamConnector#close()} on the <tt>StreamConnector</tt> wrapped
+ * by this instance.
+ */
+ public void close()
+ {
+ streamConnector.close();
+ }
+
+ public DatagramSocket getControlSocket()
+ {
+ return streamConnector.getControlSocket();
+ }
+
+ public Socket getControlTCPSocket()
+ {
+ return streamConnector.getControlTCPSocket();
+ }
+
+ public DatagramSocket getDataSocket()
+ {
+ return streamConnector.getDataSocket();
+ }
+
+ public Socket getDataTCPSocket()
+ {
+ return streamConnector.getDataTCPSocket();
+ }
+
+ public Protocol getProtocol()
+ {
+ return streamConnector.getProtocol();
+ }
+
+ public void started()
+ {
+ streamConnector.started();
+ }
+
+ public void stopped()
+ {
+ streamConnector.stopped();
+ }
+}
diff --git a/src/net/java/sip/communicator/service/neomedia/StreamConnectorFactory.java b/src/net/java/sip/communicator/service/neomedia/StreamConnectorFactory.java
new file mode 100644
index 0000000..3a662f9
--- /dev/null
+++ b/src/net/java/sip/communicator/service/neomedia/StreamConnectorFactory.java
@@ -0,0 +1,22 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.neomedia;
+
+/**
+ * Represents a factory of <tt>StreamConnector</tt> instances.
+ *
+ * @author Lyubomir Marinov
+ */
+public interface StreamConnectorFactory
+{
+ /**
+ * Initializes a <tt>StreamConnector</tt> instance.
+ *
+ * @return a <tt>StreamConnector</tt> instance
+ */
+ public StreamConnector createStreamConnector();
+}
diff --git a/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java b/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java
index 980b619..f9bdcee 100644
--- a/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java
+++ b/src/net/java/sip/communicator/service/neomedia/VideoMediaStream.java
@@ -11,7 +11,7 @@ import java.util.*;
import java.util.List;
import net.java.sip.communicator.service.neomedia.control.*;
-import net.java.sip.communicator.service.neomedia.event.*;
+import net.java.sip.communicator.util.event.*;
/**
* Extends the <tt>MediaStream</tt> interface and adds methods specific to
diff --git a/src/net/java/sip/communicator/service/notification/LogMessageNotificationAction.java b/src/net/java/sip/communicator/service/notification/LogMessageNotificationAction.java
index 6efdb31..1a3f795 100644
--- a/src/net/java/sip/communicator/service/notification/LogMessageNotificationAction.java
+++ b/src/net/java/sip/communicator/service/notification/LogMessageNotificationAction.java
@@ -1,58 +1,58 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.service.notification;
-
-/**
- * An implementation of the <tt>LogMessageNotificationHandler</tt> interface.
- *
- * @author Yana Stamcheva
- */
-public class LogMessageNotificationAction
- extends NotificationAction
-{
- /**
- * Indicates that this log is of type trace. If this <tt>logType</tt> is set
- * the messages would be logged as trace logs.
- */
- public static final String TRACE_LOG_TYPE = "TraceLog";
-
- /**
- * Indicates that this log is of type info. If this <tt>logType</tt> is set
- * the messages would be logged as info logs.
- */
- public static final String INFO_LOG_TYPE = "InfoLog";
-
- /**
- * Indicates that this log is of type error. If this <tt>logType</tt> is set
- * the messages would be logged as error logs.
- */
- public static final String ERROR_LOG_TYPE = "ErrorLog";
-
- private String logType;
-
- /**
- * Creates an instance of <tt>LogMessageNotificationHandlerImpl</tt> by
- * specifying the log type.
- *
- * @param logType the type of the log
- */
- public LogMessageNotificationAction(String logType)
- {
- super(NotificationAction.ACTION_LOG_MESSAGE);
- this.logType = logType;
- }
-
- /**
- * Returns the type of the log
- *
- * @return the type of the log
- */
- public String getLogType()
- {
- return logType;
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.notification;
+
+/**
+ * An implementation of the <tt>LogMessageNotificationHandler</tt> interface.
+ *
+ * @author Yana Stamcheva
+ */
+public class LogMessageNotificationAction
+ extends NotificationAction
+{
+ /**
+ * Indicates that this log is of type trace. If this <tt>logType</tt> is set
+ * the messages would be logged as trace logs.
+ */
+ public static final String TRACE_LOG_TYPE = "TraceLog";
+
+ /**
+ * Indicates that this log is of type info. If this <tt>logType</tt> is set
+ * the messages would be logged as info logs.
+ */
+ public static final String INFO_LOG_TYPE = "InfoLog";
+
+ /**
+ * Indicates that this log is of type error. If this <tt>logType</tt> is set
+ * the messages would be logged as error logs.
+ */
+ public static final String ERROR_LOG_TYPE = "ErrorLog";
+
+ private String logType;
+
+ /**
+ * Creates an instance of <tt>LogMessageNotificationHandlerImpl</tt> by
+ * specifying the log type.
+ *
+ * @param logType the type of the log
+ */
+ public LogMessageNotificationAction(String logType)
+ {
+ super(NotificationAction.ACTION_LOG_MESSAGE);
+ this.logType = logType;
+ }
+
+ /**
+ * Returns the type of the log
+ *
+ * @return the type of the log
+ */
+ public String getLogType()
+ {
+ return logType;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
index 797dea8..10a2f6c 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
+++ b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
@@ -11,6 +11,7 @@ import java.util.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* Provides a default implementation for most of the <tt>CallPeer</tt> methods
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java b/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
index 9e7d29f..a3a2bc6 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
+++ b/src/net/java/sip/communicator/service/protocol/AbstractConferenceMember.java
@@ -6,13 +6,13 @@
*/
package net.java.sip.communicator.service.protocol;
-import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* Provides the default implementation of the <tt>ConferenceMember</tt>
* interface.
*
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
* @author Yana Stamcheva
* @author Emil Ivov
*/
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
index 186030f..6870764 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetVideoTelephony.java
@@ -12,7 +12,7 @@ import java.text.*;
import java.util.List;
import net.java.sip.communicator.service.neomedia.*;
-import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.event.*;
/**
* Represents an <tt>OperationSet</tt> giving access to video-specific
diff --git a/src/net/java/sip/communicator/service/protocol/event/SizeChangeVideoEvent.java b/src/net/java/sip/communicator/service/protocol/event/SizeChangeVideoEvent.java
deleted file mode 100644
index 38bbe3d..0000000
--- a/src/net/java/sip/communicator/service/protocol/event/SizeChangeVideoEvent.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.service.protocol.event;
-
-import java.awt.*;
-
-/**
- * Represents a <tt>VideoEvent</tt> which notifies about an update to the size
- * of a specific visual <tt>Component</tt> depicting video.
- *
- * @author Lubomir Marinov
- */
-public class SizeChangeVideoEvent
- extends VideoEvent
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The type of a <tt>VideoEvent</tt> which notifies about an update to the
- * size of a specific visual <tt>Component</tt> depicting video.
- */
- public static final int VIDEO_SIZE_CHANGE = 3;
-
- /**
- * The new height of the associated visual <tt>Component</tt>.
- */
- private final int height;
-
- /**
- * The new width of the associated visual <tt>Component</tt>.
- */
- private final int width;
-
- /**
- * Initializes a new <tt>SizeChangeVideoEvent</tt> which is to notify about
- * an update to the size of a specific visual <tt>Component</tt> depicting
- * video.
- *
- * @param source the source of the new <tt>SizeChangeVideoEvent</tt>
- * @param visualComponent the visual <tt>Component</tt> depicting video
- * with the updated size
- * @param origin the origin of the video the new
- * <tt>SizeChangeVideoEvent</tt> is to notify about
- * @param width the new width of <tt>visualComponent</tt>
- * @param height the new height of <tt>visualComponent</tt>
- */
- public SizeChangeVideoEvent(
- Object source,
- Component visualComponent,
- int origin,
- int width,
- int height)
- {
- super(source, VIDEO_SIZE_CHANGE, visualComponent, origin);
-
- this.width = width;
- this.height = height;
- }
-
- /**
- * Gets the new height of the associated visual <tt>Component</tt>.
- *
- * @return the new height of the associated visual <tt>Component</tt>
- */
- public int getHeight()
- {
- return height;
- }
-
- /**
- * Gets the new width of the associated visual <tt>Component</tt>.
- *
- * @return the new width of the associated visual <tt>Component</tt>
- */
- public int getWidth()
- {
- return width;
- }
-}
diff --git a/src/net/java/sip/communicator/service/protocol/event/VideoEvent.java b/src/net/java/sip/communicator/service/protocol/event/VideoEvent.java
deleted file mode 100644
index 329a446..0000000
--- a/src/net/java/sip/communicator/service/protocol/event/VideoEvent.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.service.protocol.event;
-
-import java.awt.*;
-import java.util.*;
-
-/**
- * Represents an event fired by providers of visual <code>Component</code>s
- * depicting video to notify about changes in the availability of such
- * <code>Component</code>s.
- *
- * @author Lubomir Marinov
- */
-public class VideoEvent
- extends EventObject
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The video origin of a <code>VideoEvent</code> which is local to the
- * executing client such as a local video capture device.
- */
- public static final int LOCAL = 1;
-
- /**
- * The video origin of a <code>VideoEvent</code> which is remote to the
- * executing client such as a video being remotely streamed from a
- * <code>CallPeer</code>.
- */
- public static final int REMOTE = 2;
-
- /**
- * The type of a <code>VideoEvent</code> which notifies about a specific
- * visual <code>Component</code> depicting video being made available by the
- * firing provider.
- */
- public static final int VIDEO_ADDED = 1;
-
- /**
- * The type of a <code>VideoEvent</code> which notifies about a specific
- * visual <code>Component</code> depicting video no longer being made
- * available by the firing provider.
- */
- public static final int VIDEO_REMOVED = 2;
-
- /**
- * The indicator which determines whether this event and, more specifically,
- * the visual <code>Component</code> it describes have been consumed and
- * should be considered owned, referenced (which is important because
- * <code>Component</code>s belong to a single <code>Container</code> at a
- * time).
- */
- private boolean consumed;
-
- /**
- * The origin of the video this <code>VideoEvent</code> notifies about which
- * is one of {@link #LOCAL} and {@link #REMOTE}.
- */
- private final int origin;
-
- /**
- * The type of availability change this <code>VideoEvent</code> notifies
- * about which is one of {@link #VIDEO_ADDED} and {@link #VIDEO_REMOVED}.
- */
- private final int type;
-
- /**
- * The visual <code>Component</code> depicting video which had its
- * availability changed and which this <code>VideoEvent</code> notifies
- * about.
- */
- private final Component visualComponent;
-
- /**
- * Initializes a new <code>VideoEvent</code> which is to notify about a
- * specific change in the availability of a specific visual
- * <code>Component</code> depicting video and being provided by a specific
- * source.
- *
- * @param source the source of the new <code>VideoEvent</code> and the
- * provider of the visual <code>Component</code> depicting video
- * @param type the type of the availability change which has caused the new
- * <code>VideoEvent</code> to be fired
- * @param visualComponent the visual <code>Component</code> depicting video
- * which had its availability in the <code>source</code> provider
- * changed
- * @param origin the origin of the video the new <code>VideoEvent</code> is
- * to notify about
- */
- public VideoEvent(Object source, int type, Component visualComponent,
- int origin)
- {
- super(source);
-
- this.type = type;
- this.visualComponent = visualComponent;
- this.origin = origin;
- }
-
- /**
- * Consumes this event and, more specifically, marks the
- * <code>Component</code> it describes as owned, referenced in order to let
- * other potential consumers know about its current ownership status (which
- * is important because <code>Component</code>s belong to a single
- * <code>Container</code> at a time).
- */
- public void consume()
- {
- consumed = true;
- }
-
- /**
- * Gets the origin of the video this <code>VideoEvent</code> notifies about
- * which is one of {@link #LOCAL} and {@link #REMOTE}.
- *
- * @return one of {@link #LOCAL} and {@link #REMOTE} which specifies the
- * origin of the video this <code>VideoEvent</code> notifies about
- */
- public int getOrigin()
- {
- return origin;
- }
-
- /**
- * Gets the type of availability change this <code>VideoEvent</code>
- * notifies about which is one of {@link #VIDEO_ADDED} and
- * {@link #VIDEO_REMOVED}.
- *
- * @return one of {@link #VIDEO_ADDED} and {@link #VIDEO_REMOVED} which
- * describes the type of availability change this
- * <code>VideoEvent</code> notifies about
- */
- public int getType()
- {
- return type;
- }
-
- /**
- * Gets the visual <code>Component</code> depicting video which had its
- * availability changed and which this <code>VideoEvent</code> notifies
- * about.
- *
- * @return the visual <code>Component</code> depicting video which had its
- * availability changed and which this <code>VideoEvent</code>
- * notifies about
- */
- public Component getVisualComponent()
- {
- return visualComponent;
- }
-
- /**
- * Determines whether this event and, more specifically, the visual
- * <code>Component</code> it describes have been consumed and should be
- * considered owned, referenced (which is important because
- * <code>Component</code>s belong to a single <code>Container</code> at a
- * time).
- *
- * @return <tt>true</tt> if this event and, more specifically, the visual
- * <code>Component</code> it describes have been consumed and should
- * be considered owned, referenced (which is important because
- * <code>Component</code>s belong to a single <code>Container</code>
- * at a time); otherwise, <tt>false</tt>
- */
- public boolean isConsumed()
- {
- return consumed;
- }
-}
diff --git a/src/net/java/sip/communicator/service/protocol/event/VideoListener.java b/src/net/java/sip/communicator/service/protocol/event/VideoListener.java
deleted file mode 100644
index 0f46828..0000000
--- a/src/net/java/sip/communicator/service/protocol/event/VideoListener.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package net.java.sip.communicator.service.protocol.event;
-
-import java.util.*;
-
-/**
- * Defines the notification support informing about changes in the availability
- * of visual <tt>Components</tt> representing video such as adding and removing.
- *
- * @author Lubomir Marinov
- */
-public interface VideoListener
- extends EventListener
-{
-
- /**
- * Notifies that a visual <tt>Component</tt> representing video has been
- * added to the provider this listener has been added to.
- *
- * @param event a <tt>VideoEvent</tt> describing the added visual
- * <tt>Component</tt> representing video and the provider it was added into
- */
- void videoAdded(VideoEvent event);
-
- /**
- * Notifies that a visual <tt>Component</tt> representing video has been
- * removed from the provider this listener has been added to.
- *
- * @param event a <tt>VideoEvent</tt> describing the removed visual
- * <tt>Component</tt> representing video and the provider it was removed
- * from
- */
- void videoRemoved(VideoEvent event);
-
- /**
- * Notifies about an update to a visual <tt>Component</tt> representing
- * video.
- *
- * @param event a <tt>VideoEvent</tt> describing the visual
- * <tt>Component</tt> related to the update and the details of the specific
- * update
- */
- void videoUpdate(VideoEvent event);
-}
diff --git a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java
index 39fb00a..43da709 100644
--- a/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java
+++ b/src/net/java/sip/communicator/service/protocol/media/AbstractOperationSetVideoTelephony.java
@@ -13,7 +13,7 @@ import java.util.List;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.event.*;
/**
* Represents a default implementation of <tt>OperationSetVideoTelephony</tt> in
diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
index 1e8a2ba..cd50650 100644
--- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
+++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
@@ -17,16 +17,13 @@ import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.SizeChangeVideoEvent;
-import net.java.sip.communicator.service.protocol.event.VideoEvent;
-import net.java.sip.communicator.service.protocol.event.VideoListener;
-import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
/**
* A utility class implementing media control code shared between current
* telephony implementations. This class is only meant for use by protocol
- * implementations and should/could not be accessed by bundles that are simply
- * using the telephony functionalities.
+ * implementations and should not be accessed by bundles that are simply using
+ * the telephony functionalities.
*
* @param <T> the peer extension class like for example <tt>CallPeerSipImpl</tt>
* or <tt>CallPeerJabberImpl</tt>
@@ -34,18 +31,11 @@ import net.java.sip.communicator.util.*;
* @author Emil Ivov
* @author Lyubomir Marinov
*/
-public abstract class CallPeerMediaHandler<
- T extends MediaAwareCallPeer<?, ?, ?>>
+public abstract class CallPeerMediaHandler
+ <T extends MediaAwareCallPeer<?, ?, ?>>
extends PropertyChangeNotifier
{
/**
- * The <tt>Logger</tt> used by the <tt>CallPeerMediaHandler</tt>
- * class and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(CallPeerMediaHandler.class);
-
- /**
* The name of the <tt>CallPeerMediaHandler</tt> property which specifies
* the local SSRC of its audio <tt>MediaStream</tt>.
*/
@@ -83,12 +73,24 @@ public abstract class CallPeerMediaHandler<
= MediaDirection.RECVONLY;
/**
+ * The <tt>VideoMediaStream</tt> which this instance uses to send and
+ * receive video.
+ */
+ private VideoMediaStream videoStream;
+
+ /**
* Determines whether or not streaming local audio is currently enabled.
*/
private MediaDirection audioDirectionUserPreference
= MediaDirection.SENDRECV;
/**
+ * The <tt>AudioMediaStream</tt> which this instance uses to send and
+ * receive audio.
+ */
+ private AudioMediaStream audioStream;
+
+ /**
* List of advertised encryption methods. Indicated before establishing the
* call.
*/
@@ -102,42 +104,12 @@ public abstract class CallPeerMediaHandler<
private final T peer;
/**
- * A reference to the object that would be responsible for SRTP control
- * and which most often would be the peer itself.
+ * The <tt>SrtpListener</tt> which is responsible for the SRTP control. Most
+ * often than not, it is the <tt>peer</tt> itself.
*/
private final SrtpListener srtpListener;
/**
- * The RTP stream that this media handler uses to send audio.
- */
- private AudioMediaStream audioStream = null;
-
- /**
- * The last-known local SSRC of {@link #audioStream}.
- */
- private long audioLocalSSRC = SSRC_UNKNOWN;
-
- /**
- * The last-known remote SSRC of {@link #audioStream}.
- */
- private long audioRemoteSSRC = SSRC_UNKNOWN;
-
- /**
- * The RTP stream that this media handler uses to send video.
- */
- private VideoMediaStream videoStream = null;
-
- /**
- * The last-known local SSRC of {@link #videoStream}.
- */
- private long videoLocalSSRC = SSRC_UNKNOWN;
-
- /**
- * The last-known remote SSRC of {@link #videoStream}.
- */
- private long videoRemoteSSRC = SSRC_UNKNOWN;
-
- /**
* The listener that the <tt>CallPeer</tt> registered for local user audio
* level events.
*/
@@ -179,12 +151,6 @@ public abstract class CallPeerMediaHandler<
private boolean locallyOnHold = false;
/**
- * Indicates whether this handler has already started at least one of its
- * streams, at least once.
- */
- private boolean started = false;
-
- /**
* Contains all dynamic payload type mappings that have been made for this
* call.
*/
@@ -199,22 +165,14 @@ public abstract class CallPeerMediaHandler<
= new DynamicRTPExtensionsRegistry();
/**
- * Holds the SRTP controls used for the current call.
- */
- private SortedMap<MediaTypeSrtpControl, SrtpControl> srtpControls =
- new TreeMap<MediaTypeSrtpControl, SrtpControl>();
-
- /**
- * The <tt>KeyFrameControl</tt> currently known to this
- * <tt>CallPeerMediaHandlerSipImpl</tt> and made available by
- * {@link #videoStream}.
+ * The <tt>PropertyChangeListener</tt> which listens to changes in the
+ * values of the properties of the <tt>Call</tt> of {@link #peer}.
*/
- private KeyFrameControl keyFrameControl;
+ private final CallPropertyChangeListener callPropertyChangeListener;
/**
* The <tt>KeyFrameRequester</tt> implemented by this
- * <tt>CallPeerMediaHandlerSipImpl</tt> and provided to
- * {@link #keyFrameControl}.
+ * <tt>CallPeerMediaHandler</tt>.
*/
private final KeyFrameControl.KeyFrameRequester keyFrameRequester
= new KeyFrameControl.KeyFrameRequester()
@@ -226,117 +184,75 @@ public abstract class CallPeerMediaHandler<
};
/**
- * The <tt>List</tt> of <tt>VideoListener</tt>s interested in
- * <tt>VideoEvent</tt>s fired by this instance or rather its
- * <tt>VideoMediaStream</tt>.
+ * The state of this instance which may be shared with multiple other
+ * <tt>CallPeerMediaHandler</tt>s.
*/
- private final List<VideoListener> videoListeners
- = new LinkedList<VideoListener>();
+ private MediaHandler mediaHandler;
/**
* The <tt>PropertyChangeListener</tt> which listens to changes in the
- * values of the properties of {@link #audioStream} and
- * {@link #videoStream}.
+ * values of the properties of the <tt>MediaStream</tt>s of this instance.
*/
private final PropertyChangeListener streamPropertyChangeListener
= new PropertyChangeListener()
- {
-
- /**
- * Notifies this <tt>PropertyChangeListener</tt> that the value of
- * a specific property of the notifier it is registered with has
- * changed.
- *
- * @param evt a <tt>PropertyChangeEvent</tt> which describes the
- * source of the event, the name of the property which has changed
- * its value and the old and new values of the property
- * @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
- */
- public void propertyChange(PropertyChangeEvent evt)
{
- String propertyName = evt.getPropertyName();
-
- if (MediaStream.PNAME_LOCAL_SSRC.equals(propertyName))
+ /**
+ * Notifies this <tt>PropertyChangeListener</tt> that the value of
+ * a specific property of the notifier it is registered with has
+ * changed.
+ *
+ * @param evt a <tt>PropertyChangeEvent</tt> which describes the
+ * source of the event, the name of the property which has changed
+ * its value and the old and new values of the property
+ * @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt)
{
- Object source = evt.getSource();
-
- if (source == audioStream)
- setAudioLocalSSRC(audioStream.getLocalSourceID());
- else if (source == videoStream)
- setVideoLocalSSRC(videoStream.getLocalSourceID());
+ firePropertyChange(
+ evt.getPropertyName(),
+ evt.getOldValue(),
+ evt.getNewValue());
}
- else if (MediaStream.PNAME_REMOTE_SSRC.equals(propertyName))
- {
- Object source = evt.getSource();
+ };
- if (source == audioStream)
- setAudioRemoteSSRC(audioStream.getRemoteSourceID());
- else if (source == videoStream)
- setVideoRemoteSSRC(videoStream.getRemoteSourceID());
- }
- }
- };
+ /**
+ * The aid which implements the boilerplate related to adding and removing
+ * <tt>VideoListener</tt>s and firing <tt>VideoEvent</tt>s to them on behalf
+ * of this instance.
+ */
+ private final VideoNotifierSupport videoNotifierSupport
+ = new VideoNotifierSupport(this, true);
/**
- * The neomedia <tt>VideoListener</tt> which listens to {@link #videoStream}
- * for changes in the availability of visual <tt>Component</tt>s displaying
- * remote video and re-fires them as
- * <tt>net.java.sip.communicator.service.protocol.event.VideoEvent</tt>s
+ * The <tt>VideoListener</tt> which listens to the video
+ * <tt>MediaStream</tt> of this instance for changes in the availability of
+ * visual <tt>Component</tt>s displaying remote video and re-fires them as
* 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 <tt>VideoListener</tt> that a new visual
- * <tt>Component</tt> displaying remote video has been added in
- * {@link CallPeerMediaHandler#videoStream}.
- *
- * @param event the neomedia <tt>VideoEvent</tt> which specifies the
- * newly-added visual <tt>Component</tt> displaying remote video
- */
- public void videoAdded(
- net.java.sip.communicator.service.neomedia.event.VideoEvent
- event)
+ private final VideoListener videoStreamVideoListener
+ = new VideoListener()
{
- if (fireVideoEvent(
- event.getType(),
- event.getVisualComponent(),
- event.getOrigin()))
- event.consume();
- }
+ public void videoAdded(VideoEvent event)
+ {
+ VideoEvent clone = event.clone(CallPeerMediaHandler.this);
- /**
- * Notifies this neomedia <tt>VideoListener</tt> that a visual
- * <tt>Component</tt> displaying remote video has been removed from
- * {@link CallPeerMediaHandler#videoStream}.
- *
- * @param event the neomedia <tt>VideoEvent</tt> which specifies the
- * removed visual <tt>Component</tt> displaying remote video
- */
- public void videoRemoved(
- net.java.sip.communicator.service.neomedia.event.VideoEvent
- event)
- {
- // VIDEO_REMOVED is forwarded the same way as VIDEO_ADDED is.
- videoAdded(event);
- }
+ fireVideoEvent(clone);
+ if (clone.isConsumed())
+ event.consume();
+ }
- public void videoUpdate(
- net.java.sip.communicator.service.neomedia.event.VideoEvent
- event)
- {
- fireVideoEvent(
- neomedia2protocol(event, CallPeerMediaHandler.this));
- }
- };
+ public void videoRemoved(VideoEvent event)
+ {
+ // Forwarded in the same way as VIDEO_ADDED.
+ videoAdded(event);
+ }
- /**
- * The <tt>PropertyChangeListener</tt> which listens to changes in the
- * values of the properties of the <tt>Call</tt> of {@link #peer}.
- */
- private final CallPropertyChangeListener callPropertyChangeListener;
+ public void videoUpdate(VideoEvent event)
+ {
+ // Forwarded in the same way as VIDEO_ADDED.
+ videoAdded(event);
+ }
+ };
/**
* Creates a new handler that will be managing media streams for
@@ -352,8 +268,10 @@ public abstract class CallPeerMediaHandler<
this.peer = peer;
this.srtpListener = srtpListener;
+ setMediaHandler(new MediaHandler());
+
/*
- * Listener to the call of peer in order to track the user's choice with
+ * Listen to the call of peer in order to track the user's choice with
* respect to the default audio device.
*/
MediaAwareCall<?, ?, ?> call = this.peer.getCall();
@@ -380,42 +298,51 @@ public abstract class CallPeerMediaHandler<
{
this.locallyOnHold = locallyOnHold;
+ // On hold.
if(locallyOnHold)
{
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
if(audioStream != null)
{
- audioStream.setDirection(audioStream.getDirection()
- .and(MediaDirection.SENDONLY));
+ audioStream.setDirection(
+ audioStream.getDirection().and(
+ MediaDirection.SENDONLY));
audioStream.setMute(locallyOnHold);
}
+
+ MediaStream videoStream = getStream(MediaType.VIDEO);
+
if(videoStream != null)
{
- videoStream.setDirection(videoStream.getDirection()
- .and(MediaDirection.SENDONLY));
+ videoStream.setDirection(
+ videoStream.getDirection().and(
+ MediaDirection.SENDONLY));
videoStream.setMute(locallyOnHold);
}
}
- else
+ /*
+ * Off hold. Make sure that we re-enable sending only if other party is
+ * not on hold.
+ */
+ else if (!CallPeerState.ON_HOLD_MUTUALLY.equals(getPeer().getState()))
{
- //off hold - make sure that we re-enable sending, only
- // if other party is not on hold
- if (CallPeerState.ON_HOLD_MUTUALLY.equals(
- getPeer().getState()))
- {
- return;
- }
+ MediaStream audioStream = getStream(MediaType.AUDIO);
if(audioStream != null)
{
- audioStream.setDirection(audioStream.getDirection()
- .or(MediaDirection.SENDONLY));
+ audioStream.setDirection(
+ audioStream.getDirection().or(MediaDirection.SENDONLY));
audioStream.setMute(locallyOnHold);
}
- if(videoStream != null
- && videoStream.getDirection() != MediaDirection.INACTIVE)
+
+ MediaStream videoStream = getStream(MediaType.VIDEO);
+
+ if((videoStream != null)
+ && (videoStream.getDirection() != MediaDirection.INACTIVE))
{
- videoStream.setDirection(videoStream.getDirection()
- .or(MediaDirection.SENDONLY));
+ videoStream.setDirection(
+ videoStream.getDirection().or(MediaDirection.SENDONLY));
videoStream.setMute(locallyOnHold);
}
}
@@ -436,34 +363,45 @@ public abstract class CallPeerMediaHandler<
if (callPropertyChangeListener != null)
callPropertyChangeListener.call.removePropertyChangeListener(
callPropertyChangeListener);
+
+ setMediaHandler(null);
}
/**
- * Closes the <tt>MediaStream</tt> that this <tt>MediaHandler</tt> uses for
- * specified media <tt>type</tt> and prepares it for garbage collection.
+ * Closes the <tt>MediaStream</tt> that this instance uses for a specific
+ * <tt>MediaType</tt> and prepares it for garbage collection.
*
* @param type the <tt>MediaType</tt> that we'd like to stop a stream for.
*/
protected void closeStream(MediaType type)
{
- if (type == MediaType.AUDIO)
- setAudioStream(null);
- else
- setVideoStream(null);
-
- getTransportManager().closeStreamConnector(type);
+ /*
+ * This CallPeerMediaHandler releases its reference to the MediaStream
+ * it has initialized via #initStream().
+ */
+ boolean mediaHandlerCloseStream = false;
- // Clear the SRTP controls used for the associated Call.
- Iterator<MediaTypeSrtpControl> it = srtpControls.keySet().iterator();
- while (it.hasNext())
+ switch (type)
{
- MediaTypeSrtpControl mct = it.next();
- if (mct.mediaType == type)
+ case AUDIO:
+ if (audioStream != null)
+ {
+ audioStream = null;
+ mediaHandlerCloseStream = true;
+ }
+ break;
+ case VIDEO:
+ if (videoStream != null)
{
- srtpControls.get(mct).cleanup();
- it.remove();
+ videoStream = null;
+ mediaHandlerCloseStream = true;
}
+ break;
}
+ if (mediaHandlerCloseStream)
+ mediaHandler.closeStream(this, type);
+
+ getTransportManager().closeStreamConnector(type);
}
/**
@@ -491,53 +429,13 @@ public abstract class CallPeerMediaHandler<
*/
public void setMute(boolean mute)
{
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
if (audioStream != null)
audioStream.setMute(mute);
}
/**
- * Creates a new
- * <tt>net.java.sip.communicator.service.protocol.event.VideoEvent</tt>
- * instance which represents the same notification/information as a specific
- * <tt>net.java.sip.communicator.service.neomedia.event.VideoEvent</tt>.
- *
- * @param neomediaEvent the
- * <tt>net.java.sip.communicator.service.neomedia.event.VideoEvent</tt> to
- * represent as a
- * <tt>net.java.sip.communicator.service.protocol.event.VideoEvent</tt>
- * @param sender the <tt>Object</tt> to be reported as the source of the
- * new <tt>VideoEvent</tt>
- * @return a new
- * <tt>net.java.sip.communicator.service.protocol.event.VideoEvent</tt>
- * which represents the same notification/information as the specified
- * <tt>neomediaEvent</tt>
- */
- private static VideoEvent neomedia2protocol(
- net.java.sip.communicator.service.neomedia.event.VideoEvent
- neomediaEvent,
- Object sender)
- {
- if (neomediaEvent instanceof net.java.sip.communicator.service
- .neomedia.event.SizeChangeVideoEvent)
- {
- net.java.sip.communicator.service.neomedia.event.SizeChangeVideoEvent
- neomediaSizeChangeEvent
- = (net.java.sip.communicator.service.neomedia.event
- .SizeChangeVideoEvent)neomediaEvent;
-
- return
- new SizeChangeVideoEvent(
- sender,
- neomediaEvent.getVisualComponent(),
- neomediaEvent.getOrigin(),
- neomediaSizeChangeEvent.getWidth(),
- neomediaSizeChangeEvent.getHeight());
- }
- else
- throw new IllegalArgumentException("neomediaEvent");
- }
-
- /**
* Determines whether the audio stream of this media handler is currently
* on mute.
*
@@ -546,6 +444,8 @@ public abstract class CallPeerMediaHandler<
*/
public boolean isMute()
{
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
return (audioStream != null) && audioStream.isMute();
}
@@ -614,32 +514,6 @@ public abstract class CallPeerMediaHandler<
}
/**
- * Sets the <tt>KeyFrameControl</tt> currently known to this
- * <tt>CallPeerMediaHandlerSipImpl</tt> made available by a specific
- * <tt>VideoMediaStream</tt>.
- *
- * @param videoStream the <tt>VideoMediaStream</tt> the
- * <tt>KeyFrameControl</tt> of which is to be set as the currently known to
- * this <tt>CallPeerMediaHandlerSipImpl</tt>
- */
- private void setKeyFrameControlFromVideoStream(VideoMediaStream videoStream)
- {
- KeyFrameControl keyFrameControl
- = (videoStream == null) ? null : videoStream.getKeyFrameControl();
-
- if (this.keyFrameControl != keyFrameControl)
- {
- if (this.keyFrameControl != null)
- this.keyFrameControl.removeKeyFrameRequester(keyFrameRequester);
-
- this.keyFrameControl = keyFrameControl;
-
- if (this.keyFrameControl != null)
- this.keyFrameControl.addKeyFrameRequester(-1, keyFrameRequester);
- }
- }
-
- /**
* Specifies whether this media handler should be allowed to transmit
* local audio.
*
@@ -665,67 +539,24 @@ public abstract class CallPeerMediaHandler<
}
/**
- * Sets the last-known local SSRC of {@link #audioStream}.
- *
- * @param audioLocalSSRC the last-known local SSRC of {@link #audioStream}
- */
- private void setAudioLocalSSRC(long audioLocalSSRC)
- {
- if (this.audioLocalSSRC != audioLocalSSRC)
- {
- long oldValue = this.audioLocalSSRC;
-
- this.audioLocalSSRC = audioLocalSSRC;
-
- firePropertyChange(AUDIO_LOCAL_SSRC, oldValue, this.audioLocalSSRC);
- }
- }
-
- /**
- * Sets the last-known remote SSRC of {@link #audioStream}.
- *
- * @param audioRemoteSSRC the last-known remote SSRC of {@link #audioStream}
- */
- private void setAudioRemoteSSRC(long audioRemoteSSRC)
- {
- if (this.audioRemoteSSRC != audioRemoteSSRC)
- {
- long oldValue = this.audioRemoteSSRC;
-
- this.audioRemoteSSRC = audioRemoteSSRC;
-
- firePropertyChange(
- AUDIO_REMOTE_SSRC,
- oldValue,
- this.audioRemoteSSRC);
- }
- }
-
- /**
* Returns the secure state of the call. If both audio and video is secured.
*
* @return the call secure state
*/
public boolean isSecure()
{
- /*
- * If a stream for a specific MediaType does not exist, it's said to be
- * secure.
- */
- boolean isAudioSecured
- = (audioStream == null)
- || audioStream.getSrtpControl().getSecureCommunicationStatus();
-
- if (!isAudioSecured)
- return false;
-
- boolean isVideoSecured
- = (videoStream == null)
- || videoStream.getSrtpControl().getSecureCommunicationStatus();
-
- if (!isVideoSecured)
- return false;
+ for (MediaType mediaType : MediaType.values())
+ {
+ MediaStream stream = getStream(mediaType);
+ /*
+ * If a stream for a specific MediaType does not exist, it's
+ * considered secure.
+ */
+ if ((stream != null)
+ && !stream.getSrtpControl().getSecureCommunicationStatus())
+ return false;
+ }
return true;
}
@@ -738,8 +569,9 @@ public abstract class CallPeerMediaHandler<
*/
public SrtpControlType[] getAdvertisedEncryptionMethods()
{
- return advertisedEncryptionMethods.toArray(
- new SrtpControlType[advertisedEncryptionMethods.size()]);
+ return
+ advertisedEncryptionMethods.toArray(
+ new SrtpControlType[advertisedEncryptionMethods.size()]);
}
/**
@@ -762,7 +594,9 @@ public abstract class CallPeerMediaHandler<
*/
public void startSrtpMultistream(SrtpControl master)
{
- if(videoStream != null)
+ MediaStream videoStream = getStream(MediaType.VIDEO);
+
+ if (videoStream != null)
videoStream.getSrtpControl().setMultistream(master);
}
@@ -775,7 +609,7 @@ public abstract class CallPeerMediaHandler<
*/
public long getAudioRemoteSSRC()
{
- return audioRemoteSSRC;
+ return mediaHandler.getRemoteSSRC(this, MediaType.AUDIO);
}
/**
@@ -814,196 +648,7 @@ public abstract class CallPeerMediaHandler<
*/
public void addVideoListener(VideoListener listener)
{
- if (listener == null)
- throw new NullPointerException("listener");
-
- synchronized (videoListeners)
- {
- if (!videoListeners.contains(listener))
- videoListeners.add(listener);
- }
- }
-
- /**
- * Sets the RTP media stream that this instance uses to stream audio to a
- * specific <tt>AudioMediaStream</tt>.
- *
- * @param audioStream the <tt>AudioMediaStream</tt> to be set as the RTP
- * media stream that this instance uses to stream audio
- */
- protected void setAudioStream(AudioMediaStream audioStream)
- {
- if (this.audioStream != audioStream)
- {
- if (this.audioStream != null)
- {
- this.audioStream
- .removePropertyChangeListener(
- streamPropertyChangeListener);
-
- this.audioStream.close();
- }
-
- this.audioStream = audioStream;
-
- long audioLocalSSRC;
- long audioRemoteSSRC;
-
- if (this.audioStream != null)
- {
- this.audioStream
- .addPropertyChangeListener(
- streamPropertyChangeListener);
- audioLocalSSRC = this.audioStream.getLocalSourceID();
- audioRemoteSSRC = this.audioStream.getRemoteSourceID();
- }
- else
- audioLocalSSRC = audioRemoteSSRC = SSRC_UNKNOWN;
-
- setAudioLocalSSRC(audioLocalSSRC);
- setAudioRemoteSSRC(audioRemoteSSRC);
- }
- }
-
- /**
- * Sets the last-known local SSRC of {@link #videoStream}.
- *
- * @param videoLocalSSRC the last-known local SSRC of {@link #videoStream}
- */
- private void setVideoLocalSSRC(long videoLocalSSRC)
- {
- if (this.videoLocalSSRC != videoLocalSSRC)
- {
- long oldValue = this.videoLocalSSRC;
-
- this.videoLocalSSRC = videoLocalSSRC;
-
- firePropertyChange(VIDEO_LOCAL_SSRC, oldValue, this.videoLocalSSRC);
- }
- }
-
- /**
- * Sets the last-known remote SSRC of {@link #videoStream}.
- *
- * @param videoRemoteSSRC the last-known remote SSRC of {@link #videoStream}
- */
- private void setVideoRemoteSSRC(long videoRemoteSSRC)
- {
- if (this.videoRemoteSSRC != videoRemoteSSRC)
- {
- long oldValue = this.videoRemoteSSRC;
-
- this.videoRemoteSSRC = videoRemoteSSRC;
-
- firePropertyChange(
- VIDEO_REMOTE_SSRC,
- oldValue,
- this.videoRemoteSSRC);
- }
- }
-
- /**
- * Sets the RTP media stream that this instance uses to stream video to a
- * specific <tt>VideoMediaStream</tt>.
- *
- * @param videoStream the <tt>VideoMediaStream</tt> 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.
- */
- List<Component> oldVisualComponents = null;
-
- if (this.videoStream != null)
- {
- this.videoStream.removePropertyChangeListener(
- streamPropertyChangeListener);
-
- this.videoStream.removeVideoListener(videoStreamVideoListener);
- oldVisualComponents = this.videoStream.getVisualComponents();
-
- /*
- * The current videoStream is going away so this
- * CallPeerMediaHandlerSipImpl should no longer use its
- * KeyFrameControl.
- */
- setKeyFrameControlFromVideoStream(null);
-
- this.videoStream.close();
- }
-
- this.videoStream = videoStream;
-
- /*
- * The videoStream has just changed so this
- * CallPeerMediaHandlerSipImpl should use its KeyFrameControl.
- */
- setKeyFrameControlFromVideoStream(this.videoStream);
-
- long videoLocalSSRC;
- long videoRemoteSSRC;
- /*
- * Make sure we will notify the registered VideoListeners about
- * changes in the availability of video in the new videoStream.
- */
- List<Component> newVisualComponents = null;
-
- if (this.videoStream != null)
- {
- this.videoStream.addPropertyChangeListener(
- streamPropertyChangeListener);
- videoLocalSSRC = this.videoStream.getLocalSourceID();
- videoRemoteSSRC = this.videoStream.getRemoteSourceID();
-
- this.videoStream.addVideoListener(videoStreamVideoListener);
- newVisualComponents = this.videoStream.getVisualComponents();
- }
- else
- videoLocalSSRC = videoRemoteSSRC = SSRC_UNKNOWN;
-
- setVideoLocalSSRC(videoLocalSSRC);
- setVideoRemoteSSRC(videoRemoteSSRC);
-
- /*
- * Notify the VideoListeners in case there was a change in the
- * availability of the visual Components displaying remote video.
- */
- if ((oldVisualComponents != null) && !oldVisualComponents.isEmpty())
- {
- /*
- * Discard Components which are present in the old and in the
- * new Lists.
- */
- if (newVisualComponents == null)
- newVisualComponents = Collections.emptyList();
- for (Component oldVisualComponent : oldVisualComponents)
- {
- if (!newVisualComponents.remove(oldVisualComponent))
- {
- fireVideoEvent(
- VideoEvent.VIDEO_REMOVED,
- oldVisualComponent,
- VideoEvent.REMOTE);
- }
- }
- }
- if ((newVisualComponents != null) && !newVisualComponents.isEmpty())
- {
- for (Component newVisualComponent : newVisualComponents)
- {
- fireVideoEvent(
- VideoEvent.VIDEO_ADDED,
- newVisualComponent,
- VideoEvent.REMOTE);
- }
- }
- }
+ videoNotifierSupport.addVideoListener(listener);
}
/**
@@ -1029,40 +674,10 @@ public abstract class CallPeerMediaHandler<
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;
- default:
- throw new IllegalArgumentException("type");
- }
-
- consumed = event.isConsumed();
- }
- else
- consumed = false;
- return consumed;
+ return
+ videoNotifierSupport.fireVideoEvent(
+ type, visualComponent, origin,
+ true);
}
/**
@@ -1075,28 +690,7 @@ public abstract class CallPeerMediaHandler<
*/
public void fireVideoEvent(VideoEvent event)
{
- VideoListener[] listeners;
-
- synchronized (videoListeners)
- {
- listeners
- = videoListeners
- .toArray(new VideoListener[videoListeners.size()]);
- }
-
- for (VideoListener listener : listeners)
- switch (event.getType())
- {
- case VideoEvent.VIDEO_ADDED:
- listener.videoAdded(event);
- break;
- case VideoEvent.VIDEO_REMOVED:
- listener.videoRemoved(event);
- break;
- default:
- listener.videoUpdate(event);
- break;
- }
+ videoNotifierSupport.fireVideoEvent(event, true);
}
/**
@@ -1106,10 +700,12 @@ public abstract class CallPeerMediaHandler<
*/
public Component createLocalVisualComponent()
{
+ MediaStream videoStream = getStream(MediaType.VIDEO);
+
return
((videoStream == null) || !isLocalVideoTransmissionEnabled())
? null
- : videoStream.createLocalVisualComponent();
+ : ((VideoMediaStream) videoStream).createLocalVisualComponent();
}
/**
@@ -1120,8 +716,13 @@ public abstract class CallPeerMediaHandler<
*/
public void disposeLocalVisualComponent(Component component)
{
+ MediaStream videoStream = getStream(MediaType.VIDEO);
+
if (videoStream != null)
- videoStream.disposeLocalVisualComponent(component);
+ {
+ ((VideoMediaStream) videoStream).disposeLocalVisualComponent(
+ component);
+ }
}
/**
@@ -1150,12 +751,16 @@ public abstract class CallPeerMediaHandler<
*/
public List<Component> getVisualComponents()
{
+ MediaStream videoStream = getStream(MediaType.VIDEO);
List<Component> visualComponents;
if (videoStream == null)
visualComponents = Collections.emptyList();
else
- visualComponents = videoStream.getVisualComponents();
+ {
+ visualComponents
+ = ((VideoMediaStream) videoStream).getVisualComponents();
+ }
return visualComponents;
}
@@ -1170,11 +775,7 @@ public abstract class CallPeerMediaHandler<
*/
public void removeVideoListener(VideoListener listener)
{
- if (listener != null)
- synchronized (videoListeners)
- {
- videoListeners.remove(listener);
- }
+ videoNotifierSupport.removeVideoListener(listener);
}
/**
@@ -1193,8 +794,13 @@ public abstract class CallPeerMediaHandler<
{
this.localAudioLevelListener = listener;
- if(audioStream != null)
- audioStream.setLocalUserAudioLevelListener(listener);
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
+ if (audioStream != null)
+ {
+ ((AudioMediaStream) audioStream).setLocalUserAudioLevelListener(
+ listener);
+ }
}
}
@@ -1214,8 +820,13 @@ public abstract class CallPeerMediaHandler<
{
this.streamAudioLevelListener = listener;
- if(audioStream != null)
- audioStream.setStreamAudioLevelListener(listener);
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
+ if (audioStream != null)
+ {
+ ((AudioMediaStream) audioStream).setStreamAudioLevelListener(
+ listener);
+ }
}
}
@@ -1234,19 +845,26 @@ public abstract class CallPeerMediaHandler<
{
this.csrcAudioLevelListener = csrcAudioLevelListener;
- if(audioStream != null)
- audioStream.setCsrcAudioLevelListener(csrcAudioLevelListener);
+ MediaStream audioStream = getStream(MediaType.AUDIO);
+
+ if (audioStream != null)
+ {
+ ((AudioMediaStream) audioStream).setCsrcAudioLevelListener(
+ csrcAudioLevelListener);
+ }
}
}
/**
- * Returns the currently valid <tt>SrtpControls</tt> map.
+ * Gets the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this
+ * instance.
*
- * @return the currently valid <tt>SrtpControls</tt> map.
+ * @return the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this
+ * instance
*/
protected Map<MediaTypeSrtpControl, SrtpControl> getSrtpControls()
{
- return this.srtpControls;
+ return mediaHandler.getSrtpControls(this);
}
/**
@@ -1271,7 +889,7 @@ public abstract class CallPeerMediaHandler<
* @return the newly created <tt>MediaStream</tt>.
*
* @throws OperationFailedException if creating the stream fails for any
- * reason (like for example accessing the device or setting the format).
+ * reason (like, for example, accessing the device or setting the format).
*/
protected MediaStream initStream(StreamConnector connector,
MediaDevice device,
@@ -1282,130 +900,27 @@ public abstract class CallPeerMediaHandler<
boolean masterStream)
throws OperationFailedException
{
- MediaType mediaType = device.getMediaType();
- MediaStream stream = getStream(mediaType);
-
- if (stream == null)
- {
- if (logger.isTraceEnabled() && (mediaType != format.getMediaType()))
- logger.trace("The media types of device and format differ.");
-
- MediaService mediaService =
- ProtocolMediaActivator.getMediaService();
- // By default the SrtpControlType is ZRTP.
- SrtpControlType srtpControlType = SrtpControlType.ZRTP;
- // But if a SrtpControl exists already, we switch to this
- // SrtpControlType.
- if(srtpControls.size() > 0)
- {
- srtpControlType = srtpControls.firstKey().srtpControlType;
- }
- MediaTypeSrtpControl mediaTypeSrtpControl =
- new MediaTypeSrtpControl(mediaType, srtpControlType);
- // check whether a control already exists
- SrtpControl control = srtpControls.get(mediaTypeSrtpControl);
- if(control == null)
- {
- // this creates the default control, currently ZRTP without
- // the hello-hash
- // The creation of the SrtpControl is done in the
- // MediaStreamImpl (which creates a new ZrtpControlImpl()), but
- // this was done without linking to the srtpControls Map.
- stream = mediaService.createMediaStream(connector, device);
- srtpControls.put(mediaTypeSrtpControl, stream.getSrtpControl());
- }
- else
- {
- stream = mediaService.createMediaStream(
- connector, device, control);
- }
- }
- else
- {
- //this is a reinit
- }
-
- return
- configureStream(
- device, format, target, direction, rtpExtensions, stream,
+ MediaStream stream
+ = mediaHandler.initStream(
+ this,
+ connector,
+ device,
+ format,
+ target,
+ direction,
+ rtpExtensions,
masterStream);
- }
-
- /**
- * Configures <tt>stream</tt> to use the specified <tt>format</tt>,
- * <tt>target</tt>, <tt>target</tt>, and <tt>direction</tt>.
- *
- * @param device the <tt>MediaDevice</tt> to be used by <tt>stream</tt>
- * for capture and playback
- * @param format the <tt>MediaFormat</tt> that we'd like the new stream
- * to transmit in.
- * @param target the <tt>MediaStreamTarget</tt> containing the RTP and
- * RTCP address:port couples that the new stream would be sending
- * packets to.
- * @param direction the <tt>MediaDirection</tt> that we'd like the new
- * stream to use (i.e. sendonly, sendrecv, recvonly, or inactive).
- * @param rtpExtensions the list of <tt>RTPExtension</tt>s that should be
- * enabled for this stream.
- * @param stream the <tt>MediaStream</tt> that we'd like to configure.
- * @param masterStream whether the stream to be used as master if secured
- *
- * @return the <tt>MediaStream</tt> that we received as a parameter (for
- * convenience reasons).
- *
- * @throws OperationFailedException if setting the <tt>MediaFormat</tt>
- * or connecting to the specified <tt>MediaDevice</tt> fails for some
- * reason.
- */
- protected MediaStream configureStream( MediaDevice device,
- MediaFormat format,
- MediaStreamTarget target,
- MediaDirection direction,
- List<RTPExtension> rtpExtensions,
- MediaStream stream,
- boolean masterStream)
- throws OperationFailedException
- {
- registerDynamicPTsWithStream(stream);
- registerRTPExtensionsWithStream(rtpExtensions, stream);
-
- stream.setDevice(device);
- stream.setTarget(target);
- stream.setDirection(direction);
- stream.setFormat(format);
- MediaAwareCall<?, ?, ?> call = peer.getCall();
- MediaType mediaType
- = (stream instanceof AudioMediaStream)
- ? MediaType.AUDIO
- : MediaType.VIDEO;
-
- stream.setRTPTranslator(call.getRTPTranslator(mediaType));
-
- switch (mediaType)
+ switch (device.getMediaType())
{
case AUDIO:
- setAudioStream((AudioMediaStream) stream);
- registerAudioLevelListeners(audioStream);
+ audioStream = (AudioMediaStream) stream;
break;
-
case VIDEO:
- setVideoStream((VideoMediaStream) stream);
+ videoStream = (VideoMediaStream) stream;
break;
}
- if (call.isDefaultEncrypted())
- {
- /*
- * We'll use the audio stream as the master stream when using SRTP
- * multistreams.
- */
- SrtpControl srtpControl = stream.getSrtpControl();
-
- srtpControl.setMasterSession(masterStream);
- srtpControl.setSrtpListener(srtpListener);
- srtpControl.start(mediaType);
- }
-
return stream;
}
@@ -1446,12 +961,7 @@ public abstract class CallPeerMediaHandler<
*/
public boolean processKeyFrameRequest()
{
- KeyFrameControl keyFrameControl = this.keyFrameControl;
-
- return
- (keyFrameControl == null)
- ? null
- : keyFrameControl.keyFrameRequest();
+ return mediaHandler.processKeyFrameRequest(this);
}
/**
@@ -1467,7 +977,7 @@ public abstract class CallPeerMediaHandler<
if (MediaAwareCall.DEFAULT_DEVICE.equals(event.getPropertyName()))
{
/*
- * XXX We only support changing the default audio device at the time
+ * XXX We support changing the default audio device only at the time
* of this writing.
*/
MediaStream stream = getStream(MediaType.AUDIO);
@@ -1495,23 +1005,18 @@ public abstract class CallPeerMediaHandler<
*/
void registerAudioLevelListeners(AudioMediaStream audioStream)
{
- // if we already have a local level listener - register it now.
synchronized (localAudioLevelListenerLock)
{
if (localAudioLevelListener != null)
- audioStream
- .setLocalUserAudioLevelListener(localAudioLevelListener);
+ audioStream.setLocalUserAudioLevelListener(
+ localAudioLevelListener);
}
-
- // if we already have a stream level listener - register it now.
synchronized (streamAudioLevelListenerLock)
{
if (streamAudioLevelListener != null)
audioStream
.setStreamAudioLevelListener(streamAudioLevelListener);
}
-
- // if we already have a csrc level listener - register it now.
synchronized (csrcAudioLevelListenerLock)
{
if (csrcAudioLevelListener != null)
@@ -1520,47 +1025,6 @@ public abstract class CallPeerMediaHandler<
}
/**
- * Registers all dynamic payload mappings known to this
- * <tt>MediaHandler</tt> with the specified <tt>MediaStream</tt>.
- *
- * @param stream the <tt>MediaStream</tt> that we'd like to register our
- * dynamic payload mappings with.
- */
- private void registerDynamicPTsWithStream(MediaStream stream)
- {
- for (Map.Entry<MediaFormat, Byte> mapEntry
- : getDynamicPayloadTypes().getMappings().entrySet())
- {
- byte pt = mapEntry.getValue();
- MediaFormat fmt = mapEntry.getKey();
-
- stream.addDynamicRTPPayloadType(pt, fmt);
- }
- }
-
- /**
- * Registers with the specified <tt>MediaStream</tt> all RTP extensions
- * negotiated by this <tt>MediaHandler</tt>.
- *
- * @param stream the <tt>MediaStream</tt> that we'd like to register our
- * <tt>RTPExtension</tt>s with.
- * @param rtpExtensions the list of <tt>RTPExtension</tt>s that should be
- * enabled for <tt>stream</tt>.
- */
- private void registerRTPExtensionsWithStream(
- List<RTPExtension> rtpExtensions,
- MediaStream stream)
- {
- for ( RTPExtension rtpExtension : rtpExtensions)
- {
- byte extensionID
- = rtpExtensionsRegistry.getExtensionMapping(rtpExtension);
-
- stream.addRTPExtension(extensionID, rtpExtension);
- }
- }
-
- /**
* Gets the <tt>MediaStream</tt> of this <tt>CallPeerMediaHandler</tt> which
* is of a specific <tt>MediaType</tt>. If this instance doesn't have such a
* <tt>MediaStream</tt>, returns <tt>null</tt>
@@ -1592,12 +1056,13 @@ public abstract class CallPeerMediaHandler<
*/
public boolean isRemotelyOnHold()
{
- if(audioStream != null && audioStream.getDirection().allowsSending())
- return false;
-
- if(videoStream != null && videoStream.getDirection().allowsSending())
- return false;
+ for (MediaType mediaType : MediaType.values())
+ {
+ MediaStream stream = getStream(mediaType);
+ if ((stream != null) && stream.getDirection().allowsSending())
+ return false;
+ }
return true;
}
@@ -1783,18 +1248,6 @@ public abstract class CallPeerMediaHandler<
}
/**
- * Returns <tt>true</tt> if this handler has already started at least one
- * of its streams, at least once, and <tt>false</tt> otherwise.
- *
- * @return <tt>true</tt> if this handler has already started at least one
- * of its streams, at least once, and <tt>false</tt> otherwise.
- */
- public boolean isStarted()
- {
- return started;
- }
-
- /**
* Starts this <tt>CallPeerMediaHandler</tt>. If it has already been
* started, does nothing.
*
@@ -1805,34 +1258,37 @@ public abstract class CallPeerMediaHandler<
public void start()
throws IllegalStateException
{
- if(isStarted())
- return;
+ MediaStream stream;
- MediaStream stream = getStream(MediaType.AUDIO);
+ stream = getStream(MediaType.AUDIO);
if ((stream != null)
&& !stream.isStarted()
&& isLocalAudioTransmissionEnabled())
{
- getTransportManager().setTrafficClass(stream.getTarget(),
- MediaType.AUDIO);
+ getTransportManager().setTrafficClass(
+ stream.getTarget(),
+ MediaType.AUDIO);
stream.start();
}
stream = getStream(MediaType.VIDEO);
- if ((stream != null))
+ if (stream != null)
{
- /* Inform listener of LOCAL_VIDEO_STREAMING only once the video
- * starts, so that VideoMediaDeviceSession has correct MediaDevice
+ /*
+ * Inform listener of LOCAL_VIDEO_STREAMING only once the video
+ * starts so that VideoMediaDeviceSession has correct MediaDevice
* set (switch from desktop streaming to webcam video or vice-versa
* issue)
*/
- firePropertyChange(OperationSetVideoTelephony.LOCAL_VIDEO_STREAMING,
+ firePropertyChange(
+ OperationSetVideoTelephony.LOCAL_VIDEO_STREAMING,
null, this.videoDirectionUserPreference);
if(!stream.isStarted())
{
- getTransportManager().setTrafficClass(stream.getTarget(),
- MediaType.VIDEO);
+ getTransportManager().setTrafficClass(
+ stream.getTarget(),
+ MediaType.VIDEO);
stream.start();
// send empty packet to deblock some kind of RTP proxy to let
@@ -1927,35 +1383,16 @@ public abstract class CallPeerMediaHandler<
}
/**
- * Returns the SRTP control type used for a given media type (AUDIO or
- * VIDEO).
+ * Gets the SRTP control type used for a given media type.
*
- * @param mediaType The media type (AUDIO or VIDEO) which may use SRTP
- * enabled thanks to a given SRTP control type.
- *
- * @return the SRTP control type used (MIKEY, SDES, ZRTP) for the given
- * media, or null if SRTP is not enabled for this media type.
+ * @param mediaType the <tt>MediaType</tt> to get the SRTP control type for
+ * @return the SRTP control type (MIKEY, SDES, ZRTP) used for the given
+ * media type or <tt>null</tt> if SRTP is not enabled for the given media
+ * type
*/
public SrtpControlType getEncryptionMethod(MediaType mediaType)
{
- SrtpControl srtpControl = null;
-
- // Goes through the different SRTP control type and stops if we found
- // the one used for this media stream.
- for(SrtpControlType srtpControlType : SrtpControlType.values())
- {
- // If this SRTP control type exists and is activate to secure the
- // communication.
- if((srtpControl = srtpControls.get(new MediaTypeSrtpControl(
- mediaType, srtpControlType)))
- != null
- && srtpControl.getSecureCommunicationStatus())
- {
- return srtpControlType;
- }
- }
-
- return null;
+ return mediaHandler.getEncryptionMethod(this, mediaType);
}
/**
@@ -1967,10 +1404,44 @@ public abstract class CallPeerMediaHandler<
*/
public String getICECandidateExtendedType()
{
- if(getTransportManager() == null)
+ TransportManager<?> transportManager = getTransportManager();
+
+ return
+ (transportManager == null)
+ ? null
+ : transportManager.getICECandidateExtendedType();
+ }
+
+ public MediaHandler getMediaHandler()
+ {
+ return mediaHandler;
+ }
+
+ public void setMediaHandler(MediaHandler mediaHandler)
+ {
+ if (this.mediaHandler != mediaHandler)
{
- return null;
+ if (this.mediaHandler != null)
+ {
+ this.mediaHandler.removeKeyFrameRequester(keyFrameRequester);
+ this.mediaHandler.removePropertyChangeListener(
+ streamPropertyChangeListener);
+ if (srtpListener != null)
+ this.mediaHandler.removeSrtpListener(srtpListener);
+ this.mediaHandler.removeVideoListener(videoStreamVideoListener);
+ }
+
+ this.mediaHandler = mediaHandler;
+
+ if (this.mediaHandler != null)
+ {
+ this.mediaHandler.addKeyFrameRequester(-1, keyFrameRequester);
+ this.mediaHandler.addPropertyChangeListener(
+ streamPropertyChangeListener);
+ if (srtpListener != null)
+ this.mediaHandler.addSrtpListener(srtpListener);
+ this.mediaHandler.addVideoListener(videoStreamVideoListener);
+ }
}
- return getTransportManager().getICECandidateExtendedType();
}
}
diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java
new file mode 100644
index 0000000..1d739f0
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/media/MediaHandler.java
@@ -0,0 +1,984 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.protocol.media;
+
+import java.awt.*;
+import java.beans.*;
+import java.util.*;
+import java.util.List;
+
+import net.java.sip.communicator.service.neomedia.*;
+import net.java.sip.communicator.service.neomedia.control.*;
+import net.java.sip.communicator.service.neomedia.device.*;
+import net.java.sip.communicator.service.neomedia.event.*;
+import net.java.sip.communicator.service.neomedia.format.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.event.*;
+
+/**
+ * Implements media control code which allows state sharing among multiple
+ * <tt>CallPeerMediaHandler</tt>s.
+ *
+ * @author Lyubomir Marinov
+ */
+public class MediaHandler
+ extends PropertyChangeNotifier
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>MediaHandler</tt> class and its
+ * instances for logging output.
+ */
+ private static final Logger logger = Logger.getLogger(MediaHandler.class);
+
+ /**
+ * The <tt>AudioMediaStream</tt> which this instance uses to send and
+ * receive audio.
+ */
+ private AudioMediaStream audioStream;
+
+ /**
+ * The <tt>KeyFrameControl</tt> currently known to this
+ * <tt>MediaHandler</tt> and made available by {@link #videoStream}.
+ */
+ private KeyFrameControl keyFrameControl;
+
+ /**
+ * The <tt>KeyFrameRequester</tt> implemented by this
+ * <tt>MediaHandler</tt> and provided to {@link #keyFrameControl}.
+ */
+ private final KeyFrameControl.KeyFrameRequester keyFrameRequester
+ = new KeyFrameControl.KeyFrameRequester()
+ {
+ public boolean requestKeyFrame()
+ {
+ return MediaHandler.this.requestKeyFrame();
+ }
+ };
+
+ private final List<KeyFrameControl.KeyFrameRequester> keyFrameRequesters
+ = new LinkedList<KeyFrameControl.KeyFrameRequester>();
+
+ /**
+ * The last-known local SSRCs of the <tt>MediaStream</tt>s of this instance
+ * indexed by <tt>MediaType</tt> ordinal.
+ */
+ private final long[] localSSRCs;
+
+ /**
+ * The last-known remote SSRCs of the <tt>MediaStream</tt>s of this instance
+ * indexed by <tt>MediaType</tt> ordinal.
+ */
+ private final long[] remoteSSRCs;
+
+ /**
+ * The <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this instance.
+ */
+ private final SortedMap<MediaTypeSrtpControl, SrtpControl> srtpControls
+ = new TreeMap<MediaTypeSrtpControl, SrtpControl>();
+
+ private final SrtpListener srtpListener
+ = new SrtpListener()
+ {
+ public void securityMessageReceived(
+ String message, String i18nMessage, int severity)
+ {
+ for (SrtpListener listener : getSrtpListeners())
+ listener.securityMessageReceived(
+ message, i18nMessage, severity);
+ }
+
+ public void securityTimeout(int sessionType)
+ {
+ for (SrtpListener listener : getSrtpListeners())
+ listener.securityTimeout(sessionType);
+ }
+
+ public void securityTurnedOff(int sessionType)
+ {
+ for (SrtpListener listener : getSrtpListeners())
+ listener.securityTurnedOff(sessionType);
+ }
+
+ public void securityTurnedOn(
+ int sessionType, String cipher, SrtpControl sender)
+ {
+ for (SrtpListener listener : getSrtpListeners())
+ listener.securityTurnedOn(sessionType, cipher, sender);
+ }
+ };
+
+ private final List<SrtpListener> srtpListeners
+ = new LinkedList<SrtpListener>();
+
+ /**
+ * The <tt>PropertyChangeListener</tt> which listens to changes in the
+ * values of the properties of the <tt>MediaStream</tt>s of this instance.
+ */
+ private final PropertyChangeListener streamPropertyChangeListener
+ = new PropertyChangeListener()
+ {
+ /**
+ * Notifies this <tt>PropertyChangeListener</tt> that the value of
+ * a specific property of the notifier it is registered with has
+ * changed.
+ *
+ * @param evt a <tt>PropertyChangeEvent</tt> which describes the
+ * source of the event, the name of the property which has changed
+ * its value and the old and new values of the property
+ * @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String propertyName = evt.getPropertyName();
+
+ if (MediaStream.PNAME_LOCAL_SSRC.equals(propertyName))
+ {
+ Object source = evt.getSource();
+
+ if (source == audioStream)
+ setLocalSSRC(
+ MediaType.AUDIO,
+ audioStream.getLocalSourceID());
+ else if (source == videoStream)
+ setLocalSSRC(
+ MediaType.VIDEO,
+ videoStream.getLocalSourceID());
+ }
+ else if (MediaStream.PNAME_REMOTE_SSRC.equals(propertyName))
+ {
+ Object source = evt.getSource();
+
+ if (source == audioStream)
+ setRemoteSSRC(
+ MediaType.AUDIO,
+ audioStream.getRemoteSourceID());
+ else if (source == videoStream)
+ setRemoteSSRC(
+ MediaType.VIDEO,
+ videoStream.getRemoteSourceID());
+ }
+ }
+ };
+
+ private final VideoNotifierSupport videoNotifierSupport
+ = new VideoNotifierSupport(this, true);
+
+ /**
+ * The <tt>VideoMediaStream</tt> which this instance uses to send and
+ * receive video.
+ */
+ private VideoMediaStream videoStream;
+
+ /**
+ * The <tt>VideoListener</tt> which listens to {@link #videoStream} for
+ * changes in the availability of visual <tt>Component</tt>s displaying
+ * remote video and re-fires them as originating from this instance.
+ */
+ private final VideoListener videoStreamVideoListener
+ = new VideoListener()
+ {
+ public void videoAdded(VideoEvent event)
+ {
+ VideoEvent clone = event.clone(MediaHandler.this);
+
+ fireVideoEvent(clone);
+ if (clone.isConsumed())
+ event.consume();
+ }
+
+ public void videoRemoved(VideoEvent event)
+ {
+ // Forwarded in the same way as VIDEO_ADDED.
+ videoAdded(event);
+ }
+
+ public void videoUpdate(VideoEvent event)
+ {
+ // Forwarded in the same way as VIDEO_ADDED.
+ videoAdded(event);
+ }
+ };
+
+ public MediaHandler()
+ {
+ int mediaTypeValueCount = MediaType.values().length;
+
+ localSSRCs = new long[mediaTypeValueCount];
+ Arrays.fill(localSSRCs, CallPeerMediaHandler.SSRC_UNKNOWN);
+ remoteSSRCs = new long[mediaTypeValueCount];
+ Arrays.fill(remoteSSRCs, CallPeerMediaHandler.SSRC_UNKNOWN);
+ }
+
+ boolean addKeyFrameRequester(
+ int index,
+ KeyFrameControl.KeyFrameRequester keyFrameRequester)
+ {
+ if (keyFrameRequester == null)
+ throw new NullPointerException("keyFrameRequester");
+ else
+ {
+ synchronized (keyFrameRequesters)
+ {
+ if (keyFrameRequesters.contains(keyFrameRequester))
+ return false;
+ else
+ {
+ keyFrameRequesters.add(
+ (index == -1)
+ ? keyFrameRequesters.size()
+ : index,
+ keyFrameRequester);
+ return true;
+ }
+ }
+ }
+ }
+
+ void addSrtpListener(SrtpListener listener)
+ {
+ if (listener == null)
+ throw new NullPointerException("listener");
+ else
+ {
+ synchronized (srtpListeners)
+ {
+ if (!srtpListeners.contains(listener))
+ srtpListeners.add(listener);
+ }
+ }
+ }
+
+ /**
+ * Registers a specific <tt>VideoListener</tt> with this instance so that it
+ * starts receiving notifications from it about changes in the availability
+ * of visual <tt>Component</tt>s displaying video.
+ *
+ * @param listener the <tt>VideoListener</tt> to be registered with this
+ * instance and to start receiving notifications from it about changes in
+ * the availability of visual <tt>Component</tt>s displaying video
+ */
+ void addVideoListener(VideoListener listener)
+ {
+ videoNotifierSupport.addVideoListener(listener);
+ }
+
+ /**
+ * Closes the <tt>MediaStream</tt> that this instance uses for a specific
+ * <tt>MediaType</tt> and prepares it for garbage collection.
+ *
+ * @param type the <tt>MediaType</tt> that we'd like to stop a stream for.
+ */
+ protected void closeStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaType type)
+ {
+ if (type == MediaType.AUDIO)
+ setAudioStream(null);
+ else
+ setVideoStream(null);
+
+ // Clean up the SRTP controls used for the associated Call.
+ Iterator<Map.Entry<MediaTypeSrtpControl, SrtpControl>> iter
+ = srtpControls.entrySet().iterator();
+
+ while (iter.hasNext())
+ {
+ Map.Entry<MediaTypeSrtpControl, SrtpControl> entry = iter.next();
+
+ if (entry.getKey().mediaType == type)
+ {
+ entry.getValue().cleanup();
+ iter.remove();
+ }
+ }
+ }
+
+ /**
+ * Configures <tt>stream</tt> to use the specified <tt>device</tt>,
+ * <tt>format</tt>, <tt>target</tt>, <tt>direction</tt>, etc.
+ *
+ * @param device the <tt>MediaDevice</tt> to be used by <tt>stream</tt>
+ * for capture and playback
+ * @param format the <tt>MediaFormat</tt> that we'd like the new stream
+ * to transmit in.
+ * @param target the <tt>MediaStreamTarget</tt> containing the RTP and
+ * RTCP address:port couples that the new stream would be sending
+ * packets to.
+ * @param direction the <tt>MediaDirection</tt> that we'd like the new
+ * stream to use (i.e. sendonly, sendrecv, recvonly, or inactive).
+ * @param rtpExtensions the list of <tt>RTPExtension</tt>s that should be
+ * enabled for this stream.
+ * @param stream the <tt>MediaStream</tt> that we'd like to configure.
+ * @param masterStream whether the stream to be used as master if secured
+ *
+ * @return the <tt>MediaStream</tt> that we received as a parameter (for
+ * convenience reasons).
+ *
+ * @throws OperationFailedException if setting the <tt>MediaFormat</tt>
+ * or connecting to the specified <tt>MediaDevice</tt> fails for some
+ * reason.
+ */
+ protected MediaStream configureStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaDevice device,
+ MediaFormat format,
+ MediaStreamTarget target,
+ MediaDirection direction,
+ List<RTPExtension> rtpExtensions,
+ MediaStream stream,
+ boolean masterStream)
+ throws OperationFailedException
+ {
+ registerDynamicPTsWithStream(callPeerMediaHandler, stream);
+ registerRTPExtensionsWithStream(
+ callPeerMediaHandler,
+ rtpExtensions, stream);
+
+ stream.setDevice(device);
+ stream.setTarget(target);
+ stream.setDirection(direction);
+ stream.setFormat(format);
+
+ MediaAwareCall<?, ?, ?> call = callPeerMediaHandler.getPeer().getCall();
+ MediaType mediaType
+ = (stream instanceof AudioMediaStream)
+ ? MediaType.AUDIO
+ : MediaType.VIDEO;
+
+ stream.setRTPTranslator(call.getRTPTranslator(mediaType));
+
+ switch (mediaType)
+ {
+ case AUDIO:
+ setAudioStream((AudioMediaStream) stream);
+ callPeerMediaHandler.registerAudioLevelListeners(audioStream);
+ break;
+
+ case VIDEO:
+ setVideoStream((VideoMediaStream) stream);
+ break;
+ }
+
+ if (call.isDefaultEncrypted())
+ {
+ /*
+ * We'll use the audio stream as the master stream when using SRTP
+ * multistreams.
+ */
+ SrtpControl srtpControl = stream.getSrtpControl();
+
+ srtpControl.setMasterSession(masterStream);
+ srtpControl.setSrtpListener(srtpListener);
+ srtpControl.start(mediaType);
+ }
+
+ return stream;
+ }
+
+ /**
+ * Notifies the <tt>VideoListener</tt>s registered with this
+ * <tt>MediaHandler</tt> about a specific type of change in the availability
+ * of a specific visual <tt>Component</tt> depicting video.
+ *
+ * @param type the type of change as defined by <tt>VideoEvent</tt> in the
+ * availability of the specified visual <tt>Component</tt> depicting video
+ * @param visualComponent the visual <tt>Component</tt> depicting video
+ * which has been added or removed in this <tt>MediaHandler</tt>
+ * @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 <tt>true</tt> if this event and, more specifically, the visual
+ * <tt>Component</tt> it describes have been consumed and should be
+ * considered owned, referenced (which is important because
+ * <tt>Component</tt>s belong to a single <tt>Container</tt> at a time);
+ * otherwise, <tt>false</tt>
+ */
+ protected boolean fireVideoEvent(
+ int type,
+ Component visualComponent,
+ int origin)
+ {
+ return
+ videoNotifierSupport.fireVideoEvent(
+ type, visualComponent, origin,
+ true);
+ }
+
+ /**
+ * Notifies the <tt>VideoListener</tt>s registered with this
+ * <tt>MediaHandler</tt> about a specific <tt>VideoEvent</tt>.
+ *
+ * @param event the <tt>VideoEvent</tt> to fire to the
+ * <tt>VideoListener</tt>s registered with this <tt>MediaHandler</tt>
+ */
+ protected void fireVideoEvent(VideoEvent event)
+ {
+ videoNotifierSupport.fireVideoEvent(event, true);
+ }
+
+ /**
+ * Gets the SRTP control type used for a given media type.
+ *
+ * @param mediaType the <tt>MediaType</tt> to get the SRTP control type for
+ * @return the SRTP control type (MIKEY, SDES, ZRTP) used for the given
+ * media type or <tt>null</tt> if SRTP is not enabled for the given media
+ * type
+ */
+ SrtpControlType getEncryptionMethod(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaType mediaType)
+ {
+ /*
+ * Find the first existing SRTP control type for the specified media
+ * type which is active i.e. secures the communication.
+ */
+ for(SrtpControlType srtpControlType : SrtpControlType.values())
+ {
+ SrtpControl srtpControl
+ = getSrtpControls(callPeerMediaHandler).get(
+ new MediaTypeSrtpControl(mediaType, srtpControlType));
+
+ if((srtpControl != null)
+ && srtpControl.getSecureCommunicationStatus())
+ {
+ return srtpControlType;
+ }
+ }
+
+ return null;
+ }
+
+ long getRemoteSSRC(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaType mediaType)
+ {
+ return remoteSSRCs[mediaType.ordinal()];
+ }
+
+ /**
+ * Gets the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this
+ * instance.
+ *
+ * @return the <tt>SrtpControl</tt>s of the <tt>MediaStream</tt>s of this
+ * instance
+ */
+ Map<MediaTypeSrtpControl, SrtpControl> getSrtpControls(
+ CallPeerMediaHandler<?> callPeerMediaHandler)
+ {
+ return srtpControls;
+ }
+
+ private SrtpListener[] getSrtpListeners()
+ {
+ synchronized (srtpListeners)
+ {
+ return
+ srtpListeners.toArray(new SrtpListener[srtpListeners.size()]);
+ }
+ }
+
+ /**
+ * Gets the <tt>MediaStream</tt> of this instance which is of a specific
+ * <tt>MediaType</tt>. If this instance doesn't have such a
+ * <tt>MediaStream</tt>, returns <tt>null</tt>
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> to
+ * retrieve
+ * @return the <tt>MediaStream</tt> of this <tt>CallPeerMediaHandler</tt>
+ * which is of the specified <tt>mediaType</tt> if this instance has such a
+ * <tt>MediaStream</tt>; otherwise, <tt>null</tt>
+ */
+ MediaStream getStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaType mediaType)
+ {
+ switch (mediaType)
+ {
+ case AUDIO:
+ return audioStream;
+ case VIDEO:
+ return videoStream;
+ default:
+ throw new IllegalArgumentException("mediaType");
+ }
+ }
+
+ /**
+ * Creates if necessary, and configures the stream that this
+ * <tt>MediaHandler</tt> is using for the <tt>MediaType</tt> matching the
+ * one of the <tt>MediaDevice</tt>.
+ *
+ * @param connector the <tt>MediaConnector</tt> that we'd like to bind the
+ * newly created stream to.
+ * @param device the <tt>MediaDevice</tt> that we'd like to attach the newly
+ * created <tt>MediaStream</tt> to.
+ * @param format the <tt>MediaFormat</tt> that we'd like the new
+ * <tt>MediaStream</tt> to be set to transmit in.
+ * @param target the <tt>MediaStreamTarget</tt> containing the RTP and RTCP
+ * address:port couples that the new stream would be sending packets to.
+ * @param direction the <tt>MediaDirection</tt> that we'd like the new
+ * stream to use (i.e. sendonly, sendrecv, recvonly, or inactive).
+ * @param rtpExtensions the list of <tt>RTPExtension</tt>s that should be
+ * enabled for this stream.
+ * @param masterStream whether the stream to be used as master if secured
+ *
+ * @return the newly created <tt>MediaStream</tt>.
+ *
+ * @throws OperationFailedException if creating the stream fails for any
+ * reason (like for example accessing the device or setting the format).
+ */
+ MediaStream initStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ StreamConnector connector,
+ MediaDevice device,
+ MediaFormat format,
+ MediaStreamTarget target,
+ MediaDirection direction,
+ List<RTPExtension> rtpExtensions,
+ boolean masterStream)
+ throws OperationFailedException
+ {
+ MediaType mediaType = device.getMediaType();
+ MediaStream stream = getStream(callPeerMediaHandler, mediaType);
+
+ if (stream == null)
+ {
+ if (logger.isTraceEnabled() && (mediaType != format.getMediaType()))
+ logger.trace("The media types of device and format differ.");
+
+ MediaService mediaService
+ = ProtocolMediaActivator.getMediaService();
+ /*
+ * The default SrtpControlType is ZRTP. But if a SrtpControl exists
+ * already, it determines the SrtpControlType.
+ */
+ SrtpControlType srtpControlType
+ = (srtpControls.size() > 0)
+ ? srtpControls.firstKey().srtpControlType
+ : SrtpControlType.ZRTP;
+ MediaTypeSrtpControl mediaTypeSrtpControl
+ = new MediaTypeSrtpControl(mediaType, srtpControlType);
+ SrtpControl srtpControl = srtpControls.get(mediaTypeSrtpControl);
+
+ // If a SrtpControl does not exist yet, create a default one.
+ if (srtpControl == null)
+ {
+ /*
+ * The default SrtpControl is currently ZRTP without the
+ * hello-hash. It is created by the MediaStream implementation.
+ * Consequently, it needs to be linked to the srtpControls Map.
+ */
+ stream = mediaService.createMediaStream(connector, device);
+ srtpControls.put(mediaTypeSrtpControl, stream.getSrtpControl());
+ }
+ else
+ {
+ stream
+ = mediaService.createMediaStream(
+ connector,
+ device,
+ srtpControl);
+ }
+ }
+ else
+ {
+ // this is a reinit
+ }
+
+ return
+ configureStream(
+ callPeerMediaHandler,
+ device, format, target, direction, rtpExtensions, stream,
+ masterStream);
+ }
+
+ /**
+ * Processes a request for a (video) key frame from a remote peer to the
+ * local peer.
+ *
+ * @return <tt>true</tt> if the request for a (video) key frame has been
+ * honored by the local peer; otherwise, <tt>false</tt>
+ */
+ boolean processKeyFrameRequest(CallPeerMediaHandler<?> callPeerMediaHandler)
+ {
+ KeyFrameControl keyFrameControl = this.keyFrameControl;
+
+ return
+ (keyFrameControl == null)
+ ? null
+ : keyFrameControl.keyFrameRequest();
+ }
+
+ /**
+ * Registers all dynamic payload mappings known to this
+ * <tt>MediaHandler</tt> with the specified <tt>MediaStream</tt>.
+ *
+ * @param stream the <tt>MediaStream</tt> that we'd like to register our
+ * dynamic payload mappings with.
+ */
+ private void registerDynamicPTsWithStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ MediaStream stream)
+ {
+ for (Map.Entry<MediaFormat, Byte> mapEntry
+ : callPeerMediaHandler.getDynamicPayloadTypes().getMappings()
+ .entrySet())
+ {
+ byte pt = mapEntry.getValue();
+ MediaFormat fmt = mapEntry.getKey();
+
+ stream.addDynamicRTPPayloadType(pt, fmt);
+ }
+ }
+
+ /**
+ * Registers with the specified <tt>MediaStream</tt> all RTP extensions
+ * negotiated by this <tt>MediaHandler</tt>.
+ *
+ * @param stream the <tt>MediaStream</tt> that we'd like to register our
+ * <tt>RTPExtension</tt>s with.
+ * @param rtpExtensions the list of <tt>RTPExtension</tt>s that should be
+ * enabled for <tt>stream</tt>.
+ */
+ private void registerRTPExtensionsWithStream(
+ CallPeerMediaHandler<?> callPeerMediaHandler,
+ List<RTPExtension> rtpExtensions,
+ MediaStream stream)
+ {
+ DynamicRTPExtensionsRegistry rtpExtensionsRegistry
+ = callPeerMediaHandler.getRtpExtensionsRegistry();
+
+ for (RTPExtension rtpExtension : rtpExtensions)
+ {
+ byte extensionID
+ = rtpExtensionsRegistry.getExtensionMapping(rtpExtension);
+
+ stream.addRTPExtension(extensionID, rtpExtension);
+ }
+ }
+
+ boolean removeKeyFrameRequester(
+ KeyFrameControl.KeyFrameRequester keyFrameRequester)
+ {
+ if (keyFrameRequester == null)
+ return false;
+ else
+ {
+ synchronized (keyFrameRequesters)
+ {
+ return keyFrameRequesters.remove(keyFrameRequester);
+ }
+ }
+ }
+
+ void removeSrtpListener(SrtpListener listener)
+ {
+ if (listener != null)
+ {
+ synchronized (srtpListeners)
+ {
+ srtpListeners.remove(listener);
+ }
+ }
+ }
+
+ /**
+ * Unregisters a specific <tt>VideoListener</tt> from this instance so that
+ * it stops receiving notifications from it about changes in the
+ * availability of visual <tt>Component</tt>s displaying video.
+ *
+ * @param listener the <tt>VideoListener</tt> to be unregistered from this
+ * instance and to stop receiving notifications from it about changes in the
+ * availability of visual <tt>Component</tt>s displaying video
+ */
+ void removeVideoListener(VideoListener listener)
+ {
+ videoNotifierSupport.removeVideoListener(listener);
+ }
+
+ /**
+ * Requests a key frame from the remote peer of the associated
+ * <tt>VideoMediaStream</tt> of this <tt>MediaHandler</tt>.
+ *
+ * @return <tt>true</tt> if this <tt>MediaHandler</tt> has indeed requested
+ * a key frame from the remote peer of its associated
+ * <tt>VideoMediaStream</tt> in response to the call; otherwise,
+ * <tt>false</tt>
+ */
+ protected boolean requestKeyFrame()
+ {
+ KeyFrameControl.KeyFrameRequester[] keyFrameRequesters;
+
+ synchronized (this.keyFrameRequesters)
+ {
+ keyFrameRequesters
+ = this.keyFrameRequesters.toArray(
+ new KeyFrameControl.KeyFrameRequester[
+ this.keyFrameRequesters.size()]);
+ }
+
+ for (KeyFrameControl.KeyFrameRequester keyFrameRequester
+ : keyFrameRequesters)
+ {
+ if (keyFrameRequester.requestKeyFrame())
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets the <tt>AudioMediaStream</tt> which this instance is to use to send
+ * and receive audio.
+ *
+ * @param audioStream the <tt>AudioMediaStream</tt> which this instance is
+ * to use to send and receive audio
+ */
+ private void setAudioStream(AudioMediaStream audioStream)
+ {
+ if (this.audioStream != audioStream)
+ {
+ if (this.audioStream != null)
+ {
+ this.audioStream
+ .removePropertyChangeListener(
+ streamPropertyChangeListener);
+
+ this.audioStream.close();
+ }
+
+ this.audioStream = audioStream;
+
+ long audioLocalSSRC;
+ long audioRemoteSSRC;
+
+ if (this.audioStream != null)
+ {
+ this.audioStream
+ .addPropertyChangeListener(
+ streamPropertyChangeListener);
+ audioLocalSSRC = this.audioStream.getLocalSourceID();
+ audioRemoteSSRC = this.audioStream.getRemoteSourceID();
+ }
+ else
+ {
+ audioLocalSSRC
+ = audioRemoteSSRC
+ = CallPeerMediaHandler.SSRC_UNKNOWN;
+ }
+
+ setLocalSSRC(MediaType.AUDIO, audioLocalSSRC);
+ setRemoteSSRC(MediaType.AUDIO, audioRemoteSSRC);
+ }
+ }
+
+ /**
+ * Sets the <tt>KeyFrameControl</tt> currently known to this
+ * <tt>MediaHandler</tt> made available by a specific
+ * <tt>VideoMediaStream</tt>.
+ *
+ * @param videoStream the <tt>VideoMediaStream</tt> the
+ * <tt>KeyFrameControl</tt> of which is to be set as the currently known to
+ * this <tt>MediaHandler</tt>
+ */
+ private void setKeyFrameControlFromVideoStream(VideoMediaStream videoStream)
+ {
+ KeyFrameControl keyFrameControl
+ = (videoStream == null) ? null : videoStream.getKeyFrameControl();
+
+ if (this.keyFrameControl != keyFrameControl)
+ {
+ if (this.keyFrameControl != null)
+ this.keyFrameControl.removeKeyFrameRequester(keyFrameRequester);
+
+ this.keyFrameControl = keyFrameControl;
+
+ if (this.keyFrameControl != null)
+ this.keyFrameControl.addKeyFrameRequester(-1, keyFrameRequester);
+ }
+ }
+
+ /**
+ * Sets the last-known local SSRC of the <tt>MediaStream</tt> of a specific
+ * <tt>MediaType</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> to
+ * set the last-known local SSRC of
+ * @param localSSRC the last-known local SSRC of the <tt>MediaStream</tt> of
+ * the specified <tt>mediaType</tt>
+ */
+ private void setLocalSSRC(MediaType mediaType, long localSSRC)
+ {
+ int index = mediaType.ordinal();
+ long oldValue = localSSRCs[index];
+
+ if (oldValue != localSSRC)
+ {
+ localSSRCs[index] = localSSRC;
+
+ String property;
+
+ switch (mediaType)
+ {
+ case AUDIO:
+ property = CallPeerMediaHandler.AUDIO_LOCAL_SSRC;
+ break;
+ case VIDEO:
+ property = CallPeerMediaHandler.VIDEO_LOCAL_SSRC;
+ break;
+ default:
+ property = null;
+ }
+ if (property != null)
+ firePropertyChange(property, oldValue, localSSRC);
+ }
+ }
+
+ /**
+ * Sets the last-known local SSRC of the <tt>MediaStream</tt> of a specific
+ * <tt>MediaType</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> to
+ * set the last-known local SSRC of
+ * @param localSSRC the last-known local SSRC of the <tt>MediaStream</tt> of
+ * the specified <tt>mediaType</tt>
+ */
+ private void setRemoteSSRC(MediaType mediaType, long remoteSSRC)
+ {
+ int index = mediaType.ordinal();
+ long oldValue = remoteSSRCs[index];
+
+ if (oldValue != remoteSSRC)
+ {
+ remoteSSRCs[index] = remoteSSRC;
+
+ String property;
+
+ switch (mediaType)
+ {
+ case AUDIO:
+ property = CallPeerMediaHandler.AUDIO_REMOTE_SSRC;
+ break;
+ case VIDEO:
+ property = CallPeerMediaHandler.VIDEO_REMOTE_SSRC;
+ break;
+ default:
+ property = null;
+ }
+ if (property != null)
+ firePropertyChange(property, oldValue, remoteSSRC);
+ }
+ }
+
+ /**
+ * Sets the <tt>VideoMediaStream</tt> which this instance is to use to send
+ * and receive video.
+ *
+ * @param videoStream the <tt>VideoMediaStream</tt> which this instance is
+ * to use to send and receive 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.
+ */
+ List<Component> oldVisualComponents = null;
+
+ if (this.videoStream != null)
+ {
+ this.videoStream.removePropertyChangeListener(
+ streamPropertyChangeListener);
+
+ this.videoStream.removeVideoListener(videoStreamVideoListener);
+ oldVisualComponents = this.videoStream.getVisualComponents();
+
+ /*
+ * The current videoStream is going away so this
+ * CallPeerMediaHandler should no longer use its
+ * KeyFrameControl.
+ */
+ setKeyFrameControlFromVideoStream(null);
+
+ this.videoStream.close();
+ }
+
+ this.videoStream = videoStream;
+
+ /*
+ * The videoStream has just changed so this CallPeerMediaHandler
+ * should use its KeyFrameControl.
+ */
+ setKeyFrameControlFromVideoStream(this.videoStream);
+
+ long videoLocalSSRC;
+ long videoRemoteSSRC;
+ /*
+ * Make sure we will notify the registered VideoListeners about
+ * changes in the availability of video in the new videoStream.
+ */
+ List<Component> newVisualComponents = null;
+
+ if (this.videoStream != null)
+ {
+ this.videoStream.addPropertyChangeListener(
+ streamPropertyChangeListener);
+ videoLocalSSRC = this.videoStream.getLocalSourceID();
+ videoRemoteSSRC = this.videoStream.getRemoteSourceID();
+
+ this.videoStream.addVideoListener(videoStreamVideoListener);
+ newVisualComponents = this.videoStream.getVisualComponents();
+ }
+ else
+ {
+ videoLocalSSRC
+ = videoRemoteSSRC
+ = CallPeerMediaHandler.SSRC_UNKNOWN;
+ }
+
+ setLocalSSRC(MediaType.VIDEO, videoLocalSSRC);
+ setRemoteSSRC(MediaType.VIDEO, videoRemoteSSRC);
+
+ /*
+ * Notify the VideoListeners in case there was a change in the
+ * availability of the visual Components displaying remote video.
+ */
+ if ((oldVisualComponents != null) && !oldVisualComponents.isEmpty())
+ {
+ /*
+ * Discard Components which are present in the old and in the
+ * new Lists.
+ */
+ if (newVisualComponents == null)
+ newVisualComponents = Collections.emptyList();
+ for (Component oldVisualComponent : oldVisualComponents)
+ {
+ if (!newVisualComponents.remove(oldVisualComponent))
+ {
+ fireVideoEvent(
+ VideoEvent.VIDEO_REMOVED,
+ oldVisualComponent,
+ VideoEvent.REMOTE);
+ }
+ }
+ }
+ if ((newVisualComponents != null) && !newVisualComponents.isEmpty())
+ {
+ for (Component newVisualComponent : newVisualComponents)
+ {
+ fireVideoEvent(
+ VideoEvent.VIDEO_ADDED,
+ newVisualComponent,
+ VideoEvent.REMOTE);
+ }
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java
index 5758e84..5ee00ff 100644
--- a/src/net/java/sip/communicator/service/protocol/media/TransportManager.java
+++ b/src/net/java/sip/communicator/service/protocol/media/TransportManager.java
@@ -155,30 +155,50 @@ public abstract class TransportManager<U extends MediaAwareCallPeer<?, ?, ?>>
}
/**
- * Closes both the control and the data socket of the specified connector
- * and releases its reference (if it wasn't the case already).
+ * Closes the existing <tt>StreamConnector</tt>, if any, associated with a
+ * specific <tt>MediaType</tt> and removes its reference from this
+ * <tt>TransportManager</tt>.
*
- * @param mediaType the type of the connector we'd like to close.
+ * @param mediaType the <tt>MediaType</tt> associated with the
+ * <tt>StreamConnector</tt> to close
*/
public void closeStreamConnector(MediaType mediaType)
{
int index = mediaType.ordinal();
- StreamConnector connector = streamConnectors[index];
+ StreamConnector streamConnector = streamConnectors[index];
- if (connector != null)
+ if (streamConnector != null)
{
- /*
- * XXX The connected owns the sockets so it is important that it
- * decides whether to close them i.e. this TransportManager is not
- * allowed to explicitly close the sockets by itself.
- */
- connector.close();
-
+ closeStreamConnector(mediaType, streamConnector);
streamConnectors[index] = null;
}
}
/**
+ * Closes a specific <tt>StreamConnector</tt> associated with a specific
+ * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to
+ * the specified <tt>streamConnector</tt>, it remains. Allows extenders to
+ * override and perform additional customizations to the closing of the
+ * specified <tt>streamConnector</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> associated with the specified
+ * <tt>streamConnector</tt>
+ * @param streamConnector the <tt>StreamConnector</tt> to be closed
+ * @see #closeStreamConnector(MediaType)
+ */
+ protected void closeStreamConnector(
+ MediaType mediaType,
+ StreamConnector streamConnector)
+ {
+ /*
+ * XXX The connected owns the sockets so it is important that it
+ * decides whether to close them i.e. this TransportManager is not
+ * allowed to explicitly close the sockets by itself.
+ */
+ streamConnector.close();
+ }
+
+ /**
* Creates a media <tt>StreamConnector</tt>. The method takes into account
* the minimum and maximum media port boundaries.
*
diff --git a/src/net/java/sip/communicator/service/protocol/media/protocol.media.manifest.mf b/src/net/java/sip/communicator/service/protocol/media/protocol.media.manifest.mf
index 27715f1..c855c6f 100644
--- a/src/net/java/sip/communicator/service/protocol/media/protocol.media.manifest.mf
+++ b/src/net/java/sip/communicator/service/protocol/media/protocol.media.manifest.mf
@@ -15,5 +15,6 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.util,
+ net.java.sip.communicator.util.event,
org.ice4j.ice
Export-Package: net.java.sip.communicator.service.protocol.media
diff --git a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
index 153ca98..775a2ab 100644
--- a/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
+++ b/src/net/java/sip/communicator/service/protocol/protocol.provider.manifest.mf
@@ -8,8 +8,9 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.credentialsstorage,
net.java.sip.communicator.service.neomedia,
+ net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
- net.java.sip.communicator.service.resources
+ net.java.sip.communicator.util.event
Export-Package: net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.aimconstants,
net.java.sip.communicator.service.protocol.event,
diff --git a/src/net/java/sip/communicator/util/PropertyChangeNotifier.java b/src/net/java/sip/communicator/util/event/PropertyChangeNotifier.java
index 9039ea9..a5f683f 100644
--- a/src/net/java/sip/communicator/util/PropertyChangeNotifier.java
+++ b/src/net/java/sip/communicator/util/event/PropertyChangeNotifier.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.util;
+package net.java.sip.communicator.util.event;
import java.util.*;
import java.beans.*;
@@ -13,7 +13,7 @@ import java.beans.*;
* Represents a source of <tt>PropertyChangeEvent</tt>s which notifies
* <tt>PropertyChangeListener</tt>s about changes in the values of properties.
*
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
*/
public class PropertyChangeNotifier
{
diff --git a/src/net/java/sip/communicator/service/neomedia/event/SizeChangeVideoEvent.java b/src/net/java/sip/communicator/util/event/SizeChangeVideoEvent.java
index 5db15b4..e4982bb 100644
--- a/src/net/java/sip/communicator/service/neomedia/event/SizeChangeVideoEvent.java
+++ b/src/net/java/sip/communicator/util/event/SizeChangeVideoEvent.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.service.neomedia.event;
+package net.java.sip.communicator.util.event;
import java.awt.*;
@@ -12,7 +12,7 @@ import java.awt.*;
* Represents a <tt>VideoEvent</tt> which notifies about an update to the size
* of a specific visual <tt>Component</tt> depicting video.
*
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
*/
public class SizeChangeVideoEvent
extends VideoEvent
@@ -64,6 +64,16 @@ public class SizeChangeVideoEvent
this.height = height;
}
+ @Override
+ public VideoEvent clone(Object source)
+ {
+ return
+ new SizeChangeVideoEvent(
+ source,
+ getVisualComponent(), getOrigin(),
+ getWidth(), getHeight());
+ }
+
/**
* Gets the new height of the associated visual <tt>Component</tt>.
*
diff --git a/src/net/java/sip/communicator/service/neomedia/event/VideoEvent.java b/src/net/java/sip/communicator/util/event/VideoEvent.java
index 6135f47..695e004 100644
--- a/src/net/java/sip/communicator/service/neomedia/event/VideoEvent.java
+++ b/src/net/java/sip/communicator/util/event/VideoEvent.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.service.neomedia.event;
+package net.java.sip.communicator.util.event;
import java.awt.*;
import java.util.*;
@@ -14,7 +14,7 @@ import java.util.*;
* depicting video to notify about changes in the availability of such
* <tt>Component</tt>s.
*
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
*/
public class VideoEvent
extends EventObject
@@ -104,6 +104,14 @@ public class VideoEvent
this.origin = origin;
}
+ public VideoEvent clone(Object source)
+ {
+ return
+ new VideoEvent(
+ source,
+ getType(), getVisualComponent(), getOrigin());
+ }
+
/**
* Consumes this event and, more specifically, marks the <tt>Component</tt>
* it describes as owned, referenced in order to let other potential
diff --git a/src/net/java/sip/communicator/service/neomedia/event/VideoListener.java b/src/net/java/sip/communicator/util/event/VideoListener.java
index 50c7089..25266a7 100644
--- a/src/net/java/sip/communicator/service/neomedia/event/VideoListener.java
+++ b/src/net/java/sip/communicator/util/event/VideoListener.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.service.neomedia.event;
+package net.java.sip.communicator.util.event;
import java.util.*;
@@ -13,7 +13,7 @@ import java.util.*;
* of visual <tt>Component</tt>s representing video such as adding and
* removing.
*
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
*/
public interface VideoListener
extends EventListener
diff --git a/src/net/java/sip/communicator/service/neomedia/event/VideoNotifierSupport.java b/src/net/java/sip/communicator/util/event/VideoNotifierSupport.java
index 6029b89..af30eaf 100644
--- a/src/net/java/sip/communicator/service/neomedia/event/VideoNotifierSupport.java
+++ b/src/net/java/sip/communicator/util/event/VideoNotifierSupport.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.service.neomedia.event;
+package net.java.sip.communicator.util.event;
import java.awt.*;
import java.util.*;
diff --git a/src/net/java/sip/communicator/util/util.manifest.mf b/src/net/java/sip/communicator/util/util.manifest.mf
index 1c73e3e..8e980fc 100644
--- a/src/net/java/sip/communicator/util/util.manifest.mf
+++ b/src/net/java/sip/communicator/util/util.manifest.mf
@@ -42,11 +42,12 @@ Import-Package: org.xml.sax,
com.sun.awt,
org.xbill.DNS
Export-Package: net.java.sip.communicator.util.xml,
+ net.java.sip.communicator.util.swing.transparent,
net.java.sip.communicator.util.swing.plaf,
net.java.sip.communicator.util.swing.event,
- net.java.sip.communicator.util.swing,
- net.java.sip.communicator.util.swing.transparent,
net.java.sip.communicator.util.swing.border,
- net.java.sip.communicator.util,
+ net.java.sip.communicator.util.swing,
net.java.sip.communicator.util.skin,
- net.java.sip.communicator.util.launchutils
+ net.java.sip.communicator.util.launchutils,
+ net.java.sip.communicator.util.event,
+ net.java.sip.communicator.util