1

I'm working on creating a SIP client using Python and PJSUA2. I'm testing it with a SIP server running on the same host. When I call makeCall() Wireshark shows the following:

  • The INVITE messages being sent to the server
  • The server responding back with 180 - Ringing
  • The server responding back with 200 - OK

My client never ACKs the 200-OK message. Should this be done automatically or is there something I need to configure to get it to ACK?

import time
import pjsua2 as pj

class MyCall(pj.Call):
    def onCallState(self, prm):
        print("***OnCallState: ")

        call_info = self.getInfo()
        print("current state is " + str(call_info.state) + " " + call_info.stateText)
        print("last status code: " + str(call_info.lastStatusCode))

ep = pj.Endpoint()
ep_cfg = pj.EpConfig()
ep_cfg.uaConfig.threadCnt = 0
ep.libCreate()
ep.libInit(ep_cfg)

sipTpConfig = pj.TransportConfig()
ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig)
ep.libStart()

# Create the account information
acfg = pj.AccountConfig()
acfg.idUri = "sip:test@pjsip.org";
acfg.regConfig.registrarUri = "sip:pjsip.org";
cred = pj.AuthCredInfo("digest", "*", "test", 0, "pwtest");
acfg.sipConfig.authCreds.append( cred );

acc = pj.Account()
acc.create(acfg)

call = MyCall(acc, pj.PJSUA_INVALID_ID)
prm = pj.CallOpParam(True)

call.makeCall("sip:service@127.0.0.1", prm)

time.sleep(5)
ep.libDestroy()
del ep
jmq
  • 1,559
  • 9
  • 21

2 Answers2

1

Don't you need a callback for the account? I have a helper file where I answer the call on incoming call. I don't know if this is the issue, I came here for wisdom as well...

import sys
import pjsua as pj
import threading
import time

current_call=None

# Callback to receive events from Account
class MyAccountCallback(pj.AccountCallback):
    sem = None

    def __init__(self, account):
        pj.AccountCallback.__init__(self, account)

    def wait(self):
        self.sem = threading.Semaphore(0)
        self.sem.acquire()

    #Notification when account registration state has changed
    def on_reg_state(self):
        if self.sem:
            if self.account.info().reg_status >= 200:
                self.sem.release()

    # Notification on incoming call
    def on_incoming_call(self, call):
        global current_call
        if current_call:
            call.answer(486, "Busy")
            return

        print "Incoming call from ", call.info().remote_uri

        current_call = call
        call_cb = MyCallCallback(current_call)
        current_call.set_callback(call_cb)

        current_call.answer(180)
        time.sleep(0.1)
        current_call.answer(200)


# Callback to receive events from Call
class MyCallCallback(pj.CallCallback):
    def __init__(self, call=None):
        pj.CallCallback.__init__(self, call)

    # Notification when call state has changed
    def on_state(self):
        global current_call
        print "Call is ", self.call.info().state_text,
        print "last code =", self.call.info().last_code,
        print "(" + self.call.info().last_reason + ")"
        if self.call.info().state == pj.CallState.DISCONNECTED:
            current_call = None
            print 'Current call is', current_call

    # Notification when call's media state has changed.
    def on_media_state(self):
        if (self.call.info().media_state == pj.MediaState.ACTIVE):
            # Connect the call to sound device
            call_slot = self.call.info().conf_slot
            pj.Lib.instance().conf_connect(call_slot, 0)
            pj.Lib.instance().conf_connect(0, call_slot)
            print "\nMEDIA IS ACTIVE\n"
        else:
            print "\n\MEDIA IS *NOT* ACTIVE\n"
Julia
  • 36
  • 4
  • You are probably right about needing a callback. I'm using pjsua2 which doesn't have the AccountCallback class so I'm not sure what I need to set up. – jmq Jun 10 '20 at 17:59
1

I finally figured it out. I'll post it here in case someone else runs into the same problem. I needed to tell the endpoint to handle events:

while True:
    ep.libHandleEvents(10)

From https://www.pjsip.org/docs/book-latest/html/endpoint.html#starting-the-library

int libHandleEvents(unsigned msec_timeout) Poll pjsua for events, and if necessary block the caller thread for the specified maximum interval (in miliseconds).

Application doesn’t normally need to call this function if it has configured worker thread (thread_cnt field) in pjsua_config structure, because polling then will be done by these worker threads instead.

If EpConfig::UaConfig::mainThreadOnly is enabled and this function is called from the main thread (by default the main thread is thread that calls libCreate()), this function will also scan and run any pending jobs in the list.

jmq
  • 1,559
  • 9
  • 21