wiki:Python_SIP/Accounts

Version 5 (modified by bennylp, 11 years ago) (diff)

--

SIP Accounts

Accounts provide identity (or identities) of the user who is currently using the application. An account has one SIP Uniform Resource Identifier (URI) associated with it. 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 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.

At least one account MUST be created in the application, since any outgoing requests require an account context. If no user association is required, application can create a user-less account by calling lib.create_account_for_transport(). A userless account identifies local endpoint instead of a particular user, and it corresponds to a particular transport instance.

Also one account must be set as the default account, which will be used as the account identity when pjsua fails to match the request with any accounts using the stricter matching rules.

Creating Userless Accounts

A user-less account is an account without user ID (duh!). It identifies a particular SIP endpoint rather than a particular user. Some other SIP softphones may call this peer-to-peer mode, which means that we are calling another computer via its address rather than calling a particular user ID.

So for example, we might identify ourself as "sip:192.168.0.15" (a user-less account) rather than "sip:user@pjsip.org" (an account).

In pjsua, a user-less account may be created for each particular transport instance. Creating user-less account is very simple, all we need is the transport instance which is returned by lib.create_transport() method as explained in previous chapter.

Here's a snippet:

acc_cb = MyAccountCallback()
acc = lib.create_account_for_transport(udp, cb=acc_cb)

The account callback will be explained later. Basically it is used to capture events emitted by the account, such as incoming call events.

Once the account is created, you can use the instance as a normal account. More will be explained later.

Accounts created this way will have its URI derived from the transport address. For example, if the transport address is "192.168.0.15:5080", then the account's URI for UDP transport will be "sip:192.168.0.15:5080", or "sip:192.168.0.15:5080;transport=tcp" for TCP transport.

Creating Account

For the "normal" account, we need to configure AccountConfig and call lib.create_account() to create the account.

At the very minimum, pjsua only requires the account's ID, which is an URI to identify the account (or in SIP terms, it's called Address of Record/AOR). Here's a snippet:

try:
    acc_cfg = pjsua.AccountConfig()
    acc_cfg.id = "sip:user@pjsip.org"

    acc_cb = MyAccountCallback()
    acc = lib.create_account(acc_cfg, cb=acc_cb)

except pjsua.Error, err:
    print 'Error creating account:', err

Again the account callback will be explained later.

The account created above doesn't do anything except to provide identity in the "From:" header for outgoing requests. The account will not register to SIP server or anything.

Typically you will want the account to authenticate and register to your SIP server so that you can receive incoming calls. To do that you will need to configure some more settings in your AccountConfig, something like this:

try:
    acc_cfg = pjsua.AccountConfig()
    acc_cfg.id = "sip:someuser@pjsip.org"
    acc_cfg.reg_uri = "sip:pjsip.org"
    acc_cfg.proxy = [ "sip:pjsip.org;lr" ]
    acc_cfg.auth_cred = [ AuthCred("*", "someuser", "secretpass") ]

    acc_cb = MyAccountCallback()
    acc = lib.create_account(acc_cfg, cb=acc_cb)

except pjsua.Error, err:
    print 'Error creating account:', err

Or alternatively, for typical account settings like above, you can build the account config like this:

    acc_cfg = pjsua.AccountConfig("pjsip.org", "someuser", "secretpass")

More on Account Settings

There are many more settings that can be specified in AccountConfig, like:

  • whether reliable provisional response (SIP 100rel) is required
  • whether presence publication (PUBLISH) is enabled
  • Secure RTP (SRTP) related settings
  • etc.

Please see AccountConfig reference documentation for more info.

Account Operations

Some of the operations to the Account object:

  • make outgoing calls
  • add buddy objects
  • set account's presence status
  • stop/start SIP registration

Please see the reference documentation for Account (link above) for more info. Calls, presence, and buddy list will be explained in later chapters.

Account Callbacks

Account object emits events, such as:

  • the status of SIP registration
  • incoming calls
  • incoming presence subscription requests
  • incoming instant message not from buddy

These events will be reported to AccountCallback instance that is registered to the account object. Application MUST install a class derived from AccountCallback class if it wants to handle these events.

If these events are not handled, default actions will be invoked:

  • incoming calls will be rejected
  • incoming presence subscription requests will be accepted
  • incoming instant messages from non-buddy will be ignored

Creating Custom Callback Class

To create a custom callback class to handle the events, derive a class from AccountCallback and override the relevant class method to handle the particular events. For example:

class MyAccountCallback(pjsua.AccountCallback):
    def __init__(self, account=None):
        pjsua.AccountCallback.__init__(self, account)

    def on_incoming_call(self, call):
        call.hangup(501, "Sorry, not ready to accept calls yet")

    def on_reg_state(self):
        print "Registration complete, status=", self.account.info().reg_status, \
              "(" + self.account.info().reg_reason + ")"

In the account callback, you can refer the Account instance of this callback with self.account.

Installing Your Callback to Account Object

Once you've created the custom callback class, you can install the callback by specifying your callback instance when creating the account, like the previous examples. Here they are again:

    my_cb = MyAccountCallback()
    acc = lib.create_account_for_transport(udp, cb=my_cb)

or

    acc_cfg = pjsua.AccountConfig("pjsip.org", "someuser", "secretpass")
    my_cb = MyAccountCallback()
    acc = lib.create_account(acc_cfg, cb=my_cb)

(Note: we omit error handling for brevity)

That is the preferred way to install the callback, i.e. to specify the callback instance when creating the account. However, if for some reason you cannot do this, you may install the callback later using Account's set_callback() method. You SHOULD, however, protect the code fragment with library lock to avoid loosing events. A sample code to do this:

    # Create account without callback. Protect the fragment with the
    # library lock below.
    lck = lib.auto_lock()
    acc_cfg = pjsua.AccountConfig("pjsip.org", "someuser", "secretpass")
    acc = lib.create_account(acc_cfg)

    # Do something else
    ...

    # Create and install the callback
    my_cb = MyAccountCallback()
    acc.set_callback(my_cb)

    # Release library lock
    del lck

Without the library lock like above, it is possible that some events may be delivered to the account while the callback has not been installed yet, thus we're loosing the events. The lock will prevent that from happening (in this case).

However, please bear in mind that while you're holding the library lock, the worker thread can't run, so incoming SIP messages will be queued in socket buffer and some timers may be delayed. So please use the library lock wisely.