= PJSIP Tutorial (Using PJSUA-API) = As you can see from the diagram in [/docs.htm PJSIP Documentation] page, PJSIP software consists of multiple API abstractions. This tutorial uses [/pjsip/docs/html/group__PJSUA__LIB.htm 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 #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!!", (int)ci.remote_info.slen, ci.remote_info.ptr)); // 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, (int)ci.state_text.slen, ci.state_text.ptr)); } static void error_exit(const char *title, pj_status_t status) { pjsua_perror(THIS_FILE, title, status); pjsua_destroy(); exit(1); } 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; pjsua_config_default(&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; pjsua_logging_config_default(&log_cfg); 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; pjsua_transport_config_default(&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); cfg.id = pj_str("sip:bulukucing@iptel.org"); cfg.reg_uri = pj_str("sip:iptel.org"); cfg.cred_count = 1; cfg.cred_info[0].realm = pj_str("iptel.org"); 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') break; } // Destroy pjsua pjsua_destroy(); return 0; } }}}