Hi, I have a problem with registering threads (via method py_pjsua.thread_register()). I attached a complete code below. The interesting part is at the bottom in the "for" loop (rest is taken from Python example from site). When I make call from the same thread everything is ok (actual code). When i uncomment first line in the loop (and comment second) i have error: Assertion failed: !"Calling pjlib from unknown/external thread. You must " "register external threads with pj_thread_register() " "before calling any pjlib functions.", file ../src/pj/os_core_win32.c, line 521 My question is - where should i put thread registration code ? What about function app_init() and add_account() - should i call this methods in each thread ? Any help is appreciated. Chris import py_pjsua import sys import thread # Globals # g_ua_cfg = None g_acc_id = py_pjsua.PJSUA_INVALID_ID g_current_call = py_pjsua.PJSUA_INVALID_ID g_wav_files = [] g_wav_id = 0 g_wav_port = 0 g_rec_file = "" g_rec_id = 0 g_rec_port = 0 # # Configurations # THIS_FILE = "SIPSender.py" C_QUIT = 0 C_LOG_LEVEL = 4 # STUN config. # Set C_STUN_HOST to the address:port of the STUN server to enable STUN # C_STUN_HOST = "" #C_STUN_HOST = "192.168.0.2" #C_STUN_HOST = "stun.iptel.org:3478" # SIP port C_SIP_PORT = 5061 # Utility: display PJ error and exit # def err_exit(title, rc): py_pjsua.perror(THIS_FILE, title, rc) py_pjsua.destroy() exit(1) # Logging function (also callback, called by pjsua-lib) # def log_cb(level, str, len): print(str) def write_log(level, str): log_cb(level, "LOGGER " + str + "\n", 0) # Utility to get call info # def call_name(call_id): ci = py_pjsua.call_get_info(call_id) return "[Call " + `call_id` + " " + ci.remote_info + "]" # Callback when call state has changed. # def on_call_state(call_id, e): global g_current_call ci = py_pjsua.call_get_info(call_id) write_log(3, call_name(call_id) + " state = " + `ci.state_text`) if ci.state == py_pjsua.PJSIP_INV_STATE_DISCONNECTED: g_current_call = py_pjsua.PJSUA_INVALID_ID # Callback for incoming call # def on_incoming_call(acc_id, call_id, rdata): global g_current_call if g_current_call != py_pjsua.PJSUA_INVALID_ID: # There's call in progress - answer Busy py_pjsua.call_answer(call_id, 486, None, None) return g_current_call = call_id ci = py_pjsua.call_get_info(call_id) write_log(3, "*** Incoming call: " + call_name(call_id) + "***") write_log(3, "*** Press a to answer or h to hangup ***") # Callback when media state has changed (e.g. established or terminated) # def on_call_media_state(call_id): ci = py_pjsua.call_get_info(call_id) if ci.media_status == py_pjsua.PJSUA_CALL_MEDIA_ACTIVE: py_pjsua.conf_connect(ci.conf_slot, 0) py_pjsua.conf_connect(0, ci.conf_slot) write_log(3, call_name(call_id) + ": media is active") else: write_log(3, call_name(call_id) + ": media is inactive") # Callback when account registration state has changed # def on_reg_state(acc_id): acc_info = py_pjsua.acc_get_info(acc_id) if acc_info.has_registration != 0: cmd = "registration" else: cmd = "unregistration" if acc_info.status != 0 and acc_info.status != 200: write_log(3, "Account " + cmd + " failed: rc=" + `acc_info.status` + " " + acc_info.status_text) else: write_log(3, "Account " + cmd + " success") # Received the status of previous call transfer request # def on_call_transfer_status(call_id, status_code, status_text, final, p_cont): strfinal = "" if final == 1: strfinal = "[final]" write_log(3, "Call " + `call_id` + ": transfer status= " + `status_code` + " " + status_text+ " " + strfinal) if status_code/100 == 2: write_log(3, "Call " + `call_id` + " : call transfered successfully, disconnecting call") status = py_pjsua.call_hangup(call_id, 410, None, None) p_cont = 0 # Callback on incoming call transfer request # def on_call_transfer_request(call_id, dst, code): write_log(3, "Call transfer request from " + `call_id` + " to " + dst + " with code " + `code`) # # Initialize pjsua. # def app_init(): global g_acc_id, g_ua_cfg # Create pjsua before anything else status = py_pjsua.create() if status != 0: err_exit("pjsua create() error", status) # Create and initialize logging config log_cfg = py_pjsua.logging_config_default() log_cfg.level = C_LOG_LEVEL log_cfg.cb = log_cb # Create and initialize pjsua config # Note: for this Python module, thread_cnt must be 0 since Python # doesn't like to be called from alien thread (pjsua's thread # in this case) ua_cfg = py_pjsua.config_default() ua_cfg.thread_cnt = 0 ua_cfg.user_agent = "PJSUA/Python 0.1" ua_cfg.cb.on_incoming_call = on_incoming_call ua_cfg.cb.on_call_media_state = on_call_media_state ua_cfg.cb.on_reg_state = on_reg_state ua_cfg.cb.on_call_state = on_call_state ua_cfg.cb.on_call_transfer_status = on_call_transfer_status ua_cfg.cb.on_call_transfer_request = on_call_transfer_request # Configure STUN setting if C_STUN_HOST != "": ua_cfg.stun_host = C_STUN_HOST; # Create and initialize media config med_cfg = py_pjsua.media_config_default() med_cfg.ec_tail_len = 0 # # Initialize pjsua!! # status = py_pjsua.init(ua_cfg, log_cfg, med_cfg) if status != 0: err_exit("pjsua init() error", status) # Configure UDP transport config transport_cfg = py_pjsua.transport_config_default() transport_cfg.port = C_SIP_PORT # Create UDP transport status, transport_id = \ py_pjsua.transport_create(py_pjsua.PJSIP_TRANSPORT_UDP, transport_cfg) if status != 0: err_exit("Error creating UDP transport", status) # Create initial default account status, acc_id = py_pjsua.acc_add_local(transport_id, 1) if status != 0: err_exit("Error creating account", status) g_acc_id = acc_id g_ua_cfg = ua_cfg print g_acc_id # Add SIP account interractively # def add_account(): global g_acc_id acc_domain = "localhost" acc_username = "1000" acc_passwd ="1234" confirm = "" # Configure account configuration acc_cfg = py_pjsua.acc_config_default() acc_cfg.id = "sip:" + acc_username + "@" + acc_domain acc_cfg.reg_uri = "sip:" + acc_domain cred_info = py_pjsua.Pjsip_Cred_Info() cred_info.realm = "*" cred_info.scheme = "digest" cred_info.username = acc_username cred_info.data_type = 0 cred_info.data = acc_passwd acc_cfg.cred_info.append(1) acc_cfg.cred_info[0] = cred_info # Add new SIP account status, acc_id = py_pjsua.acc_add(acc_cfg, 1) if status != 0: py_pjsua.perror(THIS_FILE, "Error adding SIP account", status) else: g_acc_id = acc_id write_log(3, "Account " + acc_cfg.id + " added") return # # Worker thread function. # Python doesn't like it when it's called from an alien thread # (pjsua's worker thread, in this case), so for Python we must # disable worker thread in pjsua and poll pjsua from Python instead. # def worker_thread_main(arg): global C_QUIT thread_desc = 0; status = py_pjsua.thread_register("python worker", thread_desc) if status != 0: py_pjsua.perror(THIS_FILE, "Error registering thread", status) else: while C_QUIT == 0: py_pjsua.handle_events(50) print "Worker thread quitting.." C_QUIT = 2 # Start pjsua # def app_start(): # Done with initialization, start pjsua!! # status = py_pjsua.start() if status != 0: err_exit("Error starting pjsua!", status) # Start worker thread thr = thread.start_new(worker_thread_main, (0,)) print "PJSUA Started!!" def func(arg): print str(thread.get_ident()) + " calls " + str(arg) status, call_id = py_pjsua.call_make_call(g_acc_id, "sip:" + str(arg) + "@192.168.169.99", 0, 0, None) if status != 0: py_pjsua.perror(THIS_FILE, "Error making call", status) else: g_current_call = call_id time.sleep(5) py_pjsua.call_hangup(g_current_call, 603, None, None) # Main thread app_init() add_account() app_start() for i in range(1001,1002): #thread.start_new_thread(func, (i, )) status, call_id = py_pjsua.call_make_call(g_acc_id, "sip:" + str(i) + "@213.134.170.23", 0, 0, None) import time time.sleep(10)