* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/80/807803c4678028b3cec3005f751515ebf1d0fd87.svn-base b/jni/pjproject-android/.svn/pristine/80/807803c4678028b3cec3005f751515ebf1d0fd87.svn-base
new file mode 100644
index 0000000..6dc5efe
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/80/807803c4678028b3cec3005f751515ebf1d0fd87.svn-base
@@ -0,0 +1,2946 @@
+# $Id$
+#
+# Object oriented PJSUA wrapper.
+#
+# Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+#
+
+"""Multimedia communication client library based on SIP protocol.
+
+This implements a fully featured multimedia communication client 
+library based on PJSIP stack (http://www.pjsip.org)
+
+
+1. FEATURES
+
+  - Session Initiation Protocol (SIP) features:
+     - Basic registration and call
+     - Multiple accounts
+     - Call hold, attended and unattended call transfer
+     - Presence
+     - Instant messaging
+     - Multiple SIP accounts
+  - Media features:
+     - Audio
+     - Conferencing
+     - Narrowband and wideband
+     - Codecs: PCMA, PCMU, GSM, iLBC, Speex, G.722, L16
+     - RTP/RTCP
+     - Secure RTP (SRTP)
+     - WAV playback, recording, and playlist
+  - NAT traversal features
+     - Symmetric RTP
+     - STUN
+     - TURN
+     - ICE
+ 
+
+2. USING
+
+See http://www.pjsip.org/trac/wiki/Python_SIP_Tutorial for a more thorough
+tutorial. The paragraphs below explain basic tasks on using this module.
+
+
+"""
+import _pjsua
+import thread
+import threading
+import weakref
+import time
+
+class Error:
+    """Error exception class.
+    
+    Member documentation:
+
+    op_name  -- name of the operation that generated this error.
+    obj      -- the object that generated this error.
+    err_code -- the error code.
+
+    """
+    op_name = ""
+    obj = None
+    err_code = -1
+    _err_msg = ""
+
+    def __init__(self, op_name, obj, err_code, err_msg=""):
+        self.op_name = op_name
+        self.obj = obj
+        self.err_code = err_code
+        self._err_msg = err_msg
+
+    def err_msg(self):
+        "Retrieve the description of the error."
+        if self._err_msg != "":
+            return self._err_msg
+        self._err_msg = Lib.strerror(self.err_code)
+        return self._err_msg
+
+    def __str__(self):
+        return "Object: " + str(self.obj) + ", operation=" + self.op_name + \
+               ", error=" + self.err_msg()
+
+# 
+# Constants
+#
+
+class TransportType:
+    """SIP transport type constants.
+    
+    Member documentation:
+    UNSPECIFIED -- transport type is unknown or unspecified
+    UDP         -- UDP transport
+    TCP         -- TCP transport
+    TLS         -- TLS transport
+    IPV6        -- this is not a transport type but rather a flag
+                   to select the IPv6 version of a transport
+    UDP_IPV6    -- IPv6 UDP transport
+    TCP_IPV6    -- IPv6 TCP transport
+    """
+    UNSPECIFIED = 0
+    UDP = 1
+    TCP = 2
+    TLS = 3
+    IPV6 = 128
+    UDP_IPV6 = UDP + IPV6
+    TCP_IPV6 = TCP + IPV6
+
+class TransportFlag:
+    """Transport flags to indicate the characteristics of the transport.
+    
+    Member documentation:
+    
+    RELIABLE    -- transport is reliable.
+    SECURE      -- transport is secure.
+    DATAGRAM    -- transport is datagram based.
+    
+    """
+    RELIABLE = 1
+    SECURE = 2
+    DATAGRAM = 4
+
+class CallRole:
+    """Call role constants.
+    
+    Member documentation:
+
+    CALLER  -- role is caller
+    CALLEE  -- role is callee
+
+    """
+    CALLER = 0
+    CALLEE = 1
+
+class CallState:
+    """Call state constants.
+    
+    Member documentation:
+
+    NULL            -- call is not initialized.
+    CALLING         -- initial INVITE is sent.
+    INCOMING        -- initial INVITE is received.
+    EARLY           -- provisional response has been sent or received.
+    CONNECTING      -- 200/OK response has been sent or received.
+    CONFIRMED       -- ACK has been sent or received.
+    DISCONNECTED    -- call is disconnected.
+    """
+    NULL = 0
+    CALLING = 1
+    INCOMING = 2
+    EARLY = 3
+    CONNECTING = 4
+    CONFIRMED = 5
+    DISCONNECTED = 6
+
+
+class MediaState:
+    """Call media state constants.
+    
+    Member documentation:
+
+    NULL        -- media is not available.
+    ACTIVE      -- media is active.
+    LOCAL_HOLD  -- media is put on-hold by local party.
+    REMOTE_HOLD -- media is put on-hold by remote party.
+    ERROR       -- media error (e.g. ICE negotiation failure).
+    """
+    NULL = 0
+    ACTIVE = 1
+    LOCAL_HOLD = 2
+    REMOTE_HOLD = 3
+    ERROR = 4
+
+
+class MediaDir:
+    """Media direction constants.
+    
+    Member documentation:
+
+    NULL              -- media is not active
+    ENCODING          -- media is active in transmit/encoding direction only.
+    DECODING          -- media is active in receive/decoding direction only
+    ENCODING_DECODING -- media is active in both directions.
+    """
+    NULL = 0
+    ENCODING = 1
+    DECODING = 2
+    ENCODING_DECODING = 3
+
+
+class PresenceActivity:
+    """Presence activities constants.
+    
+    Member documentation:
+
+    UNKNOWN -- the person activity is unknown
+    AWAY    -- the person is currently away
+    BUSY    -- the person is currently engaging in other activity
+    """
+    UNKNOWN = 0
+    AWAY = 1
+    BUSY = 2
+
+
+class SubscriptionState:
+    """Presence subscription state constants.
+
+    """
+    NULL = 0
+    SENT = 1
+    ACCEPTED = 2
+    PENDING = 3
+    ACTIVE = 4
+    TERMINATED = 5
+    UNKNOWN = 6
+
+
+class TURNConnType:
+    """These constants specifies the connection type to TURN server.
+    
+    Member documentation:
+    UDP     -- use UDP transport.
+    TCP     -- use TCP transport.
+    TLS     -- use TLS transport.
+    """
+    UDP = 17
+    TCP = 6
+    TLS = 255
+
+
+class UAConfig:
+    """User agent configuration to be specified in Lib.init().
+    
+    Member documentation:
+
+    max_calls   -- maximum number of calls to be supported.
+    nameserver  -- list of nameserver hostnames or IP addresses. Nameserver
+                   must be configured if DNS SRV resolution is desired.
+    stun_domain -- if nameserver is configured, this can be used to query
+                   the STUN server with DNS SRV.
+    stun_host   -- the hostname or IP address of the STUN server. This will
+                   also be used if DNS SRV resolution for stun_domain fails.
+    user_agent  -- Optionally specify the user agent name.
+    """
+    max_calls = 4
+    nameserver = []
+    stun_domain = ""
+    stun_host = ""
+    user_agent = "pjsip python"
+    
+    def _cvt_from_pjsua(self, cfg):
+        self.max_calls = cfg.max_calls
+        self.thread_cnt = cfg.thread_cnt
+        self.nameserver = cfg.nameserver
+        self.stun_domain = cfg.stun_domain
+        self.stun_host = cfg.stun_host
+        self.user_agent = cfg.user_agent
+
+    def _cvt_to_pjsua(self):
+        cfg = _pjsua.config_default()
+        cfg.max_calls = self.max_calls
+        cfg.thread_cnt = 0
+        cfg.nameserver = self.nameserver
+        cfg.stun_domain = self.stun_domain
+        cfg.stun_host = self.stun_host
+        cfg.user_agent = self.user_agent
+        return cfg
+
+
+class LogConfig:
+    """Logging configuration to be specified in Lib.init().
+    
+    Member documentation:
+
+    msg_logging   -- specify if SIP messages should be logged. Set to
+                     True.
+    level         -- specify the input verbosity level.
+    console_level -- specify the output verbosity level.
+    decor         -- specify log decoration.
+    filename      -- specify the log filename.
+    callback      -- specify callback to be called to write the logging
+                     messages. Sample function:
+
+                     def log_cb(level, str, len):
+                        print str,
+
+    """
+    msg_logging = True
+    level = 5
+    console_level = 5
+    decor = 0
+    filename = ""
+    callback = None
+    
+    def __init__(self, level=-1, filename="", callback=None,
+                 console_level=-1):
+        self._cvt_from_pjsua(_pjsua.logging_config_default())
+        if level != -1:
+            self.level = level
+        if filename != "":
+            self.filename = filename
+        if callback != None:
+            self.callback = callback
+        if console_level != -1:
+            self.console_level = console_level
+
+    def _cvt_from_pjsua(self, cfg):
+        self.msg_logging = cfg.msg_logging
+        self.level = cfg.level
+        self.console_level = cfg.console_level
+        self.decor = cfg.decor
+        self.filename = cfg.log_filename
+        self.callback = cfg.cb
+
+    def _cvt_to_pjsua(self):
+        cfg = _pjsua.logging_config_default()
+        cfg.msg_logging = self.msg_logging
+        cfg.level = self.level
+        cfg.console_level = self.console_level
+        cfg.decor = self.decor
+        cfg.log_filename = self.filename
+        cfg.cb = self.callback
+        return cfg
+
+
+class MediaConfig:
+    """Media configuration to be specified in Lib.init().
+    
+    Member documentation:
+    
+    clock_rate          -- specify the core clock rate of the audio,
+                           most notably the conference bridge.
+    snd_clock_rate      -- optionally specify different clock rate for
+                           the sound device.
+    snd_auto_close_time -- specify the duration in seconds when the
+                           sound device should be closed after inactivity
+                           period.
+    channel_count       -- specify the number of channels to open the sound
+                           device and the conference bridge.
+    audio_frame_ptime   -- specify the length of audio frames in millisecond.
+    max_media_ports     -- specify maximum number of audio ports to be
+                           supported by the conference bridge.
+    quality             -- specify the audio quality setting (1-10)
+    ptime               -- specify the audio packet length of transmitted
+                           RTP packet.
+    no_vad              -- disable Voice Activity Detector (VAD) or Silence
+                           Detector (SD)
+    ilbc_mode           -- specify iLBC codec mode (must be 30 for now)
+    tx_drop_pct         -- randomly drop transmitted RTP packets (for
+                           simulation). Number is in percent.
+    rx_drop_pct         -- randomly drop received RTP packets (for
+                           simulation). Number is in percent.
+    ec_options          -- Echo Canceller option (specify zero).
+    ec_tail_len         -- specify Echo Canceller tail length in milliseconds.
+                           Value zero will disable the echo canceller.
+    jb_min              -- specify the minimum jitter buffer size in
+                           milliseconds. Put -1 for default.
+    jb_max              -- specify the maximum jitter buffer size in
+                           milliseconds. Put -1 for default.
+    enable_ice          -- enable Interactive Connectivity Establishment (ICE)
+    enable_turn         -- enable TURN relay. TURN server settings must also
+                           be configured.
+    turn_server         -- specify the domain or hostname or IP address of
+                           the TURN server, in "host[:port]" format.
+    turn_conn_type      -- specify connection type to the TURN server, from
+                           the TURNConnType constant.
+    turn_cred           -- specify AuthCred for the TURN credential.
+    """
+    clock_rate = 16000
+    snd_clock_rate = 0
+    snd_auto_close_time = 5
+    channel_count = 1
+    audio_frame_ptime = 20
+    max_media_ports = 32
+    quality = 6
+    ptime = 0
+    no_vad = False
+    ilbc_mode = 30
+    tx_drop_pct = 0
+    rx_drop_pct = 0
+    ec_options = 0
+    ec_tail_len = 256
+    jb_min = -1
+    jb_max = -1
+    enable_ice = True
+    enable_turn = False
+    turn_server = ""
+    turn_conn_type = TURNConnType.UDP
+    turn_cred = None
+     
+    def __init__(self):
+        default = _pjsua.media_config_default()
+        self._cvt_from_pjsua(default)
+
+    def _cvt_from_pjsua(self, cfg):
+        self.clock_rate = cfg.clock_rate
+        self.snd_clock_rate = cfg.snd_clock_rate
+        self.snd_auto_close_time = cfg.snd_auto_close_time
+        self.channel_count = cfg.channel_count
+        self.audio_frame_ptime = cfg.audio_frame_ptime
+        self.max_media_ports = cfg.max_media_ports
+        self.quality = cfg.quality
+        self.ptime = cfg.ptime
+        self.no_vad = cfg.no_vad
+        self.ilbc_mode = cfg.ilbc_mode
+        self.tx_drop_pct = cfg.tx_drop_pct
+        self.rx_drop_pct = cfg.rx_drop_pct
+        self.ec_options = cfg.ec_options
+        self.ec_tail_len = cfg.ec_tail_len
+        self.jb_min = cfg.jb_min
+        self.jb_max = cfg.jb_max
+        self.enable_ice = cfg.enable_ice
+        self.enable_turn = cfg.enable_turn
+        self.turn_server = cfg.turn_server
+        self.turn_conn_type = cfg.turn_conn_type
+        if cfg.turn_username:
+            self.turn_cred = AuthCred(cfg.turn_realm, cfg.turn_username,
+                                      cfg.turn_passwd, cfg.turn_passwd_type)
+        else:
+            self.turn_cred = None
+
+    def _cvt_to_pjsua(self):
+        cfg = _pjsua.media_config_default()
+        cfg.clock_rate = self.clock_rate
+        cfg.snd_clock_rate = self.snd_clock_rate
+        cfg.snd_auto_close_time = self.snd_auto_close_time
+        cfg.channel_count = self.channel_count
+        cfg.audio_frame_ptime = self.audio_frame_ptime
+        cfg.max_media_ports = self.max_media_ports
+        cfg.quality = self.quality
+        cfg.ptime = self.ptime
+        cfg.no_vad = self.no_vad
+        cfg.ilbc_mode = self.ilbc_mode
+        cfg.tx_drop_pct = self.tx_drop_pct
+        cfg.rx_drop_pct = self.rx_drop_pct
+        cfg.ec_options = self.ec_options
+        cfg.ec_tail_len = self.ec_tail_len
+        cfg.jb_min = self.jb_min
+        cfg.jb_max = self.jb_max
+        cfg.enable_ice = self.enable_ice
+        cfg.enable_turn = self.enable_turn
+        cfg.turn_server = self.turn_server
+        cfg.turn_conn_type = self.turn_conn_type
+        if self.turn_cred:
+            cfg.turn_realm = self.turn_cred.realm
+            cfg.turn_username = self.turn_cred.username
+            cfg.turn_passwd_type = self.turn_cred.passwd_type
+            cfg.turn_passwd = self.turn_cred.passwd
+        return cfg
+
+
+class TransportConfig:
+    """SIP transport configuration class.
+    
+    Member configuration:
+
+    port        -- port number.
+    bound_addr  -- optionally specify the address to bind the socket to.
+                   Default is empty to bind to INADDR_ANY.
+    public_addr -- optionally override the published address for this
+                   transport. If empty, the default behavior is to get
+                   the public address from STUN or from the selected
+                   local interface. Format is "host:port".
+    qos_type    -- High level traffic classification.
+                   Enumerator:
+                     0: PJ_QOS_TYPE_BEST_EFFORT
+                          Best effort traffic (default value). Any QoS function calls with 
+                          specifying this value are effectively no-op
+                     1: PJ_QOS_TYPE_BACKGROUND
+                          Background traffic.
+                     2: PJ_QOS_TYPE_VIDEO
+                          Video traffic.
+                     3: PJ_QOS_TYPE_VOICE
+                          Voice traffic.
+                     4: PJ_QOS_TYPE_CONTROL
+                          Control traffic.
+    qos_params_flags    -- Determines which values to set, bitmask of pj_qos_flag.
+                             PJ_QOS_PARAM_HAS_DSCP = 1
+                             PJ_QOS_PARAM_HAS_SO_PRIO = 2
+                             PJ_QOS_PARAM_HAS_WMM = 4
+    qos_params_dscp_val -- The 6 bits DSCP value to set.
+    qos_params_so_prio  -- Socket SO_PRIORITY value.
+    qos_params_wmm_prio -- Standard WMM priorities.
+                            Enumerator:
+                              0: PJ_QOS_WMM_PRIO_BULK_EFFORT: Bulk effort priority
+                              1: PJ_QOS_WMM_PRIO_BULK: Bulk priority.
+                              2: PJ_QOS_WMM_PRIO_VIDEO: Video priority
+                              3: PJ_QOS_WMM_PRIO_VOICE: Voice priority.
+    """
+    port = 0
+    bound_addr = ""
+    public_addr = ""
+    
+    qos_type = 0
+    qos_params_flags = 0
+    qos_params_dscp_val = 0
+    qos_params_so_prio = 0
+    qos_params_wmm_prio = 0
+    
+    
+
+    def __init__(self, port=0, 
+                 bound_addr="", public_addr=""):
+        self.port = port
+        self.bound_addr = bound_addr
+        self.public_addr = public_addr
+
+    def _cvt_from_pjsua(self, cfg):
+        self.port = cfg.port
+        self.bound_addr = cfg.bound_addr
+        self.public_addr = cfg.public_addr
+        self.qos_type = cfg.qos_type
+        self.qos_params_flags = cfg.qos_params_flags
+        self.qos_params_dscp_val = cfg.qos_params_dscp_val
+        self.qos_params_so_prio = cfg.qos_params_so_prio
+        self.qos_params_wmm_prio = cfg.qos_params_wmm_prio
+
+    def _cvt_to_pjsua(self):
+        cfg = _pjsua.transport_config_default()
+        cfg.port = self.port
+        cfg.bound_addr = self.bound_addr
+        cfg.public_addr = self.public_addr
+        cfg.qos_type = self.qos_type
+        cfg.qos_params_flags = self.qos_params_flags
+        cfg.qos_params_dscp_val = self.qos_params_dscp_val
+        cfg.qos_params_so_prio = self.qos_params_so_prio
+        cfg.qos_params_wmm_prio = self.qos_params_wmm_prio
+
+        return cfg
+
+
+class TransportInfo:
+    """SIP transport info.
+    
+    Member documentation:
+
+    type        -- transport type, from TransportType constants.
+    description -- longer description for this transport.
+    is_reliable -- True if transport is reliable.
+    is_secure   -- True if transport is secure.
+    is_datagram -- True if transport is datagram based.
+    host        -- the IP address of this transport.
+    port        -- the port number.
+    ref_cnt     -- number of objects referencing this transport.
+    """
+    type = ""
+    description = ""
+    is_reliable = False
+    is_secure = False
+    is_datagram = False
+    host = ""
+    port = 0
+    ref_cnt = 0
+    
+    def __init__(self, ti):
+        self.type = ti.type_name
+        self.description = ti.info
+        self.is_reliable = (ti.flag & TransportFlag.RELIABLE)
+        self.is_secure = (ti.flag & TransportFlag.SECURE)
+        self.is_datagram = (ti.flag & TransportFlag.DATAGRAM)
+        self.host = ti.addr
+        self.port = ti.port
+        self.ref_cnt = ti.usage_count
+    
+    
+class Transport:
+    "SIP transport class."
+    _id = -1
+    _lib = None
+    _obj_name = ""
+
+    def __init__(self, lib, id):
+        self._lib = weakref.proxy(lib)
+        self._id = id
+        self._obj_name = "{Transport " + self.info().description + "}"
+        _Trace((self, 'created'))
+
+    def __del__(self):
+        _Trace((self, 'destroyed'))
+        
+    def __str__(self):
+        return self._obj_name
+
+    def info(self):
+        """Get TransportInfo.
+        """
+        lck = self._lib.auto_lock()
+        ti = _pjsua.transport_get_info(self._id)
+        if not ti:
+            self._lib._err_check("info()", self, -1, "Invalid transport")
+        return TransportInfo(ti)
+
+    def enable(self):
+        """Enable this transport."""
+        lck = self._lib.auto_lock()
+        err = _pjsua.transport_set_enable(self._id, True)
+        self._lib._err_check("enable()", self, err)
+
+    def disable(self):
+        """Disable this transport."""
+        lck = self._lib.auto_lock()
+        err = _pjsua.transport_set_enable(self._id, 0)
+        self._lib._err_check("disable()", self, err)
+
+    def close(self, force=False):
+        """Close and destroy this transport.
+
+        Keyword argument:
+        force   -- force deletion of this transport (not recommended).
+        """
+        lck = self._lib.auto_lock()
+        err = _pjsua.transport_close(self._id, force)
+        self._lib._err_check("close()", self, err)
+
+
+class SIPUri:
+    """Helper class to parse the most important components of SIP URI.
+
+    Member documentation:
+
+    scheme    -- URI scheme ("sip" or "sips")
+    user      -- user part of the URI (may be empty)
+    host      -- host name part
+    port      -- optional port number (zero if port is not specified).
+    transport -- transport parameter, or empty if transport is not
+                 specified.
+
+    """
+    scheme = ""
+    user = ""
+    host = ""
+    port = 0
+    transport = ""
+
+    def __init__(self, uri=None):
+        if uri:
+            self.decode(uri)
+
+    def decode(self, uri):
+        """Parse SIP URL.
+
+        Keyword argument:
+        uri -- the URI string.
+
+        """
+        self.scheme, self.user, self.host, self.port, self.transport = \
+            _pjsua.parse_simple_uri(uri)
+
+    def encode(self):
+        """Encode this object into SIP URI string.
+
+        Return:
+            URI string.
+
+        """
+        output = self.scheme + ":"
+        if self.user and len(self.user):
+            output = output + self.user + "@"
+        output = output + self.host
+        if self.port:
+            output = output + ":" + output(self.port)
+        if self.transport:
+            output = output + ";transport=" + self.transport
+        return output
+
+
+class AuthCred:
+    """Authentication credential for SIP or TURN account.
+    
+    Member documentation:
+
+    scheme      -- authentication scheme (default is "Digest")
+    realm       -- realm
+    username    -- username
+    passwd_type -- password encoding (zero for plain-text)
+    passwd      -- the password
+    """
+    scheme = "Digest"
+    realm = "*"
+    username = ""
+    passwd_type = 0
+    passwd = ""
+
+    def __init__(self, realm, username, passwd, scheme="Digest", passwd_type=0):
+        self.scheme = scheme
+        self.realm = realm
+        self.username = username
+        self.passwd_type = passwd_type
+        self.passwd = passwd
+
+
+class AccountConfig:
+    """ This describes account configuration to create an account.
+
+    Member documentation:
+
+    priority                -- account priority for matching incoming
+                               messages.
+    id                      -- SIP URI of this account. This setting is
+                               mandatory.
+    force_contact           -- force to use this URI as Contact URI. Setting
+                               this value is generally not recommended.
+    reg_uri                 -- specify the registrar URI. Mandatory if
+                               registration is required.
+    reg_timeout             -- specify the SIP registration refresh interval
+                               in seconds.
+    require_100rel          -- specify if reliable provisional response is
+                               to be enforced (with Require header).
+    publish_enabled         -- specify if PUBLISH should be used. When
+                               enabled, the PUBLISH will be sent to the
+                               registrar.
+    pidf_tuple_id           -- optionally specify the tuple ID in outgoing
+                               PIDF document.
+    proxy                   -- list of proxy URI.
+    auth_cred               -- list of AuthCred containing credentials to
+                               authenticate against the registrars and
+                               the proxies.
+    auth_initial_send       -- specify if empty Authorization header should be
+                               sent. May be needed for IMS.
+    auth_initial_algorithm  -- when auth_initial_send is enabled, optionally
+                               specify the authentication algorithm to use.
+                               Valid values are "md5", "akav1-md5", or
+                               "akav2-md5". 
+    transport_id            -- optionally specify the transport ID to be used
+                               by this account. Shouldn't be needed unless
+                               for specific requirements (e.g. in multi-homed
+                               scenario).
+    allow_contact_rewrite   -- specify whether the account should learn its
+                               Contact address from REGISTER response and 
+                               update the registration accordingly. Default is
+                               True.
+    ka_interval             -- specify the interval to send NAT keep-alive 
+                               packet.
+    ka_data                 -- specify the NAT keep-alive packet contents.
+    use_srtp                -- specify the SRTP usage policy. Valid values
+                               are: 0=disable, 1=optional, 2=mandatory.
+                               Default is 0.
+    srtp_secure_signaling   -- specify the signaling security level required
+                               by SRTP. Valid values are: 0=no secure 
+                               transport is required, 1=hop-by-hop secure
+                               transport such as TLS is required, 2=end-to-
+                               end secure transport is required (i.e. "sips").
+    rtp_transport_cfg       -- the rtp-transport-configuration that is usede, when
+                               a rtp-connection is being established.
+    """
+    priority = 0
+    id = ""
+    force_contact = ""
+    reg_uri = ""
+    reg_timeout = 0
+    require_100rel = False
+    publish_enabled = False
+    pidf_tuple_id = ""
+    proxy = []
+    auth_cred = []
+    auth_initial_send = False
+    auth_initial_algorithm = ""
+    transport_id = -1
+    allow_contact_rewrite = True
+    ka_interval = 15
+    ka_data = "\r\n"
+    use_srtp = 0
+    srtp_secure_signaling = 1
+    rtp_transport_cfg = None
+
+    def __init__(self, domain="", username="", password="", 
+                 display="", registrar="", proxy=""):
+        """
+        Construct account config. If domain argument is specified, 
+        a typical configuration will be built.
+
+        Keyword arguments:
+        domain    -- domain name of the server.
+        username  -- user name.
+        password  -- plain-text password.
+        display   -- optional display name for the user name.
+        registrar -- the registrar URI. If domain name is specified
+                     and this argument is empty, the registrar URI
+                     will be constructed from the domain name.
+        proxy     -- the proxy URI. If domain name is specified
+                     and this argument is empty, the proxy URI
+                     will be constructed from the domain name.
+
+        """
+        default = _pjsua.acc_config_default()
+        self._cvt_from_pjsua(default)
+        if domain!="":
+            self.build_config(domain, username, password,
+                              display, registrar, proxy)
+        self.rtp_transport_cfg = TransportConfig()
+
+    def build_config(self, domain, username, password, display="",
+                     registrar="", proxy="", rtp_transport_cfg = None):
+        """
+        Construct account config. If domain argument is specified, 
+        a typical configuration will be built.
+
+        Keyword arguments:
+        domain    -- domain name of the server.
+        username  -- user name.
+        password  -- plain-text password.
+        display   -- optional display name for the user name.
+        registrar -- the registrar URI. If domain name is specified
+                     and this argument is empty, the registrar URI
+                     will be constructed from the domain name.
+        proxy     -- the proxy URI. If domain name is specified
+                     and this argument is empty, the proxy URI
+                     will be constructed from the domain name.
+
+        """
+        if display != "":
+            display = display + " "
+        userpart = username
+        if userpart != "":
+            userpart = userpart + "@"
+        self.id = display + "<sip:" + userpart + domain + ">"
+        self.reg_uri = registrar
+        if self.reg_uri == "":
+            self.reg_uri = "sip:" + domain
+        if proxy == "":
+            proxy = "sip:" + domain + ";lr"
+        if proxy.find(";lr") == -1:
+            proxy = proxy + ";lr"
+        self.proxy.append(proxy)
+        if username != "":
+            self.auth_cred.append(AuthCred("*", username, password))
+        
+        if (rtp_transport_cfg is not None):
+            self.rtp_transport_cfg = rtp_transport_cfg
+        else:
+            self.rtp_transport_cfg = TransportConfig()
+    
+    def _cvt_from_pjsua(self, cfg):
+        self.priority = cfg.priority
+        self.id = cfg.id
+        self.force_contact = cfg.force_contact
+        self.reg_uri = cfg.reg_uri
+        self.reg_timeout = cfg.reg_timeout
+        self.require_100rel = cfg.require_100rel
+        self.publish_enabled = cfg.publish_enabled
+        self.pidf_tuple_id = cfg.pidf_tuple_id
+        self.proxy = cfg.proxy
+        for cred in cfg.cred_info:
+            self.auth_cred.append(AuthCred(cred.realm, cred.username, 
+                                           cred.data, cred.scheme,
+                                           cred.data_type))
+        self.auth_initial_send = cfg.auth_initial_send
+        self.auth_initial_algorithm = cfg.auth_initial_algorithm
+        self.transport_id = cfg.transport_id
+        self.allow_contact_rewrite = cfg.allow_contact_rewrite
+        self.ka_interval = cfg.ka_interval
+        self.ka_data = cfg.ka_data
+        self.use_srtp = cfg.use_srtp
+        self.srtp_secure_signaling = cfg.srtp_secure_signaling
+        if (self.rtp_transport_cfg is not None):
+            self.rtp_transport_cfg._cvt_from_pjsua(cfg.rtp_transport_cfg)
+
+    def _cvt_to_pjsua(self):
+        cfg = _pjsua.acc_config_default()
+        cfg.priority = self.priority
+        cfg.id = self.id
+        cfg.force_contact = self.force_contact
+        cfg.reg_uri = self.reg_uri
+        cfg.reg_timeout = self.reg_timeout
+        cfg.require_100rel = self.require_100rel
+        cfg.publish_enabled = self.publish_enabled
+        cfg.pidf_tuple_id = self.pidf_tuple_id
+        cfg.proxy = self.proxy
+        for cred in self.auth_cred:
+            c = _pjsua.Pjsip_Cred_Info()
+            c.realm = cred.realm
+            c.scheme = cred.scheme
+            c.username = cred.username
+            c.data_type = cred.passwd_type
+            c.data = cred.passwd
+            cfg.cred_info.append(c)
+            
+        cfg.auth_initial_send = self.auth_initial_send
+        cfg.auth_initial_algorithm = self.auth_initial_algorithm
+        cfg.transport_id = self.transport_id
+        cfg.allow_contact_rewrite = self.allow_contact_rewrite
+        cfg.ka_interval = self.ka_interval
+        cfg.ka_data = self.ka_data
+        cfg.use_srtp = self.use_srtp
+        cfg.srtp_secure_signaling = self.srtp_secure_signaling
+
+        if (self.rtp_transport_cfg is not None):
+            cfg.rtp_transport_cfg = self.rtp_transport_cfg._cvt_to_pjsua()
+        
+        return cfg
+ 
+ 
+# Account information
+class AccountInfo:
+    """This describes Account info. Application retrives account info
+    with Account.info().
+
+    Member documentation:
+
+    is_default      -- True if this is the default account.
+    uri             -- the account URI.
+    reg_active      -- True if registration is active for this account.
+    reg_expires     -- contains the current registration expiration value,
+                       in seconds.
+    reg_status      -- the registration status. If the value is less than
+                       700, it specifies SIP status code. Value greater than
+                       this specifies the error code.
+    reg_reason      -- contains the registration status text (e.g. the
+                       error message).
+    online_status   -- the account's presence online status, True if it's 
+                       publishing itself as online.
+    online_text     -- the account's presence status text.
+
+    """
+    is_default = False
+    uri = ""
+    reg_active = False
+    reg_expires = -1
+    reg_status = 0
+    reg_reason = ""
+    online_status = False
+    online_text = ""
+
+    def __init__(self, ai):
+        self.is_default = ai.is_default
+        self.uri = ai.acc_uri
+        self.reg_active = ai.has_registration
+        self.reg_expires = ai.expires
+        self.reg_status = ai.status
+        self.reg_reason = ai.status_text
+        self.online_status = ai.online_status
+        self.online_text = ai.online_status_text
+
+# Account callback
+class AccountCallback:
+    """Class to receive notifications on account's events.
+
+    Derive a class from this class and register it to the Account object
+    using Account.set_callback() to start receiving events from the Account
+    object.
+
+    Member documentation:
+
+    account     -- the Account object.
+
+    """
+    account = None
+
+    def __init__(self, account=None):
+        self._set_account(account)
+
+    def __del__(self):
+        pass
+
+    def _set_account(self, account):
+        if account:
+            self.account = weakref.proxy(account)
+        else:
+            self.account = None
+
+    def on_reg_state(self):
+        """Notification that the registration status has changed.
+        """
+        pass
+
+    def on_incoming_call(self, call):
+        """Notification about incoming call.
+
+        Application should implement one of on_incoming_call() or
+	on_incoming_call2(), otherwise, the default behavior is to
+        reject the call with default status code. Note that if both are
+	implemented, only on_incoming_call2() will be called.
+
+        Keyword arguments:
+        call    -- the new incoming call
+        """
+        call.hangup()
+
+    def on_incoming_call2(self, call, rdata):
+        """Notification about incoming call, with received SIP message info.
+
+        Application should implement one of on_incoming_call() or
+	on_incoming_call2(), otherwise, the default behavior is to
+        reject the call with default status code. Note that if both are
+	implemented, only on_incoming_call2() will be called.
+
+        Keyword arguments:
+        call    -- the new incoming call
+        rdata   -- the received message
+        """
+        call.hangup()
+	
+    def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres_obj):
+        """Notification when incoming SUBSCRIBE request is received. 
+        
+        Application may use this callback to authorize the incoming 
+        subscribe request (e.g. ask user permission if the request 
+        should be granted)
+
+        Keyword arguments:
+        buddy       -- The buddy object, if buddy is found. Otherwise
+                       the value is None.
+        from_uri    -- The URI string of the sender.
+        pres_obj    -- Opaque presence subscription object, which is
+                       needed by Account.pres_notify()
+
+        Return:
+            Tuple (code, reason), where:
+             code:      The status code. If code is >= 300, the
+                        request is rejected. If code is 200, the
+                        request is accepted and NOTIFY will be sent
+                        automatically. If code is 202, application
+                        must accept or reject the request later with
+                        Account.press_notify().
+             reason:    Optional reason phrase, or None to use the
+                        default reasoh phrase for the status code.
+        """
+        return (200, None)
+
+    def on_pager(self, from_uri, contact, mime_type, body):
+        """
+        Notification that incoming instant message is received on
+        this account.
+
+        Keyword arguments:
+        from_uri   -- sender's URI
+        contact    -- sender's Contact URI
+        mime_type  -- MIME type of the instant message body
+        body       -- the instant message body
+
+        """
+        pass
+
+    def on_pager_status(self, to_uri, body, im_id, code, reason):
+        """
+        Notification about the delivery status of previously sent
+        instant message.
+
+        Keyword arguments:
+        to_uri  -- the destination URI of the message
+        body    -- the message body
+        im_id   -- message ID
+        code    -- SIP status code
+        reason  -- SIP reason phrase
+
+        """
+        pass
+
+    def on_typing(self, from_uri, contact, is_typing):
+        """
+        Notification that remote is typing or stop typing.
+
+        Keyword arguments:
+        buddy     -- Buddy object for the sender, if found. Otherwise
+                     this will be None
+        from_uri  -- sender's URI of the indication
+        contact   -- sender's contact URI
+        is_typing -- boolean to indicate whether remote is currently
+                     typing an instant message.
+
+        """
+        pass
+
+    def on_mwi_info(self, body):
+        """
+        Notification about change in Message Summary / Message Waiting
+	Indication (RFC 3842) status. MWI subscription must be enabled
+	in the account config to receive this notification.
+
+        Keyword arguments:
+        body      -- String containing message body as received in the
+                     NOTIFY request.
+
+        """
+        pass
+
+
+
+class Account:
+    """This describes SIP account class.
+
+    PJSUA accounts provide identity (or identities) of the user who is 
+    currently using the application. In SIP terms, the identity is used 
+    as the From header in outgoing requests.
+
+    Account may or may not have client registration associated with it. 
+    An account is also associated with route set and some authentication 
+    credentials, which are used when sending SIP request messages using 
+    the account. An account also has presence's online status, which 
+    will be reported to remote peer when they subscribe to the account's 
+    presence, or which is published to a presence server if presence 
+    publication is enabled for the account.
+
+    Account is created with Lib.create_account(). At least one account 
+    MUST be created. If no user association is required, application can 
+    create a userless account by calling Lib.create_account_for_transport().
+    A userless account identifies local endpoint instead of a particular 
+    user, and it correspond with a particular transport instance.
+
+    Also one account must be set as the default account, which is used as 
+    the account to use when PJSUA fails to match a request with any other
+    accounts.
+
+    """
+    _id = -1        
+    _lib = None
+    _cb = AccountCallback(None)
+    _obj_name = ""
+
+    def __init__(self, lib, id, cb=None):
+        """Construct this class. This is normally called by Lib class and
+        not by application.
+
+        Keyword arguments:
+        lib -- the Lib instance.
+        id  -- the pjsua account ID.
+        cb  -- AccountCallback instance to receive events from this Account.
+               If callback is not specified here, it must be set later
+               using set_callback().
+        """
+        self._id = id
+        self._lib = weakref.ref(lib)
+        self._obj_name = "{Account " + self.info().uri + "}"
+        self.set_callback(cb)
+        _pjsua.acc_set_user_data(self._id, self)
+        _Trace((self, 'created'))
+
+    def __del__(self):
+        if self._id != -1:
+            _pjsua.acc_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
+
+    def __str__(self):
+        return self._obj_name
+
+    def info(self):
+        """Retrieve AccountInfo for this account.
+        """
+        lck = self._lib().auto_lock()
+        ai = _pjsua.acc_get_info(self._id)
+        if ai==None:
+            self._lib()._err_check("info()", self, -1, "Invalid account")
+        return AccountInfo(ai)
+
+    def is_valid(self):
+        """
+        Check if this account is still valid.
+
+        """
+        lck = self._lib().auto_lock()
+        return _pjsua.acc_is_valid(self._id)
+
+    def set_callback(self, cb):
+        """Register callback to receive notifications from this object.
+
+        Keyword argument:
+        cb  -- AccountCallback instance.
+
+        """
+        if cb:
+            self._cb = cb
+        else:
+            self._cb = AccountCallback(self)
+        self._cb._set_account(self)
+
+    def set_default(self):
+        """ Set this account as default account to send outgoing requests
+        and as the account to receive incoming requests when more exact
+        matching criteria fails.
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_default(self._id)
+        self._lib()._err_check("set_default()", self, err)
+
+    def is_default(self):
+        """ Check if this account is the default account.
+
+        """
+        lck = self._lib().auto_lock()
+        def_id = _pjsua.acc_get_default()
+        return self.is_valid() and def_id==self._id
+
+    def delete(self):
+        """ Delete this account.
+        
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_user_data(self._id, 0)
+        self._lib()._err_check("delete()", self, err)
+        err = _pjsua.acc_del(self._id)
+        self._lib()._err_check("delete()", self, err)
+        self._id = -1
+
+    def set_basic_status(self, is_online):
+        """ Set basic presence status of this account.
+
+        Keyword argument:
+        is_online   -- boolean to indicate basic presence availability.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_online_status(self._id, is_online)
+        self._lib()._err_check("set_basic_status()", self, err)
+
+    def set_presence_status(self, is_online, 
+                            activity=PresenceActivity.UNKNOWN, 
+                            pres_text="", rpid_id=""):
+        """ Set presence status of this account. 
+        
+        Keyword arguments:
+        is_online   -- boolean to indicate basic presence availability
+        activity    -- value from PresenceActivity
+        pres_text   -- optional string to convey additional information about
+                       the activity (such as "On the phone")
+        rpid_id     -- optional string to be placed as RPID ID. 
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_online_status2(self._id, is_online, activity,
+                                            pres_text, rpid_id)
+        self._lib()._err_check("set_presence_status()", self, err)
+
+    def set_registration(self, renew):
+        """Manually renew registration or unregister from the server.
+
+        Keyword argument:
+        renew   -- boolean to indicate whether registration is renewed.
+                   Setting this value for False will trigger unregistration.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_registration(self._id, renew)
+        self._lib()._err_check("set_registration()", self, err)
+
+    def set_transport(self, transport):
+        """Set this account to only use the specified transport to send
+        outgoing requests.
+
+        Keyword argument:
+        transport   -- Transport object.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_transport(self._id, transport._id)
+        self._lib()._err_check("set_transport()", self, err)
+
+    def make_call(self, dst_uri, cb=None, hdr_list=None):
+        """Make outgoing call to the specified URI.
+
+        Keyword arguments:
+        dst_uri  -- Destination SIP URI.
+        cb       -- CallCallback instance to be installed to the newly
+                    created Call object. If this CallCallback is not
+                    specified (i.e. None is given), it must be installed
+                    later using call.set_callback().
+        hdr_list -- Optional list of headers to be sent with outgoing
+                    INVITE
+
+        Return:
+            Call instance.
+        """
+        lck = self._lib().auto_lock()
+        call = Call(self._lib(), -1, cb)
+        err, cid = _pjsua.call_make_call(self._id, dst_uri, 0, 
+                                         call, Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("make_call()", self, err)
+        call.attach_to_id(cid)
+        return call
+
+    def add_buddy(self, uri, cb=None):
+        """Add new buddy.
+
+        Keyword argument:
+        uri     -- SIP URI of the buddy
+        cb      -- BuddyCallback instance to be installed to the newly
+                   created Buddy object. If this callback is not specified
+                   (i.e. None is given), it must be installed later using
+                   buddy.set_callback().
+
+        Return:
+            Buddy object
+        """
+        lck = self._lib().auto_lock()
+        buddy_cfg = _pjsua.buddy_config_default()
+        buddy_cfg.uri = uri
+        buddy_cfg.subscribe = False
+        err, buddy_id = _pjsua.buddy_add(buddy_cfg)
+        self._lib()._err_check("add_buddy()", self, err)
+        buddy = Buddy(self._lib(), buddy_id, self, cb)
+        return buddy
+
+    def pres_notify(self, pres_obj, state, reason="", hdr_list=None):
+        """Send NOTIFY to inform account presence status or to terminate
+        server side presence subscription.
+        
+        Keyword arguments:
+        pres_obj    -- The subscription object from on_incoming_subscribe()
+                       callback
+        state       -- Subscription state, from SubscriptionState
+        reason      -- Optional reason phrase.
+        hdr_list    -- Optional header list.
+        """
+        lck = self._lib().auto_lock()
+        _pjsua.acc_pres_notify(self._id, pres_obj, state, reason, 
+                               Lib._create_msg_data(hdr_list))
+    
+    def send_pager(self, uri, text, im_id=0, content_type="text/plain", \
+                   hdr_list=None):
+        """Send instant message to arbitrary URI.
+
+        Keyword arguments:
+        text         -- Instant message to be sent
+        uri          -- URI to send the Instant Message to.
+        im_id        -- Optional instant message ID to identify this
+                        instant message when delivery status callback
+                        is called.
+        content_type -- MIME type identifying the instant message
+        hdr_list     -- Optional list of headers to be sent with the
+                        request.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.im_send(self._id, uri, \
+                             content_type, text, \
+                             Lib._create_msg_data(hdr_list), \
+                             im_id)
+        self._lib()._err_check("send_pager()", self, err)
+
+class CallCallback:
+    """Class to receive event notification from Call objects. 
+
+    Use Call.set_callback() method to install instance of this callback 
+    class to receive event notifications from the call object.
+
+    Member documentation:
+
+    call    -- the Call object.
+
+    """
+    call = None
+
+    def __init__(self, call=None):
+        self._set_call(call)
+
+    def __del__(self):
+        pass
+
+    def _set_call(self, call):
+        if call:
+            self.call = weakref.proxy(call)
+        else:
+            self.call = None
+
+    def on_state(self):
+        """Notification that the call's state has changed.
+
+        """
+        pass
+
+    def on_media_state(self):
+        """Notification that the call's media state has changed.
+
+        """
+        pass
+
+    def on_dtmf_digit(self, digits):
+        """Notification on incoming DTMF digits.
+
+        Keyword argument:
+        digits  -- string containing the received digits.
+
+        """
+        pass
+
+    def on_transfer_request(self, dst, code):
+        """Notification that call is being transfered by remote party. 
+
+        Application can decide to accept/reject transfer request by returning
+        code greater than or equal to 500. The default behavior is to accept 
+        the transfer by returning 202.
+
+        Keyword arguments:
+        dst     -- string containing the destination URI
+        code    -- the suggested status code to return to accept the request.
+
+        Return:
+        the callback should return 202 to accept the request, or 300-699 to
+        reject the request.
+
+        """
+        return code
+
+    def on_transfer_status(self, code, reason, final, cont):
+        """
+        Notification about the status of previous call transfer request. 
+
+        Keyword arguments:
+        code    -- SIP status code to indicate completion status.
+        text    -- SIP status reason phrase.
+        final   -- if True then this is a final status and no further
+                   notifications will be sent for this call transfer
+                   status.
+        cont    -- suggested return value.
+
+        Return:
+        If the callback returns false then no further notification will
+        be sent for the transfer request for this call.
+
+        """
+        return cont
+
+    def on_replace_request(self, code, reason):
+        """Notification when incoming INVITE with Replaces header is received. 
+
+        Application may reject the request by returning value greather than
+        or equal to 500. The default behavior is to accept the request.
+
+        Keyword arguments:
+        code    -- default status code to return
+        reason  -- default reason phrase to return
+
+        Return:
+        The callback should return (code, reason) tuple.
+
+        """
+        return code, reason
+
+    def on_replaced(self, new_call):
+        """
+        Notification that this call will be replaced with new_call. 
+        After this callback is called, this call will be disconnected.
+
+        Keyword arguments:
+        new_call    -- the new call that will replace this call.
+        """
+        pass
+
+    def on_pager(self, mime_type, body):
+        """
+        Notification that incoming instant message is received on
+        this call.
+
+        Keyword arguments:
+        mime_type  -- MIME type of the instant message body.
+        body       -- the instant message body.
+
+        """
+        pass
+
+    def on_pager_status(self, body, im_id, code, reason):
+        """
+        Notification about the delivery status of previously sent
+        instant message.
+
+        Keyword arguments:
+        body    -- message body
+        im_id   -- message ID
+        code    -- SIP status code
+        reason  -- SIP reason phrase
+
+        """
+        pass
+
+    def on_typing(self, is_typing):
+        """
+        Notification that remote is typing or stop typing.
+
+        Keyword arguments:
+        is_typing -- boolean to indicate whether remote is currently
+                     typing an instant message.
+
+        """
+        pass
+
+
+class CallInfo:
+    """This structure contains various information about Call.
+
+    Application may retrieve this information with Call.info().
+
+    Member documentation:
+
+    role            -- CallRole
+    account         -- Account object.
+    uri             -- SIP URI of local account.
+    contact         -- local Contact URI.
+    remote_uri      -- remote SIP URI.
+    remote_contact  -- remote Contact URI
+    sip_call_id     -- call's Call-ID identification
+    state           -- CallState
+    state_text      -- state text.
+    last_code       -- last SIP status code
+    last_reason     -- text phrase for last_code
+    media_state     -- MediaState
+    media_dir       -- MediaDir
+    conf_slot       -- conference slot number for this call.
+    call_time       -- call's connected duration in seconds.
+    total_time      -- total call duration in seconds.
+    """
+    role = CallRole.CALLER
+    account = None
+    uri = ""
+    contact = ""
+    remote_uri = ""
+    remote_contact = ""
+    sip_call_id = ""
+    state = CallState.NULL
+    state_text = ""
+    last_code = 0
+    last_reason = ""
+    media_state = MediaState.NULL
+    media_dir = MediaDir.NULL
+    conf_slot = -1
+    call_time = 0
+    total_time = 0
+
+    def __init__(self, lib=None, ci=None):
+        if lib and ci:
+            self._cvt_from_pjsua(lib, ci)
+
+    def _cvt_from_pjsua(self, lib, ci):
+        self.role = ci.role
+        self.account = lib._lookup_account(ci.acc_id)
+        self.uri = ci.local_info
+        self.contact = ci.local_contact
+        self.remote_uri = ci.remote_info
+        self.remote_contact = ci.remote_contact
+        self.sip_call_id = ci.call_id
+        self.state = ci.state
+        self.state_text = ci.state_text
+        self.last_code = ci.last_status
+        self.last_reason = ci.last_status_text
+        self.media_state = ci.media_status
+        self.media_dir = ci.media_dir
+        self.conf_slot = ci.conf_slot
+        self.call_time = ci.connect_duration / 1000
+        self.total_time = ci.total_duration / 1000
+
+
+class Call:
+    """This class represents SIP call.
+
+    Application initiates outgoing call with Account.make_call(), and
+    incoming calls are reported in AccountCallback.on_incoming_call().
+    """
+    _id = -1
+    _cb = None
+    _lib = None
+    _obj_name = ""
+
+    def __init__(self, lib, call_id, cb=None):
+        self._lib = weakref.ref(lib)
+        self.set_callback(cb)
+        self.attach_to_id(call_id)
+        _Trace((self, 'created'))
+
+    def __del__(self):
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
+
+    def __str__(self):
+        return self._obj_name
+
+    def attach_to_id(self, call_id):
+        lck = self._lib().auto_lock()
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, 0)
+        self._id = call_id
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, self)
+            self._obj_name = "{Call " + self.info().remote_uri + "}"
+        else:
+            self._obj_name = "{Call object}"
+
+    def set_callback(self, cb):
+        """
+        Set callback object to retrieve event notifications from this call.
+
+        Keyword arguments:
+        cb  -- CallCallback instance.
+        """
+        if cb:
+            self._cb = cb
+        else:
+            self._cb = CallCallback(self)
+        self._cb._set_call(self)
+
+    def info(self):
+        """
+        Get the CallInfo.
+        """
+        lck = self._lib().auto_lock()
+        ci = _pjsua.call_get_info(self._id)
+        if not ci:
+            self._lib()._err_check("info", self, -1, "Invalid call")
+        call_info = CallInfo(self._lib(), ci)
+        return call_info
+
+    def is_valid(self):
+        """
+        Check if this call is still valid.
+        """
+        lck = self._lib().auto_lock()
+        return _pjsua.call_is_active(self._id)
+
+    def dump_status(self, with_media=True, indent="", max_len=1024):
+        """
+        Dump the call status.
+        """
+        lck = self._lib().auto_lock()
+        return _pjsua.call_dump(self._id, with_media, max_len, indent)
+
+    def answer(self, code=200, reason="", hdr_list=None):
+        """
+        Send provisional or final response to incoming call.
+
+        Keyword arguments:
+        code     -- SIP status code.
+        reason   -- Reason phrase. Put empty to send default reason
+                    phrase for the status code.
+        hdr_list -- Optional list of headers to be sent with the
+                    INVITE response.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_answer(self._id, code, reason, 
+                                   Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("answer()", self, err)
+
+    def hangup(self, code=603, reason="", hdr_list=None):
+        """
+        Terminate the call.
+
+        Keyword arguments:
+        code     -- SIP status code.
+        reason   -- Reason phrase. Put empty to send default reason
+                    phrase for the status code.
+        hdr_list -- Optional list of headers to be sent with the
+                    message.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_hangup(self._id, code, reason, 
+                                   Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("hangup()", self, err)
+
+    def hold(self, hdr_list=None):
+        """
+        Put the call on hold.
+
+        Keyword arguments:
+        hdr_list -- Optional list of headers to be sent with the
+                    message.
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_set_hold(self._id, Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("hold()", self, err)
+
+    def unhold(self, hdr_list=None):
+        """
+        Release the call from hold.
+
+        Keyword arguments:
+        hdr_list -- Optional list of headers to be sent with the
+                    message.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_reinvite(self._id, True, 
+                                     Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("unhold()", self, err)
+
+    def reinvite(self, hdr_list=None):
+        """
+        Send re-INVITE and optionally offer new codecs to use.
+
+        Keyword arguments:
+        hdr_list   -- Optional list of headers to be sent with the
+                      message.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_reinvite(self._id, True, 
+                                     Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("reinvite()", self, err)
+
+    def update(self, hdr_list=None, options=0):
+        """
+        Send UPDATE and optionally offer new codecs to use.
+
+        Keyword arguments:
+        hdr_list   -- Optional list of headers to be sent with the
+                      message.
+        options    -- Must be zero for now.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_update(self._id, options, 
+                                   Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("update()", self, err)
+
+    def transfer(self, dest_uri, hdr_list=None):
+        """
+        Transfer the call to new destination.
+
+        Keyword arguments:
+        dest_uri -- Specify the SIP URI to transfer the call to.
+        hdr_list -- Optional list of headers to be sent with the
+                    message.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_xfer(self._id, dest_uri, 
+                                 Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("transfer()", self, err)
+
+    def transfer_to_call(self, call, hdr_list=None, options=0):
+        """
+        Attended call transfer.
+
+        Keyword arguments:
+        call     -- The Call object to transfer call to.
+        hdr_list -- Optional list of headers to be sent with the
+                    message.
+        options  -- Must be zero for now.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_xfer_replaces(self._id, call._id, options,
+                                          Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("transfer_to_call()", self, err)
+
+    def dial_dtmf(self, digits):
+        """
+        Send DTMF digits with RTP event package.
+
+        Keyword arguments:
+        digits  -- DTMF digit string.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_dial_dtmf(self._id, digits)
+        self._lib()._err_check("dial_dtmf()", self, err)
+
+    def send_request(self, method, hdr_list=None, content_type=None,
+                     body=None):
+        """
+        Send arbitrary request to remote call. 
+        
+        This is useful for example to send INFO request. Note that this 
+        function should not be used to send request that will change the 
+        call state such as CANCEL or BYE.
+
+        Keyword arguments:
+        method       -- SIP method name.
+        hdr_list     -- Optional header list to be sent with the request.
+        content_type -- Content type to describe the body, if the body
+                        is present
+        body         -- Optional SIP message body.
+
+        """
+        lck = self._lib().auto_lock()
+        if hdr_list or body:
+            msg_data = _pjsua.Msg_Data()
+            if hdr_list:
+                msg_data.hdr_list = hdr_list
+            if content_type:
+                msg_data.content_type = content_type
+            if body:
+                msg_data.msg_body = body
+        else:
+            msg_data = None
+                
+        err = _pjsua.call_send_request(self._id, method, msg_data)
+        self._lib()._err_check("send_request()", self, err)
+
+    def send_pager(self, text, im_id=0, content_type="text/plain", 
+    		   hdr_list=None):
+        """Send instant message inside a call.
+
+        Keyword arguments:
+        text         -- Instant message to be sent
+        im_id        -- Optional instant message ID to identify this
+                        instant message when delivery status callback
+                        is called.
+        content_type -- MIME type identifying the instant message
+        hdr_list     -- Optional list of headers to be sent with the
+                        request.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.call_send_im(self._id, \
+                             content_type, text, \
+                             Lib._create_msg_data(hdr_list), \
+                             im_id)
+        self._lib()._err_check("send_pager()", self, err)
+
+  
+class BuddyInfo:
+    """This class contains information about Buddy. Application may 
+    retrieve this information by calling Buddy.info().
+
+    Member documentation:
+
+    uri             -- the Buddy URI.
+    contact         -- the Buddy Contact URI, if available.
+    online_status   -- the presence online status.
+    online_text     -- the presence online status text.
+    activity        -- the PresenceActivity
+    subscribed      -- specify whether buddy's presence status is currently
+                       being subscribed.
+    sub_state       -- SubscriptionState
+    sub_term_reason -- The termination reason string of the last presence
+                       subscription to this buddy, if any.
+    """
+    uri = ""
+    contact = ""
+    online_status = 0
+    online_text = ""
+    activity = PresenceActivity.UNKNOWN
+    subscribed = False
+    sub_state = SubscriptionState.NULL
+    sub_term_reason = ""
+
+    def __init__(self, pjsua_bi=None):
+        if pjsua_bi:
+            self._cvt_from_pjsua(pjsua_bi)
+
+    def _cvt_from_pjsua(self, inf):
+        self.uri = inf.uri
+        self.contact = inf.contact
+        self.online_status = inf.status
+        self.online_text = inf.status_text
+        self.activity = inf.activity
+        self.subscribed = inf.monitor_pres
+        self.sub_state = inf.sub_state
+        self.sub_term_reason = inf.sub_term_reason
+
+
+class BuddyCallback:
+    """This class can be used to receive notifications about Buddy's
+    presence status change. Application needs to derive a class from
+    this class, and register the instance with Buddy.set_callback().
+
+    Member documentation:
+
+    buddy   -- the Buddy object.
+    """
+    buddy = None
+
+    def __init__(self, buddy=None):
+        self._set_buddy(buddy)
+
+    def _set_buddy(self, buddy):
+        if buddy:
+            self.buddy = weakref.proxy(buddy)
+        else:
+            self.buddy = None
+
+    def on_state(self):
+        """
+        Notification that buddy's presence state has changed. Application
+        may then retrieve the new status with Buddy.info() function.
+        """
+        pass
+   
+    def on_pager(self, mime_type, body):
+        """Notification that incoming instant message is received from
+        this buddy.
+
+        Keyword arguments:
+        mime_type  -- MIME type of the instant message body
+        body       -- the instant message body
+
+        """
+        pass
+
+    def on_pager_status(self, body, im_id, code, reason):
+        """Notification about the delivery status of previously sent
+        instant message.
+
+        Keyword arguments:
+        body    -- the message body
+        im_id   -- message ID
+        code    -- SIP status code
+        reason  -- SIP reason phrase
+
+        """
+        pass
+
+    def on_typing(self, is_typing):
+        """Notification that remote is typing or stop typing.
+
+        Keyword arguments:
+        is_typing -- boolean to indicate whether remote is currently
+                     typing an instant message.
+
+        """
+        pass
+
+
+class Buddy:
+    """A Buddy represents person or remote agent.
+
+    This class provides functions to subscribe to buddy's presence and
+    to send or receive instant messages from the buddy.
+    """
+    _id = -1
+    _lib = None
+    _cb = None
+    _obj_name = ""
+    _acc = None
+
+    def __init__(self, lib, id, account, cb):
+        self._id = id
+        self._lib = weakref.ref(lib)
+        self._acc = weakref.ref(account)
+        self._obj_name = "{Buddy " + self.info().uri + "}"
+        self.set_callback(cb)
+        _pjsua.buddy_set_user_data(self._id, self)
+        _Trace((self, 'created'))
+
+    def __del__(self):
+        if self._id != -1:
+            _pjsua.buddy_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
+
+    def __str__(self):
+        return self._obj_name
+
+    def info(self):
+        """
+        Get buddy info as BuddyInfo.
+        """
+        lck = self._lib().auto_lock()
+        return BuddyInfo(_pjsua.buddy_get_info(self._id))
+
+    def set_callback(self, cb):
+        """Install callback to receive notifications from this object.
+
+        Keyword argument:
+        cb  -- BuddyCallback instance.
+        """
+        if cb:
+            self._cb = cb
+        else:
+            self._cb = BuddyCallback(self)
+        self._cb._set_buddy(self)
+
+    def subscribe(self):
+        """
+        Subscribe to buddy's presence status notification.
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.buddy_subscribe_pres(self._id, True)
+        self._lib()._err_check("subscribe()", self, err)
+
+    def unsubscribe(self):
+        """
+        Unsubscribe from buddy's presence status notification.
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.buddy_subscribe_pres(self._id, False)
+        self._lib()._err_check("unsubscribe()", self, err)
+
+    def delete(self):
+        """
+        Remove this buddy from the buddy list.
+        """
+        lck = self._lib().auto_lock()
+        if self._id != -1:
+            _pjsua.buddy_set_user_data(self._id, 0)
+        err = _pjsua.buddy_del(self._id)
+        self._lib()._err_check("delete()", self, err)
+
+    def send_pager(self, text, im_id=0, content_type="text/plain", \
+                   hdr_list=None):
+        """Send instant message to remote buddy.
+
+        Keyword arguments:
+        text         -- Instant message to be sent
+        im_id        -- Optional instant message ID to identify this
+                        instant message when delivery status callback
+                        is called.
+        content_type -- MIME type identifying the instant message
+        hdr_list     -- Optional list of headers to be sent with the
+                        request.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.im_send(self._acc()._id, self.info().uri, \
+                             content_type, text, \
+                             Lib._create_msg_data(hdr_list), \
+                             im_id)
+        self._lib()._err_check("send_pager()", self, err)
+
+    def send_typing_ind(self, is_typing=True, hdr_list=None):
+        """Send typing indication to remote buddy.
+
+        Keyword argument:
+        is_typing -- boolean to indicate wheter user is typing.
+        hdr_list  -- Optional list of headers to be sent with the
+                     request.
+
+        """
+        lck = self._lib().auto_lock()
+        err = _pjsua.im_typing(self._acc()._id, self.info().uri, \
+                               is_typing, Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("send_typing_ind()", self, err)
+
+
+
+# Sound device info
+class SoundDeviceInfo:
+    """This described the sound device info.
+
+    Member documentation:
+    name                -- device name.
+    input_channels      -- number of capture channels supported.
+    output_channels     -- number of playback channels supported.
+    default_clock_rate  -- default sampling rate.
+    """
+    name = ""
+    input_channels = 0
+    output_channels = 0
+    default_clock_rate = 0
+
+    def __init__(self, sdi):
+        self.name = sdi.name
+        self.input_channels = sdi.input_count
+        self.output_channels = sdi.output_count
+        self.default_clock_rate = sdi.default_samples_per_sec
+
+
+# Codec info
+class CodecInfo:
+    """This describes codec info.
+
+    Member documentation:
+    name            -- codec name
+    priority        -- codec priority (0-255)
+    clock_rate      -- clock rate
+    channel_count   -- number of channels
+    avg_bps         -- average bandwidth in bits per second
+    frm_ptime       -- base frame length in milliseconds
+    ptime           -- RTP frame length in milliseconds.
+    pt              -- payload type.
+    vad_enabled     -- specify if Voice Activity Detection is currently
+                       enabled.
+    plc_enabled     -- specify if Packet Lost Concealment is currently
+                       enabled.
+    """
+    name = ""
+    priority = 0
+    clock_rate = 0
+    channel_count = 0
+    avg_bps = 0
+    frm_ptime = 0
+    ptime = 0
+    pt = 0
+    vad_enabled = False
+    plc_enabled = False
+
+    def __init__(self, codec_info, codec_param):
+        self.name = codec_info.codec_id
+        self.priority = codec_info.priority
+        self.clock_rate = codec_param.info.clock_rate
+        self.channel_count = codec_param.info.channel_cnt
+        self.avg_bps = codec_param.info.avg_bps
+        self.frm_ptime = codec_param.info.frm_ptime
+        self.ptime = codec_param.info.frm_ptime * \
+                        codec_param.setting.frm_per_pkt
+        self.ptime = codec_param.info.pt
+        self.vad_enabled = codec_param.setting.vad
+        self.plc_enabled = codec_param.setting.plc
+
+    def _cvt_to_pjsua(self):
+        ci = _pjsua.Codec_Info()
+        ci.codec_id = self.name
+        ci.priority = self.priority
+        return ci
+
+
+# Codec parameter
+class CodecParameter:
+    """This specifies various parameters that can be configured for codec.
+
+    Member documentation:
+
+    ptime       -- specify the outgoing RTP packet length in milliseconds.
+    vad_enabled -- specify if VAD should be enabled.
+    plc_enabled -- specify if PLC should be enabled.
+    """
+    ptime = 0
+    vad_enabled = False
+    plc_enabled = False
+    _codec_param = None
+    
+    def __init__(self, codec_param):
+        self.ptime = codec_param.info.frm_ptime * \
+                        codec_param.setting.frm_per_pkt
+        self.vad_enabled = codec_param.setting.vad
+        self.plc_enabled = codec_param.setting.plc
+        self._codec_param = codec_param
+
+    def _cvt_to_pjsua(self):
+        self._codec_param.setting.frm_per_pkt = self.ptime / \
+                                                self._codec_param.info.frm_ptime
+        self._codec_param.setting.vad = self.vad_enabled
+        self._codec_param.setting.plc = self.plc_enabled
+        return self._codec_param
+
+
+# Library mutex
+class _LibMutex:
+    def __init__(self, lck):
+        self._lck = lck
+        self._lck.acquire()
+	#_Trace(('lock acquired',))
+
+    def __del__(self):
+        try:
+            self._lck.release()
+	    #_Trace(('lock released',))
+        except:
+	    #_Trace(('lock release error',))
+            pass
+
+
+# PJSUA Library
+_lib = None
+enable_trace = False
+
+class Lib:
+    """Library instance.
+    
+    """
+    _quit = False
+    _has_thread = False
+    _lock = None
+
+    def __init__(self):
+        global _lib
+        if _lib:
+            raise Error("__init()__", None, -1, 
+                        "Library instance already exist")
+
+        self._lock = threading.RLock()
+        err = _pjsua.create()
+        self._err_check("_pjsua.create()", None, err)
+        _lib = self
+
+    def __del__(self):
+        _pjsua.destroy()
+        del self._lock
+        _Trace(('Lib destroyed',))
+
+    def __str__(self):
+        return "Lib"
+
+    @staticmethod
+    def instance():
+        """Return singleton instance of Lib.
+        """
+        return _lib
+
+    def init(self, ua_cfg=None, log_cfg=None, media_cfg=None):
+        """
+        Initialize pjsua with the specified configurations.
+
+        Keyword arguments:
+        ua_cfg      -- optional UAConfig instance
+        log_cfg     -- optional LogConfig instance
+        media_cfg   -- optional MediaConfig instance
+
+        """
+        if not ua_cfg: ua_cfg = UAConfig()
+        if not log_cfg: log_cfg = LogConfig()
+        if not media_cfg: media_cfg = MediaConfig()
+
+        py_ua_cfg = ua_cfg._cvt_to_pjsua()
+        py_ua_cfg.cb.on_call_state = _cb_on_call_state
+        py_ua_cfg.cb.on_incoming_call = _cb_on_incoming_call
+        py_ua_cfg.cb.on_call_media_state = _cb_on_call_media_state
+        py_ua_cfg.cb.on_dtmf_digit = _cb_on_dtmf_digit
+        py_ua_cfg.cb.on_call_transfer_request = _cb_on_call_transfer_request
+        py_ua_cfg.cb.on_call_transfer_status = _cb_on_call_transfer_status
+        py_ua_cfg.cb.on_call_replace_request = _cb_on_call_replace_request
+        py_ua_cfg.cb.on_call_replaced = _cb_on_call_replaced
+        py_ua_cfg.cb.on_reg_state = _cb_on_reg_state
+        py_ua_cfg.cb.on_incoming_subscribe = _cb_on_incoming_subscribe
+        py_ua_cfg.cb.on_buddy_state = _cb_on_buddy_state
+        py_ua_cfg.cb.on_pager = _cb_on_pager
+        py_ua_cfg.cb.on_pager_status = _cb_on_pager_status
+        py_ua_cfg.cb.on_typing = _cb_on_typing
+	py_ua_cfg.cb.on_mwi_info = _cb_on_mwi_info;
+
+        err = _pjsua.init(py_ua_cfg, log_cfg._cvt_to_pjsua(), 
+                          media_cfg._cvt_to_pjsua())
+        self._err_check("init()", self, err)
+
+    def destroy(self):
+        """Destroy the library, and pjsua."""
+        global _lib
+        if self._has_thread:
+            self._quit = 1
+            loop = 0
+            while self._quit != 2 and loop < 400:
+                self.handle_events(5)
+                loop = loop + 1
+                time.sleep(0.050)
+        _pjsua.destroy()
+        _lib = None
+
+    def start(self, with_thread=True):
+        """Start the library. 
+
+        Keyword argument:
+        with_thread -- specify whether the module should create worker
+                       thread.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.start()
+        self._err_check("start()", self, err)
+        self._has_thread = with_thread
+        if self._has_thread:
+            thread.start_new(_worker_thread_main, (0,))
+
+    def handle_events(self, timeout=50):
+        """Poll the events from underlying pjsua library.
+        
+        Application must poll the stack periodically if worker thread
+        is disable when starting the library.
+
+        Keyword argument:
+        timeout -- in milliseconds.
+
+        """
+        lck = self.auto_lock()
+        return _pjsua.handle_events(timeout)
+
+    def thread_register(self, name):
+	"""Register external threads (threads that are not created by PJSIP,
+	such as threads that are created by Python API) to PJSIP.
+
+	The call must be made from the new thread before calling any pjlib 
+	functions.
+
+	Keyword arguments:
+	name	-- Non descriptive name for the thread
+	"""
+	dummy = 1
+	err = _pjsua.thread_register(name, dummy)
+	self._err_check("thread_register()", self, err)
+
+    def verify_sip_url(self, sip_url):
+        """Verify that the specified string is a valid URI. 
+        
+        Keyword argument:
+        sip_url -- the URL string.
+        
+        Return:
+            0 is the the URI is valid, otherwise the appropriate error 
+            code is returned.
+
+        """
+        lck = self.auto_lock()
+        return _pjsua.verify_sip_url(sip_url)
+
+    def create_transport(self, type, cfg=None):
+        """Create SIP transport instance of the specified type. 
+        
+        Keyword arguments:
+        type    -- transport type from TransportType constant.
+        cfg     -- TransportConfig instance
+
+        Return:
+            Transport object
+
+        """
+        lck = self.auto_lock()
+        if not cfg: cfg=TransportConfig()
+        err, tp_id = _pjsua.transport_create(type, cfg._cvt_to_pjsua())
+        self._err_check("create_transport()", self, err)
+        return Transport(self, tp_id)
+
+    def create_account(self, acc_config, set_default=True, cb=None):
+        """
+        Create a new local pjsua account using the specified configuration.
+
+        Keyword arguments:
+        acc_config  -- AccountConfig
+        set_default -- boolean to specify whether to use this as the
+                       default account.
+        cb          -- AccountCallback instance.
+
+        Return:
+            Account instance
+
+        """
+        lck = self.auto_lock()
+        err, acc_id = _pjsua.acc_add(acc_config._cvt_to_pjsua(), set_default)
+        self._err_check("create_account()", self, err)
+        return Account(self, acc_id, cb)
+
+    def create_account_for_transport(self, transport, set_default=True,
+                                     cb=None):
+        """Create a new local pjsua transport for the specified transport.
+
+        Keyword arguments:
+        transport   -- the Transport instance.
+        set_default -- boolean to specify whether to use this as the
+                       default account.
+        cb          -- AccountCallback instance.
+
+        Return:
+            Account instance
+
+        """
+        lck = self.auto_lock()
+        err, acc_id = _pjsua.acc_add_local(transport._id, set_default)
+        self._err_check("create_account_for_transport()", self, err)
+        return Account(self, acc_id, cb)
+
+    def hangup_all(self):
+        """Hangup all calls.
+
+        """
+        lck = self.auto_lock()
+        _pjsua.call_hangup_all()
+
+    # Sound device API
+
+    def enum_snd_dev(self):
+        """Enumerate sound devices in the system.
+
+        Return:
+            list of SoundDeviceInfo. The index of the element specifies
+            the device ID for the device.
+        """
+        lck = self.auto_lock()
+        sdi_list = _pjsua.enum_snd_devs()
+        info = []
+        for sdi in sdi_list:
+            info.append(SoundDeviceInfo(sdi))
+        return info
+
+    def get_snd_dev(self):
+        """Get the device IDs of current sound devices used by pjsua.
+
+        Return:
+            (capture_dev_id, playback_dev_id) tuple
+        """
+        lck = self.auto_lock()
+        return _pjsua.get_snd_dev()
+
+    def set_snd_dev(self, capture_dev, playback_dev):
+        """Change the current sound devices.
+
+        Keyword arguments:
+        capture_dev  -- the device ID of capture device to be used
+        playback_dev -- the device ID of playback device to be used.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.set_snd_dev(capture_dev, playback_dev)
+        self._err_check("set_current_sound_devices()", self, err)
+    
+    def set_null_snd_dev(self):
+        """Disable the sound devices. This is useful if the system
+        does not have sound device installed.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.set_null_snd_dev()
+        self._err_check("set_null_snd_dev()", self, err)
+
+    
+    # Conference bridge
+
+    def conf_get_max_ports(self):
+        """Get the conference bridge capacity.
+
+        Return:
+            conference bridge capacity.
+
+        """
+        lck = self.auto_lock()
+        return _pjsua.conf_get_max_ports()
+
+    def conf_connect(self, src_slot, dst_slot):
+        """Establish unidirectional media flow from souce to sink. 
+        
+        One source may transmit to multiple destinations/sink. And if 
+        multiple sources are transmitting to the same sink, the media 
+        will be mixed together. Source and sink may refer to the same ID, 
+        effectively looping the media.
+
+        If bidirectional media flow is desired, application needs to call
+        this function twice, with the second one having the arguments 
+        reversed.
+
+        Keyword arguments:
+        src_slot    -- integer to identify the conference slot number of
+                       the source/transmitter.
+        dst_slot    -- integer to identify the conference slot number of    
+                       the destination/receiver.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.conf_connect(src_slot, dst_slot)
+        self._err_check("conf_connect()", self, err)
+    
+    def conf_disconnect(self, src_slot, dst_slot):
+        """Disconnect media flow from the source to destination port.
+
+        Keyword arguments:
+        src_slot    -- integer to identify the conference slot number of
+                       the source/transmitter.
+        dst_slot    -- integer to identify the conference slot number of    
+                       the destination/receiver.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.conf_disconnect(src_slot, dst_slot)
+        self._err_check("conf_disconnect()", self, err)
+
+    def conf_set_tx_level(self, slot, level):
+        """Adjust the signal level to be transmitted from the bridge to 
+        the specified port by making it louder or quieter.
+
+        Keyword arguments:
+        slot        -- integer to identify the conference slot number.
+        level       -- Signal level adjustment. Value 1.0 means no level
+                       adjustment, while value 0 means to mute the port.
+        """
+        lck = self.auto_lock()
+        err = _pjsua.conf_set_tx_level(slot, level)
+        self._err_check("conf_set_tx_level()", self, err)
+        
+    def conf_set_rx_level(self, slot, level):
+        """Adjust the signal level to be received from the specified port
+        (to the bridge) by making it louder or quieter.
+
+        Keyword arguments:
+        slot        -- integer to identify the conference slot number.
+        level       -- Signal level adjustment. Value 1.0 means no level
+                       adjustment, while value 0 means to mute the port.
+        """
+        lck = self.auto_lock()
+        err = _pjsua.conf_set_rx_level(slot, level)
+        self._err_check("conf_set_rx_level()", self, err)
+        
+    def conf_get_signal_level(self, slot):
+        """Get last signal level transmitted to or received from the 
+        specified port. The signal levels are float values from 0.0 to 1.0,
+        with 0.0 indicates no signal, and 1.0 indicates the loudest signal
+        level.
+
+        Keyword arguments:
+        slot        -- integer to identify the conference slot number.
+
+        Return value:
+            (tx_level, rx_level) tuple.
+        """
+        lck = self.auto_lock()
+        err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot)
+        self._err_check("conf_get_signal_level()", self, err)
+        return (tx_level, rx_level)
+        
+
+
+    # Codecs API
+
+    def enum_codecs(self):
+        """Return list of codecs supported by pjsua.
+
+        Return:
+            list of CodecInfo
+
+        """
+        lck = self.auto_lock()
+        ci_list = _pjsua.enum_codecs()
+        codec_info = []
+        for ci in ci_list:
+            cp = _pjsua.codec_get_param(ci.codec_id)
+            if cp:
+                codec_info.append(CodecInfo(ci, cp))
+        return codec_info
+
+    def set_codec_priority(self, name, priority):
+        """Change the codec priority.
+
+        Keyword arguments:
+        name     -- Codec name
+        priority -- Codec priority, which range is 0-255.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.codec_set_priority(name, priority)
+        self._err_check("set_codec_priority()", self, err)
+
+    def get_codec_parameter(self, name):
+        """Get codec parameter for the specified codec.
+
+        Keyword arguments:
+        name    -- codec name.
+
+        """
+        lck = self.auto_lock()
+        cp = _pjsua.codec_get_param(name)
+        if not cp:
+            self._err_check("get_codec_parameter()", self, -1, 
+                            "Invalid codec name")
+        return CodecParameter(cp)
+
+    def set_codec_parameter(self, name, param):
+        """Modify codec parameter for the specified codec.
+
+        Keyword arguments:
+        name    -- codec name
+        param   -- codec parameter.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.codec_set_param(name, param._cvt_to_pjsua())
+        self._err_check("set_codec_parameter()", self, err)
+    
+    # WAV playback and recording
+
+    def create_player(self, filename, loop=False):
+        """Create WAV file player.
+
+        Keyword arguments
+        filename    -- WAV file name
+        loop        -- boolean to specify whether playback should
+                       automatically restart upon EOF
+        Return:
+            WAV player ID
+
+        """
+        lck = self.auto_lock()
+        opt = 0
+        if not loop:
+            opt = opt + 1
+        err, player_id = _pjsua.player_create(filename, opt)
+        self._err_check("create_player()", self, err)
+        return player_id
+        
+    def player_get_slot(self, player_id):
+        """Get the conference port ID for the specified player.
+
+        Keyword arguments:
+        player_id  -- the WAV player ID
+        
+        Return:
+            Conference slot number for the player
+
+        """
+        lck = self.auto_lock()
+        slot = _pjsua.player_get_conf_port(player_id)
+        if slot < 0:
+                self._err_check("player_get_slot()", self, -1, 
+                                "Invalid player id")
+        return slot
+
+    def player_set_pos(self, player_id, pos):
+        """Set WAV playback position.
+
+        Keyword arguments:
+        player_id   -- WAV player ID
+        pos         -- playback position, in samples
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.player_set_pos(player_id, pos)
+        self._err_check("player_set_pos()", self, err)
+        
+    def player_destroy(self, player_id):
+        """Destroy the WAV player.
+
+        Keyword arguments:
+        player_id   -- the WAV player ID.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.player_destroy(player_id)
+        self._err_check("player_destroy()", self, err)
+
+    def create_playlist(self, filelist, label="playlist", loop=True):
+        """Create WAV playlist.
+
+        Keyword arguments:
+        filelist    -- List of WAV file names.
+        label       -- Optional name to be assigned to the playlist
+                       object (useful for logging)
+        loop        -- boolean to specify whether playback should
+                       automatically restart upon EOF
+
+        Return:
+            playlist_id
+        """
+        lck = self.auto_lock()
+        opt = 0
+        if not loop:
+            opt = opt + 1
+        err, playlist_id = _pjsua.playlist_create(label, filelist, opt)
+        self._err_check("create_playlist()", self, err)
+        return playlist_id 
+
+    def playlist_get_slot(self, playlist_id):
+        """Get the conference port ID for the specified playlist.
+
+        Keyword arguments:
+        playlist_id  -- the WAV playlist ID
+        
+        Return:
+            Conference slot number for the playlist
+
+        """
+        lck = self.auto_lock()
+        slot = _pjsua.player_get_conf_port(playlist_id)
+        if slot < 0:
+                self._err_check("playlist_get_slot()", self, -1, 
+                                "Invalid playlist id")
+        return slot
+
+    def playlist_destroy(self, playlist_id):
+        """Destroy the WAV playlist.
+
+        Keyword arguments:
+        playlist_id   -- the WAV playlist ID.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.player_destroy(playlist_id)
+        self._err_check("playlist_destroy()", self, err)
+
+    def create_recorder(self, filename):
+        """Create WAV file recorder.
+
+        Keyword arguments
+        filename    -- WAV file name
+
+        Return:
+            WAV recorder ID
+
+        """
+        lck = self.auto_lock()
+        err, rec_id = _pjsua.recorder_create(filename, 0, None, -1, 0)
+        self._err_check("create_recorder()", self, err)
+        return rec_id
+        
+    def recorder_get_slot(self, rec_id):
+        """Get the conference port ID for the specified recorder.
+
+        Keyword arguments:
+        rec_id  -- the WAV recorder ID
+        
+        Return:
+            Conference slot number for the recorder
+
+        """
+        lck = self.auto_lock()
+        slot = _pjsua.recorder_get_conf_port(rec_id)
+        if slot < 1:
+            self._err_check("recorder_get_slot()", self, -1, 
+                            "Invalid recorder id")
+        return slot
+
+    def recorder_destroy(self, rec_id):
+        """Destroy the WAV recorder.
+
+        Keyword arguments:
+        rec_id   -- the WAV recorder ID.
+
+        """
+        lck = self.auto_lock()
+        err = _pjsua.recorder_destroy(rec_id)
+        self._err_check("recorder_destroy()", self, err)
+
+
+    # Internal functions
+
+    @staticmethod
+    def strerror(err):
+        return _pjsua.strerror(err)
+    
+    def _err_check(self, op_name, obj, err_code, err_msg=""):
+        if err_code != 0:
+            raise Error(op_name, obj, err_code, err_msg)
+
+    @staticmethod
+    def _create_msg_data(hdr_list):
+        if not hdr_list:
+            return None
+        msg_data = _pjsua.Msg_Data()
+        msg_data.hdr_list = hdr_list
+        return msg_data
+    
+    def auto_lock(self):
+        return _LibMutex(self._lock)
+
+    # Internal dictionary manipulation for calls, accounts, and buddies
+
+    def _lookup_call(self, call_id):
+        return _pjsua.call_get_user_data(call_id)
+
+    def _lookup_account(self, acc_id):
+        return _pjsua.acc_get_user_data(acc_id)
+
+    def _lookup_buddy(self, buddy_id, uri=None):
+        if buddy_id != -1:
+            buddy = _pjsua.buddy_get_user_data(buddy_id)
+        elif uri:
+            buddy_id = _pjsua.buddy_find(uri)
+            if buddy_id != -1:
+                buddy = _pjsua.buddy_get_user_data(buddy_id)
+            else:
+                buddy = None
+        else:
+            buddy = None
+            
+        return buddy 
+
+    # Account allbacks
+
+    def _cb_on_reg_state(self, acc_id):
+        acc = self._lookup_account(acc_id)
+        if acc:
+            acc._cb.on_reg_state()
+
+    def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri, 
+                                  contact_uri, pres_obj):
+        acc = self._lookup_account(acc_id)
+        if acc:
+            buddy = self._lookup_buddy(buddy_id)
+            return acc._cb.on_incoming_subscribe(buddy, from_uri, contact_uri,
+                                                 pres_obj)
+        else:
+            return (404, None)
+
+    def _cb_on_incoming_call(self, acc_id, call_id, rdata):
+        acc = self._lookup_account(acc_id)
+        if acc:
+            if 'on_incoming_call2' in acc._cb.__class__.__dict__:
+                acc._cb.on_incoming_call2( Call(self, call_id), rdata )
+            else:
+                acc._cb.on_incoming_call( Call(self, call_id) )
+        else:
+            _pjsua.call_hangup(call_id, 603, None, None)
+
+    # Call callbacks 
+
+    def _cb_on_call_state(self, call_id):
+        call = self._lookup_call(call_id)
+        if call:
+            if call._id == -1:
+                call.attach_to_id(call_id)
+            done = (call.info().state == CallState.DISCONNECTED)
+            call._cb.on_state()
+            if done:
+                _pjsua.call_set_user_data(call_id, 0)
+        else:
+            pass
+
+    def _cb_on_call_media_state(self, call_id):
+        call = self._lookup_call(call_id)
+        if call:
+            call._cb.on_media_state()
+
+    def _cb_on_dtmf_digit(self, call_id, digits):
+        call = self._lookup_call(call_id)
+        if call:
+            call._cb.on_dtmf_digit(digits)
+
+    def _cb_on_call_transfer_request(self, call_id, dst, code):
+        call = self._lookup_call(call_id)
+        if call:
+            return call._cb.on_transfer_request(dst, code)
+        else:
+            return 603
+
+    def _cb_on_call_transfer_status(self, call_id, code, text, final, cont):
+        call = self._lookup_call(call_id)
+        if call:
+            return call._cb.on_transfer_status(code, text, final, cont)
+        else:
+            return cont
+
+    def _cb_on_call_replace_request(self, call_id, rdata, code, reason):
+        call = self._lookup_call(call_id)
+        if call:
+            return call._cb.on_replace_request(code, reason)
+        else:
+            return code, reason
+
+    def _cb_on_call_replaced(self, old_call_id, new_call_id):
+        old_call = self._lookup_call(old_call_id)
+        new_call = self._lookup_call(new_call_id)
+        if old_call and new_call:
+            old_call._cb.on_replaced(new_call)
+
+    def _cb_on_pager(self, call_id, from_uri, to_uri, contact, mime_type, 
+                     body, acc_id):
+        call = None
+        if call_id != -1:
+            call = self._lookup_call(call_id)
+        if call:
+            call._cb.on_pager(mime_type, body)
+        else:
+            acc = self._lookup_account(acc_id)
+            buddy = self._lookup_buddy(-1, from_uri)
+            if buddy:
+                buddy._cb.on_pager(mime_type, body)
+            else:
+                acc._cb.on_pager(from_uri, contact, mime_type, body)
+
+    def _cb_on_pager_status(self, call_id, to_uri, body, user_data, 
+                            code, reason, acc_id):
+        call = None
+        if call_id != -1:
+            call = self._lookup_call(call_id)
+        if call:
+            call._cb.on_pager_status(body, user_data, code, reason)
+        else:
+            acc = self._lookup_account(acc_id)
+            buddy = self._lookup_buddy(-1, to_uri)
+            if buddy:
+                buddy._cb.on_pager_status(body, user_data, code, reason)
+            else:
+                acc._cb.on_pager_status(to_uri, body, user_data, code, reason)
+
+    def _cb_on_typing(self, call_id, from_uri, to_uri, contact, is_typing, 
+                      acc_id):
+        call = None
+        if call_id != -1:
+            call = self._lookup_call(call_id)
+        if call:
+            call._cb.on_typing(is_typing)
+        else:
+            acc = self._lookup_account(acc_id)
+            buddy = self._lookup_buddy(-1, from_uri)
+            if buddy:
+                buddy._cb.on_typing(is_typing)
+            else:
+                acc._cb.on_typing(from_uri, contact, is_typing)
+
+    def _cb_on_mwi_info(self, acc_id, body):
+        acc = self._lookup_account(acc_id)
+        if acc:
+            return acc._cb.on_mwi_info(body)
+
+    def _cb_on_buddy_state(self, buddy_id):
+        buddy = self._lookup_buddy(buddy_id)
+        if buddy:
+            buddy._cb.on_state()
+
+#
+# Internal
+#
+
+def _cb_on_call_state(call_id, e):
+    _lib._cb_on_call_state(call_id)
+
+def _cb_on_incoming_call(acc_id, call_id, rdata):
+    _lib._cb_on_incoming_call(acc_id, call_id, rdata)
+
+def _cb_on_call_media_state(call_id):
+    _lib._cb_on_call_media_state(call_id)
+
+def _cb_on_dtmf_digit(call_id, digits):
+    _lib._cb_on_dtmf_digit(call_id, digits)
+
+def _cb_on_call_transfer_request(call_id, dst, code):
+    return _lib._cb_on_call_transfer_request(call_id, dst, code)
+
+def _cb_on_call_transfer_status(call_id, code, reason, final, cont):
+    return _lib._cb_on_call_transfer_status(call_id, code, reason, 
+                                             final, cont)
+def _cb_on_call_replace_request(call_id, rdata, code, reason):
+    return _lib._cb_on_call_replace_request(call_id, rdata, code, reason)
+
+def _cb_on_call_replaced(old_call_id, new_call_id):
+    _lib._cb_on_call_replaced(old_call_id, new_call_id)
+
+def _cb_on_reg_state(acc_id):
+    _lib._cb_on_reg_state(acc_id)
+
+def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, contact_uri, pres):
+    return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, 
+                                          contact_uri, pres)
+
+def _cb_on_buddy_state(buddy_id):
+    _lib._cb_on_buddy_state(buddy_id)
+
+def _cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id):
+    _lib._cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id)
+
+def _cb_on_pager_status(call_id, to, body, user_data, status, reason, acc_id):
+    _lib._cb_on_pager_status(call_id, to, body, user_data, 
+                             status, reason, acc_id)
+
+def _cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id):
+    _lib._cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id)
+
+def _cb_on_mwi_info(acc_id, body):
+    _lib._cb_on_mwi_info(acc_id, body)
+
+# Worker thread
+def _worker_thread_main(arg):
+    global _lib
+    _Trace(('worker thread started..',))
+    thread_desc = 0;
+    err = _pjsua.thread_register("python worker", thread_desc)
+    _lib._err_check("thread_register()", _lib, err)
+    while _lib and _lib._quit == 0:
+        _lib.handle_events(1)
+	time.sleep(0.050)
+    if _lib:
+        _lib._quit = 2
+    _Trace(('worker thread exited..',))
+
+def _Trace(args):
+    global enable_trace
+    if enable_trace:
+        print "** ",
+        for arg in args:
+            print arg,
+        print " **"
+