diff --git a/lib/native/mac/libjnmaccoreaudio.jnilib b/lib/native/mac/libjnmaccoreaudio.jnilib
index 36b14be8817c9e4c79dcf50bdc6e2017a6509850..5be4b22878f8b79b21dab272c8c80b913127a3f2 100755
Binary files a/lib/native/mac/libjnmaccoreaudio.jnilib and b/lib/native/mac/libjnmaccoreaudio.jnilib differ
diff --git a/src/native/build.xml b/src/native/build.xml
index 503182ebe525ba5c54878364d05ffb899331f63f..6e43b3c9e8d3f49dc3c35d513e7d2e767bb13326 100644
--- a/src/native/build.xml
+++ b/src/native/build.xml
@@ -932,6 +932,8 @@
       <linkerarg value="Foundation" />
       <linkerarg value="-framework" />
       <linkerarg value="Coreaudio" />
+      <linkerarg value="-framework" />
+      <linkerarg value="AudioToolbox" />
 
       <fileset dir="${src}/native/macosx/coreaudio/lib" includes="*.c"/>
       <fileset dir="${src}/native/macosx/coreaudio/jni" includes="*.c"/>
diff --git a/src/native/macosx/coreaudio/jni/maccoreaudio_util.c b/src/native/macosx/coreaudio/jni/maccoreaudio_util.c
new file mode 100644
index 0000000000000000000000000000000000000000..2a74413146b2b7b5c39f61a5bb7b4e7afde54f87
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/maccoreaudio_util.c
@@ -0,0 +1,250 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "maccoreaudio_util.h"
+
+#include "../lib/device.h"
+
+#include <string.h>
+
+/**
+ * JNI utilities.
+ *
+ * @author Vincent Lucas
+ */
+
+// Private static objects.
+
+static JavaVM * maccoreaudio_VM = NULL;
+
+static jclass maccoreaudio_devicesChangedCallbackClass = 0;
+static jmethodID maccoreaudio_devicesChangedCallbackMethodID = 0;
+
+void maccoreaudio_initHotplug(
+        void);
+void maccoreaudio_freeHotplug(
+        void);
+
+
+// Implementation
+
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *pvt)
+{
+    maccoreaudio_VM = vm;
+    maccoreaudio_initHotplug();
+    return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM *vm, void *pvt)
+{
+    maccoreaudio_freeHotplug();
+    maccoreaudio_VM = NULL;
+}
+
+/**
+ * Gets a new <tt>jbyteArray</tt> instance which is initialized with the bytes
+ * of a specific C string i.e. <tt>const char *</tt>.
+ *
+ * @param env
+ * @param str the bytes/C string to initialize the new <tt>jbyteArray</tt>
+ * instance with
+ * @return a new <tt>jbyteArray</tt> instance which is initialized with the
+ * bytes of the specified <tt>str</tt>
+ */
+jbyteArray maccoreaudio_getStrBytes(
+        JNIEnv *env,
+        const char *str)
+{
+    jbyteArray bytes;
+
+    if (str)
+    {
+        size_t length = strlen(str);
+
+        bytes = (*env)->NewByteArray(env, length);
+        if (bytes && length)
+            (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte *) str);
+    }
+    else
+        bytes = NULL;
+    return bytes;
+}
+
+/**
+ * Returns a callback method identifier.
+ *
+ * @param env
+ * @param callback The object called back.
+ * @param callbackFunctionName The name of the function used for the callback.
+ *
+ * @return A callback method identifier. 0 if the callback function is not
+ * found.
+ */
+jmethodID maccoreaudio_getCallbackMethodID(
+        JNIEnv *env,
+        jobject callback,
+        char* callbackFunctionName)
+{
+    jclass callbackClass;
+    jmethodID callbackMethodID = NULL;
+
+    if(callback)
+    {
+        if((callbackClass = (*env)->GetObjectClass(env, callback)))
+        {
+            callbackMethodID = (*env)->GetMethodID(
+                    env,
+                    callbackClass,
+                    callbackFunctionName,
+                    "([BI)V");
+        }
+    }
+
+    return callbackMethodID;
+}
+
+/**
+ * Calls back the java side when respectively reading / wrtiting the input
+ * /output stream.
+ */
+void maccoreaudio_callbackMethod(
+        char *buffer,
+        int bufferLength,
+        void* callback,
+        void* callbackMethod)
+{
+    JNIEnv *env = NULL;
+
+    if((*maccoreaudio_VM)->AttachCurrentThreadAsDaemon(
+                maccoreaudio_VM,
+                (void**) &env,
+                NULL)
+            == 0)
+    {
+        jbyteArray bufferBytes = (*env)->NewByteArray(env, bufferLength);
+        (*env)->SetByteArrayRegion(
+                env,
+                bufferBytes,
+                0,
+                bufferLength,
+                (jbyte *) buffer);
+
+        (*env)->CallVoidMethod(
+                env,
+                callback,
+                (jmethodID) callbackMethod,
+                bufferBytes,
+                bufferLength);
+
+        jbyte* bytes = (*env)->GetByteArrayElements(env, bufferBytes, NULL);
+        memcpy(buffer, bytes, bufferLength);
+        (*env)->ReleaseByteArrayElements(env, bufferBytes, bytes, 0);
+
+        (*maccoreaudio_VM)->DetachCurrentThread(maccoreaudio_VM);
+    }
+}
+
+/**
+ * Calls back the java side when the device list has changed.
+ */
+void maccoreaudio_devicesChangedCallbackMethod(void)
+{
+    JNIEnv *env = NULL;
+
+    if((*maccoreaudio_VM)->AttachCurrentThreadAsDaemon(
+                maccoreaudio_VM,
+                (void**) &env,
+                NULL)
+            == 0)
+    {
+        jclass class = maccoreaudio_devicesChangedCallbackClass;
+        jmethodID methodID = maccoreaudio_devicesChangedCallbackMethodID;
+        if(class && methodID)
+        {
+            (*env)->CallStaticVoidMethod(env, class, methodID);
+        }
+
+        (*maccoreaudio_VM)->DetachCurrentThread(maccoreaudio_VM);
+    }
+}
+
+/**
+ * Initializes the hotplug callback process.
+ */
+void maccoreaudio_initHotplug(
+        void)
+{
+    JNIEnv *env = NULL;
+
+    if((*maccoreaudio_VM)->AttachCurrentThreadAsDaemon(
+                maccoreaudio_VM,
+                (void**) &env,
+                NULL)
+            == 0)
+    {
+        if(maccoreaudio_devicesChangedCallbackClass == NULL
+                && maccoreaudio_devicesChangedCallbackMethodID == NULL)
+        {
+            jclass devicesChangedCallbackClass = (*env)->FindClass(
+                    env,
+                    "org/jitsi/impl/neomedia/CoreAudioDevice");
+
+            if (devicesChangedCallbackClass)
+            {
+                devicesChangedCallbackClass
+                    = (*env)->NewGlobalRef(env, devicesChangedCallbackClass);
+                if (devicesChangedCallbackClass)
+                {
+                    jmethodID devicesChangedCallbackMethodID
+                        = (*env)->GetStaticMethodID(
+                                env,
+                                devicesChangedCallbackClass,
+                                "devicesChangedCallback",
+                                "()V");
+
+                    if (devicesChangedCallbackMethodID)
+                    {
+                        maccoreaudio_devicesChangedCallbackClass
+                            = devicesChangedCallbackClass;
+                        maccoreaudio_devicesChangedCallbackMethodID
+                            = devicesChangedCallbackMethodID;
+
+                        maccoreaudio_initializeHotplug(
+                                maccoreaudio_devicesChangedCallbackMethod);
+                    }
+                }
+            }
+        }
+        (*maccoreaudio_VM)->DetachCurrentThread(maccoreaudio_VM);
+    }
+}
+
+/**
+ * Frees the hotplug callback process.
+ */
+void maccoreaudio_freeHotplug(
+        void)
+{
+    maccoreaudio_uninitializeHotplug();
+    JNIEnv *env = NULL;
+
+    if((*maccoreaudio_VM)->AttachCurrentThreadAsDaemon(
+                maccoreaudio_VM,
+                (void**) &env,
+                NULL)
+            == 0)
+    {
+        (*env)->DeleteGlobalRef(
+                env,
+                maccoreaudio_devicesChangedCallbackClass);
+        (*maccoreaudio_VM)->DetachCurrentThread(maccoreaudio_VM);
+    }
+    maccoreaudio_devicesChangedCallbackClass = NULL;
+    maccoreaudio_devicesChangedCallbackMethodID = NULL;
+}
diff --git a/src/native/macosx/coreaudio/jni/maccoreaudio_util.h b/src/native/macosx/coreaudio/jni/maccoreaudio_util.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f8b5cb96e81dad75d87449f3cebd6e9c13fadb5
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/maccoreaudio_util.h
@@ -0,0 +1,42 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#ifndef __maccoreaudio_util_h
+#define __maccoreaudio_util_h
+
+#include <jni.h>
+
+/**
+ * JNI utilities.
+ *
+ * @author Vincent Lucas
+ */
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *pvt);
+
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM *vm, void *pvt);
+
+jbyteArray maccoreaudio_getStrBytes(
+        JNIEnv *env,
+        const char *str);
+
+jmethodID maccoreaudio_getCallbackMethodID(
+        JNIEnv *env,
+        jobject callback,
+        char* callbackFunctionName);
+
+void maccoreaudio_callbackMethod(
+        char *buffer,
+        int bufferLength,
+        void* callback,
+        void* callbackMethod);
+
+void maccoreaudio_devicesChangedCallbackMethod(
+        void);
+
+#endif
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.c b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.c
index 7ad57402963b4483622548bf350f73a17ff950d2..38022b13b19b467e0247bfaa86c73bfe6a38d972 100644
--- a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.c
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.c
@@ -8,31 +8,28 @@
 #include "org_jitsi_impl_neomedia_CoreAudioDevice.h"
 
 #include "../lib/device.h"
+#include "maccoreaudio_util.h"
 
 /**
  * JNI code for CoreAudioDevice.
  *
- * @author Vicnent Lucas
+ * @author Vincent Lucas
  */
 
-// Private functions
-
-static jbyteArray getStrBytes(JNIEnv *env, const char *str);
-
 // Implementation
 
 JNIEXPORT jint JNICALL
 Java_org_jitsi_impl_neomedia_CoreAudioDevice_initDevices
   (JNIEnv *env, jclass clazz)
 {
-    return initDevices();
+    return maccoreaudio_initDevices();
 }
 
 JNIEXPORT void JNICALL
 Java_org_jitsi_impl_neomedia_CoreAudioDevice_freeDevices
   (JNIEnv *env, jclass clazz)
 {
-    freeDevices();
+    maccoreaudio_freeDevices();
 }
 
 JNIEXPORT jbyteArray JNICALL
@@ -40,8 +37,8 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_getDeviceNameBytes
   (JNIEnv *env, jclass clazz, jstring deviceUID)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    char * deviceName = getDeviceName(deviceUIDPtr);
-    jbyteArray deviceNameBytes = getStrBytes(env, deviceName);
+    char * deviceName = maccoreaudio_getDeviceName(deviceUIDPtr);
+    jbyteArray deviceNameBytes = maccoreaudio_getStrBytes(env, deviceName);
     // Free
     free(deviceName);
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
@@ -54,9 +51,10 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_getDeviceModelIdentifierBytes
   (JNIEnv *env, jclass clazz, jstring deviceUID)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    char * deviceModelIdentifier = getDeviceModelIdentifier(deviceUIDPtr);
+    char * deviceModelIdentifier
+        = maccoreaudio_getDeviceModelIdentifier(deviceUIDPtr);
     jbyteArray deviceModelIdentifierBytes
-        = getStrBytes(env, deviceModelIdentifier);
+        = maccoreaudio_getStrBytes(env, deviceModelIdentifier);
     // Free
     free(deviceModelIdentifier);
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
@@ -69,7 +67,7 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_setInputDeviceVolume
   (JNIEnv *env, jclass clazz, jstring deviceUID, jfloat volume)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    jint err = setInputDeviceVolume(deviceUIDPtr, volume);
+    jint err = maccoreaudio_setInputDeviceVolume(deviceUIDPtr, volume);
     // Free
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
 
@@ -81,7 +79,7 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_setOutputDeviceVolume
   (JNIEnv *env, jclass clazz, jstring deviceUID, jfloat volume)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    jint err = setOutputDeviceVolume(deviceUIDPtr, volume);
+    jint err = maccoreaudio_setOutputDeviceVolume(deviceUIDPtr, volume);
     // Free
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
 
@@ -93,7 +91,7 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_getInputDeviceVolume
   (JNIEnv *env, jclass clazz, jstring deviceUID)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    jfloat volume = getInputDeviceVolume(deviceUIDPtr);
+    jfloat volume = maccoreaudio_getInputDeviceVolume(deviceUIDPtr);
     // Free
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
 
@@ -105,36 +103,9 @@ Java_org_jitsi_impl_neomedia_CoreAudioDevice_getOutputDeviceVolume
   (JNIEnv *env, jclass clazz, jstring deviceUID)
 {
     const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
-    jfloat volume = getOutputDeviceVolume(deviceUIDPtr);
+    jfloat volume = maccoreaudio_getOutputDeviceVolume(deviceUIDPtr);
     // Free
     (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
 
     return volume;
 }
-
-/**
- * Gets a new <tt>jbyteArray</tt> instance which is initialized with the bytes
- * of a specific C string i.e. <tt>const char *</tt>.
- *
- * @param env
- * @param str the bytes/C string to initialize the new <tt>jbyteArray</tt>
- * instance with
- * @return a new <tt>jbyteArray</tt> instance which is initialized with the
- * bytes of the specified <tt>str</tt>
- */
-static jbyteArray getStrBytes(JNIEnv *env, const char *str)
-{
-    jbyteArray bytes;
-
-    if (str)
-    {
-        size_t length = strlen(str);
-
-        bytes = (*env)->NewByteArray(env, length);
-        if (bytes && length)
-            (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte *) str);
-    }
-    else
-        bytes = NULL;
-    return bytes;
-}
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.h b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.h
index c02caef497f49e3594a415c5df6a0ce6922936de..df7d7a743d2cf4ccb226dffc29c899480698ae83 100644
--- a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.h
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_CoreAudioDevice.h
@@ -9,19 +9,19 @@ extern "C" {
 #endif
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    initDevices
- * Signature: ()I
+ * Method:    freeDevices
+ * Signature: ()V
  */
-JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_initDevices
+JNIEXPORT void JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_freeDevices
   (JNIEnv *, jclass);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    freeDevices
- * Signature: ()V
+ * Method:    getDeviceModelIdentifierBytes
+ * Signature: (Ljava/lang/String;)[B
  */
-JNIEXPORT void JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_freeDevices
-  (JNIEnv *, jclass);
+JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getDeviceModelIdentifierBytes
+  (JNIEnv *, jclass, jstring);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
@@ -33,43 +33,43 @@ JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getDev
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    getDeviceModelIdentifierBytes
- * Signature: (Ljava/lang/String;)[B
+ * Method:    getInputDeviceVolume
+ * Signature: (Ljava/lang/String;)F
  */
-JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getDeviceModelIdentifierBytes
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getInputDeviceVolume
   (JNIEnv *, jclass, jstring);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    setInputDeviceVolume
- * Signature: (Ljava/lang/String;F)I
+ * Method:    getOutputDeviceVolume
+ * Signature: (Ljava/lang/String;)F
  */
-JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_setInputDeviceVolume
-  (JNIEnv *, jclass, jstring, jfloat);
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getOutputDeviceVolume
+  (JNIEnv *, jclass, jstring);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    setOutputDeviceVolume
- * Signature: (Ljava/lang/String;F)I
+ * Method:    initDevices
+ * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_setOutputDeviceVolume
-  (JNIEnv *, jclass, jstring, jfloat);
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_initDevices
+  (JNIEnv *, jclass);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    getInputDeviceVolume
- * Signature: (Ljava/lang/String;)F
+ * Method:    setInputDeviceVolume
+ * Signature: (Ljava/lang/String;F)I
  */
-JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getInputDeviceVolume
-  (JNIEnv *, jclass, jstring);
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_setInputDeviceVolume
+  (JNIEnv *, jclass, jstring, jfloat);
 
 /*
  * Class:     org_jitsi_impl_neomedia_CoreAudioDevice
- * Method:    getOutputDeviceVolume
- * Signature: (Ljava/lang/String;)F
+ * Method:    setOutputDeviceVolume
+ * Signature: (Ljava/lang/String;F)I
  */
-JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_getOutputDeviceVolume
-  (JNIEnv *, jclass, jstring);
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_CoreAudioDevice_setOutputDeviceVolume
+  (JNIEnv *, jclass, jstring, jfloat);
 
 #ifdef __cplusplus
 }
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.c b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.c
new file mode 100644
index 0000000000000000000000000000000000000000..5ec04cd9267143d09efb6d49aed9cedf271626e3
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.c
@@ -0,0 +1,277 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "org_jitsi_impl_neomedia_MacCoreAudioDevice.h"
+
+#include "../lib/device.h"
+#include "maccoreaudio_util.h"
+
+/**
+ * JNI code for CoreAudioDevice.
+ *
+ * @author Vicnent Lucas
+ */
+
+// Implementation
+
+JNIEXPORT jobjectArray JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDeviceUIDList
+  (JNIEnv *env, jclass clazz)
+{
+    jobjectArray javaDeviceUIDList = NULL;
+
+    char ** deviceUIDList;
+    int nbDevices = maccoreaudio_getDeviceUIDList(&deviceUIDList);
+    if(nbDevices != -1)
+    {
+        jstring deviceUID;
+        jclass stringClass = (*env)->FindClass(env, "java/lang/String");
+        javaDeviceUIDList
+            = (*env)->NewObjectArray(env, nbDevices, stringClass, NULL);
+        int i;
+        for(i = 0; i < nbDevices; ++i)
+        {
+            deviceUID = (*env)->NewStringUTF(env, deviceUIDList[i]);
+            if(deviceUID != NULL)
+            {
+                (*env)->SetObjectArrayElement(
+                        env,
+                        javaDeviceUIDList,
+                        i,
+                        deviceUID);
+            }
+
+            free(deviceUIDList[i]);
+        }
+
+        free(deviceUIDList);
+    }
+
+    return javaDeviceUIDList;
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_isInputDevice
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint isInputDevice = maccoreaudio_isInputDevice(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return (isInputDevice != 0);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_isOutputDevice
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint isOutputDevice = maccoreaudio_isOutputDevice(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return (isOutputDevice != 0);
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getTransportTypeBytes
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    const char * transportType = maccoreaudio_getTransportType(deviceUIDPtr);
+    jbyteArray transportTypeBytes
+        = maccoreaudio_getStrBytes(env, transportType);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return transportTypeBytes;
+}
+
+JNIEXPORT jfloat JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getNominalSampleRate
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jfloat rate = maccoreaudio_getNominalSampleRate(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return rate;
+}
+
+JNIEXPORT jfloat JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getMinimalNominalSampleRate
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    Float64 minRate;
+    Float64 maxRate;
+    if(maccoreaudio_getAvailableNominalSampleRates(
+                deviceUIDPtr,
+                &minRate,
+                &maxRate)
+            != noErr)
+    {
+        fprintf(stderr,
+                "MacCoreAudioDevice_getMinimalNominalSampleRate\
+                    \n\tmaccoreaudio_getAvailableNominalSampleRates\n");
+        fflush(stderr);
+        return -1.0;
+    }
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return minRate;
+}
+
+JNIEXPORT jfloat JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getMaximalNominalSampleRate
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    Float64 minRate;
+    Float64 maxRate;
+    if(maccoreaudio_getAvailableNominalSampleRates(
+                deviceUIDPtr,
+                &minRate,
+                &maxRate)
+            != noErr)
+    {
+        fprintf(stderr,
+                "MacCoreAudioDevice_getMaximalNominalSampleRate\
+                    \n\tmaccoreaudio_getAvailableNominalSampleRates\n");
+        fflush(stderr);
+        return -1.0;
+    }
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return maxRate;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDefaultInputDeviceUIDBytes
+  (JNIEnv *env, jclass clazz)
+{
+    char* defaultInputDeviceUID = maccoreaudio_getDefaultInputDeviceUID();
+    jbyteArray defaultInputDeviceUIDBytes
+        = maccoreaudio_getStrBytes(env, defaultInputDeviceUID);
+    // Free
+    free(defaultInputDeviceUID);
+
+    return defaultInputDeviceUIDBytes;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDefaultOutputDeviceUIDBytes
+  (JNIEnv *env, jclass clazz)
+{
+    char* defaultOutputDeviceUID
+        = maccoreaudio_getDefaultOutputDeviceUID();
+    jbyteArray defaultOutputDeviceUIDBytes
+        = maccoreaudio_getStrBytes(env, defaultOutputDeviceUID);
+    // Free
+    free(defaultOutputDeviceUID);
+
+    return defaultOutputDeviceUIDBytes;
+}
+
+JNIEXPORT jlong JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_startStream
+  (JNIEnv *env, jclass clazz, jstring deviceUID, jobject callback,
+        jfloat sampleRate,
+        jint nbChannels,
+        jint bitsPerChannel,
+        jboolean isFloat,
+        jboolean isBigEndian,
+        jboolean isNonInterleaved)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jobject callbackObject = (*env)->NewGlobalRef(env, callback);
+    maccoreaudio_stream* stream = NULL;
+
+    if(maccoreaudio_isInputDevice(deviceUIDPtr)) // input
+    {
+        jmethodID callbackMethod = maccoreaudio_getCallbackMethodID(
+                env,
+                callback,
+                "readInput");
+        stream = maccoreaudio_startInputStream(
+                deviceUIDPtr,
+                (void*) maccoreaudio_callbackMethod,
+                callbackObject,
+                callbackMethod,
+                sampleRate,
+                nbChannels,
+                bitsPerChannel,
+                isFloat,
+                isBigEndian,
+                isNonInterleaved);
+    }
+    else if(maccoreaudio_isOutputDevice(deviceUIDPtr)) // output
+    {
+        jmethodID callbackMethod = maccoreaudio_getCallbackMethodID(
+                env,
+                callback,
+                "writeOutput");
+        stream = maccoreaudio_startOutputStream(
+                deviceUIDPtr,
+                (void*) maccoreaudio_callbackMethod,
+                callbackObject,
+                callbackMethod,
+                sampleRate,
+                nbChannels,
+                bitsPerChannel,
+                isFloat,
+                isBigEndian,
+                isNonInterleaved);
+    }
+
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return (long) stream;
+}
+
+JNIEXPORT void JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_stopStream
+  (JNIEnv *env, jclass clazz, jstring deviceUID, jlong streamPtr)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    maccoreaudio_stream * stream = (maccoreaudio_stream*) (long) streamPtr;
+
+    maccoreaudio_stopStream(deviceUIDPtr, stream);
+
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+    (*env)->DeleteGlobalRef(env, stream->callbackObject);
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_countInputChannels
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint nbChannels = maccoreaudio_countInputChannels(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return nbChannels;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_countOutputChannels
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint nbChannels = maccoreaudio_countOutputChannels(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return nbChannels;
+}
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.h b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.h
new file mode 100644
index 0000000000000000000000000000000000000000..729cbe1dc32745fc6b70aa71b32f3de124d98944
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_MacCoreAudioDevice.h
@@ -0,0 +1,125 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_jitsi_impl_neomedia_MacCoreAudioDevice */
+
+#ifndef _Included_org_jitsi_impl_neomedia_MacCoreAudioDevice
+#define _Included_org_jitsi_impl_neomedia_MacCoreAudioDevice
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef org_jitsi_impl_neomedia_MacCoreAudioDevice_DEFAULT_SAMPLE_RATE
+#define org_jitsi_impl_neomedia_MacCoreAudioDevice_DEFAULT_SAMPLE_RATE 44100.0
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getDeviceUIDList
+ * Signature: ()[Ljava/lang/String;
+ */
+JNIEXPORT jobjectArray JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDeviceUIDList
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    isInputDevice
+ * Signature: (Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_isInputDevice
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    isOutputDevice
+ * Signature: (Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_isOutputDevice
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getTransportTypeBytes
+ * Signature: (Ljava/lang/String;)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getTransportTypeBytes
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getNominalSampleRate
+ * Signature: (Ljava/lang/String;)F
+ */
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getNominalSampleRate
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getMinimalNominalSampleRate
+ * Signature: (Ljava/lang/String;)F
+ */
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getMinimalNominalSampleRate
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getMaximalNominalSampleRate
+ * Signature: (Ljava/lang/String;)F
+ */
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getMaximalNominalSampleRate
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getDefaultInputDeviceUIDBytes
+ * Signature: ()[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDefaultInputDeviceUIDBytes
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    getDefaultOutputDeviceUIDBytes
+ * Signature: ()[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_getDefaultOutputDeviceUIDBytes
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    startStream
+ * Signature: (Ljava/lang/String;Ljava/lang/Object;)J
+ */
+JNIEXPORT jlong JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_startStream
+  (JNIEnv *, jclass, jstring, jobject,
+        jfloat sampleRate,
+        jint nbChannels,
+        jint bitsPerChannel,
+        jboolean isFloat,
+        jboolean isBigEndian,
+        jboolean isNonInterleaved);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    stopStream
+ * Signature: (Ljava/lang/String;J)V
+ */
+JNIEXPORT void JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_stopStream
+  (JNIEnv *, jclass, jstring, jlong);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    countInputChannels
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_countInputChannels
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_MacCoreAudioDevice
+ * Method:    countOutputChannels
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_MacCoreAudioDevice_countOutputChannels
+  (JNIEnv *, jclass, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/native/macosx/coreaudio/lib/device.c b/src/native/macosx/coreaudio/lib/device.c
index 0894ae2aaacb64cab59e97c4c26721a6413a4fc3..d517c2f3ee54169d6b4f401323debd2f366685fc 100644
--- a/src/native/macosx/coreaudio/lib/device.c
+++ b/src/native/macosx/coreaudio/lib/device.c
@@ -9,39 +9,139 @@
 #include <CoreAudio/CoreAudio.h>
 #include <CoreFoundation/CFString.h>
 #include <stdio.h>
+
 /**
  * Functions to list, access and modifies audio devices via coreaudio.
  *
  * @author Vincent Lucas
  */
+const char* transportTypeAggregate = "Aggregate";
+const char* transportTypeAirPlay = "AirPlay";
+const char* transportTypeAutoAggregate = "Auto aggregate";
+const char* transportTypeAVB = "AVB";
+const char* transportTypeBlueTooth = "Bluetooth";
+const char* transportTypeBuiltIn = "Built-in";
+const char* transportTypeDisplayPort = "DisplayPort";
+const char* transportTypeFireWire = "FireWire";
+const char* transportTypeHDMI = "HDMI";
+const char* transportTypePCI = "PCI";
+const char* transportTypeThunderbolt = "Thunderbolt";
+const char* transportTypeUnknown = "Unknown";
+const char* transportTypeUSB = "USB";
+const char* transportTypeVirtual = "Virtual";
 
 /**
  * Private definition of functions,
  */
-char* getDeviceProperty(
+
+AudioDeviceID maccoreaudio_getDevice(
+        const char * deviceUID);
+
+AudioDeviceID maccoreaudio_getDeviceForSpecificScope(
+        const char * deviceUID,
+        UInt32 inputOutputScope);
+
+char* maccoreaudio_getDeviceProperty(
         const char * deviceUID,
         AudioObjectPropertySelector propertySelector);
 
-OSStatus setDeviceVolume(
+char* maccoreaudio_getAudioDeviceProperty(
+        AudioDeviceID device,
+        AudioObjectPropertySelector propertySelector);
+
+OSStatus maccoreaudio_setDeviceVolume(
         const char * deviceUID,
         Float32 volume,
         UInt32 inputOutputScope);
 
-Float32 getDeviceVolume(
+Float32 maccoreaudio_getDeviceVolume(
         const char * deviceUID,
         UInt32 inputOutputScope);
 
-OSStatus getChannelsForStereo(
+OSStatus maccoreaudio_getChannelsForStereo(
         const char * deviceUID,
         UInt32 * channels);
 
+int maccoreaudio_countChannels(
+        const char * deviceUID,
+        AudioObjectPropertyScope inputOutputScope);
+
+static OSStatus maccoreaudio_devicesChangedCallback(
+        AudioObjectID inObjectID,
+        UInt32 inNumberAddresses,
+        const AudioObjectPropertyAddress inAddresses[],
+        void *inClientData);
+
+OSStatus maccoreaudio_initConverter(
+        const char * deviceUID,
+        const AudioStreamBasicDescription * javaFormat,
+        unsigned char isJavaFormatSource,
+        AudioConverterRef * converter,
+        double * conversionRatio);
+
+inline UInt32 CalculateLPCMFlags (
+        UInt32 inValidBitsPerChannel,
+        UInt32 inTotalBitsPerChannel,
+        bool inIsFloat,
+        bool inIsBigEndian,
+        bool inIsNonInterleaved);
+
+inline void FillOutASBDForLPCM(
+        AudioStreamBasicDescription * outASBD,
+        Float64 inSampleRate,
+        UInt32 inChannelsPerFrame,
+        UInt32 inValidBitsPerChannel,
+        UInt32 inTotalBitsPerChannel,
+        bool inIsFloat,
+        bool inIsBigEndian,
+        bool inIsNonInterleaved);
+
+char* maccoreaudio_getDefaultDeviceUID(
+        UInt32 inputOutputScope);
+
+maccoreaudio_stream * maccoreaudio_startStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        void* readWriteFunction,
+        unsigned char isJavaFormatSource,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved);
+
+OSStatus maccoreaudio_readInputStream(
+        AudioDeviceID inDevice,
+        const AudioTimeStamp* inNow,
+        const AudioBufferList* inInputData,
+        const AudioTimeStamp* inInputTime,
+        AudioBufferList* outOutputData,
+        const AudioTimeStamp* inOutputTime,
+        void* inClientData);
+
+OSStatus maccoreaudio_writeOutputStream(
+        AudioDeviceID inDevice,
+        const AudioTimeStamp* inNow,
+        const AudioBufferList* inInputData,
+        const AudioTimeStamp* inInputTime,
+        AudioBufferList* outOutputData,
+        const AudioTimeStamp* inOutputTime,
+        void* inClientData);
+
+OSStatus maccoreaudio_getStreamVirtualFormat(
+        AudioStreamID stream,
+        AudioStreamBasicDescription * format);
+
 /**
  * Do nothing: there is no need to initializes anything to get device
  * information on MacOsX.
  *
  * @return Always returns 0 (always works).
  */
-int initDevices(void)
+int maccoreaudio_initDevices(void)
 {
     return 0;
 }
@@ -50,22 +150,75 @@ int initDevices(void)
  * Do nothing: there is no need to frees anything once getting device
  * information is finished on MacOsX.
  */
-void freeDevices(void)
+void maccoreaudio_freeDevices(void)
 {
     // Nothing to do.
 }
 
+/**
+ * Returns if the audio device is an input device.
+ *
+ * @param deviceUID The device UID.
+ *
+ * @return True if the given device identifier correspond to an input device.
+ * False otherwise.
+ */
+int maccoreaudio_isInputDevice(
+        const char * deviceUID)
+{
+    return (maccoreaudio_countChannels(
+                deviceUID,
+                kAudioDevicePropertyScopeInput) > 0);
+}
+
+/**
+ * Returns if the audio device is an output device.
+ *
+ * @param deviceUID The device UID.
+ *
+ * @return True if the given device identifier correspond to an output device.
+ * False otherwise.
+ */
+int maccoreaudio_isOutputDevice(
+        const char * deviceUID)
+{
+    return (maccoreaudio_countChannels(
+                deviceUID,
+                kAudioDevicePropertyScopeOutput) > 0);
+}
+
 /**
  * Returns the audio device corresponding to the UID given in parameter. Or
  * kAudioObjectUnknown if the device is nonexistant or if anything as failed.
  *
- * @pqrqm deviceUID The device UID.
+ * @param deviceUID The device UID.
  *
  * @return The audio device corresponding to the UID given in parameter. Or
  * kAudioObjectUnknown if the device is nonexistant or if anything as failed.
  */
-AudioDeviceID getDevice(
+AudioDeviceID maccoreaudio_getDevice(
         const char * deviceUID)
+{
+    return maccoreaudio_getDeviceForSpecificScope(
+            deviceUID,
+            kAudioObjectPropertyScopeGlobal);
+}
+
+/**
+ * Returns the audio device corresponding to the UID given in parameter for the
+ * specified scope (global, input or output). Or kAudioObjectUnknown if the
+ * device is nonexistant or if anything as failed.
+ *
+ * @param deviceUID The device UID.
+ * @param inputOutputScope The scope to tell if this is an output or an input
+ * device.
+ *
+ * @return The audio device corresponding to the UID given in parameter. Or
+ * kAudioObjectUnknown if the device is nonexistant or if anything as failed.
+ */
+AudioDeviceID maccoreaudio_getDeviceForSpecificScope(
+        const char * deviceUID,
+        UInt32 inputOutputScope)
 {
     OSStatus err = noErr;
     AudioObjectPropertyAddress address;
@@ -93,7 +246,7 @@ AudioDeviceID getDevice(
     translation.mOutputDataSize = sizeof(device);
     size = sizeof(translation);
     address.mSelector = kAudioHardwarePropertyDeviceForUID;
-    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mScope = inputOutputScope;
     address.mElement = kAudioObjectPropertyElementMaster;
 
     if((err = AudioObjectGetPropertyData(
@@ -117,6 +270,84 @@ AudioDeviceID getDevice(
     return device;
 }
 
+/**
+ * Returns the default input device UID.
+ *
+ * @return The default input device UID. NULL if an error occurs.
+ */
+char* maccoreaudio_getDefaultInputDeviceUID(void)
+{
+    return maccoreaudio_getDefaultDeviceUID(kAudioDevicePropertyScopeInput);
+}
+
+/**
+ * Returns the default output device UID.
+ *
+ * @return The default output device UID. NULL if an error occurs.
+ */
+char* maccoreaudio_getDefaultOutputDeviceUID(void)
+{
+    return maccoreaudio_getDefaultDeviceUID(kAudioDevicePropertyScopeOutput);
+}
+
+/**
+ * Returns the default device UID for input or ouput.
+ *
+ * @param inputOutputScope The scope to tell if this is an output or an input
+ * device.
+ *
+ * @return The default device UID for input or ouput. NULL if an error occurs.
+ */
+char* maccoreaudio_getDefaultDeviceUID(
+        UInt32 inputOutputScope)
+{
+    OSStatus err = noErr;
+    AudioDeviceID device;
+    UInt32 size = sizeof(AudioDeviceID);
+    AudioObjectPropertyAddress address;
+    char * deviceUID = NULL;
+
+    if(inputOutputScope == kAudioDevicePropertyScopeInput)
+    {
+        address.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+    }
+    else
+    {
+        address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+    }
+    address.mScope = inputOutputScope;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    if((err = AudioObjectGetPropertyData(
+                    kAudioObjectSystemObject,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &device))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_getDefaultDeviceUID (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                    ((int) err));
+        return NULL;
+    }
+
+    if((deviceUID = maccoreaudio_getAudioDeviceProperty(
+                    device,
+                    kAudioDevicePropertyDeviceUID))
+            == NULL)
+    {
+        fprintf(stderr,
+                "maccoreaudio_getDefaultDeviceUID (coreaudio/device.c): \
+                    \n\tgetAudioDeviceProperty\n");
+        return NULL;
+    }
+
+    return deviceUID;
+}
+
 /**
  * Returns the device name for the given device. Or NULL, if not available. The
  * returned string must be freed by the caller.
@@ -126,10 +357,10 @@ AudioDeviceID getDevice(
  * @return The device name for the given device. Or NULL, if not available. The
  * returned string must be freed by the caller.
  */
-char* getDeviceName(
+char* maccoreaudio_getDeviceName(
         const char * deviceUID)
 {
-    return getDeviceProperty(deviceUID, kAudioObjectPropertyName);
+    return maccoreaudio_getDeviceProperty(deviceUID, kAudioObjectPropertyName);
 }
 
 /**
@@ -141,33 +372,31 @@ char* getDeviceName(
  * @return The device model identifier for the given device. Or NULL, if not
  * available. The returned string must be freed by the caller.
  */
-char* getDeviceModelIdentifier(
+char* maccoreaudio_getDeviceModelIdentifier(
         const char * deviceUID)
 {
-    return getDeviceProperty(deviceUID, kAudioDevicePropertyModelUID);
+    return
+        maccoreaudio_getDeviceProperty(deviceUID, kAudioDevicePropertyModelUID);
 }
 
 /**
- * Returns the requested device property for the given device. Or NULL, if not
- * available. The returned string must be freed by the caller.
+ * Returns the requested device property for the given device UID. Or NULL, if
+ * not available. The returned string must be freed by the caller.
  *
- * @param device The device to get the name from.
+ * @param deviceUID The device identifier to get the property from.
  * @param propertySelector The property we want to retrieve.
  *
- * @return The requested device property for the given device. Or NULL, if not
- * available. The returned string must be freed by the caller.
+ * @return The requested device property for the given device UID. Or NULL, if
+ * not available. The returned string must be freed by the caller.
  */
-char* getDeviceProperty(
+char* maccoreaudio_getDeviceProperty(
         const char * deviceUID,
         AudioObjectPropertySelector propertySelector)
 {
     AudioDeviceID device;
-    OSStatus err = noErr;
-    AudioObjectPropertyAddress address;
-    UInt32 size;
 
     // Gets the correspoding device
-    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
     {
         fprintf(stderr,
                 "getDeviceProperty (coreaudio/device.c): \
@@ -175,6 +404,27 @@ char* getDeviceProperty(
         return NULL;
     }
 
+    return maccoreaudio_getAudioDeviceProperty(device, propertySelector);
+}
+
+/**
+ * Returns the requested device property for the given device. Or NULL, if not
+ * available. The returned string must be freed by the caller.
+ *
+ * @param device The device to get the name from.
+ * @param propertySelector The property we want to retrieve.
+ *
+ * @return The requested device property for the given device. Or NULL, if not
+ * available. The returned string must be freed by the caller.
+ */
+char* maccoreaudio_getAudioDeviceProperty(
+        AudioDeviceID device,
+        AudioObjectPropertySelector propertySelector)
+{
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+
     // Gets the device property
     CFStringRef deviceProperty;
     size = sizeof(deviceProperty);
@@ -230,11 +480,11 @@ char* getDeviceProperty(
  * @return noErr if everything works well. Another value if an error has
  * occured.  
  */
-OSStatus setInputDeviceVolume(
+OSStatus maccoreaudio_setInputDeviceVolume(
         const char * deviceUID,
         Float32 volume)
 {
-    return setDeviceVolume(
+    return maccoreaudio_setDeviceVolume(
             deviceUID,
             volume,
             kAudioDevicePropertyScopeInput);
@@ -250,11 +500,11 @@ OSStatus setInputDeviceVolume(
  * @return noErr if everything works well. Another value if an error has
  * occured.  
  */
-OSStatus setOutputDeviceVolume(
+OSStatus maccoreaudio_setOutputDeviceVolume(
         const char * deviceUID,
         Float32 volume)
 {
-    return setDeviceVolume(
+    return maccoreaudio_setDeviceVolume(
             deviceUID,
             volume,
             kAudioDevicePropertyScopeOutput);
@@ -274,7 +524,7 @@ OSStatus setOutputDeviceVolume(
  * @return noErr if everything works well. Another value if an error has
  * occured.  
  */
-OSStatus setDeviceVolume(
+OSStatus maccoreaudio_setDeviceVolume(
         const char * deviceUID,
         Float32 volume,
         UInt32 inputOutputScope)
@@ -286,7 +536,7 @@ OSStatus setDeviceVolume(
     UInt32 channels[2];
 
     // Gets the correspoding device
-    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
     {
         fprintf(stderr,
                 "setDeviceVolume (coreaudio/device.c): \
@@ -295,7 +545,7 @@ OSStatus setDeviceVolume(
     }
 
     // get the input device stereo channels
-    if((getChannelsForStereo(deviceUID, channels)) != noErr)
+    if((maccoreaudio_getChannelsForStereo(deviceUID, channels)) != noErr)
     {
         fprintf(stderr,
                 "setDeviceVolume (coreaudio/device.c): \
@@ -355,10 +605,10 @@ OSStatus setDeviceVolume(
  * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
  * if an error occurs.
  */
-Float32 getInputDeviceVolume(
+Float32 maccoreaudio_getInputDeviceVolume(
         const char * deviceUID)
 {
-    return getDeviceVolume(
+    return maccoreaudio_getDeviceVolume(
             deviceUID,
             kAudioDevicePropertyScopeInput);
 }
@@ -371,10 +621,10 @@ Float32 getInputDeviceVolume(
  * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
  * if an error occurs.
  */
-Float32 getOutputDeviceVolume(
+Float32 maccoreaudio_getOutputDeviceVolume(
         const char * deviceUID)
 {
-    return getDeviceVolume(
+    return maccoreaudio_getDeviceVolume(
             deviceUID,
             kAudioDevicePropertyScopeOutput);
 }
@@ -391,7 +641,7 @@ Float32 getOutputDeviceVolume(
  * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
  * if an error occurs.
  */
-Float32 getDeviceVolume(
+Float32 maccoreaudio_getDeviceVolume(
         const char * deviceUID,
         UInt32 inputOutputScope)
 {
@@ -403,7 +653,7 @@ Float32 getDeviceVolume(
     UInt32 channels[2];
 
     // Gets the correspoding device
-    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
     {
         fprintf(stderr,
                 "getDeviceVolume (coreaudio/device.c): \
@@ -412,7 +662,7 @@ Float32 getDeviceVolume(
     }
 
     // get the input device stereo channels
-    if((getChannelsForStereo(deviceUID, channels)) != noErr)
+    if((maccoreaudio_getChannelsForStereo(deviceUID, channels)) != noErr)
     {
         fprintf(stderr,
                 "getDeviceVolume (coreaudio/device.c): \
@@ -474,7 +724,7 @@ Float32 getDeviceVolume(
  * @return An OSStatus set to noErr if everything works well. Any other vlaue
  * otherwise.
  */
-OSStatus getChannelsForStereo(
+OSStatus maccoreaudio_getChannelsForStereo(
         const char * deviceUID,
         UInt32 * channels)
 {
@@ -484,7 +734,7 @@ OSStatus getChannelsForStereo(
     UInt32 size;
 
     // Gets the correspoding device
-    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
     {
         fprintf(stderr,
                 "getChannelsForStereo (coreaudio/device.c): \
@@ -514,3 +764,989 @@ OSStatus getChannelsForStereo(
 
     return err;
 }
+
+/**
+ * Returns the number of channels avaialable for input device.
+ *
+ * @param deviceUID The device UID to get the channels from.
+ *
+ * @return The number of channels avaialable for a given input device.
+ * -1 if an error occurs.
+ */
+int maccoreaudio_countInputChannels(
+        const char * deviceUID)
+{
+    return maccoreaudio_countChannels(
+                deviceUID,
+                kAudioDevicePropertyScopeInput);
+}
+
+/**
+ * Returns the number of channels avaialable for output device.
+ *
+ * @param deviceUID The device UID to get the channels from.
+ *
+ * @return The number of channels avaialable for a given output device.
+ * -1 if an error occurs.
+ */
+int maccoreaudio_countOutputChannels(
+        const char * deviceUID)
+{
+    return maccoreaudio_countChannels(
+                deviceUID,
+                kAudioDevicePropertyScopeOutput);
+}
+
+/**
+ * Returns the number of channels avaialable for a given input / output device.
+ *
+ * @param deviceUID The device UID to get the channels from.
+ * @param inputOutputScope The scope to tell if this is an output or an input
+ * device.
+ *
+ * @return The number of channels avaialable for a given input / output device.
+ * -1 if an error occurs.
+ */
+int maccoreaudio_countChannels(
+        const char * deviceUID,
+        AudioObjectPropertyScope inputOutputScope)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+    AudioBufferList *audioBufferList = NULL;
+    int nbChannels = 0;
+    int i;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getChannelsForStereo (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return -1;
+    }
+
+    // Gets the size of the streams for this device.
+    address.mSelector = kAudioDevicePropertyStreamConfiguration;
+    address.mScope = inputOutputScope;
+    address.mElement = kAudioObjectPropertyElementWildcard; // 0
+    if((err = AudioObjectGetPropertyDataSize(device, &address, 0, NULL, &size))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_countChannels (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyDataSize, err: %d\n",
+                    ((int) err));
+        return -1;
+    }
+
+    // Gets the number of channels ofr each stream.
+    if((audioBufferList = (AudioBufferList *) malloc(size)) == NULL)
+    {
+        perror("maccoreaudio_countChannels (coreaudio/device.c): \
+                \n\tmalloc\n");
+        return -1;
+    }
+    if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    audioBufferList))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_countChannels (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                    ((int) err));
+        return -1;
+    }
+    for(i = 0; i < audioBufferList->mNumberBuffers; ++i)
+    {
+        nbChannels += audioBufferList->mBuffers[i].mNumberChannels;
+    }
+    free(audioBufferList);
+
+    return nbChannels;
+}
+
+/**
+ * Returns the nominal sample rate for the given device.
+ *
+ * @param deviceUID The device UID to get the channels from.
+ *
+ * @return The nominal sample rate for the given device. -1.0 if an error
+ * occurs.
+ */
+Float64 maccoreaudio_getNominalSampleRate(
+        const char * deviceUID)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+    Float64 rate = -1.0;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getNominalSampleRate (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return -1.0;
+    }
+
+    // Gets the sample rate.
+    size = sizeof(Float64);
+    address.mSelector = kAudioDevicePropertyNominalSampleRate;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &rate))
+            != noErr)
+    {
+        fprintf(stderr,
+                "getNominalSampleRate (coreaudio/device.c): \
+                    \n\tAudioObjactGetPropertyData, err: %d\n",
+                    (int) err);
+        return -1.0;
+    }
+
+    return rate;
+}
+
+/**
+ * Gets the minimal and maximal nominal sample rate for the given device.
+ *
+ * @param deviceUID The device UID to get the channels from.
+ * @param minRate The minimal rate available for this device.
+ * @param maxRate The maximal rate available for this device.
+ *
+ * @return noErr if everything is alright. -1.0 if an error occurs.
+ */
+OSStatus maccoreaudio_getAvailableNominalSampleRates(
+        const char * deviceUID,
+        Float64 * minRate,
+        Float64 * maxRate)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+    AudioValueRange minMaxRate;
+    minMaxRate.mMinimum = -1.0;
+    minMaxRate.mMaximum = -1.0;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getAvailableNominalSampleRates (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return -1.0;
+    }
+
+    // Gets the available sample ratea.
+    size = sizeof(AudioValueRange);
+    address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &minMaxRate))
+            != noErr)
+    {
+        fprintf(stderr,
+                "getAvailableNominalSampleRates (coreaudio/device.c): \
+                    \n\tAudioObjactGetPropertyData, err: %d\n",
+                    (int) err);
+        return -1.0;
+    }
+
+    (*minRate) = minMaxRate.mMinimum;
+    (*maxRate) = minMaxRate.mMaximum;
+
+    return noErr;
+}
+
+/**
+ * Lists the audio devices available and stores their UIDs in the provided
+ * parameter.
+ *
+ * @param deviceUIDList A pointer which will be filled in with a list of device
+ * UID strings. The caller is responsible to free this list and all the items.
+ *
+ * @return -1 in case of error. Otherwise, returns the number of devices stored
+ * in the deviceUIDList.
+ */
+int maccoreaudio_getDeviceUIDList(
+        char *** deviceUIDList)
+{
+    OSStatus err = noErr;
+    UInt32 propsize;
+    int nbDevices = -1;
+
+    AudioObjectPropertyAddress address =
+    {
+        kAudioHardwarePropertyDevices,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster
+    };
+    if((err = AudioObjectGetPropertyDataSize(
+                    kAudioObjectSystemObject,
+                    &address,
+                    0,
+                    NULL,
+                    &propsize))
+            != noErr)
+    {
+        fprintf(stderr,
+                "getDeviceUIDList (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyDataSize, err: %d\n",
+                    ((int) err));
+        return -1;
+    }
+
+    nbDevices = propsize / sizeof(AudioDeviceID);    
+    AudioDeviceID *devices = NULL;
+    if((devices = (AudioDeviceID*) malloc(nbDevices * sizeof(AudioDeviceID)))
+            == NULL)
+    {
+        perror("getDeviceUIDList (coreaudio/device.c): \
+                    \n\tmalloc\n");
+        return -1;
+    }
+
+    if((err = AudioObjectGetPropertyData(
+                    kAudioObjectSystemObject,
+                    &address,
+                    0,
+                    NULL,
+                    &propsize,
+                    devices))
+            != noErr)
+    {
+        free(devices);
+        fprintf(stderr,
+                "getDeviceUIDList (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                    ((int) err));
+        return -1;
+    }
+
+    if(((*deviceUIDList) = (char**) malloc(nbDevices * sizeof(char*)))
+            == NULL)
+    {
+        free(devices);
+        perror("getDeviceUIDList (coreaudio/device.c): \
+                    \n\tmalloc\n");
+        return -1;
+    }
+
+    int i;
+    for(i = 0; i < nbDevices; ++i)
+    {
+        if(((*deviceUIDList)[i] = maccoreaudio_getAudioDeviceProperty(
+                        devices[i],
+                        kAudioDevicePropertyDeviceUID))
+                == NULL)
+        {
+            int j;
+            for(j = 0; j < i; ++j)
+            {
+                free((*deviceUIDList)[j]);
+            }
+            free(*deviceUIDList);
+            free(devices);
+            fprintf(stderr,
+                    "getDeviceUIDList (coreaudio/device.c): \
+                    \n\tgetAudioDeviceProperty\n");
+            return -1;
+        }
+    }
+
+    free(devices);
+
+    return nbDevices;
+}
+ 
+/**
+ * Registers the listener for new plugged-in/out devices.
+ */
+void maccoreaudio_initializeHotplug(
+        void* callbackFunction)
+{
+    AudioObjectPropertyAddress address =
+    {
+        kAudioHardwarePropertyDevices,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster
+    };
+
+    AudioObjectAddPropertyListener(
+            kAudioObjectSystemObject,
+            &address,
+            maccoreaudio_devicesChangedCallback,
+            callbackFunction);
+}
+
+/**
+ * Unregisters the listener for new plugged-in/out devices.
+ */
+void maccoreaudio_uninitializeHotplug()
+{
+    AudioObjectPropertyAddress address =
+    {
+        kAudioHardwarePropertyDevices,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster
+    };
+
+    AudioObjectRemovePropertyListener(
+            kAudioObjectSystemObject,
+            &address,
+            maccoreaudio_devicesChangedCallback,
+            NULL);
+}
+
+/**
+ * The callback function called when a device is plugged-in/out.
+ *
+ * @param inObjectID The AudioObject whose properties have changed.
+ * @param inNumberAddresses The number of elements in the inAddresses array.
+ * @param inAddresses An array of AudioObjectPropertyAddresses indicating which
+ * properties changed.
+ * @param inClientData A pointer to client data established when the listener
+ * proc was registered with the AudioObject.
+ *
+ * @return The return value is currently unused and should always be 0.
+ */
+static OSStatus maccoreaudio_devicesChangedCallback(
+        AudioObjectID inObjectID,
+        UInt32 inNumberAddresses,
+        const AudioObjectPropertyAddress inAddresses[],
+        void *inClientData)
+{
+    void (*callbackFunction) (void) = inClientData;
+    callbackFunction();
+
+    return noErr;
+}
+
+/**
+ * Returns a string identifier of the device transport type.
+ *
+ * @param deviceUID The device UID to get the transport type from.
+ *
+ * @return The string identifier of the device transport type. Or NULL if
+ * failed.
+ */
+const char* maccoreaudio_getTransportType(
+        const char * deviceUID)
+{
+    AudioDeviceID device;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "maccoreaudio_getTransportType (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return NULL;
+    }
+    // target device transport type property
+    AudioObjectPropertyAddress address;
+    address.mSelector = kAudioDevicePropertyTransportType;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    OSStatus err;
+    unsigned int transportType = 0;
+    UInt32 size = sizeof(transportType);
+    if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &transportType))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_getTransportType (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData: err: %d\n",
+                    (int) err);
+        return NULL;
+    }
+
+    switch(transportType)
+    {
+        case kAudioDeviceTransportTypeAggregate:
+            return transportTypeAggregate;
+            break;
+        case kAudioDeviceTransportTypeAirPlay:
+            return transportTypeAirPlay;
+            break;
+        case kAudioDeviceTransportTypeAutoAggregate:
+            return transportTypeAutoAggregate;
+            break;
+        case kAudioDeviceTransportTypeAVB:
+            return transportTypeAVB;
+            break;
+        case kAudioDeviceTransportTypeBluetooth:
+            return transportTypeBlueTooth;
+            break;
+        case kAudioDeviceTransportTypeBuiltIn:
+            return transportTypeBuiltIn;
+            break;
+        case kAudioDeviceTransportTypeDisplayPort:
+            return transportTypeDisplayPort;
+            break;
+        case kAudioDeviceTransportTypeFireWire:
+            return transportTypeFireWire;
+            break;
+        case kAudioDeviceTransportTypeHDMI:
+            return transportTypeHDMI;
+            break;
+        case kAudioDeviceTransportTypePCI:
+            return transportTypePCI;
+            break;
+        case kAudioDeviceTransportTypeThunderbolt:
+            return transportTypeThunderbolt;
+            break;
+        case kAudioDeviceTransportTypeUnknown:
+            return transportTypeUnknown;
+            break;
+        case kAudioDeviceTransportTypeUSB:
+            return transportTypeUSB;
+            break;
+        case kAudioDeviceTransportTypeVirtual:
+            return transportTypeVirtual;
+            break;
+        default:
+            return NULL;
+            break;
+    }
+}
+
+maccoreaudio_stream * maccoreaudio_startInputStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved)
+{
+    return maccoreaudio_startStream(
+            deviceUID,
+            callbackFunction,
+            callbackObject,
+            callbackMethod,
+            maccoreaudio_readInputStream,
+            false,
+            sampleRate,
+            nbChannels,
+            bitsPerChannel,
+            isFloat,
+            isBigEndian,
+            isNonInterleaved);
+}
+
+maccoreaudio_stream * maccoreaudio_startOutputStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved)
+{
+    return maccoreaudio_startStream(
+            deviceUID,
+            callbackFunction,
+            callbackObject,
+            callbackMethod,
+            maccoreaudio_writeOutputStream,
+            true,
+            sampleRate,
+            nbChannels,
+            bitsPerChannel,
+            isFloat,
+            isBigEndian,
+            isNonInterleaved);
+}
+
+/**
+ * The the IO processing of a device.
+ *
+ * @param deviceUID The device UID to get the data from / to.
+ * @param callbackFunction A function called 
+ * @param readWriteFunction A function pointer called by the IO when data are
+ * available for read / write.
+ */
+maccoreaudio_stream * maccoreaudio_startStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        void* readWriteFunction,
+        unsigned char isJavaFormatSource,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved)
+{
+    AudioDeviceID device;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "maccoreaudio_startStream (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return NULL;
+    }
+
+    // Init the stream structure.
+    maccoreaudio_stream * stream;
+    if((stream = (maccoreaudio_stream*) malloc(sizeof(maccoreaudio_stream)))
+            == NULL)
+    {
+        perror("maccoreaudio_startStream (coreaudio/device.c): \
+                    \n\tmalloc\n");
+        return NULL;
+    }
+    stream->ioProcId = NULL;
+    stream->callbackFunction = callbackFunction;
+    stream->callbackObject = callbackObject;
+    stream->callbackMethod = callbackMethod;
+
+    AudioStreamBasicDescription javaFormat;
+    FillOutASBDForLPCM(
+            &javaFormat,
+            sampleRate,
+            nbChannels,
+            bitsPerChannel,
+            bitsPerChannel,
+            isFloat,
+            isBigEndian,
+            isNonInterleaved); 
+    if(maccoreaudio_initConverter(
+                deviceUID,
+                &javaFormat,
+                isJavaFormatSource,
+                &stream->converter,
+                &stream->conversionRatio)
+            != noErr)
+    {
+        free(stream);
+        fprintf(stderr,
+                "maccoreaudio_startStream (coreaudio/device.c): \
+                    \n\tmaccoreaudio_initConverter\n");
+        return NULL;
+    }
+
+    //  register the IOProc
+    if(AudioDeviceCreateIOProcID(
+            device,
+            readWriteFunction,
+            stream,
+            &stream->ioProcId) != noErr)
+    {
+        free(stream);
+        fprintf(stderr,
+                "maccoreaudio_startStream (coreaudio/device.c): \
+                    \n\tAudioDeviceIOProcID\n");
+        return NULL;
+    }
+
+    //  start IO
+    AudioDeviceStart(device, stream->ioProcId);
+
+    return stream;
+}
+
+void maccoreaudio_stopStream(
+        const char * deviceUID,
+        maccoreaudio_stream * stream)
+{
+    AudioDeviceID device;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "maccoreaudio_stopStream (coreaudio/device.c): \
+                    \n\tgetDevice: %s\n",
+                    deviceUID);
+        fflush(stderr);
+        return;
+    }
+
+    //  stop IO
+    AudioDeviceStop(device, stream->ioProcId);
+
+    //  unregister the IOProc
+    AudioDeviceDestroyIOProcID(device, stream->ioProcId);
+
+    AudioConverterDispose(stream->converter);
+
+    free(stream);
+}
+
+OSStatus maccoreaudio_readInputStream(
+        AudioDeviceID inDevice,
+        const AudioTimeStamp* inNow,
+        const AudioBufferList* inInputData,
+        const AudioTimeStamp* inInputTime,
+        AudioBufferList* outOutputData,
+        const AudioTimeStamp* inOutputTime,
+        void* inClientData)
+{
+    OSStatus err = noErr;
+    maccoreaudio_stream * stream = (maccoreaudio_stream*) inClientData;
+    void (*callbackFunction) (char*, int, void*, void*)
+        = stream->callbackFunction;
+    UInt32 tmpLength
+        = inInputData->mBuffers[0].mDataByteSize * stream->conversionRatio;
+    char tmpBuffer[tmpLength];
+    int i;
+    for(i = 0; i < inInputData->mNumberBuffers; ++i)
+    {
+        if(inInputData->mBuffers[i].mData != NULL
+                && inInputData->mBuffers[i].mDataByteSize > 0)
+        {
+            if((err = AudioConverterConvertBuffer(
+                            stream->converter,
+                            inInputData->mBuffers[i].mDataByteSize,
+                            inInputData->mBuffers[i].mData,
+                            &tmpLength,
+                            tmpBuffer))
+                    != noErr)
+            {
+                fprintf(stderr,
+                        "maccoreaudio_readInputStream (coreaudio/device.c): \
+                            \n\tAudioConverterConvertBuffer: %x\n",
+                            (int) err);
+                fflush(stderr);
+                return err;
+            }
+
+            callbackFunction(
+                    tmpBuffer,
+                    tmpLength,
+                    stream->callbackObject,
+                    stream->callbackMethod);
+        }
+    }
+
+    return noErr;
+}
+
+OSStatus maccoreaudio_writeOutputStream(
+        AudioDeviceID inDevice,
+        const AudioTimeStamp* inNow,
+        const AudioBufferList* inInputData,
+        const AudioTimeStamp* inInputTime,
+        AudioBufferList* outOutputData,
+        const AudioTimeStamp* inOutputTime,
+        void* inClientData)
+{
+    OSStatus err = noErr;
+
+    maccoreaudio_stream * stream = (maccoreaudio_stream*) inClientData;
+    void (*callbackFunction) (char*, int, void*, void*)
+        = stream->callbackFunction;
+
+    if(outOutputData->mNumberBuffers == 0)
+    {
+        return err;
+    }
+
+    int tmpLength
+        = outOutputData->mBuffers[0].mDataByteSize * stream->conversionRatio;
+    char tmpBuffer[tmpLength];
+
+    callbackFunction(
+            tmpBuffer,
+            tmpLength,
+            stream->callbackObject,
+            stream->callbackMethod);
+
+    if((err = AudioConverterConvertBuffer(
+                    stream->converter,
+                    tmpLength,
+                    tmpBuffer,
+                    &outOutputData->mBuffers[0].mDataByteSize,
+                    outOutputData->mBuffers[0].mData))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_writeOutputStream (coreaudio/device.c): \
+                    \n\tAudioConverterConvertBuffer\n");
+        fflush(stderr);
+        return err;
+    }
+
+    // Copies the same data into the other buffers.
+    int i;
+    UInt32 length;
+    for(i = 1; i < outOutputData->mNumberBuffers; ++i)
+    {
+        // Copies available data.
+        length = outOutputData->mBuffers[i].mDataByteSize;
+        if(length > outOutputData->mBuffers[0].mDataByteSize)
+        {
+            length = outOutputData->mBuffers[0].mDataByteSize;
+        }
+        memcpy(
+                outOutputData->mBuffers[i].mData,
+                outOutputData->mBuffers[0].mData,
+                length);
+
+        // Resets the resting buffer.
+        if(outOutputData->mBuffers[i].mDataByteSize
+                > outOutputData->mBuffers[0].mDataByteSize)
+        {
+            memset(
+                    outOutputData->mBuffers[i].mData
+                        + outOutputData->mBuffers[0].mDataByteSize,
+                    0,
+                    outOutputData->mBuffers[i].mDataByteSize
+                        - outOutputData->mBuffers[0].mDataByteSize);
+        }
+    }
+
+    return noErr;
+}
+
+/**
+ * Returns the stream virtual format for a given stream.
+ *
+ * @param stream The stream to get the format.
+ * @param format The variable to write the forat into.
+ *
+ * @return noErr if everything works fine. Any other value if failed.
+ */
+OSStatus maccoreaudio_getStreamVirtualFormat(
+        AudioStreamID stream,
+        AudioStreamBasicDescription * format)
+{
+    // Gets the audio format of the stream.
+    OSStatus err = noErr;
+    UInt32 size = sizeof(AudioStreamBasicDescription);
+    AudioObjectPropertyAddress address;
+    address.mSelector = kAudioStreamPropertyVirtualFormat;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+    if((err = AudioObjectGetPropertyData(
+                    stream,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    format))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_getStreamVirtualFormat (coreaudio/device.c): \
+                \n\tAudioObjectGetPropertyData, err: 0x%x\n",
+                ((int) err));
+        fflush(stderr);
+        return err;
+    }
+
+    return err;
+}
+
+/**
+ * Initializes a new audio converter to work between the given device and the
+ * format description.
+ *
+ * @param deviceUID The device identifier.
+ * @param javaFormat The format needed by the upper layer Java aplication.
+ * @param isJavaFormatSource True if the Java format is the source of this
+ * converter and the device the ouput. False otherwise.
+ * @param converter A pointer to the converter used to store the new created
+ * converter.
+ *
+ * @return noErr if everything works correctly. Any other vlue otherwise.
+ */
+OSStatus maccoreaudio_initConverter(
+        const char * deviceUID,
+        const AudioStreamBasicDescription * javaFormat,
+        unsigned char isJavaFormatSource,
+        AudioConverterRef * converter,
+        double * conversionRatio)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+
+    // Gets the correspoding device
+    if((device = maccoreaudio_getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "maccoreaudio_initConverter (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        fflush(stderr);
+        return kAudioObjectUnknown;
+    }
+
+    AudioStreamBasicDescription deviceFormat;
+    AudioStreamID audioStreamIds[1];
+    UInt32 size = sizeof(AudioStreamID *);
+    address.mSelector = kAudioDevicePropertyStreams;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+    if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &audioStreamIds))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_countChannels (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: 0x%x\n",
+                    ((int) err));
+        fflush(stderr);
+        return err;
+    }
+
+    if((err = maccoreaudio_getStreamVirtualFormat(
+                    audioStreamIds[0],
+                    &deviceFormat))
+                != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_countChannels (coreaudio/device.c): \
+                    \n\tmaccoreaudiogetStreamVirtualFormat, err: 0x%x\n",
+                    ((int) err));
+        fflush(stderr);
+        return err;
+    }
+    
+    const AudioStreamBasicDescription *inFormat = javaFormat;
+    const AudioStreamBasicDescription *outFormat = &deviceFormat;
+    if(!isJavaFormatSource)
+    {
+        inFormat = &deviceFormat;
+        outFormat = javaFormat;
+    }
+
+    if((err = AudioConverterNew(inFormat, outFormat, converter))
+            != noErr)
+    {
+        fprintf(stderr,
+                "maccoreaudio_countChannels (coreaudio/device.c): \
+                    \n\tAudioConverterNew, err: 0x%x\n",
+                    ((int) err));
+        fflush(stderr);
+        return err;
+    }
+
+    *conversionRatio =
+        ((double) javaFormat->mBytesPerFrame)
+            / ((double) deviceFormat.mBytesPerFrame)
+        * javaFormat->mSampleRate / deviceFormat.mSampleRate;
+
+    return err;
+}
+
+/**
+ * Computes the value for the audio stream basic description mFormatFlags
+ * field for linear PCM data. This function does not support specifying sample
+ * formats that are either unsigned integer or low-aligned.
+ *
+ * @param inValidBitsPerChannel The number of valid bits in each sample.
+ * @param inTotalBitsPerChannel The total number of bits in each sample.
+ * @param inIsFloat Use true if the samples are represented with floating point
+             numbers.
+ * @param inIsBigEndian Use true if the samples are big endian.
+ * @param inIsNonInterleaved Use true if the samples are noninterleaved.
+ *
+ * @return A UInt32 value containing the calculated format flags.
+ */
+inline UInt32 CalculateLPCMFlags (
+        UInt32 inValidBitsPerChannel,
+        UInt32 inTotalBitsPerChannel,
+        bool inIsFloat,
+        bool inIsBigEndian,
+        bool inIsNonInterleaved)
+{
+    return
+        (inIsFloat ? kAudioFormatFlagIsFloat : kAudioFormatFlagIsSignedInteger)
+        | (inIsBigEndian ? ((UInt32)kAudioFormatFlagIsBigEndian) : 0)
+        | ((!inIsFloat && (inValidBitsPerChannel == inTotalBitsPerChannel)) ?
+                kAudioFormatFlagIsPacked : kAudioFormatFlagIsAlignedHigh)
+        | (inIsNonInterleaved ? ((UInt32)kAudioFormatFlagIsNonInterleaved) : 0);
+}
+
+/**
+ * Fills AudioStreamBasicDescription information.
+ *
+ * @param outASBD On output, a filled-out AudioStreamBasicDescription structure.
+ * @param inSampleRate The number of sample frames per second of the data in the
+ * stream.
+ * @param inChannelsPerFrame The number of channels in each frame of data.
+ * @param inValidBitsPerChannel The number of valid bits in each sample.
+ * @param inTotalBitsPerChannel The total number of bits in each sample.
+ * @param inIsFloat Use true if the samples are represented as floating-point
+ * numbers.
+ * @param inIsBigEndian Use true if the samples are big endian.
+ * @param inIsNonInterleaved Use true if the samples are noninterleaved.
+ */
+inline void FillOutASBDForLPCM(
+        AudioStreamBasicDescription * outASBD,
+        Float64 inSampleRate,
+        UInt32 inChannelsPerFrame,
+        UInt32 inValidBitsPerChannel,
+        UInt32 inTotalBitsPerChannel,
+        bool inIsFloat,
+        bool inIsBigEndian,
+        bool inIsNonInterleaved)
+{
+    outASBD->mSampleRate = inSampleRate;
+    outASBD->mFormatID = kAudioFormatLinearPCM;
+    outASBD->mFormatFlags = CalculateLPCMFlags(
+            inValidBitsPerChannel,
+            inTotalBitsPerChannel,
+            inIsFloat,
+            inIsBigEndian,
+            inIsNonInterleaved);
+    outASBD->mBytesPerPacket =
+        (inIsNonInterleaved ? 1 : inChannelsPerFrame) *
+        (inTotalBitsPerChannel/8);
+    outASBD->mFramesPerPacket = 1;
+    outASBD->mBytesPerFrame =
+        (inIsNonInterleaved ? 1 : inChannelsPerFrame) *
+        (inTotalBitsPerChannel/8);
+    outASBD->mChannelsPerFrame = inChannelsPerFrame;
+    outASBD->mBitsPerChannel = inValidBitsPerChannel;
+}
diff --git a/src/native/macosx/coreaudio/lib/device.h b/src/native/macosx/coreaudio/lib/device.h
index 3a78e8441442973c21c6da3a28c79dd4b98b55ae..b13fc50f477d948ce212b975ba615cf8d801060e 100644
--- a/src/native/macosx/coreaudio/lib/device.h
+++ b/src/native/macosx/coreaudio/lib/device.h
@@ -7,6 +7,7 @@
 #ifndef device_h
 #define device_h
 
+#include <AudioToolbox/AudioConverter.h>
 #include <CoreAudio/CoreAudio.h>
 #include <CoreFoundation/CFString.h>
 #include <stdio.h>
@@ -17,30 +18,105 @@
  *
  * @author Vincent Lucas
  */
-int initDevices(void);
+typedef struct
+{
+    AudioDeviceIOProcID ioProcId;
+    void* callbackFunction;
+    void* callbackObject;
+    void* callbackMethod;
+    AudioConverterRef converter;
+    double conversionRatio;
+} maccoreaudio_stream;
 
-void freeDevices(void);
+int maccoreaudio_initDevices(
+        void);
 
-AudioDeviceID getDevice(
+void maccoreaudio_freeDevices(
+        void);
+
+int maccoreaudio_isInputDevice(
+        const char * deviceUID);
+
+int maccoreaudio_isOutputDevice(
         const char * deviceUID);
 
-char* getDeviceName(
+char* maccoreaudio_getDeviceName(
         const char * deviceUID);
 
-char* getDeviceModelIdentifier(
+char* maccoreaudio_getDeviceModelIdentifier(
         const char * deviceUID);
 
-OSStatus setInputDeviceVolume(
+OSStatus maccoreaudio_setInputDeviceVolume(
         const char * deviceUID,
         Float32 volume);
 
-OSStatus setOutputDeviceVolume(
+OSStatus maccoreaudio_setOutputDeviceVolume(
         const char * deviceUID,
         Float32 volume);
 
-Float32 getInputDeviceVolume(
+Float32 maccoreaudio_getInputDeviceVolume(
         const char * deviceUID);
 
-Float32 getOutputDeviceVolume(
+Float32 maccoreaudio_getOutputDeviceVolume(
         const char * deviceUID);
+
+int maccoreaudio_getDeviceUIDList(
+        char *** deviceUIDList);
+
+const char* maccoreaudio_getTransportType(
+        const char * deviceUID);
+
+Float64 maccoreaudio_getNominalSampleRate(
+        const char * deviceUID);
+
+OSStatus maccoreaudio_getAvailableNominalSampleRates(
+        const char * deviceUID,
+        Float64 * minRate,
+        Float64 * maxRate);
+
+char* maccoreaudio_getDefaultInputDeviceUID(
+        void);
+
+char* maccoreaudio_getDefaultOutputDeviceUID(
+        void);
+
+int maccoreaudio_countInputChannels(
+        const char * deviceUID);
+
+int maccoreaudio_countOutputChannels(
+        const char * deviceUID);
+
+maccoreaudio_stream * maccoreaudio_startInputStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved);
+
+maccoreaudio_stream * maccoreaudio_startOutputStream(
+        const char * deviceUID,
+        void* callbackFunction,
+        void* callbackObject,
+        void* callbackMethod,
+        float sampleRate,
+        UInt32 nbChannels,
+        UInt32 bitsPerChannel,
+        unsigned char isFloat,
+        unsigned char isBigEndian,
+        unsigned char isNonInterleaved);
+
+void maccoreaudio_stopStream(
+        const char * deviceUID,
+        maccoreaudio_stream * stream);
+
+void maccoreaudio_initializeHotplug(
+        void* callbackFunction);
+
+void maccoreaudio_uninitializeHotplug();
+
 #endif
diff --git a/src/org/jitsi/impl/neomedia/CoreAudioDevice.java b/src/org/jitsi/impl/neomedia/CoreAudioDevice.java
index 07230e01b88b55f8d458c1e68fa202586834fbd1..ae2d56b4739602ff4a38a1dffbaf4d27c7c0863f 100644
--- a/src/org/jitsi/impl/neomedia/CoreAudioDevice.java
+++ b/src/org/jitsi/impl/neomedia/CoreAudioDevice.java
@@ -108,4 +108,28 @@ public static native int setInputDeviceVolume(
     public static native int setOutputDeviceVolume(
             String deviceUID,
             float volume);
+
+    private static Runnable devicesChangedCallback;
+
+    /**
+     * Implements a callback which gets called by the native coreaudio
+     * counterpart to notify the Java counterpart that the list of devices has
+     * changed.
+     */
+    public static void devicesChangedCallback()
+    {
+        Runnable devicesChangedCallback
+            = CoreAudioDevice.devicesChangedCallback;
+
+        if(devicesChangedCallback != null)
+        {
+            devicesChangedCallback.run();
+        }
+    }
+
+    public static void setDevicesChangedCallback(
+            Runnable devicesChangedCallback)
+    {
+        CoreAudioDevice.devicesChangedCallback = devicesChangedCallback;
+    }
 }
diff --git a/src/org/jitsi/impl/neomedia/MacCoreAudioDevice.java b/src/org/jitsi/impl/neomedia/MacCoreAudioDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..530ce28fc4b342c0e0a9f9fa49cdb5750d932153
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/MacCoreAudioDevice.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;
+
+import org.jitsi.util.*;
+
+/**
+ * Extension for the JNI link to the MacOsX CoreAudio library.
+ *
+ * @author Vincent Lucas
+ */
+public class MacCoreAudioDevice
+    extends CoreAudioDevice
+{
+    /**
+     * The number of milliseconds to be read from or written to a native
+     * CoreAudio stream in a single transfer of data.
+     */
+    public static final int DEFAULT_MILLIS_PER_BUFFER = 20;
+
+    /**
+     * The default value for the sample rate of the input and the output
+     * MacCoreaudio streams with which they are to be opened if no other
+     * specific sample rate is specified to the MacCoreaudio <tt>DataSource</tt>
+     * or <tt>MacCoreaudioRenderer</tt> that they represent.
+     */
+    public static final double DEFAULT_SAMPLE_RATE = 44100.0;
+
+    public static native String[] getDeviceUIDList();
+
+    public static native boolean isInputDevice(String deviceUID);
+
+    public static native boolean isOutputDevice(String deviceUID);
+
+    public static String getTransportType(String deviceUID)
+    {
+        // Prevent an access violation.
+        if (deviceUID == null)
+            throw new NullPointerException("deviceUID");
+
+        byte[] transportTypeBytes = getTransportTypeBytes(deviceUID);
+        String transportType = StringUtils.newString(transportTypeBytes);
+
+        return transportType;
+    }
+
+    public static native byte[] getTransportTypeBytes(String deviceUID);
+
+    public static native float getNominalSampleRate(String deviceUID);
+
+    public static native float getMinimalNominalSampleRate(String deviceUID);
+
+    public static native float getMaximalNominalSampleRate(String deviceUID);
+
+    public static String getDefaultInputDeviceUID()
+    {
+        byte[] defaultInputDeviceUIDBytes = getDefaultInputDeviceUIDBytes();
+        String defaultInputDeviceUID
+            = StringUtils.newString(defaultInputDeviceUIDBytes);
+
+        return defaultInputDeviceUID;
+    }
+
+    public static native byte[] getDefaultInputDeviceUIDBytes();
+
+    public static String getDefaultOutputDeviceUID()
+    {
+        byte[] defaultOutputDeviceUIDBytes = getDefaultOutputDeviceUIDBytes();
+        String defaultOutputDeviceUID
+            = StringUtils.newString(defaultOutputDeviceUIDBytes);
+
+        return defaultOutputDeviceUID;
+    }
+
+    public static native byte[] getDefaultOutputDeviceUIDBytes();
+
+    public static native long startStream(
+            String deviceUID,
+            Object callback,
+            float sampleRate,
+            int nbChannels,
+            int bitsPerChannel,
+            boolean isFloat,
+            boolean isBigEndian,
+            boolean isNonInterleaved);
+
+    public static native void stopStream(String deviceUID, long stream);
+
+    public static native int countInputChannels(String deviceUID);
+
+    public static native int countOutputChannels(String deviceUID);
+}
diff --git a/src/org/jitsi/impl/neomedia/device/AudioSystem.java b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
index bc3a9d784a89ce1f0260a04e45d0c3691c4077ae..962a1cd2a5967077cc55ec376acbe2d83eb9aff8 100644
--- a/src/org/jitsi/impl/neomedia/device/AudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
@@ -78,6 +78,12 @@ public enum DataFlow
 
     public static final String LOCATOR_PROTOCOL_JAVASOUND = "javasound";
 
+    /**
+     * The protocol of the <tt>MediaLocator</tt>s identifying
+     * <tt>CaptureDeviceInfo</tt>s contributed by <tt>MacCoreaudioSystem</tt>.
+     */
+    public static final String LOCATOR_PROTOCOL_MACCOREAUDIO = "maccoreaudio";
+
     public static final String LOCATOR_PROTOCOL_OPENSLES = "opensles";
 
     public static final String LOCATOR_PROTOCOL_PORTAUDIO = "portaudio";
diff --git a/src/org/jitsi/impl/neomedia/device/DeviceSystem.java b/src/org/jitsi/impl/neomedia/device/DeviceSystem.java
index 32751a3702c9a0f09c90867739a9e19434b50485..f228d3970c5a38d321da2a1cbc9a7132d884462b 100644
--- a/src/org/jitsi/impl/neomedia/device/DeviceSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/DeviceSystem.java
@@ -205,6 +205,7 @@ public static void initializeDeviceSystems(MediaType mediaType)
                         : null,
                     OSUtils.IS_WINDOWS ? ".WASAPISystem" : null,
                     OSUtils.IS_ANDROID ? null : ".PortAudioSystem",
+                    OSUtils.IS_MAC ? ".MacCoreaudioSystem" : null,
                     ".NoneAudioSystem"
                 };
             break;
diff --git a/src/org/jitsi/impl/neomedia/device/MacCoreaudioSystem.java b/src/org/jitsi/impl/neomedia/device/MacCoreaudioSystem.java
new file mode 100644
index 0000000000000000000000000000000000000000..484c5ae6f5bf56a2bab51c4f41315c7c491ff91e
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/device/MacCoreaudioSystem.java
@@ -0,0 +1,785 @@
+/*
+ * 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.lang.ref.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.media.*;
+import javax.media.format.*;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.control.*;
+import org.jitsi.impl.neomedia.jmfext.media.renderer.audio.*;
+import org.jitsi.util.*;
+
+/**
+ * Creates MacCoreaudio capture devices by enumerating all host devices that
+ * have input channels.
+ *
+ * @author Vincent Lucas
+ */
+public class MacCoreaudioSystem
+    extends AudioSystem
+{
+    /**
+     * Represents a listener which is to be notified before and after
+     * MacCoreaudio's native function <tt>UpdateAvailableDeviceList()</tt> is
+     * invoked.
+     */
+    public interface UpdateAvailableDeviceListListener
+        extends EventListener
+    {
+        /**
+         * Notifies this listener that MacCoreaudio's native function
+         * <tt>UpdateAvailableDeviceList()</tt> was invoked.
+         *
+         * @throws Exception if this implementation encounters an error. Any
+         * <tt>Throwable</tt> apart from <tt>ThreadDeath</tt> will be ignored
+         * after it is logged for debugging purposes.
+         */
+        void didUpdateAvailableDeviceList()
+            throws Exception;
+
+        /**
+         * Notifies this listener that MacCoreaudio's native function
+         * <tt>UpdateAvailableDeviceList()</tt> will be invoked.
+         *
+         * @throws Exception if this implementation encounters an error. Any
+         * <tt>Throwable</tt> apart from <tt>ThreadDeath</tt> will be ignored
+         * after it is logged for debugging purposes.
+         */
+        void willUpdateAvailableDeviceList()
+            throws Exception;
+    }
+
+    /**
+     * The protocol of the <tt>MediaLocator</tt>s identifying MacCoreaudio
+     * <tt>CaptureDevice</tt>s
+     */
+    private static final String LOCATOR_PROTOCOL
+        = LOCATOR_PROTOCOL_MACCOREAUDIO;
+
+    /**
+     * The <tt>Logger</tt> used by the <tt>MacCoreaudioSystem</tt> class and its
+     * instances for logging output.
+     */
+    private static final Logger logger
+        = Logger.getLogger(MacCoreaudioSystem.class);
+
+    /**
+     * The number of times that {@link #willPaOpenStream()} has been
+     * invoked without an intervening {@link #didPaOpenStream()} i.e. the
+     * number of MacCoreaudio clients which are currently executing
+     * <tt>Pa_OpenStream</tt> and which are thus inhibiting
+     * <tt>Pa_UpdateAvailableDeviceList</tt>.
+     */
+    private static int openStream = 0;
+
+    /**
+     * The <tt>Object</tt> which synchronizes that access to
+     * {@link #paOpenStream} and {@link #updateAvailableDeviceList}.
+     */
+    private static final Object openStreamSyncRoot = new Object();
+
+    /**
+     * The number of times that {@link #willPaUpdateAvailableDeviceList()}
+     * has been invoked without an intervening
+     * {@link #didPaUpdateAvailableDeviceList()} i.e. the number of
+     * MacCoreaudio clients which are currently executing
+     * <tt>Pa_UpdateAvailableDeviceList</tt> and which are thus inhibiting
+     * <tt>Pa_OpenStream</tt>.
+     */
+    private static int updateAvailableDeviceList = 0;
+
+    /**
+     * The list of <tt>PaUpdateAvailableDeviceListListener</tt>s which are to be
+     * notified before and after MacCoreaudio's native function
+     * <tt>Pa_UpdateAvailableDeviceList()</tt> is invoked.
+     */
+    private static final List<WeakReference<UpdateAvailableDeviceListListener>>
+        updateAvailableDeviceListListeners
+        = new LinkedList<WeakReference<UpdateAvailableDeviceListListener>>();
+
+    /**
+     * The <tt>Object</tt> which ensures that MacCoreaudio's native function
+     * <tt>UpdateAvailableDeviceList()</tt> will not be invoked concurrently.
+     * The condition should hold true on the native side but, anyway, it shoul
+     * not hurt (much) to enforce it on the Java side as well.
+     */
+    private static final Object updateAvailableDeviceListSyncRoot
+        = new Object();
+
+    /**
+     * Adds a listener which is to be notified before and after MacCoreaudio's
+     * native function <tt>UpdateAvailableDeviceList()</tt> is invoked.
+     * <p>
+     * <b>Note</b>: The <tt>MacCoreaudioSystem</tt> class keeps a
+     * <tt>WeakReference</tt> to the specified <tt>listener</tt> in order to
+     * avoid memory leaks.
+     * </p>
+     *
+     * @param listener the <tt>UpdateAvailableDeviceListListener</tt> to be
+     * notified before and after MacCoreaudio's native function
+     * <tt>UpdateAvailableDeviceList()</tt> is invoked
+     */
+    public static void addUpdateAvailableDeviceListListener(
+            UpdateAvailableDeviceListListener listener)
+    {
+        if(listener == null)
+            throw new NullPointerException("listener");
+
+        synchronized(updateAvailableDeviceListListeners)
+        {
+            Iterator<WeakReference<UpdateAvailableDeviceListListener>> i
+                = updateAvailableDeviceListListeners.iterator();
+            boolean add = true;
+
+            while(i.hasNext())
+            {
+                UpdateAvailableDeviceListListener l = i.next().get();
+
+                if(l == null)
+                    i.remove();
+                else if(l.equals(listener))
+                    add = false;
+            }
+            if(add)
+            {
+                updateAvailableDeviceListListeners.add(
+                        new WeakReference<UpdateAvailableDeviceListListener>(
+                                listener));
+            }
+        }
+    }
+
+    /**
+     * Notifies <tt>MacCoreaudioSystem</tt> that a MacCoreaudio client finished
+     * executing <tt>OpenStream</tt>.
+     */
+    public static void didOpenStream()
+    {
+        synchronized (openStreamSyncRoot)
+        {
+            openStream--;
+            if (openStream < 0)
+                openStream = 0;
+
+            openStreamSyncRoot.notifyAll();
+        }
+    }
+
+    /**
+     * Notifies <tt>MacCoreaudioSystem</tt> that a MacCoreaudio client finished
+     * executing <tt>UpdateAvailableDeviceList</tt>.
+     */
+    private static void didUpdateAvailableDeviceList()
+    {
+        synchronized(openStreamSyncRoot)
+        {
+            updateAvailableDeviceList--;
+            if (updateAvailableDeviceList < 0)
+                updateAvailableDeviceList = 0;
+
+            openStreamSyncRoot.notifyAll();
+        }
+
+        fireUpdateAvailableDeviceListEvent(false);
+    }
+
+    /**
+     * Notifies the registered <tt>UpdateAvailableDeviceListListener</tt>s
+     * that MacCoreaudio's native function
+     * <tt>UpdateAvailableDeviceList()</tt> will be or was invoked.
+     *
+     * @param will <tt>true</tt> if MacCoreaudio's native function
+     * <tt>UpdateAvailableDeviceList()</tt> will be invoked or <tt>false</tt>
+     * if it was invoked
+     */
+    private static void fireUpdateAvailableDeviceListEvent(boolean will)
+    {
+        try
+        {
+            List<WeakReference<UpdateAvailableDeviceListListener>> ls;
+
+            synchronized(updateAvailableDeviceListListeners)
+            {
+                ls = new
+                    ArrayList<WeakReference<UpdateAvailableDeviceListListener>>(
+                            updateAvailableDeviceListListeners);
+            }
+
+            for(WeakReference<UpdateAvailableDeviceListListener> wr : ls)
+            {
+                UpdateAvailableDeviceListListener l = wr.get();
+                if(l != null)
+                {
+                    try
+                    {
+                        if(will)
+                            l.willUpdateAvailableDeviceList();
+                        else
+                            l.didUpdateAvailableDeviceList();
+                    }
+                    catch (Throwable t)
+                    {
+                        if(t instanceof ThreadDeath)
+                            throw(ThreadDeath) t;
+                        else
+                        {
+                            logger.error(
+                                    "UpdateAvailableDeviceListListener."
+                                    + (will ? "will" : "did")
+                                    + "UpdateAvailableDeviceList failed.",
+                                    t);
+                        }
+                    }
+                }
+            }
+        }
+        catch(Throwable t)
+        {
+            if(t instanceof ThreadDeath)
+                throw(ThreadDeath) t;
+        }
+    }
+
+    /**
+     * Gets a sample rate supported by a MacCoreaudio device with a specific
+     * device index with which it is to be registered with JMF.
+     *
+     * @param input <tt>true</tt> if the supported sample rate is to be
+     * retrieved for the MacCoreaudio device with the specified device index as
+     * an input device or <tt>false</tt> for an output device
+     * @param deviceUID The device identifier.
+     * @param channelCount number of channel
+     * @param sampleFormat sample format
+     *
+     * @return a sample rate supported by the MacCoreaudio device with the
+     * specified device index with which it is to be registered with JMF
+     */
+    private static double getSupportedSampleRate(
+            boolean input,
+            String deviceUID)
+    {
+        double supportedSampleRate = MacCoreAudioDevice.DEFAULT_SAMPLE_RATE;
+        double defaultSampleRate
+            = MacCoreAudioDevice.getNominalSampleRate(deviceUID);
+
+        if (defaultSampleRate >= MediaUtils.MAX_AUDIO_SAMPLE_RATE)
+        {
+            supportedSampleRate = defaultSampleRate;
+        }
+
+        return supportedSampleRate;
+    }
+
+    /**
+     * Waits for all MacCoreaudio clients to finish executing
+     * <tt>OpenStream</tt>.
+     */
+    private static void waitForOpenStream()
+    {
+        boolean interrupted = false;
+
+        while(openStream > 0)
+        {
+            try
+            {
+                openStreamSyncRoot.wait();
+            }
+            catch(InterruptedException ie)
+            {
+                interrupted = true;
+            }
+        }
+        if(interrupted)
+            Thread.currentThread().interrupt();
+    }
+
+    /**
+     * Waits for all MacCoreaudio clients to finish executing
+     * <tt>UpdateAvailableDeviceList</tt>.
+     */
+    private static void waitForUpdateAvailableDeviceList()
+    {
+        boolean interrupted = false;
+
+        while (updateAvailableDeviceList > 0)
+        {
+            try
+            {
+                openStreamSyncRoot.wait();
+            }
+            catch (InterruptedException ie)
+            {
+                interrupted = true;
+            }
+        }
+        if (interrupted)
+            Thread.currentThread().interrupt();
+    }
+
+    /**
+     * Notifies <tt>MacCoreaudioSystem</tt> that a MacCoreaudio client will
+     * start executing <tt>OpenStream</tt>.
+     */
+    public static void willOpenStream()
+    {
+        synchronized (openStreamSyncRoot)
+        {
+            waitForUpdateAvailableDeviceList();
+
+            openStream++;
+            openStreamSyncRoot.notifyAll();
+        }
+    }
+
+    /**
+     * Notifies <tt>MacCoreaudioSystem</tt> that a MacCoreaudio client will
+     * start executing <tt>UpdateAvailableDeviceList</tt>.
+     */
+    private static void willUpdateAvailableDeviceList()
+    {
+        synchronized(openStreamSyncRoot)
+        {
+            waitForOpenStream();
+
+            updateAvailableDeviceList++;
+            openStreamSyncRoot.notifyAll();
+        }
+
+        fireUpdateAvailableDeviceListEvent(true);
+    }
+
+    private Runnable devicesChangedCallback;
+
+    /**
+     * Initializes a new <tt>MacCoreaudioSystem</tt> instance which creates
+     * MacCoreaudio capture and playback devices by enumerating all host devices
+     * with input channels.
+     *
+     * @throws Exception if anything wrong happens while creating the
+     * MacCoreaudio capture and playback devices
+     */
+    MacCoreaudioSystem()
+        throws Exception
+    {
+        super(
+                LOCATOR_PROTOCOL,
+                0 | FEATURE_NOTIFY_AND_PLAYBACK_DEVICES | FEATURE_REINITIALIZE);
+    }
+
+    /**
+     * Sorts a specific list of <tt>CaptureDeviceInfo2</tt>s so that the
+     * ones representing USB devices appear at the beginning/top of the
+     * specified list.
+     *
+     * @param devices the list of <tt>CaptureDeviceInfo2</tt>s to be
+     * sorted so that the ones representing USB devices appear at the
+     * beginning/top of the list
+     */
+    private void bubbleUpUsbDevices(List<CaptureDeviceInfo2> devices)
+    {
+        if(!devices.isEmpty())
+        {
+            List<CaptureDeviceInfo2> nonUsbDevices
+                = new ArrayList<CaptureDeviceInfo2>(devices.size());
+
+            for(Iterator<CaptureDeviceInfo2> i = devices.iterator();
+                    i.hasNext();)
+            {
+                CaptureDeviceInfo2 d = i.next();
+
+                if(!d.isSameTransportType("USB"))
+                {
+                    nonUsbDevices.add(d);
+                    i.remove();
+                }
+            }
+            if(!nonUsbDevices.isEmpty())
+            {
+                for (CaptureDeviceInfo2 d : nonUsbDevices)
+                    devices.add(d);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doInitialize()
+        throws Exception
+    {
+        if(!CoreAudioDevice.isLoaded)
+        {
+            String message = "MacOSX CoreAudio library is not loaded";
+            if (logger.isInfoEnabled())
+            {
+                logger.info(message);
+            }
+            throw new Exception(message);
+        }
+
+        // Initializes the library only at the first run.
+        if(devicesChangedCallback == null)
+        {
+            CoreAudioDevice.initDevices();
+        }
+
+        int channels = 1;
+        int sampleSizeInBits = 16;
+        String defaultInputdeviceUID
+            = MacCoreAudioDevice.getDefaultInputDeviceUID();
+        String defaultOutputdeviceUID
+            = MacCoreAudioDevice.getDefaultOutputDeviceUID();
+        List<CaptureDeviceInfo2> captureAndPlaybackDevices
+            = new LinkedList<CaptureDeviceInfo2>();
+        List<CaptureDeviceInfo2> captureDevices
+            = new LinkedList<CaptureDeviceInfo2>();
+        List<CaptureDeviceInfo2> playbackDevices
+            = new LinkedList<CaptureDeviceInfo2>();
+        final boolean loggerIsDebugEnabled = logger.isDebugEnabled();
+
+
+        String[] deviceUIDList = MacCoreAudioDevice.getDeviceUIDList();
+        for(int i = 0; i < deviceUIDList.length; ++i)
+        {
+            String deviceUID = deviceUIDList[i];
+            String name = CoreAudioDevice.getDeviceName(deviceUID);
+            boolean isInputDevice = MacCoreAudioDevice.isInputDevice(deviceUID);
+            boolean isOutputDevice
+                = MacCoreAudioDevice.isOutputDevice(deviceUID);
+            String transportType
+                = MacCoreAudioDevice.getTransportType(deviceUID);
+            String modelIdentifier = null;
+            String locatorRemainder = name;
+
+            if (deviceUID != null)
+            {
+                modelIdentifier
+                    = CoreAudioDevice.getDeviceModelIdentifier(deviceUID);
+                locatorRemainder = deviceUID;
+            }
+
+            /*
+             * TODO The intention of reinitialize() was to perform the
+             * initialization from scratch. However, AudioSystem was later
+             * changed to disobey. But we should at least search through both
+             * CAPTURE_INDEX and PLAYBACK_INDEX.
+             */
+            List<CaptureDeviceInfo2> existingCdis
+                = getDevices(DataFlow.CAPTURE);
+            CaptureDeviceInfo2 cdi = null;
+
+            if (existingCdis != null)
+            {
+                for (CaptureDeviceInfo2 existingCdi : existingCdis)
+                {
+                    /*
+                     * The deviceUID is optional so a device may be identified
+                     * by deviceUID if it is available or by name if the
+                     * deviceUID is not available.
+                     */
+                    String id = existingCdi.getIdentifier();
+
+                    if (id.equals(deviceUID) || id.equals(name))
+                    {
+                        cdi = existingCdi;
+                        break;
+                    }
+                }
+            }
+
+            if (cdi == null)
+            {
+                cdi
+                    = new CaptureDeviceInfo2(
+                            name,
+                            new MediaLocator(
+                                LOCATOR_PROTOCOL + ":#" + locatorRemainder),
+                            new Format[]
+                            {
+                                new AudioFormat(
+                                    AudioFormat.LINEAR,
+                                    isInputDevice
+                                    ? getSupportedSampleRate(
+                                        true,
+                                        deviceUID)
+                                    : MacCoreAudioDevice.DEFAULT_SAMPLE_RATE,
+                                    sampleSizeInBits,
+                                    channels,
+                                    AudioFormat.LITTLE_ENDIAN,
+                                    AudioFormat.SIGNED,
+                                    Format.NOT_SPECIFIED /* frameSizeInBits */,
+                                    Format.NOT_SPECIFIED /* frameRate */,
+                                    Format.byteArray)
+                            },
+                    deviceUID,
+                    transportType,
+                    modelIdentifier);
+            }
+
+            boolean isDefaultInputDevice
+                = deviceUID.equals(defaultInputdeviceUID);
+            boolean isDefaultOutputDevice
+                = deviceUID.equals(defaultOutputdeviceUID);
+
+            /*
+             * When we perform automatic selection of capture and
+             * playback/notify devices, we would like to pick up devices from
+             * one and the same hardware because that sound like a natural
+             * expectation from the point of view of the user. In order to
+             * achieve that, we will bring the devices which support both
+             * capture and playback to the top.
+             */
+            if(isInputDevice)
+            {
+                List<CaptureDeviceInfo2> devices;
+
+                if(isOutputDevice)
+                    devices = captureAndPlaybackDevices;
+                else
+                    devices = captureDevices;
+
+                if(isDefaultInputDevice
+                        || (isOutputDevice && isDefaultOutputDevice))
+                {
+                    devices.add(0, cdi);
+                    if (loggerIsDebugEnabled)
+                        logger.debug("Added default capture device: " + name);
+                }
+                else
+                {
+                    devices.add(cdi);
+                    if (loggerIsDebugEnabled)
+                        logger.debug("Added capture device: " + name);
+                }
+
+                if(loggerIsDebugEnabled && isInputDevice)
+                {
+                    if(isDefaultOutputDevice)
+                        logger.debug("Added default playback device: " + name);
+                    else
+                        logger.debug("Added playback device: " + name);
+                }
+            }
+            else if(isOutputDevice)
+            {
+                if(isDefaultOutputDevice)
+                {
+                    playbackDevices.add(0, cdi);
+                    if (loggerIsDebugEnabled)
+                        logger.debug("Added default playback device: " + name);
+                }
+                else
+                {
+                    playbackDevices.add(cdi);
+                    if (loggerIsDebugEnabled)
+                        logger.debug("Added playback device: " + name);
+                }
+            }
+        }
+
+        /*
+         * Make sure that devices which support both capture and playback are
+         * reported as such and are preferred over devices which support either
+         * capture or playback (in order to achieve our goal to have automatic
+         * selection pick up devices from one and the same hardware).
+         */
+        bubbleUpUsbDevices(captureDevices);
+        bubbleUpUsbDevices(playbackDevices);
+        if(!captureDevices.isEmpty() && !playbackDevices.isEmpty())
+        {
+            /*
+             * Event if we have not been provided with the information regarding
+             * the matching of the capture and playback/notify devices from one
+             * and the same hardware, we may still be able to deduce it by
+             * examining their names.
+             */
+            matchDevicesByName(captureDevices, playbackDevices);
+        }
+        /*
+         * Of course, of highest reliability is the fact that a specific
+         * instance supports both capture and playback.
+         */
+        if(!captureAndPlaybackDevices.isEmpty())
+        {
+            bubbleUpUsbDevices(captureAndPlaybackDevices);
+            for (int i = captureAndPlaybackDevices.size() - 1; i >= 0; i--)
+            {
+                CaptureDeviceInfo2 cdi = captureAndPlaybackDevices.get(i);
+
+                captureDevices.add(0, cdi);
+                playbackDevices.add(0, cdi);
+            }
+        }
+
+        setCaptureDevices(captureDevices);
+        setPlaybackDevices(playbackDevices);
+
+        if(devicesChangedCallback == null)
+        {
+            devicesChangedCallback
+                = new Runnable()
+                {
+                    public void run()
+                    {
+                        try
+                        {
+                            reinitialize();
+                        }
+                        catch (Throwable t)
+                        {
+                            if (t instanceof ThreadDeath)
+                                throw (ThreadDeath) t;
+
+                            logger.warn(
+                                "Failed to reinitialize MacCoreaudio devices",
+                                t);
+                        }
+                    }
+                };
+            CoreAudioDevice.setDevicesChangedCallback(
+                    devicesChangedCallback);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getRendererClassName()
+    {
+        return MacCoreaudioRenderer.class.getName();
+    }
+
+    /**
+     * Attempts to reorder specific lists of capture and playback/notify
+     * <tt>CaptureDeviceInfo2</tt>s so that devices from the same
+     * hardware appear at the same indices in the respective lists. The judgment
+     * with respect to the belonging to the same hardware is based on the names
+     * of the specified <tt>CaptureDeviceInfo2</tt>s. The implementation
+     * is provided as a fallback to stand in for scenarios in which more
+     * accurate relevant information is not available.
+     *
+     * @param captureDevices
+     * @param playbackDevices
+     */
+    private void matchDevicesByName(
+            List<CaptureDeviceInfo2> captureDevices,
+            List<CaptureDeviceInfo2> playbackDevices)
+    {
+        Iterator<CaptureDeviceInfo2> captureIter
+            = captureDevices.iterator();
+        Pattern pattern
+            = Pattern.compile(
+                    "array|headphones|microphone|speakers|\\p{Space}|\\(|\\)",
+                    Pattern.CASE_INSENSITIVE);
+        LinkedList<CaptureDeviceInfo2> captureDevicesWithPlayback
+            = new LinkedList<CaptureDeviceInfo2>();
+        LinkedList<CaptureDeviceInfo2> playbackDevicesWithCapture
+            = new LinkedList<CaptureDeviceInfo2>();
+        int count = 0;
+
+        while (captureIter.hasNext())
+        {
+            CaptureDeviceInfo2 captureDevice = captureIter.next();
+            String captureName = captureDevice.getName();
+
+            if (captureName != null)
+            {
+                captureName = pattern.matcher(captureName).replaceAll("");
+                if (captureName.length() != 0)
+                {
+                    Iterator<CaptureDeviceInfo2> playbackIter
+                        = playbackDevices.iterator();
+                    CaptureDeviceInfo2 matchingPlaybackDevice = null;
+
+                    while (playbackIter.hasNext())
+                    {
+                        CaptureDeviceInfo2 playbackDevice
+                            = playbackIter.next();
+                        String playbackName = playbackDevice.getName();
+
+                        if (playbackName != null)
+                        {
+                            playbackName
+                                = pattern
+                                    .matcher(playbackName)
+                                        .replaceAll("");
+                            if (captureName.equals(playbackName))
+                            {
+                                playbackIter.remove();
+                                matchingPlaybackDevice = playbackDevice;
+                                break;
+                            }
+                        }
+                    }
+                    if (matchingPlaybackDevice != null)
+                    {
+                        captureIter.remove();
+                        captureDevicesWithPlayback.add(captureDevice);
+                        playbackDevicesWithCapture.add(
+                                matchingPlaybackDevice);
+                        count++;
+                    }
+                }
+            }
+        }
+
+        for (int i = count - 1; i >= 0; i--)
+        {
+            captureDevices.add(0, captureDevicesWithPlayback.get(i));
+            playbackDevices.add(0, playbackDevicesWithCapture.get(i));
+        }
+    }
+
+    /**
+     * Reinitializes this <tt>MacCoreaudioSystem</tt> in order to bring it up to
+     * date with possible changes in the MacCoreaudio devices. Invokes
+     * <tt>Pa_UpdateAvailableDeviceList()</tt> to update the devices on the
+     * native side and then {@link #initialize()} to reflect any changes on the
+     * Java side. Invoked by MacCoreaudio when it detects that the list of
+     * devices has changed.
+     *
+     * @throws Exception if there was an error during the invocation of
+     * <tt>Pa_UpdateAvailableDeviceList()</tt> and
+     * <tt>DeviceSystem.initialize()</tt>
+     */
+    private void reinitialize()
+        throws Exception
+    {
+        synchronized (updateAvailableDeviceListSyncRoot)
+        {
+            willUpdateAvailableDeviceList();
+            didUpdateAvailableDeviceList();
+        }
+
+        /*
+         * XXX We will likely minimize the risk of crashes on the native side
+         * even further by invoking initialize() with
+         * Pa_UpdateAvailableDeviceList locked. Unfortunately, that will likely
+         * increase the risks of deadlocks on the Java side.
+         */
+        invokeDeviceSystemInitialize(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * The implementation of <tt>MacCoreaudioSystem</tt> always returns
+     * &quot;MacCoreaudio&quot;.
+     */
+    @Override
+    public String toString()
+    {
+        return "MacCoreaudio";
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/DataSource.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/DataSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..c149899f84664d4c7519b4374dcdc7efc57e9353
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/DataSource.java
@@ -0,0 +1,251 @@
+/*
+ * 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.jmfext.media.protocol.maccoreaudio;
+
+import java.io.*;
+
+import javax.media.*;
+import javax.media.control.*;
+
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.impl.neomedia.jmfext.media.protocol.*;
+import org.jitsi.util.*;
+
+/**
+ * Implements <tt>DataSource</tt> and <tt>CaptureDevice</tt> for MacCoreaudio.
+ *
+ * @author Vincent Lucas
+ */
+public class DataSource
+    extends AbstractPullBufferCaptureDevice
+{
+    /**
+     * The <tt>Logger</tt> used by the <tt>DataSource</tt> class and its
+     * instances for logging output.
+     */
+    private static final Logger logger = Logger.getLogger(DataSource.class);
+
+    /**
+     * The indicator which determines whether this <tt>DataSource</tt> will
+     * use audio quality improvement in accord with the preferences of the user.
+     */
+    private final boolean audioQualityImprovement;
+
+    /**
+     * The list of <tt>Format</tt>s in which this <tt>DataSource</tt> is
+     * capable of capturing audio data.
+     */
+    private final Format[] supportedFormats;
+
+    /**
+     * Initializes a new <tt>DataSource</tt> instance.
+     */
+    public DataSource()
+    {
+        this.supportedFormats = null;
+        this.audioQualityImprovement = true;
+    }
+
+    /**
+     * Initializes a new <tt>DataSource</tt> instance from a specific
+     * <tt>MediaLocator</tt>.
+     *
+     * @param locator the <tt>MediaLocator</tt> to create the new instance from
+     */
+    public DataSource(MediaLocator locator)
+    {
+        this(locator, null, true);
+    }
+
+    /**
+     * Initializes a new <tt>DataSource</tt> instance from a specific
+     * <tt>MediaLocator</tt> and which has a specific list of <tt>Format</tt>
+     * in which it is capable of capturing audio data overriding its
+     * registration with JMF and optionally uses audio quality improvement in
+     * accord with the preferences of the user.
+     *
+     * @param locator the <tt>MediaLocator</tt> to create the new instance from
+     * @param supportedFormats the list of <tt>Format</tt>s in which the new
+     * instance is to be capable of capturing audio data
+     * @param audioQualityImprovement <tt>true</tt> if audio quality improvement
+     * is to be enabled in accord with the preferences of the user or
+     * <tt>false</tt> to completely disable audio quality improvement
+     */
+    public DataSource(
+            MediaLocator locator,
+            Format[] supportedFormats,
+            boolean audioQualityImprovement)
+    {
+        super(locator);
+
+        this.supportedFormats
+            = (supportedFormats == null)
+                ? null
+                : supportedFormats.clone();
+        this.audioQualityImprovement = audioQualityImprovement;
+    }
+
+    /**
+     * Creates a new <tt>PullBufferStream</tt> which is to be at a specific
+     * zero-based index in the list of streams of this
+     * <tt>PullBufferDataSource</tt>. The <tt>Format</tt>-related information of
+     * the new instance is to be abstracted by a specific
+     * <tt>FormatControl</tt>.
+     *
+     * @param streamIndex the zero-based index of the <tt>PullBufferStream</tt>
+     * in the list of streams of this <tt>PullBufferDataSource</tt>
+     * @param formatControl the <tt>FormatControl</tt> which is to abstract the
+     * <tt>Format</tt>-related information of the new instance
+     * @return a new <tt>PullBufferStream</tt> which is to be at the specified
+     * <tt>streamIndex</tt> in the list of streams of this
+     * <tt>PullBufferDataSource</tt> and which has its <tt>Format</tt>-related
+     * information abstracted by the specified <tt>formatControl</tt>
+     * @see AbstractPullBufferCaptureDevice#createStream(int, FormatControl)
+     */
+    @Override
+    protected MacCoreaudioStream createStream(
+            int streamIndex,
+            FormatControl formatControl)
+    {
+        return new MacCoreaudioStream(
+                this,
+                formatControl,
+                audioQualityImprovement);
+    }
+
+    /**
+     * Opens a connection to the media source specified by the
+     * <tt>MediaLocator</tt> of this <tt>DataSource</tt>.
+     *
+     * @throws IOException if anything goes wrong while opening the connection
+     * to the media source specified by the <tt>MediaLocator</tt> of this
+     * <tt>DataSource</tt>
+     * @see AbstractPullBufferCaptureDevice#doConnect()
+     */
+    @Override
+    protected void doConnect()
+        throws IOException
+    {
+        super.doConnect();
+
+        String deviceID = getDeviceID();
+
+        synchronized (getStreamSyncRoot())
+        {
+            for (Object stream : getStreams())
+                ((MacCoreaudioStream) stream).setDeviceUID(deviceID);
+        }
+    }
+
+    /**
+     * Closes the connection to the media source specified by the
+     * <tt>MediaLocator</tt> of this <tt>DataSource</tt>. Allows extenders to
+     * override and be sure that there will be no request to close a connection
+     * if the connection has not been opened yet.
+     */
+    @Override
+    protected void doDisconnect()
+    {
+        try
+        {
+            synchronized (getStreamSyncRoot())
+            {
+                Object[] streams = streams();
+
+                if (streams != null)
+                {
+                    for (Object stream : streams)
+                    {
+                        ((MacCoreaudioStream) stream).setDeviceUID(null);
+                    }
+                }
+            }
+        }
+        finally
+        {
+            super.doDisconnect();
+        }
+    }
+
+    /**
+     * Gets the device index of the MacCoreaudio device identified by the
+     * <tt>MediaLocator</tt> of this <tt>DataSource</tt>.
+     *
+     * @return the device index of a MacCoreaudio device identified by the
+     * <tt>MediaLocator</tt> of this <tt>DataSource</tt>
+     * @throws IllegalStateException if there is no <tt>MediaLocator</tt>
+     * associated with this <tt>DataSource</tt>
+     */
+    private String getDeviceID()
+    {
+        MediaLocator locator = getLocator();
+
+        if (locator == null)
+            throw new IllegalStateException("locator");
+        else
+            return getDeviceID(locator);
+    }
+
+    /**
+     * Gets the device index of a MacCoreaudio device from a specific
+     * <tt>MediaLocator</tt> identifying it.
+     *
+     * @param locator the <tt>MediaLocator</tt> identifying the device index of
+     * a MacCoreaudio device to get
+     * @return the device index of a MacCoreaudio device identified by
+     * <tt>locator</tt>
+     */
+    public static String getDeviceID(MediaLocator locator)
+    {
+        if (locator == null)
+        {
+            /*
+             * Explicitly throw a NullPointerException because the implicit one
+             * does not have a message and is thus a bit more difficult to
+             * debug.
+             */
+            throw new NullPointerException("locator");
+        }
+        else if (AudioSystem.LOCATOR_PROTOCOL_MACCOREAUDIO.equalsIgnoreCase(
+                locator.getProtocol()))
+        {
+            String remainder = locator.getRemainder();
+
+            if ((remainder != null) && (remainder.charAt(0) == '#'))
+                remainder = remainder.substring(1);
+            return remainder;
+        }
+        else
+        {
+            throw new IllegalArgumentException("locator.protocol");
+        }
+    }
+
+    /**
+     * Gets the <tt>Format</tt>s which are to be reported by a
+     * <tt>FormatControl</tt> as supported formats for a
+     * <tt>PullBufferStream</tt> at a specific zero-based index in the list of
+     * streams of this <tt>PullBufferDataSource</tt>.
+     *
+     * @param streamIndex the zero-based index of the <tt>PullBufferStream</tt>
+     * for which the specified <tt>FormatControl</tt> is to report the list of
+     * supported <tt>Format</tt>s
+     * @return an array of <tt>Format</tt>s to be reported by a
+     * <tt>FormatControl</tt> as the supported formats for the
+     * <tt>PullBufferStream</tt> at the specified <tt>streamIndex</tt> in the
+     * list of streams of this <tt>PullBufferDataSource</tt>
+     * @see AbstractPullBufferCaptureDevice#getSupportedFormats(int)
+     */
+    @Override
+    protected Format[] getSupportedFormats(int streamIndex)
+    {
+        return
+            (supportedFormats == null)
+                ? super.getSupportedFormats(streamIndex)
+                : supportedFormats;
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/MacCoreaudioStream.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/MacCoreaudioStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..40711edb53b065c9d8117e56e8b25cd6633d22ce
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/maccoreaudio/MacCoreaudioStream.java
@@ -0,0 +1,440 @@
+/*
+ * 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.jmfext.media.protocol.maccoreaudio;
+
+import java.io.*;
+import java.util.*;
+
+import javax.media.*;
+import javax.media.control.*;
+import javax.media.format.*;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.codec.*;
+import org.jitsi.impl.neomedia.control.*;
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.impl.neomedia.jmfext.media.protocol.*;
+import org.jitsi.service.neomedia.*;
+import org.jitsi.util.*;
+
+/**
+ * Implements <tt>PullBufferStream</tt> for MacCoreaudio.
+ *
+ * @author Vincent Lucas
+ */
+public class MacCoreaudioStream
+    extends AbstractPullBufferStream<DataSource>
+{
+    /**
+     * The <tt>Logger</tt> used by the <tt>MacCoreaudioStream</tt> class and its
+     * instances for logging output.
+     */
+    private static final Logger logger
+        = Logger.getLogger(MacCoreaudioStream.class);
+
+    /**
+     * The indicator which determines whether audio quality improvement is
+     * enabled for this <tt>MacCoreaudioStream</tt> in accord with the
+     * preferences of the user.
+     */
+    private final boolean audioQualityImprovement;
+
+    /**
+     * The number of bytes to read from a native MacCoreaudio stream in a single
+     * invocation. Based on {@link #framesPerBuffer}.
+     */
+    private int bytesPerBuffer;
+
+    /**
+     * The device identifier (the device UID, or if not available, the device
+     * name) of the MacCoreaudio device read through this
+     * <tt>PullBufferStream</tt>.
+     */
+    private String deviceUID;
+
+    /**
+     * A mutual eclusion used to avoid conflict when starting / stoping the
+     * stream for this stream;
+     */
+    private Object startStopMutex = new Object();
+
+    /**
+     * The buffer which stores the outgoing data before sending them to
+     * the RTP stack.
+     */
+    private byte[] buffer = null;
+
+    /**
+     * A list of already allocated buffers, ready to accept new captured data.
+     */
+    private Vector<byte[]> freeBufferList = new Vector<byte[]>();
+
+    /**
+     * A list of already allocated and filled buffers, ready to be send throw
+     * the network.
+     */
+    private Vector<byte[]> fullBufferList = new Vector<byte[]>();
+
+    /**
+     * The number of data available to feed the RTP stack.
+     */
+    private int nbBufferData = 0;
+
+    /**
+     * The last-known <tt>Format</tt> of the media data made available by this
+     * <tt>PullBufferStream</tt>.
+     */
+    private AudioFormat format = null;
+
+    /**
+     * The <tt>GainControl</tt> through which the volume/gain of captured media
+     * is controlled.
+     */
+    private final GainControl gainControl;
+
+    private final MacCoreaudioSystem.UpdateAvailableDeviceListListener
+        updateAvailableDeviceListListener
+            = new MacCoreaudioSystem.UpdateAvailableDeviceListListener()
+            {
+                /**
+                 * The device ID (could be deviceUID or name but that is not
+                 * really of concern to MacCoreaudioStream) used before and
+                 * after (if still available) the update.
+                 */
+                private String deviceUID = null;
+
+                private boolean start = false;
+
+                public void didUpdateAvailableDeviceList()
+                    throws Exception
+                {
+                    synchronized(startStopMutex)
+                    {
+                        setDeviceUID(deviceUID);
+                        if(start)
+                            start();
+                        deviceUID = null;
+                        start = false;
+                    }
+                }
+
+                public void willUpdateAvailableDeviceList()
+                    throws Exception
+                {
+                    synchronized(startStopMutex)
+                    {
+                        if (stream == 0)
+                        {
+                            deviceUID = null;
+                            start = false;
+                        }
+                        else
+                        {
+                            deviceUID = MacCoreaudioStream.this.deviceUID;
+                            start = true;
+                            stop();
+                            setDeviceUID(null);
+                        }
+                    }
+                }
+            };
+
+    /**
+     * Current sequence number.
+     */
+    private int sequenceNumber = 0;
+
+    /**
+     * The stream structure used by the native maccoreaudio library.
+     */
+    private long stream = 0;
+
+    /**
+     * Initializes a new <tt>MacCoreaudioStream</tt> instance which is to have
+     * its <tt>Format</tt>-related information abstracted by a specific
+     * <tt>FormatControl</tt>.
+     *
+     * @param dataSource the <tt>DataSource</tt> which is creating the new
+     * instance so that it becomes one of its <tt>streams</tt>
+     * @param formatControl the <tt>FormatControl</tt> which is to abstract the
+     * <tt>Format</tt>-related information of the new instance
+     * @param audioQualityImprovement <tt>true</tt> to enable audio quality
+     * improvement for the new instance in accord with the preferences of the
+     * user or <tt>false</tt> to completely disable audio quality improvement
+     */
+    public MacCoreaudioStream(
+            DataSource dataSource,
+            FormatControl formatControl,
+            boolean audioQualityImprovement)
+    {
+        super(dataSource, formatControl);
+
+        this.audioQualityImprovement = audioQualityImprovement;
+
+        MediaServiceImpl mediaServiceImpl
+            = NeomediaServiceUtils.getMediaServiceImpl();
+
+        gainControl = (mediaServiceImpl == null)
+            ? null
+            : (GainControl) mediaServiceImpl.getInputVolumeControl();
+
+        // XXX We will add a UpdateAvailableDeviceListListener and will not
+        // remove it because we will rely on MacCoreaudioSystem's use of
+        // WeakReference.
+        MacCoreaudioSystem.addUpdateAvailableDeviceListListener(
+                updateAvailableDeviceListListener);
+    }
+
+    private void connect()
+    {
+        AudioFormat format = (AudioFormat) getFormat();
+        int channels = format.getChannels();
+        if (channels == Format.NOT_SPECIFIED)
+            channels = 1;
+        int sampleSizeInBits = format.getSampleSizeInBits();
+        double sampleRate = format.getSampleRate();
+        int framesPerBuffer
+            = (int) ((sampleRate * MacCoreAudioDevice.DEFAULT_MILLIS_PER_BUFFER)
+                    / (channels * 1000));
+        bytesPerBuffer = (sampleSizeInBits / 8) * channels * framesPerBuffer;
+
+        // Know the Format in which this MacCoreaudioStream will output audio
+        // data so that it can report it without going through its DataSource.
+        this.format = new AudioFormat(
+                AudioFormat.LINEAR,
+                sampleRate,
+                sampleSizeInBits,
+                channels,
+                AudioFormat.LITTLE_ENDIAN,
+                AudioFormat.SIGNED,
+                Format.NOT_SPECIFIED /* frameSizeInBits */,
+                Format.NOT_SPECIFIED /* frameRate */,
+                Format.byteArray);
+    }
+
+    /**
+     * Gets the <tt>Format</tt> of this <tt>PullBufferStream</tt> as directly
+     * known by it.
+     *
+     * @return the <tt>Format</tt> of this <tt>PullBufferStream</tt> as directly
+     * known by it or <tt>null</tt> if this <tt>PullBufferStream</tt> does not
+     * directly know its <tt>Format</tt> and it relies on the
+     * <tt>PullBufferDataSource</tt> which created it to report its
+     * <tt>Format</tt>
+     * @see AbstractPullBufferStream#doGetFormat()
+     */
+    @Override
+    protected Format doGetFormat()
+    {
+        return (format == null) ? super.doGetFormat() : format;
+    }
+
+    /**
+     * Reads media data from this <tt>PullBufferStream</tt> into a specific
+     * <tt>Buffer</tt> with blocking.
+     *
+     * @param buffer the <tt>Buffer</tt> in which media data is to be read from
+     * this <tt>PullBufferStream</tt>
+     * @throws IOException if anything goes wrong while reading media data from
+     * this <tt>PullBufferStream</tt> into the specified <tt>buffer</tt>
+     */
+    public void read(Buffer buffer)
+        throws IOException
+    {
+        int length = 0;
+        byte[] data = AbstractCodec2.validateByteArraySize(
+                        buffer,
+                        bytesPerBuffer,
+                        false);
+
+        synchronized(startStopMutex)
+        {
+            // Waits for the next buffer.
+            while(this.fullBufferList.size() == 0 && stream != 0)
+            {
+                try
+                {
+                    startStopMutex.wait();
+                }
+                catch(InterruptedException ex)
+                {}
+            }
+
+            // If the stream is running.
+            if(stream != 0)
+            {
+                this.freeBufferList.add(data);
+                data = this.fullBufferList.remove(0);
+                length = data.length;
+            }
+        }
+
+        // Take into account the user's preferences with respect to the
+        // input volume.
+        if(length != 0 && gainControl != null)
+        {
+            BasicVolumeControl.applyGain(
+                    gainControl,
+                    data,
+                    0,
+                    length);
+        }
+
+        long bufferTimeStamp = System.nanoTime();
+
+        buffer.setData(data);
+        buffer.setFlags(Buffer.FLAG_SYSTEM_TIME);
+        if (format != null)
+            buffer.setFormat(format);
+        buffer.setHeader(null);
+        buffer.setLength(length);
+        buffer.setOffset(0);
+        buffer.setSequenceNumber(sequenceNumber++);
+        buffer.setTimeStamp(bufferTimeStamp);
+    }
+
+    /**
+     * Sets the device index of the MacCoreaudio device to be read through this
+     * <tt>PullBufferStream</tt>.
+     *
+     * @param deviceID The ID of the device used to be read trough this
+     * MacCoreaudioStream.  This String contains the deviceUID, or if not
+     * available, the device name.  If set to null, then there was no device
+     * used before the update.
+     */
+    void setDeviceUID(String deviceUID)
+    {
+        synchronized(startStopMutex)
+        {
+            if (this.deviceUID != null)
+            {
+                // If there is a running stream, then close it.
+                try
+                {
+                    stop();
+                }
+                catch(IOException ioex)
+                {
+                    logger.info(ioex);
+                }
+
+                // Make sure this AbstractPullBufferStream asks its DataSource
+                // for the Format in which it is supposed to output audio data
+                // the next time it is opened instead of using its Format from a
+                // previous open.
+                this.format = null;
+            }
+            this.deviceUID = deviceUID;
+
+            if (this.deviceUID != null)
+            {
+                connect();
+            }
+        }
+    }
+
+    /**
+     * Starts the transfer of media data from this <tt>PullBufferStream</tt>.
+     */
+    @Override
+    public void start()
+        throws IOException
+    {
+        synchronized(startStopMutex)
+        {
+            if(stream == 0 && deviceUID != null)
+            {
+                buffer = new byte[bytesPerBuffer];
+                nbBufferData = 0;
+                this.fullBufferList.clear();
+                this.freeBufferList.clear();
+
+                MacCoreaudioSystem.willOpenStream();
+                stream = MacCoreAudioDevice.startStream(
+                        deviceUID,
+                        this,
+                        (float) format.getSampleRate(),
+                        format.getChannels(),
+                        format.getSampleSizeInBits(),
+                        false,
+                        format.getEndian() == AudioFormat.BIG_ENDIAN,
+                        false);
+                MacCoreaudioSystem.didOpenStream();
+            }
+        }
+    }
+
+    /**
+     * Stops the transfer of media data from this <tt>PullBufferStream</tt>.
+     */
+    @Override
+    public void stop()
+        throws IOException
+    {
+        synchronized(startStopMutex)
+        {
+            if(stream != 0 && deviceUID != null)
+            {
+                MacCoreAudioDevice.stopStream(deviceUID, stream);
+                stream = 0;
+                this.fullBufferList.clear();
+                this.freeBufferList.clear();
+                startStopMutex.notify();
+            }
+        }
+    }
+
+    /**
+     * Callback which receives the data from the coreaudio library.
+     *
+     * @param buffer The data captured from the input.
+     * @param bufferLength The length of the data captured.
+     */
+    public void readInput(byte[] buffer, int bufferLength)
+    {
+        int nbCopied = 0;
+        while(bufferLength > 0)
+        {
+            int length = this.buffer.length - nbBufferData;
+            if(bufferLength < length)
+            {
+                length = bufferLength;
+            }
+
+            System.arraycopy(
+                    buffer,
+                    nbCopied,
+                    this.buffer,
+                    nbBufferData,
+                    length);
+
+            nbBufferData += length;
+            nbCopied += length;
+            bufferLength -= length;
+
+            if(nbBufferData == this.buffer.length)
+            {
+                this.fullBufferList.add(this.buffer);
+                this.buffer = null;
+                nbBufferData = 0;
+                synchronized(startStopMutex)
+                {
+                    startStopMutex.notify();
+                    if(this.freeBufferList.size() > 0)
+                    {
+                        this.buffer = this.freeBufferList.remove(0);
+                    }
+                }
+
+                if(this.buffer == null)
+                {
+                    this.buffer = new byte[bytesPerBuffer];
+                }
+            }
+        }
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/MacCoreaudioRenderer.java b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/MacCoreaudioRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1657c645a6779840ea8ada88908cfebd604cbcce
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/MacCoreaudioRenderer.java
@@ -0,0 +1,592 @@
+/*
+ * 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.jmfext.media.renderer.audio;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import javax.media.*;
+import javax.media.format.*;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.control.*;
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.service.neomedia.*;
+import org.jitsi.util.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implements an audio <tt>Renderer</tt> which uses MacOSX Coreaudio.
+ *
+ * @author Vincent Lucas
+ */
+public class MacCoreaudioRenderer
+    extends AbstractAudioRenderer<MacCoreaudioSystem>
+{
+    /**
+     * The <tt>Logger</tt> used by the <tt>MacCoreaudioRenderer</tt> class and
+     * its instances for logging output.
+     */
+    private static final Logger logger
+        = Logger.getLogger(MacCoreaudioRenderer.class);
+
+    /**
+     * The device used for this renderer.
+     */
+    private String deviceUID = null;
+
+    /**
+     * The stream structure used by the native maccoreaudio library.
+     */
+    private long stream = 0;
+
+    /**
+     * A mutual eclusion used to avoid conflict when starting / stoping the
+     * stream for this renderer;
+     */
+    private Object startStopMutex = new Object();
+
+    /**
+     * The buffer which stores th incoming data before sending them to
+     * CoreAudio.
+     */
+    private byte[] buffer = null;
+
+    /**
+     * The number of data available to feed CoreAudio output.
+     */
+    private int nbBufferData = 0;
+
+    private boolean isStopping = false;
+
+    /**
+     * The constant which represents an empty array with
+     * <tt>Format</tt> element type. Explicitly defined in order to
+     * reduce unnecessary allocations.
+     */
+    private static final Format[] EMPTY_SUPPORTED_INPUT_FORMATS
+        = new Format[0];
+
+    /**
+     * The human-readable name of the <tt>MacCoreaudioRenderer</tt> JMF plug-in.
+     */
+    private static final String PLUGIN_NAME = "MacCoreaudio Renderer";
+
+    /**
+     * The list of JMF <tt>Format</tt>s of audio data which
+     * <tt>MacCoreaudioRenderer</tt> instances are capable of rendering.
+     */
+    private static final Format[] SUPPORTED_INPUT_FORMATS;
+
+    /**
+     * The list of the sample rates supported by <tt>MacCoreaudioRenderer</tt>
+     * as input.
+     */
+    private static final double[] SUPPORTED_INPUT_SAMPLE_RATES
+        = new double[] { 8000, 11025, 16000, 22050, 32000, 44100, 48000 };
+
+    static
+    {
+        int count = SUPPORTED_INPUT_SAMPLE_RATES.length;
+
+        SUPPORTED_INPUT_FORMATS = new Format[count];
+        for (int i = 0; i < count; i++)
+        {
+            SUPPORTED_INPUT_FORMATS[i]
+                = new AudioFormat(
+                        AudioFormat.LINEAR,
+                        SUPPORTED_INPUT_SAMPLE_RATES[i],
+                        16 /* sampleSizeInBits */,
+                        Format.NOT_SPECIFIED /* channels */,
+                        AudioFormat.LITTLE_ENDIAN,
+                        AudioFormat.SIGNED,
+                        Format.NOT_SPECIFIED /* frameSizeInBits */,
+                        Format.NOT_SPECIFIED /* frameRate */,
+                        Format.byteArray);
+        }
+    }
+
+    /**
+     * The <tt>UpdateAvailableDeviceListListener</tt> which is to be notified
+     * before and after MacCoreaudio's native function
+     * <tt>UpdateAvailableDeviceList()</tt> is invoked. It will close
+     * {@link #stream} before the invocation in order to mitigate memory
+     * corruption afterwards and it will attempt to restore the state of this
+     * <tt>Renderer</tt> after the invocation.
+     */
+    private final MacCoreaudioSystem.UpdateAvailableDeviceListListener
+        updateAvailableDeviceListListener
+            = new MacCoreaudioSystem.UpdateAvailableDeviceListListener()
+    {
+        private boolean start = false;
+
+        public void didUpdateAvailableDeviceList()
+            throws Exception
+        {
+            synchronized(startStopMutex)
+            {
+                updateDeviceUID();
+                if(start)
+                {
+                    open();
+                    start();
+                }
+            }
+        }
+
+        public void willUpdateAvailableDeviceList()
+            throws Exception
+        {
+            synchronized(startStopMutex)
+            {
+                start = false;
+                if(stream != 0)
+                {
+                    start = true;
+                    stop();
+                }
+            }
+        }
+    };
+
+    /**
+     * Array of supported input formats.
+     */
+    private Format[] supportedInputFormats;
+
+    /**
+     * Initializes a new <tt>MacCoreaudioRenderer</tt> instance.
+     */
+    public MacCoreaudioRenderer()
+    {
+        this(true);
+    }
+
+    /**
+     * Initializes a new <tt>MacCoreaudioRenderer</tt> instance which is to
+     * either perform playback or sound a notification.
+     *
+     * @param playback <tt>true</tt> if the new instance is to perform playback
+     * or <tt>false</tt> if the new instance is to sound a notification
+     */
+    public MacCoreaudioRenderer(boolean enableVolumeControl)
+    {
+        super(
+                AudioSystem.LOCATOR_PROTOCOL_MACCOREAUDIO,
+                enableVolumeControl
+                    ? AudioSystem.DataFlow.PLAYBACK
+                    : AudioSystem.DataFlow.NOTIFY);
+
+        // XXX We will add a PaUpdateAvailableDeviceListListener and will not
+        // remove it because we will rely on MacCoreaudioSystem's use of
+        // WeakReference.
+        MacCoreaudioSystem.addUpdateAvailableDeviceListListener(
+                updateAvailableDeviceListListener);
+    }
+
+    /**
+     * Closes this <tt>PlugIn</tt>.
+     */
+    @Override
+    public void close()
+    {
+        stop();
+        super.close();
+    }
+
+    /**
+     * Gets the descriptive/human-readable name of this JMF plug-in.
+     *
+     * @return the descriptive/human-readable name of this JMF plug-in
+     */
+    public String getName()
+    {
+        return PLUGIN_NAME;
+    }
+
+    /**
+     * Gets the list of JMF <tt>Format</tt>s of audio data which this
+     * <tt>Renderer</tt> is capable of rendering.
+     *
+     * @return an array of JMF <tt>Format</tt>s of audio data which this
+     * <tt>Renderer</tt> is capable of rendering
+     */
+    @Override
+    public Format[] getSupportedInputFormats()
+    {
+        if (supportedInputFormats == null)
+        {
+            MediaLocator locator = getLocator();
+            this.updateDeviceUID();
+
+            if(deviceUID == null)
+            {
+                supportedInputFormats = SUPPORTED_INPUT_FORMATS;
+            }
+            else
+            {
+                int minOutputChannels = 1;
+                // The maximum output channels may be a lot and checking all of
+                // them will take a lot of time. Besides, we currently support
+                // at most 2.
+                int maxOutputChannels
+                    = Math.min(
+                            MacCoreAudioDevice.countOutputChannels(deviceUID),
+                            2);
+                List<Format> supportedInputFormats
+                    = new ArrayList<Format>(SUPPORTED_INPUT_FORMATS.length);
+
+                for (Format supportedInputFormat : SUPPORTED_INPUT_FORMATS)
+                {
+                    getSupportedInputFormats(
+                            supportedInputFormat,
+                            minOutputChannels,
+                            maxOutputChannels,
+                            supportedInputFormats);
+                }
+
+                this.supportedInputFormats
+                    = supportedInputFormats.isEmpty()
+                        ? EMPTY_SUPPORTED_INPUT_FORMATS
+                        : supportedInputFormats.toArray(
+                                EMPTY_SUPPORTED_INPUT_FORMATS);
+            }
+        }
+        return
+            (supportedInputFormats.length == 0)
+                ? EMPTY_SUPPORTED_INPUT_FORMATS
+                : supportedInputFormats.clone();
+    }
+
+    private void getSupportedInputFormats(
+            Format format,
+            int minOutputChannels,
+            int maxOutputChannels,
+            List<Format> supportedInputFormats)
+    {
+        AudioFormat audioFormat = (AudioFormat) format;
+        int sampleSizeInBits = audioFormat.getSampleSizeInBits();
+        double sampleRate = audioFormat.getSampleRate();
+        float minRate
+            = MacCoreAudioDevice.getMinimalNominalSampleRate(deviceUID);
+        float maxRate
+            = MacCoreAudioDevice.getMaximalNominalSampleRate(deviceUID);
+
+        for(int channels = minOutputChannels;
+                channels <= maxOutputChannels;
+                channels++)
+        {
+            if(sampleRate >= minRate && sampleRate <= maxRate)
+            {
+                supportedInputFormats.add(
+                        new AudioFormat(
+                            audioFormat.getEncoding(),
+                            sampleRate,
+                            sampleSizeInBits,
+                            channels,
+                            audioFormat.getEndian(),
+                            audioFormat.getSigned(),
+                            Format.NOT_SPECIFIED, // frameSizeInBits
+                            Format.NOT_SPECIFIED, // frameRate
+                            audioFormat.getDataType()));
+            }
+        }
+    }
+
+    /**
+     * Opens the MacCoreaudio device and output stream represented by this
+     * instance which are to be used to render audio.
+     *
+     * @throws ResourceUnavailableException if the MacCoreaudio device or output
+     * stream cannot be created or opened
+     */
+    @Override
+    public void open()
+        throws ResourceUnavailableException
+    {
+        synchronized(startStopMutex)
+        {
+            if(stream == 0)
+            {
+                MacCoreaudioSystem.willOpenStream();
+                try
+                {
+                    if(!this.updateDeviceUID())
+                    {
+                        throw new ResourceUnavailableException(
+                                "No locator/MediaLocator is set.");
+                    }
+                }
+                finally
+                {
+                    MacCoreaudioSystem.didOpenStream();
+                }
+
+            }
+            super.open();
+        }
+    }
+
+    /**
+     * Notifies this instance that the value of the
+     * {@link AudioSystem#PROP_PLAYBACK_DEVICE} property of its associated
+     * <tt>AudioSystem</tt> has changed.
+     *
+     * @param ev a <tt>PropertyChangeEvent</tt> which specifies details about
+     * the change such as the name of the property and its old and new values
+     */
+    @Override
+    protected synchronized void playbackDevicePropertyChange(
+            PropertyChangeEvent ev)
+    {
+        synchronized(startStopMutex)
+        {
+            stop();
+            updateDeviceUID();
+            start();
+        }
+    }
+
+    /**
+     * Renders the audio data contained in a specific <tt>Buffer</tt> onto the
+     * MacCoreaudio device represented by this <tt>Renderer</tt>.
+     *
+     * @param buffer the <tt>Buffer</tt> which contains the audio data to be
+     * rendered
+     * @return <tt>BUFFER_PROCESSED_OK</tt> if the specified <tt>buffer</tt> has
+     * been successfully processed
+     */
+    public int process(Buffer buffer)
+    {
+        synchronized(startStopMutex)
+        {
+            if(stream != 0 && !isStopping)
+            {
+                // Take into account the user's preferences with respect to the
+                // output volume.
+                GainControl gainControl = getGainControl();
+                if (gainControl != null)
+                {
+                    BasicVolumeControl.applyGain(
+                            gainControl,
+                            (byte[]) buffer.getData(),
+                            buffer.getOffset(),
+                            buffer.getLength());
+                }
+
+                int length = buffer.getLength();
+
+                // Update the buffer size if too small.
+                int timeout = 2000;
+                int maxNbBuffers
+                    = timeout / MacCoreAudioDevice.DEFAULT_MILLIS_PER_BUFFER;
+                updateBufferLength(
+                        Math.min(
+                            nbBufferData + length,
+                            length * maxNbBuffers));
+
+                if(nbBufferData + length > this.buffer.length)
+                {
+                    length = this.buffer.length - nbBufferData;
+                }
+
+                // Copy the received data.
+                System.arraycopy(
+                        (byte[]) buffer.getData(),
+                        buffer.getOffset(),
+                        this.buffer,
+                        nbBufferData,
+                        length);
+                nbBufferData += length;
+            }
+        }
+        return BUFFER_PROCESSED_OK;
+    }
+
+    /**
+     * Sets the <tt>MediaLocator</tt> which specifies the device index of the
+     * MacCoreaudio device to be used by this instance for rendering.
+     *
+     * @param locator a <tt>MediaLocator</tt> which specifies the device index
+     * of the MacCoreaudio device to be used by this instance for rendering
+     */
+    @Override
+    public void setLocator(MediaLocator locator)
+    {
+        super.setLocator(locator);
+
+        this.updateDeviceUID();
+
+        supportedInputFormats = null;
+    }
+
+    /**
+     * Starts the rendering process. Any audio data available in the internal
+     * resources associated with this <tt>MacCoreaudioRenderer</tt> will begin
+     * being rendered.
+     */
+    public void start()
+    {
+        // Start the stream
+        synchronized(startStopMutex)
+        {
+            if(stream == 0 && deviceUID != null)
+            {
+                int nbChannels = inputFormat.getChannels();
+                if (nbChannels == Format.NOT_SPECIFIED)
+                    nbChannels = 1;
+
+                MacCoreaudioSystem.willOpenStream();
+                stream = MacCoreAudioDevice.startStream(
+                        deviceUID,
+                        this,
+                        (float) inputFormat.getSampleRate(),
+                        nbChannels,
+                        inputFormat.getSampleSizeInBits(),
+                        false,
+                        inputFormat.getEndian() == AudioFormat.BIG_ENDIAN,
+                        false);
+                MacCoreaudioSystem.didOpenStream();
+            }
+        }
+    }
+
+    /**
+     * Stops the rendering process.
+     */
+    public void stop()
+    {
+        synchronized(startStopMutex)
+        {
+            if(stream != 0 && deviceUID != null && !isStopping)
+            {
+                this.isStopping = true;
+                long timeout = 500;
+                long startTime = System.currentTimeMillis();
+                long currentTime = startTime;
+                long startNbData = nbBufferData;
+                while(nbBufferData > 0
+                        && (currentTime - startTime) < timeout)
+                {
+                    try
+                    {
+                        startStopMutex.wait(timeout);
+                    }
+                    catch(InterruptedException ex)
+                    {
+                    }
+
+                    currentTime = System.currentTimeMillis();
+                    if(startNbData > nbBufferData)
+                    {
+                        startTime = currentTime;
+                        startNbData = nbBufferData;
+                    }
+                }
+
+                MacCoreAudioDevice.stopStream(deviceUID, stream);
+                stream = 0;
+                buffer = null;
+                nbBufferData = 0;
+                this.isStopping = false;
+            }
+        }
+    }
+
+    /**
+     * Writes the data received to the buffer give in arguments, which is
+     * provided by the CoreAudio library.
+     *
+     * @param buffer The buffer to fill in provided by the CoreAudio library.
+     * @param bufferLength The length of the buffer provided.
+     */
+    public void writeOutput(byte[] buffer, int bufferLength)
+    {
+        synchronized(startStopMutex)
+        {
+            updateBufferLength(bufferLength);
+
+            int i = 0;
+            int length = nbBufferData;
+            if(bufferLength < length)
+            {
+                length = bufferLength;
+            }
+
+            System.arraycopy(this.buffer, 0, buffer, 0, length);
+
+            // Fiils the end of the buffer with silence.
+            if(length < bufferLength)
+            {
+                Arrays.fill(buffer, length, bufferLength, (byte) 0);
+            }
+
+
+            nbBufferData -= length;
+            if(nbBufferData > 0)
+            {
+                System.arraycopy(
+                        this.buffer, length, this.buffer, 0, nbBufferData);
+            }
+            // If the stop process is waiting, notifies it  that every sample
+            // has been consummed.
+            // (nbBufferData == 0)
+            else
+            {
+                startStopMutex.notify();
+            }
+        }
+    }
+
+    /**
+     * Updates the deviceUID based on the current locator.
+     *
+     * @return True if the deviceUID has been updated. False otherwise.
+     */
+    private boolean updateDeviceUID()
+    {
+        MediaLocator locator = getLocator();
+        if(locator != null)
+        {
+            String remainder = locator.getRemainder();
+            if(remainder != null && remainder.length() > 1)
+            {
+                synchronized(startStopMutex)
+                {
+                    this.deviceUID = remainder.substring(1);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void updateBufferLength(int newLength)
+    {
+        synchronized(startStopMutex)
+        {
+            if(this.buffer == null)
+            {
+                this.buffer = new byte[newLength];
+                nbBufferData = 0;
+            }
+            else if(newLength > this.buffer.length)
+            {
+                byte[] newBuffer = new byte[newLength];
+                System.arraycopy(
+                        this.buffer,
+                        0,
+                        newBuffer,
+                        0,
+                        nbBufferData);
+                this.buffer = newBuffer;
+            }
+        }
+    }
+}