Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 1 | # $Id$ |
| 2 | # |
| 3 | # Sample and simple Python script to make and receive calls, and do |
| 4 | # presence and instant messaging/IM using PJSUA-API binding for Python. |
| 5 | # |
| 6 | # Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> |
| 7 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 8 | import py_pjsua |
| 9 | import sys |
| 10 | import thread |
| 11 | |
| 12 | # |
| 13 | # Configurations |
| 14 | # |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 15 | THIS_FILE = "pjsua_app.py" |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 16 | C_QUIT = 0 |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 17 | C_LOG_LEVEL = 4 |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 18 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 19 | # STUN config. |
| 20 | # Set C_STUN_SRV to the address of the STUN server to enable STUN |
| 21 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 22 | C_STUN_SRV = "" |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 23 | C_SIP_PORT = 5060 |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 24 | C_STUN_PORT = 3478 |
| 25 | |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 26 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 27 | # Globals |
| 28 | # |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 29 | g_ua_cfg = None |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 30 | g_acc_id = py_pjsua.PJSUA_INVALID_ID |
| 31 | g_current_call = py_pjsua.PJSUA_INVALID_ID |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 32 | g_wav_files = [] |
| 33 | g_wav_id = 0 |
| 34 | g_wav_port = 0 |
| 35 | g_rec_file = "" |
| 36 | g_rec_id = 0 |
| 37 | g_rec_port = 0 |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 38 | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 39 | # Utility: display PJ error and exit |
| 40 | # |
| 41 | def err_exit(title, rc): |
| 42 | py_pjsua.perror(THIS_FILE, title, rc) |
| 43 | exit(1) |
| 44 | |
| 45 | |
| 46 | # Logging function (also callback, called by pjsua-lib) |
| 47 | # |
| 48 | def log_cb(level, str, len): |
| 49 | if level <= C_LOG_LEVEL: |
| 50 | print str, |
| 51 | |
| 52 | def write_log(level, str): |
| 53 | log_cb(level, str + "\n", 0) |
| 54 | |
| 55 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 56 | # Utility to get call info |
| 57 | # |
| 58 | def call_name(call_id): |
| 59 | ci = py_pjsua.call_get_info(call_id) |
| 60 | return "[Call " + `call_id` + " " + ci.remote_info + "]" |
| 61 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 62 | # Callback when call state has changed. |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 63 | # |
| 64 | def on_call_state(call_id, e): |
| 65 | global g_current_call |
| 66 | ci = py_pjsua.call_get_info(call_id) |
| 67 | write_log(3, call_name(call_id) + " state = " + `ci.state_text`) |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 68 | if ci.state == py_pjsua.PJSIP_INV_STATE_DISCONNECTED: |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 69 | g_current_call = py_pjsua.PJSUA_INVALID_ID |
| 70 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 71 | # Callback for incoming call |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 72 | # |
| 73 | def on_incoming_call(acc_id, call_id, rdata): |
| 74 | global g_current_call |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 75 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 76 | if g_current_call != py_pjsua.PJSUA_INVALID_ID: |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 77 | # There's call in progress - answer Busy |
| 78 | py_pjsua.call_answer(call_id, 486, None, None) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 79 | return |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 80 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 81 | g_current_call = call_id |
| 82 | ci = py_pjsua.call_get_info(call_id) |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 83 | write_log(3, "*** Incoming call: " + call_name(call_id) + "***") |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 84 | write_log(3, "*** Press a to answer or h to hangup ***") |
| 85 | |
| 86 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 87 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 88 | # Callback when media state has changed (e.g. established or terminated) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 89 | # |
| 90 | def on_call_media_state(call_id): |
| 91 | ci = py_pjsua.call_get_info(call_id) |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 92 | if ci.media_status == py_pjsua.PJSUA_CALL_MEDIA_ACTIVE: |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 93 | py_pjsua.conf_connect(ci.conf_slot, 0) |
| 94 | py_pjsua.conf_connect(0, ci.conf_slot) |
| 95 | write_log(3, call_name(call_id) + ": media is active") |
| 96 | else: |
| 97 | write_log(3, call_name(call_id) + ": media is inactive") |
| 98 | |
| 99 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 100 | # Callback when account registration state has changed |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 101 | # |
| 102 | def on_reg_state(acc_id): |
| 103 | acc_info = py_pjsua.acc_get_info(acc_id) |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 104 | if acc_info.has_registration != 0: |
| 105 | cmd = "registration" |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 106 | else: |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 107 | cmd = "unregistration" |
| 108 | if acc_info.status != 0 and acc_info.status != 200: |
| 109 | write_log(3, "Account " + cmd + " failed: rc=" + `acc_info.status` + " " + acc_info.status_text) |
| 110 | else: |
| 111 | write_log(3, "Account " + cmd + " success") |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 112 | |
| 113 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 114 | # Callback when buddy's presence state has changed |
| 115 | # |
Fahris | e314b88 | 2007-02-02 10:52:04 +0000 | [diff] [blame] | 116 | def on_buddy_state(buddy_id): |
| 117 | write_log(3, "On Buddy state called") |
| 118 | buddy_info = py_pjsua.buddy_get_info(buddy_id) |
| 119 | if buddy_info.status != 0 and buddy_info.status != 200: |
| 120 | write_log(3, "Status of " + `buddy_info.uri` + " is " + `buddy_info.status_text`) |
| 121 | else: |
| 122 | write_log(3, "Status : " + `buddy_info.status`) |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 123 | |
| 124 | # Callback on incoming pager (MESSAGE) |
| 125 | # |
Fahris | e314b88 | 2007-02-02 10:52:04 +0000 | [diff] [blame] | 126 | def on_pager(call_id, strfrom, strto, contact, mime_type, text): |
| 127 | write_log(3, "MESSAGE from " + `strfrom` + " : " + `text`) |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 128 | |
| 129 | |
| 130 | # Callback on the delivery status of outgoing pager (MESSAGE) |
| 131 | # |
Fahris | e314b88 | 2007-02-02 10:52:04 +0000 | [diff] [blame] | 132 | def on_pager_status(call_id, strto, body, user_data, status, reason): |
| 133 | write_log(3, "MESSAGE to " + `strto` + " status " + `status` + " reason " + `reason`) |
| 134 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 135 | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 136 | # Received typing indication |
| 137 | # |
| 138 | def on_typing(call_id, strfrom, to, contact, is_typing): |
| 139 | str_t = "" |
| 140 | if is_typing: |
| 141 | str_t = "is typing.." |
| 142 | else: |
| 143 | str_t = "has stopped typing" |
| 144 | write_log(3, "IM indication: " + strfrom + " " + str_t) |
| 145 | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 146 | # Received the status of previous call transfer request |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 147 | # |
| 148 | def on_call_transfer_status(call_id,status_code,status_text,final,p_cont): |
| 149 | strfinal = "" |
| 150 | if final == 1: |
| 151 | strfinal = "[final]" |
| 152 | |
| 153 | write_log(3, "Call " + `call_id` + ": transfer status= " + `status_code` + " " + status_text+ " " + strfinal) |
| 154 | |
| 155 | if status_code/100 == 2: |
| 156 | write_log(3, "Call " + `call_id` + " : call transfered successfully, disconnecting call") |
| 157 | status = py_pjsua.call_hangup(call_id, 410, None, None) |
| 158 | p_cont = 0 |
| 159 | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 160 | # Callback on incoming call transfer request |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 161 | # |
| 162 | def on_call_transfer_request(call_id, dst, code): |
| 163 | write_log(3, "Call transfer request from " + `call_id` + " to " + dst + " with code " + `code`) |
| 164 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 165 | # |
| 166 | # Initialize pjsua. |
| 167 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 168 | def app_init(): |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 169 | global g_acc_id, g_ua_cfg |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 170 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 171 | # Create pjsua before anything else |
| 172 | status = py_pjsua.create() |
| 173 | if status != 0: |
| 174 | err_exit("pjsua create() error", status) |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 175 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 176 | # Create and initialize logging config |
| 177 | log_cfg = py_pjsua.logging_config_default() |
| 178 | log_cfg.level = C_LOG_LEVEL |
| 179 | log_cfg.cb = log_cb |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 180 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 181 | # Create and initialize pjsua config |
| 182 | # Note: for this Python module, thread_cnt must be 0 since Python |
| 183 | # doesn't like to be called from alien thread (pjsua's thread |
| 184 | # in this case) |
| 185 | ua_cfg = py_pjsua.config_default() |
| 186 | ua_cfg.thread_cnt = 0 |
| 187 | ua_cfg.user_agent = "PJSUA/Python 0.1" |
| 188 | ua_cfg.cb.on_incoming_call = on_incoming_call |
| 189 | ua_cfg.cb.on_call_media_state = on_call_media_state |
| 190 | ua_cfg.cb.on_reg_state = on_reg_state |
| 191 | ua_cfg.cb.on_call_state = on_call_state |
Fahris | e314b88 | 2007-02-02 10:52:04 +0000 | [diff] [blame] | 192 | ua_cfg.cb.on_buddy_state = on_buddy_state |
| 193 | ua_cfg.cb.on_pager = on_pager |
| 194 | ua_cfg.cb.on_pager_status = on_pager_status |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 195 | ua_cfg.cb.on_typing = on_typing |
| 196 | ua_cfg.cb.on_call_transfer_status = on_call_transfer_status |
| 197 | ua_cfg.cb.on_call_transfer_request = on_call_transfer_request |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 198 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 199 | # Create and initialize media config |
| 200 | med_cfg = py_pjsua.media_config_default() |
| 201 | med_cfg.ec_tail_len = 0 |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 202 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 203 | # |
| 204 | # Initialize pjsua!! |
| 205 | # |
| 206 | status = py_pjsua.init(ua_cfg, log_cfg, med_cfg) |
| 207 | if status != 0: |
| 208 | err_exit("pjsua init() error", status) |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 209 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 210 | # Configure STUN config |
Benny Prijono | fbec997 | 2007-04-02 16:56:20 +0000 | [diff] [blame] | 211 | #stun_cfg = py_pjsua.stun_config_default() |
| 212 | #stun_cfg.stun_srv1 = C_STUN_SRV |
| 213 | #stun_cfg.stun_srv2 = C_STUN_SRV |
| 214 | #stun_cfg.stun_port1 = C_STUN_PORT |
| 215 | #stun_cfg.stun_port2 = C_STUN_PORT |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 216 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 217 | # Configure UDP transport config |
| 218 | transport_cfg = py_pjsua.transport_config_default() |
| 219 | transport_cfg.port = C_SIP_PORT |
Benny Prijono | fbec997 | 2007-04-02 16:56:20 +0000 | [diff] [blame] | 220 | #transport_cfg.stun_config = stun_cfg |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 221 | if C_STUN_SRV != "": |
| 222 | transport_cfg.use_stun = 1 |
| 223 | |
| 224 | # Create UDP transport |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 225 | status, transport_id = \ |
| 226 | py_pjsua.transport_create(py_pjsua.PJSIP_TRANSPORT_UDP, transport_cfg) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 227 | if status != 0: |
| 228 | py_pjsua.destroy() |
| 229 | err_exit("Error creating UDP transport", status) |
| 230 | |
| 231 | # Create initial default account |
| 232 | status, acc_id = py_pjsua.acc_add_local(transport_id, 1) |
| 233 | if status != 0: |
| 234 | py_pjsua.destroy() |
| 235 | err_exit("Error creating account", status) |
| 236 | |
| 237 | g_acc_id = acc_id |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 238 | g_ua_cfg = ua_cfg |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 239 | |
| 240 | # Add SIP account interractively |
| 241 | # |
| 242 | def add_account(): |
| 243 | global g_acc_id |
| 244 | |
| 245 | acc_domain = "" |
| 246 | acc_username = "" |
| 247 | acc_passwd ="" |
| 248 | confirm = "" |
| 249 | |
| 250 | # Input account configs |
| 251 | print "Your SIP domain (e.g. myprovider.com): ", |
| 252 | acc_domain = sys.stdin.readline() |
| 253 | if acc_domain == "\n": |
| 254 | return |
| 255 | acc_domain = acc_domain.replace("\n", "") |
| 256 | |
| 257 | print "Your username (e.g. alice): ", |
| 258 | acc_username = sys.stdin.readline() |
| 259 | if acc_username == "\n": |
| 260 | return |
| 261 | acc_username = acc_username.replace("\n", "") |
| 262 | |
| 263 | print "Your password (e.g. secret): ", |
| 264 | acc_passwd = sys.stdin.readline() |
| 265 | if acc_passwd == "\n": |
| 266 | return |
| 267 | acc_passwd = acc_passwd.replace("\n", "") |
| 268 | |
| 269 | # Configure account configuration |
| 270 | acc_cfg = py_pjsua.acc_config_default() |
| 271 | acc_cfg.id = "sip:" + acc_username + "@" + acc_domain |
| 272 | acc_cfg.reg_uri = "sip:" + acc_domain |
| 273 | acc_cfg.cred_count = 1 |
| 274 | acc_cfg.cred_info[0].realm = acc_domain |
| 275 | acc_cfg.cred_info[0].scheme = "digest" |
| 276 | acc_cfg.cred_info[0].username = acc_username |
| 277 | acc_cfg.cred_info[0].data_type = 0 |
| 278 | acc_cfg.cred_info[0].data = acc_passwd |
| 279 | |
| 280 | # Add new SIP account |
| 281 | status, acc_id = py_pjsua.acc_add(acc_cfg, 1) |
| 282 | if status != 0: |
| 283 | py_pjsua.perror(THIS_FILE, "Error adding SIP account", status) |
| 284 | else: |
| 285 | g_acc_id = acc_id |
| 286 | write_log(3, "Account " + acc_cfg.id + " added") |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 287 | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 288 | def add_player(): |
| 289 | global g_wav_files |
| 290 | global g_wav_id |
| 291 | global g_wav_port |
| 292 | |
| 293 | file_name = "" |
| 294 | status = -1 |
| 295 | wav_id = 0 |
| 296 | |
| 297 | print "Enter the path of the file player(e.g. /tmp/audio.wav): ", |
| 298 | file_name = sys.stdin.readline() |
| 299 | if file_name == "\n": |
| 300 | return |
| 301 | file_name = file_name.replace("\n", "") |
| 302 | status, wav_id = py_pjsua.player_create(file_name, 0) |
| 303 | if status != 0: |
| 304 | py_pjsua.perror(THIS_FILE, "Error adding file player ", status) |
| 305 | else: |
| 306 | g_wav_files.append(file_name) |
| 307 | if g_wav_id == 0: |
| 308 | g_wav_id = wav_id |
| 309 | g_wav_port = py_pjsua.player_get_conf_port(wav_id) |
| 310 | write_log(3, "File player " + file_name + " added") |
| 311 | |
| 312 | def add_recorder(): |
| 313 | global g_rec_file |
| 314 | global g_rec_id |
| 315 | global g_rec_port |
| 316 | |
| 317 | file_name = "" |
| 318 | status = -1 |
| 319 | rec_id = 0 |
| 320 | |
| 321 | print "Enter the path of the file recorder(e.g. /tmp/audio.wav): ", |
| 322 | file_name = sys.stdin.readline() |
| 323 | if file_name == "\n": |
| 324 | return |
| 325 | file_name = file_name.replace("\n", "") |
| 326 | status, rec_id = py_pjsua.recorder_create(file_name, 0, None, 0, 0) |
| 327 | if status != 0: |
| 328 | py_pjsua.perror(THIS_FILE, "Error adding file recorder ", status) |
| 329 | else: |
| 330 | g_rec_file = file_name |
| 331 | g_rec_id = rec_id |
| 332 | g_rec_port = py_pjsua.recorder_get_conf_port(rec_id) |
| 333 | write_log(3, "File recorder " + file_name + " added") |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 334 | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 335 | def conf_list(): |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 336 | ports = None |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 337 | print "Conference ports : " |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 338 | ports = py_pjsua.enum_conf_ports() |
| 339 | |
| 340 | for port in ports: |
| 341 | info = None |
| 342 | info = py_pjsua.conf_get_port_info(port) |
| 343 | txlist = "" |
| 344 | for i in range(info.listener_cnt): |
| 345 | txlist = txlist + "#" + `info.listeners[i]` + " " |
| 346 | |
| 347 | print "Port #" + `info.slot_id` + "[" + `(info.clock_rate/1000)` + "KHz/" + `(info.samples_per_frame * 1000 / info.clock_rate)` + "ms] " + info.name + " transmitting to: " + txlist |
| 348 | |
| 349 | def connect_port(): |
| 350 | src_port = 0 |
| 351 | dst_port = 0 |
| 352 | |
| 353 | print "Connect src port # (empty to cancel): " |
| 354 | src_port = sys.stdin.readline() |
| 355 | if src_port == "\n": |
| 356 | return |
| 357 | src_port = src_port.replace("\n", "") |
| 358 | src_port = int(src_port) |
| 359 | print "To dst port # (empty to cancel): " |
| 360 | dst_port = sys.stdin.readline() |
| 361 | if dst_port == "\n": |
| 362 | return |
| 363 | dst_port = dst_port.replace("\n", "") |
| 364 | dst_port = int(dst_port) |
| 365 | status = py_pjsua.conf_connect(src_port, dst_port) |
| 366 | if status != 0: |
| 367 | py_pjsua.perror(THIS_FILE, "Error connecting port ", status) |
| 368 | else: |
| 369 | write_log(3, "Port connected from " + `src_port` + " to " + `dst_port`) |
| 370 | |
| 371 | def disconnect_port(): |
| 372 | src_port = 0 |
| 373 | dst_port = 0 |
| 374 | |
| 375 | print "Disconnect src port # (empty to cancel): " |
| 376 | src_port = sys.stdin.readline() |
| 377 | if src_port == "\n": |
| 378 | return |
| 379 | src_port = src_port.replace("\n", "") |
| 380 | src_port = int(src_port) |
| 381 | print "From dst port # (empty to cancel): " |
| 382 | dst_port = sys.stdin.readline() |
| 383 | if dst_port == "\n": |
| 384 | return |
| 385 | dst_port = dst_port.replace("\n", "") |
| 386 | dst_port = int(dst_port) |
| 387 | status = py_pjsua.conf_disconnect(src_port, dst_port) |
| 388 | if status != 0: |
| 389 | py_pjsua.perror(THIS_FILE, "Error disconnecting port ", status) |
| 390 | else: |
| 391 | write_log(3, "Port disconnected " + `src_port` + " from " + `dst_port`) |
| 392 | |
| 393 | def dump_call_quality(): |
| 394 | global g_current_call |
| 395 | |
| 396 | buf = "" |
| 397 | if g_current_call != -1: |
| 398 | buf = py_pjsua.call_dump(g_current_call, 1, 1024, " ") |
| 399 | write_log(3, "\n" + buf) |
| 400 | else: |
| 401 | write_log(3, "No current call") |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 402 | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 403 | def xfer_call(): |
| 404 | global g_current_call |
| 405 | |
| 406 | if g_current_call == -1: |
| 407 | |
| 408 | write_log(3, "No current call") |
| 409 | |
| 410 | else: |
| 411 | call = g_current_call |
| 412 | ci = py_pjsua.call_get_info(g_current_call) |
| 413 | print "Transfering current call ["+ `g_current_call` + "] " + ci.remote_info |
| 414 | print "Enter sip url : " |
| 415 | url = sys.stdin.readline() |
| 416 | if url == "\n": |
| 417 | return |
| 418 | url = url.replace("\n", "") |
| 419 | if call != g_current_call: |
| 420 | print "Call has been disconnected" |
| 421 | return |
| 422 | msg_data = py_pjsua.msg_data_init() |
| 423 | status = py_pjsua.call_xfer(g_current_call, url, msg_data); |
| 424 | if status != 0: |
| 425 | py_pjsua.perror(THIS_FILE, "Error transfering call ", status) |
| 426 | else: |
| 427 | write_log(3, "Call transfered to " + url) |
| 428 | |
| 429 | def xfer_call_replaces(): |
| 430 | if g_current_call == -1: |
| 431 | write_log(3, "No current call") |
| 432 | else: |
| 433 | call = g_current_call |
| 434 | |
| 435 | ids = py_pjsua.enum_calls() |
| 436 | if len(ids) <= 1: |
| 437 | print "There are no other calls" |
| 438 | return |
| 439 | |
| 440 | ci = py_pjsua.call_get_info(g_current_call) |
| 441 | print "Transfer call [" + `g_current_call` + "] " + ci.remote_info + " to one of the following:" |
| 442 | for i in range(0, len(ids)): |
| 443 | if ids[i] == call: |
| 444 | continue |
| 445 | call_info = py_pjsua.call_get_info(ids[i]) |
| 446 | print `ids[i]` + " " + call_info.remote_info + " [" + call_info.state_text + "]" |
| 447 | |
| 448 | print "Enter call number to be replaced : " |
| 449 | buf = sys.stdin.readline() |
| 450 | buf = buf.replace("\n","") |
| 451 | if buf == "": |
| 452 | return |
| 453 | dst_call = int(buf) |
| 454 | |
| 455 | if call != g_current_call: |
| 456 | print "Call has been disconnected" |
| 457 | return |
| 458 | |
| 459 | if dst_call == call: |
| 460 | print "Destination call number must not be the same as the call being transfered" |
| 461 | return |
| 462 | |
| 463 | if dst_call >= py_pjsua.PJSUA_MAX_CALLS: |
| 464 | print "Invalid destination call number" |
| 465 | return |
| 466 | |
| 467 | if py_pjsua.call_is_active(dst_call) == 0: |
| 468 | print "Invalid destination call number" |
| 469 | return |
| 470 | |
| 471 | py_pjsua.call_xfer_replaces(call, dst_call, 0, None) |
| 472 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 473 | # |
| 474 | # Worker thread function. |
| 475 | # Python doesn't like it when it's called from an alien thread |
| 476 | # (pjsua's worker thread, in this case), so for Python we must |
| 477 | # disable worker thread in pjsua and poll pjsua from Python instead. |
| 478 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 479 | def worker_thread_main(arg): |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 480 | global C_QUIT |
| 481 | thread_desc = 0; |
| 482 | status = py_pjsua.thread_register("python worker", thread_desc) |
| 483 | if status != 0: |
| 484 | py_pjsua.perror(THIS_FILE, "Error registering thread", status) |
| 485 | else: |
| 486 | while C_QUIT == 0: |
| 487 | py_pjsua.handle_events(50) |
| 488 | print "Worker thread quitting.." |
| 489 | C_QUIT = 2 |
| 490 | |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 491 | |
| 492 | # Start pjsua |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 493 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 494 | def app_start(): |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 495 | # Done with initialization, start pjsua!! |
| 496 | # |
| 497 | status = py_pjsua.start() |
| 498 | if status != 0: |
| 499 | py_pjsua.destroy() |
| 500 | err_exit("Error starting pjsua!", status) |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 501 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 502 | # Start worker thread |
| 503 | thr = thread.start_new(worker_thread_main, (0,)) |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 504 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 505 | print "PJSUA Started!!" |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 506 | |
| 507 | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 508 | # Print account and buddy list |
| 509 | def print_acc_buddy_list(): |
| 510 | global g_acc_id |
| 511 | |
| 512 | acc_ids = py_pjsua.enum_accs() |
| 513 | print "Account list:" |
| 514 | for acc_id in acc_ids: |
| 515 | acc_info = py_pjsua.acc_get_info(acc_id) |
| 516 | if acc_info.has_registration == 0: |
| 517 | acc_status = acc_info.status_text |
| 518 | else: |
| 519 | acc_status = `acc_info.status` + "/" + acc_info.status_text + " (expires=" + `acc_info.expires` + ")" |
| 520 | |
| 521 | if acc_id == g_acc_id: |
| 522 | print " *", |
| 523 | else: |
| 524 | print " ", |
| 525 | |
| 526 | print "[" + `acc_id` + "] " + acc_info.acc_uri + ": " + acc_status |
| 527 | print " Presence status: ", |
| 528 | if acc_info.online_status != 0: |
| 529 | print "Online" |
| 530 | else: |
| 531 | print "Invisible" |
| 532 | |
| 533 | if py_pjsua.get_buddy_count() > 0: |
| 534 | print "" |
| 535 | print "Buddy list:" |
| 536 | buddy_ids = py_pjsua.enum_buddies() |
| 537 | for buddy_id in buddy_ids: |
| 538 | bi = py_pjsua.buddy_get_info(buddy_id) |
| 539 | print " [" + `buddy_id` + "] " + bi.status_text + " " + bi.uri |
| 540 | |
| 541 | |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 542 | # Print application menu |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 543 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 544 | def print_menu(): |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 545 | print "" |
| 546 | print ">>>" |
| 547 | print_acc_buddy_list() |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 548 | print """ |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 549 | +============================================================================+ |
| 550 | | Call Commands : | Buddy, IM & Presence: | Account: | |
| 551 | | | | | |
| 552 | | m Make call | +b Add buddy | +a Add account | |
| 553 | | a Answer current call | -b Delete buddy | -a Delete accnt | |
| 554 | | h Hangup current call | | | |
| 555 | | H Hold call | i Send instant message | rr register | |
| 556 | | v re-inVite (release Hold) | s Subscribe presence | ru Unregister | |
| 557 | | # Send DTMF string | u Unsubscribe presence | | |
| 558 | | dq Dump curr. call quality | t ToGgle Online status | | |
| 559 | | +--------------------------+------------------+ |
| 560 | | x Xfer call | Media Commands: | Status: | |
| 561 | | X Xfer with Replaces | | | |
| 562 | | | cl List ports | d Dump status | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 563 | | | cc Connect port | dd Dump detail | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 564 | | | cd Disconnect port | | |
| 565 | | | +p Add file player | | |
| 566 | |------------------------------+ +r Add file recorder | | |
| 567 | | q Quit application | | | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 568 | +============================================================================+""" |
| 569 | print "You have " + `py_pjsua.call_get_count()` + " active call(s)" |
| 570 | print ">>>", |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 571 | |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 572 | # Menu |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 573 | # |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 574 | def app_menu(): |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 575 | global g_acc_id |
| 576 | global g_current_call |
| 577 | |
| 578 | quit = 0 |
| 579 | while quit == 0: |
| 580 | print_menu() |
| 581 | choice = sys.stdin.readline() |
| 582 | |
| 583 | if choice[0] == "q": |
| 584 | quit = 1 |
| 585 | |
| 586 | elif choice[0] == "i": |
| 587 | # Sending IM |
| 588 | print "Send IM to SIP URL: ", |
| 589 | url = sys.stdin.readline() |
| 590 | if url == "\n": |
| 591 | continue |
| 592 | |
| 593 | # Send typing indication |
| 594 | py_pjsua.im_typing(g_acc_id, url, 1, None) |
| 595 | |
| 596 | print "The content: ", |
| 597 | message = sys.stdin.readline() |
| 598 | if message == "\n": |
| 599 | py_pjsua.im_typing(g_acc_id, url, 0, None) |
| 600 | continue |
| 601 | |
| 602 | # Send the IM! |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 603 | py_pjsua.im_send(g_acc_id, url, None, message, None, 0) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 604 | |
| 605 | elif choice[0] == "m": |
| 606 | # Make call |
| 607 | print "Using account ", g_acc_id |
| 608 | print "Make call to SIP URL: ", |
| 609 | url = sys.stdin.readline() |
| 610 | url = url.replace("\n", "") |
| 611 | if url == "": |
| 612 | continue |
| 613 | |
| 614 | # Initiate the call! |
| 615 | status, call_id = py_pjsua.call_make_call(g_acc_id, url, 0, 0, None) |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 616 | |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 617 | if status != 0: |
| 618 | py_pjsua.perror(THIS_FILE, "Error making call", status) |
| 619 | else: |
| 620 | g_current_call = call_id |
| 621 | |
| 622 | elif choice[0] == "+" and choice[1] == "b": |
| 623 | # Add new buddy |
| 624 | bc = py_pjsua.Buddy_Config() |
| 625 | print "Buddy URL: ", |
| 626 | bc.uri = sys.stdin.readline() |
| 627 | if bc.uri == "\n": |
| 628 | continue |
| 629 | |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 630 | bc.uri = bc.uri.replace("\n", "") |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 631 | bc.subscribe = 1 |
| 632 | status, buddy_id = py_pjsua.buddy_add(bc) |
| 633 | if status != 0: |
| 634 | py_pjsua.perror(THIS_FILE, "Error adding buddy", status) |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 635 | elif choice[0] == "-" and choice[1] == "b": |
| 636 | print "Enter buddy ID to delete : " |
| 637 | buf = sys.stdin.readline() |
| 638 | buf = buf.replace("\n","") |
| 639 | if buf == "": |
| 640 | continue |
| 641 | i = int(buf) |
| 642 | if py_pjsua.buddy_is_valid(i) == 0: |
| 643 | print "Invalid buddy id " + `i` |
| 644 | else: |
| 645 | py_pjsua.buddy_del(i) |
| 646 | print "Buddy " + `i` + " deleted" |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 647 | elif choice[0] == "+" and choice[1] == "a": |
| 648 | # Add account |
| 649 | add_account() |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 650 | elif choice[0] == "-" and choice[1] == "a": |
| 651 | print "Enter account ID to delete : " |
| 652 | buf = sys.stdin.readline() |
| 653 | buf = buf.replace("\n","") |
| 654 | if buf == "": |
| 655 | continue |
| 656 | i = int(buf) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 657 | |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 658 | if py_pjsua.acc_is_valid(i) == 0: |
| 659 | print "Invalid account id " + `i` |
| 660 | else: |
| 661 | py_pjsua.acc_del(i) |
| 662 | print "Account " + `i` + " deleted" |
| 663 | |
| 664 | elif choice[0] == "+" and choice[1] == "p": |
| 665 | add_player() |
| 666 | elif choice[0] == "+" and choice[1] == "r": |
| 667 | add_recorder() |
| 668 | elif choice[0] == "c" and choice[1] == "l": |
| 669 | conf_list() |
| 670 | elif choice[0] == "c" and choice[1] == "c": |
| 671 | connect_port() |
| 672 | elif choice[0] == "c" and choice[1] == "d": |
| 673 | disconnect_port() |
| 674 | elif choice[0] == "d" and choice[1] == "q": |
| 675 | dump_call_quality() |
| 676 | elif choice[0] == "x": |
| 677 | xfer_call() |
| 678 | elif choice[0] == "X": |
| 679 | xfer_call_replaces() |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 680 | elif choice[0] == "h": |
| 681 | if g_current_call != py_pjsua.PJSUA_INVALID_ID: |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 682 | py_pjsua.call_hangup(g_current_call, 603, None, None) |
| 683 | else: |
| 684 | print "No current call" |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 685 | elif choice[0] == "H": |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 686 | if g_current_call != py_pjsua.PJSUA_INVALID_ID: |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 687 | py_pjsua.call_set_hold(g_current_call, None) |
| 688 | |
| 689 | else: |
| 690 | print "No current call" |
| 691 | elif choice[0] == "v": |
| 692 | if g_current_call != py_pjsua.PJSUA_INVALID_ID: |
| 693 | |
| 694 | py_pjsua.call_reinvite(g_current_call, 1, None); |
| 695 | |
| 696 | else: |
| 697 | print "No current call" |
| 698 | elif choice[0] == "#": |
| 699 | if g_current_call == py_pjsua.PJSUA_INVALID_ID: |
| 700 | print "No current call" |
| 701 | elif py_pjsua.call_has_media(g_current_call) == 0: |
| 702 | print "Media is not established yet!" |
| 703 | else: |
| 704 | call = g_current_call |
| 705 | print "DTMF strings to send (0-9*#A-B)" |
| 706 | buf = sys.stdin.readline() |
| 707 | buf = buf.replace("\n", "") |
| 708 | if buf == "": |
| 709 | continue |
| 710 | if call != g_current_call: |
| 711 | print "Call has been disconnected" |
| 712 | continue |
| 713 | status = py_pjsua.call_dial_dtmf(g_current_call, buf) |
| 714 | if status != 0: |
| 715 | py_pjsua.perror(THIS_FILE, "Unable to send DTMF", status); |
| 716 | else: |
| 717 | print "DTMF digits enqueued for transmission" |
| 718 | elif choice[0] == "s": |
| 719 | print "Subscribe presence of (buddy id) : " |
| 720 | buf = sys.stdin.readline() |
| 721 | buf = buf.replace("\n","") |
| 722 | if buf == "": |
| 723 | continue |
| 724 | i = int(buf) |
| 725 | py_pjsua.buddy_subscribe_pres(i, 1) |
| 726 | elif choice[0] == "u": |
| 727 | print "Unsubscribe presence of (buddy id) : " |
| 728 | buf = sys.stdin.readline() |
| 729 | buf = buf.replace("\n","") |
| 730 | if buf == "": |
| 731 | continue |
| 732 | i = int(buf) |
| 733 | py_pjsua.buddy_subscribe_pres(i, 0) |
| 734 | elif choice[0] == "t": |
| 735 | acc_info = py_pjsua.acc_get_info(g_acc_id) |
| 736 | if acc_info.online_status == 0: |
| 737 | acc_info.online_status = 1 |
| 738 | else: |
| 739 | acc_info.online_status = 0 |
| 740 | py_pjsua.acc_set_online_status(g_acc_id, acc_info.online_status) |
| 741 | st = "" |
| 742 | if acc_info.online_status == 0: |
| 743 | st = "offline" |
| 744 | else: |
| 745 | st = "online" |
| 746 | print "Setting " + acc_info.acc_uri + " online status to " + st |
| 747 | elif choice[0] == "r": |
| 748 | if choice[1] == "r": |
| 749 | py_pjsua.acc_set_registration(g_acc_id, 1) |
| 750 | elif choice[1] == "u": |
| 751 | py_pjsua.acc_set_registration(g_acc_id, 0) |
| 752 | elif choice[0] == "d": |
Benny Prijono | da275f6 | 2007-02-18 23:49:14 +0000 | [diff] [blame] | 753 | py_pjsua.dump(choice[1] == "d") |
Fahris | 89ea3d0 | 2007-02-07 08:18:35 +0000 | [diff] [blame] | 754 | elif choice[0] == "a": |
| 755 | if g_current_call != py_pjsua.PJSUA_INVALID_ID: |
| 756 | |
Benny Prijono | aa28604 | 2007-02-03 17:23:22 +0000 | [diff] [blame] | 757 | py_pjsua.call_answer(g_current_call, 200, None, None) |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 758 | else: |
| 759 | print "No current call" |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 760 | |
| 761 | |
| 762 | # |
| 763 | # main |
| 764 | # |
| 765 | app_init() |
| 766 | app_start() |
| 767 | app_menu() |
| 768 | |
| 769 | # |
| 770 | # Done, quitting.. |
| 771 | # |
| 772 | print "PJSUA shutting down.." |
| 773 | C_QUIT = 1 |
Benny Prijono | ed7a5a7 | 2007-01-29 18:36:38 +0000 | [diff] [blame] | 774 | # Give the worker thread chance to quit itself |
| 775 | while C_QUIT != 2: |
| 776 | py_pjsua.handle_events(50) |
| 777 | |
| 778 | print "PJSUA destroying.." |
Benny Prijono | dc30870 | 2006-12-09 00:39:42 +0000 | [diff] [blame] | 779 | py_pjsua.destroy() |
| 780 | |