summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/py_pjsua/pjsua_app.py
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip-apps/src/py_pjsua/pjsua_app.py')
-rw-r--r--pjsip-apps/src/py_pjsua/pjsua_app.py421
1 files changed, 301 insertions, 120 deletions
diff --git a/pjsip-apps/src/py_pjsua/pjsua_app.py b/pjsip-apps/src/py_pjsua/pjsua_app.py
index 74817d32..5c8e6ed9 100644
--- a/pjsip-apps/src/py_pjsua/pjsua_app.py
+++ b/pjsip-apps/src/py_pjsua/pjsua_app.py
@@ -5,152 +5,328 @@ import thread
#
# Configurations
#
-APP = "pjsua_app.py"
+THIS_FILE = "pjsua_app.py"
C_QUIT = 0
-C_LOG_LEVEL = 3
+C_LOG_LEVEL = 4
-C_SIP_PORT = 5060
+# STUN config.
+# Set C_STUN_SRV to the address of the STUN server to enable STUN
+#
C_STUN_SRV = ""
+C_SIP_PORT = 5060
C_STUN_PORT = 3478
-C_ACC_REGISTRAR = ""
-#C_ACC_REGISTRAR = "sip:iptel.org"
-C_ACC_ID = "sip:bulukucing1@iptel.org"
-C_ACC_REALM = "iptel.org"
-C_ACC_USERNAME = "bulukucing1"
-C_ACC_PASSWORD = "netura"
-# Display PJ error and exit
+# Globals
+#
+g_acc_id = py_pjsua.PJSUA_INVALID_ID
+g_current_call = py_pjsua.PJSUA_INVALID_ID
+
+
+# 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 + "]"
+
+# Handler when invite 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 == 6:
+ g_current_call = py_pjsua.PJSUA_INVALID_ID
+
+# Handler 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:
+ py_pjsua.call_answer(call_id, 486, "", None)
+ return
+ g_current_call = call_id
+ ci = py_pjsua.call_get_info(call_id)
+ write_log(3, "Incoming call: " + call_name(call_id))
+ py_pjsua.call_answer(call_id, 200, "", None)
+
+
+# Handler 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 == 1:
+ 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")
+
+
+# Handler when account registration state has changed
+#
+def on_reg_state(acc_id):
+ acc_info = py_pjsua.acc_get_info(acc_id)
+ if acc_info.status != 0 and acc_info.status != 200:
+ write_log(3, "Account (un)registration failed: rc=" + `acc_info.status` + " " + acc_info.status_text)
+ else:
+ write_log(3, "Account successfully (un)registered")
+
+
+# Utility: display PJ error and exit
+#
def err_exit(title, rc):
- py_pjsua.perror(APP, title, rc)
+ py_pjsua.perror(THIS_FILE, title, rc)
exit(1)
-# Logging callback
+
+# Logging function (also callback, called by pjsua-lib)
+#
def log_cb(level, str, len):
- if level >= C_LOG_LEVEL:
+ if level <= C_LOG_LEVEL:
print str,
-# Initialize pjsua
+def write_log(level, str):
+ log_cb(level, str + "\n", 0)
+
+
+#
+# Initialize pjsua.
+#
def app_init():
- # 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
- ua_cfg = py_pjsua.config_default()
- ua_cfg.thread_cnt = 0
- ua_cfg.user_agent = "PJSUA/Python 0.1"
-
- # 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 STUN config
- stun_cfg = py_pjsua.stun_config_default()
- stun_cfg.stun_srv1 = C_STUN_SRV
- stun_cfg.stun_srv2 = C_STUN_SRV
- stun_cfg.stun_port1 = C_STUN_PORT
- stun_cfg.stun_port2 = C_STUN_PORT
-
- # Configure UDP transport config
- transport_cfg = py_pjsua.transport_config_default()
- transport_cfg.port = C_SIP_PORT
- transport_cfg.stun_config = stun_cfg
- if C_STUN_SRV != "":
- transport_cfg.use_stun = 1
-
- # Create UDP transport
- # Note: transport_id is supposed to be integer
- status, transport_id = py_pjsua.transport_create(1, transport_cfg)
- if status != 0:
- py_pjsua.destroy()
- err_exit("Error creating UDP transport", status)
-
-
- # Configure account configuration
- acc_cfg = py_pjsua.acc_config_default()
- acc_cfg.id = C_ACC_ID
- acc_cfg.reg_uri = C_ACC_REGISTRAR
- acc_cfg.cred_count = 1
- acc_cfg.cred_info[0].realm = C_ACC_REALM
- acc_cfg.cred_info[0].scheme = "digest"
- acc_cfg.cred_info[0].username = C_ACC_USERNAME
- acc_cfg.cred_info[0].data_type = 0
- acc_cfg.cred_info[0].data = C_ACC_PASSWORD
-
- # Add new SIP account
- # Note: acc_id is supposed to be integer
- status, acc_id = py_pjsua.acc_add(acc_cfg, 1)
- if status != 0:
- py_pjsua.destroy()
- err_exit("Error adding SIP account", status)
-
-
-# Worker thread function
+ global g_acc_id
+
+ # 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
+
+ # 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 STUN config
+ stun_cfg = py_pjsua.stun_config_default()
+ stun_cfg.stun_srv1 = C_STUN_SRV
+ stun_cfg.stun_srv2 = C_STUN_SRV
+ stun_cfg.stun_port1 = C_STUN_PORT
+ stun_cfg.stun_port2 = C_STUN_PORT
+
+ # Configure UDP transport config
+ transport_cfg = py_pjsua.transport_config_default()
+ transport_cfg.port = C_SIP_PORT
+ transport_cfg.stun_config = stun_cfg
+ if C_STUN_SRV != "":
+ transport_cfg.use_stun = 1
+
+ # Create UDP transport
+ status, transport_id = py_pjsua.transport_create(1, transport_cfg)
+ if status != 0:
+ py_pjsua.destroy()
+ 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:
+ py_pjsua.destroy()
+ err_exit("Error creating account", status)
+
+ g_acc_id = acc_id
+
+# Add SIP account interractively
+#
+def add_account():
+ global g_acc_id
+
+ acc_domain = ""
+ acc_username = ""
+ acc_passwd =""
+ confirm = ""
+
+ # Input account configs
+ print "Your SIP domain (e.g. myprovider.com): ",
+ acc_domain = sys.stdin.readline()
+ if acc_domain == "\n":
+ return
+ acc_domain = acc_domain.replace("\n", "")
+
+ print "Your username (e.g. alice): ",
+ acc_username = sys.stdin.readline()
+ if acc_username == "\n":
+ return
+ acc_username = acc_username.replace("\n", "")
+
+ print "Your password (e.g. secret): ",
+ acc_passwd = sys.stdin.readline()
+ if acc_passwd == "\n":
+ return
+ acc_passwd = acc_passwd.replace("\n", "")
+
+ # 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
+ acc_cfg.cred_count = 1
+ acc_cfg.cred_info[0].realm = acc_domain
+ acc_cfg.cred_info[0].scheme = "digest"
+ acc_cfg.cred_info[0].username = acc_username
+ acc_cfg.cred_info[0].data_type = 0
+ acc_cfg.cred_info[0].data = acc_passwd
+
+ # 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")
+
+
+#
+# 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):
- thread_desc = 0;
- status = py_pjsua.thread_register("worker thread", thread_desc)
- if status != 0:
- py_pjsua.perror(APP, "Error registering thread", status)
- else:
- while C_QUIT == 0:
- py_pjsua.handle_events(50)
+ 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:
- py_pjsua.destroy()
- err_exit("Error starting pjsua!", status)
-
- # Start worker thread
- thr = thread.start_new(worker_thread_main, (0,))
+ # Done with initialization, start pjsua!!
+ #
+ status = py_pjsua.start()
+ if status != 0:
+ py_pjsua.destroy()
+ err_exit("Error starting pjsua!", status)
+
+ # Start worker thread
+ thr = thread.start_new(worker_thread_main, (0,))
- print "PJSUA Started!!"
+ print "PJSUA Started!!"
# Print application menu
+#
def print_menu():
- print "Menu:"
- print " q Quit application"
- print " s Add buddy"
- print "Choice: ",
+ print """
+Menu:
+ q Quit application
+ +a Add account
+ +b Add buddy
+ m Make call
+ h Hangup current call (if any)
+ i Send instant message
+ """
+ print "Choice: ",
-
# Menu
+#
def app_menu():
- quit = 0
- while quit == 0:
- print_menu()
- choice = sys.stdin.readline()
- if choice[0] == "q":
- quit = 1
- elif choice[0] == "s":
- bc = py_pjsua.Buddy_Config()
- print "Buddy URI: ",
- bc.uri = sys.stdin.readline()
- if bc.uri == "":
- continue
+ global g_acc_id
+ global g_current_call
+
+ quit = 0
+ while quit == 0:
+ print_menu()
+ choice = sys.stdin.readline()
+
+ if choice[0] == "q":
+ quit = 1
+
+ elif choice[0] == "i":
+ # Sending IM
+ print "Send IM to SIP URL: ",
+ url = sys.stdin.readline()
+ if url == "\n":
+ continue
+
+ # Send typing indication
+ py_pjsua.im_typing(g_acc_id, url, 1, None)
+
+ print "The content: ",
+ message = sys.stdin.readline()
+ if message == "\n":
+ py_pjsua.im_typing(g_acc_id, url, 0, None)
+ continue
+
+ # Send the IM!
+ py_pjsua.im_send(g_acc_id, url, "", message, None, 0)
+
+ elif choice[0] == "m":
+ # Make call
+ print "Using account ", g_acc_id
+ print "Make call to SIP URL: ",
+ url = sys.stdin.readline()
+ url = url.replace("\n", "")
+ if url == "":
+ continue
+
+ # Initiate the call!
+ status, call_id = py_pjsua.call_make_call(g_acc_id, url, 0, 0, None)
+
+ if status != 0:
+ py_pjsua.perror(THIS_FILE, "Error making call", status)
+ else:
+ g_current_call = call_id
+
+ elif choice[0] == "+" and choice[1] == "b":
+ # Add new buddy
+ bc = py_pjsua.Buddy_Config()
+ print "Buddy URL: ",
+ bc.uri = sys.stdin.readline()
+ if bc.uri == "\n":
+ continue
- bc.subscribe = 1
- status = py_pjsua.buddy_add(bc)
- if status != 0:
- py_pjsua.perror(APP, "Error adding buddy", status)
+ bc.subscribe = 1
+ status, buddy_id = py_pjsua.buddy_add(bc)
+ if status != 0:
+ py_pjsua.perror(THIS_FILE, "Error adding buddy", status)
+
+ elif choice[0] == "+" and choice[1] == "a":
+ # Add account
+ add_account()
+
+ elif choice[0] == "h":
+ if g_current_call != py_pjsua.PJSUA_INVALID_ID:
+ py_pjsua.call_hangup(g_current_call, 603, "", None)
+ else:
+ print "No current call"
#
@@ -165,5 +341,10 @@ app_menu()
#
print "PJSUA shutting down.."
C_QUIT = 1
+# Give the worker thread chance to quit itself
+while C_QUIT != 2:
+ py_pjsua.handle_events(50)
+
+print "PJSUA destroying.."
py_pjsua.destroy()