Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/pjsua2.i	(revision 4668)
@@ -75,4 +75,5 @@
 %template(AudioMediaVector)		std::vector<pj::AudioMedia*>;
 %template(MediaFormatVector)		std::vector<pj::MediaFormat*>;
+%template(AudioDevInfoVector)	std::vector<AudioDevInfo*>;
 
 %include "pjsua2/media.hpp"
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.i	(revision 4668)
@@ -46,4 +46,8 @@
 
 enum pjmedia_vid_dev_std_index {PJMEDIA_VID_DEFAULT_CAPTURE_DEV = -1, PJMEDIA_VID_DEFAULT_RENDER_DEV = -2, PJMEDIA_VID_INVALID_DEV = -3};
+
+typedef enum pjmedia_aud_dev_route {PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0, PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1, PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2, PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4} pjmedia_aud_dev_route;
+
+typedef enum pjmedia_aud_dev_cap {PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16, PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32, PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256, PJMEDIA_AUD_DEV_CAP_EC = 512, PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024, PJMEDIA_AUD_DEV_CAP_VAD = 2048, PJMEDIA_AUD_DEV_CAP_CNG = 4096, PJMEDIA_AUD_DEV_CAP_PLC = 8192, PJMEDIA_AUD_DEV_CAP_MAX = 16384} pjmedia_aud_dev_cap;
 
 enum pjmedia_file_writer_option {PJMEDIA_FILE_WRITE_PCM = 0, PJMEDIA_FILE_WRITE_ALAW = 1, PJMEDIA_FILE_WRITE_ULAW = 2};
Index: /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip-apps/src/swig/symbols.lst	(revision 4668)
@@ -12,4 +12,5 @@
 pjmedia/vid_stream.h		pjmedia_vid_stream_rc_method
 pjmedia-videodev/videodev.h	pjmedia_vid_dev_index pjmedia_vid_dev_std_index
+pjmedia-audiodev/audiodev.h 	pjmedia_aud_dev_route pjmedia_aud_dev_cap
 pjmedia/wav_port.h              pjmedia_file_writer_option pjmedia_file_player_option
 pjmedia/types.h                 pjmedia_type pjmedia_dir pjmedia_tp_proto
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/endpoint.hpp	(revision 4668)
@@ -1005,4 +1005,6 @@
     const AudioMediaVector &mediaEnumPorts() const throw(Error);
 
+    AudDevManager &audDevManager();
+
 public:
     /*
@@ -1072,4 +1074,5 @@
     LogWriter			*writer;	// Custom writer, if any
     AudioMediaVector 	 	 mediaList;
+    AudDevManager		 audioDevMgr;
 
     /* Endpoint static callbacks */
@@ -1193,4 +1196,5 @@
                               pjmedia_transport *base_tp,
                               unsigned flags);
+
 };
 
Index: /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/media.hpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/media.hpp	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip/include/pjsua2/media.hpp	(revision 4668)
@@ -72,4 +72,14 @@
     pj_uint32_t	avgBps;		/**< Average bitrate			*/
     pj_uint32_t	maxBps;		/**< Maximum bitrate			*/
+
+    /**
+     * Construct from pjmedia_format.
+     */
+    void fromPj(const pjmedia_format &format);
+
+    /**
+     * Export to pjmedia_format.
+     */
+    pjmedia_format toPj() const;
 };
 
@@ -90,5 +100,4 @@
 typedef std::vector<MediaFormat*> MediaFormatVector;
 
-
 /**
  * This structure descibes information about a particular media port that
@@ -384,4 +393,665 @@
 };
 
+/*************************************************************************
+* Sound device management
+*/
+
+/**
+ * Audio device information structure.
+ */
+struct AudioDevInfo
+{
+    /**
+     * The device name
+     */
+    string name;
+
+    /**
+     * Maximum number of input channels supported by this device. If the
+     * value is zero, the device does not support input operation (i.e.
+     * it is a playback only device).
+     */
+    unsigned inputCount;
+
+    /**
+     * Maximum number of output channels supported by this device. If the
+     * value is zero, the device does not support output operation (i.e.
+     * it is an input only device).
+     */
+    unsigned outputCount;
+
+    /**
+     * Default sampling rate.
+     */
+    unsigned defaultSamplesPerSec;
+
+    /**
+     * The underlying driver name
+     */
+    string driver;
+
+    /**
+     * Device capabilities, as bitmask combination of #pjmedia_aud_dev_cap.
+     */
+    unsigned caps;
+
+    /**
+     * Supported audio device routes, as bitmask combination of
+     * #pjmedia_aud_dev_route. The value may be zero if the device
+     * does not support audio routing.
+     */
+    unsigned routes;
+
+    /**
+     * Array of supported extended audio formats
+     */
+    MediaFormatVector extFmt;
+
+    /**
+     * Construct from pjmedia_aud_dev_info.
+     */
+    void fromPj(const pjmedia_aud_dev_info &dev_info);
+
+    /**
+     * Destructor.
+     */
+    ~AudioDevInfo();
+};
+
+/** Array of audio device info */
+typedef std::vector<AudioDevInfo*> AudioDevInfoVector;
+
+/**
+ * Audio device manager.
+ */
+class AudDevManager
+{
+public:
+    /**
+     * Get currently active capture sound devices. If sound devices has not been
+     * created, it is possible that the function returns -1 as device IDs.
+     *
+     * @return 	Device ID of the capture device.
+     */
+    int getCaptureDev() const throw(Error);
+
+    /**
+     * Get currently active playback sound devices. If sound devices has not
+     * been created, it is possible that the function returns -1 as device IDs.
+     *
+     * @return 	Device ID of the playback device.
+     */
+    int getPlaybackDev() const throw(Error);
+
+    /**
+     * Select or change capture sound device. Application may call this
+     * function at any time to replace current sound device.
+     *
+     * @param capture dev   	Device ID of the capture device.
+     */
+    void setCaptureDev(int capture_dev) const throw(Error);
+
+    /**
+     * Select or change playback sound device. Application may call this
+     * function at any time to replace current sound device.
+     *
+     * @param playback_dev   	Device ID of the playback device.
+     */
+    void setPlaybackDev(int playback_dev) const throw(Error);
+
+    /**
+     * Enum all audio devices installed in the system.
+     *
+     * @return		The list of audio device info.
+     */
+    const AudioDevInfoVector &enumDev() throw(Error);
+
+    /**
+     * Set pjsua to use null sound device. The null sound device only provides
+     * the timing needed by the conference bridge, and will not interract with
+     * any hardware.
+     *
+     */
+    void setNullDev() throw(Error);
+
+    /**
+     * Disconnect the main conference bridge from any sound devices, and let
+     * application connect the bridge to it's own sound device/master port.
+     *
+     * @return		The port interface of the conference bridge,
+     *			so that application can connect this to it's own
+     *			sound device or master port.
+     */
+    MediaPort *setNoDev();
+
+    /**
+     * Change the echo cancellation settings.
+     *
+     * The behavior of this function depends on whether the sound device is
+     * currently active, and if it is, whether device or software AEC is
+     * being used.
+     *
+     * If the sound device is currently active, and if the device supports AEC,
+     * this function will forward the change request to the device and it will
+     * be up to the device on whether support the request. If software AEC is
+     * being used (the software EC will be used if the device does not support
+     * AEC), this function will change the software EC settings. In all cases,
+     * the setting will be saved for future opening of the sound device.
+     *
+     * If the sound device is not currently active, this will only change the
+     * default AEC settings and the setting will be applied next time the
+     * sound device is opened.
+     *
+     * @param tail_msec		The tail length, in miliseconds. Set to zero to
+     *				disable AEC.
+     * @param options		Options to be passed to pjmedia_echo_create().
+     *				Normally the value should be zero.
+     *
+     */
+    void setEcOptions(unsigned tail_msec, unsigned options) throw(Error);
+
+    /**
+     * Get current echo canceller tail length.
+     *
+     * @return		The EC tail length in milliseconds,
+     *			If AEC is disabled, the value will be zero.
+     */
+    unsigned getEcTail() const throw(Error);
+
+    /**
+     * Check whether the sound device is currently active. The sound device
+     * may be inactive if the application has set the auto close feature to
+     * non-zero (the sndAutoCloseTime setting in #MediaConfig), or
+     * if null sound device or no sound device has been configured via the
+     * #setNoDev() function.
+     */
+    bool sndIsActive() const;
+
+    /**
+     * Refresh the list of sound devices installed in the system. This method
+     * will only refresh the list of audio device so all active audio streams
+     * will be unaffected. After refreshing the device list, application MUST
+     * make sure to update all index references to audio devices before calling
+     * any method that accepts audio device index as its parameter.
+     *
+     */
+    void refreshDevs() throw(Error);
+
+    /**
+     * Get the number of sound devices installed in the system.
+     *
+     * @return 	The number of sound devices installed in the system.
+     *
+     */
+    unsigned getDevCount() const;
+
+    /**
+     * Get device information.
+     *
+     * @param id		The audio device ID.
+     *
+     * @return			The device information which will be filled in
+     * 				by this method once it returns successfully.
+     */
+    AudioDevInfo getDevInfo(int id) const throw(Error);
+
+    /**
+     * Lookup device index based on the driver and device name.
+     *
+     * @param drv_name	The driver name.
+     * @param dev_name	The device name.
+     *
+     * @return		The device ID. If the device is not found, Error will be
+     * 			thrown.
+     *
+     */
+    int lookupDev(const string &drv_name,
+		  const string &dev_name) const throw(Error);
+
+    /**
+     * Get string info for the specified capability.
+     *
+     * @param cap		The capability ID.
+     *
+     * @return			Capability name.
+     */
+    string capName(pjmedia_aud_dev_cap cap) const;
+
+    /**
+     * This will configure audio format capability (other than PCM) to the
+     * sound device being used. If sound device is currently active, the method
+     * will forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param format	The audio format.
+     * @param keep	Specify whether the setting is to be kept for
+     * 			future use.
+     *
+     */
+    void
+    setExtFormat(const MediaFormatAudio &format, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio format capability (other than PCM) of the sound device
+     * being used. If sound device is currently active, the method will forward
+     * the request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_EXT_FORMAT capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    The audio format.
+     *
+     */
+    MediaFormatAudio getExtFormat() const throw(Error);
+
+    /**
+     * This will configure audio input latency control or query capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance to be
+     * applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param latency_msec	    The input latency.
+     * @param keep		    Specify whether the setting is to be kept
+     *				    for future use.
+     *
+     */
+    void
+    setInputLatency(unsigned latency_msec, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input latency control or query capability of the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark the
+     * setting as kept, then that setting will be returned. Otherwise, this
+     * method will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    The audio input latency.
+     *
+     */
+    unsigned getInputLatency() const throw(Error);
+
+    /**
+     * This will configure audio output latency control or query capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance to be
+     * applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param latency_msec    	The output latency.
+     * @param keep		Specify whether the setting is to be kept
+     * 				for future use.
+     *
+     */
+    void
+    setOutputLatency(unsigned latency_msec, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output latency control or query capability of the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark the
+     * setting as kept, then that setting will be returned. Otherwise, this
+     * method will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY capability in AudioDevInfo.caps flags,
+     * otherwise Error will be thrown.
+     *
+     * @return	    The audio output latency.
+     *
+     */
+    unsigned getOutputLatency() const throw(Error);
+
+    /**
+     * This will configure audio input volume level capability to the
+     * sound device being used.
+     * If sound device is currently active, the method will forward the
+     * setting to the sound device instance to be applied immediately,
+     * if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param volume	The input volume level, in percent.
+     * @param keep	Specify whether the setting is to be kept for future
+     * 			use.
+     *
+     */
+    void setInputVolume(unsigned volume, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input volume level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.     *
+
+     * @return	    The audio input volume level, in percent.
+     *
+     */
+    unsigned getInputVolume() const throw(Error);
+
+    /**
+     * This will configure audio output volume level capability to the sound
+     * device being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param volume	The output volume level, in percent.
+     * @param keep	Specify whether the setting is to be kept
+     * 			for future use.
+     *
+     */
+    void setOutputVolume(unsigned volume, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output volume level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio output volume level, in percent.
+     *
+     */
+    unsigned getOutputVolume() const throw(Error);
+
+    /**
+     * Get the audio input signal level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio input signal level, in percent.
+     *
+     */
+    unsigned getInputSignal() const throw(Error);
+
+    /**
+     * Get the audio output signal level capability of the sound device being
+     * used. If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio output signal level, in percent.
+     *
+     */
+    unsigned getOutputSignal() const throw(Error);
+
+    /**
+     * This will configure audio input route capability to the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param route	The audio input route.
+     * @param keep	Specify whether the setting is to be kept
+     * 			for future use.
+     *
+     */
+    void
+    setInputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio input route capability of the sound device being used.
+     * If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio input route.
+     *
+     */
+    pjmedia_aud_dev_route getInputRoute() const throw(Error);
+
+    /**
+     * This will configure audio output route capability to the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the setting to the sound device instance to be applied
+     * immediately, if it supports it.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param route	The audio output route.
+     * @param keep	Specify whether the setting is to be kept
+     * 			for future use.
+     *
+     */
+    void
+    setOutputRoute(pjmedia_aud_dev_route route, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio output route capability of the sound device being used.
+     * If sound device is currently active, the method will forward the
+     * request to the sound device. If sound device is currently inactive,
+     * and if application had previously set the setting and mark the setting
+     * as kept, then that setting will be returned. Otherwise, this method
+     * will raise error.
+     *
+     * This method is only valid if the device has
+     * PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE capability in AudioDevInfo.caps
+     * flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio output route.
+     *
+     */
+    pjmedia_aud_dev_route getOutputRoute() const throw(Error);
+
+    /**
+     * This will configure audio voice activity detection capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable voice activity detection
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setVad(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio voice activity detection capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_VAD
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio voice activity detection feature.
+     *
+     */
+    bool getVad() const throw(Error);
+
+    /**
+     * This will configure audio comfort noise generation capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable comfort noise generation
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setCng(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio comfort noise generation capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_CNG
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio comfort noise generation feature.
+     *
+     */
+    bool getCng() const throw(Error);
+
+    /**
+     * This will configure audio packet loss concealment capability to
+     * the sound device being used. If sound device is currently active,
+     * the method will forward the setting to the sound device instance
+     * to be applied immediately, if it supports it.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * Note that in case the setting is kept for future use, it will be applied
+     * to any devices, even when application has changed the sound device to be
+     * used.
+     *
+     * @param enable		Enable/disable packet loss concealment
+     *				feature. Set true to enable.
+     * @param keep		Specify whether the setting is to be kept for
+     *				future use.
+     *
+     */
+    void setPlc(bool enable, bool keep=true) throw(Error);
+
+    /**
+     * Get the audio packet loss concealment capability of the sound device
+     * being used. If sound device is currently active, the method will
+     * forward the request to the sound device. If sound device is currently
+     * inactive, and if application had previously set the setting and mark
+     * the setting as kept, then that setting will be returned. Otherwise,
+     * this method will raise error.
+     *
+     * This method is only valid if the device has PJMEDIA_AUD_DEV_CAP_PLC
+     * capability in AudioDevInfo.caps flags, otherwise Error will be thrown.
+     *
+     * @return	    The audio packet loss concealment feature.
+     *
+     */
+    bool getPlc() const throw(Error);
+
+private:
+    AudioDevInfoVector		 audioDevList;
+
+    /**
+     * Constructor.
+     */
+    AudDevManager();
+
+    /**
+     * Destructor.
+     */
+    ~AudDevManager();
+
+    void clearAudioDevList();
+    int getActiveDev(bool is_capture) const throw(Error);
+
+    friend class Endpoint;
+};
+
 } // namespace pj
 
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/endpoint.cpp	(revision 4668)
@@ -1360,2 +1360,7 @@
     return (it != mediaList.end());
 }
+
+AudDevManager &Endpoint::audDevManager()
+{
+    return audioDevMgr;
+}
Index: /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/media.cpp
===================================================================
--- /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/media.cpp	(revision 4667)
+++ /pjproject/branches/projects/pjsua2/pjsip/src/pjsua2/media.cpp	(revision 4668)
@@ -28,4 +28,45 @@
 #define THIS_FILE		"media.cpp"
 #define MAX_FILE_NAMES 		64
+#define MAX_DEV_COUNT		64
+
+///////////////////////////////////////////////////////////////////////////////
+void MediaFormatAudio::fromPj(const pjmedia_format &format)
+{
+    if ((format.type != PJMEDIA_TYPE_AUDIO) &&
+	(format.detail_type != PJMEDIA_FORMAT_DETAIL_AUDIO))
+    {
+	type = PJMEDIA_TYPE_UNKNOWN;
+	return;
+    }
+
+    id = format.id;
+    type = format.type;
+
+    /* Detail. */
+    clockRate = format.det.aud.clock_rate;
+    channelCount = format.det.aud.channel_count;
+    frameTimeUsec = format.det.aud.frame_time_usec;
+    bitsPerSample = format.det.aud.bits_per_sample;
+    avgBps = format.det.aud.avg_bps;
+    maxBps = format.det.aud.max_bps;
+}
+
+pjmedia_format MediaFormatAudio::toPj() const
+{
+    pjmedia_format pj_format;
+
+    pj_format.id = id;
+    pj_format.type = type;
+
+    pj_format.detail_type = PJMEDIA_FORMAT_DETAIL_AUDIO;
+    pj_format.det.aud.clock_rate = clockRate;
+    pj_format.det.aud.channel_count = channelCount;
+    pj_format.det.aud.frame_time_usec = frameTimeUsec;
+    pj_format.det.aud.bits_per_sample = bitsPerSample;
+    pj_format.det.aud.avg_bps = avgBps;
+    pj_format.det.aud.max_bps = maxBps;
+
+    return pj_format;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -254,5 +295,4 @@
 
 ///////////////////////////////////////////////////////////////////////////////
-
 AudioMediaRecorder::AudioMediaRecorder()
 : recorderId(PJSUA_INVALID_ID)
@@ -287,3 +327,379 @@
     registerMediaPort(NULL);
 }
-
+///////////////////////////////////////////////////////////////////////////////
+void AudioDevInfo::fromPj(const pjmedia_aud_dev_info &dev_info)
+{
+    name = dev_info.name;
+    inputCount = dev_info.input_count;
+    outputCount = dev_info.output_count;
+    defaultSamplesPerSec = dev_info.default_samples_per_sec;
+    driver = dev_info.driver;
+    caps = dev_info.caps;
+    routes = dev_info.routes;
+
+    for (unsigned i=0; i<dev_info.ext_fmt_cnt;++i) {
+	MediaFormatAudio *format = new MediaFormatAudio;
+
+	format->fromPj(dev_info.ext_fmt[i]);
+	if (format->type == PJMEDIA_TYPE_AUDIO)
+	    extFmt.push_back(format);
+    }
+}
+
+AudioDevInfo::~AudioDevInfo()
+{
+    for(unsigned i=0;i<extFmt.size();++i) {
+	delete extFmt[i];
+    }
+    extFmt.clear();
+}
+///////////////////////////////////////////////////////////////////////////////
+/* Audio device operations. */
+
+int AudDevManager::getCaptureDev() const throw(Error)
+{
+    return getActiveDev(true);
+}
+
+int AudDevManager::getPlaybackDev() const throw(Error)
+{
+    return getActiveDev(false);
+}
+
+void AudDevManager::setCaptureDev(int capture_dev) const throw(Error)
+{
+    int playback_dev = getPlaybackDev();
+
+    PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) );
+}
+
+void AudDevManager::setPlaybackDev(int playback_dev) const throw(Error)
+{
+    int capture_dev = getCaptureDev();
+
+    PJSUA2_CHECK_EXPR( pjsua_set_snd_dev(capture_dev, playback_dev) );
+}
+
+const AudioDevInfoVector &AudDevManager::enumDev() throw(Error)
+{
+    pjmedia_aud_dev_info pj_info[MAX_DEV_COUNT];
+    unsigned count;
+
+    clearAudioDevList();
+
+    PJSUA2_CHECK_EXPR( pjsua_enum_aud_devs(pj_info, &count) );
+
+    for (unsigned i = 0; (i<count && i<MAX_DEV_COUNT) ;++i) {
+	AudioDevInfo *dev_info = new AudioDevInfo;
+	dev_info->fromPj(pj_info[i]);
+	audioDevList.push_back(dev_info);
+    }
+    return audioDevList;
+}
+
+void AudDevManager::setNullDev() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_set_null_snd_dev() );
+}
+
+MediaPort *AudDevManager::setNoDev()
+{
+    return (MediaPort*)pjsua_set_no_snd_dev();
+}
+
+void AudDevManager::setEcOptions(unsigned tail_msec,
+				 unsigned options) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_set_ec(tail_msec, options) );
+}
+
+unsigned AudDevManager::getEcTail() const throw(Error)
+{
+    unsigned tail_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_get_ec_tail(&tail_msec) );
+
+    return tail_msec;
+}
+
+bool AudDevManager::sndIsActive() const
+{
+    return pjsua_snd_is_active();
+}
+
+void AudDevManager::refreshDevs() throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_refresh() );
+}
+
+unsigned AudDevManager::getDevCount() const
+{
+    return pjmedia_aud_dev_count();
+}
+
+AudioDevInfo
+AudDevManager::getDevInfo(int id) const throw(Error)
+{
+    AudioDevInfo dev_info;
+    pjmedia_aud_dev_info pj_info;
+
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_get_info(id, &pj_info) );
+
+    dev_info.fromPj(pj_info);
+    return dev_info;
+}
+
+int AudDevManager::lookupDev(const string &drv_name,
+			     const string &dev_name) const throw(Error)
+{
+    pjmedia_aud_dev_index pj_idx = 0;
+
+    PJSUA2_CHECK_EXPR( pjmedia_aud_dev_lookup(drv_name.c_str(),
+					      dev_name.c_str(),
+					      &pj_idx) );
+
+    return pj_idx;
+}
+
+
+string AudDevManager::capName(pjmedia_aud_dev_cap cap) const
+{
+    return pjmedia_aud_dev_cap_name(cap, NULL);
+}
+
+void
+AudDevManager::setExtFormat(const MediaFormatAudio &format,
+			    bool keep) throw(Error)
+{
+    pjmedia_format pj_format = format.toPj();
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT,
+					     &pj_format,
+					     keep) );
+}
+
+MediaFormatAudio AudDevManager::getExtFormat() const throw(Error)
+{
+    pjmedia_format pj_format;
+    MediaFormatAudio format;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT,
+					     &pj_format) );
+
+    format.fromPj(pj_format);
+
+    return format;
+}
+
+void AudDevManager::setInputLatency(unsigned latency_msec,
+				    bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+					     &latency_msec,
+					     keep) );
+}
+
+unsigned AudDevManager::getInputLatency() const throw(Error)
+{
+    unsigned latency_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
+					     &latency_msec) );
+
+    return latency_msec;
+}
+
+void
+AudDevManager::setOutputLatency(unsigned latency_msec,
+				bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+					     &latency_msec,
+					     keep) );
+}
+
+unsigned AudDevManager::getOutputLatency() const throw(Error)
+{
+    unsigned latency_msec = 0;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
+					     &latency_msec) );
+
+    return latency_msec;
+}
+
+void AudDevManager::setInputVolume(unsigned volume, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+				  &volume,
+				  keep) );
+}
+
+unsigned AudDevManager::getInputVolume() const throw(Error)
+{
+    unsigned volume = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+				  &volume) );
+
+    return volume;
+}
+
+void AudDevManager::setOutputVolume(unsigned volume, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+				  &volume,
+				  keep) );
+}
+
+unsigned AudDevManager::getOutputVolume() const throw(Error)
+{
+    unsigned volume = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+				  &volume) );
+
+    return volume;
+}
+
+unsigned AudDevManager::getInputSignal() const throw(Error)
+{
+    unsigned signal = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER,
+				  &signal) );
+
+    return signal;
+}
+
+unsigned AudDevManager::getOutputSignal() const throw(Error)
+{
+    unsigned signal = 0;
+
+    PJSUA2_CHECK_EXPR(
+	    pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER,
+				  &signal) );
+
+    return signal;
+}
+
+void
+AudDevManager::setInputRoute(pjmedia_aud_dev_route route,
+			     bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+					     &route,
+					     keep) );
+}
+
+pjmedia_aud_dev_route AudDevManager::getInputRoute() const throw(Error)
+{
+    pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
+					     &route) );
+
+    return route;
+}
+
+void
+AudDevManager::setOutputRoute(pjmedia_aud_dev_route route,
+			      bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+					     &route,
+					     keep) );
+}
+
+pjmedia_aud_dev_route AudDevManager::getOutputRoute() const throw(Error)
+{
+    pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
+					     &route) );
+
+    return route;
+}
+
+void AudDevManager::setVad(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_VAD,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getVad() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_VAD,
+					     &enable) );
+
+    return enable;
+}
+
+void AudDevManager::setCng(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_CNG,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getCng() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_CNG,
+					     &enable) );
+
+    return enable;
+}
+
+void AudDevManager::setPlc(bool enable, bool keep) throw(Error)
+{
+    PJSUA2_CHECK_EXPR( pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_PLC,
+					     &enable,
+					     keep) );
+}
+
+bool AudDevManager::getPlc() const throw(Error)
+{
+    bool enable = false;
+
+    PJSUA2_CHECK_EXPR( pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_PLC,
+					     &enable) );
+
+    return enable;
+}
+
+AudDevManager::AudDevManager()
+{
+
+
+}
+
+AudDevManager::~AudDevManager()
+{
+    clearAudioDevList();
+}
+
+void AudDevManager::clearAudioDevList()
+{
+    for(unsigned i=0;i<audioDevList.size();++i) {
+	delete audioDevList[i];
+    }
+    audioDevList.clear();
+}
+
+int AudDevManager::getActiveDev(bool is_capture) const throw(Error)
+{
+    int capture_dev = 0, playback_dev = 0;
+    PJSUA2_CHECK_EXPR( pjsua_get_snd_dev(&capture_dev, &playback_dev) );
+
+    return is_capture?capture_dev:playback_dev;
+}
