Simple pjsua2 agent to receive/place calls from CLI with python swig module

W
wyre12@gmail.com
Wed, Jan 25, 2023 2:35 PM

Hi all,

I’ve been trying for a few days to simplify the pygui example to get rid of all tkinter gui. I’d like a simple agent to receive/place calls from CLI. This is my code

import pjsua2 as pj
import time


class Endpoint(pj.Endpoint):
    """
    This is high level Python object inherited from pj.Endpoint
    """
    instance = None

    def __init__(self):
        pj.Endpoint.__init__(self)
        Endpoint.instance = self


def validateUri(uri):
    return Endpoint.instance.utilVerifyUri(uri) == pj.PJ_SUCCESS


def validateSipUri(uri):
    return Endpoint.instance.utilVerifySipUri(uri) == pj.PJ_SUCCESS


# Call class
class Call(pj.Call):
    """
    High level Python Call object, derived from pjsua2's Call object.
    """
    def __init__(self, acc, peer_uri='', call_id=pj.PJSUA_INVALID_ID):
        pj.Call.__init__(self, acc, call_id)
        self.acc = acc
        self.peerUri = peer_uri
        self.connected = False
        self.onhold = False

    def onCallState(self, prm):
        ci = self.getInfo()
        self.connected = ci.state == pj.PJSIP_INV_STATE_CONFIRMED

    def onCallMediaState(self, prm):
        ci = self.getInfo()
        for mi in ci.media:
            if mi.type == pj.PJMEDIA_TYPE_AUDIO and \
              (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or
               mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD):
                m = self.getMedia(mi.index)
                am = pj.AudioMedia.typecastFromMedia(m)
                # connect ports
                Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am)
                am.startTransmit(Endpoint.instance.audDevManager().getPlaybackDevMedia())

                if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold:
                    print("'%s' sets call onhold" % self.peerUri)
                    self.onhold = True
                elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold:
                    print("'%s' sets call active" % self.peerUri)
                    self.onhold = False

    def onDtmfDigit(self, prm):
        # print("Got DTMF:" + prm.digit)
        pass

    def onCallMediaTransportState(self, prm):
        # print("Media transport state")
        pass


class Account(pj.Account):
    def onRegState(self, prm):
        print("***OnRegState: " + prm.reason)

    def onMwiInfo(self, prm):
        print("OnMwiState: " + prm.reason)

    def onBuddyState(self, prm):
        print("OnBuddyState: " + prm.reason)

    def onIncommingSubscribe(self, prm):
        print("OnSubscribeState: " + prm.reason)

    def OnIncomingCall(self, prm):
        c = Call(self, call_id=prm.callId)
        call_prm = pj.CallOpParam()
        call_prm.statusCode = 180
        c.answer(call_prm)
        ci = c.getInfo()
        if input(f"Accept call from {ci.remoteUri}?") == u'yes':
            call_prm.statusCode = 200
            c.answer(call_prm)
        else:
            c.hangup(call_prm)


def initalise_sip_stack():

    ep_cfg = pj.EpConfig()
    ep = Endpoint()
    ep.libCreate()
    ep.libInit(ep_cfg)

    sip_tp_config = pj.TransportConfig()
    sip_tp_config.port = 5060
    ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sip_tp_config)
    ep.libStart()

    acfg = pj.AccountConfig()
    acfg.idUri = "sip:056004@172.20.1.8"
    acfg.regConfig.registrarUri = "sip:172.20.1.8"
    creds = pj.AuthCredInfo("digest", "*", "056004", 0, "alfatec")
    acfg.sipConfig.authCreds.append(creds)

    acc = Account()
    acc.create(acfg)

    return ep


if __name__ == "__main__":

    endpoint = initalise_sip_stack()

    time.sleep(600)

    endpoint.libDestroy()

However when I call from another contact (056003) to my agent … I can see there is no available account


15:30:50.045          pjsua_core.c  .RX 949 bytes Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48) from UDP 172.20.1.8:5060:

INVITE sip:056004@172.20.1.2:5060;ob SIP/2.0

Via: SIP/2.0/UDP 0.0.0.0:5060;rport;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d

From: "MI 3" sip:056003@172.20.1.8;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e

To: sip:056004@172.20.1.2;ob

Contact: sip:056003@0.0.0.0:5060

Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82

CSeq: 11024 INVITE

Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER

Supported: 100rel, timer, replaces, norefersub, histinfo

Session-Expires: 1800

Min-SE: 90

P-Asserted-Identity: "MI 3" sip:056003@172.20.1.8

Max-Forwards: 70

User-Agent: FPBX-15.0.23(16.16.1)

Content-Type: application/sdp

Content-Length:  233

v=0

o=- 1055573632 1055573632 IN IP4 172.20.1.8

s=Asterisk

c=IN IP4 172.20.1.8

t=0 0

m=audio 25808 RTP/AVP 9 101

a=rtpmap:9 G722/8000

a=rtpmap:101 telephone-event/8000

a=fmtp:101 0-16

a=ptime:20

a=maxptime:150

a=sendrecv

--end msg--

15:30:50.045          pjsua_call.c  .Incoming Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48)

15:30:50.045            pjsua_acc.c  ..No available account to handle Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48)

15:30:50.045          pjsua_core.c  ..TX 402 bytes Response msg 480/INVITE/cseq=11024 (tdta0x7fe8c4006b78) to UDP 172.20.1.8:5060:

SIP/2.0 480 Temporarily Unavailable

Via: SIP/2.0/UDP 0.0.0.0:5060;rport=5060;received=172.20.1.8;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d

Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82

From: "MI 3" sip:056003@172.20.1.8;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e

To: sip:056004@172.20.1.2;ob;tag=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d

CSeq: 11024 INVITE

Content-Length:  0

--end msg--

15:30:50.045          pjsua_call.c  ..Unable to accept incoming call (no available account)

15:30:50.047          pjsua_core.c  .RX 433 bytes Request msg ACK/cseq=11024 (rdata0x7fe8c4001c48) from UDP 172.20.1.8:5060:

ACK sip:056004@172.20.1.2:5060;ob SIP/2.0

Via: SIP/2.0/UDP 0.0.0.0:5060;rport;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d

From: "MI 3" sip:056003@172.20.1.8;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e

To: sip:056004@172.20.1.2;ob;tag=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d

Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82

CSeq: 11024 ACK

Max-Forwards: 70

User-Agent: FPBX-15.0.23(16.16.1)

Content-Length:  0

The agent is able to register and I can see both agents in the asterisk cli, but not sure why the other agent is not able to place the call to this one.

Hi all, I’ve been trying for a few days to simplify the pygui example to get rid of all tkinter gui. I’d like a simple agent to receive/place calls from CLI. This is my code ``` import pjsua2 as pj import time class Endpoint(pj.Endpoint): """ This is high level Python object inherited from pj.Endpoint """ instance = None def __init__(self): pj.Endpoint.__init__(self) Endpoint.instance = self def validateUri(uri): return Endpoint.instance.utilVerifyUri(uri) == pj.PJ_SUCCESS def validateSipUri(uri): return Endpoint.instance.utilVerifySipUri(uri) == pj.PJ_SUCCESS # Call class class Call(pj.Call): """ High level Python Call object, derived from pjsua2's Call object. """ def __init__(self, acc, peer_uri='', call_id=pj.PJSUA_INVALID_ID): pj.Call.__init__(self, acc, call_id) self.acc = acc self.peerUri = peer_uri self.connected = False self.onhold = False def onCallState(self, prm): ci = self.getInfo() self.connected = ci.state == pj.PJSIP_INV_STATE_CONFIRMED def onCallMediaState(self, prm): ci = self.getInfo() for mi in ci.media: if mi.type == pj.PJMEDIA_TYPE_AUDIO and \ (mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE or mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD): m = self.getMedia(mi.index) am = pj.AudioMedia.typecastFromMedia(m) # connect ports Endpoint.instance.audDevManager().getCaptureDevMedia().startTransmit(am) am.startTransmit(Endpoint.instance.audDevManager().getPlaybackDevMedia()) if mi.status == pj.PJSUA_CALL_MEDIA_REMOTE_HOLD and not self.onhold: print("'%s' sets call onhold" % self.peerUri) self.onhold = True elif mi.status == pj.PJSUA_CALL_MEDIA_ACTIVE and self.onhold: print("'%s' sets call active" % self.peerUri) self.onhold = False def onDtmfDigit(self, prm): # print("Got DTMF:" + prm.digit) pass def onCallMediaTransportState(self, prm): # print("Media transport state") pass class Account(pj.Account): def onRegState(self, prm): print("***OnRegState: " + prm.reason) def onMwiInfo(self, prm): print("OnMwiState: " + prm.reason) def onBuddyState(self, prm): print("OnBuddyState: " + prm.reason) def onIncommingSubscribe(self, prm): print("OnSubscribeState: " + prm.reason) def OnIncomingCall(self, prm): c = Call(self, call_id=prm.callId) call_prm = pj.CallOpParam() call_prm.statusCode = 180 c.answer(call_prm) ci = c.getInfo() if input(f"Accept call from {ci.remoteUri}?") == u'yes': call_prm.statusCode = 200 c.answer(call_prm) else: c.hangup(call_prm) def initalise_sip_stack(): ep_cfg = pj.EpConfig() ep = Endpoint() ep.libCreate() ep.libInit(ep_cfg) sip_tp_config = pj.TransportConfig() sip_tp_config.port = 5060 ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sip_tp_config) ep.libStart() acfg = pj.AccountConfig() acfg.idUri = "sip:056004@172.20.1.8" acfg.regConfig.registrarUri = "sip:172.20.1.8" creds = pj.AuthCredInfo("digest", "*", "056004", 0, "alfatec") acfg.sipConfig.authCreds.append(creds) acc = Account() acc.create(acfg) return ep if __name__ == "__main__": endpoint = initalise_sip_stack() time.sleep(600) endpoint.libDestroy() ``` However when I call from another contact (056003) to my agent … I can see there is no available account > \ > 15:30:50.045 pjsua_core.c .RX 949 bytes Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48) from UDP 172.20.1.8:5060: > > INVITE sip:056004@172.20.1.2:5060;ob SIP/2.0 > > Via: SIP/2.0/UDP 0.0.0.0:5060;rport;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d > > From: "MI 3" <sip:056003@172.20.1.8>;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e > > To: <sip:056004@172.20.1.2;ob> > > Contact: <sip:056003@0.0.0.0:5060> > > Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82 > > CSeq: 11024 INVITE > > Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER > > Supported: 100rel, timer, replaces, norefersub, histinfo > > Session-Expires: 1800 > > Min-SE: 90 > > P-Asserted-Identity: "MI 3" <sip:056003@172.20.1.8> > > Max-Forwards: 70 > > User-Agent: FPBX-15.0.23(16.16.1) > > Content-Type: application/sdp > > Content-Length: 233 > > v=0 > > o=- 1055573632 1055573632 IN IP4 172.20.1.8 > > s=Asterisk > > c=IN IP4 172.20.1.8 > > t=0 0 > > m=audio 25808 RTP/AVP 9 101 > > a=rtpmap:9 G722/8000 > > a=rtpmap:101 telephone-event/8000 > > a=fmtp:101 0-16 > > a=ptime:20 > > a=maxptime:150 > > a=sendrecv > > \--end msg-- > > 15:30:50.045 pjsua_call.c .Incoming Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48) > > 15:30:50.045 pjsua_acc.c ..No available account to handle Request msg INVITE/cseq=11024 (rdata0x7fe8c4001c48) > > 15:30:50.045 pjsua_core.c ..TX 402 bytes Response msg 480/INVITE/cseq=11024 (tdta0x7fe8c4006b78) to UDP 172.20.1.8:5060: > > SIP/2.0 480 Temporarily Unavailable > > Via: SIP/2.0/UDP 0.0.0.0:5060;rport=5060;received=172.20.1.8;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d > > Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82 > > From: "MI 3" <sip:056003@172.20.1.8>;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e > > To: <sip:056004@172.20.1.2;ob>;tag=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d > > CSeq: 11024 INVITE > > Content-Length: 0 > > \--end msg-- > > 15:30:50.045 pjsua_call.c ..Unable to accept incoming call (no available account) > > 15:30:50.047 pjsua_core.c .RX 433 bytes Request msg ACK/cseq=11024 (rdata0x7fe8c4001c48) from UDP 172.20.1.8:5060: > > ACK sip:056004@172.20.1.2:5060;ob SIP/2.0 > > Via: SIP/2.0/UDP 0.0.0.0:5060;rport;branch=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d > > From: "MI 3" <sip:056003@172.20.1.8>;tag=e0d412a9-2d1d-45ef-be7f-04f0a8bdaf2e > > To: <sip:056004@172.20.1.2;ob>;tag=z9hG4bKPj9599ad6d-2486-4483-8041-afac3bfdc05d > > Call-ID: 1b79c22a-dc7c-4652-92ae-416f3eafcd82 > > CSeq: 11024 ACK > > Max-Forwards: 70 > > User-Agent: FPBX-15.0.23(16.16.1) > > Content-Length: 0 The agent is able to register and I can see both agents in the asterisk cli, but not sure why the other agent is not able to place the call to this one.