diff --git a/src/org/jitsi/impl/neomedia/device/AudioSystem.java b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
index a7d937a196e662df7c899a7102ba13bd36e883ae..b60e21d233c2a49f0adf7187118fc8642e58d20c 100644
--- a/src/org/jitsi/impl/neomedia/device/AudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
@@ -26,12 +26,6 @@ public abstract class AudioSystem
 
     public static final int FEATURE_NOTIFY_AND_PLAYBACK_DEVICES = 8;
 
-    private static final int FLAG_CAPTURE_DEVICE_IS_NULL = 1;
-
-    private static final int FLAG_NOTIFY_DEVICE_IS_NULL = 2;
-
-    private static final int FLAG_PLAYBACK_DEVICE_IS_NULL = 4;
-
     public static final String LOCATOR_PROTOCOL_AUDIORECORD = "audiorecord";
     
     public static final String LOCATOR_PROTOCOL_JAVASOUND = "javasound";
@@ -42,12 +36,6 @@ public abstract class AudioSystem
 
     public static final String LOCATOR_PROTOCOL_PULSEAUDIO = "pulseaudio";
 
-    public static final String PROP_CAPTURE_DEVICE = "captureDevice";
-
-    public static final String PROP_NOTIFY_DEVICE = "notifyDevice";
-
-    public static final String PROP_PLAYBACK_DEVICE = "playbackDevice";
-
     public static AudioSystem getAudioSystem(String locatorProtocol)
     {
         AudioSystem[] audioSystems = getAudioSystems();
@@ -89,15 +77,25 @@ public static AudioSystem[] getAudioSystems()
                 : audioSystems.toArray(new AudioSystem[audioSystems.size()]);
     }
 
-    private CaptureDeviceInfo captureDevice;
-
-    private int flags;
+    /**
+     * The index of the capture devices.
+     */
+    public static final int CAPTURE_INDEX = 0;
 
-    private CaptureDeviceInfo notifyDevice;
+    /**
+     * The index of the notify devices.
+     */
+    public static final int NOTIFY_INDEX = 1;
 
-    private CaptureDeviceInfo playbackDevice;
+    /**
+     * The index of the playback devices.
+     */
+    public static final int PLAYBACK_INDEX = 2;
 
-    private List<CaptureDeviceInfo> playbackDevices;
+    /**
+     * The list of the devices: capture, notify or playback.
+     */
+    private Devices[] devices;
 
     protected AudioSystem(String locatorProtocol)
         throws Exception
@@ -111,188 +109,86 @@ protected AudioSystem(String locatorProtocol, int features)
         super(MediaType.AUDIO, locatorProtocol, features);
     }
 
-    public CaptureDeviceInfo getCaptureDevice()
+    /**
+     * Gets the list of a kind of devices: cpature, notify or playback.
+     *
+     * @param index The index of the specific devices: cpature, notify or
+     * playback.
+     *
+     * @return The list of a kind of devices: cpature, notify or playback.
+     */
+    public List<CaptureDeviceInfo> getDevices(int index)
     {
-        List<CaptureDeviceInfo> captureDevices = null;
-
-        if (this.captureDevice != null)
-        {
-            if (captureDevices == null)
-                captureDevices = getCaptureDevices();
-            if ((captureDevices == null)
-                    || !captureDevices.contains(this.captureDevice))
-                setCaptureDevice(null, false);
-        }
-
-        CaptureDeviceInfo captureDevice = this.captureDevice;
-
-        if ((captureDevice == null)
-                && ((flags & FLAG_CAPTURE_DEVICE_IS_NULL) == 0))
-        {
-            if (captureDevices == null)
-                captureDevices = getCaptureDevices();
-            if ((captureDevices != null) && (captureDevices.size() > 0))
-            {
-                captureDevice = loadDevice(PROP_CAPTURE_DEVICE, captureDevices);
-                if (captureDevice == null)
-                    captureDevice = captureDevices.get(0);
-            }
-        }
-        return captureDevice;
+        return this.devices[index].getDevices(getLocatorProtocol());
     }
 
-    public List<CaptureDeviceInfo> getCaptureDevices()
-    {
-        MediaServiceImpl mediaServiceImpl
-            = NeomediaServiceUtils.getMediaServiceImpl();
-        DeviceConfiguration deviceConfiguration
-            = (mediaServiceImpl == null)
-                ? null
-                : mediaServiceImpl.getDeviceConfiguration();
-        List<CaptureDeviceInfo> deviceList;
-
-        if (deviceConfiguration == null)
-        {
-            /*
-             * XXX The initialization of MediaServiceImpl is very complex so it
-             * is wise to not reference it at the early stage of its
-             * initialization. The same goes for DeviceConfiguration. If for
-             * some reason one of the two is not available at this time, just
-             * fall back go something that makes sense.
-             */
-            @SuppressWarnings("unchecked")
-            Vector<CaptureDeviceInfo> audioCaptureDeviceInfos
-                = CaptureDeviceManager.getDeviceList(
-                        new AudioFormat(AudioFormat.LINEAR, -1, 16, -1));
-
-            deviceList = audioCaptureDeviceInfos;
-        }
-        else
-        {
-            deviceList = deviceConfiguration.getAvailableAudioCaptureDevices();
-        }
-
-        return
-            filterDeviceListByLocatorProtocol(deviceList, getLocatorProtocol());
-    }
-
-    public CaptureDeviceInfo getNotifyDevice()
+    /**
+     * Gets the selected device for a specific kind: cpature, notify or
+     * playback.
+     *
+     * @param index The index of the specific devices: cpature, notify or
+     * playback.
+     *
+     * @return The selected device for a specific kind: cpature, notify or
+     * playback.
+     */
+    public CaptureDeviceInfo getDevice(int index)
     {
-        List<CaptureDeviceInfo> notifyDevices = null;
-
-        if (this.notifyDevice != null)
-        {
-            if (notifyDevices == null)
-                notifyDevices = getNotifyDevices();
-            if ((notifyDevices == null)
-                    || !notifyDevices.contains(this.notifyDevice))
-                setNotifyDevice(null, false);
-        }
-
-        CaptureDeviceInfo notifyDevice = this.notifyDevice;
-
-        if ((notifyDevice == null)
-                && ((flags & FLAG_NOTIFY_DEVICE_IS_NULL) == 0))
-        {
-            if (notifyDevices == null)
-                notifyDevices = getNotifyDevices();
-            if ((notifyDevices != null) && (notifyDevices.size() > 0))
-            {
-                notifyDevice = loadDevice(PROP_NOTIFY_DEVICE, notifyDevices);
-                if (notifyDevice == null)
-                    notifyDevice = notifyDevices.get(0);
-            }
-        }
-        return notifyDevice;
+        return this.devices[index].getDevice(getLocatorProtocol());
     }
 
-    public List<CaptureDeviceInfo> getNotifyDevices()
+    /**
+     * Sets the list of a kind of devices: cpature, notify or playback.
+     *
+     * @param activeCaptureDevices The list of a kind of devices: cpature,
+     * notify or playback.
+     */
+    protected void setCaptureDevices(
+            List<CaptureDeviceInfo> activeCaptureDevices)
     {
-        return getPlaybackDevices();
+        this.devices[CAPTURE_INDEX].setActiveDevices(activeCaptureDevices);
     }
 
-    public CaptureDeviceInfo getPlaybackDevice()
+    /**
+     * Selects the active device.
+     *
+     * @param index The index corresponding to a specific device kind:
+     * capture/notify/playback.
+     * @param device The selected active device.
+     * @param save Flag set to true in order to save this choice in the
+     * configuration. False otherwise.
+     */
+    public void setDevice(int index, CaptureDeviceInfo device, boolean save)
     {
-        List<CaptureDeviceInfo> playbackDevices = null;
-
-        if (this.playbackDevice != null)
-        {
-            if (playbackDevices == null)
-                playbackDevices = getPlaybackDevices();
-            if ((playbackDevices == null)
-                    || !playbackDevices.contains(this.playbackDevice))
-                setPlaybackDevice(null, false);
-        }
-
-        CaptureDeviceInfo playbackDevice = this.playbackDevice;
-
-        if ((playbackDevice == null)
-                && ((flags & FLAG_PLAYBACK_DEVICE_IS_NULL) == 0))
-        {
-            if (playbackDevices == null)
-                playbackDevices = getPlaybackDevices();
-            if ((playbackDevices != null) && (playbackDevices.size() > 0))
-            {
-                playbackDevice
-                    = loadDevice(PROP_PLAYBACK_DEVICE, playbackDevices);
-                if (playbackDevice == null)
-                    playbackDevice = playbackDevices.get(0);
-            }
-        }
-        return playbackDevice;
+        this.devices[index].setDevice(getLocatorProtocol(), device, save);
     }
 
-    public List<CaptureDeviceInfo> getPlaybackDevices()
+    /**
+     * Sets the list of the active devices.
+     *
+     * @param activePlaybackDevices The list of the active devices.
+     */
+    protected void setPlaybackDevices(
+            List<CaptureDeviceInfo> activePlaybackDevices)
     {
-        List<CaptureDeviceInfo> playbackDevices = this.playbackDevices;
-
-        return
-            (playbackDevices == null)
-                ? null
-                : new ArrayList<CaptureDeviceInfo>(playbackDevices);
+        this.devices[PLAYBACK_INDEX].setActiveDevices(activePlaybackDevices);
+        // The active notify device list is a copy of the playback one.
+        this.devices[NOTIFY_INDEX].setActiveDevices(activePlaybackDevices);
     }
 
     /**
-     * Loads the user's preference with respect to a <tt>CaptureDeviceInfo</tt>
-     * among a specific list of <tt>CaptureDeviceInfo</tt>s from the
-     * <tt>ConfigurationService</tt>.
-     *
-     * @param property the name of the <tt>ConfigurationService</tt> property
-     * which specifies the user's preference with respect to a
-     * <tt>CaptureDeviceInfo</tt> among the specified list of
-     * <tt>CaptureDeviceInfo</tt>s 
-     * @param devices the list of <tt>CaptureDeviceInfo</tt>s which are valid
-     * selections for the user's preference
-     * @return a <tt>CaptureDeviceInfo</tt> among the specified <tt>devices</tt>
-     * which represents the user's preference stored in the
-     * <tt>ConfigurationService</tt>
+     * Pre-initializes this audio system before setting the different devices.
      */
-    private CaptureDeviceInfo loadDevice(
-            String property,
-            List<CaptureDeviceInfo> devices)
+    protected void preInitialize()
     {
-        ConfigurationService cfg = LibJitsi.getConfigurationService();
-
-        if (cfg != null)
+        super.preInitialize();
+        if(this.devices == null)
         {
-            property
-                = DeviceConfiguration.PROP_AUDIO_SYSTEM
-                    + "."
-                    + getLocatorProtocol()
-                    + "."
-                    + property;
-
-            String name = cfg.getString(property);
-
-            if ((name != null)
-                    && !NoneAudioSystem.LOCATOR_PROTOCOL.equalsIgnoreCase(name))
-            {
-                for (CaptureDeviceInfo device : devices)
-                    if (name.equals(device.getName()))
-                        return device;
-            }
+            this.devices = new Devices[3];
+            this.devices[0] = new CaptureDevices(this);
+            this.devices[1] = new NotifyDevices(this);
+            this.devices[2] = new PlaybackDevices(this);
         }
-        return null;
     }
 
     @Override
@@ -306,24 +202,7 @@ protected void postInitialize()
         {
             try
             {
-                if (captureDevice != null)
-                {
-                    List<CaptureDeviceInfo> captureDevices
-                        = getCaptureDevices();
-
-                    if ((captureDevices == null)
-                            || !captureDevices.contains(captureDevice))
-                        setCaptureDevice(null, false);
-                }
-                else
-                {
-                    /*
-                     * If captureDevice is null, it means that a device is to be
-                     * used as the default. The default in question may have
-                     * changed.
-                     */
-                    setCaptureDevice(null, false);
-                }
+                this.postInitializeSpecificDevices(CAPTURE_INDEX);
             }
             finally
             {
@@ -331,46 +210,11 @@ protected void postInitialize()
                 {
                     try
                     {
-                        if (notifyDevice != null)
-                        {
-                            List<CaptureDeviceInfo> notifyDevices
-                                = getNotifyDevices();
-
-                            if ((notifyDevices == null)
-                                    || !notifyDevices.contains(notifyDevice))
-                                setNotifyDevice(null, false);
-                        }
-                        else
-                        {
-                            /*
-                             * If notifyDevice is null, it means that a device
-                             * is to be used as the default. The default in
-                             * question may have changed.
-                             */
-                            setNotifyDevice(null, false);
-                        }
+                        this.postInitializeSpecificDevices(NOTIFY_INDEX);
                     }
                     finally
                     {
-                        if (playbackDevice != null)
-                        {
-                            List<CaptureDeviceInfo> playbackDevices
-                                = getPlaybackDevices();
-
-                            if ((playbackDevices == null)
-                                    || !playbackDevices.contains(
-                                            playbackDevice))
-                                setPlaybackDevice(null, false);
-                        }
-                        else
-                        {
-                            /*
-                             * If playbackDevice is null, it means that a device
-                             * is to be used as the default. The default in
-                             * question may have changed.
-                             */
-                            setPlaybackDevice(null, false);
-                        }
+                        this.postInitializeSpecificDevices(PLAYBACK_INDEX);
                     }
                 }
             }
@@ -378,157 +222,65 @@ protected void postInitialize()
     }
 
     /**
-     * Saves the user's preference with respect to a specific
-     * <tt>CaptureDeviceInfo</tt> in the <tt>ConfigurationService</tt>.
+     * Sets the device lists after the different audio systems (portaudio,
+     * pulseaudio, etc) have finished to detects the evices.
      *
-     * @param property the name of the <tt>ConfigurationService</tt> property
-     * into which the user's preference with respect to the specified
-     * <tt>CaptureDeviceInfo</tt> is to be saved
-     * @param device the <tt>CaptureDeviceInfo</tt> which is the user's
-     * preference
-     * @param isNull <tt>true</tt> if the user's preference with respect to the
-     * specified <tt>device</tt> is <tt>null</tt>; otherwise, <tt>false</tt>
+     * @param index The index corresponding to a specific device kind:
+     * capture/notify/playback.
      */
-    private void saveDevice(
-            String property,
-            CaptureDeviceInfo device,
-            boolean isNull)
-    {
-        ConfigurationService cfg = LibJitsi.getConfigurationService();
-
-        if (cfg != null)
-        {
-            property
-                = DeviceConfiguration.PROP_AUDIO_SYSTEM
-                    + "."
-                    + getLocatorProtocol()
-                    + "."
-                    + property;
-            if (device == null)
-            {
-                if (isNull)
-                    cfg.setProperty(property, NoneAudioSystem.LOCATOR_PROTOCOL);
-                else
-                    cfg.removeProperty(property);
-            }
-            else
-                cfg.setProperty(property, device.getName());
-        }
-    }
-
-    public void setCaptureDevice(CaptureDeviceInfo captureDevice, boolean save)
+    protected void postInitializeSpecificDevices(int index)
     {
-        if ((this.captureDevice != captureDevice) || (captureDevice == null))
+        // If there is a selected device, checks if it is part of the current
+        // active devices.
+        if (this.devices[index].getDevice(getLocatorProtocol()) != null)
         {
-            CaptureDeviceInfo oldValue = this.captureDevice;
-
-            this.captureDevice = captureDevice;
+            List<CaptureDeviceInfo> devices = this.devices[index].getDevices(
+                    getLocatorProtocol());
 
-            if (save)
+            if ((devices == null)
+                    || !devices.contains(this.devices[index].getDevice(
+                            getLocatorProtocol())))
             {
-                boolean isNull = (this.captureDevice == null);
-
-                if (isNull)
-                    flags |= FLAG_CAPTURE_DEVICE_IS_NULL;
-                else
-                    flags &= ~FLAG_CAPTURE_DEVICE_IS_NULL;
-
-                saveDevice(PROP_CAPTURE_DEVICE, this.captureDevice, isNull);
+                // The selected device is not part of the active devices, then
+                // set it to null (but not saved).
+                this.devices[index].setDevice(
+                        getLocatorProtocol(),
+                        null,
+                        false);
             }
-
-            CaptureDeviceInfo newValue = getCaptureDevice();
-
-            if (oldValue != newValue)
-                firePropertyChange(PROP_CAPTURE_DEVICE, oldValue, newValue);
         }
-    }
-
-    protected void setCaptureDevices(List<CaptureDeviceInfo> captureDevices)
-    {
-        if (captureDevices != null)
-        {
-            boolean commit = false;
-
-            for (CaptureDeviceInfo captureDevice : captureDevices)
-            {
-                CaptureDeviceManager.addDevice(captureDevice);
-                commit = true;
-            }
-            if (commit && !MediaServiceImpl.isJmfRegistryDisableLoad())
-            {
-                try
-                {
-                    CaptureDeviceManager.commit();
-                }
-                catch (IOException ioe)
-                {
-                    // Whatever.
-                }
-            }
-        }
-    }
-
-    public void setNotifyDevice(CaptureDeviceInfo notifyDevice, boolean save)
-    {
-        if ((this.notifyDevice != notifyDevice) || (notifyDevice == null))
-        {
-            CaptureDeviceInfo oldValue = this.notifyDevice;
-
-            this.notifyDevice = notifyDevice;
-
-            if (save)
-            {
-                boolean isNull = (this.notifyDevice == null);
-
-                if (isNull)
-                    flags |= FLAG_NOTIFY_DEVICE_IS_NULL;
-                else
-                    flags &= ~FLAG_NOTIFY_DEVICE_IS_NULL;
-
-                saveDevice(PROP_NOTIFY_DEVICE, this.notifyDevice, isNull);
-            }
-
-            CaptureDeviceInfo newValue = getNotifyDevice();
-
-            if (oldValue != newValue)
-                firePropertyChange(PROP_NOTIFY_DEVICE, oldValue, newValue);
-        }
-    }
-
-    public void setPlaybackDevice(
-            CaptureDeviceInfo playbackDevice,
-            boolean save)
-    {
-        if ((this.playbackDevice != playbackDevice) || (playbackDevice == null))
+        else
         {
-            CaptureDeviceInfo oldValue = this.playbackDevice;
-
-            this.playbackDevice = playbackDevice;
-
-            if (save)
-            {
-                boolean isNull = (this.playbackDevice == null);
-
-                if (isNull)
-                    flags |= FLAG_PLAYBACK_DEVICE_IS_NULL;
-                else
-                    flags &= ~FLAG_PLAYBACK_DEVICE_IS_NULL;
-
-                saveDevice(PROP_PLAYBACK_DEVICE, this.playbackDevice, isNull);
-            }
-
-            CaptureDeviceInfo newValue = getPlaybackDevice();
-
-            if (oldValue != newValue)
-                firePropertyChange(PROP_PLAYBACK_DEVICE, oldValue, newValue);
+            /*
+             * If the device is null, it means that a device is to be
+             * used as the default. The default in question may have
+             * changed.
+             */
+            this.devices[index].setDevice(getLocatorProtocol(), null, false);
         }
     }
 
-    protected void setPlaybackDevices(List<CaptureDeviceInfo> playbackDevices)
+    /**
+     * Fires a new <tt>PropertyChangeEvent</tt> to the
+     * <tt>PropertyChangeListener</tt>s registered with this
+     * <tt>PropertyChangeNotifier</tt> in order to notify about a change in the
+     * value of a specific property which had its old value modified to a
+     * specific new value. <tt>PropertyChangeNotifier</tt> does not check
+     * whether the specified <tt>oldValue</tt> and <tt>newValue</tt> are indeed
+     * different.
+     * 
+     * @param property the name of the property of this
+     * <tt>PropertyChangeNotifier</tt> which had its value changed
+     * @param oldValue the value of the property with the specified name before
+     * the change
+     * @param newValue the value of the property with the specified name after
+     * the change
+     */
+    public void propertyChange(
+            String property,
+            Object oldValue,
+            Object newValue)
     {
-        this.playbackDevices
-            = (playbackDevices == null)
-                ? null
-                : new ArrayList<CaptureDeviceInfo>(playbackDevices);
+        firePropertyChange(property, oldValue, newValue);
     }
 }
diff --git a/src/org/jitsi/impl/neomedia/device/CaptureDevices.java b/src/org/jitsi/impl/neomedia/device/CaptureDevices.java
new file mode 100644
index 0000000000000000000000000000000000000000..05dee0e657720655793690b1bbc8836997316eee
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/device/CaptureDevices.java
@@ -0,0 +1,138 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.device;
+
+import java.io.*;
+import java.util.*;
+
+import javax.media.*;
+import javax.media.format.*;
+
+import org.jitsi.impl.neomedia.*;
+
+/**
+ * Manages the list of active (currently pluged-in) capture devices and manages
+ * user preferences between all known devices (previously and actually
+ * plugged-in).
+ *
+ * @author Vincent Lucas
+ */
+public class CaptureDevices
+    extends Devices
+{
+    /**
+     * The flag nuber if the capture device is null.
+     */
+    protected static final int FLAG_DEVICE_IS_NULL = 1;
+
+    /**
+     * The property of the capture devices.
+     */
+    public static final String PROP_DEVICE = "captureDevice";
+
+    /**
+     * Initializes the capture device list managment.
+     *
+     * @param audioSystem The audio system managing this capture device list.
+     */
+    public CaptureDevices(AudioSystem audioSystem)
+    {
+        super(audioSystem);
+    }
+
+    /**
+     * Returns the list of the active devices.
+     *
+     * @param locator The string representation of the locator.
+     *
+     * @return The list of the active devices.
+     */
+    public List<CaptureDeviceInfo> getDevices(String locator)
+    {
+        MediaServiceImpl mediaServiceImpl
+            = NeomediaServiceUtils.getMediaServiceImpl();
+        DeviceConfiguration deviceConfiguration = (mediaServiceImpl == null)
+                ? null
+                : mediaServiceImpl.getDeviceConfiguration();
+        List<CaptureDeviceInfo> deviceList;
+
+        if (deviceConfiguration == null)
+        {
+            /*
+             * XXX The initialization of MediaServiceImpl is very complex so it
+             * is wise to not reference it at the early stage of its
+             * initialization. The same goes for DeviceConfiguration. If for
+             * some reason one of the two is not available at this time, just
+             * fall back go something that makes sense.
+             */
+            @SuppressWarnings("unchecked")
+            Vector<CaptureDeviceInfo> audioCaptureDeviceInfos
+                = CaptureDeviceManager.getDeviceList(
+                        new AudioFormat(AudioFormat.LINEAR, -1, 16, -1));
+
+            deviceList = audioCaptureDeviceInfos;
+        }
+        else
+        {
+            deviceList = deviceConfiguration.getAvailableAudioCaptureDevices();
+        }
+
+        return DeviceSystem.filterDeviceListByLocatorProtocol(
+                deviceList,
+                locator);
+    }
+
+    /**
+     * Sets the list of the active devices.
+     *
+     * @param activeDevices The list of the active devices.
+     */
+    public void setActiveDevices(List<CaptureDeviceInfo> activeDevices)
+    {
+        if(activeDevices != null)
+        {
+            boolean commit = false;
+
+            for (CaptureDeviceInfo activeDevice : activeDevices)
+            {
+                CaptureDeviceManager.addDevice(activeDevice);
+                commit = true;
+            }
+            if (commit && !MediaServiceImpl.isJmfRegistryDisableLoad())
+            {
+                try
+                {
+                    CaptureDeviceManager.commit();
+                }
+                catch (IOException ioe)
+                {
+                    // Whatever.
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the flag nuber if the capture device is null.
+     *
+     * @return The flag nuber if the capture device is null.
+     */
+    protected int getFlagDeviceIsNull()
+    {
+        return FLAG_DEVICE_IS_NULL;
+    }
+
+    /**
+     * Returns the property of the capture devices.
+     *
+     * @return The property of the capture devices.
+     */
+    protected String getPropDevice()
+    {
+        return PROP_DEVICE;
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java b/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
index 8c025e56e4506e13ccfc645ada82dd9181724d78..1c8ba4d31a1850eaf5b105dc54addc8205dfae32 100644
--- a/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
+++ b/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
@@ -43,21 +43,21 @@ public class DeviceConfiguration
      * the device used by <tt>DeviceConfiguration</tt> for audio capture.
      */
     public static final String AUDIO_CAPTURE_DEVICE
-        = AudioSystem.PROP_CAPTURE_DEVICE;
+        = CaptureDevices.PROP_DEVICE;
 
     /**
      * The name of the <tt>DeviceConfiguration</tt> property which represents
      * the device used by <tt>DeviceConfiguration</tt> for audio notify.
      */
     public static final String AUDIO_NOTIFY_DEVICE
-        = AudioSystem.PROP_NOTIFY_DEVICE;
+        = NotifyDevices.PROP_DEVICE;
 
     /**
      * The name of the <tt>DeviceConfiguration</tt> property which represents
      * the device used by <tt>DeviceConfiguration</tt> for audio playback.
      */
     public static final String AUDIO_PLAYBACK_DEVICE
-        = AudioSystem.PROP_PLAYBACK_DEVICE;
+        = PlaybackDevices.PROP_DEVICE;
 
     /**
      * The list of class names of custom <tt>Renderer</tt> implementations to be
@@ -399,7 +399,9 @@ public CaptureDeviceInfo getAudioCaptureDevice()
     {
         AudioSystem audioSystem = getAudioSystem();
 
-        return (audioSystem == null) ? null : audioSystem.getCaptureDevice();
+        return (audioSystem == null)
+            ? null
+            : audioSystem.getDevice(AudioSystem.CAPTURE_INDEX);
     }
 
     /**
@@ -444,7 +446,7 @@ public AudioSystem[] getAvailableAudioSystems()
                         audioSystem.getLocatorProtocol()))
                 {
                     List<CaptureDeviceInfo> captureDevices
-                        = audioSystem.getCaptureDevices();
+                        = audioSystem.getDevices(AudioSystem.CAPTURE_INDEX);
 
                     if ((captureDevices == null)
                             || (captureDevices.size() <= 0))
@@ -458,13 +460,15 @@ public AudioSystem[] getAvailableAudioSystems()
                         else
                         {
                             List<CaptureDeviceInfo> notifyDevices
-                                = audioSystem.getNotifyDevices();
+                                = audioSystem.getDevices(
+                                        AudioSystem.NOTIFY_INDEX);
 
                             if ((notifyDevices == null)
                                     || (notifyDevices.size() <= 0))
                             {
                                 List<CaptureDeviceInfo> playbackDevices
-                                    = audioSystem.getPlaybackDevices();
+                                    = audioSystem.getDevices(
+                                        AudioSystem.PLAYBACK_INDEX);
     
                                 if ((playbackDevices == null)
                                         || (playbackDevices.size() <= 0))
@@ -651,7 +655,9 @@ public CaptureDeviceInfo getAudioNotifyDevice()
     {
         AudioSystem audioSystem = getAudioSystem();
 
-        return (audioSystem == null) ? null : audioSystem.getNotifyDevice();
+        return (audioSystem == null)
+            ? null
+            : audioSystem.getDevice(AudioSystem.NOTIFY_INDEX);
     }
 
     /**
@@ -989,9 +995,9 @@ public void propertyChange(PropertyChangeEvent event)
     {
         String propertyName = event.getPropertyName();
 
-        if (AudioSystem.PROP_CAPTURE_DEVICE.equals(propertyName)
-                || AudioSystem.PROP_NOTIFY_DEVICE.equals(propertyName)
-                || AudioSystem.PROP_PLAYBACK_DEVICE.equals(propertyName))
+        if (AUDIO_CAPTURE_DEVICE.equals(propertyName)
+                || AUDIO_NOTIFY_DEVICE.equals(propertyName)
+                || AUDIO_PLAYBACK_DEVICE.equals(propertyName))
         {
             firePropertyChange(
                     propertyName,
diff --git a/src/org/jitsi/impl/neomedia/device/Devices.java b/src/org/jitsi/impl/neomedia/device/Devices.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc34943b2f092a924220ee84c8cd0979d27a791c
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/device/Devices.java
@@ -0,0 +1,371 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.device;
+
+import java.util.*;
+
+import javax.media.*;
+
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.libjitsi.*;
+
+/**
+ * Manages the list of active (currently pluged-in) capture/notify/playback
+ * devices and manages user preferences between all known devices (previously
+ * and actually plugged-in).
+ *
+ * @author Vincent Lucas
+ */
+public abstract class Devices
+{
+    /**
+     * The selected active device.
+     */
+    protected CaptureDeviceInfo device = null;
+
+    /**
+     * The list of device names saved by the congifuration service and
+     * previously saved given user preference order.
+     */
+    protected List<String> devicePreferences = new ArrayList<String>();
+
+    /**
+     * The flags that can save if the FLAG_DEVICE_IS_NULL state is set.
+     */
+    private int flags;
+
+    /**
+     * The audio system managing this device list.
+     */
+    private AudioSystem audioSystem;
+
+    /**
+     * Initializes the device list managment.
+     *
+     * @param audioSystem The audio system managing this device list.
+     */
+    public Devices(AudioSystem audioSystem)
+    {
+        this.audioSystem = audioSystem;
+    }
+
+    /**
+     * Gets the selected active device.
+     *
+     * @param locator The string representation of the locator.
+     *
+     * @return The selected active device.
+     */
+    public CaptureDeviceInfo getDevice(String locator)
+    {
+        List<CaptureDeviceInfo> devices = getDevices(locator);
+
+        // Reinit the selected device if this one is no more in the active list.
+        if (this.device != null)
+        {
+            if ((devices == null) || !devices.contains(this.device))
+            {
+                setDevice(locator, null, false);
+            }
+        }
+
+        CaptureDeviceInfo device = this.device;
+
+        // If the device is null and the device is not desactivated, then try to
+        // find the user preferred one between the active devices.
+        if ((device == null) && ((flags & getFlagDeviceIsNull()) == 0))
+        {
+            if ((devices != null) && (devices.size() > 0))
+            {
+                device = loadDevice(locator, getPropDevice(), devices);
+                if (device == null)
+                    device = devices.get(0);
+            }
+        }
+        return device;
+    }
+
+    /**
+     * Returns the list of the active devices.
+     *
+     * @param locator The string representation of the locator.
+     *
+     * @return The list of the active devices.
+     */
+    public abstract List<CaptureDeviceInfo> getDevices(String locator);
+
+    /**
+     * Loads the user's preference with respect to a <tt>CaptureDeviceInfo</tt>
+     * among a specific list of <tt>CaptureDeviceInfo</tt>s from the
+     * <tt>ConfigurationService</tt>.
+     *
+     * @param locator The string representation of the locator.
+     * @param property the name of the <tt>ConfigurationService</tt> property
+     * which specifies the user's preference with respect to a
+     * <tt>CaptureDeviceInfo</tt> among the specified list of
+     * <tt>CaptureDeviceInfo</tt>s 
+     * @param devices the list of <tt>CaptureDeviceInfo</tt>s which are valid
+     * selections for the user's preference
+     * @return a <tt>CaptureDeviceInfo</tt> among the specified <tt>devices</tt>
+     * which represents the user's preference stored in the
+     * <tt>ConfigurationService</tt>
+     */
+    private CaptureDeviceInfo loadDevice(
+            String locator,
+            String property,
+            List<CaptureDeviceInfo> devices)
+    {
+        loadDevicePreferences(locator, property);
+
+        // Search if an active device is a new one (is not stored in the
+        // preferences yet). If true, then active this device and set it as
+        // default device.
+        for(CaptureDeviceInfo device : devices)
+        {
+            if(!devicePreferences.contains(device.getName()))
+            {
+                // Adds the device in the preference list (to the end of the
+                // list, but the save device will push it to the top of active
+                // devices).
+                devicePreferences.add(device.getName());
+                this.saveDevice(locator, property, device, false);
+            }
+        }
+        // Search if an active device match one of the previsouly configured in
+        // the preferences.
+        for(int i = 0; i < devicePreferences.size(); ++i)
+        {
+            for(CaptureDeviceInfo device : devices)
+                if (devicePreferences.get(i).equals(device.getName()))
+                    return device;
+        }
+
+        // If no active devices matches a configured one, then gets the first
+        // active device available.
+        if(devices.size() > 0)
+        {
+            return devices.get(0);
+        }
+
+        // Else if nothing was found, then returns null.
+        return null;
+    }
+
+    /**
+     * Loads device name ordered with user's preference from the
+     * <tt>ConfigurationService</tt>.
+     *
+     * @param locator The string representation of the locator.
+     * @param property the name of the <tt>ConfigurationService</tt> property
+     * which specifies the user's preference.
+     */
+    private void loadDevicePreferences(String locator, String property)
+    {
+        ConfigurationService cfg = LibJitsi.getConfigurationService();
+
+        if (cfg != null)
+        {
+            String new_property
+                = DeviceConfiguration.PROP_AUDIO_SYSTEM
+                    + "."
+                    + locator
+                    + "."
+                    + property
+                    + "_list";
+
+            String deviceNamesString = cfg.getString(new_property);
+
+            if (deviceNamesString != null
+                    && !NoneAudioSystem.LOCATOR_PROTOCOL.equalsIgnoreCase(
+                        deviceNamesString))
+            {
+                devicePreferences.clear();
+                // We must parce the string in order to load the device list.
+                String[] deviceNames = deviceNamesString
+                    .substring(2, deviceNamesString.length() - 2)
+                    .split("\", \"");
+                for(int i = 0; i < deviceNames.length; ++i)
+                {
+                    devicePreferences.add(deviceNames[i]);
+                }
+            }
+            // Else, use the old property to load the last preferred device.
+            // This whole "else" block may be removed in the future.
+            else
+            {
+                String old_property
+                    = DeviceConfiguration.PROP_AUDIO_SYSTEM
+                        + "."
+                        + locator
+                        + "."
+                        + property;
+
+                deviceNamesString = cfg.getString(old_property);
+
+                if (deviceNamesString != null
+                        && !NoneAudioSystem.LOCATOR_PROTOCOL.equalsIgnoreCase(
+                            deviceNamesString))
+                {
+                    devicePreferences.clear();
+                    devicePreferences.add(deviceNamesString);
+                }
+            }
+        }
+    }
+
+    /**
+     * Saves the new selected device in top of the user preferences.
+     *
+     * @param locator The string representation of the locator.
+     * @param property the name of the <tt>ConfigurationService</tt> property
+     * into which the user's preference with respect to the specified
+     * <tt>CaptureDeviceInfo</tt> is to be saved
+     * @param device the <tt>CaptureDeviceInfo</tt> selected by the user.
+     * @param isNull <tt>true</tt> if the user's preference with respect to the
+     * specified <tt>device</tt> is <tt>null</tt>; otherwise, <tt>false</tt>
+     */
+    private void saveDevice(
+            String locator,
+            String property,
+            CaptureDeviceInfo device,
+            boolean isNull)
+    {
+        ConfigurationService cfg = LibJitsi.getConfigurationService();
+
+        if (cfg != null)
+        {
+            property
+                = DeviceConfiguration.PROP_AUDIO_SYSTEM
+                    + "." + locator
+                    + "." + property
+                    + "_list";
+            if(device == null)
+            {
+                if(isNull)
+                {
+                    cfg.setProperty(property, NoneAudioSystem.LOCATOR_PROTOCOL);
+                }
+                else
+                {
+                    cfg.removeProperty(property);
+                }
+            }
+            else
+            {
+                // Sorts the user preferences to put the selected device on top.
+                ArrayList resultList
+                    = new ArrayList<String>(devicePreferences.size() + 1);
+                List<CaptureDeviceInfo> devices = getDevices(locator);
+                boolean firstActiveFound = false;
+                for(int i = 0; i < devicePreferences.size(); ++i)
+                {
+                    // Checks if this element is an active device.
+                    for(int j = 0; !firstActiveFound && j < devices.size(); ++j)
+                    {
+                        if(devicePreferences.get(i).equals(
+                                    devices.get(j).getName()))
+                        {
+                            // The first active device is found, then place the
+                            // selected device at the previous place.
+                            resultList.add(device.getName());
+                            firstActiveFound = true;
+                        }
+                    }
+
+                    // Checks that we do not add dupplicate of the selected
+                    // device>
+                    if(!devicePreferences.get(i).equals(device.getName()))
+                    {
+                        resultList.add(devicePreferences.get(i));
+                    }
+                }
+                // Updates the preferences list with the ew one computed.
+                devicePreferences = resultList;
+
+                // Saves the user preferences.
+                StringBuffer value = new StringBuffer("[\"");
+                if(devicePreferences.size() > 0)
+                {
+                    value.append(devicePreferences.get(0));
+                    for(int i = 1; i < devicePreferences.size(); ++i)
+                    {
+                        value.append("\", \"" + devicePreferences.get(i));
+                    }
+                }
+                value.append("\"]");
+                cfg.setProperty(property, value.toString());
+            }
+        }
+    }
+
+    /**
+     * Selects the active device.
+     *
+     * @param locator The string representation of the locator.
+     * @param device The selected active device.
+     * @param save Flag set to true in order to save this choice in the
+     * configuration. False otherwise.
+     */
+    public void setDevice(
+            String locator,
+            CaptureDeviceInfo device,
+            boolean save)
+    {
+        // Checks if there is a change.
+        if ((this.device != device) || (device == null))
+        {
+            CaptureDeviceInfo oldValue = this.device;
+            this.device = device;
+
+            // Saves the new selected device in top of the user preferences.
+            if (save)
+            {
+                boolean isNull = (this.device == null);
+
+                if (isNull)
+                    flags |= getFlagDeviceIsNull();
+                else
+                    flags &= ~getFlagDeviceIsNull();
+
+                saveDevice(locator, getPropDevice(), this.device, isNull);
+            }
+
+            CaptureDeviceInfo newValue = getDevice(locator);
+
+            if (oldValue != newValue)
+            {
+                if(this.audioSystem != null)
+                {
+                    this.audioSystem
+                        .propertyChange(getPropDevice(), oldValue, newValue);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the list of the active devices.
+     *
+     * @param activeDevices The list of the active devices.
+     */
+    public abstract void setActiveDevices(
+            List<CaptureDeviceInfo> activeDevices);
+
+    /**
+     * Returns the flag nuber if the capture device is null.
+     *
+     * @return The flag nuber if the capture device is null.
+     */
+    protected abstract int getFlagDeviceIsNull();
+
+    /**
+     * Returns the property of the capture devices.
+     *
+     * @return The property of the capture devices.
+     */
+    protected abstract String getPropDevice();
+}
diff --git a/src/org/jitsi/impl/neomedia/device/NotifyDevices.java b/src/org/jitsi/impl/neomedia/device/NotifyDevices.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff76b22acc25ef5c704b25f567de056ad56c1347
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/device/NotifyDevices.java
@@ -0,0 +1,62 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.device;
+
+import java.util.*;
+
+import javax.media.*;
+
+/**
+ * Manages the list of active (currently pluged-in) notify devices and manages
+ * user preferences between all known devices (previously and actually
+ * plugged-in).
+ *
+ * @author Vincent Lucas
+ */
+public class NotifyDevices
+    extends PlaybackDevices
+{
+    /**
+     * The flag nuber if the notify device is null.
+     */
+    protected static final int FLAG_DEVICE_IS_NULL = 2;
+
+    /**
+     * The property of the notify devices.
+     */
+    public static final String PROP_DEVICE = "notifyDevice";
+
+    /**
+     * Initializes the notify device list managment.
+     *
+     * @param audioSystem The audio system managing this notify device list.
+     */
+    public NotifyDevices(AudioSystem audioSystem)
+    {
+        super(audioSystem);
+    }
+
+    /**
+     * Returns the flag nuber if the capture device is null.
+     *
+     * @return The flag nuber if the capture device is null.
+     */
+    protected int getFlagDeviceIsNull()
+    {
+        return FLAG_DEVICE_IS_NULL;
+    }
+
+    /**
+     * Returns the property of the capture devices.
+     *
+     * @return The property of the capture devices.
+     */
+    protected String getPropDevice()
+    {
+        return PROP_DEVICE;
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java b/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2ba74231c934b636f628c48d2de43a2bc4abb83
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java
@@ -0,0 +1,96 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.device;
+
+import java.util.*;
+
+import javax.media.*;
+
+/**
+ * Manages the list of active (currently pluged-in) playback devices and manages
+ * user preferences between all known devices (previously and actually
+ * plugged-in).
+ *
+ * @author Vincent Lucas
+ */
+public class PlaybackDevices
+    extends Devices
+{
+    /**
+     * The flag nuber if the playback device is null.
+     */
+    protected static final int FLAG_DEVICE_IS_NULL = 4;
+
+    /**
+     * The property of the playback devices.
+     */
+    public static final String PROP_DEVICE = "playbackDevice";
+
+    /**
+     * The list of active (actually plugged-in) playback devices.
+     */
+    private List<CaptureDeviceInfo> activePlaybackDevices = null;
+
+    /**
+     * Initializes the playback device list managment.
+     *
+     * @param audioSystem The audio system managing this playback device list.
+     */
+    public PlaybackDevices(AudioSystem audioSystem)
+    {
+        super(audioSystem);
+    }
+
+    /**
+     * Returns the list of the active devices.
+     *
+     * @param locator The string representation of the locator.
+     *
+     * @return The list of the active devices.
+     */
+    public List<CaptureDeviceInfo> getDevices(String locator)
+    {
+        List<CaptureDeviceInfo> activePlaybackDevices
+            = this.activePlaybackDevices;
+
+        return (activePlaybackDevices == null)
+                ? null
+                : new ArrayList<CaptureDeviceInfo>(activePlaybackDevices);
+    }
+
+    /**
+     * Sets the list of the active devices.
+     *
+     * @param activeDevices The list of the active devices.
+     */
+    public void setActiveDevices(List<CaptureDeviceInfo> activeDevices)
+    {
+        this.activePlaybackDevices = (activeDevices == null)
+                ? null
+                : new ArrayList<CaptureDeviceInfo>(activeDevices);
+    }
+
+    /**
+     * Returns the flag nuber if the capture device is null.
+     *
+     * @return The flag nuber if the capture device is null.
+     */
+    protected int getFlagDeviceIsNull()
+    {
+        return FLAG_DEVICE_IS_NULL;
+    }
+
+    /**
+     * Returns the property of the capture devices.
+     *
+     * @return The property of the capture devices.
+     */
+    protected String getPropDevice()
+    {
+        return PROP_DEVICE;
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java b/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
index 6a78d3625930e7741c37a6a33eed9f6243d8a8b6..09b8c170282510eff7a205facb740b04cd3028b3 100644
--- a/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
@@ -69,7 +69,8 @@ public Renderer createRenderer(boolean playback)
             locator = null;
         else
         {
-            CaptureDeviceInfo notifyDevice = getNotifyDevice();
+            CaptureDeviceInfo notifyDevice
+                = getDevice(AudioSystem.NOTIFY_INDEX);
 
             if (notifyDevice == null)
                 return null;
diff --git a/src/org/jitsi/impl/neomedia/device/PulseAudioSystem.java b/src/org/jitsi/impl/neomedia/device/PulseAudioSystem.java
index aacdbc7a46d78494d7553d6857e3539aad2730aa..3c76ee51f067e0bd7c6250fa48296bf7fdee6f92 100644
--- a/src/org/jitsi/impl/neomedia/device/PulseAudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/PulseAudioSystem.java
@@ -170,7 +170,8 @@ public Renderer createRenderer(boolean playback)
             locator = null;
         else
         {
-            CaptureDeviceInfo notifyDevice = getNotifyDevice();
+            CaptureDeviceInfo notifyDevice
+                = getDevice(AudioSystem.NOTIFY_INDEX);
 
             if (notifyDevice == null)
                 return null;
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java
index f37ba16eeafdecbaf24f9bf6576046f64dcbce45..0b24896cec01df8c77b9a9bfce8391c42261d2ee 100644
--- a/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java
@@ -78,7 +78,7 @@ public MediaLocator getLocator()
         if ((locator == null) && (audioSystem != null))
         {
             CaptureDeviceInfo playbackDevice
-                = audioSystem.getPlaybackDevice();
+                = audioSystem.getDevice(AudioSystem.PLAYBACK_INDEX);
 
             if (playbackDevice != null)
                 locator = playbackDevice.getLocator();
@@ -121,7 +121,7 @@ protected void playbackDevicePropertyChange(PropertyChangeEvent event)
 
     private void propertyChange(PropertyChangeEvent event)
     {
-        if (AudioSystem.PROP_PLAYBACK_DEVICE.equals(event.getPropertyName()))
+        if (PlaybackDevices.PROP_DEVICE.equals(event.getPropertyName()))
             playbackDevicePropertyChange(event);
     }