= PJSUA Python Module = [[TracNav(Python_SIP_TOC)]] The PJSUA for Python Module is an object oriented Python wrapper/abstraction for [http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB.htm PJSUA API]. It provides high level API for constructing SIP multimedia user agent applications. It wraps together the signaling, media, and NAT traversal functionality into an easy to use call control API, account management, buddy list management, presence, instant messaging, along with multimedia features such as local conferencing, file streaming, local playback, and voice recording, and powerful NAT traversal techniques utilizing STUN, TURN, and ICE. == What is it? == This is the new Python wrapper for [http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB.htm PJSUA API] which is available in PJSIP version 0.9.5 and later. It is much easier to use, much more Python-ish, and it deprecates the old [wiki:Py_PJSUA py_pjsua] Python module. The Python wrapper is implemented in two modules: '''pjsua''' module: :: This is the higher level abstraction for [http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB.htm PJSUA API]. It is object oriented and implemented purely on Python, on top of {{{_pjsua}}} module, and it is the one described on this article. '''_pjsua''' module: :: The "_pjsua" module (with underscore) is the low-level C Python module which provides Python binding to [http://www.pjsip.org/pjsip/docs/html/group__PJSUA__LIB.htm PJSUA API]. This module is the successor of [wiki:Py_PJSUA py_pjsua] module which now has been deprecated. Applications should use the '''pjsua''' module rather than '''_pjsua''' module, since it is easier to use and it is the module which API compatibility will be maintained between releases. == Getting the Module == #getting The Python module source codes along with some samples are included in PJSIP distribution, in [source:pjproject/trunk/pjsip-apps/src/python pjproject/pjsip-apps/src/python] directory. == Building The Modules == #build Using Microsoft Visual Studio projects: * Open '''pjsip-apps.dsw''' from {{{pjsip-apps\build}}} directory. * Select '''python_pjsua''' project as the active project. * Build the project * The {{{_pjsua.pyd}}} Python module will be placed in {{{pjsip-apps\lib}}} directory. * Copy {{{_pjsua.pyd}}} and {{{pjsua.py}}} from {{{pjsip-apps\src\python}}} directory to Python's site_packages directory (see Python manual for this), or alternatively add the directories where these files reside to your {{{PYTHONPATH}}} environment variable. Using Python build script: * Go to {{{pjsip-apps/src/python}}} directory. * Run '''{{{'python ./setup.py build'}}}''' * The Python module will be placed in {{{build}}} directory inside current directory. * Alternatively run '''{{{'python ./setup.py install'}}}''' to install both '''_pjsua''' and '''pjsua''' modules to Python's site_packages directory. [[BR]] == Developing Python SIP Application == #develop === Concepts === #concepts ==== Asynchronous Operations ==== If you have developed applications with PJSIP you'll know about this already, but this concept probably needs to be explained a little bit here to new PJSIP users. In PJSIP, all operations that involve sending and receiving SIP messages are asynchronous, meaning that the function that invokes the operation will complete immediately, and you will be given the completion status as callbacks. Take a look for example the {{{make_call()}}} method of the [http://www.pjsip.org/python/pjsua.htm#Account Account] class. This function is used to initiate outgoing call to a destination. When this function returns successfully, it does not mean that the call has been established, but rather that the call has been '''initiated''' successfully. You will be given the report of the call completion (such as ''Ringing'' or ''Connected''/''Confirmed''' events) in the {{{on_state()}}} callback of [http://www.pjsip.org/python/pjsua.htm#CallCallback CallCallback] class. ==== Basic Usage Pattern ==== Ah, talking about callbacks. ==== Error Handling ==== By convention, we use exceptions as means to report error, as this would make the program flows more naturally. Operations which yield error will raise [http://www.pjsip.org/python/pjsua.htm#Error pjsua.Error] exception. Here is a sample: {{{ #!python import pjsua try: call = acc.make_call('sip:buddy@example.org') except pjsua.Error, err: print 'Exception has occured:', err except: print 'Ouch..' }}} The sample above will print the full error information to stdout. If you prefer to display the error in more structured manner, the [http://www.pjsip.org/python/pjsua.htm#Error pjsua.Error] class has several members to explain the error, such as the object name that raised the error, the operation name, and the error message itself. == Lib Class == The [http://www.pjsip.org/python/pjsua.htm#Lib Lib] class provides the base API's to communicate with PJSUA-API and to create objects (such as [http://www.pjsip.org/python/pjsua.htm#Account Account] and [http://www.pjsip.org/python/pjsua.htm#Transport Transport]). [[BR]] ==== Initializing the Library ==== Instantiate the library: {{{ #!python import pjsua as pj lib = pj.Lib() }}} then initialize and start the library. {{{ #!python try: lib.init() lib.start() except pj.Error, e: print "Error initializing library:", e }}} Both the {{{init()}}} and {{{start()}}} methods above may be given additional parameters. Please see [http://www.pjsip.org/python/pjsua.htm#Lib Lib] class reference manual for more information. [[BR]] == Transport == Application needs to create one or more [http://www.pjsip.org/python/pjsua.htm#Transport Transport] objects before it can send or receive SIP messages: {{{ #!python try: udp = lib.create_transport(pj.TransportType.UDP) except pj.Error, e: print "Error creating transport:", e }}} [[BR]] == Accounts == Application must create at least one [http://www.pjsip.org/python/pjsua.htm#Account Account] before it can send and receive SIP messages. An account specifies the '''From:''' URI, so it's needed before you can send SIP messages. There are two types of accounts in pjsua: * real account: this is an account that can register to a SIP server * transport account: this corresponds to one [http://www.pjsip.org/python/pjsua.htm#Transport Transport]. So for example if we have created UDP transport which listens to {{{192.168.0.1:5080}}}, the transport account will have URI: "{{{sip:192.168.0.1:5080}}}" (rather than, say, "{{{sip:user@domain}}}"). There can be more than one accounts in an application. [[BR]] ==== Creating Accounts ==== To create transport account: {{{ #!python try: acc = lib.create_account_for_transport(udp) except pj.Error, e: print "Error creating UDP local account:", e }}} To create a real account account, first you must configure an [http://www.pjsip.org/python/pjsua.htm#AccountConfig AccountConfig], then create the account: {{{ #!python try: acc_cfg = pj.AccountConfig() acc_cfg.id = "sip:user@pjsip.org" acc_cfg.reg_uri = "sip:pjsip.org" acc_cfg.proxy = [""] acc_cfg.auth_cred = [pj.AuthCred("*", "user", "password")] acc = lib.create_account(acc_cfg, True) except pj.Error, e: print "Error creating account:", e }}} Alternatively, for typical account config like above, we can do like this: {{{ #!python try: acc = lib.create_account(pj.AccountConfig("pjsip.org", "username", "password"), True) except pj.Error, e: print "Error creating account:", e }}} [[BR]] ==== Getting Events from Account ==== [http://www.pjsip.org/python/pjsua.htm#Account Account] object emits events such as incoming call and registration state. To capture events from [http://www.pjsip.org/python/pjsua.htm#Account Account], first you need to derive your account callback class from [http://www.pjsip.org/python/pjsua.htm#AccountCallback AccountCallback] class and implement the relevant callback methods: {{{ #!python class MyAccountCallback(pj.AccountCallback): def __init__(self, account): pj.AccountCallback.__init__(self, account) def on_reg_state(self): print "Account", self.account.info().uri, print "registration status is", self.account.info().reg_reason def on_incoming_call(self, call): print "Incoming call from", call.info().remote_uri call.answer(200) }}} (Note: we've touched the [http://www.pjsip.org/python/pjsua.htm#Call Call] object a little bit above, that will be explained later). Then install the callback to [http://www.pjsip.org/python/pjsua.htm#Account Account] object: {{{ #!python acc_cb = MyAccountCallback(acc) acc.set_callback(acc_cb) }}} [[BR]] ==== Account Sample Application ==== For a complete account sample application (including registration), please see source:pjproject/trunk/pjsip-apps/src/python/samples/registration.py [[BR]] == Calls == ==== Creating Calls ==== Incoming call events are reported via [http://www.pjsip.org/python/pjsua.htm#AccountCallback AccountCallback]'s {{{on_incoming_call()}}} callback as shown above. To make outgoing call: {{{ #!python try: call = acc.make_call("sip:buddy@pjsip.org") except pj.Error, e: print "Error in making call:", e }}} Note that as with all PJSIP operations, the {{{make_call()}}} function is asynchronous; it will not block until the call is connected, but rather it will return immediately as soon as the initial INVITE request is sent. Application is then informed about the call completion via [http://www.pjsip.org/python/pjsua.htm#CallCallback CallCallback] object (see below). [[BR]] ==== Getting Events from Call ==== To retrieve events from a call, derive a class from [http://www.pjsip.org/python/pjsua.htm#CallCallback CallCallback] class and implement the methods that you want to be notified about. Normally at the very least you'd want to implement {{{on_state()}}} and {{{on_media_state()}}} methods: {{{ #!python class MyCallCallback(pj.CallCallback): def __init__(self, call): pj.CallCallback.__init__(self, call) def on_state(self): print "Call with", self.call.info().remote_uri, print "is", self.call.info().state_text def on_media_state(self): if self.call.info().media_state == pj.MediaState.ACTIVE: # Connect the call to sound device call_slot = self.call.info().conf_slot pj.Lib.instance().conf_connect(call_slot, 0) pj.Lib.instance().conf_connect(0, call_slot) print "Media is now active" else: print "Media is inactive" }}} Then install your callback to the call object: {{{ #!python call_cb = MyCallCallback(call) call.set_callback(call_cb) }}} [[BR]] ==== Call Sample Application ==== For a complete call sample application, please see source:pjproject/trunk/pjsip-apps/src/python/samples/call.py [[BR]] == Presence and Instant Messaging == (The doc is TBD) An [http://www.pjsip.org/python/pjsua.htm#Account Account] has a presence status associated with it, and when the presence status is changed (with {{{set_basic_status()}}} or {{{set_presence_status()}}}), the changes will be propagated to presence subscriber with using either PUBLISH or NOTIFY SIP methods. To subscribe to buddy's presence status, application creates [http://www.pjsip.org/python/pjsua.htm#Buddy Buddy] object using account's {{{add_buddy()}}} method. Changes in buddy's presence status will be reported via [http://www.pjsip.org/python/pjsua.htm#BuddyCallback BuddyCallback] class, which must be derived and then installed to the Buddy object. For a complete presence and instant messaging sample application, please see source:pjproject/trunk/pjsip-apps/src/python/samples/presence.py [[BR]] == Working with Media == The [http://www.pjsip.org/python/pjsua.htm#Lib Lib] class provides API to manage media, such as: * create WAV file player or recorder * manage conference bridge connections * manage codecs * etc. [[BR]] = Reference Documentation = Please see [http://www.pjsip.org/python/pjsua.htm pjsua] Python module documentation for reference.