aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2012-01-18 16:48:29 +0000
committerSebastien Vincent <seb@jitsi.org>2012-01-18 16:48:29 +0000
commiteadc63024f6e89e5c34a0caa77423511685e5076 (patch)
treee6f8cf2d45eefeeed0636c7bba83e29a79c294db /src/net/java/sip/communicator/impl
parent78cefa3a27f81ea48ee6017a390db627191b50ed (diff)
downloadjitsi-eadc63024f6e89e5c34a0caa77423511685e5076.zip
jitsi-eadc63024f6e89e5c34a0caa77423511685e5076.tar.gz
jitsi-eadc63024f6e89e5c34a0caa77423511685e5076.tar.bz2
Adds support for audio devices hotplug as well as to change input/output audio devices during a call.
Diffstat (limited to 'src/net/java/sip/communicator/impl')
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java1
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java54
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java57
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java99
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java60
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java8
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/audiolevel/AudioLevelEffect.java16
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferStream.java32
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/AbstractMediaDevice.java7
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java14
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java48
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java250
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/JmfDeviceDetector.java24
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java4
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java81
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java31
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java78
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioDeviceChangedCallback.java20
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java4
19 files changed, 722 insertions, 166 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
index acd9393..cd73947 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
@@ -13,7 +13,6 @@ import javax.swing.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.call.CallPeerAdapter;
-import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.skin.*;
diff --git a/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java b/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
index ee86f0d..f16c52d 100644
--- a/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
+++ b/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
@@ -14,12 +14,14 @@ import javax.swing.event.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.impl.neomedia.device.*;
+import net.java.sip.communicator.impl.neomedia.portaudio.*;
/**
* @author Lubomir Marinov
*/
public class DeviceConfigurationComboBoxModel
- implements ComboBoxModel
+ implements ComboBoxModel,
+ PortAudioDeviceChangedCallback
{
/**
* Encapsulates CaptureDeviceInfo
@@ -133,6 +135,11 @@ public class DeviceConfigurationComboBoxModel
this.deviceConfiguration = deviceConfiguration;
this.type = type;
+
+ if(type == AUDIO)
+ {
+ PortAudio.addDeviceChangedCallback(this);
+ }
}
public void addListDataListener(ListDataListener listener)
@@ -169,10 +176,9 @@ public class DeviceConfigurationComboBoxModel
*/
private CaptureDevice[] getDevices()
{
- if (devices != null)
+ if (type != AUDIO && devices != null)
return devices;
-
CaptureDeviceInfo[] infos;
switch (type)
{
@@ -229,7 +235,6 @@ public class DeviceConfigurationComboBoxModel
throw new IllegalStateException("type");
}
-
for (CaptureDevice device : getDevices())
{
if (CaptureDevice.equals(device.info, info))
@@ -332,4 +337,45 @@ public class DeviceConfigurationComboBoxModel
devices = null;
}
}
+
+ /**
+ * Reinitializes audio devices.
+ */
+ public void reinitAudio()
+ {
+ if(type == AUDIO)
+ {
+ devices = null;
+ // only for PortAudio
+ if(deviceConfiguration.getAudioSystem().equals(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO))
+ {
+ String systemName = DeviceConfiguration.AUDIO_NONE;
+
+ deviceConfiguration.setAudioSystem(systemName, null, false);
+ fireContentsChanged(-1, -1);
+ setSelectedItem(DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO);
+ fireContentsChanged(-1, -1);
+ }
+ }
+ }
+
+ /**
+ * Callback when PortAudio device changed.
+ */
+ public void deviceChanged()
+ {
+ if(!SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ deviceChanged();
+ }
+ });
+ return;
+ }
+ reinitAudio();
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java
index cbcac53..b52def9 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaConfiguration.java
@@ -85,7 +85,7 @@ public class MediaConfiguration
* Creates the ui controls for portaudio.
* @param portAudioPanel the panel
*/
- private static void createPortAudioControls(JPanel portAudioPanel)
+ static void createPortAudioControls(JPanel portAudioPanel)
{
GridBagConstraints constraints = new GridBagConstraints();
constraints.insets = new Insets(3, 0, 3, 5);
@@ -182,13 +182,23 @@ public class MediaConfiguration
}
/**
- * Creates all the controls for a type(AUDIO or VIDEO)
+ * Creates basic controls for a type(AUDIO or VIDEO)
* @param type the type.
* @return the build Component.
*/
- private static Component createControls(int type)
+ public static Component createBasicControls(int type)
+ {
+ return createBasicControls(type, true);
+ }
+
+ /**
+ * Creates basic controls for a type(AUDIO or VIDEO)
+ * @param type the type.
+ * @param addTypeCbo add the type combobox
+ * @return the build Component.
+ */
+ public static Component createBasicControls(int type, boolean addTypeCbo)
{
- SIPCommTabbedPane container = new SIPCommTabbedPane();
final JComboBox cboDevice = new JComboBox();
cboDevice.setEditable(false);
cboDevice.setModel( new DeviceConfigurationComboBoxModel(
@@ -232,18 +242,22 @@ public class MediaConfiguration
else
portAudioPanel = null;
- JLabel label = new JLabel(getLabelText(type));
- label.setDisplayedMnemonic(getDisplayedMnemonic(type));
- label.setLabelFor(cboDevice);
+ JPanel pnlDeviceAndDetails = new TransparentPanel(new BorderLayout());
- Container pnlDevice
- = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
- pnlDevice.setMaximumSize(new Dimension(WIDTH, 25));
- pnlDevice.add(label);
- pnlDevice.add(cboDevice);
+ if(addTypeCbo)
+ {
+ Container pnlDevice
+ = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
+ JLabel label = new JLabel(getLabelText(type));
- JPanel pnlDeviceAndDetails = new TransparentPanel(new BorderLayout());
- pnlDeviceAndDetails.add(pnlDevice, BorderLayout.NORTH);
+ label.setDisplayedMnemonic(getDisplayedMnemonic(type));
+ label.setLabelFor(cboDevice);
+
+ pnlDevice.setMaximumSize(new Dimension(WIDTH, 25));
+ pnlDevice.add(label);
+ pnlDevice.add(cboDevice);
+ pnlDeviceAndDetails.add(pnlDevice, BorderLayout.NORTH);
+ }
// if creating controls for audio will add devices panel
// otherwise it is video controls and will add preview panel
@@ -258,6 +272,19 @@ public class MediaConfiguration
);
}
+ return pnlDeviceAndDetails;
+ }
+
+ /**
+ * Creates all the controls (including encoding) for a type(AUDIO or VIDEO)
+ * @param type the type.
+ * @return the build Component.
+ */
+ private static Component createControls(int type)
+ {
+ SIPCommTabbedPane container = new SIPCommTabbedPane();
+ Component pnlDeviceAndDetails = createBasicControls(type);
+
ResourceManagementService R = NeomediaActivator.getResources();
container.insertTab(
R.getI18NString("impl.media.configform.DEVICES"),
@@ -265,7 +292,7 @@ public class MediaConfiguration
container.insertTab(
R.getI18NString("impl.media.configform.ENCODINGS"),
null, createEncodingControls(type), null, 1);
- if (portAudioPanel == null)
+ if (type == DeviceConfigurationComboBoxModel.VIDEO)
container.insertTab(
R.getI18NString("impl.media.configform.VIDEO_MORE_SETTINGS"),
null, createVideoAdvancedSettings(), null, 2);
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
index c5e8b35..932923e 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
@@ -23,6 +23,7 @@ import net.java.sip.communicator.impl.neomedia.codec.*;
import net.java.sip.communicator.impl.neomedia.codec.video.*;
import net.java.sip.communicator.impl.neomedia.device.*;
import net.java.sip.communicator.impl.neomedia.format.*;
+import net.java.sip.communicator.impl.neomedia.portaudio.*;
import net.java.sip.communicator.impl.neomedia.protocol.*;
import net.java.sip.communicator.impl.neomedia.transform.sdes.*;
import net.java.sip.communicator.service.configuration.*;
@@ -39,7 +40,8 @@ import net.java.sip.communicator.util.swing.*;
* @author Dmitri Melnikov
*/
public class MediaServiceImpl
- implements MediaService
+ implements MediaService,
+ PortAudioDeviceChangedCallback
{
/**
* The logger.
@@ -154,6 +156,11 @@ public class MediaServiceImpl
new ArrayList<Recorder.Listener>();
/**
+ * Audio configuration panel.
+ */
+ private SIPCommDialog audioConfiguration = null;
+
+ /**
* Create a <tt>MediaStream</tt> which will use a specific
* <tt>MediaDevice</tt> for capture and playback of media. The new instance
* will not have a <tt>StreamConnector</tt> at the time of its construction
@@ -559,6 +566,52 @@ public class MediaServiceImpl
encodingConfiguration.initializeFormatPreferences();
encodingConfiguration.registerCustomPackages();
encodingConfiguration.registerCustomCodecs();
+
+ if(!OSUtils.IS_ANDROID)
+ {
+ final Component panel = MediaConfiguration.createBasicControls(
+ DeviceConfigurationComboBoxModel.AUDIO, false);
+
+ audioConfiguration = new SIPCommDialog()
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void close(boolean isEscaped)
+ {
+ setVisible(false);
+ }
+ };
+
+ TransparentPanel mainPanel = new TransparentPanel(new
+ BorderLayout());
+ TransparentPanel btnPanel = new TransparentPanel(new
+ FlowLayout(FlowLayout.RIGHT));
+ JButton btn = new JButton(NeomediaActivator.getResources().
+ getI18NString("service.gui.CLOSE"));
+ btn.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent evt)
+ {
+ audioConfiguration.setVisible(false);
+ }
+ });
+ btnPanel.add(btn);
+ mainPanel.add(panel, BorderLayout.CENTER);
+ mainPanel.add(btnPanel, BorderLayout.SOUTH);
+
+ audioConfiguration.add(mainPanel);
+ audioConfiguration.validate();
+ audioConfiguration.pack();
+
+ PortAudio.addDeviceChangedCallback(this);
+ }
}
/**
@@ -567,6 +620,7 @@ public class MediaServiceImpl
*/
void stop()
{
+ PortAudio.removeDeviceChangedCallback(this);
}
/**
@@ -582,7 +636,7 @@ public class MediaServiceImpl
/**
* Creates <tt>SDesControl</tt> used to control all SDes options.
- *
+ *
* @return SDesControl instance.
*/
public SDesControl createSDesControl()
@@ -1318,4 +1372,45 @@ public class MediaServiceImpl
{
return recorderListeners.iterator();
}
+
+ /**
+ * Callback called from native PortAudio side that notify device changed.
+ */
+ public void deviceChanged()
+ {
+ showAudioConfiguration();
+ }
+
+ /**
+ * Show audio configuration panel when media devices change.
+ */
+ private void showAudioConfiguration()
+ {
+ if(audioConfiguration == null)
+ {
+ return;
+ }
+
+ if (!SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ showAudioConfiguration();
+ }
+ });
+ return;
+ }
+
+ SwingUtilities.updateComponentTreeUI(
+ audioConfiguration.getComponent(0));
+ audioConfiguration.pack();
+ audioConfiguration.repaint();
+
+ if(!audioConfiguration.isVisible())
+ {
+ audioConfiguration.setVisible(true);
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
index 2b8e720..f6d7c21 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java
@@ -45,7 +45,8 @@ public class MediaStreamImpl
implements ReceiveStreamListener,
SendStreamListener,
SessionListener,
- RemoteListener
+ RemoteListener,
+ PropertyChangeListener
{
/**
* The <tt>Logger</tt> used by the <tt>MediaStreamImpl</tt> class and its
@@ -223,6 +224,11 @@ public class MediaStreamImpl
private StatisticsEngine statisticsEngine = null;
/**
+ * If the device session has been reinited.
+ */
+ private boolean deviceSessionReinit = false;
+
+ /**
* Initializes a new <tt>MediaStreamImpl</tt> instance which will use the
* specified <tt>MediaDevice</tt> for both capture and playback of media.
* The new instance will not have an associated <tt>StreamConnector</tt> and
@@ -266,6 +272,9 @@ public class MediaStreamImpl
*/
setDevice(device);
+ NeomediaActivator.getMediaServiceImpl().getDeviceConfiguration().
+ addPropertyChangeListener(this);
+
//TODO add option to disable ZRTP, e.g. by implementing a NullControl
this.srtpControl
= (srtpControl == null) ? new ZrtpControlImpl() : srtpControl;
@@ -499,6 +508,8 @@ public class MediaStreamImpl
*/
public void close()
{
+ NeomediaActivator.getMediaServiceImpl().getDeviceConfiguration().
+ removePropertyChangeListener(this);
stop();
closeSendStreams();
@@ -1359,9 +1370,14 @@ public class MediaStreamImpl
deviceSessionPropertyChangeListener);
// keep player active
- deviceSession.setDisposePlayerOnClose(false);
+ deviceSession.setDisposePlayerOnClose(
+ (deviceSession instanceof VideoMediaDeviceSession)
+ == false);
deviceSession.close();
+
+ deviceSession.getDevice().close();
deviceSession = null;
+ deviceSessionReinit = true;
}
else
{
@@ -1404,7 +1420,9 @@ public class MediaStreamImpl
synchronized (receiveStreamSyncRoot)
{
if (receiveStream != null)
+ {
deviceSession.setReceiveStream(receiveStream);
+ }
}
}
}
@@ -2346,7 +2364,8 @@ public class MediaStreamImpl
buff = new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX);
buff.append("call stats for incoming ")
- .append(getFormat().getMediaType()).append(" stream SSRC:")
+ .append(getFormat() != null ? getFormat().getMediaType() : "")
+ .append(" stream SSRC:")
.append(getRemoteSourceID())
.append("\n").append(StatisticsEngine.RTP_STAT_PREFIX)
.append("packets received: ").append(rs.getPacketsRecd())
@@ -2394,4 +2413,39 @@ public class MediaStreamImpl
logger.error("Error writing statistics", t);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // for the moment only handle audio device
+ if(this instanceof VideoMediaStreamImpl)
+ {
+ return;
+ }
+
+ String prop = evt.getPropertyName();
+
+ if(prop.equals(DeviceConfiguration.AUDIO_CAPTURE_DEVICE))
+ {
+ if(evt.getOldValue() == evt.getNewValue())
+ return;
+
+ if(deviceSessionReinit == false)
+ firePropertyChange("CHANGE_CAPTURE_DEV", evt.getOldValue(),
+ evt.getNewValue());
+
+ deviceSessionReinit = false;
+/*
+ MediaServiceImpl mediaService =
+ NeomediaActivator.getMediaServiceImpl();
+ MediaFormat format = getFormat();
+ this.setDevice(mediaService.createMixer(
+ mediaService.getDefaultDevice(MediaType.AUDIO,
+ MediaUseCase.CALL)));
+ this.setFormat(format);
+*/
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java b/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java
index cef3cb4..cd24643 100644
--- a/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/ZrtpControlImpl.java
@@ -205,7 +205,7 @@ public class ZrtpControlImpl
* calls this method to start the multi-stream ZRTP sessions.
*
* enable auto-start mode (auto-sensing) to the engine.
- * @param multiStreamData
+ * @param master master SRTP data
*/
public void setMultistream(SrtpControl master)
{
@@ -258,7 +258,7 @@ public class ZrtpControlImpl
/*
* (non-Javadoc)
- *
+ *
* @see
* net.java.sip.communicator.service.neomedia.ZrtpControl#getSecurityString
* ()
@@ -270,7 +270,7 @@ public class ZrtpControlImpl
/*
* (non-Javadoc)
- *
+ *
* @see
* net.java.sip.communicator.service.neomedia.ZrtpControl#isSecurityVerified
* ()
@@ -282,7 +282,7 @@ public class ZrtpControlImpl
/**
* Returns false, ZRTP exchanges is keys over the media path.
- *
+ *
* @return false
*/
public boolean requiresSecureSignalingTransport()
diff --git a/src/net/java/sip/communicator/impl/neomedia/audiolevel/AudioLevelEffect.java b/src/net/java/sip/communicator/impl/neomedia/audiolevel/AudioLevelEffect.java
index 5f4d99e..7d6e6d1 100644
--- a/src/net/java/sip/communicator/impl/neomedia/audiolevel/AudioLevelEffect.java
+++ b/src/net/java/sip/communicator/impl/neomedia/audiolevel/AudioLevelEffect.java
@@ -55,6 +55,11 @@ public class AudioLevelEffect
private Format[] supportedAudioFormats;
/**
+ * Audio level listener.
+ */
+ private SimpleAudioLevelListener audioLevelListener = null;
+
+ /**
* The minimum and maximum values of the scale
*/
public AudioLevelEffect()
@@ -87,8 +92,19 @@ public class AudioLevelEffect
*/
public void setAudioLevelListener(SimpleAudioLevelListener listener)
{
+ audioLevelListener = listener;
eventDispatcher.setAudioLevelListener(listener);
}
+
+ /**
+ * Returns audio level listener.
+ *
+ * @return audio level listener or <tt>null</tt> if not exist
+ */
+ public SimpleAudioLevelListener getAudioLevelListener()
+ {
+ return audioLevelListener;
+ }
/**
* Lists all of the input formats that this codec accepts.
diff --git a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferStream.java b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferStream.java
index 8b8afc6..dc2bd55 100644
--- a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferStream.java
+++ b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferStream.java
@@ -19,7 +19,7 @@ import net.java.sip.communicator.util.*;
/**
* Represents a <tt>PushBufferStream</tt> containing the result of the audio
* mixing of <tt>DataSource</tt>s.
- *
+ *
* @author Lyubomir Marinov
*/
public class AudioMixingPushBufferStream
@@ -84,7 +84,7 @@ public class AudioMixingPushBufferStream
* input data of a specific <tt>AudioMixerPushBufferStream</tt> and
* excluding from the mix the audio contributions of a specific
* <tt>AudioMixingPushBufferDataSource</tt>.
- *
+ *
* @param audioMixerStream the <tt>AudioMixerPushBufferStream</tt> reading
* data from input <tt>DataSource</tt>s and to push it to the new
* <tt>AudioMixingPushBufferStream</tt>
@@ -147,7 +147,7 @@ public class AudioMixingPushBufferStream
* Gets the <tt>AudioMixingPushBufferDataSource</tt> which created and owns
* this instance and defines the input data which is to not be mixed in the
* output of this <tt>PushBufferStream</tt>.
- *
+ *
* @return the <tt>AudioMixingPushBufferDataSource</tt> which created and
* owns this instance and defines the input data which is to not be mixed in
* the output of this <tt>PushBufferStream</tt>
@@ -173,7 +173,7 @@ public class AudioMixingPushBufferStream
/**
* Gets the maximum possible value for an audio sample of a specific
* <tt>AudioFormat</tt>.
- *
+ *
* @param outputFormat the <tt>AudioFormat</tt> of which to get the maximum
* possible value for an audio sample
* @return the maximum possible value for an audio sample of the specified
@@ -205,9 +205,9 @@ public class AudioMixingPushBufferStream
* Mixes as in audio mixing a specified collection of audio sample sets and
* returns the resulting mix audio sample set in a specific
* <tt>AudioFormat</tt>.
- *
+ *
* @param inputSamples the collection of audio sample sets to be mixed into
- * one audio sample set in the sense of audio mixing
+ * one audio sample set in the sense of audio mixing
* @param outputFormat the <tt>AudioFormat</tt> in which the resulting mix
* audio sample set is to be produced
* @param outputSampleCount the size of the resulting mix audio sample set
@@ -240,7 +240,7 @@ public class AudioMixingPushBufferStream
}
int maxOutputSample;
-
+
try
{
maxOutputSample = getMaxOutputSample(outputFormat);
@@ -249,23 +249,23 @@ public class AudioMixingPushBufferStream
{
throw new UnsupportedOperationException(ufex);
}
-
+
for (int[] inputStreamSamples : inputSamples)
{
-
+
if (inputStreamSamples == null)
continue;
-
+
int inputStreamSampleCount = inputStreamSamples.length;
-
+
if (inputStreamSampleCount <= 0)
continue;
-
+
for (int i = 0; i < inputStreamSampleCount; i++)
{
int inputStreamSample = inputStreamSamples[i];
int outputSample = outputSamples[i];
-
+
outputSamples[i]
= inputStreamSample
+ outputSample
@@ -318,7 +318,7 @@ public class AudioMixingPushBufferStream
AudioFormat outputFormat = getFormat();
int[] outputSamples
= mix(inputSamples, outputFormat, maxInputSampleCount);
-
+
Class<?> outputDataType = outputFormat.getDataType();
if (Format.byteArray.equals(outputDataType))
@@ -361,7 +361,7 @@ public class AudioMixingPushBufferStream
* Sets the collection of audio sample sets to be mixed in the sense of
* audio mixing by this stream when data is read from it. Triggers a push to
* the clients of this stream.
- *
+ *
* @param inputSamples the collection of audio sample sets to be mixed by
* this stream when data is read from it
* @param maxInputSampleCount the maximum number of per-stream audio samples
@@ -442,7 +442,7 @@ public class AudioMixingPushBufferStream
* Converts an integer to a series of bytes and writes the result into a
* specific output array of bytes starting the writing at a specific offset
* in it.
- *
+ *
* @param input the integer to be written out as a series of bytes
* @param output the output to receive the conversion of the specified
* integer to a series of bytes
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/AbstractMediaDevice.java b/src/net/java/sip/communicator/impl/neomedia/device/AbstractMediaDevice.java
index 5e5b60c..3d22b92 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/AbstractMediaDevice.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/AbstractMediaDevice.java
@@ -133,4 +133,11 @@ public abstract class AbstractMediaDevice
{
return null;
}
+
+ /**
+ * Closes this <tt>MediaDevice</tt>.
+ */
+ public void close()
+ {
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java
index 4de2de3..bb368c6 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/AudioMediaDeviceSession.java
@@ -258,4 +258,18 @@ public class AudioMediaDeviceSession
{
return -1;
}
+
+ /**
+ * Transfer rendering part from <tt>session</tt> to this instance.
+ *
+ * @param session <tt>MediaDeviceSession</tt> to transfer data from
+ */
+ protected void transferRenderingSession(MediaDeviceSession session)
+ {
+ AudioMediaDeviceSession amds = (AudioMediaDeviceSession)session;
+ this.setStreamAudioLevelListener(
+ amds.streamAudioLevelEffect.getAudioLevelListener());
+ this.setLocalUserAudioLevelListener(
+ amds.localUserAudioLevelEffect.getAudioLevelListener());
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
index d3806ca..9a38148 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/AudioMixerMediaDevice.java
@@ -153,6 +153,24 @@ public class AudioMixerMediaDevice
}
/**
+ * Closes this <tt>MediaDevice</tt> and removes all of the
+ * <tt>MediaDeviceSession</tt> of its <tt>AudioMixerMediaDeviceSession</tt>.
+ */
+ @Override
+ public void close()
+ {
+ List<MediaDeviceSession> sessions = new ArrayList<MediaDeviceSession>();
+ for(MediaStreamMediaDeviceSession s :
+ deviceSession.mediaStreamMediaDeviceSessions)
+ {
+ sessions.add(s);
+ }
+
+ for(MediaDeviceSession s : sessions)
+ s.close();
+ }
+
+ /**
* Connects to a specific <tt>CaptureDevice</tt> given in the form of a
* <tt>DataSource</tt>.
*
@@ -212,11 +230,35 @@ public class AudioMixerMediaDevice
public synchronized MediaDeviceSession createSession()
{
if (deviceSession == null)
+ {
deviceSession = new AudioMixerMediaDeviceSession();
+ }
return new MediaStreamMediaDeviceSession(deviceSession);
}
/**
+ * Creates a new <tt>MediaDeviceSession</tt> instance which is to represent
+ * the use of this <tt>MediaDevice</tt> by a <tt>MediaStream</tt> and the
+ * rendering part of a previous <tt>MediaDeviceSession</tt>.
+ *
+ * @param oldSession previous <tt>MediaDeviceSession</tt>
+ * @return a new <tt>MediaDeviceSession</tt> instance which is to represent
+ * the use of this <tt>MediaDevice</tt> by a <tt>MediaStream</tt>
+ */
+ @Override
+ public MediaDeviceSession createSession(MediaDeviceSession oldSession)
+ {
+ MediaStreamMediaDeviceSession session =
+ (MediaStreamMediaDeviceSession)createSession();
+ MediaStreamMediaDeviceSession old =
+ (MediaStreamMediaDeviceSession)oldSession;
+
+ session.setStreamAudioLevelListener(old.streamAudioLevelListener);
+ session.setLocalUserAudioLevelListener(old.localUserAudioLevelListener);
+ return session;
+ }
+
+ /**
* Notifies all currently registered <tt>SimpleAudioLevelListener</tt>s
* that our local media now has audio level <tt>level</tt>.
*
@@ -271,7 +313,9 @@ public class AudioMixerMediaDevice
if (inputDataSource == captureDevice)
AudioMixerMediaDevice.this.connect(dataSource);
else
+ {
super.connect(dataSource, inputDataSource);
+ }
}
@Override
@@ -326,9 +370,11 @@ public class AudioMixerMediaDevice
&& buffer.getData() != null)
{
if(! streamEventDispatcher.isRunning())
+ {
new Thread(streamEventDispatcher,
"StreamAudioLevelDispatcher (Mixer Edition)")
.start();
+ }
streamEventDispatcher.addData(buffer);
}
}
@@ -713,7 +759,9 @@ public class AudioMixerMediaDevice
if (mediaStreamMediaDeviceSessions
.remove(mediaStreamMediaDeviceSession)
&& mediaStreamMediaDeviceSessions.isEmpty())
+ {
close();
+ }
}
}
}
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 823b9b0..8ec5c51 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/DeviceConfiguration.java
@@ -19,6 +19,7 @@ import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.codec.*;
import net.java.sip.communicator.impl.neomedia.codec.video.*;
import net.java.sip.communicator.impl.neomedia.jmfext.media.renderer.audio.*;
+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.*;
@@ -35,7 +36,8 @@ import net.java.sip.communicator.util.*;
@SuppressWarnings("unchecked")
public class DeviceConfiguration
extends PropertyChangeNotifier
- implements PropertyChangeListener
+ implements PropertyChangeListener,
+ PortAudioDeviceChangedCallback
{
/**
@@ -324,6 +326,7 @@ public class DeviceConfiguration
}
registerCustomRenderers();
+ PortAudio.addDeviceChangedCallback(this);
}
/**
@@ -348,114 +351,8 @@ public class DeviceConfiguration
*/
private void extractConfiguredCaptureDevices()
{
- ConfigurationService config
- = NeomediaActivator.getConfigurationService();
-
- if (logger.isInfoEnabled())
- logger.info("Scanning for configured Audio Devices.");
- CaptureDeviceInfo[] audioCaptureDevices =
- getAvailableAudioCaptureDevices();
- if (config.getBoolean(PROP_AUDIO_DEVICE_IS_DISABLED, false))
- {
- audioCaptureDevice = null;
- audioSystem = AUDIO_NONE;
- }
- else if (audioCaptureDevices.length < 1)
- {
- logger.warn("No Audio Device was found.");
- audioCaptureDevice = null;
- audioSystem = AUDIO_NONE;
- }
- else
- {
- if (logger.isDebugEnabled())
- logger.debug("Found " + audioCaptureDevices.length
- + " capture devices: " + audioCaptureDevices);
-
- String audioDevName = config.getString(PROP_AUDIO_DEVICE);
-
- if(audioDevName == null || audioDevName.equals(AUDIO_NONE))
- {
- // the default behaviour if nothing set is to use PortAudio
- // this will also choose the capture device
- if(PortAudioAuto.isSupported())
- {
- setAudioSystem(AUDIO_SYSTEM_PORTAUDIO, null, false);
- if(audioDevName != null
- && audioDevName.equals(AUDIO_NONE))
- setAudioCaptureDevice(null, false);
- }
- else
- {
- setAudioPlaybackDevice(null, false);
- setAudioNotifyDevice(null, false);
- if (OSUtils.IS_ANDROID)
- {
- setAudioCaptureDevice(audioCaptureDevices[0], false);
- }
- else
- {
- setAudioCaptureDevice(null, false);
- setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null, false);
- }
- }
- }
- else
- {
- for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices)
- {
- if (audioDevName.equals(captureDeviceInfo.getName()))
- {
- setAudioSystem(getAudioSystem(captureDeviceInfo),
- captureDeviceInfo, false);
- break;
- }
- }
-
- if(getAudioSystem() == null || !PortAudioAuto.isSupported())
- {
- logger.warn("Computer sound config changed or " +
- "there is a problem since last config was saved, " +
- "will back to default");
- setAudioPlaybackDevice(null, false);
- setAudioNotifyDevice(null, false);
- setAudioCaptureDevice(null, false);
- setAudioSystem(AUDIO_SYSTEM_PORTAUDIO, null, false);
- }
- }
- if (audioCaptureDevice != null)
- if (logger.isInfoEnabled())
- logger.info("Found " + audioCaptureDevice.getName()
- + " as an audio capture device.");
- }
-
- if (config.getBoolean(PROP_VIDEO_DEVICE_IS_DISABLED, false))
- videoCaptureDevice = null;
- else
- {
- if (logger.isInfoEnabled())
- logger.info("Scanning for configured Video Devices.");
-
- Format[] formats
- = new Format[]
- {
- new AVFrameFormat(),
- new VideoFormat(VideoFormat.RGB),
- new VideoFormat(VideoFormat.YUV),
- new VideoFormat(Constants.H264)
- };
-
- for (Format format : formats)
- {
- videoCaptureDevice
- = extractConfiguredVideoCaptureDevice(format);
- if (videoCaptureDevice != null)
- break;
- }
- if (videoCaptureDevice == null)
- if (logger.isInfoEnabled())
- logger.info("No Video Device was found.");
- }
+ extractConfiguredAudioCaptureDevices();
+ extractConfiguredVideoCaptureDevices();
}
/**
@@ -1400,6 +1297,141 @@ public class DeviceConfiguration
{
videoMaxBandwidth = -1;
}
+ }
+
+ /**
+ * Detects audio capture devices configured through JMF and disable audio if
+ * none was found.
+ */
+ private void extractConfiguredAudioCaptureDevices()
+ {
+ ConfigurationService config
+ = NeomediaActivator.getConfigurationService();
+
+ if (logger.isInfoEnabled())
+ logger.info("Scanning for configured Audio Devices.");
+
+ CaptureDeviceInfo[] audioCaptureDevices =
+ getAvailableAudioCaptureDevices();
+ if (config.getBoolean(PROP_AUDIO_DEVICE_IS_DISABLED, false))
+ {
+ audioCaptureDevice = null;
+ audioSystem = AUDIO_NONE;
+ }
+ else if (audioCaptureDevices.length < 1)
+ {
+ logger.warn("No Audio Device was found.");
+ audioCaptureDevice = null;
+ audioSystem = AUDIO_NONE;
+ }
+ else
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Found " + audioCaptureDevices.length
+ + " capture devices: " + audioCaptureDevices);
+
+ String audioDevName = config.getString(PROP_AUDIO_DEVICE);
+
+ if(audioDevName == null || audioDevName.equals(AUDIO_NONE))
+ {
+ // the default behaviour if nothing set is to use PortAudio
+ // this will also choose the capture device
+ if(PortAudioAuto.isSupported())
+ {
+ setAudioSystem(AUDIO_SYSTEM_PORTAUDIO, null, false);
+ if(audioDevName != null
+ && audioDevName.equals(AUDIO_NONE))
+ setAudioCaptureDevice(null, false);
+ }
+ else
+ {
+ setAudioPlaybackDevice(null, false);
+ setAudioNotifyDevice(null, false);
+ if (OSUtils.IS_ANDROID)
+ {
+ setAudioCaptureDevice(audioCaptureDevices[0], false);
+ }
+ else
+ {
+ setAudioCaptureDevice(null, false);
+ setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null, false);
+ }
+ }
+ }
+ else
+ {
+ for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices)
+ {
+ if (audioDevName.equals(captureDeviceInfo.getName()))
+ {
+ setAudioSystem(getAudioSystem(captureDeviceInfo),
+ captureDeviceInfo, false);
+ break;
+ }
+ }
+
+ if(getAudioSystem() == null || !PortAudioAuto.isSupported())
+ {
+ logger.warn("Computer sound config changed or " +
+ "there is a problem since last config was saved, " +
+ "will back to default");
+ setAudioPlaybackDevice(null, false);
+ setAudioNotifyDevice(null, false);
+ setAudioCaptureDevice(null, false);
+ setAudioSystem(AUDIO_SYSTEM_PORTAUDIO, null, false);
+ }
+ }
+ if (audioCaptureDevice != null)
+ if (logger.isInfoEnabled())
+ logger.info("Found " + audioCaptureDevice.getName()
+ + " as an audio capture device.");
+ }
+ }
+
+ /**
+ * Detects video capture devices configured through JMF and disable video if
+ * none was found.
+ */
+ private void extractConfiguredVideoCaptureDevices()
+ {
+ ConfigurationService config
+ = NeomediaActivator.getConfigurationService();
+
+ if (config.getBoolean(PROP_VIDEO_DEVICE_IS_DISABLED, false))
+ videoCaptureDevice = null;
+ else
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Scanning for configured Video Devices.");
+
+ Format[] formats
+ = new Format[]
+ {
+ new AVFrameFormat(),
+ new VideoFormat(VideoFormat.RGB),
+ new VideoFormat(VideoFormat.YUV),
+ new VideoFormat(Constants.H264)
+ };
+ for (Format format : formats)
+ {
+ videoCaptureDevice
+ = extractConfiguredVideoCaptureDevice(format);
+ if (videoCaptureDevice != null)
+ break;
+ }
+ if (videoCaptureDevice == null)
+ if (logger.isInfoEnabled())
+ logger.info("No Video Device was found.");
+ }
+ }
+
+ /**
+ * Callback when PortAudio device changed.
+ */
+ public void deviceChanged()
+ {
+ JmfDeviceDetector.reinitializePortAudio();
+ extractConfiguredAudioCaptureDevices();
}
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/JmfDeviceDetector.java b/src/net/java/sip/communicator/impl/neomedia/device/JmfDeviceDetector.java
index c4e5505..2e1a1f5 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/JmfDeviceDetector.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/JmfDeviceDetector.java
@@ -62,6 +62,11 @@ public class JmfDeviceDetector
= "sip-communicator.org";
/**
+ * PortAudioAuto reference.
+ */
+ private static PortAudioAuto portaudioAuto = null;
+
+ /**
* Default constructor - does nothing.
*/
public JmfDeviceDetector()
@@ -187,7 +192,7 @@ public class JmfDeviceDetector
}
try
{
- new PortAudioAuto();
+ portaudioAuto = new PortAudioAuto();
}
catch (Throwable exc)
{
@@ -347,4 +352,21 @@ public class JmfDeviceDetector
{
new JmfDeviceDetector().reinitializeVideo();
}
+
+ /**
+ * Reinitialize PortAudio devices.
+ */
+ public static void reinitializePortAudio()
+ {
+ if(portaudioAuto != null)
+ {
+ try
+ {
+ portaudioAuto.reinit();
+ }
+ catch(Exception e)
+ {
+ }
+ }
+ }
}
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 90ac280..f7dee1e 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
@@ -340,6 +340,10 @@ public class MediaDeviceSession
// playback
if (disposePlayerOnClose)
disposePlayer();
+
+ processor = null;
+ player = null;
+ captureDevice = null;
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java b/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java
index d8f4302..93eaf9d 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/PortAudioAuto.java
@@ -66,6 +66,38 @@ public class PortAudioAuto
PortAudioAuto()
throws Exception
{
+ reinit();
+
+ // now add it as available audio system to DeviceConfiguration
+ DeviceConfiguration.addAudioSystem(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO);
+
+ supported = true;
+ }
+
+ /**
+ * PortAudio device changed callback.
+ */
+ public void deviceChanged()
+ {
+ try
+ {
+ reinit();
+ }
+ catch(Exception e)
+ {
+ logger.warn("Error while reinitialize PortAudio devices", e);
+ }
+ }
+
+ /**
+ * Reinitializes PortAudio system.
+ * @throws Exception if anything wrong happens while creating the PortAudio
+ * capture devices
+ */
+ public void reinit()
+ throws Exception
+ {
// if PortAudio has a problem initializing like missing native
// components it will trow exception here and PortAudio rendering will
// not be inited.
@@ -73,10 +105,14 @@ public class PortAudioAuto
int defaultInputDeviceIx = PortAudio.Pa_GetDefaultInputDevice();
int defaultOutputDeviceIx = PortAudio.Pa_GetDefaultOutputDevice();
+
+ playbackDevices = null;
+ defaultPlaybackDevice = null;
+ defaultCaptureDevice = null;
/*
// for windows we will search for directsound devices, to set them as
- // defualt, we need info for defualt devices host api
+ // default, we need info for default devices host api
PortAudio.PaHostApiTypeId defaultInputHostApi
= PortAudio.PaHostApiTypeId.undefined;
PortAudio.PaHostApiTypeId defaultOutputHostApi
@@ -97,6 +133,42 @@ public class PortAudioAuto
}
*/
+ AudioFormat fmt = new AudioFormat(
+ AudioFormat.LINEAR,
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED,
+ AudioFormat.LITTLE_ENDIAN,
+ AudioFormat.SIGNED,
+ Format.NOT_SPECIFIED /* frameSizeInBits */,
+ Format.NOT_SPECIFIED /* frameRate */,
+ Format.byteArray);
+
+ Iterator<?> it = CaptureDeviceManager.getDeviceList(fmt).iterator();
+ List<CaptureDeviceInfo> devicesToRemove =
+ new ArrayList<CaptureDeviceInfo>();
+
+ while(it.hasNext())
+ {
+ CaptureDeviceInfo ifo = (CaptureDeviceInfo)it.next();
+
+ if(!ifo.getLocator().getProtocol().equals("portaudio"))
+ {
+ continue;
+ }
+ else
+ {
+ devicesToRemove.add(ifo);
+ }
+ }
+
+ for(CaptureDeviceInfo info : devicesToRemove)
+ {
+ CaptureDeviceManager.removeDevice(info);
+ }
+ if(devicesToRemove.size() > 0)
+ CaptureDeviceManager.commit();
+
Vector<CaptureDeviceInfo> playbackDevVector =
new Vector<CaptureDeviceInfo>();
int channels = 1;
@@ -201,14 +273,7 @@ public class PortAudioAuto
}
playbackDevices = playbackDevVector.toArray(new CaptureDeviceInfo[0]);
-
CaptureDeviceManager.commit();
-
- // now add it as available audio system to DeviceConfiguration
- DeviceConfiguration.addAudioSystem(
- DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO);
-
- supported = true;
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java
index e48aa61..44014bd 100644
--- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java
+++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java
@@ -89,6 +89,11 @@ public class PortAudioRenderer
private static MediaLocator defaultLocator;
/**
+ * Value to indicate if default locator has changed.
+ */
+ private static boolean changeDefaultLocator = false;
+
+ /**
* The audio samples left unwritten by a previous call to
* {@link #process(Buffer)}. As {@link #bytesPerBuffer} number of
* bytes are always written, the number of the unwritten audio samples is
@@ -437,6 +442,24 @@ public class PortAudioRenderer
}
/**
+ * Update default locator.
+ */
+ private void updateDefaultLocator()
+ {
+ try
+ {
+ stop();
+ close();
+ open();
+ start();
+ }
+ catch(ResourceUnavailableException e)
+ {
+ logger.info("Error reinit default portaudio locator", e);
+ }
+ }
+
+ /**
* Renders the audio data contained in a specific <tt>Buffer</tt> onto the
* PortAudio device represented by this <tt>Renderer</tt>.
*
@@ -447,6 +470,12 @@ public class PortAudioRenderer
*/
public int process(Buffer buffer)
{
+ if(changeDefaultLocator && locator == null)
+ {
+ updateDefaultLocator();
+ changeDefaultLocator = false;
+ }
+
synchronized (this)
{
if (!started || (stream == 0))
@@ -568,6 +597,8 @@ public class PortAudioRenderer
return;
PortAudioRenderer.defaultLocator = defaultLocator;
+
+ changeDefaultLocator = true;
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java
index f4bd7c2..e20ffe6 100644
--- a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java
+++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudio.java
@@ -9,6 +9,7 @@ package net.java.sip.communicator.impl.neomedia.portaudio;
import java.io.*;
import java.lang.reflect.*;
import java.nio.charset.*;
+import java.util.*;
import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.codec.*;
@@ -19,16 +20,27 @@ import net.java.sip.communicator.util.*;
*
* @author Lyubomir Marinov
* @author Damian Minkov
+ * @author Sebastien Vincent
*/
public final class PortAudio
{
-
/**
* The <tt>Logger</tt> used by the <tt>PortAudio</tt> class for logging
* output.
*/
private static final Logger logger = Logger.getLogger(PortAudio.class);
+ /**
+ * List of device changed callbacks.
+ */
+ public static final List<PortAudioDeviceChangedCallback> callbacks =
+ new Vector<PortAudioDeviceChangedCallback>();
+
+ /**
+ * Synchronization object.
+ */
+ private static final Object syncRoot = new Object();
+
static
{
System.loadLibrary("jnportaudio");
@@ -82,6 +94,9 @@ public final class PortAudio
*/
public static final double LATENCY_UNSPECIFIED = 0d;
+ /**
+ * PortAudio "no device" constant.
+ */
public static final int paNoDevice = -1;
/**
@@ -499,6 +514,15 @@ public final class PortAudio
int numberOfWrites)
throws PortAudioException;
+ /**
+ * Gets the human-readable name of the <tt>PaDeviceInfo</tt> specified by a
+ * pointer to it.
+ *
+ * @param deviceInfo the pointer to the <tt>PaDeviceInfo</tt> to get the
+ * human-readable name of
+ * @return the human-readable name of the <tt>PaDeviceInfo</tt> pointed to
+ * by <tt>deviceInfo</tt>
+ */
public static String PaDeviceInfo_getName(long deviceInfo)
{
byte[] nameBytes = PaDeviceInfo_getNameBytes(deviceInfo);
@@ -648,6 +672,12 @@ public final class PortAudio
*/
public static native int PaHostApiInfo_getType(long hostApiInfo);
+ /**
+ * Free StreamParameters resources specified by a pointer to it.
+ *
+ * @param streamParameters the pointer to the <tt>PaStreamParameters</tt>
+ * to free
+ */
public static void PaStreamParameters_free(long streamParameters)
{
try
@@ -734,6 +764,52 @@ public final class PortAudio
long echoFilterLengthInMillis);
/**
+ * Updates available device lists in PortAudio.
+ */
+ private static native void updateAvailableDeviceList();
+
+ /**
+ * Adds a device changed callback.
+ *
+ * @param cb callback that will be called if device are added/removed
+ */
+ public static void addDeviceChangedCallback(
+ PortAudioDeviceChangedCallback cb)
+ {
+ if(!callbacks.contains(cb))
+ callbacks.add(cb);
+ }
+
+ /**
+ * Removes a device changed callback.
+ *
+ * @param cb callback that will be called if device are added/removed
+ */
+ public static void removeDeviceChangedCallback(
+ PortAudioDeviceChangedCallback cb)
+ {
+ if(callbacks.contains(cb))
+ callbacks.remove(cb);
+ }
+
+
+ /**
+ * Callback called from native PortAudio side that notify device changed.
+ */
+ public static void deviceChanged()
+ {
+ synchronized(syncRoot)
+ {
+ updateAvailableDeviceList();
+
+ for(PortAudioDeviceChangedCallback cb : callbacks)
+ {
+ cb.deviceChanged();
+ }
+ }
+ }
+
+ /**
* Prevents the creation of <tt>PortAudio</tt> instances.
*/
private PortAudio()
diff --git a/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioDeviceChangedCallback.java b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioDeviceChangedCallback.java
new file mode 100644
index 0000000..6c6bdcf
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/neomedia/portaudio/PortAudioDeviceChangedCallback.java
@@ -0,0 +1,20 @@
+/*
+ * 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.neomedia.portaudio;
+
+/**
+ * Interface to be notified by PortAudio device changed callback.
+ *
+ * @author Sebastien Vincent
+ */
+public interface PortAudioDeviceChangedCallback
+{
+ /**
+ * Callback when PortAudio device changed.
+ */
+ public void deviceChanged();
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
index bad9afc..fc992d4 100644
--- a/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
+++ b/src/net/java/sip/communicator/impl/neomedia/transform/csrc/CsrcTransformEngine.java
@@ -137,9 +137,9 @@ public class CsrcTransformEngine
*/
public synchronized RawPacket transform(RawPacket pkt)
{
- // if somebody has modified the packet and added an extension
+ // if somebody has modified the packet and added an extension
// don't process it. As ZRTP creates special rtp packets carring no
- // rtp data and those packets are used only by zrtp we don't use them.
+ // rtp data and those packets are used only by zrtp we don't use them.
if(pkt.getExtensionBit())
return pkt;