P2P SIP Call with Python and PJSUA

Goal

Establish a SIP call between your own computer and an embedded device within the same network. A script on the device will detect an incoming call and asks the user to accept through the command line. If accepted, an audio file from the file system of the device will be played.

Overview

We have to write 2 scripts:

*make_call.py *

  • On the computer, making a call to the SIP uri (address) of the device

receive_call.py

  • On the device, receiving the call and accepting output of audio

Usage

The order is not required, but we suggest starting the scripts like this.

Device:

Bash
Copy

Computer:

Bash
Copy

Example: python make_call.py sip:192.168.XX.XXX:port

If you don't know the SIP uri of your device, invoke the script like this (on the device):

Bash
Copy

This will print the SIP uri to the console and you can copy it to your computer.

In-Depth Explanation

make_call.py

First, import modules and decleration of a LOG_LEVEL variable, which specifies the input verbosity level of our callbacks. We also define a CALL_STATUS dictionary which we will use to change the state of our later call.

Python
Copy

Definition of a logging function for later callbacks.

Python
Copy

All operations that involve sending and receiving SIP messages are asynchronous. Functions that invoke an operation will complete immediately and will give the completion status as callbacks to our running program.

Before we can have a look at our execution pipeline, we need to define a MyCallCallback(pjsua.CallCallback) class. This class inherits attributes/methods from the pjsua.CallCallback class.

It is used to receive/handle events from an ongoing call.

Python
Copy
MethodDescription
init(self, call=None) 
Will be called after a new class instance was created; connect the class to a specific call object.

on_state(self)


Print the status of the call to the console (when status has changed), handle status of call.


    Now, we are ready to take a look into the execution logic of the script, starting at the bottom.

    Python
    Copy

    As we can see, the main() function of the script gets invoked first.

    Python
    Copy

    Here, we check if the script was called with an additional argument specified (i.e. the SIP uri of our device). If no argument was provided, the script will stop (sys.exit(1)). See Section "Usage" for more info.

    Python
    Copy

    Initializing of the pjsua lib instance, create transport (type UDP). Then set status of call to "start".

    Python
    Copy

    This is the main (menu) loop of the program. Here we wait till the callbacks of the call arrive and handle the call status accordingly.

    Python
    Copy

    Finally, clean up and delete the lib instance.

    receive_call.py

    A usually, imports and parameter defintion.

    Python
    Copy

    Then, helper functions to save the process ID, logging the callbacks and play the audio file.

    Python
    Copy

    Now 2 classes to handle callbacks from a call. For an explanation, please the Apx II inside the SIP Announcement Player Code Documentation document.

    Python
    Copy

    Execution starts at the bottom of the script.

    Python
    Copy

    We jump to the main() function. Invoke the function to save the process ID from above and create a pjsua library instance.

    Python
    Copy

    Initialize the new library instance with the callback logging function and verbosity level. Create SIP transport instance of the specified type (UDP).

    Python
    Copy

    If the scirpt is called with an additional argument "show_uri", print the SIP uri to the console (see Usage section above).

    Python
    Copy

    Start the library and create an account for transport, attaching the MyAccountCallback.

    Python
    Copy

    Main (menu) loop to to handle incoming call.

    Python
    Copy

    Shut down the library instance after everything is done.

    Python
    Copy
    Type to search, ESC to discard
    Type to search, ESC to discard
    Type to search, ESC to discard