Inspired by Andrijan Möcker’s “Überallklingel” (Everywhere Bell), I decided to create my own. However, I wanted to use it with a motion sensor and therefore to be able to turn it off and on again, when necessary. I also felt that using Asterisk was overkill and it could be done quickly and much easier with a SIP VoIP library. Boy, was I mistaken! But I believe the result was worth all the pain.
Design goals
As detailed in the previous parts of this series, the small device should be able to initiate a call to have the phone ring when some activity is detected, i.e., someone entering. Also, someone should be able to call the device on the phone to turn the alerting function on and off. There, a simple Interactive Voice Response menu should be presented, where the current state is presented, changes can be initiated using DTMF key presses, and they would be confirmed. Nothing too unexpected, so far.
Looking for a SIP library
With SIP, the Session Initiation Protocol, being so popular for Voice over IP applications, I thought that finding a nice SIP library would be a breeze.
In fact, there are tons of those libraries. However, most of them only handle sending individual low-level protocol messages, of which there are a dozen or so until a call can be made. Even the ones touted “high-level libraries” were in a form where you had to write dozens, if not hundreds of lines of code just to initiate or accept a voice call. Several did not have examples or high-level documentation. And the interactions between SIP, RTP and the sound encodings were numerous and complicated. For a SIP newbie, this was too much of a hurdle.
The one which looked most promising, was PJSIP‘s PJSUA (Simple User Agent): Step-by-step documentation, multiple useful examples, Python bindings. I immediately started writing my code. It looked like a relaxed winter afternoon project.
Giving up
Then, the problems started: The program would start crashing somewhere in the automatically generated Python-to-C bindings. A few hours of trying and debugging later, I gave up. (And neither did I want to get involved with Java, C++, nor C#.)
Reinvigoration
However, I was too stubborn to give up completely after having already gone so far[1]This is why some multi-million IT projects require much more money than originally planned. So I found a pure C library, eXosip, based on osip and ortp. eXosip had the best documentation and examples seen so far. This sounded convincing. And motivating.
However, I still found “high-level API” to be an euphemism, even for eXosip.
FlexoSIP is born
So a higher-level interface had to be created. As eXosip added the “eX” prefix to “osip” to indicate a higher abstraction layer, I wanted to add another prefix for the even higher level of abstraction. “Fl” was the only thing that came to mind. It does not stand so much for flexibility (in fact, flexibility often comes at the price of complexity, which I wanted to reduce). Instead, it stands for “fast lane”, the quick entry into the SIP world.[2]FlexoSIP, an end-point library, is not to be confused with Flexisip, a SIP server.
Simplifications
One of the restrictions: It can only handle a single call (incoming or outgoing) at a time. Also, FlexoSIP cannot receive audio, just send it. And it can only send aLaw 8 kHz sounds. Despite being written in C, It also allows a simple form of function overloading based on weak symbols: Some functions are pre-implemented in FlexoSIP, but can be overridden in the main application.
Operations
As a result, making a call or receiving a call is simple:
- Call fesip_listen() to enable networking
- Call fesip_register() to register itself as a SIP endpoint with the telephone switch or PBX
- Call fesip_wait_registered() to check for success
- To create an outgoing call, use fesip_call() to initiate it
- Then enter the main event loop, where you call fesip_handle_event() regularly. This uses callbacks to notify you.
- One of this callbacks is fesip_event_answered(), when the call was picked up.
- There you can use fesip_play() to send a recorded message.
- Another event is fesip_event_dtmf() to indicate the other side pushed a button.
- When the call is terminated, fesip_event_terminated() is called.
Source code
The source code for FlexoSIP is on GitHub and includes a simple demo application doing exactly what has been described above. In the 113 lines of code, it also shows how to parse parameters from config files and how multiple playout messages can be enqueued. I also created a high-level, application-driven documentation. It can be used as an entry point into understanding SIP and RTP, together with the source code.
Feel free to use FlexoSIP in any of your applications!
And, as it is free software, do not hesitate to use your four freedoms!