def __init__(self, user, log_level=6): ## Load config file self._config = ConfigParser.ConfigParser() self._config.read('./config.cfg') ## Initialize logger dispsip._user = user self._user = user self._user_code = user.user_code self.logger = logging.getLogger(self._user_code + '.dispsip') self.logger.info('Initializing SIP device for user: %s', self._user_code) ## Initializing SIP device if not pj.Lib.instance(): lib = pj.Lib() else: lib = pj.Lib.instance() my_ua_cfg = pj.UAConfig() my_media_cfg = pj.MediaConfig() try: self.logger.debug('Initializing PJSUA library') lib.init(log_cfg=pj.LogConfig(level=log_level, callback=log_cb), ua_cfg=my_ua_cfg, media_cfg=my_media_cfg) self.logger.debug('Setting null sound device in PJSUA library') lib.set_null_snd_dev() except pj.Error, e: self.logger.error('Lib Initialization error: %s', e)
def init(self): """Initialize global pjsip library. General codec and device configuration should be done here. """ try: # Get log level from command line log_level = Settings().verbose nameserver = Settings().nameserver if nameserver: ua_cfg = pjsua.UAConfig() ua_cfg.nameserver = Settings().nameserver else: ua_cfg = None # Initializa PJSUA self.lib = pjsua.Lib() self.lib.init(log_cfg=pjsua.LogConfig(level=log_level, callback=self.pjlog_cb), ua_cfg=ua_cfg) if Settings().transport == 'tcp': self.transport_tcp = self.lib.create_transport(pjsua.TransportType.TCP) elif Settings().transport == 'tls': self.transport_tls = self.lib.create_transport(pjsua.TransportType.TLS) else: self.transport_udp = self.lib.create_transport(pjsua.TransportType.UDP) self.lib.set_null_snd_dev() self.lib.start() except pjsua.Error, e: self.lib.destroy() print "Exception: " + str(e)
def initLib(self): try: self.lib = pj.Lib() except: raise UnrecoverableFailure uaconfig = pj.UAConfig() #uaconfig.stun_host = STUN_SERVER if self.account: if self.account.has_key('useragent'): uaconfig.user_agent = self.account['useragent'] def log_cb(lvl, str, len): pass print str try: self.lib.init(ua_cfg=uaconfig, log_cfg=pj.LogConfig(level=5, callback=log_cb)) self.lib.create_transport(pj.TransportType.TCP) self.lib.start() except: raise UnrecoverableFailure self.broadcastStatus(CONNECTING) self.accountResolver = AccountResolver(self.callback_accountsRetrieved, clientid=None)
def __ami_connect(self, ami): """ Handler for when AMI has started. We use AMI connection as the signal to start creating PJSUA accounts and starting PJLIB. """ self.ami_connected += 1 if (self.ami_connected < len(self.test_object.ami)): LOGGER.info("{0} ami connected. Waiting for " "{1}".format(self.ami_connected, len(self.test_object.ami))) return ua_cfg = pj.UAConfig() ua_cfg.max_calls = self.max_calls self.lib = pj.Lib() try: self.lib.init(ua_cfg=ua_cfg) self.__create_transports() self.lib.set_null_snd_dev() self.__create_accounts() self.lib.start() except pj.Error, exception: LOGGER.error("Exception: " + str(exception)) self.lib.destroy() self.lib = None self.test_object.stop_reactor()
def create_UAConfig(): logger.debug("create_UAConfig") # Doc: http://www.pjsip.org/python/pjsua.htm#UAConfig UAConfig = pj.UAConfig() UAConfig.max_calls = conf.get_int(SIPPHONE_SECTION, 'ua.max_calls', 1) UAConfig.nameserver = conf.get_list(SIPPHONE_SECTION, 'ua.nameserver', []) UAConfig.stun_domain = conf.get(SIPPHONE_SECTION, 'ua.stun_domain', '') UAConfig.stun_host = conf.get(SIPPHONE_SECTION, 'ua.stun_host', '') UAConfig.user_agent = 'DoorPi' return UAConfig
def start(self): ua = pj.UAConfig() ua.nameserver = ['8.8.8.8', '8.8.4.4'] ua.user_agent = "PJSIP Python EasyPhone" media = pj.MediaConfig() media.enable_ice = True logger = pj.LogConfig(level=self.verbose, callback=logger_callback) self.pjsip.init(ua_cfg=ua, media_cfg=media, log_cfg=logger) self.pjsip.create_transport(pj.TransportType.TCP, pj.TransportConfig()) self.pjsip.set_null_snd_dev() self.pjsip.start() self.pjsip.handle_events()
def __init__(self,lib=None): if lib: self.lib=lib elif pj.Lib.instance(): self.lib=pj.Lib.instance() else: self.lib = pj.Lib() ua=pj.UAConfig() ua.max_calls=40 self.lib.init(log_cfg = pj.LogConfig(level=2,callback=log_cb),ua_cfg=ua) self._transport=self.lib.create_transport(pj.TransportType.UDP, pj.TransportConfig(0)) self.lib.start() devs=self.lib.enum_snd_dev() if not len(devs): self.lib.set_null_snd_dev() self.player=Player() self.created={}
def init(self): self.status = States.init self.lib = pj.Lib() self.cfg_ua = pj.UAConfig() self.cfg_md = pj.MediaConfig() self.cfg_ua.max_calls, self.cfg_ua.user_agent = 32, "TrashTalker/1.0" self.cfg_md.no_vad, self.cfg_md.enable_ice = True, False self.cfg_lg = pj.LogConfig(level=self.LOG_LEVEL, callback=PJLog) self.lib.init(ua_cfg=cfg_ua, media_cfg=cfg_md, log_cfg=cfg_lg) self.lib.set_null_snd_dev() self.lib.start(with_thread=True) self.transport = self.lib.create_transport( pj.TransportType.UDP, pj.TransportConfig(self.port)) self.account = self.lib.create_account_for_transport(self.transport, cb=AccountCb()) self.uri = "sip:%s:%s" % (self.transport.info().host, self.transport.info().port) self.status &= States.done
def start_pjsip(self): self.domain = None self.account = None self.lib = pj.Lib() ua_cfg = pj.UAConfig() ua_cfg.max_calls = 3000 ua_cfg.user_agent = 'WebAutoDialer' self.lib.init(ua_cfg=ua_cfg, log_cfg=pj.LogConfig(level=5, callback=self._log_cb)) self.lib.start() log.info('Started PJSIP library with maximum calls number ' + str(ua_cfg.max_calls)) codecs = self.lib.enum_codecs() for codec in codecs: #adjust codecs priorities codec_priority = codec.priority if 'G729' in codec.name or 'PCMA' in codec.name: codec_priority = 255 self.lib.set_codec_priority(codec.name, codec_priority) log.info('Codec name ' + codec.name + ', priority ' + str(codec_priority))
log("Media are closed...") def make_call(uri): try: log("Making call to:" + uri) return acc.make_call(uri, cb=MyCallCallback()) except pj.Error, e: log("Exception: " + str(e)) return None lib = pj.Lib() try: ua_cfg = pj.UAConfig() loging_cfg = pj.LogConfig() loging_cfg.level = log_level loging_cfg.callback = log_cb media_cfg = pj.MediaConfig() if snd_clock_rate != -1: media_cfg.clock_rate = snd_clock_rate if ptime != -1: media_cfg.ptime = ptime if echo_cancel_type != -1: media_cfg.ec_options = echo_cancel_type if echo_cancel_tail != -1: media_cfg.ec_tail_len = echo_cancel_tail if quality != -1:
def __init__(self): super( Example,self ).__init__() self.initUI() lib = pj.Lib() current_call = None my_ua_cfg = pj.UAConfig() my_ua_cfg.nameserver = ['8.8.8.8', '8.8.4.4'] my_ua_cfg.user_agent = "hanxiaotian_bupt" # http://www.pjsip.org/python/pjsua.htm#MediaConfig my_media_cfg = pj.MediaConfig() my_media_cfg.enable_ice = True # # Procedure: Initialize > Create Transpot > Start > Handle calls > Shutdown # # https://trac.pjsip.org/repos/wiki/Python_SIP/Settings#StartupandShutdown # Initialize the Library lib.init(ua_cfg=my_ua_cfg, media_cfg=my_media_cfg, log_cfg = pj.LogConfig(level=3, callback=log_cb)) # Create One or More Transports transport = lib.create_transport(pj.TransportType.TCP, pj.TransportConfig()) #transport = lib.create_transport(pj.TransportType.UDP, pj.TransportConfig(0)) #transport = lib.create_transport(pj.TransportType.TLS, pj.TransportConfig(port=5060)) # SSL lib.set_null_snd_dev() # Starting the Library lib.start() lib.handle_events() # # Registration # acc_cfg = pj.AccountConfig() succeed = 0 while (succeed == 0): print "---------------------------------------------------------------------" acc_cfg.id = "sip:[email protected]" # acc_cfg.reg_uri = "sip:10.103.241.142;transport=tcp" # acc_cfg.proxy = [] server = "10.103.241.142" acc_cfg.proxy = [ "sip:10.103.241.142;transport=tcp;lr" ] # realm = "han" # username = "******" # passwd = "101" print "---------------------------------------------------------------------" acc_cfg.auth_cred = [pj.AuthCred(realm, username ,passwd)] self.acc_cb = MyAccountCallback() self.acc = lib.create_account(acc_cfg, cb=self.acc_cb) self.acc_cb.wait() # Conditions are not correct, because when "IP address change detected for account", all other accounts is Forbidden if ((str(self.acc.info().reg_status) == "200") or (str(self.acc.info().reg_status) == "403")): succeed = 1 else: print "" print "Registration failed, status=", self.acc.info().reg_status, \ "(" + self.acc.info().reg_reason + ")" print "" print "Please try again !"
def main(argv): global broker global pj global lib global args app_name = "SIP2MQTT" parser = argparse.ArgumentParser( description= 'A SIP monitoring tool that publishes incoming calls with CallerID to an MQTT channel' ) requiredNamed = parser.add_argument_group('required named arguments') requiredNamed.add_argument("-a", "--mqtt_domain", type=str, required=True, help="the MQTT broker domain string", default=os.environ.get('MQTT_DOMAIN', None)) requiredNamed.add_argument("-t", "--mqtt_port", type=int, required=True, help="the MQTT broker port number", default=os.environ.get('MQTT_PORT', None)) parser.add_argument("--mqtt_keepalive", type=int, required=False, help="the MQTT broker keep alive in seconds", default=60) parser.add_argument("--mqtt_protocol", type=str, required=False, help="the MQTT broker protocol", default="MQTTv311", choices=['MQTTv31', 'MQTTv311']) requiredNamed.add_argument("-u", "--mqtt_username", type=str, required=True, help="the MQTT broker username", default=os.environ.get('MQTT_USERNAME', None)) requiredNamed.add_argument("-p", "--mqtt_password", type=str, required=False, help="the MQTT broker password", default=os.environ.get('MQTT_PASSWORD', None)) parser.add_argument("--mqtt_topic", type=str, required=False, help="the MQTT broker topic", default=os.environ.get('MQTT_TOPIC', "home/sip")) requiredNamed.add_argument("-d", "--sip_domain", type=str, required=True, help="the SIP domain", default=os.environ.get('SIP_DOMAIN', None)) parser.add_argument("--sip_port", type=int, required=False, help="the SIP transport port number", default=os.environ.get('SIP_PORT', 5060)) requiredNamed.add_argument("-n", "--sip_username", type=str, required=True, help="the SIP username", default=os.environ.get('SIP_USERNAME', None)) requiredNamed.add_argument("-s", "--sip_password", type=str, required=False, help="the SIP password", default=os.environ.get('SIP_PASSWORD', None)) parser.add_argument("--sip_display", type=str, required=False, help="the SIP user display name", default=app_name) parser.add_argument("--log_level", type=int, required=False, help="the application log level", default=3, choices=[0, 1, 2, 3]) parser.add_argument("-v", "--verbosity", action="count", help="increase output verbosity", default=3) args = parser.parse_args() log_level = logging.INFO #Deault logging level if args.verbosity == 1: log_level = logging.ERROR elif args.verbosity == 2: log_level = logging.WARN elif args.verbosity == 3: log_level = logging.INFO elif args.verbosity >= 4: log_level = logging.DEBUG # Configure logging # logging.basicConfig(filename="sip2mqtt.log", format="%(asctime)s - %(levelname)s - %(message)s", # datefmt="%m/%d/%Y %I:%M:%S %p", level=log_level) root = logging.getLogger() root.setLevel(log_level) # ch = logging.StreamHandler(sys.stdout) # ch.setLevel(log_level) # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # ch.setFormatter(formatter) # root.addHandler(ch) # A more docker-friendly approach is to output to stdout logging.basicConfig(stream=sys.stdout, format="%(asctime)s - %(levelname)s - %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p", level=log_level) # Log startup messages and our configuration parameters logging.info("------------------------") logging.info("Starting up...") logging.info("--- MQTT Broker Configuration ---") logging.info("Domain: " + args.mqtt_domain) logging.info("Port: " + str(args.mqtt_port)) logging.info("Protocol: " + args.mqtt_protocol) logging.info("Username: "******"Keepalive Interval: " + str(args.mqtt_keepalive)) logging.info("Status Topic: " + args.mqtt_topic) logging.info("--- SIP Configuration ---") logging.info("Domain: " + args.sip_domain) logging.info("Username: "******"DisplayName: " + args.sip_display) try: # Handle mqtt connection and callbacks broker = mqtt.Client(client_id="", clean_session=True, userdata=None, protocol=eval("mqtt." + args.mqtt_protocol)) broker.username_pw_set(args.mqtt_username, password=args.mqtt_password) broker.on_connect = mqtt_connect #broker.on_message = mqtt_message #don't need this callback for now broker.connect(args.mqtt_domain, args.mqtt_port, args.mqtt_keepalive) # Create library instance of Lib class lib = pj.Lib() ua = pj.UAConfig() ua.user_agent = app_name mc = pj.MediaConfig() mc.clock_rate = 8000 lib.init(ua_cfg=ua, log_cfg=pj.LogConfig(level=args.verbosity, callback=None), media_cfg=mc) lib.create_transport(pj.TransportType.UDP, pj.TransportConfig(args.sip_port)) lib.set_null_snd_dev() lib.start() acc_cfg = pj.AccountConfig() acc_cfg.id = "sip:" + args.sip_username + "@" + args.sip_domain acc_cfg.reg_uri = "sip:" + args.sip_domain acc_cfg.auth_cred = [ pj.AuthCred(args.sip_domain, args.sip_username, args.sip_password) ] acc_cfg.allow_contact_rewrite = False acc = lib.create_account(acc_cfg) acc_cb = SMAccountCallback(acc) acc.set_callback(acc_cb) logging.info("-- Registration Complete --") logging.info('SIP: Status = ' + str(acc.info().reg_status) + ' (' + acc.info().reg_reason + ')') except pj.Error, e: logging.critical(("Exception: " + str(e))) lib.destroy() sys.exit(1)
try: print "Inside record-samples.py script" # Create library instance lib = pj.Lib() # Init library with default config med_conf = pj.MediaConfig() med_conf.clock_rate = CALL_SAMPLING_RATE med_conf.ec_options = 0 med_conf.jb_max = 0 med_conf.jb_min = 0 med_conf.max_media_ports = 1020 ua_config = pj.UAConfig() ua_config.max_calls = 1000 lib.init(ua_cfg=ua_config, log_cfg=pj.LogConfig(level=7, callback=log_cb), media_cfg=med_conf) # Remedy the non availibility of sound device lib.set_null_snd_dev() # Create UDP transport which listens to any available port transport = lib.create_transport(pj.TransportType.UDP, pj.TransportConfig(VOIP_SERVER_PORT)) RECORDING = True lib.start()
player_slot = lib.player_get_slot(self._player) #lib.conf_connect(call_slot, 0) #lib.conf_connect(0, call_slot) lib.conf_connect(player_slot, call_slot) #lib.conf_connect(call_slot, player_slot) lib.conf_connect(player_slot, 0) print "Hello world, I can talk!" # Check command line argument if len(sys.argv) != 4: print "Usage: call.py <dst-URI> <ext> <pass>" sys.exit(1) try: uc = pj.UAConfig() #uc.max_calls = 20 # Create library instance lib = pj.Lib() # Init library with default config log_config = pj.LogConfig(level=5, callback=log_cb) log_config.msg_logging = False lib.init(ua_cfg=uc, log_cfg=log_config) # Create UDP transport which listens to any available port transport = lib.create_transport(pj.TransportType.UDP) # Start the library lib.start()
class SIPCallRecordVerify: ua_cfg = pj.UAConfig() ua_cfg.max_calls = 10 ua_cfg.nameserver = ["8.8.8.8"] ua_cfg.user_agent = "SIPCallRecordVerify" media_cfg = pj.MediaConfig() media_cfg.channel_count = 8 media_cfg.max_media_ports = 8 def __init__(self, caller, calling, log_level=3): self.verify = Verify() if not self.verify.setup(): sys.exit(1) self.lib = pj.Lib() self.lib.init( ua_cfg=self.ua_cfg, log_cfg=pj.LogConfig( level=7, callback=lambda level, str, len: logging.debug(str.strip())), media_cfg=self.media_cfg) self.lib.start(with_thread=True) self.caller_ddi, self.caller_account, self.caller_cb, self.caller_cfg = self.register( caller, default=True) self.calling_ddi, self.calling_account, self.calling_cb, self.calling_cfg = self.register( calling) def register(self, config, default=False): for k, v in config.iteritems(): config[k] = str(v) ddi = config['ddi'] logging.info("Creating transport for %s" % (config['uri'])) transport = self.lib.create_transport(pj.TransportType.UDP) logging.info( "Listening on %s:%d for %s" % (transport.info().host, transport.info().port, config['uri'])) logging.info("Attempting registration for %s" % config['uri']) account_cfg = pj.AccountConfig(domain=config['domain'], username=config['username'], password=config['password'], proxy=config['proxy']) account_cfg.id = config['uri'] account = self.lib.create_account(acc_config=account_cfg, set_default=default) account.set_transport(transport) account_cb = AccountHandler(account) account.set_callback(account_cb) account_cb.wait() logging.info("%s registered, status: %s (%s)" % (config['uri'], account.info().reg_status, account.info().reg_reason)) return (ddi, account, account_cb, account_cfg) def start_caller(self, audiotest, interval=300): try: call = None end = time.time() + interval while True: if call: while call.is_valid(): logging.info("Call in progress") sleep(1) continue remaining = end - time.time() logging.info("Seconds until next call: %d" % remaining) if time.time() <= end: sleep(1) continue end = time.time() + interval logging.info("Making call") call, callhandler = self.caller_cb.new_call( "%s@%s" % (self.calling_ddi, self.caller_cfg.proxy[0][4:-3])) if call: while call.info().state != pj.CallState.CONFIRMED: logging.info("Looping call state check with %s" % call.info().state) sleep(1) continue sleep(1) callhandler.play_file(audiotest['filename'], True) # TODO: Fetch recording, convert to text, validate. sleep(1) call.hangup() sleep(1) except pj.Error, e: logging.error("Exception: " + str(e))
# Notification when call's media state has changed. def on_media_state(self): global lib if self.call.info().media_state == pj.MediaState.ACTIVE: # Connect the call to sound device call_slot = self.call.info().conf_slot lib.conf_connect(call_slot, 0) lib.conf_connect(0, call_slot) try: # Create library instance lib = pj.Lib() # Create a user agent ua = pj.UAConfig() ua.max_calls = 100 ua.user_agent = sys.argv[0] # Init library with default config lib.init(ua) # Create UDP transport which listens to any available port transport = lib.create_transport(pj.TransportType.UDP) # Start the library lib.start() # Create local/user-less account acc = lib.create_account(pj.AccountConfig( config.get("account", "domain"),
class Softphone: ua_cfg = pj.UAConfig() log_cfg = pj.LogConfig() log_cfg.callback = lambda level, str, len: logger.info(str.strip()) media_cfg = pj.MediaConfig( ) # look at the options it takes: https://www.pjsip.org/python/pjsua.htm#MediaConfig def __init__( self, max_calls=1, # TODO: Add support for multiple simultaneous calls. nameserver=['1.1.1.1'], user_agent='Python Softphone', log_level=5, sample_rate=48000, duration_ms=20, channel_count=2, max_media_ports=8, thread=True): """ :param max_calls: Integer - Maximum simultaneous calls. :param nameserver: List - A list of DNS server(s) to use. :param user_agent: String - User-agent. :param log_level: Integer - Level to use for the logger. :sample_rate: Integer - Sample rate (hz) to capture and playback audio. :duration_ms: Integer - Milliseconds per audio frame. :channel_count: Integer - Number of channels to use. 1 for Mono, 2 for Stereo. :max_media_ports: Integer - PJSIP maximum media ports. :thread: Boolean - Use a threaded instance of softphone. """ self.pid = os.getpid() # Media config self.media_cfg.clock_rate = sample_rate self.media_cfg.channel_count = channel_count #self.media_cfg.snd_clock_rate = sample_rate# Clock rate to be applied when opening the sound device. If value is zero, conference bridge clock rate will be used. self.media_cfg.audio_frame_ptime = duration_ms # Default: 20 ms audio data #self.media_cfg.no_vad = True # disable voice activation detection #self.media_cfg.enable_ice = False self.media_cfg.max_media_ports = max_media_ports # User-agent config self.ua_cfg.max_calls = max_calls self.ua_cfg.nameserver = nameserver self.ua_cfg.user_agent = user_agent # Log config self.log_cfg.level = log_level # Lib settings (put this in run() instead when using multiprocessing.Process) self.lib = pj.Lib() # Singleton instance self.lib.init(ua_cfg=self.ua_cfg, log_cfg=self.log_cfg, media_cfg=self.media_cfg) self.lib.start(with_thread=thread) # Playback / Recording varaibles self.player = None self.recorder = None # Stream callback id and slot self.audio_cb_id = None self.audio_cb_slot = None # Call variables self.call_handler = True self.current_call = None logger.info(f"Object created.") def __del__(self): self.lib.destroy() logger.info(f"Object destroyed.") def register(self, server, port, username, password, default_account=False, proxy=None, protocol='UDP', bind_address='127.0.0.1', bind_port=0): """ Register an account at i.e. Asterisk PBX, and set network transport options. Returns: Account registered, account callback handler. """ if protocol == 'UDP': protocol = pj.TransportType.UDP elif protocol == 'TCP': protocol = pj.TransportType.TCP elif protocol == 'TLS': protocol = pj.TransportType.TLS else: logger.info(f"Error: Invalid protocol type.") logger.info("Creating transport and generating SIP URI.") transport = self.lib.create_transport( protocol, pj.TransportConfig( 0 ) # TransportConfig(host=bind_address, port=bind_port) # TODO: Add bind_address and bind_port here. ) public_sip_uri = f"sip:{username}@{str(transport.info().host)}:{str(transport.info().port)}" logger.info( f"Listening on {transport.info().host}:{transport.info().port} for {public_sip_uri}." ) logger.info( f"Attempting registration for {public_sip_uri} at {server}:{port}." ) account_cfg = pj.AccountConfig(domain=server + ":" + port, username=username, password=password) account_cfg.id = public_sip_uri account = self.lib.create_account(acc_config=account_cfg, set_default=default_account) account.set_transport(transport) account_handler = AccountHandler(lib=self.lib, account=account) account.set_callback(account_handler) logger.info(f"Waiting for registration...") account_handler.wait() logger.info( f"Successfully registered {public_sip_uri}, status: {account.info().reg_status} ({account.info().reg_reason})." ) return account def unregister(self, account): """ Unregister a registered account. """ logger.info( f"Attempting to unregister account by deletion: {account}.") account.delete() logger.info(f"Successfully unregistered and deleted account.") def call(self, account, sip_uri): """ Make a new outgoing call to sip_uri from SIP account. """ try: if self.current_call: logger.info(f"Already have a call.") return if self.lib.verify_sip_url(sip_uri) != 0: logger.info(f"Invalid SIP URI.") return logger.info(f"Attempting new call to {sip_uri}") lck = self.lib.auto_lock() # To prevent deadlocks call_handler = CallHandler(lib=self.lib, audio_cb_slot=self.audio_cb_slot) self.current_call = account.make_call(sip_uri, cb=call_handler) logger.info(f"Current call is {self.current_call}.") del lck except pj.Error as e: logger.info(f"Error: {e}") self.current_call = None self.lib.destroy() def end_call(self): """ Hang up an ongoing call for SIP account. """ try: if not self.current_call: logger.info("There is no call.") return if not self.current_call.is_valid( ): # Is this needed? Used by g-farrow, but might be handled already. logger.info("Call has already ended.") return self.current_call.hangup() self.current_call = None logger.info(f"Call ended.") except pj.Error as e: logger.info(f"Error: {e}") def wait_for_active_audio(self): """ Wait until call audio is activated. """ while self.current_call.info().media_state != pj.MediaState.ACTIVE: time.sleep(0.5) def wait_for_confirmed_call(self): """ Wait until call has been confirmed. """ while self.current_call.info().state != pj.CallState.CONFIRMED: time.sleep(0.5) def get_call_length(self): """ Get the length of the current call in seconds. :return call_length, total_length: Tuple (Call Connection length (seconds), Total Length (seconds)) """ if not self.current_call: raise PhoneCallNotInProgress("The call does not exist") call_length = self.current_call.info().call_time total_length = self.current_call.info().total_time logger.info( "Call duration information: connection '{call_length}' second(s), total '{total_length}' second(s)." ) return call_length, total_length def send_dtmf_key_tones(self, digits): """ Send DTMF keypad tones to the call. :param digits: String - Digits to send over the call """ logger.debug("Sending DTMF key tones: '{digits}'") self.current_call.dial_dtmf(digits) logger.debug("DTMF tones sent") def get_sound_devices(self): """ Get a detailed list of available sound devices. Returns a list of available sound devices. """ sounddevices = [] snd_devs = self.lib.enum_snd_dev() i = 0 for snd_dev in snd_devs: dev = {} dev['index'] = i dev['name'] = snd_dev.name dev['input_channels'] = snd_dev.input_channels dev['output_channels'] = snd_dev.output_channels dev['sample_rate'] = snd_dev.default_clock_rate sounddevices.append(dev) i += 1 return sounddevices def set_null_sound_device(self): """ Set NULL sound device / Do not use system audio device. """ self.lib.set_null_snd_dev() logger.info(f"Using NULL sound device.") def get_capture_device(self): """ Get capture device ID currently in use. """ capture_id, playback_id = self.lib.get_snd_dev() return capture_id def set_capture_device(self, capture_id): """ Set capture device ID to be used. """ _, playback_id = self.lib.get_snd_dev() self.lib.set_snd_dev(capture_id, playback_id) logger.info(f"Capture device set to: {capture_id}") def get_playback_device(self): """ Get playback device ID currently in use. """ capture_id, playback_id = self.lib.get_snd_dev() return playback_id def set_playback_device(self, playback_id): """ Set playback device ID to be used. """ capture_id, _ = self.lib.get_snd_dev() self.lib.set_snd_dev(capture_id, playback_id) logger.info(f"Playback device set to: {playback_id}") def capture(self, file_name): """ Save call audio to WAV file. """ if os.path.exists(file_name): raise FileExistsError( "A file with this name already exists: {file_name}") self.recorder = self.lib.create_recorder(file_name) recorder_slot = self.lib.recorder_get_slot(self.recorder) #self.lib.conf_connect(recorder_slot, self.current_call.info().conf_slot) # not needed? or? self.lib.conf_connect(self.current_call.info().conf_slot, recorder_slot) logger.info(f"Started audio capture.") def stop_capturing(self): """ Stop capturing call audio to file """ recorder_slot = self.lib.recorder_get_slot(self.recorder) self.lib.conf_disconnect(self.current_call.info().conf_slot, recorder_slot) self.lib.recorder_destroy(self.recorder) self.recorder = None logger.info(f"Stopped audio capture.") def playback(self, file_name): """ Playback a WAV file into call. :param file_path: String - path to the audio (WAV) file to be played to the call. """ if not os.path.exists(file_name): raise FileNotFoundError("Cannot find audio file: {file_name}") if not os.path.isfile(file_name): raise FileNotFoundError( "The audio file is not a file: {file_path}") self.player = self.lib.create_player(file_name) player_slot = self.lib.player_get_slot(self.player) self.lib.conf_connect(player_slot, self.current_call.info().conf_slot) logger.info(f"Started audio playback.") def stop_playback(self): """ Stop playing audio file to call """ player_slot = self.lib.player_get_slot(self.player) self.lib.conf_disconnect(player_slot, self.current_call.info().conf_slot) self.lib.player_destroy(self.player) self.player = None logger.info(f"Stopped audio playback.") def create_audio_stream(self, audio_callback): self.audio_cb_id = self.lib.create_audio_cb(audio_callback) self.audio_cb_slot = self.lib.audio_cb_get_slot(self.audio_cb_id) logger.info(f"Created audio callback.") def destroy_audio_stream(self): self.lib.audio_cb_destroy(self.audio_cb_id) self.audio_cb_id = None self.audio_cb_slot = None logger.info(f"Destroyed audio callback.")
lib = None print('Destroyed') else: print('Already destroyed') atexit.register(pjLibDestroy) print('Making configuration...') logingConfiguration = pj.LogConfig() logingConfiguration.console_level = 6 logingConfiguration.level = 6 logingConfiguration.filename = '/tmp/pj.log' #logingConfiguration.callback=log_cb pjUAconfig = pj.UAConfig() pjUAconfig.max_calls = 20 pjUAconfig.user_agent = 'Yealink SIP-T29G 46.80.0.125' #pjUAconfig.user_agent = 'Tadiran SIP-T328P 2.72.19.4 00:15:65:50:81:1b' #listenAddr = str(os.environ.get('TC_EXT_TRUNK_IP')) listenAddr = config.testConfigJson['SystemVars'][0]['%%IP%%'] #listenAddr = '192.168.118.12' print('Init lib pj...') try: lib.init(ua_cfg=pjUAconfig, log_cfg=logingConfiguration) except pj.Error as e: print('Lib pjsua init exception: ' + str(e)) #pjLibDestroy() sys.exit(1)