Version 1 (modified by bennylp, 14 years ago) (diff)


PJSIP Tutorial (Using PJSUA-API)

As you can see from the diagram in PJSIP Documentation page, PJSIP software consists of multiple API abstractions. This tutorial uses PJSUA-API, the highest layer of abstraction of all, which combines PJSIP (the SIP stack library) and PJMEDIA (the media stack library).

A Simple SIP User Agent

Is it possible to create a simple but rather complete SIP user agent, with registration, digest authentication, SDP negotiation, and fully featured media in under than 100 lines of code?

Well, lets see.

#include <pjsua-lib/pjsua.h>

#define THIS_FILE   "APP"

static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
			     pjsip_rx_data *rdata)
    pjsua_call_info ci;

    pjsua_call_get_info(call_id, &ci);

    PJ_LOG(3,(THIS_FILE, "Incoming call from %.*s!!",

    // Automatically answer incoming call with 200/OK
    pjsua_call_answer(call_id, 200, NULL, NULL);

static void on_call_media_state(pjsua_call_id call_id)
    pjsua_call_info ci;

    pjsua_call_get_info(call_id, &ci);

    if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
	// When media is active, connect call to sound device.
	pjsua_conf_connect(ci.conf_slot, 0);
	pjsua_conf_connect(0, ci.conf_slot);

static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
    pjsua_call_info ci;

    pjsua_call_get_info(call_id, &ci);
    PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,

static void error_exit(const char *title, pj_status_t status)
    pjsua_perror(THIS_FILE, title, status);

int main(int argc, char *argv[])
    pjsua_acc_id acc_id;
    pj_status_t status;

    // Create pjsua first!
    status = pjsua_create();
    if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);

    // If argument is specified, it's got to be a valid SIP URL
    if (argc > 1) {
	status = pjsua_verify_sip_url(argv[1]);
	if (status != PJ_SUCCESS) error_exit("Invalid URL in argument", status);

    // Init pjsua
	pjsua_config cfg;
	pjsua_logging_config log_cfg;

	cfg.cb.on_incoming_call = &on_incoming_call;
	cfg.cb.on_call_media_state = &on_call_media_state;
	cfg.cb.on_call_state = &on_call_state;

	log_cfg.console_level = 4;

	status = pjsua_init(&cfg, &log_cfg, NULL);
	if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);

    // Add UDP transport.
	pjsua_transport_config cfg;

	cfg.port = 5060;
	status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);
	if (status != PJ_SUCCESS) error_exit("Error creating transport", status);

    // Initialization is done, now start pjsua
    status = pjsua_start();
    if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);

    // Register to SIP account.
	pjsua_acc_config cfg;

	pjsua_acc_config_default(&cfg); = pj_str("");
	cfg.reg_uri = pj_str("");
	cfg.cred_count = 1;
	cfg.cred_info[0].realm = pj_str("");
	cfg.cred_info[0].scheme = pj_str("digest");
	cfg.cred_info[0].username = pj_str("bulukucing");
	cfg.cred_info[0].data_type = 0;
	cfg.cred_info[0].data = pj_str("netura");

	status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
	if (status != PJ_SUCCESS) error_exit("Error adding account", status);

    // If URL is specified, make call to the URL.
    if (argc > 1) {
	pj_str_t uri = pj_str(argv[1]);
	status = pjsua_call_make_call(acc_id, &uri, 0, NULL, NULL, NULL);
	if (status != PJ_SUCCESS) error_exit("Error making call", status);

    // Wait until user press "q" to quit.
    for (;;) {
	char option[10];

	puts("Press 'q' to quit");
	fgets(option, sizeof(option), stdin);
	if (option[0] == 'q')

    // Destroy pjsua

    return 0;