blob: 6dc5efe1a803ac5424c959e510accaa99e7f1238 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001# $Id$
2#
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".
477 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.
502 """
503 port = 0
504 bound_addr = ""
505 public_addr = ""
506
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
514
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
521 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
531 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
536 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
542 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").
756 rtp_transport_cfg -- the rtp-transport-configuration that is usede, when
757 a rtp-connection is being established.
758 """
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
777 rtp_transport_cfg = None
778
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)
803 self.rtp_transport_cfg = TransportConfig()
804
805 def build_config(self, domain, username, password, display="",
806 registrar="", proxy="", rtp_transport_cfg = None):
807 """
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))
840
841 if (rtp_transport_cfg is not None):
842 self.rtp_transport_cfg = rtp_transport_cfg
843 else:
844 self.rtp_transport_cfg = TransportConfig()
845
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
868 if (self.rtp_transport_cfg is not None):
869 self.rtp_transport_cfg._cvt_from_pjsua(cfg.rtp_transport_cfg)
870
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)
890
891 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
899
900 if (self.rtp_transport_cfg is not None):
901 cfg.rtp_transport_cfg = self.rtp_transport_cfg._cvt_to_pjsua()
902
903 return cfg
904
905
906# Account information
907class AccountInfo:
908 """This describes Account info. Application retrives account info
909 with Account.info().
910
911 Member documentation:
912
913 is_default -- True if this is the default account.
914 uri -- the account URI.
915 reg_active -- True if registration is active for this account.
916 reg_expires -- contains the current registration expiration value,
917 in seconds.
918 reg_status -- the registration status. If the value is less than
919 700, it specifies SIP status code. Value greater than
920 this specifies the error code.
921 reg_reason -- contains the registration status text (e.g. the
922 error message).
923 online_status -- the account's presence online status, True if it's
924 publishing itself as online.
925 online_text -- the account's presence status text.
926
927 """
928 is_default = False
929 uri = ""
930 reg_active = False
931 reg_expires = -1
932 reg_status = 0
933 reg_reason = ""
934 online_status = False
935 online_text = ""
936
937 def __init__(self, ai):
938 self.is_default = ai.is_default
939 self.uri = ai.acc_uri
940 self.reg_active = ai.has_registration
941 self.reg_expires = ai.expires
942 self.reg_status = ai.status
943 self.reg_reason = ai.status_text
944 self.online_status = ai.online_status
945 self.online_text = ai.online_status_text
946
947# Account callback
948class AccountCallback:
949 """Class to receive notifications on account's events.
950
951 Derive a class from this class and register it to the Account object
952 using Account.set_callback() to start receiving events from the Account
953 object.
954
955 Member documentation:
956
957 account -- the Account object.
958
959 """
960 account = None
961
962 def __init__(self, account=None):
963 self._set_account(account)
964
965 def __del__(self):
966 pass
967
968 def _set_account(self, account):
969 if account:
970 self.account = weakref.proxy(account)
971 else:
972 self.account = None
973
974 def on_reg_state(self):
975 """Notification that the registration status has changed.
976 """
977 pass
978
979 def on_incoming_call(self, call):
980 """Notification about incoming call.
981
982 Application should implement one of on_incoming_call() or
983 on_incoming_call2(), otherwise, the default behavior is to
984 reject the call with default status code. Note that if both are
985 implemented, only on_incoming_call2() will be called.
986
987 Keyword arguments:
988 call -- the new incoming call
989 """
990 call.hangup()
991
992 def on_incoming_call2(self, call, rdata):
993 """Notification about incoming call, with received SIP message info.
994
995 Application should implement one of on_incoming_call() or
996 on_incoming_call2(), otherwise, the default behavior is to
997 reject the call with default status code. Note that if both are
998 implemented, only on_incoming_call2() will be called.
999
1000 Keyword arguments:
1001 call -- the new incoming call
1002 rdata -- the received message
1003 """
1004 call.hangup()
1005
1006 def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres_obj):
1007 """Notification when incoming SUBSCRIBE request is received.
1008
1009 Application may use this callback to authorize the incoming
1010 subscribe request (e.g. ask user permission if the request
1011 should be granted)
1012
1013 Keyword arguments:
1014 buddy -- The buddy object, if buddy is found. Otherwise
1015 the value is None.
1016 from_uri -- The URI string of the sender.
1017 pres_obj -- Opaque presence subscription object, which is
1018 needed by Account.pres_notify()
1019
1020 Return:
1021 Tuple (code, reason), where:
1022 code: The status code. If code is >= 300, the
1023 request is rejected. If code is 200, the
1024 request is accepted and NOTIFY will be sent
1025 automatically. If code is 202, application
1026 must accept or reject the request later with
1027 Account.press_notify().
1028 reason: Optional reason phrase, or None to use the
1029 default reasoh phrase for the status code.
1030 """
1031 return (200, None)
1032
1033 def on_pager(self, from_uri, contact, mime_type, body):
1034 """
1035 Notification that incoming instant message is received on
1036 this account.
1037
1038 Keyword arguments:
1039 from_uri -- sender's URI
1040 contact -- sender's Contact URI
1041 mime_type -- MIME type of the instant message body
1042 body -- the instant message body
1043
1044 """
1045 pass
1046
1047 def on_pager_status(self, to_uri, body, im_id, code, reason):
1048 """
1049 Notification about the delivery status of previously sent
1050 instant message.
1051
1052 Keyword arguments:
1053 to_uri -- the destination URI of the message
1054 body -- the message body
1055 im_id -- message ID
1056 code -- SIP status code
1057 reason -- SIP reason phrase
1058
1059 """
1060 pass
1061
1062 def on_typing(self, from_uri, contact, is_typing):
1063 """
1064 Notification that remote is typing or stop typing.
1065
1066 Keyword arguments:
1067 buddy -- Buddy object for the sender, if found. Otherwise
1068 this will be None
1069 from_uri -- sender's URI of the indication
1070 contact -- sender's contact URI
1071 is_typing -- boolean to indicate whether remote is currently
1072 typing an instant message.
1073
1074 """
1075 pass
1076
1077 def on_mwi_info(self, body):
1078 """
1079 Notification about change in Message Summary / Message Waiting
1080 Indication (RFC 3842) status. MWI subscription must be enabled
1081 in the account config to receive this notification.
1082
1083 Keyword arguments:
1084 body -- String containing message body as received in the
1085 NOTIFY request.
1086
1087 """
1088 pass
1089
1090
1091
1092class Account:
1093 """This describes SIP account class.
1094
1095 PJSUA accounts provide identity (or identities) of the user who is
1096 currently using the application. In SIP terms, the identity is used
1097 as the From header in outgoing requests.
1098
1099 Account may or may not have client registration associated with it.
1100 An account is also associated with route set and some authentication
1101 credentials, which are used when sending SIP request messages using
1102 the account. An account also has presence's online status, which
1103 will be reported to remote peer when they subscribe to the account's
1104 presence, or which is published to a presence server if presence
1105 publication is enabled for the account.
1106
1107 Account is created with Lib.create_account(). At least one account
1108 MUST be created. If no user association is required, application can
1109 create a userless account by calling Lib.create_account_for_transport().
1110 A userless account identifies local endpoint instead of a particular
1111 user, and it correspond with a particular transport instance.
1112
1113 Also one account must be set as the default account, which is used as
1114 the account to use when PJSUA fails to match a request with any other
1115 accounts.
1116
1117 """
1118 _id = -1
1119 _lib = None
1120 _cb = AccountCallback(None)
1121 _obj_name = ""
1122
1123 def __init__(self, lib, id, cb=None):
1124 """Construct this class. This is normally called by Lib class and
1125 not by application.
1126
1127 Keyword arguments:
1128 lib -- the Lib instance.
1129 id -- the pjsua account ID.
1130 cb -- AccountCallback instance to receive events from this Account.
1131 If callback is not specified here, it must be set later
1132 using set_callback().
1133 """
1134 self._id = id
1135 self._lib = weakref.ref(lib)
1136 self._obj_name = "{Account " + self.info().uri + "}"
1137 self.set_callback(cb)
1138 _pjsua.acc_set_user_data(self._id, self)
1139 _Trace((self, 'created'))
1140
1141 def __del__(self):
1142 if self._id != -1:
1143 _pjsua.acc_set_user_data(self._id, 0)
1144 _Trace((self, 'destroyed'))
1145
1146 def __str__(self):
1147 return self._obj_name
1148
1149 def info(self):
1150 """Retrieve AccountInfo for this account.
1151 """
1152 lck = self._lib().auto_lock()
1153 ai = _pjsua.acc_get_info(self._id)
1154 if ai==None:
1155 self._lib()._err_check("info()", self, -1, "Invalid account")
1156 return AccountInfo(ai)
1157
1158 def is_valid(self):
1159 """
1160 Check if this account is still valid.
1161
1162 """
1163 lck = self._lib().auto_lock()
1164 return _pjsua.acc_is_valid(self._id)
1165
1166 def set_callback(self, cb):
1167 """Register callback to receive notifications from this object.
1168
1169 Keyword argument:
1170 cb -- AccountCallback instance.
1171
1172 """
1173 if cb:
1174 self._cb = cb
1175 else:
1176 self._cb = AccountCallback(self)
1177 self._cb._set_account(self)
1178
1179 def set_default(self):
1180 """ Set this account as default account to send outgoing requests
1181 and as the account to receive incoming requests when more exact
1182 matching criteria fails.
1183 """
1184 lck = self._lib().auto_lock()
1185 err = _pjsua.acc_set_default(self._id)
1186 self._lib()._err_check("set_default()", self, err)
1187
1188 def is_default(self):
1189 """ Check if this account is the default account.
1190
1191 """
1192 lck = self._lib().auto_lock()
1193 def_id = _pjsua.acc_get_default()
1194 return self.is_valid() and def_id==self._id
1195
1196 def delete(self):
1197 """ Delete this account.
1198
1199 """
1200 lck = self._lib().auto_lock()
1201 err = _pjsua.acc_set_user_data(self._id, 0)
1202 self._lib()._err_check("delete()", self, err)
1203 err = _pjsua.acc_del(self._id)
1204 self._lib()._err_check("delete()", self, err)
1205 self._id = -1
1206
1207 def set_basic_status(self, is_online):
1208 """ Set basic presence status of this account.
1209
1210 Keyword argument:
1211 is_online -- boolean to indicate basic presence availability.
1212
1213 """
1214 lck = self._lib().auto_lock()
1215 err = _pjsua.acc_set_online_status(self._id, is_online)
1216 self._lib()._err_check("set_basic_status()", self, err)
1217
1218 def set_presence_status(self, is_online,
1219 activity=PresenceActivity.UNKNOWN,
1220 pres_text="", rpid_id=""):
1221 """ Set presence status of this account.
1222
1223 Keyword arguments:
1224 is_online -- boolean to indicate basic presence availability
1225 activity -- value from PresenceActivity
1226 pres_text -- optional string to convey additional information about
1227 the activity (such as "On the phone")
1228 rpid_id -- optional string to be placed as RPID ID.
1229
1230 """
1231 lck = self._lib().auto_lock()
1232 err = _pjsua.acc_set_online_status2(self._id, is_online, activity,
1233 pres_text, rpid_id)
1234 self._lib()._err_check("set_presence_status()", self, err)
1235
1236 def set_registration(self, renew):
1237 """Manually renew registration or unregister from the server.
1238
1239 Keyword argument:
1240 renew -- boolean to indicate whether registration is renewed.
1241 Setting this value for False will trigger unregistration.
1242
1243 """
1244 lck = self._lib().auto_lock()
1245 err = _pjsua.acc_set_registration(self._id, renew)
1246 self._lib()._err_check("set_registration()", self, err)
1247
1248 def set_transport(self, transport):
1249 """Set this account to only use the specified transport to send
1250 outgoing requests.
1251
1252 Keyword argument:
1253 transport -- Transport object.
1254
1255 """
1256 lck = self._lib().auto_lock()
1257 err = _pjsua.acc_set_transport(self._id, transport._id)
1258 self._lib()._err_check("set_transport()", self, err)
1259
1260 def make_call(self, dst_uri, cb=None, hdr_list=None):
1261 """Make outgoing call to the specified URI.
1262
1263 Keyword arguments:
1264 dst_uri -- Destination SIP URI.
1265 cb -- CallCallback instance to be installed to the newly
1266 created Call object. If this CallCallback is not
1267 specified (i.e. None is given), it must be installed
1268 later using call.set_callback().
1269 hdr_list -- Optional list of headers to be sent with outgoing
1270 INVITE
1271
1272 Return:
1273 Call instance.
1274 """
1275 lck = self._lib().auto_lock()
1276 call = Call(self._lib(), -1, cb)
1277 err, cid = _pjsua.call_make_call(self._id, dst_uri, 0,
1278 call, Lib._create_msg_data(hdr_list))
1279 self._lib()._err_check("make_call()", self, err)
1280 call.attach_to_id(cid)
1281 return call
1282
1283 def add_buddy(self, uri, cb=None):
1284 """Add new buddy.
1285
1286 Keyword argument:
1287 uri -- SIP URI of the buddy
1288 cb -- BuddyCallback instance to be installed to the newly
1289 created Buddy object. If this callback is not specified
1290 (i.e. None is given), it must be installed later using
1291 buddy.set_callback().
1292
1293 Return:
1294 Buddy object
1295 """
1296 lck = self._lib().auto_lock()
1297 buddy_cfg = _pjsua.buddy_config_default()
1298 buddy_cfg.uri = uri
1299 buddy_cfg.subscribe = False
1300 err, buddy_id = _pjsua.buddy_add(buddy_cfg)
1301 self._lib()._err_check("add_buddy()", self, err)
1302 buddy = Buddy(self._lib(), buddy_id, self, cb)
1303 return buddy
1304
1305 def pres_notify(self, pres_obj, state, reason="", hdr_list=None):
1306 """Send NOTIFY to inform account presence status or to terminate
1307 server side presence subscription.
1308
1309 Keyword arguments:
1310 pres_obj -- The subscription object from on_incoming_subscribe()
1311 callback
1312 state -- Subscription state, from SubscriptionState
1313 reason -- Optional reason phrase.
1314 hdr_list -- Optional header list.
1315 """
1316 lck = self._lib().auto_lock()
1317 _pjsua.acc_pres_notify(self._id, pres_obj, state, reason,
1318 Lib._create_msg_data(hdr_list))
1319
1320 def send_pager(self, uri, text, im_id=0, content_type="text/plain", \
1321 hdr_list=None):
1322 """Send instant message to arbitrary URI.
1323
1324 Keyword arguments:
1325 text -- Instant message to be sent
1326 uri -- URI to send the Instant Message to.
1327 im_id -- Optional instant message ID to identify this
1328 instant message when delivery status callback
1329 is called.
1330 content_type -- MIME type identifying the instant message
1331 hdr_list -- Optional list of headers to be sent with the
1332 request.
1333
1334 """
1335 lck = self._lib().auto_lock()
1336 err = _pjsua.im_send(self._id, uri, \
1337 content_type, text, \
1338 Lib._create_msg_data(hdr_list), \
1339 im_id)
1340 self._lib()._err_check("send_pager()", self, err)
1341
1342class CallCallback:
1343 """Class to receive event notification from Call objects.
1344
1345 Use Call.set_callback() method to install instance of this callback
1346 class to receive event notifications from the call object.
1347
1348 Member documentation:
1349
1350 call -- the Call object.
1351
1352 """
1353 call = None
1354
1355 def __init__(self, call=None):
1356 self._set_call(call)
1357
1358 def __del__(self):
1359 pass
1360
1361 def _set_call(self, call):
1362 if call:
1363 self.call = weakref.proxy(call)
1364 else:
1365 self.call = None
1366
1367 def on_state(self):
1368 """Notification that the call's state has changed.
1369
1370 """
1371 pass
1372
1373 def on_media_state(self):
1374 """Notification that the call's media state has changed.
1375
1376 """
1377 pass
1378
1379 def on_dtmf_digit(self, digits):
1380 """Notification on incoming DTMF digits.
1381
1382 Keyword argument:
1383 digits -- string containing the received digits.
1384
1385 """
1386 pass
1387
1388 def on_transfer_request(self, dst, code):
1389 """Notification that call is being transfered by remote party.
1390
1391 Application can decide to accept/reject transfer request by returning
1392 code greater than or equal to 500. The default behavior is to accept
1393 the transfer by returning 202.
1394
1395 Keyword arguments:
1396 dst -- string containing the destination URI
1397 code -- the suggested status code to return to accept the request.
1398
1399 Return:
1400 the callback should return 202 to accept the request, or 300-699 to
1401 reject the request.
1402
1403 """
1404 return code
1405
1406 def on_transfer_status(self, code, reason, final, cont):
1407 """
1408 Notification about the status of previous call transfer request.
1409
1410 Keyword arguments:
1411 code -- SIP status code to indicate completion status.
1412 text -- SIP status reason phrase.
1413 final -- if True then this is a final status and no further
1414 notifications will be sent for this call transfer
1415 status.
1416 cont -- suggested return value.
1417
1418 Return:
1419 If the callback returns false then no further notification will
1420 be sent for the transfer request for this call.
1421
1422 """
1423 return cont
1424
1425 def on_replace_request(self, code, reason):
1426 """Notification when incoming INVITE with Replaces header is received.
1427
1428 Application may reject the request by returning value greather than
1429 or equal to 500. The default behavior is to accept the request.
1430
1431 Keyword arguments:
1432 code -- default status code to return
1433 reason -- default reason phrase to return
1434
1435 Return:
1436 The callback should return (code, reason) tuple.
1437
1438 """
1439 return code, reason
1440
1441 def on_replaced(self, new_call):
1442 """
1443 Notification that this call will be replaced with new_call.
1444 After this callback is called, this call will be disconnected.
1445
1446 Keyword arguments:
1447 new_call -- the new call that will replace this call.
1448 """
1449 pass
1450
1451 def on_pager(self, mime_type, body):
1452 """
1453 Notification that incoming instant message is received on
1454 this call.
1455
1456 Keyword arguments:
1457 mime_type -- MIME type of the instant message body.
1458 body -- the instant message body.
1459
1460 """
1461 pass
1462
1463 def on_pager_status(self, body, im_id, code, reason):
1464 """
1465 Notification about the delivery status of previously sent
1466 instant message.
1467
1468 Keyword arguments:
1469 body -- message body
1470 im_id -- message ID
1471 code -- SIP status code
1472 reason -- SIP reason phrase
1473
1474 """
1475 pass
1476
1477 def on_typing(self, is_typing):
1478 """
1479 Notification that remote is typing or stop typing.
1480
1481 Keyword arguments:
1482 is_typing -- boolean to indicate whether remote is currently
1483 typing an instant message.
1484
1485 """
1486 pass
1487
1488
1489class CallInfo:
1490 """This structure contains various information about Call.
1491
1492 Application may retrieve this information with Call.info().
1493
1494 Member documentation:
1495
1496 role -- CallRole
1497 account -- Account object.
1498 uri -- SIP URI of local account.
1499 contact -- local Contact URI.
1500 remote_uri -- remote SIP URI.
1501 remote_contact -- remote Contact URI
1502 sip_call_id -- call's Call-ID identification
1503 state -- CallState
1504 state_text -- state text.
1505 last_code -- last SIP status code
1506 last_reason -- text phrase for last_code
1507 media_state -- MediaState
1508 media_dir -- MediaDir
1509 conf_slot -- conference slot number for this call.
1510 call_time -- call's connected duration in seconds.
1511 total_time -- total call duration in seconds.
1512 """
1513 role = CallRole.CALLER
1514 account = None
1515 uri = ""
1516 contact = ""
1517 remote_uri = ""
1518 remote_contact = ""
1519 sip_call_id = ""
1520 state = CallState.NULL
1521 state_text = ""
1522 last_code = 0
1523 last_reason = ""
1524 media_state = MediaState.NULL
1525 media_dir = MediaDir.NULL
1526 conf_slot = -1
1527 call_time = 0
1528 total_time = 0
1529
1530 def __init__(self, lib=None, ci=None):
1531 if lib and ci:
1532 self._cvt_from_pjsua(lib, ci)
1533
1534 def _cvt_from_pjsua(self, lib, ci):
1535 self.role = ci.role
1536 self.account = lib._lookup_account(ci.acc_id)
1537 self.uri = ci.local_info
1538 self.contact = ci.local_contact
1539 self.remote_uri = ci.remote_info
1540 self.remote_contact = ci.remote_contact
1541 self.sip_call_id = ci.call_id
1542 self.state = ci.state
1543 self.state_text = ci.state_text
1544 self.last_code = ci.last_status
1545 self.last_reason = ci.last_status_text
1546 self.media_state = ci.media_status
1547 self.media_dir = ci.media_dir
1548 self.conf_slot = ci.conf_slot
1549 self.call_time = ci.connect_duration / 1000
1550 self.total_time = ci.total_duration / 1000
1551
1552
1553class Call:
1554 """This class represents SIP call.
1555
1556 Application initiates outgoing call with Account.make_call(), and
1557 incoming calls are reported in AccountCallback.on_incoming_call().
1558 """
1559 _id = -1
1560 _cb = None
1561 _lib = None
1562 _obj_name = ""
1563
1564 def __init__(self, lib, call_id, cb=None):
1565 self._lib = weakref.ref(lib)
1566 self.set_callback(cb)
1567 self.attach_to_id(call_id)
1568 _Trace((self, 'created'))
1569
1570 def __del__(self):
1571 if self._id != -1:
1572 _pjsua.call_set_user_data(self._id, 0)
1573 _Trace((self, 'destroyed'))
1574
1575 def __str__(self):
1576 return self._obj_name
1577
1578 def attach_to_id(self, call_id):
1579 lck = self._lib().auto_lock()
1580 if self._id != -1:
1581 _pjsua.call_set_user_data(self._id, 0)
1582 self._id = call_id
1583 if self._id != -1:
1584 _pjsua.call_set_user_data(self._id, self)
1585 self._obj_name = "{Call " + self.info().remote_uri + "}"
1586 else:
1587 self._obj_name = "{Call object}"
1588
1589 def set_callback(self, cb):
1590 """
1591 Set callback object to retrieve event notifications from this call.
1592
1593 Keyword arguments:
1594 cb -- CallCallback instance.
1595 """
1596 if cb:
1597 self._cb = cb
1598 else:
1599 self._cb = CallCallback(self)
1600 self._cb._set_call(self)
1601
1602 def info(self):
1603 """
1604 Get the CallInfo.
1605 """
1606 lck = self._lib().auto_lock()
1607 ci = _pjsua.call_get_info(self._id)
1608 if not ci:
1609 self._lib()._err_check("info", self, -1, "Invalid call")
1610 call_info = CallInfo(self._lib(), ci)
1611 return call_info
1612
1613 def is_valid(self):
1614 """
1615 Check if this call is still valid.
1616 """
1617 lck = self._lib().auto_lock()
1618 return _pjsua.call_is_active(self._id)
1619
1620 def dump_status(self, with_media=True, indent="", max_len=1024):
1621 """
1622 Dump the call status.
1623 """
1624 lck = self._lib().auto_lock()
1625 return _pjsua.call_dump(self._id, with_media, max_len, indent)
1626
1627 def answer(self, code=200, reason="", hdr_list=None):
1628 """
1629 Send provisional or final response to incoming 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 INVITE response.
1637
1638 """
1639 lck = self._lib().auto_lock()
1640 err = _pjsua.call_answer(self._id, code, reason,
1641 Lib._create_msg_data(hdr_list))
1642 self._lib()._err_check("answer()", self, err)
1643
1644 def hangup(self, code=603, reason="", hdr_list=None):
1645 """
1646 Terminate the call.
1647
1648 Keyword arguments:
1649 code -- SIP status code.
1650 reason -- Reason phrase. Put empty to send default reason
1651 phrase for the status code.
1652 hdr_list -- Optional list of headers to be sent with the
1653 message.
1654
1655 """
1656 lck = self._lib().auto_lock()
1657 err = _pjsua.call_hangup(self._id, code, reason,
1658 Lib._create_msg_data(hdr_list))
1659 self._lib()._err_check("hangup()", self, err)
1660
1661 def hold(self, hdr_list=None):
1662 """
1663 Put the call on hold.
1664
1665 Keyword arguments:
1666 hdr_list -- Optional list of headers to be sent with the
1667 message.
1668 """
1669 lck = self._lib().auto_lock()
1670 err = _pjsua.call_set_hold(self._id, Lib._create_msg_data(hdr_list))
1671 self._lib()._err_check("hold()", self, err)
1672
1673 def unhold(self, hdr_list=None):
1674 """
1675 Release the call from hold.
1676
1677 Keyword arguments:
1678 hdr_list -- Optional list of headers to be sent with the
1679 message.
1680
1681 """
1682 lck = self._lib().auto_lock()
1683 err = _pjsua.call_reinvite(self._id, True,
1684 Lib._create_msg_data(hdr_list))
1685 self._lib()._err_check("unhold()", self, err)
1686
1687 def reinvite(self, hdr_list=None):
1688 """
1689 Send re-INVITE and optionally offer new codecs to use.
1690
1691 Keyword arguments:
1692 hdr_list -- Optional list of headers to be sent with the
1693 message.
1694
1695 """
1696 lck = self._lib().auto_lock()
1697 err = _pjsua.call_reinvite(self._id, True,
1698 Lib._create_msg_data(hdr_list))
1699 self._lib()._err_check("reinvite()", self, err)
1700
1701 def update(self, hdr_list=None, options=0):
1702 """
1703 Send UPDATE and optionally offer new codecs to use.
1704
1705 Keyword arguments:
1706 hdr_list -- Optional list of headers to be sent with the
1707 message.
1708 options -- Must be zero for now.
1709
1710 """
1711 lck = self._lib().auto_lock()
1712 err = _pjsua.call_update(self._id, options,
1713 Lib._create_msg_data(hdr_list))
1714 self._lib()._err_check("update()", self, err)
1715
1716 def transfer(self, dest_uri, hdr_list=None):
1717 """
1718 Transfer the call to new destination.
1719
1720 Keyword arguments:
1721 dest_uri -- Specify the SIP URI to transfer the call to.
1722 hdr_list -- Optional list of headers to be sent with the
1723 message.
1724
1725 """
1726 lck = self._lib().auto_lock()
1727 err = _pjsua.call_xfer(self._id, dest_uri,
1728 Lib._create_msg_data(hdr_list))
1729 self._lib()._err_check("transfer()", self, err)
1730
1731 def transfer_to_call(self, call, hdr_list=None, options=0):
1732 """
1733 Attended call transfer.
1734
1735 Keyword arguments:
1736 call -- The Call object to transfer call to.
1737 hdr_list -- Optional list of headers to be sent with the
1738 message.
1739 options -- Must be zero for now.
1740
1741 """
1742 lck = self._lib().auto_lock()
1743 err = _pjsua.call_xfer_replaces(self._id, call._id, options,
1744 Lib._create_msg_data(hdr_list))
1745 self._lib()._err_check("transfer_to_call()", self, err)
1746
1747 def dial_dtmf(self, digits):
1748 """
1749 Send DTMF digits with RTP event package.
1750
1751 Keyword arguments:
1752 digits -- DTMF digit string.
1753
1754 """
1755 lck = self._lib().auto_lock()
1756 err = _pjsua.call_dial_dtmf(self._id, digits)
1757 self._lib()._err_check("dial_dtmf()", self, err)
1758
1759 def send_request(self, method, hdr_list=None, content_type=None,
1760 body=None):
1761 """
1762 Send arbitrary request to remote call.
1763
1764 This is useful for example to send INFO request. Note that this
1765 function should not be used to send request that will change the
1766 call state such as CANCEL or BYE.
1767
1768 Keyword arguments:
1769 method -- SIP method name.
1770 hdr_list -- Optional header list to be sent with the request.
1771 content_type -- Content type to describe the body, if the body
1772 is present
1773 body -- Optional SIP message body.
1774
1775 """
1776 lck = self._lib().auto_lock()
1777 if hdr_list or body:
1778 msg_data = _pjsua.Msg_Data()
1779 if hdr_list:
1780 msg_data.hdr_list = hdr_list
1781 if content_type:
1782 msg_data.content_type = content_type
1783 if body:
1784 msg_data.msg_body = body
1785 else:
1786 msg_data = None
1787
1788 err = _pjsua.call_send_request(self._id, method, msg_data)
1789 self._lib()._err_check("send_request()", self, err)
1790
1791 def send_pager(self, text, im_id=0, content_type="text/plain",
1792 hdr_list=None):
1793 """Send instant message inside a call.
1794
1795 Keyword arguments:
1796 text -- Instant message to be sent
1797 im_id -- Optional instant message ID to identify this
1798 instant message when delivery status callback
1799 is called.
1800 content_type -- MIME type identifying the instant message
1801 hdr_list -- Optional list of headers to be sent with the
1802 request.
1803
1804 """
1805 lck = self._lib().auto_lock()
1806 err = _pjsua.call_send_im(self._id, \
1807 content_type, text, \
1808 Lib._create_msg_data(hdr_list), \
1809 im_id)
1810 self._lib()._err_check("send_pager()", self, err)
1811
1812
1813class BuddyInfo:
1814 """This class contains information about Buddy. Application may
1815 retrieve this information by calling Buddy.info().
1816
1817 Member documentation:
1818
1819 uri -- the Buddy URI.
1820 contact -- the Buddy Contact URI, if available.
1821 online_status -- the presence online status.
1822 online_text -- the presence online status text.
1823 activity -- the PresenceActivity
1824 subscribed -- specify whether buddy's presence status is currently
1825 being subscribed.
1826 sub_state -- SubscriptionState
1827 sub_term_reason -- The termination reason string of the last presence
1828 subscription to this buddy, if any.
1829 """
1830 uri = ""
1831 contact = ""
1832 online_status = 0
1833 online_text = ""
1834 activity = PresenceActivity.UNKNOWN
1835 subscribed = False
1836 sub_state = SubscriptionState.NULL
1837 sub_term_reason = ""
1838
1839 def __init__(self, pjsua_bi=None):
1840 if pjsua_bi:
1841 self._cvt_from_pjsua(pjsua_bi)
1842
1843 def _cvt_from_pjsua(self, inf):
1844 self.uri = inf.uri
1845 self.contact = inf.contact
1846 self.online_status = inf.status
1847 self.online_text = inf.status_text
1848 self.activity = inf.activity
1849 self.subscribed = inf.monitor_pres
1850 self.sub_state = inf.sub_state
1851 self.sub_term_reason = inf.sub_term_reason
1852
1853
1854class BuddyCallback:
1855 """This class can be used to receive notifications about Buddy's
1856 presence status change. Application needs to derive a class from
1857 this class, and register the instance with Buddy.set_callback().
1858
1859 Member documentation:
1860
1861 buddy -- the Buddy object.
1862 """
1863 buddy = None
1864
1865 def __init__(self, buddy=None):
1866 self._set_buddy(buddy)
1867
1868 def _set_buddy(self, buddy):
1869 if buddy:
1870 self.buddy = weakref.proxy(buddy)
1871 else:
1872 self.buddy = None
1873
1874 def on_state(self):
1875 """
1876 Notification that buddy's presence state has changed. Application
1877 may then retrieve the new status with Buddy.info() function.
1878 """
1879 pass
1880
1881 def on_pager(self, mime_type, body):
1882 """Notification that incoming instant message is received from
1883 this buddy.
1884
1885 Keyword arguments:
1886 mime_type -- MIME type of the instant message body
1887 body -- the instant message body
1888
1889 """
1890 pass
1891
1892 def on_pager_status(self, body, im_id, code, reason):
1893 """Notification about the delivery status of previously sent
1894 instant message.
1895
1896 Keyword arguments:
1897 body -- the message body
1898 im_id -- message ID
1899 code -- SIP status code
1900 reason -- SIP reason phrase
1901
1902 """
1903 pass
1904
1905 def on_typing(self, is_typing):
1906 """Notification that remote is typing or stop typing.
1907
1908 Keyword arguments:
1909 is_typing -- boolean to indicate whether remote is currently
1910 typing an instant message.
1911
1912 """
1913 pass
1914
1915
1916class Buddy:
1917 """A Buddy represents person or remote agent.
1918
1919 This class provides functions to subscribe to buddy's presence and
1920 to send or receive instant messages from the buddy.
1921 """
1922 _id = -1
1923 _lib = None
1924 _cb = None
1925 _obj_name = ""
1926 _acc = None
1927
1928 def __init__(self, lib, id, account, cb):
1929 self._id = id
1930 self._lib = weakref.ref(lib)
1931 self._acc = weakref.ref(account)
1932 self._obj_name = "{Buddy " + self.info().uri + "}"
1933 self.set_callback(cb)
1934 _pjsua.buddy_set_user_data(self._id, self)
1935 _Trace((self, 'created'))
1936
1937 def __del__(self):
1938 if self._id != -1:
1939 _pjsua.buddy_set_user_data(self._id, 0)
1940 _Trace((self, 'destroyed'))
1941
1942 def __str__(self):
1943 return self._obj_name
1944
1945 def info(self):
1946 """
1947 Get buddy info as BuddyInfo.
1948 """
1949 lck = self._lib().auto_lock()
1950 return BuddyInfo(_pjsua.buddy_get_info(self._id))
1951
1952 def set_callback(self, cb):
1953 """Install callback to receive notifications from this object.
1954
1955 Keyword argument:
1956 cb -- BuddyCallback instance.
1957 """
1958 if cb:
1959 self._cb = cb
1960 else:
1961 self._cb = BuddyCallback(self)
1962 self._cb._set_buddy(self)
1963
1964 def subscribe(self):
1965 """
1966 Subscribe to buddy's presence status notification.
1967 """
1968 lck = self._lib().auto_lock()
1969 err = _pjsua.buddy_subscribe_pres(self._id, True)
1970 self._lib()._err_check("subscribe()", self, err)
1971
1972 def unsubscribe(self):
1973 """
1974 Unsubscribe from buddy's presence status notification.
1975 """
1976 lck = self._lib().auto_lock()
1977 err = _pjsua.buddy_subscribe_pres(self._id, False)
1978 self._lib()._err_check("unsubscribe()", self, err)
1979
1980 def delete(self):
1981 """
1982 Remove this buddy from the buddy list.
1983 """
1984 lck = self._lib().auto_lock()
1985 if self._id != -1:
1986 _pjsua.buddy_set_user_data(self._id, 0)
1987 err = _pjsua.buddy_del(self._id)
1988 self._lib()._err_check("delete()", self, err)
1989
1990 def send_pager(self, text, im_id=0, content_type="text/plain", \
1991 hdr_list=None):
1992 """Send instant message to remote buddy.
1993
1994 Keyword arguments:
1995 text -- Instant message to be sent
1996 im_id -- Optional instant message ID to identify this
1997 instant message when delivery status callback
1998 is called.
1999 content_type -- MIME type identifying the instant message
2000 hdr_list -- Optional list of headers to be sent with the
2001 request.
2002
2003 """
2004 lck = self._lib().auto_lock()
2005 err = _pjsua.im_send(self._acc()._id, self.info().uri, \
2006 content_type, text, \
2007 Lib._create_msg_data(hdr_list), \
2008 im_id)
2009 self._lib()._err_check("send_pager()", self, err)
2010
2011 def send_typing_ind(self, is_typing=True, hdr_list=None):
2012 """Send typing indication to remote buddy.
2013
2014 Keyword argument:
2015 is_typing -- boolean to indicate wheter user is typing.
2016 hdr_list -- Optional list of headers to be sent with the
2017 request.
2018
2019 """
2020 lck = self._lib().auto_lock()
2021 err = _pjsua.im_typing(self._acc()._id, self.info().uri, \
2022 is_typing, Lib._create_msg_data(hdr_list))
2023 self._lib()._err_check("send_typing_ind()", self, err)
2024
2025
2026
2027# Sound device info
2028class SoundDeviceInfo:
2029 """This described the sound device info.
2030
2031 Member documentation:
2032 name -- device name.
2033 input_channels -- number of capture channels supported.
2034 output_channels -- number of playback channels supported.
2035 default_clock_rate -- default sampling rate.
2036 """
2037 name = ""
2038 input_channels = 0
2039 output_channels = 0
2040 default_clock_rate = 0
2041
2042 def __init__(self, sdi):
2043 self.name = sdi.name
2044 self.input_channels = sdi.input_count
2045 self.output_channels = sdi.output_count
2046 self.default_clock_rate = sdi.default_samples_per_sec
2047
2048
2049# Codec info
2050class CodecInfo:
2051 """This describes codec info.
2052
2053 Member documentation:
2054 name -- codec name
2055 priority -- codec priority (0-255)
2056 clock_rate -- clock rate
2057 channel_count -- number of channels
2058 avg_bps -- average bandwidth in bits per second
2059 frm_ptime -- base frame length in milliseconds
2060 ptime -- RTP frame length in milliseconds.
2061 pt -- payload type.
2062 vad_enabled -- specify if Voice Activity Detection is currently
2063 enabled.
2064 plc_enabled -- specify if Packet Lost Concealment is currently
2065 enabled.
2066 """
2067 name = ""
2068 priority = 0
2069 clock_rate = 0
2070 channel_count = 0
2071 avg_bps = 0
2072 frm_ptime = 0
2073 ptime = 0
2074 pt = 0
2075 vad_enabled = False
2076 plc_enabled = False
2077
2078 def __init__(self, codec_info, codec_param):
2079 self.name = codec_info.codec_id
2080 self.priority = codec_info.priority
2081 self.clock_rate = codec_param.info.clock_rate
2082 self.channel_count = codec_param.info.channel_cnt
2083 self.avg_bps = codec_param.info.avg_bps
2084 self.frm_ptime = codec_param.info.frm_ptime
2085 self.ptime = codec_param.info.frm_ptime * \
2086 codec_param.setting.frm_per_pkt
2087 self.ptime = codec_param.info.pt
2088 self.vad_enabled = codec_param.setting.vad
2089 self.plc_enabled = codec_param.setting.plc
2090
2091 def _cvt_to_pjsua(self):
2092 ci = _pjsua.Codec_Info()
2093 ci.codec_id = self.name
2094 ci.priority = self.priority
2095 return ci
2096
2097
2098# Codec parameter
2099class CodecParameter:
2100 """This specifies various parameters that can be configured for codec.
2101
2102 Member documentation:
2103
2104 ptime -- specify the outgoing RTP packet length in milliseconds.
2105 vad_enabled -- specify if VAD should be enabled.
2106 plc_enabled -- specify if PLC should be enabled.
2107 """
2108 ptime = 0
2109 vad_enabled = False
2110 plc_enabled = False
2111 _codec_param = None
2112
2113 def __init__(self, codec_param):
2114 self.ptime = codec_param.info.frm_ptime * \
2115 codec_param.setting.frm_per_pkt
2116 self.vad_enabled = codec_param.setting.vad
2117 self.plc_enabled = codec_param.setting.plc
2118 self._codec_param = codec_param
2119
2120 def _cvt_to_pjsua(self):
2121 self._codec_param.setting.frm_per_pkt = self.ptime / \
2122 self._codec_param.info.frm_ptime
2123 self._codec_param.setting.vad = self.vad_enabled
2124 self._codec_param.setting.plc = self.plc_enabled
2125 return self._codec_param
2126
2127
2128# Library mutex
2129class _LibMutex:
2130 def __init__(self, lck):
2131 self._lck = lck
2132 self._lck.acquire()
2133 #_Trace(('lock acquired',))
2134
2135 def __del__(self):
2136 try:
2137 self._lck.release()
2138 #_Trace(('lock released',))
2139 except:
2140 #_Trace(('lock release error',))
2141 pass
2142
2143
2144# PJSUA Library
2145_lib = None
2146enable_trace = False
2147
2148class Lib:
2149 """Library instance.
2150
2151 """
2152 _quit = False
2153 _has_thread = False
2154 _lock = None
2155
2156 def __init__(self):
2157 global _lib
2158 if _lib:
2159 raise Error("__init()__", None, -1,
2160 "Library instance already exist")
2161
2162 self._lock = threading.RLock()
2163 err = _pjsua.create()
2164 self._err_check("_pjsua.create()", None, err)
2165 _lib = self
2166
2167 def __del__(self):
2168 _pjsua.destroy()
2169 del self._lock
2170 _Trace(('Lib destroyed',))
2171
2172 def __str__(self):
2173 return "Lib"
2174
2175 @staticmethod
2176 def instance():
2177 """Return singleton instance of Lib.
2178 """
2179 return _lib
2180
2181 def init(self, ua_cfg=None, log_cfg=None, media_cfg=None):
2182 """
2183 Initialize pjsua with the specified configurations.
2184
2185 Keyword arguments:
2186 ua_cfg -- optional UAConfig instance
2187 log_cfg -- optional LogConfig instance
2188 media_cfg -- optional MediaConfig instance
2189
2190 """
2191 if not ua_cfg: ua_cfg = UAConfig()
2192 if not log_cfg: log_cfg = LogConfig()
2193 if not media_cfg: media_cfg = MediaConfig()
2194
2195 py_ua_cfg = ua_cfg._cvt_to_pjsua()
2196 py_ua_cfg.cb.on_call_state = _cb_on_call_state
2197 py_ua_cfg.cb.on_incoming_call = _cb_on_incoming_call
2198 py_ua_cfg.cb.on_call_media_state = _cb_on_call_media_state
2199 py_ua_cfg.cb.on_dtmf_digit = _cb_on_dtmf_digit
2200 py_ua_cfg.cb.on_call_transfer_request = _cb_on_call_transfer_request
2201 py_ua_cfg.cb.on_call_transfer_status = _cb_on_call_transfer_status
2202 py_ua_cfg.cb.on_call_replace_request = _cb_on_call_replace_request
2203 py_ua_cfg.cb.on_call_replaced = _cb_on_call_replaced
2204 py_ua_cfg.cb.on_reg_state = _cb_on_reg_state
2205 py_ua_cfg.cb.on_incoming_subscribe = _cb_on_incoming_subscribe
2206 py_ua_cfg.cb.on_buddy_state = _cb_on_buddy_state
2207 py_ua_cfg.cb.on_pager = _cb_on_pager
2208 py_ua_cfg.cb.on_pager_status = _cb_on_pager_status
2209 py_ua_cfg.cb.on_typing = _cb_on_typing
2210 py_ua_cfg.cb.on_mwi_info = _cb_on_mwi_info;
2211
2212 err = _pjsua.init(py_ua_cfg, log_cfg._cvt_to_pjsua(),
2213 media_cfg._cvt_to_pjsua())
2214 self._err_check("init()", self, err)
2215
2216 def destroy(self):
2217 """Destroy the library, and pjsua."""
2218 global _lib
2219 if self._has_thread:
2220 self._quit = 1
2221 loop = 0
2222 while self._quit != 2 and loop < 400:
2223 self.handle_events(5)
2224 loop = loop + 1
2225 time.sleep(0.050)
2226 _pjsua.destroy()
2227 _lib = None
2228
2229 def start(self, with_thread=True):
2230 """Start the library.
2231
2232 Keyword argument:
2233 with_thread -- specify whether the module should create worker
2234 thread.
2235
2236 """
2237 lck = self.auto_lock()
2238 err = _pjsua.start()
2239 self._err_check("start()", self, err)
2240 self._has_thread = with_thread
2241 if self._has_thread:
2242 thread.start_new(_worker_thread_main, (0,))
2243
2244 def handle_events(self, timeout=50):
2245 """Poll the events from underlying pjsua library.
2246
2247 Application must poll the stack periodically if worker thread
2248 is disable when starting the library.
2249
2250 Keyword argument:
2251 timeout -- in milliseconds.
2252
2253 """
2254 lck = self.auto_lock()
2255 return _pjsua.handle_events(timeout)
2256
2257 def thread_register(self, name):
2258 """Register external threads (threads that are not created by PJSIP,
2259 such as threads that are created by Python API) to PJSIP.
2260
2261 The call must be made from the new thread before calling any pjlib
2262 functions.
2263
2264 Keyword arguments:
2265 name -- Non descriptive name for the thread
2266 """
2267 dummy = 1
2268 err = _pjsua.thread_register(name, dummy)
2269 self._err_check("thread_register()", self, err)
2270
2271 def verify_sip_url(self, sip_url):
2272 """Verify that the specified string is a valid URI.
2273
2274 Keyword argument:
2275 sip_url -- the URL string.
2276
2277 Return:
2278 0 is the the URI is valid, otherwise the appropriate error
2279 code is returned.
2280
2281 """
2282 lck = self.auto_lock()
2283 return _pjsua.verify_sip_url(sip_url)
2284
2285 def create_transport(self, type, cfg=None):
2286 """Create SIP transport instance of the specified type.
2287
2288 Keyword arguments:
2289 type -- transport type from TransportType constant.
2290 cfg -- TransportConfig instance
2291
2292 Return:
2293 Transport object
2294
2295 """
2296 lck = self.auto_lock()
2297 if not cfg: cfg=TransportConfig()
2298 err, tp_id = _pjsua.transport_create(type, cfg._cvt_to_pjsua())
2299 self._err_check("create_transport()", self, err)
2300 return Transport(self, tp_id)
2301
2302 def create_account(self, acc_config, set_default=True, cb=None):
2303 """
2304 Create a new local pjsua account using the specified configuration.
2305
2306 Keyword arguments:
2307 acc_config -- AccountConfig
2308 set_default -- boolean to specify whether to use this as the
2309 default account.
2310 cb -- AccountCallback instance.
2311
2312 Return:
2313 Account instance
2314
2315 """
2316 lck = self.auto_lock()
2317 err, acc_id = _pjsua.acc_add(acc_config._cvt_to_pjsua(), set_default)
2318 self._err_check("create_account()", self, err)
2319 return Account(self, acc_id, cb)
2320
2321 def create_account_for_transport(self, transport, set_default=True,
2322 cb=None):
2323 """Create a new local pjsua transport for the specified transport.
2324
2325 Keyword arguments:
2326 transport -- the Transport instance.
2327 set_default -- boolean to specify whether to use this as the
2328 default account.
2329 cb -- AccountCallback instance.
2330
2331 Return:
2332 Account instance
2333
2334 """
2335 lck = self.auto_lock()
2336 err, acc_id = _pjsua.acc_add_local(transport._id, set_default)
2337 self._err_check("create_account_for_transport()", self, err)
2338 return Account(self, acc_id, cb)
2339
2340 def hangup_all(self):
2341 """Hangup all calls.
2342
2343 """
2344 lck = self.auto_lock()
2345 _pjsua.call_hangup_all()
2346
2347 # Sound device API
2348
2349 def enum_snd_dev(self):
2350 """Enumerate sound devices in the system.
2351
2352 Return:
2353 list of SoundDeviceInfo. The index of the element specifies
2354 the device ID for the device.
2355 """
2356 lck = self.auto_lock()
2357 sdi_list = _pjsua.enum_snd_devs()
2358 info = []
2359 for sdi in sdi_list:
2360 info.append(SoundDeviceInfo(sdi))
2361 return info
2362
2363 def get_snd_dev(self):
2364 """Get the device IDs of current sound devices used by pjsua.
2365
2366 Return:
2367 (capture_dev_id, playback_dev_id) tuple
2368 """
2369 lck = self.auto_lock()
2370 return _pjsua.get_snd_dev()
2371
2372 def set_snd_dev(self, capture_dev, playback_dev):
2373 """Change the current sound devices.
2374
2375 Keyword arguments:
2376 capture_dev -- the device ID of capture device to be used
2377 playback_dev -- the device ID of playback device to be used.
2378
2379 """
2380 lck = self.auto_lock()
2381 err = _pjsua.set_snd_dev(capture_dev, playback_dev)
2382 self._err_check("set_current_sound_devices()", self, err)
2383
2384 def set_null_snd_dev(self):
2385 """Disable the sound devices. This is useful if the system
2386 does not have sound device installed.
2387
2388 """
2389 lck = self.auto_lock()
2390 err = _pjsua.set_null_snd_dev()
2391 self._err_check("set_null_snd_dev()", self, err)
2392
2393
2394 # Conference bridge
2395
2396 def conf_get_max_ports(self):
2397 """Get the conference bridge capacity.
2398
2399 Return:
2400 conference bridge capacity.
2401
2402 """
2403 lck = self.auto_lock()
2404 return _pjsua.conf_get_max_ports()
2405
2406 def conf_connect(self, src_slot, dst_slot):
2407 """Establish unidirectional media flow from souce to sink.
2408
2409 One source may transmit to multiple destinations/sink. And if
2410 multiple sources are transmitting to the same sink, the media
2411 will be mixed together. Source and sink may refer to the same ID,
2412 effectively looping the media.
2413
2414 If bidirectional media flow is desired, application needs to call
2415 this function twice, with the second one having the arguments
2416 reversed.
2417
2418 Keyword arguments:
2419 src_slot -- integer to identify the conference slot number of
2420 the source/transmitter.
2421 dst_slot -- integer to identify the conference slot number of
2422 the destination/receiver.
2423
2424 """
2425 lck = self.auto_lock()
2426 err = _pjsua.conf_connect(src_slot, dst_slot)
2427 self._err_check("conf_connect()", self, err)
2428
2429 def conf_disconnect(self, src_slot, dst_slot):
2430 """Disconnect media flow from the source to destination port.
2431
2432 Keyword arguments:
2433 src_slot -- integer to identify the conference slot number of
2434 the source/transmitter.
2435 dst_slot -- integer to identify the conference slot number of
2436 the destination/receiver.
2437
2438 """
2439 lck = self.auto_lock()
2440 err = _pjsua.conf_disconnect(src_slot, dst_slot)
2441 self._err_check("conf_disconnect()", self, err)
2442
2443 def conf_set_tx_level(self, slot, level):
2444 """Adjust the signal level to be transmitted from the bridge to
2445 the specified port by making it louder or quieter.
2446
2447 Keyword arguments:
2448 slot -- integer to identify the conference slot number.
2449 level -- Signal level adjustment. Value 1.0 means no level
2450 adjustment, while value 0 means to mute the port.
2451 """
2452 lck = self.auto_lock()
2453 err = _pjsua.conf_set_tx_level(slot, level)
2454 self._err_check("conf_set_tx_level()", self, err)
2455
2456 def conf_set_rx_level(self, slot, level):
2457 """Adjust the signal level to be received from the specified port
2458 (to the bridge) by making it louder or quieter.
2459
2460 Keyword arguments:
2461 slot -- integer to identify the conference slot number.
2462 level -- Signal level adjustment. Value 1.0 means no level
2463 adjustment, while value 0 means to mute the port.
2464 """
2465 lck = self.auto_lock()
2466 err = _pjsua.conf_set_rx_level(slot, level)
2467 self._err_check("conf_set_rx_level()", self, err)
2468
2469 def conf_get_signal_level(self, slot):
2470 """Get last signal level transmitted to or received from the
2471 specified port. The signal levels are float values from 0.0 to 1.0,
2472 with 0.0 indicates no signal, and 1.0 indicates the loudest signal
2473 level.
2474
2475 Keyword arguments:
2476 slot -- integer to identify the conference slot number.
2477
2478 Return value:
2479 (tx_level, rx_level) tuple.
2480 """
2481 lck = self.auto_lock()
2482 err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot)
2483 self._err_check("conf_get_signal_level()", self, err)
2484 return (tx_level, rx_level)
2485
2486
2487
2488 # Codecs API
2489
2490 def enum_codecs(self):
2491 """Return list of codecs supported by pjsua.
2492
2493 Return:
2494 list of CodecInfo
2495
2496 """
2497 lck = self.auto_lock()
2498 ci_list = _pjsua.enum_codecs()
2499 codec_info = []
2500 for ci in ci_list:
2501 cp = _pjsua.codec_get_param(ci.codec_id)
2502 if cp:
2503 codec_info.append(CodecInfo(ci, cp))
2504 return codec_info
2505
2506 def set_codec_priority(self, name, priority):
2507 """Change the codec priority.
2508
2509 Keyword arguments:
2510 name -- Codec name
2511 priority -- Codec priority, which range is 0-255.
2512
2513 """
2514 lck = self.auto_lock()
2515 err = _pjsua.codec_set_priority(name, priority)
2516 self._err_check("set_codec_priority()", self, err)
2517
2518 def get_codec_parameter(self, name):
2519 """Get codec parameter for the specified codec.
2520
2521 Keyword arguments:
2522 name -- codec name.
2523
2524 """
2525 lck = self.auto_lock()
2526 cp = _pjsua.codec_get_param(name)
2527 if not cp:
2528 self._err_check("get_codec_parameter()", self, -1,
2529 "Invalid codec name")
2530 return CodecParameter(cp)
2531
2532 def set_codec_parameter(self, name, param):
2533 """Modify codec parameter for the specified codec.
2534
2535 Keyword arguments:
2536 name -- codec name
2537 param -- codec parameter.
2538
2539 """
2540 lck = self.auto_lock()
2541 err = _pjsua.codec_set_param(name, param._cvt_to_pjsua())
2542 self._err_check("set_codec_parameter()", self, err)
2543
2544 # WAV playback and recording
2545
2546 def create_player(self, filename, loop=False):
2547 """Create WAV file player.
2548
2549 Keyword arguments
2550 filename -- WAV file name
2551 loop -- boolean to specify whether playback should
2552 automatically restart upon EOF
2553 Return:
2554 WAV player ID
2555
2556 """
2557 lck = self.auto_lock()
2558 opt = 0
2559 if not loop:
2560 opt = opt + 1
2561 err, player_id = _pjsua.player_create(filename, opt)
2562 self._err_check("create_player()", self, err)
2563 return player_id
2564
2565 def player_get_slot(self, player_id):
2566 """Get the conference port ID for the specified player.
2567
2568 Keyword arguments:
2569 player_id -- the WAV player ID
2570
2571 Return:
2572 Conference slot number for the player
2573
2574 """
2575 lck = self.auto_lock()
2576 slot = _pjsua.player_get_conf_port(player_id)
2577 if slot < 0:
2578 self._err_check("player_get_slot()", self, -1,
2579 "Invalid player id")
2580 return slot
2581
2582 def player_set_pos(self, player_id, pos):
2583 """Set WAV playback position.
2584
2585 Keyword arguments:
2586 player_id -- WAV player ID
2587 pos -- playback position, in samples
2588
2589 """
2590 lck = self.auto_lock()
2591 err = _pjsua.player_set_pos(player_id, pos)
2592 self._err_check("player_set_pos()", self, err)
2593
2594 def player_destroy(self, player_id):
2595 """Destroy the WAV player.
2596
2597 Keyword arguments:
2598 player_id -- the WAV player ID.
2599
2600 """
2601 lck = self.auto_lock()
2602 err = _pjsua.player_destroy(player_id)
2603 self._err_check("player_destroy()", self, err)
2604
2605 def create_playlist(self, filelist, label="playlist", loop=True):
2606 """Create WAV playlist.
2607
2608 Keyword arguments:
2609 filelist -- List of WAV file names.
2610 label -- Optional name to be assigned to the playlist
2611 object (useful for logging)
2612 loop -- boolean to specify whether playback should
2613 automatically restart upon EOF
2614
2615 Return:
2616 playlist_id
2617 """
2618 lck = self.auto_lock()
2619 opt = 0
2620 if not loop:
2621 opt = opt + 1
2622 err, playlist_id = _pjsua.playlist_create(label, filelist, opt)
2623 self._err_check("create_playlist()", self, err)
2624 return playlist_id
2625
2626 def playlist_get_slot(self, playlist_id):
2627 """Get the conference port ID for the specified playlist.
2628
2629 Keyword arguments:
2630 playlist_id -- the WAV playlist ID
2631
2632 Return:
2633 Conference slot number for the playlist
2634
2635 """
2636 lck = self.auto_lock()
2637 slot = _pjsua.player_get_conf_port(playlist_id)
2638 if slot < 0:
2639 self._err_check("playlist_get_slot()", self, -1,
2640 "Invalid playlist id")
2641 return slot
2642
2643 def playlist_destroy(self, playlist_id):
2644 """Destroy the WAV playlist.
2645
2646 Keyword arguments:
2647 playlist_id -- the WAV playlist ID.
2648
2649 """
2650 lck = self.auto_lock()
2651 err = _pjsua.player_destroy(playlist_id)
2652 self._err_check("playlist_destroy()", self, err)
2653
2654 def create_recorder(self, filename):
2655 """Create WAV file recorder.
2656
2657 Keyword arguments
2658 filename -- WAV file name
2659
2660 Return:
2661 WAV recorder ID
2662
2663 """
2664 lck = self.auto_lock()
2665 err, rec_id = _pjsua.recorder_create(filename, 0, None, -1, 0)
2666 self._err_check("create_recorder()", self, err)
2667 return rec_id
2668
2669 def recorder_get_slot(self, rec_id):
2670 """Get the conference port ID for the specified recorder.
2671
2672 Keyword arguments:
2673 rec_id -- the WAV recorder ID
2674
2675 Return:
2676 Conference slot number for the recorder
2677
2678 """
2679 lck = self.auto_lock()
2680 slot = _pjsua.recorder_get_conf_port(rec_id)
2681 if slot < 1:
2682 self._err_check("recorder_get_slot()", self, -1,
2683 "Invalid recorder id")
2684 return slot
2685
2686 def recorder_destroy(self, rec_id):
2687 """Destroy the WAV recorder.
2688
2689 Keyword arguments:
2690 rec_id -- the WAV recorder ID.
2691
2692 """
2693 lck = self.auto_lock()
2694 err = _pjsua.recorder_destroy(rec_id)
2695 self._err_check("recorder_destroy()", self, err)
2696
2697
2698 # Internal functions
2699
2700 @staticmethod
2701 def strerror(err):
2702 return _pjsua.strerror(err)
2703
2704 def _err_check(self, op_name, obj, err_code, err_msg=""):
2705 if err_code != 0:
2706 raise Error(op_name, obj, err_code, err_msg)
2707
2708 @staticmethod
2709 def _create_msg_data(hdr_list):
2710 if not hdr_list:
2711 return None
2712 msg_data = _pjsua.Msg_Data()
2713 msg_data.hdr_list = hdr_list
2714 return msg_data
2715
2716 def auto_lock(self):
2717 return _LibMutex(self._lock)
2718
2719 # Internal dictionary manipulation for calls, accounts, and buddies
2720
2721 def _lookup_call(self, call_id):
2722 return _pjsua.call_get_user_data(call_id)
2723
2724 def _lookup_account(self, acc_id):
2725 return _pjsua.acc_get_user_data(acc_id)
2726
2727 def _lookup_buddy(self, buddy_id, uri=None):
2728 if buddy_id != -1:
2729 buddy = _pjsua.buddy_get_user_data(buddy_id)
2730 elif uri:
2731 buddy_id = _pjsua.buddy_find(uri)
2732 if buddy_id != -1:
2733 buddy = _pjsua.buddy_get_user_data(buddy_id)
2734 else:
2735 buddy = None
2736 else:
2737 buddy = None
2738
2739 return buddy
2740
2741 # Account allbacks
2742
2743 def _cb_on_reg_state(self, acc_id):
2744 acc = self._lookup_account(acc_id)
2745 if acc:
2746 acc._cb.on_reg_state()
2747
2748 def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri,
2749 contact_uri, pres_obj):
2750 acc = self._lookup_account(acc_id)
2751 if acc:
2752 buddy = self._lookup_buddy(buddy_id)
2753 return acc._cb.on_incoming_subscribe(buddy, from_uri, contact_uri,
2754 pres_obj)
2755 else:
2756 return (404, None)
2757
2758 def _cb_on_incoming_call(self, acc_id, call_id, rdata):
2759 acc = self._lookup_account(acc_id)
2760 if acc:
2761 if 'on_incoming_call2' in acc._cb.__class__.__dict__:
2762 acc._cb.on_incoming_call2( Call(self, call_id), rdata )
2763 else:
2764 acc._cb.on_incoming_call( Call(self, call_id) )
2765 else:
2766 _pjsua.call_hangup(call_id, 603, None, None)
2767
2768 # Call callbacks
2769
2770 def _cb_on_call_state(self, call_id):
2771 call = self._lookup_call(call_id)
2772 if call:
2773 if call._id == -1:
2774 call.attach_to_id(call_id)
2775 done = (call.info().state == CallState.DISCONNECTED)
2776 call._cb.on_state()
2777 if done:
2778 _pjsua.call_set_user_data(call_id, 0)
2779 else:
2780 pass
2781
2782 def _cb_on_call_media_state(self, call_id):
2783 call = self._lookup_call(call_id)
2784 if call:
2785 call._cb.on_media_state()
2786
2787 def _cb_on_dtmf_digit(self, call_id, digits):
2788 call = self._lookup_call(call_id)
2789 if call:
2790 call._cb.on_dtmf_digit(digits)
2791
2792 def _cb_on_call_transfer_request(self, call_id, dst, code):
2793 call = self._lookup_call(call_id)
2794 if call:
2795 return call._cb.on_transfer_request(dst, code)
2796 else:
2797 return 603
2798
2799 def _cb_on_call_transfer_status(self, call_id, code, text, final, cont):
2800 call = self._lookup_call(call_id)
2801 if call:
2802 return call._cb.on_transfer_status(code, text, final, cont)
2803 else:
2804 return cont
2805
2806 def _cb_on_call_replace_request(self, call_id, rdata, code, reason):
2807 call = self._lookup_call(call_id)
2808 if call:
2809 return call._cb.on_replace_request(code, reason)
2810 else:
2811 return code, reason
2812
2813 def _cb_on_call_replaced(self, old_call_id, new_call_id):
2814 old_call = self._lookup_call(old_call_id)
2815 new_call = self._lookup_call(new_call_id)
2816 if old_call and new_call:
2817 old_call._cb.on_replaced(new_call)
2818
2819 def _cb_on_pager(self, call_id, from_uri, to_uri, contact, mime_type,
2820 body, acc_id):
2821 call = None
2822 if call_id != -1:
2823 call = self._lookup_call(call_id)
2824 if call:
2825 call._cb.on_pager(mime_type, body)
2826 else:
2827 acc = self._lookup_account(acc_id)
2828 buddy = self._lookup_buddy(-1, from_uri)
2829 if buddy:
2830 buddy._cb.on_pager(mime_type, body)
2831 else:
2832 acc._cb.on_pager(from_uri, contact, mime_type, body)
2833
2834 def _cb_on_pager_status(self, call_id, to_uri, body, user_data,
2835 code, reason, acc_id):
2836 call = None
2837 if call_id != -1:
2838 call = self._lookup_call(call_id)
2839 if call:
2840 call._cb.on_pager_status(body, user_data, code, reason)
2841 else:
2842 acc = self._lookup_account(acc_id)
2843 buddy = self._lookup_buddy(-1, to_uri)
2844 if buddy:
2845 buddy._cb.on_pager_status(body, user_data, code, reason)
2846 else:
2847 acc._cb.on_pager_status(to_uri, body, user_data, code, reason)
2848
2849 def _cb_on_typing(self, call_id, from_uri, to_uri, contact, is_typing,
2850 acc_id):
2851 call = None
2852 if call_id != -1:
2853 call = self._lookup_call(call_id)
2854 if call:
2855 call._cb.on_typing(is_typing)
2856 else:
2857 acc = self._lookup_account(acc_id)
2858 buddy = self._lookup_buddy(-1, from_uri)
2859 if buddy:
2860 buddy._cb.on_typing(is_typing)
2861 else:
2862 acc._cb.on_typing(from_uri, contact, is_typing)
2863
2864 def _cb_on_mwi_info(self, acc_id, body):
2865 acc = self._lookup_account(acc_id)
2866 if acc:
2867 return acc._cb.on_mwi_info(body)
2868
2869 def _cb_on_buddy_state(self, buddy_id):
2870 buddy = self._lookup_buddy(buddy_id)
2871 if buddy:
2872 buddy._cb.on_state()
2873
2874#
2875# Internal
2876#
2877
2878def _cb_on_call_state(call_id, e):
2879 _lib._cb_on_call_state(call_id)
2880
2881def _cb_on_incoming_call(acc_id, call_id, rdata):
2882 _lib._cb_on_incoming_call(acc_id, call_id, rdata)
2883
2884def _cb_on_call_media_state(call_id):
2885 _lib._cb_on_call_media_state(call_id)
2886
2887def _cb_on_dtmf_digit(call_id, digits):
2888 _lib._cb_on_dtmf_digit(call_id, digits)
2889
2890def _cb_on_call_transfer_request(call_id, dst, code):
2891 return _lib._cb_on_call_transfer_request(call_id, dst, code)
2892
2893def _cb_on_call_transfer_status(call_id, code, reason, final, cont):
2894 return _lib._cb_on_call_transfer_status(call_id, code, reason,
2895 final, cont)
2896def _cb_on_call_replace_request(call_id, rdata, code, reason):
2897 return _lib._cb_on_call_replace_request(call_id, rdata, code, reason)
2898
2899def _cb_on_call_replaced(old_call_id, new_call_id):
2900 _lib._cb_on_call_replaced(old_call_id, new_call_id)
2901
2902def _cb_on_reg_state(acc_id):
2903 _lib._cb_on_reg_state(acc_id)
2904
2905def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, contact_uri, pres):
2906 return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri,
2907 contact_uri, pres)
2908
2909def _cb_on_buddy_state(buddy_id):
2910 _lib._cb_on_buddy_state(buddy_id)
2911
2912def _cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id):
2913 _lib._cb_on_pager(call_id, from_uri, to, contact, mime_type, body, acc_id)
2914
2915def _cb_on_pager_status(call_id, to, body, user_data, status, reason, acc_id):
2916 _lib._cb_on_pager_status(call_id, to, body, user_data,
2917 status, reason, acc_id)
2918
2919def _cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id):
2920 _lib._cb_on_typing(call_id, from_uri, to, contact, is_typing, acc_id)
2921
2922def _cb_on_mwi_info(acc_id, body):
2923 _lib._cb_on_mwi_info(acc_id, body)
2924
2925# Worker thread
2926def _worker_thread_main(arg):
2927 global _lib
2928 _Trace(('worker thread started..',))
2929 thread_desc = 0;
2930 err = _pjsua.thread_register("python worker", thread_desc)
2931 _lib._err_check("thread_register()", _lib, err)
2932 while _lib and _lib._quit == 0:
2933 _lib.handle_events(1)
2934 time.sleep(0.050)
2935 if _lib:
2936 _lib._quit = 2
2937 _Trace(('worker thread exited..',))
2938
2939def _Trace(args):
2940 global enable_trace
2941 if enable_trace:
2942 print "** ",
2943 for arg in args:
2944 print arg,
2945 print " **"
2946