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:
Computer:
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):
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.
Definition of a logging function for later callbacks.
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.
Method | Description |
|---|---|
<p><b>init(self, call=None)</b> <br></p> | Will be called after a new class instance was created; connect the class to a specific call object.<br> |
<p><strong>on_state(self)</strong></p><ul><br></ul> | Print the status of the call to the console (when status has changed), handle status of call.<ul></ul><br> |
Now, we are ready to take a look into the execution logic of the script, starting at the bottom.
As we can see, the main() function of the script gets invoked first.
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.
Initializing of the pjsua lib instance, create transport (type UDP). Then set status of call to "start".
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.
Finally, clean up and delete the lib instance.
receive_call.py
A usually, imports and parameter defintion.
Then, helper functions to save the process ID, logging the callbacks and play the audio file.
Now 2 classes to handle callbacks from a call. For an explanation, please the Apx II inside the SIP Announcement Player Code Documentation document.
Execution starts at the bottom of the script.
We jump to the main() function. Invoke the function to save the process ID from above and create a pjsua library instance.
Initialize the new library instance with the callback logging function and verbosity level. Create SIP transport instance of the specified type (UDP).
If the scirpt is called with an additional argument "show_uri", print the SIP uri to the console (see Usage section above).
Start the library and create an account for transport, attaching the MyAccountCallback.
Main (menu) loop to to handle incoming call.
Shut down the library instance after everything is done.