blob: 3ed3bf844c9214233bc16dd09b327bde2f1ddf1e [file] [log] [blame]
Alexandre Lision94f06ba2013-12-09 16:28:33 -05001# $Id$
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002#
3# Object oriented PJSUA wrapper.
4#
5# Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20#
21
22"""Multimedia communication client library based on SIP protocol.
23
24This implements a fully featured multimedia communication client
25library based on PJSIP stack (http://www.pjsip.org)
26
27
281. FEATURES
29
30 - Session Initiation Protocol (SIP) features:
31 - Basic registration and call
32 - Multiple accounts
33 - Call hold, attended and unattended call transfer
34 - Presence
35 - Instant messaging
36 - Multiple SIP accounts
37 - Media features:
38 - Audio
39 - Conferencing
40 - Narrowband and wideband
41 - Codecs: PCMA, PCMU, GSM, iLBC, Speex, G.722, L16
42 - RTP/RTCP
43 - Secure RTP (SRTP)
44 - WAV playback, recording, and playlist
45 - NAT traversal features
46 - Symmetric RTP
47 - STUN
48 - TURN
49 - ICE
50
51
522. USING
53
54See http://www.pjsip.org/trac/wiki/Python_SIP_Tutorial for a more thorough
55tutorial. The paragraphs below explain basic tasks on using this module.
56
57
58"""
59import _pjsua
60import thread
61import threading
62import weakref
63import time
64
65class Error:
66 """Error exception class.
67
68 Member documentation:
69
70 op_name -- name of the operation that generated this error.
71 obj -- the object that generated this error.
72 err_code -- the error code.
73
74 """
75 op_name = ""
76 obj = None
77 err_code = -1
78 _err_msg = ""
79
80 def __init__(self, op_name, obj, err_code, err_msg=""):
81 self.op_name = op_name
82 self.obj = obj
83 self.err_code = err_code
84 self._err_msg = err_msg
85
86 def err_msg(self):
87 "Retrieve the description of the error."
88 if self._err_msg != "":
89 return self._err_msg
90 self._err_msg = Lib.strerror(self.err_code)
91 return self._err_msg
92
93 def __str__(self):
94 return "Object: " + str(self.obj) + ", operation=" + self.op_name + \
95 ", error=" + self.err_msg()
96
97#
98# Constants
99#
100
101class TransportType:
102 """SIP transport type constants.
103
104 Member documentation:
105 UNSPECIFIED -- transport type is unknown or unspecified
106 UDP -- UDP transport
107 TCP -- TCP transport
108 TLS -- TLS transport
109 IPV6 -- this is not a transport type but rather a flag
110 to select the IPv6 version of a transport
111 UDP_IPV6 -- IPv6 UDP transport
112 TCP_IPV6 -- IPv6 TCP transport
113 """
114 UNSPECIFIED = 0
115 UDP = 1
116 TCP = 2
117 TLS = 3
118 IPV6 = 128
119 UDP_IPV6 = UDP + IPV6
120 TCP_IPV6 = TCP + IPV6
121
122class TransportFlag:
123 """Transport flags to indicate the characteristics of the transport.
124
125 Member documentation:
126
127 RELIABLE -- transport is reliable.
128 SECURE -- transport is secure.
129 DATAGRAM -- transport is datagram based.
130
131 """
132 RELIABLE = 1
133 SECURE = 2
134 DATAGRAM = 4
135
136class CallRole:
137 """Call role constants.
138
139 Member documentation:
140
141 CALLER -- role is caller
142 CALLEE -- role is callee
143
144 """
145 CALLER = 0
146 CALLEE = 1
147
148class CallState:
149 """Call state constants.
150
151 Member documentation:
152
153 NULL -- call is not initialized.
154 CALLING -- initial INVITE is sent.
155 INCOMING -- initial INVITE is received.
156 EARLY -- provisional response has been sent or received.
157 CONNECTING -- 200/OK response has been sent or received.
158 CONFIRMED -- ACK has been sent or received.
159 DISCONNECTED -- call is disconnected.
160 """
161 NULL = 0
162 CALLING = 1
163 INCOMING = 2
164 EARLY = 3
165 CONNECTING = 4
166 CONFIRMED = 5
167 DISCONNECTED = 6
168
169
170class MediaState:
171 """Call media state constants.
172
173 Member documentation:
174
175 NULL -- media is not available.
176 ACTIVE -- media is active.
177 LOCAL_HOLD -- media is put on-hold by local party.
178 REMOTE_HOLD -- media is put on-hold by remote party.
179 ERROR -- media error (e.g. ICE negotiation failure).
180 """
181 NULL = 0
182 ACTIVE = 1
183 LOCAL_HOLD = 2
184 REMOTE_HOLD = 3
185 ERROR = 4
186
187
188class MediaDir:
189 """Media direction constants.
190
191 Member documentation:
192
193 NULL -- media is not active
194 ENCODING -- media is active in transmit/encoding direction only.
195 DECODING -- media is active in receive/decoding direction only
196 ENCODING_DECODING -- media is active in both directions.
197 """
198 NULL = 0
199 ENCODING = 1
200 DECODING = 2
201 ENCODING_DECODING = 3
202
203
204class PresenceActivity:
205 """Presence activities constants.
206
207 Member documentation:
208
209 UNKNOWN -- the person activity is unknown
210 AWAY -- the person is currently away
211 BUSY -- the person is currently engaging in other activity
212 """
213 UNKNOWN = 0
214 AWAY = 1
215 BUSY = 2
216
217
218class SubscriptionState:
219 """Presence subscription state constants.
220
221 """
222 NULL = 0
223 SENT = 1
224 ACCEPTED = 2
225 PENDING = 3
226 ACTIVE = 4
227 TERMINATED = 5
228 UNKNOWN = 6
229
230
231class TURNConnType:
232 """These constants specifies the connection type to TURN server.
233
234 Member documentation:
235 UDP -- use UDP transport.
236 TCP -- use TCP transport.
237 TLS -- use TLS transport.
238 """
239 UDP = 17
240 TCP = 6
241 TLS = 255
242
243
244class UAConfig:
245 """User agent configuration to be specified in Lib.init().
246
247 Member documentation:
248
249 max_calls -- maximum number of calls to be supported.
250 nameserver -- list of nameserver hostnames or IP addresses. Nameserver
251 must be configured if DNS SRV resolution is desired.
252 stun_domain -- if nameserver is configured, this can be used to query
253 the STUN server with DNS SRV.
254 stun_host -- the hostname or IP address of the STUN server. This will
255 also be used if DNS SRV resolution for stun_domain fails.
256 user_agent -- Optionally specify the user agent name.
257 """
258 max_calls = 4
259 nameserver = []
260 stun_domain = ""
261 stun_host = ""
262 user_agent = "pjsip python"
263
264 def _cvt_from_pjsua(self, cfg):
265 self.max_calls = cfg.max_calls
266 self.thread_cnt = cfg.thread_cnt
267 self.nameserver = cfg.nameserver
268 self.stun_domain = cfg.stun_domain
269 self.stun_host = cfg.stun_host
270 self.user_agent = cfg.user_agent
271
272 def _cvt_to_pjsua(self):
273 cfg = _pjsua.config_default()
274 cfg.max_calls = self.max_calls
275 cfg.thread_cnt = 0
276 cfg.nameserver = self.nameserver
277 cfg.stun_domain = self.stun_domain
278 cfg.stun_host = self.stun_host
279 cfg.user_agent = self.user_agent
280 return cfg
281
282
283class LogConfig:
284 """Logging configuration to be specified in Lib.init().
285
286 Member documentation:
287
288 msg_logging -- specify if SIP messages should be logged. Set to
289 True.
290 level -- specify the input verbosity level.
291 console_level -- specify the output verbosity level.
292 decor -- specify log decoration.
293 filename -- specify the log filename.
294 callback -- specify callback to be called to write the logging
295 messages. Sample function:
296
297 def log_cb(level, str, len):
298 print str,
299
300 """
301 msg_logging = True
302 level = 5
303 console_level = 5
304 decor = 0
305 filename = ""
306 callback = None
307
308 def __init__(self, level=-1, filename="", callback=None,
309 console_level=-1):
310 self._cvt_from_pjsua(_pjsua.logging_config_default())
311 if level != -1:
312 self.level = level
313 if filename != "":
314 self.filename = filename
315 if callback != None:
316 self.callback = callback
317 if console_level != -1:
318 self.console_level = console_level
319
320 def _cvt_from_pjsua(self, cfg):
321 self.msg_logging = cfg.msg_logging
322 self.level = cfg.level
323 self.console_level = cfg.console_level
324 self.decor = cfg.decor
325 self.filename = cfg.log_filename
326 self.callback = cfg.cb
327
328 def _cvt_to_pjsua(self):
329 cfg = _pjsua.logging_config_default()
330 cfg.msg_logging = self.msg_logging
331 cfg.level = self.level
332 cfg.console_level = self.console_level
333 cfg.decor = self.decor
334 cfg.log_filename = self.filename
335 cfg.cb = self.callback
336 return cfg
337
338
339class MediaConfig:
340 """Media configuration to be specified in Lib.init().
341
342 Member documentation:
343
344 clock_rate -- specify the core clock rate of the audio,
345 most notably the conference bridge.
346 snd_clock_rate -- optionally specify different clock rate for
347 the sound device.
348 snd_auto_close_time -- specify the duration in seconds when the
349 sound device should be closed after inactivity
350 period.
351 channel_count -- specify the number of channels to open the sound
352 device and the conference bridge.
353 audio_frame_ptime -- specify the length of audio frames in millisecond.
354 max_media_ports -- specify maximum number of audio ports to be
355 supported by the conference bridge.
356 quality -- specify the audio quality setting (1-10)
357 ptime -- specify the audio packet length of transmitted
358 RTP packet.
359 no_vad -- disable Voice Activity Detector (VAD) or Silence
360 Detector (SD)
361 ilbc_mode -- specify iLBC codec mode (must be 30 for now)
362 tx_drop_pct -- randomly drop transmitted RTP packets (for
363 simulation). Number is in percent.
364 rx_drop_pct -- randomly drop received RTP packets (for
365 simulation). Number is in percent.
366 ec_options -- Echo Canceller option (specify zero).
367 ec_tail_len -- specify Echo Canceller tail length in milliseconds.
368 Value zero will disable the echo canceller.
369 jb_min -- specify the minimum jitter buffer size in
370 milliseconds. Put -1 for default.
371 jb_max -- specify the maximum jitter buffer size in
372 milliseconds. Put -1 for default.
373 enable_ice -- enable Interactive Connectivity Establishment (ICE)
374 enable_turn -- enable TURN relay. TURN server settings must also
375 be configured.
376 turn_server -- specify the domain or hostname or IP address of
377 the TURN server, in "host[:port]" format.
378 turn_conn_type -- specify connection type to the TURN server, from
379 the TURNConnType constant.
380 turn_cred -- specify AuthCred for the TURN credential.
381 """
382 clock_rate = 16000
383 snd_clock_rate = 0
384 snd_auto_close_time = 5
385 channel_count = 1
386 audio_frame_ptime = 20
387 max_media_ports = 32
388 quality = 6
389 ptime = 0
390 no_vad = False
391 ilbc_mode = 30
392 tx_drop_pct = 0
393 rx_drop_pct = 0
394 ec_options = 0
395 ec_tail_len = 256
396 jb_min = -1
397 jb_max = -1
398 enable_ice = True
399 enable_turn = False
400 turn_server = ""
401 turn_conn_type = TURNConnType.UDP
402 turn_cred = None
403
404 def __init__(self):
405 default = _pjsua.media_config_default()
406 self._cvt_from_pjsua(default)
407
408 def _cvt_from_pjsua(self, cfg):
409 self.clock_rate = cfg.clock_rate
410 self.snd_clock_rate = cfg.snd_clock_rate
411 self.snd_auto_close_time = cfg.snd_auto_close_time
412 self.channel_count = cfg.channel_count
413 self.audio_frame_ptime = cfg.audio_frame_ptime
414 self.max_media_ports = cfg.max_media_ports
415 self.quality = cfg.quality
416 self.ptime = cfg.ptime
417 self.no_vad = cfg.no_vad
418 self.ilbc_mode = cfg.ilbc_mode
419 self.tx_drop_pct = cfg.tx_drop_pct
420 self.rx_drop_pct = cfg.rx_drop_pct
421 self.ec_options = cfg.ec_options
422 self.ec_tail_len = cfg.ec_tail_len
423 self.jb_min = cfg.jb_min
424 self.jb_max = cfg.jb_max
425 self.enable_ice = cfg.enable_ice
426 self.enable_turn = cfg.enable_turn
427 self.turn_server = cfg.turn_server
428 self.turn_conn_type = cfg.turn_conn_type
429 if cfg.turn_username:
430 self.turn_cred = AuthCred(cfg.turn_realm, cfg.turn_username,
431 cfg.turn_passwd, cfg.turn_passwd_type)
432 else:
433 self.turn_cred = None
434
435 def _cvt_to_pjsua(self):
436 cfg = _pjsua.media_config_default()
437 cfg.clock_rate = self.clock_rate
438 cfg.snd_clock_rate = self.snd_clock_rate
439 cfg.snd_auto_close_time = self.snd_auto_close_time
440 cfg.channel_count = self.channel_count
441 cfg.audio_frame_ptime = self.audio_frame_ptime
442 cfg.max_media_ports = self.max_media_ports
443 cfg.quality = self.quality
444 cfg.ptime = self.ptime
445 cfg.no_vad = self.no_vad
446 cfg.ilbc_mode = self.ilbc_mode
447 cfg.tx_drop_pct = self.tx_drop_pct
448 cfg.rx_drop_pct = self.rx_drop_pct
449 cfg.ec_options = self.ec_options
450 cfg.ec_tail_len = self.ec_tail_len
451 cfg.jb_min = self.jb_min
452 cfg.jb_max = self.jb_max
453 cfg.enable_ice = self.enable_ice
454 cfg.enable_turn = self.enable_turn
455 cfg.turn_server = self.turn_server
456 cfg.turn_conn_type = self.turn_conn_type
457 if self.turn_cred:
458 cfg.turn_realm = self.turn_cred.realm
459 cfg.turn_username = self.turn_cred.username
460 cfg.turn_passwd_type = self.turn_cred.passwd_type
461 cfg.turn_passwd = self.turn_cred.passwd
462 return cfg
463
464
465class TransportConfig:
466 """SIP transport configuration class.
467
468 Member configuration:
469
470 port -- port number.
471 bound_addr -- optionally specify the address to bind the socket to.
472 Default is empty to bind to INADDR_ANY.
473 public_addr -- optionally override the published address for this
474 transport. If empty, the default behavior is to get
475 the public address from STUN or from the selected
476 local interface. Format is "host:port".
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500477 qos_type -- High level traffic classification.
478 Enumerator:
479 0: PJ_QOS_TYPE_BEST_EFFORT
480 Best effort traffic (default value). Any QoS function calls with
481 specifying this value are effectively no-op
482 1: PJ_QOS_TYPE_BACKGROUND
483 Background traffic.
484 2: PJ_QOS_TYPE_VIDEO
485 Video traffic.
486 3: PJ_QOS_TYPE_VOICE
487 Voice traffic.
488 4: PJ_QOS_TYPE_CONTROL
489 Control traffic.
490 qos_params_flags -- Determines which values to set, bitmask of pj_qos_flag.
491 PJ_QOS_PARAM_HAS_DSCP = 1
492 PJ_QOS_PARAM_HAS_SO_PRIO = 2
493 PJ_QOS_PARAM_HAS_WMM = 4
494 qos_params_dscp_val -- The 6 bits DSCP value to set.
495 qos_params_so_prio -- Socket SO_PRIORITY value.
496 qos_params_wmm_prio -- Standard WMM priorities.
497 Enumerator:
498 0: PJ_QOS_WMM_PRIO_BULK_EFFORT: Bulk effort priority
499 1: PJ_QOS_WMM_PRIO_BULK: Bulk priority.
500 2: PJ_QOS_WMM_PRIO_VIDEO: Video priority
501 3: PJ_QOS_WMM_PRIO_VOICE: Voice priority.
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400502 """
503 port = 0
504 bound_addr = ""
505 public_addr = ""
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500506
507 qos_type = 0
508 qos_params_flags = 0
509 qos_params_dscp_val = 0
510 qos_params_so_prio = 0
511 qos_params_wmm_prio = 0
512
513
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400514
515 def __init__(self, port=0,
516 bound_addr="", public_addr=""):
517 self.port = port
518 self.bound_addr = bound_addr
519 self.public_addr = public_addr
520
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500521 def _cvt_from_pjsua(self, cfg):
522 self.port = cfg.port
523 self.bound_addr = cfg.bound_addr
524 self.public_addr = cfg.public_addr
525 self.qos_type = cfg.qos_type
526 self.qos_params_flags = cfg.qos_params_flags
527 self.qos_params_dscp_val = cfg.qos_params_dscp_val
528 self.qos_params_so_prio = cfg.qos_params_so_prio
529 self.qos_params_wmm_prio = cfg.qos_params_wmm_prio
530
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400531 def _cvt_to_pjsua(self):
532 cfg = _pjsua.transport_config_default()
533 cfg.port = self.port
534 cfg.bound_addr = self.bound_addr
535 cfg.public_addr = self.public_addr
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500536 cfg.qos_type = self.qos_type
537 cfg.qos_params_flags = self.qos_params_flags
538 cfg.qos_params_dscp_val = self.qos_params_dscp_val
539 cfg.qos_params_so_prio = self.qos_params_so_prio
540 cfg.qos_params_wmm_prio = self.qos_params_wmm_prio
541
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400542 return cfg
543
544
545class TransportInfo:
546 """SIP transport info.
547
548 Member documentation:
549
550 type -- transport type, from TransportType constants.
551 description -- longer description for this transport.
552 is_reliable -- True if transport is reliable.
553 is_secure -- True if transport is secure.
554 is_datagram -- True if transport is datagram based.
555 host -- the IP address of this transport.
556 port -- the port number.
557 ref_cnt -- number of objects referencing this transport.
558 """
559 type = ""
560 description = ""
561 is_reliable = False
562 is_secure = False
563 is_datagram = False
564 host = ""
565 port = 0
566 ref_cnt = 0
567
568 def __init__(self, ti):
569 self.type = ti.type_name
570 self.description = ti.info
571 self.is_reliable = (ti.flag & TransportFlag.RELIABLE)
572 self.is_secure = (ti.flag & TransportFlag.SECURE)
573 self.is_datagram = (ti.flag & TransportFlag.DATAGRAM)
574 self.host = ti.addr
575 self.port = ti.port
576 self.ref_cnt = ti.usage_count
577
578
579class Transport:
580 "SIP transport class."
581 _id = -1
582 _lib = None
583 _obj_name = ""
584
585 def __init__(self, lib, id):
586 self._lib = weakref.proxy(lib)
587 self._id = id
588 self._obj_name = "{Transport " + self.info().description + "}"
589 _Trace((self, 'created'))
590
591 def __del__(self):
592 _Trace((self, 'destroyed'))
593
594 def __str__(self):
595 return self._obj_name
596
597 def info(self):
598 """Get TransportInfo.
599 """
600 lck = self._lib.auto_lock()
601 ti = _pjsua.transport_get_info(self._id)
602 if not ti:
603 self._lib._err_check("info()", self, -1, "Invalid transport")
604 return TransportInfo(ti)
605
606 def enable(self):
607 """Enable this transport."""
608 lck = self._lib.auto_lock()
609 err = _pjsua.transport_set_enable(self._id, True)
610 self._lib._err_check("enable()", self, err)
611
612 def disable(self):
613 """Disable this transport."""
614 lck = self._lib.auto_lock()
615 err = _pjsua.transport_set_enable(self._id, 0)
616 self._lib._err_check("disable()", self, err)
617
618 def close(self, force=False):
619 """Close and destroy this transport.
620
621 Keyword argument:
622 force -- force deletion of this transport (not recommended).
623 """
624 lck = self._lib.auto_lock()
625 err = _pjsua.transport_close(self._id, force)
626 self._lib._err_check("close()", self, err)
627
628
629class SIPUri:
630 """Helper class to parse the most important components of SIP URI.
631
632 Member documentation:
633
634 scheme -- URI scheme ("sip" or "sips")
635 user -- user part of the URI (may be empty)
636 host -- host name part
637 port -- optional port number (zero if port is not specified).
638 transport -- transport parameter, or empty if transport is not
639 specified.
640
641 """
642 scheme = ""
643 user = ""
644 host = ""
645 port = 0
646 transport = ""
647
648 def __init__(self, uri=None):
649 if uri:
650 self.decode(uri)
651
652 def decode(self, uri):
653 """Parse SIP URL.
654
655 Keyword argument:
656 uri -- the URI string.
657
658 """
659 self.scheme, self.user, self.host, self.port, self.transport = \
660 _pjsua.parse_simple_uri(uri)
661
662 def encode(self):
663 """Encode this object into SIP URI string.
664
665 Return:
666 URI string.
667
668 """
669 output = self.scheme + ":"
670 if self.user and len(self.user):
671 output = output + self.user + "@"
672 output = output + self.host
673 if self.port:
674 output = output + ":" + output(self.port)
675 if self.transport:
676 output = output + ";transport=" + self.transport
677 return output
678
679
680class AuthCred:
681 """Authentication credential for SIP or TURN account.
682
683 Member documentation:
684
685 scheme -- authentication scheme (default is "Digest")
686 realm -- realm
687 username -- username
688 passwd_type -- password encoding (zero for plain-text)
689 passwd -- the password
690 """
691 scheme = "Digest"
692 realm = "*"
693 username = ""
694 passwd_type = 0
695 passwd = ""
696
697 def __init__(self, realm, username, passwd, scheme="Digest", passwd_type=0):
698 self.scheme = scheme
699 self.realm = realm
700 self.username = username
701 self.passwd_type = passwd_type
702 self.passwd = passwd
703
704
705class AccountConfig:
706 """ This describes account configuration to create an account.
707
708 Member documentation:
709
710 priority -- account priority for matching incoming
711 messages.
712 id -- SIP URI of this account. This setting is
713 mandatory.
714 force_contact -- force to use this URI as Contact URI. Setting
715 this value is generally not recommended.
716 reg_uri -- specify the registrar URI. Mandatory if
717 registration is required.
718 reg_timeout -- specify the SIP registration refresh interval
719 in seconds.
720 require_100rel -- specify if reliable provisional response is
721 to be enforced (with Require header).
722 publish_enabled -- specify if PUBLISH should be used. When
723 enabled, the PUBLISH will be sent to the
724 registrar.
725 pidf_tuple_id -- optionally specify the tuple ID in outgoing
726 PIDF document.
727 proxy -- list of proxy URI.
728 auth_cred -- list of AuthCred containing credentials to
729 authenticate against the registrars and
730 the proxies.
731 auth_initial_send -- specify if empty Authorization header should be
732 sent. May be needed for IMS.
733 auth_initial_algorithm -- when auth_initial_send is enabled, optionally
734 specify the authentication algorithm to use.
735 Valid values are "md5", "akav1-md5", or
736 "akav2-md5".
737 transport_id -- optionally specify the transport ID to be used
738 by this account. Shouldn't be needed unless
739 for specific requirements (e.g. in multi-homed
740 scenario).
741 allow_contact_rewrite -- specify whether the account should learn its
742 Contact address from REGISTER response and
743 update the registration accordingly. Default is
744 True.
745 ka_interval -- specify the interval to send NAT keep-alive
746 packet.
747 ka_data -- specify the NAT keep-alive packet contents.
748 use_srtp -- specify the SRTP usage policy. Valid values
749 are: 0=disable, 1=optional, 2=mandatory.
750 Default is 0.
751 srtp_secure_signaling -- specify the signaling security level required
752 by SRTP. Valid values are: 0=no secure
753 transport is required, 1=hop-by-hop secure
754 transport such as TLS is required, 2=end-to-
755 end secure transport is required (i.e. "sips").
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500756 rtp_transport_cfg -- the rtp-transport-configuration that is usede, when
757 a rtp-connection is being established.
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400758 """
759 priority = 0
760 id = ""
761 force_contact = ""
762 reg_uri = ""
763 reg_timeout = 0
764 require_100rel = False
765 publish_enabled = False
766 pidf_tuple_id = ""
767 proxy = []
768 auth_cred = []
769 auth_initial_send = False
770 auth_initial_algorithm = ""
771 transport_id = -1
772 allow_contact_rewrite = True
773 ka_interval = 15
774 ka_data = "\r\n"
775 use_srtp = 0
776 srtp_secure_signaling = 1
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500777 rtp_transport_cfg = None
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400778
779 def __init__(self, domain="", username="", password="",
780 display="", registrar="", proxy=""):
781 """
782 Construct account config. If domain argument is specified,
783 a typical configuration will be built.
784
785 Keyword arguments:
786 domain -- domain name of the server.
787 username -- user name.
788 password -- plain-text password.
789 display -- optional display name for the user name.
790 registrar -- the registrar URI. If domain name is specified
791 and this argument is empty, the registrar URI
792 will be constructed from the domain name.
793 proxy -- the proxy URI. If domain name is specified
794 and this argument is empty, the proxy URI
795 will be constructed from the domain name.
796
797 """
798 default = _pjsua.acc_config_default()
799 self._cvt_from_pjsua(default)
800 if domain!="":
801 self.build_config(domain, username, password,
802 display, registrar, proxy)
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500803 self.rtp_transport_cfg = _pjsua.transport_config_default()
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400804
805 def build_config(self, domain, username, password, display="",
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500806 registrar="", proxy="", rtp_transport_cfg = None):
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400807 """
808 Construct account config. If domain argument is specified,
809 a typical configuration will be built.
810
811 Keyword arguments:
812 domain -- domain name of the server.
813 username -- user name.
814 password -- plain-text password.
815 display -- optional display name for the user name.
816 registrar -- the registrar URI. If domain name is specified
817 and this argument is empty, the registrar URI
818 will be constructed from the domain name.
819 proxy -- the proxy URI. If domain name is specified
820 and this argument is empty, the proxy URI
821 will be constructed from the domain name.
822
823 """
824 if display != "":
825 display = display + " "
826 userpart = username
827 if userpart != "":
828 userpart = userpart + "@"
829 self.id = display + "<sip:" + userpart + domain + ">"
830 self.reg_uri = registrar
831 if self.reg_uri == "":
832 self.reg_uri = "sip:" + domain
833 if proxy == "":
834 proxy = "sip:" + domain + ";lr"
835 if proxy.find(";lr") == -1:
836 proxy = proxy + ";lr"
837 self.proxy.append(proxy)
838 if username != "":
839 self.auth_cred.append(AuthCred("*", username, password))
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500840
841 if (rtp_transport_cfg is not None):
842 self.rtp_transport_cfg = rtp_transport_cfg
843 else:
844 self.rtp_transport_cfg = _pjsua.Transport_Config()
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400845
846 def _cvt_from_pjsua(self, cfg):
847 self.priority = cfg.priority
848 self.id = cfg.id
849 self.force_contact = cfg.force_contact
850 self.reg_uri = cfg.reg_uri
851 self.reg_timeout = cfg.reg_timeout
852 self.require_100rel = cfg.require_100rel
853 self.publish_enabled = cfg.publish_enabled
854 self.pidf_tuple_id = cfg.pidf_tuple_id
855 self.proxy = cfg.proxy
856 for cred in cfg.cred_info:
857 self.auth_cred.append(AuthCred(cred.realm, cred.username,
858 cred.data, cred.scheme,
859 cred.data_type))
860 self.auth_initial_send = cfg.auth_initial_send
861 self.auth_initial_algorithm = cfg.auth_initial_algorithm
862 self.transport_id = cfg.transport_id
863 self.allow_contact_rewrite = cfg.allow_contact_rewrite
864 self.ka_interval = cfg.ka_interval
865 self.ka_data = cfg.ka_data
866 self.use_srtp = cfg.use_srtp
867 self.srtp_secure_signaling = cfg.srtp_secure_signaling
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500868 if (self.rtp_transport_cfg is not None):
869 self.rtp_transport_cfg._cvt_from_pjsua(cfg.rtp_transport_cfg)
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400870
871 def _cvt_to_pjsua(self):
872 cfg = _pjsua.acc_config_default()
873 cfg.priority = self.priority
874 cfg.id = self.id
875 cfg.force_contact = self.force_contact
876 cfg.reg_uri = self.reg_uri
877 cfg.reg_timeout = self.reg_timeout
878 cfg.require_100rel = self.require_100rel
879 cfg.publish_enabled = self.publish_enabled
880 cfg.pidf_tuple_id = self.pidf_tuple_id
881 cfg.proxy = self.proxy
882 for cred in self.auth_cred:
883 c = _pjsua.Pjsip_Cred_Info()
884 c.realm = cred.realm
885 c.scheme = cred.scheme
886 c.username = cred.username
887 c.data_type = cred.passwd_type
888 c.data = cred.passwd
889 cfg.cred_info.append(c)
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500890
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400891 cfg.auth_initial_send = self.auth_initial_send
892 cfg.auth_initial_algorithm = self.auth_initial_algorithm
893 cfg.transport_id = self.transport_id
894 cfg.allow_contact_rewrite = self.allow_contact_rewrite
895 cfg.ka_interval = self.ka_interval
896 cfg.ka_data = self.ka_data
897 cfg.use_srtp = self.use_srtp
898 cfg.srtp_secure_signaling = self.srtp_secure_signaling
Alexandre Lision94f06ba2013-12-09 16:28:33 -0500899
900 cfg.rtp_transport_cfg = self.rtp_transport_cfg._cvt_to_pjsua()
901
Tristan Matthews0a329cc2013-07-17 13:20:14 -0400902 return cfg
903
904
905# Account information
906class AccountInfo:
907 """This describes Account info. Application retrives account info
908 with Account.info().
909
910 Member documentation:
911
912 is_default -- True if this is the default account.
913 uri -- the account URI.
914 reg_active -- True if registration is active for this account.
915 reg_expires -- contains the current registration expiration value,
916 in seconds.
917 reg_status -- the registration status. If the value is less than
918 700, it specifies SIP status code. Value greater than
919 this specifies the error code.
920 reg_reason -- contains the registration status text (e.g. the
921 error message).
922 online_status -- the account's presence online status, True if it's
923 publishing itself as online.
924 online_text -- the account's presence status text.
925
926 """
927 is_default = False
928 uri = ""
929 reg_active = False
930 reg_expires = -1
931 reg_status = 0
932 reg_reason = ""
933 online_status = False
934 online_text = ""
935
936 def __init__(self, ai):
937 self.is_default = ai.is_default
938 self.uri = ai.acc_uri
939 self.reg_active = ai.has_registration
940 self.reg_expires = ai.expires
941 self.reg_status = ai.status
942 self.reg_reason = ai.status_text
943 self.online_status = ai.online_status
944 self.online_text = ai.online_status_text
945
946# Account callback
947class AccountCallback:
948 """Class to receive notifications on account's events.
949
950 Derive a class from this class and register it to the Account object
951 using Account.set_callback() to start receiving events from the Account
952 object.
953
954 Member documentation:
955
956 account -- the Account object.
957
958 """
959 account = None
960
961 def __init__(self, account=None):
962 self._set_account(account)
963
964 def __del__(self):
965 pass
966
967 def _set_account(self, account):
968 if account:
969 self.account = weakref.proxy(account)
970 else:
971 self.account = None
972
973 def on_reg_state(self):
974 """Notification that the registration status has changed.
975 """
976 pass
977
978 def on_incoming_call(self, call):
979 """Notification about incoming call.
980
981 Unless this callback is implemented, the default behavior is to
982 reject the call with default status code.
983
984 Keyword arguments:
985 call -- the new incoming call
986 """
987 call.hangup()
988
989 def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres_obj):
990 """Notification when incoming SUBSCRIBE request is received.
991
992 Application may use this callback to authorize the incoming
993 subscribe request (e.g. ask user permission if the request
994 should be granted)
995
996 Keyword arguments:
997 buddy -- The buddy object, if buddy is found. Otherwise
998 the value is None.
999 from_uri -- The URI string of the sender.
1000 pres_obj -- Opaque presence subscription object, which is
1001 needed by Account.pres_notify()
1002
1003 Return:
1004 Tuple (code, reason), where:
1005 code: The status code. If code is >= 300, the
1006 request is rejected. If code is 200, the
1007 request is accepted and NOTIFY will be sent
1008 automatically. If code is 202, application
1009 must accept or reject the request later with
1010 Account.press_notify().
1011 reason: Optional reason phrase, or None to use the
1012 default reasoh phrase for the status code.
1013 """
1014 return (200, None)
1015
1016 def on_pager(self, from_uri, contact, mime_type, body):
1017 """
1018 Notification that incoming instant message is received on
1019 this account.
1020
1021 Keyword arguments:
1022 from_uri -- sender's URI
1023 contact -- sender's Contact URI
1024 mime_type -- MIME type of the instant message body
1025 body -- the instant message body
1026
1027 """
1028 pass
1029
1030 def on_pager_status(self, to_uri, body, im_id, code, reason):
1031 """
1032 Notification about the delivery status of previously sent
1033 instant message.
1034
1035 Keyword arguments:
1036 to_uri -- the destination URI of the message
1037 body -- the message body
1038 im_id -- message ID
1039 code -- SIP status code
1040 reason -- SIP reason phrase
1041
1042 """
1043 pass
1044
1045 def on_typing(self, from_uri, contact, is_typing):
1046 """
1047 Notification that remote is typing or stop typing.
1048
1049 Keyword arguments:
1050 buddy -- Buddy object for the sender, if found. Otherwise
1051 this will be None
1052 from_uri -- sender's URI of the indication
1053 contact -- sender's contact URI
1054 is_typing -- boolean to indicate whether remote is currently
1055 typing an instant message.
1056
1057 """
1058 pass
1059
1060 def on_mwi_info(self, body):
1061 """
1062 Notification about change in Message Summary / Message Waiting
1063 Indication (RFC 3842) status. MWI subscription must be enabled
1064 in the account config to receive this notification.
1065
1066 Keyword arguments:
1067 body -- String containing message body as received in the
1068 NOTIFY request.
1069
1070 """
1071 pass
1072
1073
1074
1075class Account:
1076 """This describes SIP account class.
1077
1078 PJSUA accounts provide identity (or identities) of the user who is
1079 currently using the application. In SIP terms, the identity is used
1080 as the From header in outgoing requests.
1081
1082 Account may or may not have client registration associated with it.
1083 An account is also associated with route set and some authentication
1084 credentials, which are used when sending SIP request messages using
1085 the account. An account also has presence's online status, which
1086 will be reported to remote peer when they subscribe to the account's
1087 presence, or which is published to a presence server if presence
1088 publication is enabled for the account.
1089
1090 Account is created with Lib.create_account(). At least one account
1091 MUST be created. If no user association is required, application can
1092 create a userless account by calling Lib.create_account_for_transport().
1093 A userless account identifies local endpoint instead of a particular
1094 user, and it correspond with a particular transport instance.
1095
1096 Also one account must be set as the default account, which is used as
1097 the account to use when PJSUA fails to match a request with any other
1098 accounts.
1099
1100 """
1101 _id = -1
1102 _lib = None
1103 _cb = AccountCallback(None)
1104 _obj_name = ""
1105
1106 def __init__(self, lib, id, cb=None):
1107 """Construct this class. This is normally called by Lib class and
1108 not by application.
1109
1110 Keyword arguments:
1111 lib -- the Lib instance.
1112 id -- the pjsua account ID.
1113 cb -- AccountCallback instance to receive events from this Account.
1114 If callback is not specified here, it must be set later
1115 using set_callback().
1116 """
1117 self._id = id
1118 self._lib = weakref.ref(lib)
1119 self._obj_name = "{Account " + self.info().uri + "}"
1120 self.set_callback(cb)
1121 _pjsua.acc_set_user_data(self._id, self)
1122 _Trace((self, 'created'))
1123
1124 def __del__(self):
1125 if self._id != -1:
1126 _pjsua.acc_set_user_data(self._id, 0)
1127 _Trace((self, 'destroyed'))
1128
1129 def __str__(self):
1130 return self._obj_name
1131
1132 def info(self):
1133 """Retrieve AccountInfo for this account.
1134 """
1135 lck = self._lib().auto_lock()
1136 ai = _pjsua.acc_get_info(self._id)
1137 if ai==None:
1138 self._lib()._err_check("info()", self, -1, "Invalid account")
1139 return AccountInfo(ai)
1140
1141 def is_valid(self):
1142 """
1143 Check if this account is still valid.
1144
1145 """
1146 lck = self._lib().auto_lock()
1147 return _pjsua.acc_is_valid(self._id)
1148
1149 def set_callback(self, cb):
1150 """Register callback to receive notifications from this object.
1151
1152 Keyword argument:
1153 cb -- AccountCallback instance.
1154
1155 """
1156 if cb:
1157 self._cb = cb
1158 else:
1159 self._cb = AccountCallback(self)
1160 self._cb._set_account(self)
1161
1162 def set_default(self):
1163 """ Set this account as default account to send outgoing requests
1164 and as the account to receive incoming requests when more exact
1165 matching criteria fails.
1166 """
1167 lck = self._lib().auto_lock()
1168 err = _pjsua.acc_set_default(self._id)
1169 self._lib()._err_check("set_default()", self, err)
1170
1171 def is_default(self):
1172 """ Check if this account is the default account.
1173
1174 """
1175 lck = self._lib().auto_lock()
1176 def_id = _pjsua.acc_get_default()
1177 return self.is_valid() and def_id==self._id
1178
1179 def delete(self):
1180 """ Delete this account.
1181
1182 """
1183 lck = self._lib().auto_lock()
1184 err = _pjsua.acc_set_user_data(self._id, 0)
1185 self._lib()._err_check("delete()", self, err)
1186 err = _pjsua.acc_del(self._id)
1187 self._lib()._err_check("delete()", self, err)
1188 self._id = -1
1189
1190 def set_basic_status(self, is_online):
1191 """ Set basic presence status of this account.
1192
1193 Keyword argument:
1194 is_online -- boolean to indicate basic presence availability.
1195
1196 """
1197 lck = self._lib().auto_lock()
1198 err = _pjsua.acc_set_online_status(self._id, is_online)
1199 self._lib()._err_check("set_basic_status()", self, err)
1200
1201 def set_presence_status(self, is_online,
1202 activity=PresenceActivity.UNKNOWN,
1203 pres_text="", rpid_id=""):
1204 """ Set presence status of this account.
1205
1206 Keyword arguments:
1207 is_online -- boolean to indicate basic presence availability
1208 activity -- value from PresenceActivity
1209 pres_text -- optional string to convey additional information about
1210 the activity (such as "On the phone")
1211 rpid_id -- optional string to be placed as RPID ID.
1212
1213 """
1214 lck = self._lib().auto_lock()
1215 err = _pjsua.acc_set_online_status2(self._id, is_online, activity,
1216 pres_text, rpid_id)
1217 self._lib()._err_check("set_presence_status()", self, err)
1218
1219 def set_registration(self, renew):
1220 """Manually renew registration or unregister from the server.
1221
1222 Keyword argument:
1223 renew -- boolean to indicate whether registration is renewed.
1224 Setting this value for False will trigger unregistration.
1225
1226 """
1227 lck = self._lib().auto_lock()
1228 err = _pjsua.acc_set_registration(self._id, renew)
1229 self._lib()._err_check("set_registration()", self, err)
1230
1231 def set_transport(self, transport):
1232 """Set this account to only use the specified transport to send
1233 outgoing requests.
1234
1235 Keyword argument:
1236 transport -- Transport object.
1237
1238 """
1239 lck = self._lib().auto_lock()
1240 err = _pjsua.acc_set_transport(self._id, transport._id)
1241 self._lib()._err_check("set_transport()", self, err)
1242
1243 def make_call(self, dst_uri, cb=None, hdr_list=None):
1244 """Make outgoing call to the specified URI.
1245
1246 Keyword arguments:
1247 dst_uri -- Destination SIP URI.
1248 cb -- CallCallback instance to be installed to the newly
1249 created Call object. If this CallCallback is not
1250 specified (i.e. None is given), it must be installed
1251 later using call.set_callback().
1252 hdr_list -- Optional list of headers to be sent with outgoing
1253 INVITE
1254
1255 Return:
1256 Call instance.
1257 """
1258 lck = self._lib().auto_lock()
1259 call = Call(self._lib(), -1, cb)
1260 err, cid = _pjsua.call_make_call(self._id, dst_uri, 0,
1261 call, Lib._create_msg_data(hdr_list))
1262 self._lib()._err_check("make_call()", self, err)
1263 call.attach_to_id(cid)
1264 return call
1265
1266 def add_buddy(self, uri, cb=None):
1267 """Add new buddy.
1268
1269 Keyword argument:
1270 uri -- SIP URI of the buddy
1271 cb -- BuddyCallback instance to be installed to the newly
1272 created Buddy object. If this callback is not specified
1273 (i.e. None is given), it must be installed later using
1274 buddy.set_callback().
1275
1276 Return:
1277 Buddy object
1278 """
1279 lck = self._lib().auto_lock()
1280 buddy_cfg = _pjsua.buddy_config_default()
1281 buddy_cfg.uri = uri
1282 buddy_cfg.subscribe = False
1283 err, buddy_id = _pjsua.buddy_add(buddy_cfg)
1284 self._lib()._err_check("add_buddy()", self, err)
1285 buddy = Buddy(self._lib(), buddy_id, self, cb)
1286 return buddy
1287
1288 def pres_notify(self, pres_obj, state, reason="", hdr_list=None):
1289 """Send NOTIFY to inform account presence status or to terminate
1290 server side presence subscription.
1291
1292 Keyword arguments:
1293 pres_obj -- The subscription object from on_incoming_subscribe()
1294 callback
1295 state -- Subscription state, from SubscriptionState
1296 reason -- Optional reason phrase.
1297 hdr_list -- Optional header list.
1298 """
1299 lck = self._lib().auto_lock()
1300 _pjsua.acc_pres_notify(self._id, pres_obj, state, reason,
1301 Lib._create_msg_data(hdr_list))
1302
1303 def send_pager(self, uri, text, im_id=0, content_type="text/plain", \
1304 hdr_list=None):
1305 """Send instant message to arbitrary URI.
1306
1307 Keyword arguments:
1308 text -- Instant message to be sent
1309 uri -- URI to send the Instant Message to.
1310 im_id -- Optional instant message ID to identify this
1311 instant message when delivery status callback
1312 is called.
1313 content_type -- MIME type identifying the instant message
1314 hdr_list -- Optional list of headers to be sent with the
1315 request.
1316
1317 """
1318 lck = self._lib().auto_lock()
1319 err = _pjsua.im_send(self._id, uri, \
1320 content_type, text, \
1321 Lib._create_msg_data(hdr_list), \
1322 im_id)
1323 self._lib()._err_check("send_pager()", self, err)
1324
1325class CallCallback:
1326 """Class to receive event notification from Call objects.
1327
1328 Use Call.set_callback() method to install instance of this callback
1329 class to receive event notifications from the call object.
1330
1331 Member documentation:
1332
1333 call -- the Call object.
1334
1335 """
1336 call = None
1337
1338 def __init__(self, call=None):
1339 self._set_call(call)
1340
1341 def __del__(self):
1342 pass
1343
1344 def _set_call(self, call):
1345 if call:
1346 self.call = weakref.proxy(call)
1347 else:
1348 self.call = None
1349
1350 def on_state(self):
1351 """Notification that the call's state has changed.
1352
1353 """
1354 pass
1355
1356 def on_media_state(self):
1357 """Notification that the call's media state has changed.
1358
1359 """
1360 pass
1361
1362 def on_dtmf_digit(self, digits):
1363 """Notification on incoming DTMF digits.
1364
1365 Keyword argument:
1366 digits -- string containing the received digits.
1367
1368 """
1369 pass
1370
1371 def on_transfer_request(self, dst, code):
1372 """Notification that call is being transfered by remote party.
1373
1374 Application can decide to accept/reject transfer request by returning
1375 code greater than or equal to 500. The default behavior is to accept
1376 the transfer by returning 202.
1377
1378 Keyword arguments:
1379 dst -- string containing the destination URI
1380 code -- the suggested status code to return to accept the request.
1381
1382 Return:
1383 the callback should return 202 to accept the request, or 300-699 to
1384 reject the request.
1385
1386 """
1387 return code
1388
1389 def on_transfer_status(self, code, reason, final, cont):
1390 """
1391 Notification about the status of previous call transfer request.
1392
1393 Keyword arguments:
1394 code -- SIP status code to indicate completion status.
1395 text -- SIP status reason phrase.
1396 final -- if True then this is a final status and no further
1397 notifications will be sent for this call transfer
1398 status.
1399 cont -- suggested return value.
1400
1401 Return:
1402 If the callback returns false then no further notification will
1403 be sent for the transfer request for this call.
1404
1405 """
1406 return cont
1407
1408 def on_replace_request(self, code, reason):
1409 """Notification when incoming INVITE with Replaces header is received.
1410
1411 Application may reject the request by returning value greather than
1412 or equal to 500. The default behavior is to accept the request.
1413
1414 Keyword arguments:
1415 code -- default status code to return
1416 reason -- default reason phrase to return
1417
1418 Return:
1419 The callback should return (code, reason) tuple.
1420
1421 """
1422 return code, reason
1423
1424 def on_replaced(self, new_call):
1425 """
1426 Notification that this call will be replaced with new_call.
1427 After this callback is called, this call will be disconnected.
1428
1429 Keyword arguments:
1430 new_call -- the new call that will replace this call.
1431 """
1432 pass
1433
1434 def on_pager(self, mime_type, body):
1435 """
1436 Notification that incoming instant message is received on
1437 this call.
1438
1439 Keyword arguments:
1440 mime_type -- MIME type of the instant message body.
1441 body -- the instant message body.
1442
1443 """
1444 pass
1445
1446 def on_pager_status(self, body, im_id, code, reason):
1447 """
1448 Notification about the delivery status of previously sent
1449 instant message.
1450
1451 Keyword arguments:
1452 body -- message body
1453 im_id -- message ID
1454 code -- SIP status code
1455 reason -- SIP reason phrase
1456
1457 """
1458 pass
1459
1460 def on_typing(self, is_typing):
1461 """
1462 Notification that remote is typing or stop typing.
1463
1464 Keyword arguments:
1465 is_typing -- boolean to indicate whether remote is currently
1466 typing an instant message.
1467
1468 """
1469 pass
1470
1471
1472class CallInfo:
1473 """This structure contains various information about Call.
1474
1475 Application may retrieve this information with Call.info().
1476
1477 Member documentation:
1478
1479 role -- CallRole
1480 account -- Account object.
1481 uri -- SIP URI of local account.
1482 contact -- local Contact URI.
1483 remote_uri -- remote SIP URI.
1484 remote_contact -- remote Contact URI
1485 sip_call_id -- call's Call-ID identification
1486 state -- CallState
1487 state_text -- state text.
1488 last_code -- last SIP status code
1489 last_reason -- text phrase for last_code
1490 media_state -- MediaState
1491 media_dir -- MediaDir
1492 conf_slot -- conference slot number for this call.
1493 call_time -- call's connected duration in seconds.
1494 total_time -- total call duration in seconds.
1495 """
1496 role = CallRole.CALLER
1497 account = None
1498 uri = ""
1499 contact = ""
1500 remote_uri = ""
1501 remote_contact = ""
1502 sip_call_id = ""
1503 state = CallState.NULL
1504 state_text = ""
1505 last_code = 0
1506 last_reason = ""
1507 media_state = MediaState.NULL
1508 media_dir = MediaDir.NULL
1509 conf_slot = -1
1510 call_time = 0
1511 total_time = 0
1512
1513 def __init__(self, lib=None, ci=None):
1514 if lib and ci:
1515 self._cvt_from_pjsua(lib, ci)
1516
1517 def _cvt_from_pjsua(self, lib, ci):
1518 self.role = ci.role
1519 self.account = lib._lookup_account(ci.acc_id)
1520 self.uri = ci.local_info
1521 self.contact = ci.local_contact
1522 self.remote_uri = ci.remote_info
1523 self.remote_contact = ci.remote_contact
1524 self.sip_call_id = ci.call_id
1525 self.state = ci.state
1526 self.state_text = ci.state_text
1527 self.last_code = ci.last_status
1528 self.last_reason = ci.last_status_text
1529 self.media_state = ci.media_status
1530 self.media_dir = ci.media_dir
1531 self.conf_slot = ci.conf_slot
1532 self.call_time = ci.connect_duration / 1000
1533 self.total_time = ci.total_duration / 1000
1534
1535
1536class Call:
1537 """This class represents SIP call.
1538
1539 Application initiates outgoing call with Account.make_call(), and
1540 incoming calls are reported in AccountCallback.on_incoming_call().
1541 """
1542 _id = -1
1543 _cb = None
1544 _lib = None
1545 _obj_name = ""
1546
1547 def __init__(self, lib, call_id, cb=None):
1548 self._lib = weakref.ref(lib)
1549 self.set_callback(cb)
1550 self.attach_to_id(call_id)
1551 _Trace((self, 'created'))
1552
1553 def __del__(self):
1554 if self._id != -1:
1555 _pjsua.call_set_user_data(self._id, 0)
1556 _Trace((self, 'destroyed'))
1557
1558 def __str__(self):
1559 return self._obj_name
1560
1561 def attach_to_id(self, call_id):
1562 lck = self._lib().auto_lock()
1563 if self._id != -1:
1564 _pjsua.call_set_user_data(self._id, 0)
1565 self._id = call_id
1566 if self._id != -1:
1567 _pjsua.call_set_user_data(self._id, self)
1568 self._obj_name = "{Call " + self.info().remote_uri + "}"
1569 else:
1570 self._obj_name = "{Call object}"
1571
1572 def set_callback(self, cb):
1573 """
1574 Set callback object to retrieve event notifications from this call.
1575
1576 Keyword arguments:
1577 cb -- CallCallback instance.
1578 """
1579 if cb:
1580 self._cb = cb
1581 else:
1582 self._cb = CallCallback(self)
1583 self._cb._set_call(self)
1584
1585 def info(self):
1586 """
1587 Get the CallInfo.
1588 """
1589 lck = self._lib().auto_lock()
1590 ci = _pjsua.call_get_info(self._id)
1591 if not ci:
1592 self._lib()._err_check("info", self, -1, "Invalid call")
1593 call_info = CallInfo(self._lib(), ci)
1594 return call_info
1595
1596 def is_valid(self):
1597 """
1598 Check if this call is still valid.
1599 """
1600 lck = self._lib().auto_lock()
1601 return _pjsua.call_is_active(self._id)
1602
1603 def dump_status(self, with_media=True, indent="", max_len=1024):
1604 """
1605 Dump the call status.
1606 """
1607 lck = self._lib().auto_lock()
1608 return _pjsua.call_dump(self._id, with_media, max_len, indent)
1609
1610 def answer(self, code=200, reason="", hdr_list=None):
1611 """
1612 Send provisional or final response to incoming call.
1613
1614 Keyword arguments:
1615 code -- SIP status code.
1616 reason -- Reason phrase. Put empty to send default reason
1617 phrase for the status code.
1618 hdr_list -- Optional list of headers to be sent with the
1619 INVITE response.
1620
1621 """
1622 lck = self._lib().auto_lock()
1623 err = _pjsua.call_answer(self._id, code, reason,
1624 Lib._create_msg_data(hdr_list))
1625 self._lib()._err_check("answer()", self, err)
1626
1627 def hangup(self, code=603, reason="", hdr_list=None):
1628 """
1629 Terminate the call.
1630
1631 Keyword arguments:
1632 code -- SIP status code.
1633 reason -- Reason phrase. Put empty to send default reason
1634 phrase for the status code.
1635 hdr_list -- Optional list of headers to be sent with the
1636 message.
1637
1638 """
1639 lck = self._lib().auto_lock()
1640 err = _pjsua.call_hangup(self._id, code, reason,
1641 Lib._create_msg_data(hdr_list))
1642 self._lib()._err_check("hangup()", self, err)
1643
1644 def hold(self, hdr_list=None):
1645 """
1646 Put the call on hold.
1647
1648 Keyword arguments:
1649 hdr_list -- Optional list of headers to be sent with the
1650 message.
1651 """
1652 lck = self._lib().auto_lock()
1653 err = _pjsua.call_set_hold(self._id, Lib._create_msg_data(hdr_list))
1654 self._lib()._err_check("hold()", self, err)
1655
1656 def unhold(self, hdr_list=None):
1657 """
1658 Release the call from hold.
1659
1660 Keyword arguments:
1661 hdr_list -- Optional list of headers to be sent with the
1662 message.
1663
1664 """
1665 lck = self._lib().auto_lock()
1666 err = _pjsua.call_reinvite(self._id, True,
1667 Lib._create_msg_data(hdr_list))
1668 self._lib()._err_check("unhold()", self, err)
1669
1670 def reinvite(self, hdr_list=None):
1671 """
1672 Send re-INVITE and optionally offer new codecs to use.
1673
1674 Keyword arguments:
1675 hdr_list -- Optional list of headers to be sent with the
1676 message.
1677
1678 """
1679 lck = self._lib().auto_lock()
1680 err = _pjsua.call_reinvite(self._id, True,
1681 Lib._create_msg_data(hdr_list))
1682 self._lib()._err_check("reinvite()", self, err)
1683
1684 def update(self, hdr_list=None, options=0):
1685 """
1686 Send UPDATE and optionally offer new codecs to use.
1687
1688 Keyword arguments:
1689 hdr_list -- Optional list of headers to be sent with the
1690 message.
1691 options -- Must be zero for now.
1692
1693 """
1694 lck = self._lib().auto_lock()
1695 err = _pjsua.call_update(self._id, options,
1696 Lib._create_msg_data(hdr_list))
1697 self._lib()._err_check("update()", self, err)
1698
1699 def transfer(self, dest_uri, hdr_list=None):
1700 """
1701 Transfer the call to new destination.
1702
1703 Keyword arguments:
1704 dest_uri -- Specify the SIP URI to transfer the call to.
1705 hdr_list -- Optional list of headers to be sent with the
1706 message.
1707
1708 """
1709 lck = self._lib().auto_lock()
1710 err = _pjsua.call_xfer(self._id, dest_uri,
1711 Lib._create_msg_data(hdr_list))
1712 self._lib()._err_check("transfer()", self, err)
1713
1714 def transfer_to_call(self, call, hdr_list=None, options=0):
1715 """
1716 Attended call transfer.
1717
1718 Keyword arguments:
1719 call -- The Call object to transfer call to.
1720 hdr_list -- Optional list of headers to be sent with the
1721 message.
1722 options -- Must be zero for now.
1723
1724 """
1725 lck = self._lib().auto_lock()
1726 err = _pjsua.call_xfer_replaces(self._id, call._id, options,
1727 Lib._create_msg_data(hdr_list))
1728 self._lib()._err_check("transfer_to_call()", self, err)
1729
1730 def dial_dtmf(self, digits):
1731 """
1732 Send DTMF digits with RTP event package.
1733
1734 Keyword arguments:
1735 digits -- DTMF digit string.
1736
1737 """
1738 lck = self._lib().auto_lock()
1739 err = _pjsua.call_dial_dtmf(self._id, digits)
1740 self._lib()._err_check("dial_dtmf()", self, err)
1741
1742 def send_request(self, method, hdr_list=None, content_type=None,
1743 body=None):
1744 """
1745 Send arbitrary request to remote call.
1746
1747 This is useful for example to send INFO request. Note that this
1748 function should not be used to send request that will change the
1749 call state such as CANCEL or BYE.
1750
1751 Keyword arguments:
1752 method -- SIP method name.
1753 hdr_list -- Optional header list to be sent with the request.
1754 content_type -- Content type to describe the body, if the body
1755 is present
1756 body -- Optional SIP message body.
1757
1758 """
1759 lck = self._lib().auto_lock()
1760 if hdr_list or body:
1761 msg_data = _pjsua.Msg_Data()
1762 if hdr_list:
1763 msg_data.hdr_list = hdr_list
1764 if content_type:
1765 msg_data.content_type = content_type
1766 if body:
1767 msg_data.msg_body = body
1768 else:
1769 msg_data = None
1770
1771 err = _pjsua.call_send_request(self._id, method, msg_data)
1772 self._lib()._err_check("send_request()", self, err)
1773
1774 def send_pager(self, text, im_id=0, content_type="text/plain",
1775 hdr_list=None):
1776 """Send instant message inside a call.
1777
1778 Keyword arguments:
1779 text -- Instant message to be sent
1780 im_id -- Optional instant message ID to identify this
1781 instant message when delivery status callback
1782 is called.
1783 content_type -- MIME type identifying the instant message
1784 hdr_list -- Optional list of headers to be sent with the
1785 request.
1786
1787 """
1788 lck = self._lib().auto_lock()
1789 err = _pjsua.call_send_im(self._id, \
1790 content_type, text, \
1791 Lib._create_msg_data(hdr_list), \
1792 im_id)
1793 self._lib()._err_check("send_pager()", self, err)
1794
1795
1796class BuddyInfo:
1797 """This class contains information about Buddy. Application may
1798 retrieve this information by calling Buddy.info().
1799
1800 Member documentation:
1801
1802 uri -- the Buddy URI.
1803 contact -- the Buddy Contact URI, if available.
1804 online_status -- the presence online status.
1805 online_text -- the presence online status text.
1806 activity -- the PresenceActivity
1807 subscribed -- specify whether buddy's presence status is currently
1808 being subscribed.
1809 sub_state -- SubscriptionState
1810 sub_term_reason -- The termination reason string of the last presence
1811 subscription to this buddy, if any.
1812 """
1813 uri = ""
1814 contact = ""
1815 online_status = 0
1816 online_text = ""
1817 activity = PresenceActivity.UNKNOWN
1818 subscribed = False
1819 sub_state = SubscriptionState.NULL
1820 sub_term_reason = ""
1821
1822 def __init__(self, pjsua_bi=None):
1823 if pjsua_bi:
1824 self._cvt_from_pjsua(pjsua_bi)
1825
1826 def _cvt_from_pjsua(self, inf):
1827 self.uri = inf.uri
1828 self.contact = inf.contact
1829 self.online_status = inf.status
1830 self.online_text = inf.status_text
1831 self.activity = inf.activity
1832 self.subscribed = inf.monitor_pres
1833 self.sub_state = inf.sub_state
1834 self.sub_term_reason = inf.sub_term_reason
1835
1836
1837class BuddyCallback:
1838 """This class can be used to receive notifications about Buddy's
1839 presence status change. Application needs to derive a class from
1840 this class, and register the instance with Buddy.set_callback().
1841
1842 Member documentation:
1843
1844 buddy -- the Buddy object.
1845 """
1846 buddy = None
1847
1848 def __init__(self, buddy=None):
1849 self._set_buddy(buddy)
1850
1851 def _set_buddy(self, buddy):
1852 if buddy:
1853 self.buddy = weakref.proxy(buddy)
1854 else:
1855 self.buddy = None
1856
1857 def on_state(self):
1858 """
1859 Notification that buddy's presence state has changed. Application
1860 may then retrieve the new status with Buddy.info() function.
1861 """
1862 pass
1863
1864 def on_pager(self, mime_type, body):
1865 """Notification that incoming instant message is received from
1866 this buddy.
1867
1868 Keyword arguments:
1869 mime_type -- MIME type of the instant message body
1870 body -- the instant message body
1871
1872 """
1873 pass
1874
1875 def on_pager_status(self, body, im_id, code, reason):
1876 """Notification about the delivery status of previously sent
1877 instant message.
1878
1879 Keyword arguments:
1880 body -- the message body
1881 im_id -- message ID
1882 code -- SIP status code
1883 reason -- SIP reason phrase
1884
1885 """
1886 pass
1887
1888 def on_typing(self, is_typing):
1889 """Notification that remote is typing or stop typing.
1890
1891 Keyword arguments:
1892 is_typing -- boolean to indicate whether remote is currently
1893 typing an instant message.
1894
1895 """
1896 pass
1897
1898
1899class Buddy:
1900 """A Buddy represents person or remote agent.
1901
1902 This class provides functions to subscribe to buddy's presence and
1903 to send or receive instant messages from the buddy.
1904 """
1905 _id = -1
1906 _lib = None
1907 _cb = None
1908 _obj_name = ""
1909 _acc = None
1910
1911 def __init__(self, lib, id, account, cb):
1912 self._id = id
1913 self._lib = weakref.ref(lib)
1914 self._acc = weakref.ref(account)
1915 self._obj_name = "{Buddy " + self.info().uri + "}"
1916 self.set_callback(cb)
1917 _pjsua.buddy_set_user_data(self._id, self)
1918 _Trace((self, 'created'))
1919
1920 def __del__(self):
1921 if self._id != -1:
1922 _pjsua.buddy_set_user_data(self._id, 0)
1923 _Trace((self, 'destroyed'))
1924
1925 def __str__(self):
1926 return self._obj_name
1927
1928 def info(self):
1929 """
1930 Get buddy info as BuddyInfo.
1931 """
1932 lck = self._lib().auto_lock()
1933 return BuddyInfo(_pjsua.buddy_get_info(self._id))
1934
1935 def set_callback(self, cb):
1936 """Install callback to receive notifications from this object.
1937
1938 Keyword argument:
1939 cb -- BuddyCallback instance.
1940 """
1941 if cb:
1942 self._cb = cb
1943 else:
1944 self._cb = BuddyCallback(self)
1945 self._cb._set_buddy(self)
1946
1947 def subscribe(self):
1948 """
1949 Subscribe to buddy's presence status notification.
1950 """
1951 lck = self._lib().auto_lock()
1952 err = _pjsua.buddy_subscribe_pres(self._id, True)
1953 self._lib()._err_check("subscribe()", self, err)
1954
1955 def unsubscribe(self):
1956 """
1957 Unsubscribe from buddy's presence status notification.
1958 """
1959 lck = self._lib().auto_lock()
1960 err = _pjsua.buddy_subscribe_pres(self._id, False)
1961 self._lib()._err_check("unsubscribe()", self, err)
1962
1963 def delete(self):
1964 """
1965 Remove this buddy from the buddy list.
1966 """
1967 lck = self._lib().auto_lock()
1968 if self._id != -1:
1969 _pjsua.buddy_set_user_data(self._id, 0)
1970 err = _pjsua.buddy_del(self._id)
1971 self._lib()._err_check("delete()", self, err)
1972
1973 def send_pager(self, text, im_id=0, content_type="text/plain", \
1974 hdr_list=None):
1975 """Send instant message to remote buddy.
1976
1977 Keyword arguments:
1978 text -- Instant message to be sent
1979 im_id -- Optional instant message ID to identify this
1980 instant message when delivery status callback
1981 is called.
1982 content_type -- MIME type identifying the instant message
1983 hdr_list -- Optional list of headers to be sent with the
1984 request.
1985
1986 """
1987 lck = self._lib().auto_lock()
1988 err = _pjsua.im_send(self._acc()._id, self.info().uri, \
1989 content_type, text, \
1990 Lib._create_msg_data(hdr_list), \
1991 im_id)
1992 self._lib()._err_check("send_pager()", self, err)
1993
1994 def send_typing_ind(self, is_typing=True, hdr_list=None):
1995 """Send typing indication to remote buddy.
1996
1997 Keyword argument:
1998 is_typing -- boolean to indicate wheter user is typing.
1999 hdr_list -- Optional list of headers to be sent with the
2000 request.
2001
2002 """
2003 lck = self._lib().auto_lock()
2004 err = _pjsua.im_typing(self._acc()._id, self.info().uri, \
2005 is_typing, Lib._create_msg_data(hdr_list))
2006 self._lib()._err_check("send_typing_ind()", self, err)
2007
2008
2009
2010# Sound device info
2011class SoundDeviceInfo:
2012 """This described the sound device info.
2013
2014 Member documentation:
2015 name -- device name.
2016 input_channels -- number of capture channels supported.
2017 output_channels -- number of playback channels supported.
2018 default_clock_rate -- default sampling rate.
2019 """
2020 name = ""
2021 input_channels = 0
2022 output_channels = 0
2023 default_clock_rate = 0
2024
2025 def __init__(self, sdi):
2026 self.name = sdi.name
2027 self.input_channels = sdi.input_count
2028 self.output_channels = sdi.output_count
2029 self.default_clock_rate = sdi.default_samples_per_sec
2030
2031
2032# Codec info
2033class CodecInfo:
2034 """This describes codec info.
2035
2036 Member documentation:
2037 name -- codec name
2038 priority -- codec priority (0-255)
2039 clock_rate -- clock rate
2040 channel_count -- number of channels
2041 avg_bps -- average bandwidth in bits per second
2042 frm_ptime -- base frame length in milliseconds
2043 ptime -- RTP frame length in milliseconds.
2044 pt -- payload type.
2045 vad_enabled -- specify if Voice Activity Detection is currently
2046 enabled.
2047 plc_enabled -- specify if Packet Lost Concealment is currently
2048 enabled.
2049 """
2050 name = ""
2051 priority = 0
2052 clock_rate = 0
2053 channel_count = 0
2054 avg_bps = 0
2055 frm_ptime = 0
2056 ptime = 0
2057 pt = 0
2058 vad_enabled = False
2059 plc_enabled = False
2060
2061 def __init__(self, codec_info, codec_param):
2062 self.name = codec_info.codec_id
2063 self.priority = codec_info.priority
2064 self.clock_rate = codec_param.info.clock_rate
2065 self.channel_count = codec_param.info.channel_cnt
2066 self.avg_bps = codec_param.info.avg_bps
2067 self.frm_ptime = codec_param.info.frm_ptime
2068 self.ptime = codec_param.info.frm_ptime * \
2069 codec_param.setting.frm_per_pkt
2070 self.ptime = codec_param.info.pt
2071 self.vad_enabled = codec_param.setting.vad
2072 self.plc_enabled = codec_param.setting.plc
2073
2074 def _cvt_to_pjsua(self):
2075 ci = _pjsua.Codec_Info()
2076 ci.codec_id = self.name
2077 ci.priority = self.priority
2078 return ci
2079
2080
2081# Codec parameter
2082class CodecParameter:
2083 """This specifies various parameters that can be configured for codec.
2084
2085 Member documentation:
2086
2087 ptime -- specify the outgoing RTP packet length in milliseconds.
2088 vad_enabled -- specify if VAD should be enabled.
2089 plc_enabled -- specify if PLC should be enabled.
2090 """
2091 ptime = 0
2092 vad_enabled = False
2093 plc_enabled = False
2094 _codec_param = None
2095
2096 def __init__(self, codec_param):
2097 self.ptime = codec_param.info.frm_ptime * \
2098 codec_param.setting.frm_per_pkt
2099 self.vad_enabled = codec_param.setting.vad
2100 self.plc_enabled = codec_param.setting.plc
2101 self._codec_param = codec_param
2102
2103 def _cvt_to_pjsua(self):
2104 self._codec_param.setting.frm_per_pkt = self.ptime / \
2105 self._codec_param.info.frm_ptime
2106 self._codec_param.setting.vad = self.vad_enabled
2107 self._codec_param.setting.plc = self.plc_enabled
2108 return self._codec_param
2109
2110
2111# Library mutex
2112class _LibMutex:
2113 def __init__(self, lck):
2114 self._lck = lck
2115 self._lck.acquire()
2116 #_Trace(('lock acquired',))
2117
2118 def __del__(self):
2119 try:
2120 self._lck.release()
2121 #_Trace(('lock released',))
2122 except:
2123 #_Trace(('lock release error',))
2124 pass
2125
2126
2127# PJSUA Library
2128_lib = None
2129enable_trace = False
2130
2131class Lib:
2132 """Library instance.
2133
2134 """
2135 _quit = False
2136 _has_thread = False
2137 _lock = None
2138
2139 def __init__(self):
2140 global _lib
2141 if _lib:
2142 raise Error("__init()__", None, -1,
2143 "Library instance already exist")
2144
2145 self._lock = threading.RLock()
2146 err = _pjsua.create()
2147 self._err_check("_pjsua.create()", None, err)
2148 _lib = self
2149
2150 def __del__(self):
2151 _pjsua.destroy()
2152 del self._lock
2153 _Trace(('Lib destroyed',))
2154
2155 def __str__(self):
2156 return "Lib"
2157
2158 @staticmethod
2159 def instance():
2160 """Return singleton instance of Lib.
2161 """
2162 return _lib
2163
2164 def init(self, ua_cfg=None, log_cfg=None, media_cfg=None):
2165 """
2166 Initialize pjsua with the specified configurations.
2167
2168 Keyword arguments:
2169 ua_cfg -- optional UAConfig instance
2170 log_cfg -- optional LogConfig instance
2171 media_cfg -- optional MediaConfig instance
2172
2173 """
2174 if not ua_cfg: ua_cfg = UAConfig()
2175 if not log_cfg: log_cfg = LogConfig()
2176 if not media_cfg: media_cfg = MediaConfig()
2177
2178 py_ua_cfg = ua_cfg._cvt_to_pjsua()
2179 py_ua_cfg.cb.on_call_state = _cb_on_call_state
2180 py_ua_cfg.cb.on_incoming_call = _cb_on_incoming_call
2181 py_ua_cfg.cb.on_call_media_state = _cb_on_call_media_state
2182 py_ua_cfg.cb.on_dtmf_digit = _cb_on_dtmf_digit
2183 py_ua_cfg.cb.on_call_transfer_request = _cb_on_call_transfer_request
2184 py_ua_cfg.cb.on_call_transfer_status = _cb_on_call_transfer_status
2185 py_ua_cfg.cb.on_call_replace_request = _cb_on_call_replace_request
2186 py_ua_cfg.cb.on_call_replaced = _cb_on_call_replaced
2187 py_ua_cfg.cb.on_reg_state = _cb_on_reg_state
2188 py_ua_cfg.cb.on_incoming_subscribe = _cb_on_incoming_subscribe
2189 py_ua_cfg.cb.on_buddy_state = _cb_on_buddy_state
2190 py_ua_cfg.cb.on_pager = _cb_on_pager
2191 py_ua_cfg.cb.on_pager_status = _cb_on_pager_status
2192 py_ua_cfg.cb.on_typing = _cb_on_typing
2193 py_ua_cfg.cb.on_mwi_info = _cb_on_mwi_info;
2194
2195 err = _pjsua.init(py_ua_cfg, log_cfg._cvt_to_pjsua(),
2196 media_cfg._cvt_to_pjsua())
2197 self._err_check("init()", self, err)
2198
2199 def destroy(self):
2200 """Destroy the library, and pjsua."""
2201 global _lib
2202 if self._has_thread:
2203 self._quit = 1
2204 loop = 0
2205 while self._quit != 2 and loop < 400:
2206 self.handle_events(5)
2207 loop = loop + 1
2208 time.sleep(0.050)
2209 _pjsua.destroy()
2210 _lib = None
2211
2212 def start(self, with_thread=True):
2213 """Start the library.
2214
2215 Keyword argument:
2216 with_thread -- specify whether the module should create worker
2217 thread.
2218
2219 """
2220 lck = self.auto_lock()
2221 err = _pjsua.start()
2222 self._err_check("start()", self, err)
2223 self._has_thread = with_thread
2224 if self._has_thread:
2225 thread.start_new(_worker_thread_main, (0,))
2226
2227 def handle_events(self, timeout=50):
2228 """Poll the events from underlying pjsua library.
2229
2230 Application must poll the stack periodically if worker thread
2231 is disable when starting the library.
2232
2233 Keyword argument:
2234 timeout -- in milliseconds.
2235
2236 """
2237 lck = self.auto_lock()
2238 return _pjsua.handle_events(timeout)
2239
2240 def thread_register(self, name):
2241 """Register external threads (threads that are not created by PJSIP,
2242 such as threads that are created by Python API) to PJSIP.
2243
2244 The call must be made from the new thread before calling any pjlib
2245 functions.
2246
2247 Keyword arguments:
2248 name -- Non descriptive name for the thread
2249 """
2250 dummy = 1
2251 err = _pjsua.thread_register(name, dummy)
2252 self._err_check("thread_register()", self, err)
2253
2254 def verify_sip_url(self, sip_url):
2255 """Verify that the specified string is a valid URI.
2256
2257 Keyword argument:
2258 sip_url -- the URL string.
2259
2260 Return:
2261 0 is the the URI is valid, otherwise the appropriate error
2262 code is returned.
2263
2264 """
2265 lck = self.auto_lock()
2266 return _pjsua.verify_sip_url(sip_url)
2267
2268 def create_transport(self, type, cfg=None):
2269 """Create SIP transport instance of the specified type.
2270
2271 Keyword arguments:
2272 type -- transport type from TransportType constant.
2273 cfg -- TransportConfig instance
2274
2275 Return:
2276 Transport object
2277
2278 """
2279 lck = self.auto_lock()
2280 if not cfg: cfg=TransportConfig()
2281 err, tp_id = _pjsua.transport_create(type, cfg._cvt_to_pjsua())
2282 self._err_check("create_transport()", self, err)
2283 return Transport(self, tp_id)
2284
2285 def create_account(self, acc_config, set_default=True, cb=None):
2286 """
2287 Create a new local pjsua account using the specified configuration.
2288
2289 Keyword arguments:
2290 acc_config -- AccountConfig
2291 set_default -- boolean to specify whether to use this as the
2292 default account.
2293 cb -- AccountCallback instance.
2294
2295 Return:
2296 Account instance
2297
2298 """
2299 lck = self.auto_lock()
2300 err, acc_id = _pjsua.acc_add(acc_config._cvt_to_pjsua(), set_default)
2301 self._err_check("create_account()", self, err)
2302 return Account(self, acc_id, cb)
2303
2304 def create_account_for_transport(self, transport, set_default=True,
2305 cb=None):
2306 """Create a new local pjsua transport for the specified transport.
2307
2308 Keyword arguments:
2309 transport -- the Transport instance.
2310 set_default -- boolean to specify whether to use this as the
2311 default account.
2312 cb -- AccountCallback instance.
2313
2314 Return:
2315 Account instance
2316
2317 """
2318 lck = self.auto_lock()
2319 err, acc_id = _pjsua.acc_add_local(transport._id, set_default)
2320 self._err_check("create_account_for_transport()", self, err)
2321 return Account(self, acc_id, cb)
2322
2323 def hangup_all(self):
2324 """Hangup all calls.
2325
2326 """
2327 lck = self.auto_lock()
2328 _pjsua.call_hangup_all()
2329
2330 # Sound device API
2331
2332 def enum_snd_dev(self):
2333 """Enumerate sound devices in the system.
2334
2335 Return:
2336 list of SoundDeviceInfo. The index of the element specifies
2337 the device ID for the device.
2338 """
2339 lck = self.auto_lock()
2340 sdi_list = _pjsua.enum_snd_devs()
2341 info = []
2342 for sdi in sdi_list:
2343 info.append(SoundDeviceInfo(sdi))
2344 return info
2345
2346 def get_snd_dev(self):
2347 """Get the device IDs of current sound devices used by pjsua.
2348
2349 Return:
2350 (capture_dev_id, playback_dev_id) tuple
2351 """
2352 lck = self.auto_lock()
2353 return _pjsua.get_snd_dev()
2354
2355 def set_snd_dev(self, capture_dev, playback_dev):
2356 """Change the current sound devices.
2357
2358 Keyword arguments:
2359 capture_dev -- the device ID of capture device to be used
2360 playback_dev -- the device ID of playback device to be used.
2361
2362 """
2363 lck = self.auto_lock()
2364 err = _pjsua.set_snd_dev(capture_dev, playback_dev)
2365 self._err_check("set_current_sound_devices()", self, err)
2366
2367 def set_null_snd_dev(self):
2368 """Disable the sound devices. This is useful if the system
2369 does not have sound device installed.
2370
2371 """
2372 lck = self.auto_lock()
2373 err = _pjsua.set_null_snd_dev()
2374 self._err_check("set_null_snd_dev()", self, err)
2375
2376
2377 # Conference bridge
2378
2379 def conf_get_max_ports(self):
2380 """Get the conference bridge capacity.
2381
2382 Return:
2383 conference bridge capacity.
2384
2385 """
2386 lck = self.auto_lock()
2387 return _pjsua.conf_get_max_ports()
2388
2389 def conf_connect(self, src_slot, dst_slot):
2390 """Establish unidirectional media flow from souce to sink.
2391
2392 One source may transmit to multiple destinations/sink. And if
2393 multiple sources are transmitting to the same sink, the media
2394 will be mixed together. Source and sink may refer to the same ID,
2395 effectively looping the media.
2396
2397 If bidirectional media flow is desired, application needs to call
2398 this function twice, with the second one having the arguments
2399 reversed.
2400
2401 Keyword arguments:
2402 src_slot -- integer to identify the conference slot number of
2403 the source/transmitter.
2404 dst_slot -- integer to identify the conference slot number of
2405 the destination/receiver.
2406
2407 """
2408 lck = self.auto_lock()
2409 err = _pjsua.conf_connect(src_slot, dst_slot)
2410 self._err_check("conf_connect()", self, err)
2411
2412 def conf_disconnect(self, src_slot, dst_slot):
2413 """Disconnect media flow from the source to destination port.
2414
2415 Keyword arguments:
2416 src_slot -- integer to identify the conference slot number of
2417 the source/transmitter.
2418 dst_slot -- integer to identify the conference slot number of
2419 the destination/receiver.
2420
2421 """
2422 lck = self.auto_lock()
2423 err = _pjsua.conf_disconnect(src_slot, dst_slot)
2424 self._err_check("conf_disconnect()", self, err)
2425
2426 def conf_set_tx_level(self, slot, level):
2427 """Adjust the signal level to be transmitted from the bridge to
2428 the specified port by making it louder or quieter.
2429
2430 Keyword arguments:
2431 slot -- integer to identify the conference slot number.
2432 level -- Signal level adjustment. Value 1.0 means no level
2433 adjustment, while value 0 means to mute the port.
2434 """
2435 lck = self.auto_lock()
2436 err = _pjsua.conf_set_tx_level(slot, level)
2437 self._err_check("conf_set_tx_level()", self, err)
2438
2439 def conf_set_rx_level(self, slot, level):
2440 """Adjust the signal level to be received from the specified port
2441 (to the bridge) by making it louder or quieter.
2442
2443 Keyword arguments:
2444 slot -- integer to identify the conference slot number.
2445 level -- Signal level adjustment. Value 1.0 means no level
2446 adjustment, while value 0 means to mute the port.
2447 """
2448 lck = self.auto_lock()
2449 err = _pjsua.conf_set_rx_level(slot, level)
2450 self._err_check("conf_set_rx_level()", self, err)
2451
2452 def conf_get_signal_level(self, slot):
2453 """Get last signal level transmitted to or received from the
2454 specified port. The signal levels are float values from 0.0 to 1.0,
2455 with 0.0 indicates no signal, and 1.0 indicates the loudest signal
2456 level.
2457
2458 Keyword arguments:
2459 slot -- integer to identify the conference slot number.
2460
2461 Return value:
2462 (tx_level, rx_level) tuple.
2463 """
2464 lck = self.auto_lock()
2465 err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot)
2466 self._err_check("conf_get_signal_level()", self, err)
2467 return (tx_level, rx_level)
2468
2469
2470
2471 # Codecs API
2472
2473 def enum_codecs(self):
2474 """Return list of codecs supported by pjsua.
2475
2476 Return:
2477 list of CodecInfo
2478
2479 """
2480 lck = self.auto_lock()
2481 ci_list = _pjsua.enum_codecs()
2482 codec_info = []
2483 for ci in ci_list:
2484 cp = _pjsua.codec_get_param(ci.codec_id)
2485 if cp:
2486 codec_info.append(CodecInfo(ci, cp))
2487 return codec_info
2488
2489 def set_codec_priority(self, name, priority):
2490 """Change the codec priority.
2491
2492 Keyword arguments:
2493 name -- Codec name
2494 priority -- Codec priority, which range is 0-255.
2495
2496 """
2497 lck = self.auto_lock()
2498 err = _pjsua.codec_set_priority(name, priority)
2499 self._err_check("set_codec_priority()", self, err)
2500
2501 def get_codec_parameter(self, name):
2502 """Get codec parameter for the specified codec.
2503
2504 Keyword arguments:
2505 name -- codec name.
2506
2507 """
2508 lck = self.auto_lock()
2509 cp = _pjsua.codec_get_param(name)
2510 if not cp:
2511 self._err_check("get_codec_parameter()", self, -1,
2512 "Invalid codec name")
2513 return CodecParameter(cp)
2514
2515 def set_codec_parameter(self, name, param):
2516 """Modify codec parameter for the specified codec.
2517
2518 Keyword arguments:
2519 name -- codec name
2520 param -- codec parameter.
2521
2522 """
2523 lck = self.auto_lock()
2524 err = _pjsua.codec_set_param(name, param._cvt_to_pjsua())
2525 self._err_check("set_codec_parameter()", self, err)
2526
2527 # WAV playback and recording
2528
2529 def create_player(self, filename, loop=False):
2530 """Create WAV file player.
2531
2532 Keyword arguments
2533 filename -- WAV file name
2534 loop -- boolean to specify whether playback should
2535 automatically restart upon EOF
2536 Return:
2537 WAV player ID
2538
2539 """
2540 lck = self.auto_lock()
2541 opt = 0
2542 if not loop:
2543 opt = opt + 1
2544 err, player_id = _pjsua.player_create(filename, opt)
2545 self._err_check("create_player()", self, err)
2546 return player_id
2547
2548 def player_get_slot(self, player_id):
2549 """Get the conference port ID for the specified player.
2550
2551 Keyword arguments:
2552 player_id -- the WAV player ID
2553
2554 Return:
2555 Conference slot number for the player
2556
2557 """
2558 lck = self.auto_lock()
2559 slot = _pjsua.player_get_conf_port(player_id)
2560 if slot < 0:
2561 self._err_check("player_get_slot()", self, -1,
2562 "Invalid player id")
2563 return slot
2564
2565 def player_set_pos(self, player_id, pos):
2566 """Set WAV playback position.
2567
2568 Keyword arguments:
2569 player_id -- WAV player ID
2570 pos -- playback position, in samples
2571
2572 """
2573 lck = self.auto_lock()
2574 err = _pjsua.player_set_pos(player_id, pos)
2575 self._err_check("player_set_pos()", self, err)
2576
2577 def player_destroy(self, player_id):
2578 """Destroy the WAV player.
2579
2580 Keyword arguments:
2581 player_id -- the WAV player ID.
2582
2583 """
2584 lck = self.auto_lock()
2585 err = _pjsua.player_destroy(player_id)
2586 self._err_check("player_destroy()", self, err)
2587
2588 def create_playlist(self, filelist, label="playlist", loop=True):
2589 """Create WAV playlist.
2590
2591 Keyword arguments:
2592 filelist -- List of WAV file names.
2593 label -- Optional name to be assigned to the playlist
2594 object (useful for logging)
2595 loop -- boolean to specify whether playback should
2596 automatically restart upon EOF
2597
2598 Return:
2599 playlist_id
2600 """
2601 lck = self.auto_lock()
2602 opt = 0
2603 if not loop:
2604 opt = opt + 1
2605 err, playlist_id = _pjsua.playlist_create(label, filelist, opt)
2606 self._err_check("create_playlist()", self, err)
2607 return playlist_id
2608
2609 def playlist_get_slot(self, playlist_id):
2610 """Get the conference port ID for the specified playlist.
2611
2612 Keyword arguments:
2613 playlist_id -- the WAV playlist ID
2614
2615 Return:
2616 Conference slot number for the playlist
2617
2618 """
2619 lck = self.auto_lock()
2620 slot = _pjsua.player_get_conf_port(playlist_id)
2621 if slot < 0:
2622 self._err_check("playlist_get_slot()", self, -1,
2623 "Invalid playlist id")
2624 return slot
2625
2626 def playlist_destroy(self, playlist_id):
2627 """Destroy the WAV playlist.
2628
2629 Keyword arguments:
2630 playlist_id -- the WAV playlist ID.
2631
2632 """
2633 lck = self.auto_lock()
2634 err = _pjsua.player_destroy(playlist_id)
2635 self._err_check("playlist_destroy()", self, err)
2636
2637 def create_recorder(self, filename):
2638 """Create WAV file recorder.
2639
2640 Keyword arguments
2641 filename -- WAV file name
2642
2643 Return:
2644 WAV recorder ID
2645
2646 """
2647 lck = self.auto_lock()
2648 err, rec_id = _pjsua.recorder_create(filename, 0, None, -1, 0)
2649 self._err_check("create_recorder()", self, err)
2650 return rec_id
2651
2652 def recorder_get_slot(self, rec_id):
2653 """Get the conference port ID for the specified recorder.
2654
2655 Keyword arguments:
2656 rec_id -- the WAV recorder ID
2657
2658 Return:
2659 Conference slot number for the recorder
2660
2661 """
2662 lck = self.auto_lock()
2663 slot = _pjsua.recorder_get_conf_port(rec_id)
2664 if slot < 1:
2665 self._err_check("recorder_get_slot()", self, -1,
2666 "Invalid recorder id")
2667 return slot
2668
2669 def recorder_destroy(self, rec_id):
2670 """Destroy the WAV recorder.
2671
2672 Keyword arguments:
2673 rec_id -- the WAV recorder ID.
2674
2675 """
2676 lck = self.auto_lock()
2677 err = _pjsua.recorder_destroy(rec_id)
2678 self._err_check("recorder_destroy()", self, err)
2679
2680
2681 # Internal functions
2682
2683 @staticmethod
2684 def strerror(err):
2685 return _pjsua.strerror(err)
2686
2687 def _err_check(self, op_name, obj, err_code, err_msg=""):
2688 if err_code != 0:
2689 raise Error(op_name, obj, err_code, err_msg)
2690
2691 @staticmethod
2692 def _create_msg_data(hdr_list):
2693 if not hdr_list:
2694 return None
2695 msg_data = _pjsua.Msg_Data()
2696 msg_data.hdr_list = hdr_list
2697 return msg_data
2698
2699 def auto_lock(self):
2700 return _LibMutex(self._lock)
2701
2702 # Internal dictionary manipulation for calls, accounts, and buddies
2703
2704 def _lookup_call(self, call_id):
2705 return _pjsua.call_get_user_data(call_id)
2706
2707 def _lookup_account(self, acc_id):
2708 return _pjsua.acc_get_user_data(acc_id)
2709
2710 def _lookup_buddy(self, buddy_id, uri=None):
2711 if buddy_id != -1:
2712 buddy = _pjsua.buddy_get_user_data(buddy_id)
2713 elif uri:
2714 buddy_id = _pjsua.buddy_find(uri)
2715 if buddy_id != -1:
2716 buddy = _pjsua.buddy_get_user_data(buddy_id)
2717 else:
2718 buddy = None
2719 else:
2720 buddy = None
2721
2722 return buddy
2723
2724 # Account allbacks
2725
2726 def _cb_on_reg_state(self, acc_id):
2727 acc = self._lookup_account(acc_id)
2728 if acc:
2729 acc._cb.on_reg_state()
2730
2731 def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri,
2732 contact_uri, pres_obj):
2733 acc = self._lookup_account(acc_id)
2734 if acc:
2735 buddy = self._lookup_buddy(buddy_id)
2736 return acc._cb.on_incoming_subscribe(buddy, from_uri, contact_uri,
2737 pres_obj)
2738 else:
2739 return (404, None)
2740
2741 def _cb_on_incoming_call(self, acc_id, call_id, rdata):
2742 acc = self._lookup_account(acc_id)
2743 if acc:
2744 acc._cb.on_incoming_call( Call(self, call_id) )
2745 else:
2746 _pjsua.call_hangup(call_id, 603, None, None)
2747
2748 # Call callbacks
2749
2750 def _cb_on_call_state(self, call_id):
2751 call = self._lookup_call(call_id)
2752 if call:
2753 if call._id == -1:
2754 call.attach_to_id(call_id)
2755 done = (call.info().state == CallState.DISCONNECTED)
2756 call._cb.on_state()
2757 if done:
2758 _pjsua.call_set_user_data(call_id, 0)
2759 else:
2760 pass
2761
2762 def _cb_on_call_media_state(self, call_id):
2763 call = self._lookup_call(call_id)
2764 if call:
2765 call._cb.on_media_state()
2766
2767 def _cb_on_dtmf_digit(self, call_id, digits):
2768 call = self._lookup_call(call_id)
2769 if call:
2770 call._cb.on_dtmf_digit(digits)
2771
2772 def _cb_on_call_transfer_request(self, call_id, dst, code):
2773 call = self._lookup_call(call_id)
2774 if call:
2775 return call._cb.on_transfer_request(dst, code)
2776 else:
2777 return 603
2778
2779 def _cb_on_call_transfer_status(self, call_id, code, text, final, cont):
2780 call = self._lookup_call(call_id)
2781 if call:
2782 return call._cb.on_transfer_status(code, text, final, cont)
2783 else:
2784 return cont
2785
2786 def _cb_on_call_replace_request(self, call_id, rdata, code, reason):
2787 call = self._lookup_call(call_id)
2788 if call:
2789 return call._cb.on_replace_request(code, reason)
2790 else:
2791 return code, reason
2792
2793 def _cb_on_call_replaced(self, old_call_id, new_call_id):
2794 old_call = self._lookup_call(old_call_id)
2795 new_call = self._lookup_call(new_call_id)
2796 if old_call and new_call:
2797 old_call._cb.on_replaced(new_call)
2798
2799 def _cb_on_pager(self, call_id, from_uri, to_uri, contact, mime_type,
2800 body, acc_id):
2801 call = None
2802 if call_id != -1:
2803 call = self._lookup_call(call_id)
2804 if call:
2805 call._cb.on_pager(mime_type, body)
2806 else:
2807 acc = self._lookup_account(acc_id)
2808 buddy = self._lookup_buddy(-1, from_uri)
2809 if buddy:
2810 buddy._cb.on_pager(mime_type, body)
2811 else:
2812 acc._cb.on_pager(from_uri, contact, mime_type, body)
2813
2814 def _cb_on_pager_status(self, call_id, to_uri, body, user_data,
2815 code, reason, acc_id):
2816 call = None
2817 if call_id != -1:
2818 call = self._lookup_call(call_id)
2819 if call:
2820 call._cb.on_pager_status(body, user_data, code, reason)
2821 else:
2822 acc = self._lookup_account(acc_id)
2823 buddy = self._lookup_buddy(-1, to_uri)
2824 if buddy:
2825 buddy._cb.on_pager_status(body, user_data, code, reason)
2826 else:
2827 acc._cb.on_pager_status(to_uri, body, user_data, code, reason)
2828
2829 def _cb_on_typing(self, call_id, from_uri, to_uri, contact, is_typing,
2830 acc_id):
2831 call = None
2832 if call_id != -1:
2833 call = self._lookup_call(call_id)
2834 if call:
2835 call._cb.on_typing(is_typing)
2836 else:
2837 acc = self._lookup_account(acc_id)
2838 buddy = self._lookup_buddy(-1, from_uri)
2839 if buddy:
2840 buddy._cb.on_typing(is_typing)
2841 else:
2842 acc._cb.on_typing(from_uri, contact, is_typing)
2843
2844 def _cb_on_mwi_info(self, acc_id, body):
2845 acc = self._lookup_account(acc_id)
2846 if acc:
2847 return acc._cb.on_mwi_info(body)
2848
2849 def _cb_on_buddy_state(self, buddy_id):
2850 buddy = self._lookup_buddy(buddy_id)
2851 if buddy:
2852 buddy._cb.on_state()
2853
2854#
2855# Internal
2856#
2857
2858def _cb_on_call_state(call_id, e):
2859 _lib._cb_on_call_state(call_id)
2860
2861def _cb_on_incoming_call(acc_id, call_id, rdata):
2862 _lib._cb_on_incoming_call(acc_id, call_id, rdata)
2863
2864def _cb_on_call_media_state(call_id):
2865 _lib._cb_on_call_media_state(call_id)
2866
2867def _cb_on_dtmf_digit(call_id, digits):
2868 _lib._cb_on_dtmf_digit(call_id, digits)
2869
2870def _cb_on_call_transfer_request(call_id, dst, code):
2871 return _lib._cb_on_call_transfer_request(call_id, dst, code)
2872
2873def _cb_on_call_transfer_status(call_id, code, reason, final, cont):
2874 return _lib._cb_on_call_transfer_status(call_id, code, reason,
2875 final, cont)
2876def _cb_on_call_replace_request(call_id, rdata, code, reason):
2877 return _lib._cb_on_call_replace_request(call_id, rdata, code, reason)
2878
2879def _cb_on_call_replaced(old_call_id, new_call_id):
2880 _lib._cb_on_call_replaced(old_call_id, new_call_id)
2881
2882def _cb_on_reg_state(acc_id):
2883 _lib._cb_on_reg_state(acc_id)
2884
2885def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, contact_uri, pres):
2886 return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri,
2887 contact_uri, pres)
2888
2889def _cb_on_buddy_state(buddy_id):
2890 _lib._cb_on_buddy_state(buddy_id)
2891
2892def _cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id):
2893 _lib._cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id)
2894
2895def _cb_on_pager_status(call_id, to, body, user_data, status, reason, acc_id):
2896 _lib._cb_on_pager_status(call_id, to, body, user_data,
2897 status, reason, acc_id)
2898
2899def _cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id):
2900 _lib._cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id)
2901
2902def _cb_on_mwi_info(acc_id, body):
2903 _lib._cb_on_mwi_info(acc_id, body)
2904
2905# Worker thread
2906def _worker_thread_main(arg):
2907 global _lib
2908 _Trace(('worker thread started..',))
2909 thread_desc = 0;
2910 err = _pjsua.thread_register("python worker", thread_desc)
2911 _lib._err_check("thread_register()", _lib, err)
2912 while _lib and _lib._quit == 0:
2913 _lib.handle_events(1)
2914 time.sleep(0.050)
2915 if _lib:
2916 _lib._quit = 2
2917 _Trace(('worker thread exited..',))
2918
2919def _Trace(args):
2920 global enable_trace
2921 if enable_trace:
2922 print "** ",
2923 for arg in args:
2924 print arg,
2925 print " **"
2926