The API is pretty minimalistic. It is limited to a single call at a time and provides the following functionality:
- Register to the PBX
- Make a call or accept a call
- Send audio files
- Receive DTMF tones
- Hang up
This is all I needed and I guess it brings ToT devices a big step ahead (and also helps the beginner to softly make his feet wet with the complex world of SIP, SDP, and RDP).
Flexosip
uses a minimalistic approach on callback functions. Similar
to inheritance in object-oriented languages, flexosip
already
provides an implementation of all the callbacks. However, they can be
overridden in your application by just defining them again with the
same name.
(There is no magic to this, but just the power of Weak symbols. And no, it does not allow real subclassing or multiple inheritance or anything, just overriding the default implementation once.)
#include "flexosip/flexosip.h"
#include "flexosip/flexosnd.h"
#include "flexosip/flexortp.h"
To initialize debugging, call TRACE_INITIALIZE(OSIP_INFO1, NULL);
, if
you want to. (Instead of OSIP_INFO1
, you can also use another
verbosity level.)
If you want some cleanup to happen when you Ctrl-C the program, add the following:
signal(SIGINT, fesip_cleanup);
signal(SIGHUP, fesip_cleanup);
signal(SIGTERM, fesip_cleanup);
signal(SIGQUIT, fesip_cleanup);
And then, initialize the network subsystem:
fesip_listen(IPPROTO_UDP, false, 0);
Other options are described in flexosip.h
.
Before you can make or receive a call, you need to register with your PBX (aka your telephone switch or VoIP exchange). The PBX is know as the registrar in SIP parlance.
You do this by calling
fesip_register(uri, registrar, login, password);
where the uri
is typically of the form sip:<extension>@<registrar>
and the registrar sip:<hostname or domain>
, e.g.
sip:cow-bell@fritz.box
and sip:fritz.box
, respectively.
The SIP registration (like almost all SIP messages) happen in the background while your code continues executing. If you want to be sure that the registration was successful (i.e., correct credentials were supplied) or need the successful registration to continue (namely, making an outgoing call), you can do
ok = fesip_wait_registered();
which will wait and return < 0
for failure. This will wait up to
about 10 seconds or for two registration failures. (One registration
failure is normal, as the first registration attempt is done without
credentials.)
Now, your device is registered and can send and receive calls.
In your main code, you will need an event loop as follows:
while (1) {
// Handle SIP and RTP (audio)
fesip_handle_event();
// Do your own stuff
…
}
fesip_handle_event()
will sleep between 10 and 20 ms, depending on
what it has to do.
The rest of your processing loop should not take more than 10 ms, unless you know that no audio is currently playing.
When an incoming call arrives, the event handler will call your
fesip_event_invite()
callback:
int fesip_event_invite(eXosip_event_t *evt,
const char *host, int port, int format)
This is passed the incoming call event, the host and port that audio should go to, and the expected audio format (currently only PCMA/8000, aka 8 kHz A-Law). For now, you probably want to ignore them all.
To decline the call, just return SIP_BUSY_HERE
.
However, if the call should be accepted, that function should return
SIP_RINGING
for now and set a variable, which will trigger accepting
the connection in the event loop. This is necessary due to the locking
that happens in fesip_handle_event()
.
In general, you cannot call any `fesip_*()` library functions from
any of the event handlers (`fesip_event_*()`). The only exception
currently is `fesip_play()`.
Outside the event handler, back in the event loop, you call
fesip_answer();
to accept the call. If you want the caller to hear some ringing first, feel free to do this only after a few seconds.
You can then send an audio message with
fesip_play(filename);
where filename
is any 16 kHz mono file understood by
libsndfile
. I use .ogg
files.
fesip_play()
actually adds the file to the end of the play queue, so
you can call it multiple times. To stop playing and clear the play
queue (e.g., on an incoming DTMF event), call
fesnd_close();
(notice the fesnd_*
prefix).
To make a call, use
fesip_call(from, to, subject, NULL);
where from
is your own SIP address, to
is the destination's, and
subject
is the subject trasmitted (may or may not be displayed).
When the remote side declines, your
void fesip_event_terminate(eXosip_event_t *evt);
Callback will be called (which you do not need to implement, if you do not care). This you will get on all call terminations, incoming or outgoing, pending or active.
When it answers, however, the callback will be
void fesip_event_answered(eXosip_event_t *evt);
There, you can decline the call with fesip_terminate_nolock()
(created especially to be called from a callback) or start playing
audio as explained above.
When the remote side — in an incoming or outgoing call — presses a key,
void fesip_event_dtmf(char c);
is called with the ASCII character corresponding to the key pressed (typically, '0'…'9', '*', '#').
That is already everything you need to know. Now you can start your own ToT device!