Index: /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp	(revision 5716)
+++ /pjproject/trunk/pjsip-apps/src/samples/pjsua2_demo.cpp	(revision 5717)
@@ -226,4 +226,18 @@
     std::cout << "*** PJSUA2 STARTED ***" << std::endl;
 
+    /* Use Null Audio Device as main media clock. This is useful for improving
+     * media clock (see also https://trac.pjsip.org/repos/wiki/FAQ#tx-timing)
+     * especially when sound device clock is jittery.
+     */
+    ep.audDevManager().setNullDev();
+
+    /* And install sound device using Extra Audio Device */
+    ExtraAudioDevice auddev2(-1, -1);
+    try {
+	auddev2.open();
+    } catch (...) {
+	std::cout << "Extra sound device failed" << std::endl;
+    }
+
     // Create player and recorder
     {
@@ -234,6 +248,7 @@
 	amr.createRecorder("recorder_test_output.wav");
 
-	amp.startTransmit(ep.audDevManager().getPlaybackDevMedia());
 	amp.startTransmit(amr);
+	if (auddev2.isOpened())
+	    amp.startTransmit(auddev2);
 
 	pj_thread_sleep(5000);
@@ -332,5 +347,5 @@
 	ep.libCreate();
 
-	mainProg4(ep);
+	mainProg3(ep);
 	ret = PJ_SUCCESS;
     } catch (Error & err) {
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5716)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 5717)
@@ -7045,4 +7045,67 @@
 
 
+/**
+ * Opaque type of extra sound device, an additional sound device
+ * beside the primary sound device (the one instantiated via
+ * pjsua_set_snd_dev() or pjsua_set_snd_dev2()). This sound device is
+ * also registered to conference bridge so it can be used as a normal
+ * conference bridge port, e.g: connect it to/from other ports,
+ * adjust/check audio level, etc. The conference bridge port ID can be
+ * queried using pjsua_ext_snd_dev_get_conf_port().
+ *
+ * Application may also use this API to improve media clock. Normally
+ * media clock is driven by sound device in master port, but unfortunately
+ * some sound devices may produce jittery clock. To improve media clock,
+ * application can install Null Sound Device (i.e: using
+ * pjsua_set_null_snd_dev()), which will act as a master port, and instantiate
+ * the sound device as extra sound device. But note that extra sound device
+ * will not have auto-close upon idle feature.
+ */
+typedef struct pjsua_ext_snd_dev pjsua_ext_snd_dev;
+
+
+/**
+ * Create an extra sound device and register it to conference bridge.
+ *
+ * @param snd_param	Sound device port param.
+ * @param p_snd		The extra sound device instance.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_ext_snd_dev_create(pjmedia_snd_port_param *param,
+					      pjsua_ext_snd_dev **p_snd);
+
+
+/**
+ * Destroy an extra sound device and unregister it from conference bridge.
+ *
+ * @param p_snd		The extra sound device instance.
+ *
+ * @return		PJ_SUCCESS on success or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjsua_ext_snd_dev_destroy(pjsua_ext_snd_dev *snd);
+
+
+/**
+ * Get sound port instance of an extra sound device.
+ *
+ * @param snd		The extra sound device instance.
+ *
+ * @return		The sound port instance.
+ */
+PJ_DECL(pjmedia_snd_port*) pjsua_ext_snd_dev_get_snd_port(
+					    pjsua_ext_snd_dev *snd);
+
+/**
+ * Get conference port ID of an extra sound device.
+ *
+ * @param snd		The extra sound device instance.
+ *
+ * @return		The conference port ID.
+ */
+PJ_DECL(pjsua_conf_port_id) pjsua_ext_snd_dev_get_conf_port(
+					    pjsua_ext_snd_dev *snd);
+
+
 /*****************************************************************************
  * Codecs.
Index: /pjproject/trunk/pjsip/include/pjsua2/media.hpp
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5716)
+++ /pjproject/trunk/pjsip/include/pjsua2/media.hpp	(revision 5717)
@@ -1350,4 +1350,55 @@
 
 
+/**
+ * Extra audio device. This class allows application to have multiple
+ * sound device instances active concurrently. Application may also use
+ * this class to improve media clock. Normally media clock is driven by
+ * sound device in master port, but unfortunately some sound devices may
+ * produce jittery clock. To improve media clock, application can install
+ * Null Sound Device (i.e: using AudDevManager::setNullDev()), which will
+ * act as a master port, and install the sound device as extra sound device.
+ * Note that extra sound device will not have auto-close upon idle feature.
+ */
+class ExtraAudioDevice : public AudioMedia
+{
+public:
+    /**
+     * Constructor
+     *
+     * @param playdev		Playback device ID.
+     * @param recdev		Record device ID.
+     */
+    ExtraAudioDevice(int playdev, int recdev);
+
+    /**
+     * Destructor
+     */
+    virtual ~ExtraAudioDevice();
+
+    /**
+     * Open the audio device using format (e.g.: clock rate, channel count,
+     * samples per frame) matched to the conference bridge's format.
+     */
+    void open();
+
+    /**
+     * Close the audio device.
+     */
+    void close();
+
+    /**
+     * Is the extra audio device opened?
+     *
+     * @return	    		'true' if it is opened.
+     */
+    bool isOpened();
+
+protected:
+    int playDev;
+    int recDev;
+    void *ext_snd_dev;
+};
+
+
 /*************************************************************************
 * Video media
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 5716)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_aud.c	(revision 5717)
@@ -2310,3 +2310,151 @@
 }
 
+
+/*
+ * Extra sound device
+ */
+struct pjsua_ext_snd_dev
+{
+    pj_pool_t		*pool;
+    pjmedia_port	*splitcomb;
+    pjmedia_port	*rev_port;
+    pjmedia_snd_port	*snd_port;
+    pjsua_conf_port_id	 port_id;
+};
+
+
+/*
+ * Create an extra sound device and register it to conference bridge.
+ */
+PJ_DEF(pj_status_t) pjsua_ext_snd_dev_create( pjmedia_snd_port_param *param,
+					      pjsua_ext_snd_dev **p_snd)
+{
+    pjsua_ext_snd_dev *snd = NULL;
+    pj_pool_t *pool;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(param && p_snd, PJ_EINVAL);
+
+    pool = pjsua_pool_create("extsnd%p", 512, 512);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    snd = PJ_POOL_ZALLOC_T(pool, pjsua_ext_snd_dev);
+    if (!snd) {
+	pj_pool_release(pool);
+	return PJ_ENOMEM;
+    }
+
+    snd->pool = pool;
+    snd->port_id = PJSUA_INVALID_ID;
+
+    /* Create mono splitter/combiner */
+    status = pjmedia_splitcomb_create(
+				    pool, 
+				    param->base.clock_rate,
+				    param->base.channel_count,
+				    param->base.samples_per_frame,
+				    param->base.bits_per_sample,
+				    0,	/* options */
+				    &snd->splitcomb);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create reverse channel */
+    status = pjmedia_splitcomb_create_rev_channel(
+				    pool,
+				    snd->splitcomb,
+				    0	/* channel #1 */,
+				    0	/* options */,
+				    &snd->rev_port);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* And register it to conference bridge */
+    status = pjsua_conf_add_port(pool, snd->rev_port, &snd->port_id);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create sound device */
+    status = pjmedia_snd_port_create2(pool, param, &snd->snd_port);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Connect the splitter to the sound device */
+    status = pjmedia_snd_port_connect(snd->snd_port, snd->splitcomb);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Finally */
+    *p_snd = snd;
+    PJ_LOG(4,(THIS_FILE, "Extra sound device created"));
+
+on_return:
+    if (status != PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "Failed creating extra sound device"));
+	pjsua_ext_snd_dev_destroy(snd);
+    }
+
+    return status;
+}
+
+
+/*
+ * Destroy an extra sound device and unregister it from conference bridge.
+ */
+PJ_DEF(pj_status_t) pjsua_ext_snd_dev_destroy(pjsua_ext_snd_dev *snd)
+{
+    PJ_ASSERT_RETURN(snd, PJ_EINVAL);
+
+    /* Unregister from the conference bridge */
+    if (snd->port_id != PJSUA_INVALID_ID) {
+	pjsua_conf_remove_port(snd->port_id);
+	snd->port_id = PJSUA_INVALID_ID;
+    }
+
+    /* Destroy all components */
+    if (snd->snd_port) {
+	pjmedia_snd_port_disconnect(snd->snd_port);
+	pjmedia_snd_port_destroy(snd->snd_port);
+	snd->snd_port = NULL;
+    }
+    if (snd->rev_port) {
+	pjmedia_port_destroy(snd->rev_port);
+	snd->rev_port = NULL;
+    }
+    if (snd->splitcomb) {
+	pjmedia_port_destroy(snd->splitcomb);
+	snd->splitcomb = NULL;
+    }
+
+    /* Finally */
+    pj_pool_safe_release(&snd->pool);
+
+    PJ_LOG(4,(THIS_FILE, "Extra sound device destroyed"));
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get sound port instance of an extra sound device.
+ */
+PJ_DEF(pjmedia_snd_port*) pjsua_ext_snd_dev_get_snd_port(
+					    pjsua_ext_snd_dev *snd)
+{
+    PJ_ASSERT_RETURN(snd, NULL);
+    return snd->snd_port;
+}
+
+/*
+ * Get conference port ID of an extra sound device.
+ */
+PJ_DEF(pjsua_conf_port_id) pjsua_ext_snd_dev_get_conf_port(
+					    pjsua_ext_snd_dev *snd)
+{
+    PJ_ASSERT_RETURN(snd, PJSUA_INVALID_ID);
+    return snd->port_id;
+}
+
+
 #endif /* PJSUA_MEDIA_HAS_PJMEDIA */
Index: /pjproject/trunk/pjsip/src/pjsua2/media.cpp
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5716)
+++ /pjproject/trunk/pjsip/src/pjsua2/media.cpp	(revision 5717)
@@ -1029,4 +1029,74 @@
 }
 
+
+///////////////////////////////////////////////////////////////////////////////
+ExtraAudioDevice::ExtraAudioDevice (int playdev, int recdev) :
+    playDev(playdev), recDev(recdev), ext_snd_dev(NULL)
+{ 
+}
+
+ExtraAudioDevice::~ExtraAudioDevice()
+{
+    close();
+}
+
+void ExtraAudioDevice::open()
+{
+    pj_status_t status;
+
+    /* Opened already? */
+    if (isOpened())
+	return;
+
+    /* Get port info of conference bridge master port */
+    pjsua_conf_port_info master_info;
+    status = pjsua_conf_get_port_info(0, &master_info);
+    PJSUA2_CHECK_RAISE_ERROR(status);
+
+    /* Generate sound device port param */
+    pjmedia_snd_port_param param;
+    pjmedia_snd_port_param_default(&param);
+    
+    status = pjmedia_aud_dev_default_param(recDev, &param.base);
+    PJSUA2_CHECK_RAISE_ERROR(status);
+
+    param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+    param.base.play_id = playDev;
+    param.base.rec_id = recDev;
+    param.base.clock_rate = master_info.clock_rate;
+    param.base.channel_count = master_info.channel_count;
+    param.base.samples_per_frame = master_info.samples_per_frame;
+    param.base.bits_per_sample = master_info.bits_per_sample;
+
+    /* Create the extra sound device */
+    pjsua_ext_snd_dev *snd_dev;
+    status = pjsua_ext_snd_dev_create(&param, &snd_dev);
+    PJSUA2_CHECK_RAISE_ERROR(status);
+    ext_snd_dev = snd_dev;
+
+    /* Register to the conference bridge */
+    registerMediaPort(NULL);
+    id = pjsua_ext_snd_dev_get_conf_port(snd_dev);
+}
+
+bool ExtraAudioDevice::isOpened()
+{
+    return (id != PJSUA_INVALID_ID);
+}
+
+void ExtraAudioDevice::close()
+{
+    /* Unregister from the conference bridge */
+    id = PJSUA_INVALID_ID;
+    unregisterMediaPort();
+
+    /* Destroy the extra sound device */
+    if (ext_snd_dev) {
+	pjsua_ext_snd_dev *snd_dev = (pjsua_ext_snd_dev*)ext_snd_dev;
+	ext_snd_dev = NULL;
+	pjsua_ext_snd_dev_destroy(snd_dev);
+    }	
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 VideoWindow::VideoWindow(pjsua_vid_win_id win_id)
