~ubuntu-branches/ubuntu/vivid/sflphone/vivid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjsip-apps/src/python/pjsua.py

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (4.1.18 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130630114056-0np50jkyqo6vnmii
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

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