The nua engine hides many low-level signaling and media management aspects from the application programmer. It is possible to use different kind of media interfaces - even remote ones - in a fully transparent way.
The application and the protocol engine within User Agent library can be run in separate threads. Communications from the protocol engine is conveyed through a callback function. The callback function is called within context of the application, so the application must provide appropriate handle to a su_root_t object.
The following sections contain descriptions of the concepts that a user of NUA library must understand to create a working application. The other utilities (in the SU library and other libraries of Sofia software suite) might also be useful for an application developer but one must be careful when using them because they might change the behavior of the Sofia software suite in a way that causes NUA library to work incorrectly. See [su] for more detailed description of the SU services.
The root object is a handle representing the task in the application. Another way of seeing the same thing is that the root object represents the main event loop of the task. Through the root object the task code can access its context information (magic) and thread-synchronization features like wait objects, timers, and messages.
An application using NUA services must create a root object and the callback routine to handle events. The root object is created with su_root_create() function and the callback routine is registered with nua_create() function.
Root object has type su_root_t.
See documentation of <su_wait.h> and <su_root.c> for more information of root object.
See section nua_event_e for more information of the callback function.
An application using NUA services can use the memory management services provided by the SU library but it is not mandatory.
See documentation of <su_alloc.h> for more information of memory management services.
There are some tags with special meaning:
The NUA functions can be called with a list of tagged values if they have following parameters at the end of parameter list:
tag_type_t tag, tag_value_t value, ...);
The last tagged value on the parameter list must be TAG_NULL() (synonym for TAG_END()).
Every tag has two versions:
NUTAG_<tagname>
which takes a value parameter and
NUTAG_<tagname>_REF
which takes a reference parameter and is used with tl_gets() function to retrieve tag values from tag list.
For some Sofia layers (for example SIP) there exists also additional version of tags:
SIPTAG_<tagname>_STR
This tag version takes a C-language character string as parameter. The corresponding tag without _STR suffix takes a parsed value structure as parameter.
The following is an example of call to NUA function containing tagged values:
nua_unregister(op->op_handle, TAG_IF(use_registrar, NUTAG_REGISTRAR(registrar)), SIPTAG_CONTACT_STR("*"), SIPTAG_EXPIRES_STR("0"), TAG_NULL());
An application using NUA services must use tagging for the function parameter passing.
See documentation of <su_tag.h> for more information of tags and the module-specific documentation of each Sofia module for information of tags specific for that module.
Examples of useful directives/ environment variables are:
The defined debug output levels are:
An application using NUA services can also use the debugging and logging services provided by the Sofia stack but it is not mandatory.
See documentation of <su_log.h> for more information of debugging and logging services.
A NUA stack object is created by nua_create() function and deleted by nua_destroy() function. The nua_shutdown() function is used to gracefully release active the sessions by nua engine.
NUA stack object has type nua_t.
An operation handle is created explicitly by the application using NUA for sending messages (function nua_handle()) and by stack for incoming calls/sessions (starting with INVITE or MESSAGE). The handle is destroyed by the application using NUA (function nua_handle_destroy()).
Indication and response events are associated with an operation handle.
NUA operation handle has type nua_handle_t.
The communication between stack thread and application thread is asynchronous. Most of the NUA API functions cause a send of a message to the stack thread for processing and similarly when something happens in the stack thread it sends a message to the application thread. The messages to the application thread are delivered as invokes of the application callback function when the application calls su_root_run() or su_root_step() function.
For example a header named "Example" would have tags names SIPTAG_EXAMPLE(), SIPTAG_EXAMPLE_STR(), and SIPTAG_EXAMPLE_REF().
When tags are used in NUA calls the corresponding headers are added to the message. In case the header can be present only once in a message and there already exists a value for the header the value given by tag replaces the existing header value. Passing tag value NULL has no effect on headers. Passing tag value (void *)-1 removes corresponding headers from the message.
For example:
nua_subscribe(nh, SIPTAG_EVENT_STR("presence"), SIPTAG_ACCEPT(accept1), SIPTAG_ACCEPT(accept2), TAG_END());
sip_accept_t *ac = NULL; sip_event_t *o = NULL; tl_gets(tl, SIPTAG_EVENT_REF(o), /* _REF takes a reference! */ SIPTAG_ACCEPT_REF(ac), TAG_END());
The source distribution of Sofia stack contains in directory nua an example application nua_cli.c that can be studied for more complete example.
/* type for application context data */ typedef struct application application; #define NUA_MAGIC_T application /* type for operation context data */ typedef union oper_ctx_u oper_ctx_t; #define NUA_HMAGIC_T oper_ctx_t
The information area contents themselves can be defined as C structures or unions:
/* example of application context information structure */ typedef struct application { su_home_t home[1]; /* memory home */ su_root_t *root; /* root object */ nua_t *nua; /* NUA stack object */ /* other data as needed ... */ } application; /* Example of operation handle context information structure */ typedef union operation { nua_handle_t *handle; /* operation handle / struct { nua_handle_t *handle; /* operation handle / ... /* call-related information */ } call; struct { nua_handle_t *handle; /* operation handle / ... /* subscription-related information */ } subscription; /* other data as needed ... */ } operation;
NUA stack object and handle are opaque to the application programmer. Likewise, the application context is completely opaque to the NUA stack module. NUA functions are passed a pointer, and that pointer is then given back to the application within the callback parameters. In this case the application context information structure is also used to store a root object and memory home for memory handling. The application context information also contains the NUA stack object information.
If the application is not just responding to incoming SIP messages there must also be means to send messages to NUA. This can be handled for example by having a separate thread that calls NUA functions to send messages or by having a socket connection to the application for sending commands to the application (see documentation of su_wait_create() and su_root_register()).
/* Application context structure */ application appl[1] = {{{{(sizeof appl)}}}}; /* initialize system utilities */ su_init(); /* initialize memory handling */ su_home_init(appl->home); /* initialize root object */ appl->root = su_root_create(appl); if (appl->root != NULL) { /* create NUA stack */ appl->nua = nua_create(appl->root, app_callback, appl, /* tags as necessary ...*/ TAG_NULL()); if (appl->nua != NULL) { /* set necessary parameters */ nua_set_params(appl->nua, /* tags as necessary ... */ TAG_NULL()); /* enter main loop for processing of messages */ su_root_run(appl->root); /* destroy NUA stack */ nua_destroy(appl->nua); } /* deinit root object */ su_root_destroy(appl->root); appl->root = NULL; } /* deinitialize memory handling */ su_home_deinit(appl->home); /* deinitialize system utilities */ su_deinit();
void app_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { switch (event) { case nua_i_invite: app_i_invite(status, phrase, nua, magic, nh, hmagic, sip, tags); break; case nua_r_invite: app_r_invite(status, phrase, nua, magic, nh, hmagic, sip, tags); break; /* and so on ... */ default: /* unknown event -> print out error message */ if (status > 100) { printf("unknown event %d: %03d %s\n", event, status, phrase); } else { printf("unknown event %d\n", event); } tl_print(stdout, "", tags); break; } } /* app_callback */
The place_a_call() function creates an operation handle and invokes the SIP INVITE method.
operation *place_a_call(char const *name, url_t const *url) { operation *op; sip_to_t *to; /* create operation context information */ op = su_zalloc(appl->home, (sizeof *op)); if (!op) return NULL; /* Destination address */ to = sip_to_create(NULL, url); if (!to) return NULL; to->a_display = name; /* create operation handle */ op->handle = nua_handle(appl->nua, op, SIPTAG_TO(to), TAG_END()); if (op->handle == NULL) { printf("cannot create operation handle\n"); return NULL; } nua_invite(op->handle, /* other tags as needed ... */ TAG_END()); } /* place_a_call */
The app_r_invite() function is called by callback function when response to INVITE message is received. Here it is assumed that automatic acknowledge is not enabled so ACK response must be sent explicitly.
void app_r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { if (status == 200) { nua_ack(nh, TAG_END()); } else { printf("response to INVITE: %03d %s\n", status, phrase); } } /* app_r_invite */
The app_i_state() function is called by callback function when call has been successfully set up and the media has been activated.
void app_i_active(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("call active\n"); } /* app_i_active */
void app_i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("incoming call\n"); nua_respond(nh, 200, "OK", TAG_END()); } /* app_i_invite */
The app_i_active() function is called by the callback function when call has been successfully set up and the media has been activated.
void app_i_active(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("call active\n"); } /* app_i_active */
The terminate_call() function sends the SIP BYE message.
void terminate_call(void) { nua_bye(op->handle, TAG_END()); } /* terminate call */
The app_r_bye() function is called by the callback function when answer to the BYE message is received. The function destroys the call handle and releases the memory allocated to operation context information.
void app_r_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { if (status < 200) return; printf("call released\n"); /* release operation handle */ nua_handle_destroy(hmagic->handle); op->handle = NULL; /* release operation context information */ su_free(appl->home, hmagic); } /* app_r_bye */
The app_i_bye() function is called by the callback function when an incoming BYE message is received. The function destroys the call handle and releases the memory allocated to operation context information.
void app_i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("call released\n"); /* release operation handle */ nua_handle_destroy(hmagic->handle); op->handle = NULL; /* release operation context information */ su_free(appl->home, hmagic); } /* app_i_bye */
The send_message() function sends the SIP MESSAGE.
void send_message(void) { op_t *op; /* create operation context information */ op = su_zalloc(appl->home, sizeof(op_t)); if (op = NULL) { printf("cannot create operation context information\n"); return; } /* how we create destination_address? */ /* create operation handle */ op->handle = nua_handle(appl->nua, op, NUTAG_URL(destination_address), TAG_END()); if (op->handle == NULL) { printf("cannot create operation handle\n"); return; } /* send MESSAGE */ nua_message(op->handle, SIPTAG_CONTENT_TYPE_STR("text/plain"), SIPTAG_PAYLOAD_STR("Hello, world!"), /* other tags as needed ... */ TAG_END()); } /* send_message */
The app_r_message() function is called by the callback function when answer to the MESSAGE is received.
void app_r_message(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("response to MESSAGE: %03d %s\n", status, phrase); } /* app_r_message */
The app_i_message() function is called by the callback function when a SIP MESSAGE is received.
void app_i_message(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("received MESSAGE: %03d %s\n", status, phrase); printf("From: %s%s" URL_PRINT_FORMAT "\n", sip->sip_from->a_display ? sip->sip_from->a_display : "", sip->sip_from->a_display ? " " : "", URL_PRINT_ARGS(sip->sip_from->a_url)); if (sip->sip_subject) { printf("Subject: %s\n", sip->sip_subject->g_value); } if (sip->sip_payload) { fwrite(sip->sip_payload->pl_data, sip->sip_payload->pl_len, 1, stdout); fputs("\n", stdout); } } /* app_i_message */
... application_t *app; operation_t *oper; ... oper->app = app; app->nua = nua_create(ssip->s_root, app_callback, app, TAG_NULL()); ... oper->handle = nua_handle(app->nua, app, NUTAG_URL(to->a_url), SIPTAG_TO(to), ta_tags(ta)); ... nua_notifier(oper->handle, SIPTAG_EXPIRES_STR("3600"), SIPTAG_EVENT_STR("presence"), SIPTAG_CONTENT_TYPE_STR("application/pidf-partial+xml"), NUTAG_SUBSTATE(nua_substate_pending), TAG_END());
After the nua_notifier object -- the presence server -- is created, an event nua_r_notifier is returned. Status and phrase values of the app_callback function indicate the success of the creation.
Authorization of an incoming subscription (to the local presence server) can be handled in the callback function.
void app_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, application_t *app, nua_handle_t *nh, oper_t *op, sip_t const *sip, tagi_t tags[]) { nea_sub_t *subscriber = NULL; switch (event) { case nua_i_subscription: tl_gets(tags, NEATAG_SUB_REF(subscriber), TAG_END()); nua_authorize(nua_substate_active); default: break; }
The shutdown() function starts the termination.
void shutdown(void) { nua_shutdown(appl->nua); } /* shutdown */
The app_r_shutdown() function is called by the callback function when NUA stack termination is either finished or failed.
void app_r_shutdown(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { printf("shutdown: %d %s\n", status, phrase); if (status < 200) { /* shutdown in progress -> return */ return; } /* end the event loop. su_root_run() will return */ su_root_break(magic->root); } /* app_r_shutdown */