PJSIP

PJSIP is a robust, open-source SIP stack written in C and distributed under the GPL license. It provides a comprehensive framework for building SIP-based VoIP applications, WebRTC clients, and multimedia communication systems. PJSIP’s modular architecture, high performance, and extensive codec support make it an ideal choice for embedded systems, mobile applications, and enterprise communication platforms.

  1. Architecture Overview
    1. User Agent (UA)
    2. Dialog Management
    3. Module Framework
  2. Media Negotiation: SDP Offer/Answer
  3. Installation & Build
    1. Prerequisites
    2. Quick Start Guide
    3. Key Configuration Options
    4. Program
    5. Run
    6. Debug Help
  4. Key Takeaways
  5. References

Architecture Overview

pjsip dev guide architecture diagram

SIP stack written in C. Available under GPL. PJSIP is built on a layered architecture consisting of three primary components:

User Agent (UA)

The User Agent module manages SIP endpoints and their lifecycle.

Key Attributes:

  • local_info with tag
  • local_contact
  • call_id

Core Operations:

pj_status_t pjsip_ua_init(endpt, param); // Initialize the UA
pj_status_t pjsip_ua_destroy(void); // Shutdown the UA
pjsip_module* pjsip_ua_instance(void); // Get UA module instance
pjsip_endpoint* pjsip_ua_get_endpt(ua); // Get the SIP endpoint

Dialog Management

Dialogs represent the relationship between two endpoints and maintain the state of SIP communications.

Key Attributes:

  • state – Current dialog state
  • session_counter – Active session count
  • initial_cseqlocal_cseqremote_cseq – Sequence numbers
  • route_set – Route headers from responses
  • local_info / local_contact – Local endpoint information
  • remote_info / remote_contact – Remote endpoint information

Core Operations:

// Dialog creation
pj_status_t pjsip_dlg_create_uac(ua, local_uri, contact, …);
pj_status_t pjsip_dlg_create_uas(ua, rdata, contact, &dlg);
pj_status_t pjsip_dlg_fork(old_dlg, rdata, &dlg);
// Dialog configuration
pj_status_t pjsip_dlg_set_route_set(dlg, route_set);
pj_status_t pjsip_dlg_inc_session(dlg);
pj_status_t pjsip_dlg_dec_session(dlg);
pj_status_t pjsip_dlg_add_usage(dlg, mod);
// Request/Response handling
pj_status_t pjsip_dlg_create_request(dlg, method, cseq, &tdata);
pj_status_t pjsip_dlg_send_request(dlg, tdata, &tsx);
pj_status_t pjsip_dlg_create_response(dlg, rdata, code, txt, &tdata);
pj_status_t pjsip_dlg_modify_response(dlg, tdata, code, txt);
pj_status_t pjsip_dlg_send_response(dlg, tsx, tdata);
pj_status_t pjsip_dlg_send_msg(dlg, tdata);
// Dialog association helpers
pjsip_dialog* pjsip_tsx_get_dlg(tsx);
pjsip_dialog* pjsip_rdata_get_dlg(rdata);

Module Framework

Modules are pluggable components that handle specific SIP functionality.

Key Attributes:

  • name – Module identifier
  • id – Unique module ID
  • priority – Execution order

Callbacks:

pj_bool_t on_rx_request(rdata); // Incoming request handler
pj_bool_t on_rx_response(rdata); // Incoming response handler
void on_tsx_state(tsx, event); // Transaction state change handler

SDP state Offer/ Answer transition

Media Negotiation: SDP Offer/Answer

PJSIP implements the SDP Offer/Answer model for media negotiation. The negotiator manages state transitions and ensures compatible media codec selection between endpoints. This crucial component ensures both parties agree on compatible audio/video codecs before establishing media streams.

SDP negotiator class diagram PJSIP dev guide

Installation & Build

Prerequisites

Required:

  • GNU make (other make tools incompatible with PJSIP build system)
  • GNU binutils for target platform
  • GNU gcc for target platform

Optional Libraries:

  • ALSA – Audio support on Linux systems
  • OpenSSL – TLS/DTLS encryption support
  • Video Support – Video4Linux2 (v4l2), FFmpeg, libx264, libvpx, OpenH264

Quick Start Guide

Step 1: Download and Extract

wget https://www.pjsip.org/release/2.9/pjproject-2.9.tar.bz2
tar -xvjf pjproject-2.9.tar.bz2
cd pjproject-2.9

Step 2: Configure with Required Features

./configure --enable-shared --disable-static --enable-memalign-hack --enable-gpl --enable-libx264

Watch for successful feature detection:

checking opus/opus.h usability... yes
checking for opus_repacketizer_get_size in -lopus... yes
OPUS library found, OPUS support enabled

Step 3: Build and Install

make dep
make
make install

Key Configuration Options

OptionPurpose
--enable-sharedBuild shared libraries
--disable-staticDon’t build static libraries
--enable-gplEnable GPL-licensed features
--enable-memalign-hackFix memory alignment issues
--disable-soundExclude sound device support
--disable-videoDisable video features
--with-openssl=DIRSpecify OpenSSL location
--with-opus=DIRSpecify OPUS codec path
--with-ffmpeg=DIRSpecify FFmpeg location

Program

create library instance and initiate with default config and logging

lib = pj.Lib()
lib.init(log_cfg = pj.LogConfig(level=LOG_LEVEL, callback=log_cb))

create UDP transport listening on any available port

transport = lib.create_transport(pj.TransportType.UDP,
                                 pj.TransportConfig(0))

create sipuri and local account

my_sip_uri = "sip:" + transport.info().host + ":" + str(transport.info().port)
acc = lib.create_account_for_transport(transport, cb=MyAccountCallback())

Function to make call

def make_call(uri):
    try:
        print "Making call to", uri
        return acc.make_call(uri, cb=MyCallCallback())
    except pj.Error, e:
        print "Exception: " + str(e)
        return None

Ask user input for destination URI and Call

print "Enter destination URI to call: ",
input = sys.stdin.readline().rstrip("\r\n")
if input == "":
    continue
lck = lib.auto_lock()
current_call = make_call(input)
del lck

shutdown the library

transport = None
 acc.delete()
 acc = None
 lib.destroy()
 lib = None

Run

➜  ~ python simplecall.py sip:altanai@127.0.0.1

09:58:09.571        os_core_unix.c !pjlib 2.9 for POSIX initialized
09:58:09.573        sip_endpoint.c  .Creating endpoint instance…
09:58:09.574        pjlib  .select() I/O Queue created (0x7fcfe00590d8)
09:58:09.574        sip_endpoint.c  .Module "mod-msg-print" registered
09:58:09.574        sip_transport.c  .Transport manager created.
09:58:09.575        pjsua_core.c  .PJSUA state changed: NULL --> CREATED
09:58:10.073        pjsua_core.c  .pjsua version 2.9 for Darwin-18.7/x86_64 initialized
Call is  CALLING last code = 0 ()

Debug Help

Isssue1 Undefined symbols for architecture x86_64:
“_pjmedia_codec_opencore_amrnb_deinit”, referenced from:
_amr_encode_decode in mips_test.o
_create_stream_amr in mips_test.o
“_pjmedia_codec_opencore_amrnb_init”, referenced from:
_amr_encode_decode in mips_test.o
_create_stream_amr in mips_test.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [../bin/pjmedia-test-x86_64-apple-darwin18.7.0] Error 1
make[1]: *** [pjmedia-test-x86_64-apple-darwin18.7.0] Error 2
make: *** [all] Error 1

Root Cause: macOS has strict architecture compatibility requirements. This typically occurs when building for OpenCORE AMR codec support that isn’t properly linked

./configure --enable-shared --disable-static --enable-memalign-hack
make dep
make install
cd pjproject-2.9/pjsip-apps/src/python/
python setup.py install

Key Takeaways

  • Modular Architecture: User Agents, Dialogs, and Modules work together seamlessly for maximum flexibility
  • Production Ready: Battle-tested in millions of VoIP deployments worldwide
  • Cross-Platform: Runs on Linux, Windows, macOS, iOS, and Android
  • Extensive Codec Support: OPUS, SILK, G.711, G.722, and many more
  • WebRTC Compatible: Native support for WebRTC applications and data channels
  • Media Negotiation: Robust SDP Offer/Answer implementation ensures codec compatibility
  • Python Bindings: High-level Python API (PJSUA) for rapid application development

References

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.