Page MenuHomePhabricator

No OneTemporary

diff --git a/sipsimple/application.py b/sipsimple/application.py
index 667a2327..e6f6ca15 100644
--- a/sipsimple/application.py
+++ b/sipsimple/application.py
@@ -1,530 +1,530 @@
# Copyright (C) 2008-2010 AG Projects. See LICENSE for details.
#
"""
Implements a high-level application responsable for starting and stopping
various sub-systems required to implement a fully featured SIP User Agent
application.
"""
from __future__ import absolute_import, with_statement
__all__ = ["SIPApplication"]
from threading import RLock, Thread
from application.notification import IObserver, NotificationCenter
from application.python.util import Null, Singleton
from eventlet import api, coros, proc
from twisted.internet import reactor
from xcaplib import client as xcap_client
from zope.interface import implements
from sipsimple.account import Account, AccountManager
from sipsimple.audio import AudioDevice, RootAudioBridge
from sipsimple.configuration import ConfigurationManager
from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.core import AudioMixer, Engine, SIPCoreError, SIPURI
from sipsimple.lookup import DNSLookup, DNSLookupError, DNSManager
from sipsimple.session import SessionManager
from sipsimple.threading import ThreadManager, run_in_twisted_thread
from sipsimple.threading.green import Command, run_in_green_thread
from sipsimple.util import classproperty, TimestampedNotificationData
class ApplicationAttribute(object):
def __init__(self, value):
self.value = value
def __get__(self, obj, objtype):
return self.value
def __set__(self, obj, value):
self.value = value
class SIPApplication(object):
__metaclass__ = Singleton
implements(IObserver)
state = ApplicationAttribute(value=None)
end_reason = ApplicationAttribute(value=None)
alert_audio_device = ApplicationAttribute(value=None)
alert_audio_bridge = ApplicationAttribute(value=None)
voice_audio_device = ApplicationAttribute(value=None)
voice_audio_bridge = ApplicationAttribute(value=None)
_channel = ApplicationAttribute(value=coros.queue())
_nat_detect_channel = ApplicationAttribute(value=coros.queue())
_wakeup_timer = ApplicationAttribute(value=None)
_lock = ApplicationAttribute(value=RLock())
engine = Engine()
local_nat_type = ApplicationAttribute(value='unknown')
def start(self, config_backend):
with self._lock:
if self.state is not None:
raise RuntimeError("SIPApplication cannot be started from '%s' state" % self.state)
self.state = 'starting'
thread_manager = ThreadManager()
thread_manager.start()
account_manager = AccountManager()
configuration_manager = ConfigurationManager()
# load configuration
try:
configuration_manager.start(config_backend)
SIPSimpleSettings()
account_manager.load_accounts()
except:
self.state = None
raise
# start the reactor thread
self.thread = Thread(name='Reactor Thread', target=self._run_reactor)
self.thread.start()
def _run_reactor(self):
from eventlet.twistedutil import join_reactor
notification_center = NotificationCenter()
reactor.callLater(0, self._initialize_subsystems)
reactor.run(installSignalHandlers=False)
self.state = 'stopped'
notification_center.post_notification('SIPApplicationDidEnd', sender=self, data=TimestampedNotificationData(end_reason=self.end_reason))
def _initialize_subsystems(self):
account_manager = AccountManager()
dns_manager = DNSManager()
engine = Engine()
notification_center = NotificationCenter()
session_manager = SessionManager()
settings = SIPSimpleSettings()
xcap_client.DEFAULT_HEADERS = {'User-Agent': settings.user_agent}
notification_center.post_notification('SIPApplicationWillStart', sender=self, data=TimestampedNotificationData())
if self.state == 'stopping':
reactor.stop()
return
account = account_manager.default_account
# initialize core
notification_center.add_observer(self, sender=engine)
options = dict(# general
user_agent=settings.user_agent,
# SIP
ignore_missing_ack=False,
udp_port=settings.sip.udp_port if 'udp' in settings.sip.transport_list else None,
tcp_port=settings.sip.tcp_port if 'tcp' in settings.sip.transport_list else None,
tls_port=None,
# TLS
tls_protocol='TLSv1',
tls_verify_server=False,
tls_ca_file=None,
tls_cert_file=None,
tls_privkey_file=None,
tls_timeout=3000,
# rtp
rtp_port_range=(settings.rtp.port_range.start, settings.rtp.port_range.end),
# audio
codecs=list(settings.rtp.audio_codec_list),
# logging
log_level=settings.logs.pjsip_level,
trace_sip=True,
)
try:
engine.start(**options)
except SIPCoreError:
self.end_reason = 'engine failed'
reactor.stop()
return
# initialize TLS
try:
engine.set_tls_options(port=settings.sip.tls_port if 'tls' in settings.sip.transport_list else None,
protocol=settings.tls.protocol,
verify_server=account.tls.verify_server if account else False,
ca_file=settings.tls.ca_list.normalized if settings.tls.ca_list else None,
cert_file=account.tls.certificate.normalized if account and account.tls.certificate else None,
privkey_file=account.tls.certificate.normalized if account and account.tls.certificate else None,
timeout=settings.tls.timeout)
except Exception, e:
notification_center = NotificationCenter()
notification_center.post_notification('SIPApplicationFailedToStartTLS', sender=self, data=TimestampedNotificationData(error=e))
# initialize audio objects
alert_device = settings.audio.alert_device
- if alert_device not in (None, 'system_default') and alert_device not in engine.output_devices:
- alert_device = 'system_default'
+ if alert_device not in (None, u'system_default') and alert_device not in engine.output_devices:
+ alert_device = u'system_default'
input_device = settings.audio.input_device
- if input_device not in (None, 'system_default') and input_device not in engine.input_devices:
- input_device = 'system_default'
+ if input_device not in (None, u'system_default') and input_device not in engine.input_devices:
+ input_device = u'system_default'
output_device = settings.audio.output_device
- if output_device not in (None, 'system_default') and output_device not in engine.output_devices:
- output_device = 'system_default'
+ if output_device not in (None, u'system_default') and output_device not in engine.output_devices:
+ output_device = u'system_default'
try:
voice_mixer = AudioMixer(input_device, output_device, settings.audio.sample_rate, settings.audio.tail_length)
except SIPCoreError:
try:
- voice_mixer = AudioMixer('system_default', 'system_default', settings.audio.sample_rate, settings.audio.tail_length)
+ voice_mixer = AudioMixer(u'system_default', u'system_default', settings.audio.sample_rate, settings.audio.tail_length)
except SIPCoreError:
voice_mixer = AudioMixer(None, None, settings.audio.sample_rate, settings.audio.tail_length)
self.voice_audio_device = AudioDevice(voice_mixer)
self.voice_audio_bridge = RootAudioBridge(voice_mixer)
self.voice_audio_bridge.add(self.voice_audio_device)
try:
alert_mixer = AudioMixer(None, alert_device, settings.audio.sample_rate, 0)
except SIPCoreError:
try:
- alert_mixer = AudioMixer(None, 'system_default', settings.audio.sample_rate, 0)
+ alert_mixer = AudioMixer(None, u'system_default', settings.audio.sample_rate, 0)
except SIPCoreError:
alert_mixer = AudioMixer(None, None, settings.audio.sample_rate, 0)
if settings.audio.silent:
alert_mixer.output_volume = 0
self.alert_audio_device = AudioDevice(alert_mixer)
self.alert_audio_bridge = RootAudioBridge(alert_mixer)
self.alert_audio_bridge.add(self.alert_audio_device)
settings.audio.input_device = voice_mixer.input_device
settings.audio.output_device = voice_mixer.output_device
settings.audio.alert_device = alert_mixer.output_device
settings.save()
# initialize middleware components
dns_manager.start()
account_manager.start()
session_manager.start()
notification_center.add_observer(self, name='CFGSettingsObjectDidChange')
notification_center.add_observer(self, name='SIPEngineDetectedNATType')
notification_center.add_observer(self, name='DNSNameserversDidChange')
notification_center.add_observer(self, name='SystemIPAddressDidChange')
notification_center.add_observer(self, name='SystemDidWakeUpFromSleep')
self.state = 'started'
notification_center.post_notification('SIPApplicationDidStart', sender=self, data=TimestampedNotificationData())
self._detect_nat_type()
self._nat_detect_channel.send(Command('detect_nat'))
def stop(self):
with self._lock:
if self.state in (None, 'stopping', 'stopped'):
return
prev_state = self.state
self.state = 'stopping'
self.end_reason = 'application request'
notification_center = NotificationCenter()
notification_center.post_notification('SIPApplicationWillEnd', sender=self, data=TimestampedNotificationData())
if prev_state != 'starting':
self._shutdown_subsystems()
@run_in_green_thread
def _shutdown_subsystems(self):
# cleanup internals
if self._wakeup_timer is not None and self._wakeup_timer.active():
self._wakeup_timer.cancel()
self._wakeup_timer = None
# shutdown middleware components
dns_manager = DNSManager()
account_manager = AccountManager()
session_manager = SessionManager()
procs = [proc.spawn(dns_manager.stop), proc.spawn(account_manager.stop), proc.spawn(session_manager.stop)]
proc.waitall(procs)
# shutdown engine
engine = Engine()
engine.stop()
# TODO: timeout should be removed when the Engine is fixed so that it never hangs. -Saul
try:
with api.timeout(15):
while True:
notification = self._channel.wait()
if notification.name == 'SIPEngineDidEnd':
break
except api.TimeoutError:
pass
# stop threads
thread_manager = ThreadManager()
thread_manager.stop()
# stop the reactor
reactor.stop()
@classproperty
def running(cls):
return cls.state == 'started'
@classproperty
def alert_audio_mixer(cls):
return cls.alert_audio_bridge.mixer if cls.alert_audio_bridge else None
@classproperty
def voice_audio_mixer(cls):
return cls.voice_audio_bridge.mixer if cls.voice_audio_bridge else None
def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null)
handler(notification)
@run_in_twisted_thread
def _NH_SIPEngineDidEnd(self, notification):
self._channel.send(notification)
def _NH_SIPEngineDidFail(self, notification):
if not self.running:
return
self.end_reason = 'engine failed'
# this notification is always sent from the Engine's thread
reactor.callFromThread(reactor.stop)
def _NH_CFGSettingsObjectDidChange(self, notification):
engine = Engine()
settings = SIPSimpleSettings()
account_manager = AccountManager()
if notification.sender is settings:
if 'audio.sample_rate' in notification.data.modified:
alert_device = settings.audio.alert_device
- if alert_device not in (None, 'system_default') and alert_device not in engine.output_devices:
- alert_device = 'system_default'
+ if alert_device not in (None, u'system_default') and alert_device not in engine.output_devices:
+ alert_device = u'system_default'
input_device = settings.audio.input_device
- if input_device not in (None, 'system_default') and input_device not in engine.input_devices:
- input_device = 'system_default'
+ if input_device not in (None, u'system_default') and input_device not in engine.input_devices:
+ input_device = u'system_default'
output_device = settings.audio.output_device
- if output_device not in (None, 'system_default') and output_device not in engine.output_devices:
- output_device = 'system_default'
+ if output_device not in (None, u'system_default') and output_device not in engine.output_devices:
+ output_device = u'system_default'
try:
voice_mixer = AudioMixer(input_device, output_device, settings.audio.sample_rate, settings.audio.tail_length)
except SIPCoreError:
try:
- voice_mixer = AudioMixer('system_default', 'system_default', settings.audio.sample_rate, settings.audio.tail_length)
+ voice_mixer = AudioMixer(u'system_default', u'system_default', settings.audio.sample_rate, settings.audio.tail_length)
except SIPCoreError:
voice_mixer = AudioMixer(None, None, settings.audio.sample_rate, settings.audio.tail_length)
self.voice_audio_device = AudioDevice(voice_mixer)
self.voice_audio_bridge = RootAudioBridge(voice_mixer)
self.voice_audio_bridge.add(self.voice_audio_device)
try:
alert_mixer = AudioMixer(None, alert_device, settings.audio.sample_rate, 0)
except SIPCoreError:
try:
- alert_mixer = AudioMixer(None, 'system_default', settings.audio.sample_rate, 0)
+ alert_mixer = AudioMixer(None, u'system_default', settings.audio.sample_rate, 0)
except SIPCoreError:
alert_mixer = AudioMixer(None, None, settings.audio.sample_rate, 0)
self.alert_audio_device = AudioDevice(alert_mixer)
self.alert_audio_bridge = RootAudioBridge(alert_mixer)
self.alert_audio_bridge.add(self.alert_audio_device)
if settings.audio.silent:
alert_mixer.output_volume = 0
settings.audio.input_device = voice_mixer.input_device
settings.audio.output_device = voice_mixer.output_device
settings.audio.alert_device = alert_mixer.output_device
settings.save()
else:
if 'audio.input_device' in notification.data.modified or 'audio.output_device' in notification.data.modified or 'audio.tail_length' in notification.data.modified:
input_device = settings.audio.input_device
- if input_device not in (None, 'system_default') and input_device not in engine.input_devices:
- input_device = 'system_default'
+ if input_device not in (None, u'system_default') and input_device not in engine.input_devices:
+ input_device = u'system_default'
output_device = settings.audio.output_device
- if output_device not in (None, 'system_default') and output_device not in engine.output_devices:
- output_device = 'system_default'
+ if output_device not in (None, u'system_default') and output_device not in engine.output_devices:
+ output_device = u'system_default'
if input_device != self.voice_audio_bridge.mixer.input_device or output_device != self.voice_audio_bridge.mixer.output_device:
try:
self.voice_audio_bridge.mixer.set_sound_devices(input_device, output_device, settings.audio.tail_length)
except SIPCoreError:
try:
- self.voice_audio_bridge.mixer.set_sound_devices('system_default', 'system_default', settings.audio.tail_length)
+ self.voice_audio_bridge.mixer.set_sound_devices(u'system_default', u'system_default', settings.audio.tail_length)
except SIPCoreError:
self.voice_audio_bridge.mixer.set_sound_devices(None, None, settings.audio.tail_length)
settings.audio.input_device = self.voice_audio_bridge.mixer.input_device
settings.audio.output_device = self.voice_audio_bridge.mixer.output_device
settings.save()
if 'audio.alert_device' in notification.data.modified or 'audio.tail_length' in notification.data.modified:
alert_device = settings.audio.alert_device
- if alert_device not in (None, 'system_default') and alert_device not in engine.output_devices:
- alert_device = 'system_default'
+ if alert_device not in (None, u'system_default') and alert_device not in engine.output_devices:
+ alert_device = u'system_default'
if alert_device != self.alert_audio_bridge.mixer.output_device:
try:
self.alert_audio_bridge.mixer.set_sound_devices(None, alert_device, 0)
except SIPCoreError:
try:
- self.alert_audio_bridge.mixer.set_sound_devices(None, 'system_default', 0)
+ self.alert_audio_bridge.mixer.set_sound_devices(None, u'system_default', 0)
except SIPCoreError:
self.alert_audio_bridge.mixer.set_sound_devices(None, None, 0)
settings.audio.alert_device = self.alert_audio_bridge.mixer.output_device
settings.save()
if 'audio.silent' in notification.data.modified:
if settings.audio.silent:
self.alert_audio_bridge.mixer.output_volume = 0
else:
self.alert_audio_bridge.mixer.output_volume = 100
if 'user_agent' in notification.data.modified:
engine.user_agent = settings.user_agent
if 'sip.udp_port' in notification.data.modified:
engine.set_udp_port(settings.sip.udp_port)
if 'sip.tcp_port' in notification.data.modified:
engine.set_tcp_port(settings.sip.tcp_port)
if set(('sip.tls_port', 'tls.protocol', 'tls.ca_list', 'tls.timeout', 'default_account')).intersection(notification.data.modified):
account = account_manager.default_account
try:
engine.set_tls_options(port=settings.sip.tls_port,
protocol=settings.tls.protocol,
verify_server=account.tls.verify_server if account else False,
ca_file=settings.tls.ca_list.normalized if settings.tls.ca_list else None,
cert_file=account.tls.certificate.normalized if account and account.tls.certificate else None,
privkey_file=account.tls.certificate.normalized if account and account.tls.certificate else None,
timeout=settings.tls.timeout)
except Exception, e:
notification_center = NotificationCenter()
notification_center.post_notification('SIPApplicationFailedToStartTLS', sender=self, data=TimestampedNotificationData(error=e))
if 'rtp.port_range' in notification.data.modified:
engine.rtp_port_range = (settings.rtp.port_range.start, settings.rtp.port_range.end)
if 'rtp.audio_codec_list' in notification.data.modified:
engine.codecs = list(settings.rtp.audio_codec_list)
if 'logs.pjsip_level' in notification.data.modified:
engine.log_level = settings.logs.pjsip_level
elif notification.sender is account_manager.default_account:
if set(('tls.verify_server', 'tls.certificate')).intersection(notification.data.modified):
account = account_manager.default_account
try:
engine.set_tls_options(port=settings.sip.tls_port,
protocol=settings.tls.protocol,
verify_server=account.tls.verify_server,
ca_file=settings.tls.ca_list.normalized if settings.tls.ca_list else None,
cert_file=account.tls.certificate.normalized if account.tls.certificate else None,
privkey_file=account.tls.certificate.normalized if account.tls.certificate else None,
timeout=settings.tls.timeout)
except Exception, e:
notification_center = NotificationCenter()
notification_center.post_notification('SIPApplicationFailedToStartTLS', sender=self, data=TimestampedNotificationData(error=e))
def _NH_DefaultAudioDeviceDidChange(self, notification):
settings = SIPSimpleSettings()
current_input_device = self.voice_audio_bridge.mixer.input_device
current_output_device = self.voice_audio_bridge.mixer.output_device
current_alert_device = self.alert_audio_bridge.mixer.output_device
ec_tail_length = self.voice_audio_bridge.mixer.ec_tail_length
- if notification.data.changed_input and 'system_default' in (current_input_device, settings.audio.input_device):
+ if notification.data.changed_input and u'system_default' in (current_input_device, settings.audio.input_device):
try:
- self.voice_audio_bridge.mixer.set_sound_devices('system_default', current_output_device, ec_tail_length)
+ self.voice_audio_bridge.mixer.set_sound_devices(u'system_default', current_output_device, ec_tail_length)
except SIPCoreError:
self.voice_audio_bridge.mixer.set_sound_devices(None, None, ec_tail_length)
- if notification.data.changed_output and 'system_default' in (current_output_device, settings.audio.output_device):
+ if notification.data.changed_output and u'system_default' in (current_output_device, settings.audio.output_device):
try:
- self.voice_audio_bridge.mixer.set_sound_devices(current_input_device, 'system_default', ec_tail_length)
+ self.voice_audio_bridge.mixer.set_sound_devices(current_input_device, u'system_default', ec_tail_length)
except SIPCoreError:
self.voice_audio_bridge.mixer.set_sound_devices(None, None, ec_tail_length)
- if notification.data.changed_output and 'system_default' in (current_alert_device, settings.audio.alert_device):
+ if notification.data.changed_output and u'system_default' in (current_alert_device, settings.audio.alert_device):
try:
- self.alert_audio_bridge.mixer.set_sound_devices(None, 'system_default', 0)
+ self.alert_audio_bridge.mixer.set_sound_devices(None, u'system_default', 0)
except SIPCoreError:
self.alert_audio_bridge.mixer.set_sound_devices(None, None, 0)
def _NH_AudioDevicesDidChange(self, notification):
old_devices = set(notification.data.old_devices)
new_devices = set(notification.data.new_devices)
removed_devices = old_devices - new_devices
input_device = self.voice_audio_bridge.mixer.input_device
output_device = self.voice_audio_bridge.mixer.output_device
alert_device = self.alert_audio_bridge.mixer.output_device
if self.voice_audio_bridge.mixer.real_input_device in removed_devices:
- input_device = 'system_default' if new_devices else None
+ input_device = u'system_default' if new_devices else None
if self.voice_audio_bridge.mixer.real_output_device in removed_devices:
- output_device = 'system_default' if new_devices else None
+ output_device = u'system_default' if new_devices else None
if self.alert_audio_bridge.mixer.real_output_device in removed_devices:
- alert_device = 'system_default' if new_devices else None
+ alert_device = u'system_default' if new_devices else None
try:
self.voice_audio_bridge.mixer.set_sound_devices(input_device, output_device, self.voice_audio_bridge.mixer.ec_tail_length)
except SIPCoreError:
try:
- self.voice_audio_bridge.mixer.set_sound_devices('system_default', 'system_default', self.voice_audio_bridge.mixer.ec_tail_length)
+ self.voice_audio_bridge.mixer.set_sound_devices(u'system_default', u'system_default', self.voice_audio_bridge.mixer.ec_tail_length)
except SIPCoreError:
self.voice_audio_bridge.mixer.set_sound_devices(None, None, self.voice_audio_bridge.mixer.ec_tail_length)
try:
self.alert_audio_bridge.mixer.set_sound_devices(None, alert_device, 0)
except SIPCoreError:
try:
- self.alert_audio_bridge.mixer.set_sound_devices(None, 'system_default', 0)
+ self.alert_audio_bridge.mixer.set_sound_devices(None, u'system_default', 0)
except SIPCoreError:
self.alert_audio_bridge.mixer.set_sound_devices(None, None, 0)
settings = SIPSimpleSettings()
settings.audio.input_device = self.voice_audio_bridge.mixer.input_device
settings.audio.output_device = self.voice_audio_bridge.mixer.output_device
settings.audio.alert_device = self.alert_audio_bridge.mixer.output_device
settings.save()
@run_in_green_thread
def _detect_nat_type(self):
account_manager = AccountManager()
engine = Engine()
lookup = DNSLookup()
serial = 0
while True:
restart_detection = False
command = self._nat_detect_channel.wait()
if command.name != 'detect_nat':
continue
for account in (account for account in account_manager.iter_accounts() if isinstance(account, Account)):
if account.nat_traversal.stun_server_list:
stun_servers = []
for server in account.nat_traversal.stun_server_list:
try:
servers = lookup.lookup_service(SIPURI(host=server.host, port=server.port), 'stun').wait()
stun_servers.extend(servers)
except DNSLookupError:
continue
else:
try:
stun_servers = lookup.lookup_service(SIPURI(host=account.id.domain), 'stun').wait()
except DNSLookupError:
continue
for stun_server, stun_port in stun_servers:
serial += 1
engine.detect_nat_type(stun_server, stun_port, user_data=serial)
command = self._nat_detect_channel.wait()
if command.name == 'process_nat_detection' and command.data.user_data == serial and command.data.succeeded:
self.local_nat_type = command.data.nat_type.lower()
restart_detection = True
break
elif command.name == 'detect_nat':
self._nat_detect_channel.send(command)
restart_detection = True
break
if restart_detection:
break
else:
self.local_nat_type = 'unknown'
reactor.callLater(60, self._nat_detect_channel.send, Command('detect_nat'))
def _NH_DNSNameserversDidChange(self, notification):
if self.running:
self._nat_detect_channel.send(Command('detect_nat'))
def _NH_SystemIPAddressDidChange(self, notification):
if self.running:
self._nat_detect_channel.send(Command('detect_nat'))
def _NH_SystemDidWakeUpFromSleep(self, notification):
if self.running and self._wakeup_timer is None:
def wakeup_action():
if self.running:
self._nat_detect_channel.send(Command('detect_nat'))
self._wakeup_timer = None
self._wakeup_timer = reactor.callLater(5, wakeup_action) # wait for system to stabilize
def _NH_SIPEngineDetectedNATType(self, notification):
self._nat_detect_channel.send(Command('process_nat_detection', data=notification.data))
diff --git a/sipsimple/configuration/settings.py b/sipsimple/configuration/settings.py
index 74adae7b..0a7fbee4 100644
--- a/sipsimple/configuration/settings.py
+++ b/sipsimple/configuration/settings.py
@@ -1,89 +1,89 @@
# Copyright (C) 2008-2010 AG Projects. See LICENSE for details.
#
"""
SIP SIMPLE settings.
Definition of general (non-account related) settings.
"""
from application.python.util import Singleton
from sipsimple import __version__
from sipsimple.configuration import CorrelatedSetting, Setting, SettingsGroup, SettingsObject
from sipsimple.configuration.datatypes import NonNegativeInteger, PJSIPLogLevel
from sipsimple.configuration.datatypes import AudioCodecList, SampleRate
from sipsimple.configuration.datatypes import Port, PortRange, SIPTransportList, TLSProtocol
from sipsimple.configuration.datatypes import Path
__all__ = ['SIPSimpleSettings']
class AudioSettings(SettingsGroup):
- alert_device = Setting(type=str, default='system_default', nillable=True)
- input_device = Setting(type=str, default='system_default', nillable=True)
- output_device = Setting(type=str, default='system_default', nillable=True)
+ alert_device = Setting(type=unicode, default=u'system_default', nillable=True)
+ input_device = Setting(type=unicode, default=u'system_default', nillable=True)
+ output_device = Setting(type=unicode, default=u'system_default', nillable=True)
tail_length = Setting(type=NonNegativeInteger, default=200)
sample_rate = Setting(type=SampleRate, default=44100)
silent = Setting(type=bool, default=False)
class ChatSettings(SettingsGroup):
pass
class DesktopSharingSettings(SettingsGroup):
pass
class FileTransferSettings(SettingsGroup):
pass
class LogsSettings(SettingsGroup):
pjsip_level = Setting(type=PJSIPLogLevel, default=5)
class RTPSettings(SettingsGroup):
port_range = Setting(type=PortRange, default=PortRange(50000, 50500))
timeout = Setting(type=NonNegativeInteger, default=30)
audio_codec_list = Setting(type=AudioCodecList, default=AudioCodecList(('G722', 'speex', 'PCMU', 'PCMA')))
def sip_port_validator(port, sibling_port):
if port == sibling_port != 0:
raise ValueError("the TCP and TLS ports must be different")
class SIPSettings(SettingsGroup):
invite_timeout = Setting(type=NonNegativeInteger, default=90, nillable=True)
udp_port = Setting(type=Port, default=0)
tcp_port = CorrelatedSetting(type=Port, sibling='tls_port', validator=sip_port_validator, default=0)
tls_port = CorrelatedSetting(type=Port, sibling='tcp_port', validator=sip_port_validator, default=0)
transport_list = Setting(type=SIPTransportList, default=SIPTransportList(('tls', 'tcp', 'udp')))
class TLSSettings(SettingsGroup):
ca_list = Setting(type=Path, default=None, nillable=True)
protocol = Setting(type=TLSProtocol, default='TLSv1')
timeout = Setting(type=NonNegativeInteger, default=3000)
class SIPSimpleSettings(SettingsObject):
__metaclass__ = Singleton
__id__ = 'SIPSimpleSettings'
default_account = Setting(type=str, default='bonjour@local', nillable=True)
user_agent = Setting(type=str, default='sipsimple %s' % __version__)
audio = AudioSettings
chat = ChatSettings
desktop_sharing = DesktopSharingSettings
file_transfer = FileTransferSettings
logs = LogsSettings
rtp = RTPSettings
sip = SIPSettings
tls = TLSSettings
diff --git a/sipsimple/core/__init__.py b/sipsimple/core/__init__.py
index 992567d8..58da7f3f 100644
--- a/sipsimple/core/__init__.py
+++ b/sipsimple/core/__init__.py
@@ -1,13 +1,13 @@
# Copyright (C) 2010 AG Projects. See LICENSE for details.
#
from sipsimple.core._core import *
from sipsimple.core._engine import *
from sipsimple.core._primitives import *
-required_revision = 128
+required_revision = 129
if CORE_REVISION != required_revision:
raise ImportError("Wrong SIP core revision %d (expected %d)" % (CORE_REVISION, required_revision))
del required_revision
diff --git a/sipsimple/core/_core.pxd b/sipsimple/core/_core.pxd
index d843a815..2d28c542 100644
--- a/sipsimple/core/_core.pxd
+++ b/sipsimple/core/_core.pxd
@@ -1,1894 +1,1894 @@
# Copyright (C) 2008-2010 AG Projects. See LICENSE for details.
#
cdef extern from *:
ctypedef char *char_ptr_const "const char *"
enum:
PJ_SVN_REV "PJ_SVN_REVISION"
# system imports
cdef extern from "stdlib.h":
void *malloc(int size)
void free(void *ptr)
cdef extern from "string.h":
void *memcpy(void *s1, void *s2, int n)
# Python C imports
cdef extern from "Python.h":
void Py_INCREF(object obj)
void Py_DECREF(object obj)
object PyString_FromStringAndSize(char *v, int len)
char* PyString_AsString(object string) except NULL
void* PyLong_AsVoidPtr(object)
object PyLong_FromVoidPtr(void*)
double PyFloat_AsDouble(object)
void PyEval_InitThreads()
cdef extern from "stringobject.h":
ctypedef class __builtin__.str [object PyStringObject]:
pass
cdef extern from "dictobject.h":
ctypedef class __builtin__.dict [object PyDictObject]:
pass
cdef extern from "listobject.h":
ctypedef class __builtin__.list [object PyListObject]:
pass
# PJSIP imports
cdef extern from "pjlib.h":
# constants
enum:
PJ_ERR_MSG_SIZE
enum:
PJ_ERRNO_START_SYS
PJ_EBUG
PJ_ETOOMANY
enum:
PJ_MAX_OBJ_NAME
# init / shutdown
int pj_init() nogil
void pj_shutdown() nogil
# version
char *pj_get_version() nogil
# string
struct pj_str_t:
char *ptr
int slen
# errors
pj_str_t pj_strerror(int statcode, char *buf, int bufsize) nogil
# logging
enum:
PJ_LOG_MAX_LEVEL
enum pj_log_decoration:
PJ_LOG_HAS_YEAR
PJ_LOG_HAS_MONTH
PJ_LOG_HAS_DAY_OF_MON
PJ_LOG_HAS_TIME
PJ_LOG_HAS_MICRO_SEC
PJ_LOG_HAS_SENDER
void pj_log_set_decor(int decor) nogil
int pj_log_get_level() nogil
void pj_log_set_level(int level) nogil
void pj_log_set_log_func(void func(int level, char_ptr_const data, int len)) nogil
# memory management
struct pj_pool_t
struct pj_pool_factory_policy:
pass
pj_pool_factory_policy pj_pool_factory_default_policy
struct pj_pool_factory:
pass
struct pj_caching_pool:
pj_pool_factory factory
void pj_caching_pool_init(pj_caching_pool *ch_pool, pj_pool_factory_policy *policy, int max_capacity) nogil
void pj_caching_pool_destroy(pj_caching_pool *ch_pool) nogil
void *pj_pool_alloc(pj_pool_t *pool, int size) nogil
pj_pool_t *pj_pool_create_on_buf(char *name, void *buf, int size) nogil
pj_str_t *pj_strdup2_with_null(pj_pool_t *pool, pj_str_t *dst, char *src) nogil
# threads
enum:
PJ_THREAD_DESC_SIZE
struct pj_mutex_t
struct pj_rwmutex_t
struct pj_thread_t
int pj_mutex_create_simple(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil
int pj_mutex_create_recursive(pj_pool_t *pool, char *name, pj_mutex_t **mutex) nogil
int pj_mutex_lock(pj_mutex_t *mutex) nogil
int pj_mutex_unlock(pj_mutex_t *mutex) nogil
int pj_mutex_destroy(pj_mutex_t *mutex) nogil
int pj_rwmutex_create(pj_pool_t *pool, char *name, pj_rwmutex_t **mutex) nogil
int pj_rwmutex_lock_read(pj_rwmutex_t *mutex) nogil
int pj_rwmutex_lock_write(pj_rwmutex_t *mutex) nogil
int pj_rwmutex_unlock_read(pj_rwmutex_t *mutex) nogil
int pj_rwmutex_unlock_write(pj_rwmutex_t *mutex) nogil
int pj_rwmutex_destroy(pj_rwmutex_t *mutex) nogil
int pj_thread_is_registered() nogil
int pj_thread_register(char *thread_name, long *thread_desc, pj_thread_t **thread) nogil
# sockets
enum:
PJ_INET6_ADDRSTRLEN
struct pj_ioqueue_t
struct pj_addr_hdr:
unsigned int sa_family
struct pj_sockaddr:
pj_addr_hdr addr
struct pj_sockaddr_in:
pass
int pj_AF_INET() nogil
int pj_AF_INET6() nogil
int pj_sockaddr_in_init(pj_sockaddr_in *addr, pj_str_t *cp, int port) nogil
int pj_sockaddr_get_port(pj_sockaddr *addr) nogil
char *pj_sockaddr_print(pj_sockaddr *addr, char *buf, int size, unsigned int flags) nogil
int pj_sockaddr_has_addr(pj_sockaddr *addr) nogil
int pj_sockaddr_init(int af, pj_sockaddr *addr, pj_str_t *cp, unsigned int port) nogil
int pj_inet_pton(int af, pj_str_t *src, void *dst) nogil
# dns
struct pj_dns_resolver
# time
struct pj_time_val:
long sec
long msec
void pj_gettimeofday(pj_time_val *tv) nogil
# timers
struct pj_timer_heap_t
struct pj_timer_entry:
void *user_data
int id
pj_timer_entry *pj_timer_entry_init(pj_timer_entry *entry, int id, void *user_data,
void cb(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil) nogil
# lists
struct pj_list:
void *prev
void *next
void pj_list_init(pj_list *node) nogil
void pj_list_insert_after(pj_list *pos, pj_list *node) nogil
# random
void pj_srand(unsigned int seed) nogil
# maths
struct pj_math_stat:
int n
int max
int min
int last
int mean
cdef extern from "pjlib-util.h":
# init
int pjlib_util_init() nogil
cdef extern from "pjnath.h":
# init
int pjnath_init() nogil
# STUN
enum:
PJ_STUN_PORT
struct pj_stun_config:
pass
struct pj_stun_sock_cfg:
pj_sockaddr bound_addr
void pj_stun_config_init(pj_stun_config *cfg, pj_pool_factory *factory, unsigned int options,
pj_ioqueue_t *ioqueue, pj_timer_heap_t *timer_heap) nogil
# NAT detection
struct pj_stun_nat_detect_result:
int status
char *status_text
char *nat_type_name
ctypedef pj_stun_nat_detect_result *pj_stun_nat_detect_result_ptr_const "const pj_stun_nat_detect_result *"
int pj_stun_detect_nat_type(pj_sockaddr_in *server, pj_stun_config *stun_cfg, void *user_data,
void pj_stun_nat_detect_cb(void *user_data,
pj_stun_nat_detect_result_ptr_const res) with gil) nogil
# ICE
struct pj_ice_strans_cfg_stun:
pj_stun_sock_cfg cfg
pj_str_t server
unsigned int port
struct pj_ice_strans_cfg:
int af
pj_stun_config stun_cfg
pj_ice_strans_cfg_stun stun
enum pj_ice_strans_op:
PJ_ICE_STRANS_OP_INIT
PJ_ICE_STRANS_OP_NEGOTIATION
void pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) nogil
struct pj_ice_candidate_pair:
char local_type[8]
char local_ip[64]
char remote_type[8]
char remote_ip[64]
cdef extern from "pjmedia.h":
enum:
PJMEDIA_ENOSNDREC
PJMEDIA_ENOSNDPLAY
# codec manager
struct pjmedia_codec_mgr
enum:
PJMEDIA_CODEC_MGR_MAX_CODECS
struct pjmedia_codec_info:
pj_str_t encoding_name
unsigned int clock_rate
unsigned int channel_cnt
int pjmedia_codec_mgr_enum_codecs(pjmedia_codec_mgr *mgr, unsigned int *count,
pjmedia_codec_info *info, unsigned int *prio) nogil
int pjmedia_codec_mgr_set_codec_priority(pjmedia_codec_mgr *mgr, pj_str_t *codec_id, unsigned int prio) nogil
# endpoint
struct pjmedia_endpt
int pjmedia_endpt_create(pj_pool_factory *pf, pj_ioqueue_t *ioqueue, int worker_cnt, pjmedia_endpt **p_endpt) nogil
int pjmedia_endpt_destroy(pjmedia_endpt *endpt) nogil
pj_ioqueue_t *pjmedia_endpt_get_ioqueue(pjmedia_endpt *endpt) nogil
pjmedia_codec_mgr *pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt) nogil
# codecs
int pjmedia_codec_g711_init(pjmedia_endpt *endpt) nogil
int pjmedia_codec_g711_deinit() nogil
# sound devices
struct pjmedia_snd_dev_info:
char *name
int input_count
int output_count
struct pjmedia_snd_stream_info:
int play_id
int rec_id
struct pjmedia_snd_stream
ctypedef pjmedia_snd_dev_info *pjmedia_snd_dev_info_ptr_const "const pjmedia_snd_dev_info *"
ctypedef void (*audio_change_callback) (void *user_data)
enum audio_change_type:
AUDIO_CHANGE_INPUT = 1
AUDIO_CHANGE_OUTPUT = 2
struct pjmedia_audio_change_observer:
audio_change_callback default_audio_change
audio_change_callback audio_devices_will_change
audio_change_callback audio_devices_did_change
int pjmedia_add_audio_change_observer(pjmedia_audio_change_observer *audio_change_observer)
int pjmedia_del_audio_change_observer(pjmedia_audio_change_observer *audio_change_observer)
int pjmedia_snd_get_dev_count() nogil
pjmedia_snd_dev_info_ptr_const pjmedia_snd_get_dev_info(int index) nogil
int pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, pjmedia_snd_stream_info *pi) nogil
# sound port
struct pjmedia_port
struct pjmedia_snd_port
int pjmedia_snd_port_create(pj_pool_t *pool, int rec_id, int play_id, unsigned int clock_rate,
unsigned int channel_count, unsigned int samples_per_frame,
unsigned int bits_per_sample, unsigned int options, pjmedia_snd_port **p_port) nogil
int pjmedia_snd_port_create_rec(pj_pool_t *pool, int index, unsigned int clock_rate, unsigned int channel_count,
unsigned int samples_per_frame, unsigned int bits_per_sample, unsigned int options,
pjmedia_snd_port **p_port) nogil
int pjmedia_snd_port_create_player(pj_pool_t *pool, unsigned int index, unsigned int clock_rate,
unsigned int channel_count, unsigned int samples_per_frame,
unsigned int bits_per_sample, unsigned int options, pjmedia_snd_port **p_port) nogil
int pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port) nogil
int pjmedia_snd_port_disconnect(pjmedia_snd_port *snd_port) nogil
int pjmedia_snd_port_set_ec(pjmedia_snd_port *snd_port, pj_pool_t *pool, unsigned int tail_ms, int options) nogil
int pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port) nogil
pjmedia_snd_stream *pjmedia_snd_port_get_snd_stream(pjmedia_snd_port *snd_port) nogil
int pjmedia_null_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count,
unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil
int pjmedia_mixer_port_create(pj_pool_t *pool, unsigned int sampling_rate, unsigned int channel_count,
unsigned int samples_per_frame, unsigned int bits_per_sample, pjmedia_port **p_port) nogil
# master port
struct pjmedia_master_port
int pjmedia_master_port_create(pj_pool_t *pool, pjmedia_port *u_port, pjmedia_port *d_port,
unsigned int options, pjmedia_master_port **p_m) nogil
int pjmedia_master_port_start(pjmedia_master_port *m) nogil
int pjmedia_master_port_destroy(pjmedia_master_port *m, int destroy_ports) nogil
# conference bridge
enum pjmedia_conf_option:
PJMEDIA_CONF_NO_DEVICE
struct pjmedia_conf
int pjmedia_conf_create(pj_pool_t *pool, int max_slots, int sampling_rate, int channel_count,
int samples_per_frame, int bits_per_sample, int options, pjmedia_conf **p_conf) nogil
int pjmedia_conf_destroy(pjmedia_conf *conf) nogil
pjmedia_port *pjmedia_conf_get_master_port(pjmedia_conf *conf) nogil
int pjmedia_conf_add_port(pjmedia_conf *conf, pj_pool_t *pool, pjmedia_port *strm_port,
pj_str_t *name, unsigned int *p_slot) nogil
int pjmedia_conf_remove_port(pjmedia_conf *conf, unsigned int slot) nogil
int pjmedia_conf_connect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot, int level) nogil
int pjmedia_conf_disconnect_port(pjmedia_conf *conf, unsigned int src_slot, unsigned int sink_slot) nogil
int pjmedia_conf_adjust_rx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil
int pjmedia_conf_adjust_tx_level(pjmedia_conf *conf, unsigned slot, int adj_level) nogil
# sdp
enum:
PJMEDIA_MAX_SDP_FMT
enum:
PJMEDIA_MAX_SDP_ATTR
enum:
PJMEDIA_MAX_SDP_MEDIA
struct pjmedia_sdp_attr:
pj_str_t name
pj_str_t value
struct pjmedia_sdp_conn:
pj_str_t net_type
pj_str_t addr_type
pj_str_t addr
struct pjmedia_sdp_media_desc:
pj_str_t media
unsigned int port
unsigned int port_count
pj_str_t transport
unsigned int fmt_count
pj_str_t fmt[PJMEDIA_MAX_SDP_FMT]
struct pjmedia_sdp_media:
pjmedia_sdp_media_desc desc
pj_str_t info
pjmedia_sdp_conn *conn
unsigned int attr_count
pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR]
struct pjmedia_sdp_session_origin:
pj_str_t user
unsigned int id
unsigned int version
pj_str_t net_type
pj_str_t addr_type
pj_str_t addr
struct pjmedia_sdp_session_time:
unsigned int start
unsigned int stop
struct pjmedia_sdp_session:
pjmedia_sdp_session_origin origin
pj_str_t name
pj_str_t info
pjmedia_sdp_conn *conn
pjmedia_sdp_session_time time
unsigned int attr_count
pjmedia_sdp_attr *attr[PJMEDIA_MAX_SDP_ATTR]
unsigned int media_count
pjmedia_sdp_media *media[PJMEDIA_MAX_SDP_MEDIA]
ctypedef pjmedia_sdp_session *pjmedia_sdp_session_ptr_const "const pjmedia_sdp_session *"
pjmedia_sdp_media *pjmedia_sdp_media_clone(pj_pool_t *pool, pjmedia_sdp_media *rhs) nogil
# sdp negotiation
enum pjmedia_sdp_neg_state:
PJMEDIA_SDP_NEG_STATE_NULL
PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER
PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER
PJMEDIA_SDP_NEG_STATE_WAIT_NEGO
PJMEDIA_SDP_NEG_STATE_DONE
struct pjmedia_sdp_neg
int pjmedia_sdp_neg_get_neg_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil
int pjmedia_sdp_neg_get_neg_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil
int pjmedia_sdp_neg_get_active_remote(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *remote) nogil
int pjmedia_sdp_neg_get_active_local(pjmedia_sdp_neg *neg, pjmedia_sdp_session_ptr_const *local) nogil
int pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg) nogil
int pjmedia_sdp_neg_cancel_remote_offer(pjmedia_sdp_neg *neg) nogil
pjmedia_sdp_neg_state pjmedia_sdp_neg_get_state(pjmedia_sdp_neg *neg) nogil
char *pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state) nogil
# transport
enum pjmedia_transport_type:
PJMEDIA_TRANSPORT_TYPE_ICE
struct pjmedia_sock_info:
pj_sockaddr rtp_addr_name
struct pjmedia_transport:
char *name
pjmedia_transport_type type
enum pjmedia_transport_type:
PJMEDIA_TRANSPORT_TYPE_SRTP
struct pjmedia_transport_specific_info:
pjmedia_transport_type type
char *buffer
struct pjmedia_transport_info:
pjmedia_sock_info sock_info
pj_sockaddr src_rtp_name
int specific_info_cnt
pjmedia_transport_specific_info *spc_info
struct pjmedia_srtp_info:
int active
void pjmedia_transport_info_init(pjmedia_transport_info *info) nogil
int pjmedia_transport_udp_create3(pjmedia_endpt *endpt, int af, char *name, pj_str_t *addr, int port,
unsigned int options, pjmedia_transport **p_tp) nogil
int pjmedia_transport_get_info(pjmedia_transport *tp, pjmedia_transport_info *info) nogil
int pjmedia_transport_close(pjmedia_transport *tp) nogil
int pjmedia_transport_media_create(pjmedia_transport *tp, pj_pool_t *sdp_pool, unsigned int options,
pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil
int pjmedia_transport_encode_sdp(pjmedia_transport *tp, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp,
pjmedia_sdp_session *rem_sdp, unsigned int media_index) nogil
int pjmedia_transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, pjmedia_sdp_session *sdp_local,
pjmedia_sdp_session *sdp_remote, unsigned int media_index) nogil
int pjmedia_transport_media_stop(pjmedia_transport *tp) nogil
int pjmedia_endpt_create_sdp(pjmedia_endpt *endpt, pj_pool_t *pool, unsigned int stream_cnt,
pjmedia_sock_info *sock_info, pjmedia_sdp_session **p_sdp) nogil
# SRTP
enum pjmedia_srtp_use:
PJMEDIA_SRTP_MANDATORY
struct pjmedia_srtp_setting:
pjmedia_srtp_use use
void pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt) nogil
int pjmedia_transport_srtp_create(pjmedia_endpt *endpt, pjmedia_transport *tp,
pjmedia_srtp_setting *opt, pjmedia_transport **p_tp) nogil
# ICE
struct pjmedia_ice_cb:
void on_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil
void on_ice_candidates_chosen(pjmedia_transport *tp, int status, pj_ice_candidate_pair rtp_pair, pj_ice_candidate_pair rtcp_pair, char *duration, char *local_candidates, char *remote_candidates, char *valid_list) with gil
void on_ice_failure(pjmedia_transport *tp, char *reason) with gil
void on_ice_state(pjmedia_transport *tp, char *state) with gil
int pjmedia_ice_create2(pjmedia_endpt *endpt, char *name, unsigned int comp_cnt, pj_ice_strans_cfg *cfg,
pjmedia_ice_cb *cb, unsigned int options, pjmedia_transport **p_tp) nogil
# stream
enum pjmedia_dir:
PJMEDIA_DIR_ENCODING
PJMEDIA_DIR_DECODING
struct pjmedia_codec_param_setting:
unsigned int vad
struct pjmedia_codec_param:
pjmedia_codec_param_setting setting
struct pjmedia_stream_info:
pjmedia_codec_info fmt
pjmedia_codec_param *param
unsigned int tx_event_pt
struct pjmedia_rtcp_stream_stat_loss_type:
unsigned int burst
unsigned int random
struct pjmedia_rtcp_stream_stat:
unsigned int pkt
unsigned int bytes
unsigned int discard
unsigned int loss
unsigned int reorder
unsigned int dup
pj_math_stat loss_period
pjmedia_rtcp_stream_stat_loss_type loss_type
pj_math_stat jitter
struct pjmedia_rtcp_stat:
pjmedia_rtcp_stream_stat tx
pjmedia_rtcp_stream_stat rx
pj_math_stat rtt
struct pjmedia_stream
int pjmedia_stream_info_from_sdp(pjmedia_stream_info *si, pj_pool_t *pool, pjmedia_endpt *endpt,
pjmedia_sdp_session *local, pjmedia_sdp_session *remote, unsigned int stream_idx) nogil
int pjmedia_stream_create(pjmedia_endpt *endpt, pj_pool_t *pool, pjmedia_stream_info *info,
pjmedia_transport *tp, void *user_data, pjmedia_stream **p_stream) nogil
int pjmedia_stream_destroy(pjmedia_stream *stream) nogil
int pjmedia_stream_get_port(pjmedia_stream *stream, pjmedia_port **p_port) nogil
int pjmedia_stream_start(pjmedia_stream *stream) nogil
int pjmedia_stream_dial_dtmf(pjmedia_stream *stream, pj_str_t *ascii_digit) nogil
int pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,
void cb(pjmedia_stream *stream, void *user_data, int digit) with gil,
void *user_data) nogil
int pjmedia_stream_pause(pjmedia_stream *stream, pjmedia_dir dir) nogil
int pjmedia_stream_resume(pjmedia_stream *stream, pjmedia_dir dir) nogil
int pjmedia_stream_get_stat(pjmedia_stream *stream, pjmedia_rtcp_stat *stat) nogil
# wav player
enum:
PJMEDIA_FILE_NO_LOOP
int pjmedia_port_destroy(pjmedia_port *port) nogil
int pjmedia_wav_player_port_create(pj_pool_t *pool, char *filename, unsigned int ptime, unsigned int flags,
unsigned int buff_size, pjmedia_port **p_port) nogil
int pjmedia_wav_player_set_eof_cb(pjmedia_port *port, void *user_data,
int cb(pjmedia_port *port, void *usr_data) with gil) nogil
int pjmedia_wav_player_port_set_pos(pjmedia_port *port, unsigned int offset) nogil
# wav recorder
enum pjmedia_file_writer_option:
PJMEDIA_FILE_WRITE_PCM
int pjmedia_wav_writer_port_create(pj_pool_t *pool, char *filename, unsigned int clock_rate,
unsigned int channel_count, unsigned int samples_per_frame,
unsigned int bits_per_sample, unsigned int flags, int buff_size,
pjmedia_port **p_port) nogil
# tone generator
enum:
PJMEDIA_TONEGEN_MAX_DIGITS
struct pjmedia_tone_desc:
short freq1
short freq2
short on_msec
short off_msec
short volume
short flags
struct pjmedia_tone_digit:
char digit
short on_msec
short off_msec
short volume
int pjmedia_tonegen_create(pj_pool_t *pool, unsigned int clock_rate, unsigned int channel_count,
unsigned int samples_per_frame, unsigned int bits_per_sample,
unsigned int options, pjmedia_port **p_port) nogil
int pjmedia_tonegen_play(pjmedia_port *tonegen, unsigned int count, pjmedia_tone_desc *tones, unsigned int options) nogil
int pjmedia_tonegen_play_digits(pjmedia_port *tonegen, unsigned int count,
pjmedia_tone_digit *digits, unsigned int options) nogil
int pjmedia_tonegen_stop(pjmedia_port *tonegen) nogil
int pjmedia_tonegen_is_busy(pjmedia_port *tonegen) nogil
cdef extern from "pjmedia-codec.h":
# codecs
enum:
PJMEDIA_SPEEX_NO_UWB
PJMEDIA_SPEEX_NO_WB
int pjmedia_codec_gsm_init(pjmedia_endpt *endpt) nogil
int pjmedia_codec_gsm_deinit() nogil
int pjmedia_codec_g722_init(pjmedia_endpt *endpt) nogil
int pjmedia_codec_g722_deinit() nogil
int pjmedia_codec_ilbc_init(pjmedia_endpt *endpt, int mode) nogil
int pjmedia_codec_ilbc_deinit() nogil
int pjmedia_codec_speex_init(pjmedia_endpt *endpt, int options, int quality, int complexity) nogil
int pjmedia_codec_speex_deinit() nogil
cdef extern from "pjsip.h":
# messages
enum pjsip_status_code:
PJSIP_SC_TSX_TIMEOUT
PJSIP_SC_TSX_TRANSPORT_ERROR
PJSIP_TLS_EUNKNOWN
PJSIP_TLS_EINVMETHOD
PJSIP_TLS_ECACERT
PJSIP_TLS_ECERTFILE
PJSIP_TLS_EKEYFILE
PJSIP_TLS_ECIPHER
PJSIP_TLS_ECTX
struct pjsip_transport
enum pjsip_uri_context_e:
PJSIP_URI_IN_CONTACT_HDR
struct pjsip_param:
pj_str_t name
pj_str_t value
struct pjsip_uri
struct pjsip_sip_uri:
pj_str_t user
pj_str_t passwd
pj_str_t host
int port
pj_str_t user_param
pj_str_t method_param
pj_str_t transport_param
int ttl_param
int lr_param
pj_str_t maddr_param
pjsip_param other_param
pjsip_param header_param
struct pjsip_name_addr:
pj_str_t display
pjsip_uri *uri
struct pjsip_media_type:
pj_str_t type
pj_str_t subtype
pj_str_t param
enum pjsip_method_e:
PJSIP_CANCEL_METHOD
PJSIP_OTHER_METHOD
struct pjsip_method:
pjsip_method_e id
pj_str_t name
struct pjsip_host_port:
pj_str_t host
int port
enum pjsip_hdr_e:
PJSIP_H_VIA
PJSIP_H_CALL_ID
PJSIP_H_CSEQ
PJSIP_H_EXPIRES
PJSIP_H_FROM
struct pjsip_hdr:
pjsip_hdr_e type
pj_str_t name
ctypedef pjsip_hdr *pjsip_hdr_ptr_const "const pjsip_hdr*"
struct pjsip_generic_array_hdr:
unsigned int count
pj_str_t *values
struct pjsip_generic_string_hdr:
pj_str_t name
pj_str_t hvalue
struct pjsip_cid_hdr:
pj_str_t id
struct pjsip_contact_hdr:
int star
pjsip_uri *uri
int q1000
int expires
pjsip_param other_param
struct pjsip_clen_hdr:
int len
struct pjsip_ctype_hdr:
pjsip_media_type media
struct pjsip_cseq_hdr:
int cseq
pjsip_method method
struct pjsip_generic_int_hdr:
int ivalue
ctypedef pjsip_generic_int_hdr pjsip_expires_hdr
struct pjsip_fromto_hdr:
pjsip_uri *uri
pj_str_t tag
pjsip_param other_param
struct pjsip_routing_hdr:
pjsip_name_addr name_addr
pjsip_param other_param
ctypedef pjsip_routing_hdr pjsip_route_hdr
struct pjsip_retry_after_hdr:
int ivalue
pjsip_param param
pj_str_t comment
struct pjsip_via_hdr:
pj_str_t transport
pjsip_host_port sent_by
int ttl_param
int rport_param
pj_str_t maddr_param
pj_str_t recvd_param
pj_str_t branch_param
pjsip_param other_param
pj_str_t comment
enum:
PJSIP_MAX_ACCEPT_COUNT
struct pjsip_msg_body:
pjsip_media_type content_type
void *data
unsigned int len
struct pjsip_request_line:
pjsip_method method
pjsip_uri *uri
struct pjsip_status_line:
int code
pj_str_t reason
union pjsip_msg_line:
pjsip_request_line req
pjsip_status_line status
enum pjsip_msg_type_e:
PJSIP_REQUEST_MSG
PJSIP_RESPONSE_MSG
struct pjsip_msg:
pjsip_msg_type_e type
pjsip_msg_line line
pjsip_hdr hdr
pjsip_msg_body *body
struct pjsip_buffer:
char *start
char *cur
struct pjsip_tx_data_tp_info:
char *dst_name
int dst_port
pjsip_transport *transport
struct pjsip_tx_data:
pjsip_msg *msg
pj_pool_t *pool
pjsip_buffer buf
pjsip_tx_data_tp_info tp_info
struct pjsip_rx_data_tp_info:
pj_pool_t *pool
pjsip_transport *transport
struct pjsip_rx_data_pkt_info:
pj_time_val timestamp
char *packet
int len
char *src_name
int src_port
struct pjsip_rx_data_msg_info:
pjsip_msg *msg
pjsip_fromto_hdr *from_hdr "from"
pjsip_fromto_hdr *to_hdr "to"
pjsip_via_hdr *via
struct pjsip_rx_data:
pjsip_rx_data_pkt_info pkt_info
pjsip_rx_data_tp_info tp_info
pjsip_rx_data_msg_info msg_info
void *pjsip_hdr_clone(pj_pool_t *pool, void *hdr) nogil
void pjsip_msg_add_hdr(pjsip_msg *msg, pjsip_hdr *hdr) nogil
void *pjsip_msg_find_hdr(pjsip_msg *msg, pjsip_hdr_e type, void *start) nogil
void *pjsip_msg_find_hdr_by_name(pjsip_msg *msg, pj_str_t *name, void *start) nogil
pjsip_generic_string_hdr *pjsip_generic_string_hdr_create(pj_pool_t *pool, pj_str_t *hname, pj_str_t *hvalue) nogil
pjsip_contact_hdr *pjsip_contact_hdr_create(pj_pool_t *pool) nogil
pjsip_expires_hdr *pjsip_expires_hdr_create(pj_pool_t *pool, int value) nogil
pjsip_msg_body *pjsip_msg_body_create(pj_pool_t *pool, pj_str_t *type, pj_str_t *subtype, pj_str_t *text) nogil
pjsip_route_hdr *pjsip_route_hdr_init(pj_pool_t *pool, void *mem) nogil
void pjsip_sip_uri_init(pjsip_sip_uri *url, int secure) nogil
int pjsip_tx_data_dec_ref(pjsip_tx_data *tdata) nogil
void pjsip_tx_data_add_ref(pjsip_tx_data *tdata) nogil
pj_str_t *pjsip_uri_get_scheme(pjsip_uri *uri) nogil
void *pjsip_uri_get_uri(pjsip_uri *uri) nogil
int pjsip_uri_print(pjsip_uri_context_e context, void *uri, char *buf, unsigned int size) nogil
int PJSIP_URI_SCHEME_IS_SIP(pjsip_sip_uri *uri) nogil
enum:
PJSIP_PARSE_URI_AS_NAMEADDR
pjsip_uri *pjsip_parse_uri(pj_pool_t *pool, char *buf, unsigned int size, unsigned int options) nogil
void pjsip_method_init_np(pjsip_method *m, pj_str_t *str) nogil
pj_str_t *pjsip_get_status_text(int status_code) nogil
# module
enum pjsip_module_priority:
PJSIP_MOD_PRIORITY_APPLICATION
PJSIP_MOD_PRIORITY_DIALOG_USAGE
PJSIP_MOD_PRIORITY_TRANSPORT_LAYER
struct pjsip_event
struct pjsip_transaction
struct pjsip_module:
pj_str_t name
int id
int priority
int on_rx_request(pjsip_rx_data *rdata) with gil
int on_rx_response(pjsip_rx_data *rdata) with gil
int on_tx_request(pjsip_tx_data *tdata) with gil
int on_tx_response(pjsip_tx_data *tdata) with gil
void on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil
# endpoint
struct pjsip_endpoint
int pjsip_endpt_create(pj_pool_factory *pf, char *name, pjsip_endpoint **endpt) nogil
void pjsip_endpt_destroy(pjsip_endpoint *endpt) nogil
pj_pool_t *pjsip_endpt_create_pool(pjsip_endpoint *endpt, char *pool_name, int initial, int increment) nogil
void pjsip_endpt_release_pool(pjsip_endpoint *endpt, pj_pool_t *pool) nogil
int pjsip_endpt_handle_events(pjsip_endpoint *endpt, pj_time_val *max_timeout) nogil
int pjsip_endpt_register_module(pjsip_endpoint *endpt, pjsip_module *module) nogil
int pjsip_endpt_schedule_timer(pjsip_endpoint *endpt, pj_timer_entry *entry, pj_time_val *delay) nogil
void pjsip_endpt_cancel_timer(pjsip_endpoint *endpt, pj_timer_entry *entry) nogil
enum:
PJSIP_H_ACCEPT
PJSIP_H_ALLOW
PJSIP_H_SUPPORTED
pjsip_hdr_ptr_const pjsip_endpt_get_capability(pjsip_endpoint *endpt, int htype, pj_str_t *hname) nogil
int pjsip_endpt_add_capability(pjsip_endpoint *endpt, pjsip_module *mod, int htype,
pj_str_t *hname, unsigned count, pj_str_t *tags) nogil
int pjsip_endpt_create_response(pjsip_endpoint *endpt, pjsip_rx_data *rdata,
int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil
int pjsip_endpt_send_response2(pjsip_endpoint *endpt, pjsip_rx_data *rdata,
pjsip_tx_data *tdata, void *token, void *cb) nogil
int pjsip_endpt_create_request(pjsip_endpoint *endpt, pjsip_method *method, pj_str_t *target, pj_str_t *frm,
pj_str_t *to, pj_str_t *contact, pj_str_t *call_id,
int cseq,pj_str_t *text, pjsip_tx_data **p_tdata) nogil
pj_timer_heap_t *pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt) nogil
int pjsip_endpt_create_resolver(pjsip_endpoint *endpt, pj_dns_resolver **p_resv) nogil
int pjsip_endpt_set_resolver(pjsip_endpoint *endpt, pj_dns_resolver *resv) nogil
# transports
enum pjsip_ssl_method:
PJSIP_SSL_UNSPECIFIED_METHOD
PJSIP_TLSV1_METHOD
PJSIP_SSLV2_METHOD
PJSIP_SSLV3_METHOD
PJSIP_SSLV23_METHOD
struct pjsip_transport:
char *type_name
pjsip_host_port local_name
struct pjsip_tpfactory:
pjsip_host_port addr_name
int destroy(pjsip_tpfactory *factory) nogil
struct pjsip_tls_setting:
pj_str_t ca_list_file
pj_str_t cert_file
pj_str_t privkey_file
int method
int verify_server
pj_time_val timeout
enum pjsip_tpselector_type:
PJSIP_TPSELECTOR_TRANSPORT
union pjsip_tpselector_u:
pjsip_transport *transport
struct pjsip_tpselector:
pjsip_tpselector_type type
pjsip_tpselector_u u
int pjsip_transport_shutdown(pjsip_transport *tp) nogil
int pjsip_udp_transport_start(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name,
unsigned int async_cnt, pjsip_transport **p_transport) nogil
int pjsip_tcp_transport_start2(pjsip_endpoint *endpt, pj_sockaddr_in *local, pjsip_host_port *a_name,
unsigned int async_cnt, pjsip_tpfactory **p_tpfactory) nogil
int pjsip_tls_transport_start(pjsip_endpoint *endpt, pjsip_tls_setting *opt, pj_sockaddr_in *local,
pjsip_host_port *a_name, unsigned async_cnt, pjsip_tpfactory **p_factory) nogil
void pjsip_tls_setting_default(pjsip_tls_setting *tls_opt) nogil
int pjsip_transport_shutdown(pjsip_transport *tp) nogil
# transaction layer
enum pjsip_role_e:
PJSIP_ROLE_UAC
PJSIP_ROLE_UAS
enum pjsip_tsx_state_e:
PJSIP_TSX_STATE_PROCEEDING
PJSIP_TSX_STATE_COMPLETED
PJSIP_TSX_STATE_TERMINATED
struct pjsip_transaction:
int status_code
pj_str_t status_text
pjsip_role_e role
pjsip_tx_data *last_tx
pjsip_tsx_state_e state
void **mod_data
pjsip_method method
int pjsip_tsx_layer_init_module(pjsip_endpoint *endpt) nogil
int pjsip_tsx_create_key(pj_pool_t *pool, pj_str_t *key, pjsip_role_e role,
pjsip_method *method, pjsip_rx_data *rdata) nogil
pjsip_transaction *pjsip_tsx_layer_find_tsx(pj_str_t *key, int lock) nogil
int pjsip_tsx_create_uac(pjsip_module *tsx_user, pjsip_tx_data *tdata, pjsip_transaction **p_tsx) nogil
int pjsip_tsx_terminate(pjsip_transaction *tsx, int code) nogil
int pjsip_tsx_send_msg(pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil
pjsip_transaction *pjsip_rdata_get_tsx(pjsip_rx_data *rdata) nogil
int pjsip_tsx_create_uas(pjsip_module *tsx_user, pjsip_rx_data *rdata, pjsip_transaction **p_tsx) nogil
void pjsip_tsx_recv_msg(pjsip_transaction *tsx, pjsip_rx_data *rdata) nogil
# event
enum pjsip_event_id_e:
PJSIP_EVENT_TSX_STATE
PJSIP_EVENT_RX_MSG
PJSIP_EVENT_TX_MSG
PJSIP_EVENT_TRANSPORT_ERROR
PJSIP_EVENT_TIMER
union pjsip_event_body_tsx_state_src:
pjsip_rx_data *rdata
pjsip_tx_data *tdata
struct pjsip_event_body_tsx_state:
pjsip_event_body_tsx_state_src src
pjsip_transaction *tsx
pjsip_event_id_e type
struct pjsip_event_body_rx_msg:
pjsip_rx_data *rdata
union pjsip_event_body:
pjsip_event_body_tsx_state tsx_state
pjsip_event_body_rx_msg rx_msg
struct pjsip_event:
pjsip_event_id_e type
pjsip_event_body body
int pjsip_endpt_send_request(pjsip_endpoint *endpt, pjsip_tx_data *tdata, int timeout,
void *token, void cb(void *token, pjsip_event *e) with gil) nogil
# auth
enum:
PJSIP_EFAILEDCREDENTIAL
enum pjsip_cred_data_type:
PJSIP_CRED_DATA_PLAIN_PASSWD
struct pjsip_cred_info:
pj_str_t realm
pj_str_t scheme
pj_str_t username
pjsip_cred_data_type data_type
pj_str_t data
struct pjsip_auth_clt_sess:
pass
int pjsip_auth_clt_init(pjsip_auth_clt_sess *sess, pjsip_endpoint *endpt, pj_pool_t *pool, unsigned int options) nogil
int pjsip_auth_clt_set_credentials(pjsip_auth_clt_sess *sess, int cred_cnt, pjsip_cred_info *c) nogil
int pjsip_auth_clt_reinit_req(pjsip_auth_clt_sess *sess, pjsip_rx_data *rdata,
pjsip_tx_data *old_request, pjsip_tx_data **new_request) nogil
# dialog layer
ctypedef pjsip_module pjsip_user_agent
struct pjsip_dlg_party:
pjsip_contact_hdr *contact
pjsip_fromto_hdr *info
struct pjsip_dialog:
pjsip_auth_clt_sess auth_sess
pjsip_cid_hdr *call_id
pj_pool_t *pool
pjsip_dlg_party local
struct pjsip_ua_init_param:
pjsip_dialog *on_dlg_forked(pjsip_dialog *first_set, pjsip_rx_data *res) nogil
int pjsip_ua_init_module(pjsip_endpoint *endpt, pjsip_ua_init_param *prm) nogil
pjsip_user_agent *pjsip_ua_instance() nogil
int pjsip_dlg_create_uac(pjsip_user_agent *ua, pj_str_t *local_uri, pj_str_t *local_contact_uri,
pj_str_t *remote_uri, pj_str_t *target, pjsip_dialog **p_dlg) nogil
int pjsip_dlg_set_route_set(pjsip_dialog *dlg, pjsip_route_hdr *route_set) nogil
int pjsip_dlg_create_uas(pjsip_user_agent *ua, pjsip_rx_data *rdata, pj_str_t *contact, pjsip_dialog **p_dlg) nogil
int pjsip_dlg_terminate(pjsip_dialog *dlg) nogil
int pjsip_dlg_set_transport(pjsip_dialog *dlg, pjsip_tpselector *sel) nogil
int pjsip_dlg_respond(pjsip_dialog *dlg, pjsip_rx_data *rdata, int st_code,
pj_str_t *st_text, pjsip_hdr *hdr_list, pjsip_msg_body *body) nogil
int pjsip_dlg_create_response(pjsip_dialog *dlg, pjsip_rx_data *rdata,
int st_code, pj_str_t *st_text, pjsip_tx_data **tdata) nogil
int pjsip_dlg_modify_response(pjsip_dialog *dlg, pjsip_tx_data *tdata, int st_code, pj_str_t *st_text) nogil
int pjsip_dlg_send_response(pjsip_dialog *dlg, pjsip_transaction *tsx, pjsip_tx_data *tdata) nogil
void pjsip_dlg_inc_lock(pjsip_dialog *dlg) nogil
void pjsip_dlg_dec_lock(pjsip_dialog *dlg) nogil
int pjsip_dlg_inc_session(pjsip_dialog *dlg, pjsip_module *mod) nogil
int pjsip_dlg_dec_session(pjsip_dialog *dlg, pjsip_module *mod) nogil
cdef extern from "pjsip-simple/evsub_msg.h":
struct pjsip_event_hdr:
pj_str_t event_type
pj_str_t id_param
pjsip_param other_param
struct pjsip_sub_state_hdr:
pj_str_t sub_state
pj_str_t reason_param
int expires_param
int retry_after
pjsip_param other_param
cdef extern from "pjsip_simple.h":
# subscribe / notify
enum:
PJSIP_EVSUB_NO_EVENT_ID
enum pjsip_evsub_state:
PJSIP_EVSUB_STATE_PENDING
PJSIP_EVSUB_STATE_ACTIVE
PJSIP_EVSUB_STATE_TERMINATED
struct pjsip_evsub
struct pjsip_evsub_user:
void on_evsub_state(pjsip_evsub *sub, pjsip_event *event) with gil
void on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil
void on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, pj_str_t **p_st_text,
pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil
void on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
pj_str_t **p_st_text,pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil
void on_client_refresh(pjsip_evsub *sub) with gil
void on_server_timeout(pjsip_evsub *sub) with gil
int pjsip_evsub_init_module(pjsip_endpoint *endpt) nogil
int pjsip_evsub_register_pkg(pjsip_module *pkg_mod, pj_str_t *event_name,
unsigned int expires, unsigned int accept_cnt, pj_str_t *accept) nogil
int pjsip_evsub_create_uac(pjsip_dialog *dlg, pjsip_evsub_user *user_cb,
pj_str_t *event, int option, pjsip_evsub **p_evsub) nogil
int pjsip_evsub_create_uas(pjsip_dialog *dlg, pjsip_evsub_user *user_cb,
pjsip_rx_data *rdata, unsigned int option, pjsip_evsub **p_evsub) nogil
int pjsip_evsub_initiate(pjsip_evsub *sub, void *method, unsigned int expires, pjsip_tx_data **p_tdata) nogil
int pjsip_evsub_send_request(pjsip_evsub *sub, pjsip_tx_data *tdata) nogil
int pjsip_evsub_terminate(pjsip_evsub *sub, int notify) nogil
char *pjsip_evsub_get_state_name(pjsip_evsub *sub) nogil
void pjsip_evsub_set_mod_data(pjsip_evsub *sub, int mod_id, void *data) nogil
void *pjsip_evsub_get_mod_data(pjsip_evsub *sub, int mod_id) nogil
pjsip_hdr *pjsip_evsub_get_allow_events_hdr(pjsip_module *m) nogil
int pjsip_evsub_notify(pjsip_evsub *sub, pjsip_evsub_state state,
pj_str_t *state_str, pj_str_t *reason, pjsip_tx_data **p_tdata) nogil
cdef extern from "pjsip_ua.h":
# 100rel / PRACK
int pjsip_100rel_init_module(pjsip_endpoint *endpt) nogil
# invite sessions
enum pjsip_inv_option:
PJSIP_INV_SUPPORT_100REL
PJSIP_INV_IGNORE_MISSING_ACK
enum pjsip_inv_state:
PJSIP_INV_STATE_INCOMING
PJSIP_INV_STATE_CONFIRMED
struct pjsip_inv_session:
pjsip_inv_state state
void **mod_data
pjmedia_sdp_neg *neg
int cause
pj_str_t cause_text
int cancelling
pjsip_transaction *invite_tsx
struct pjsip_inv_callback:
void on_state_changed(pjsip_inv_session *inv, pjsip_event *e) with gil
void on_new_session(pjsip_inv_session *inv, pjsip_event *e) with gil
void on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil
void on_rx_offer(pjsip_inv_session *inv, pjmedia_sdp_session *offer) with gil
#void on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
void on_media_update(pjsip_inv_session *inv, int status) with gil
#void on_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata)
void on_rx_reinvite(pjsip_inv_session *inv, pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil
int pjsip_inv_usage_init(pjsip_endpoint *endpt, pjsip_inv_callback *cb) nogil
int pjsip_inv_terminate(pjsip_inv_session *inv, int st_code, int notify) nogil
int pjsip_inv_end_session(pjsip_inv_session *inv, int st_code, pj_str_t *st_text, pjsip_tx_data **p_tdata) nogil
int pjsip_inv_cancel_reinvite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil
int pjsip_inv_send_msg(pjsip_inv_session *inv, pjsip_tx_data *tdata) nogil
int pjsip_inv_verify_request(pjsip_rx_data *rdata, unsigned int *options, pjmedia_sdp_session *sdp,
pjsip_dialog *dlg, pjsip_endpoint *endpt, pjsip_tx_data **tdata) nogil
int pjsip_inv_create_uas(pjsip_dialog *dlg, pjsip_rx_data *rdata, pjmedia_sdp_session *local_sdp,
unsigned int options, pjsip_inv_session **p_inv) nogil
int pjsip_inv_initial_answer(pjsip_inv_session *inv, pjsip_rx_data *rdata, int st_code,
pj_str_t *st_text, pjmedia_sdp_session *sdp, pjsip_tx_data **p_tdata) nogil
int pjsip_inv_answer(pjsip_inv_session *inv, int st_code, pj_str_t *st_text,
pjmedia_sdp_session *local_sdp, pjsip_tx_data **p_tdata) nogil
int pjsip_inv_create_uac(pjsip_dialog *dlg, pjmedia_sdp_session *local_sdp,
unsigned int options, pjsip_inv_session **p_inv) nogil
int pjsip_inv_invite(pjsip_inv_session *inv, pjsip_tx_data **p_tdata) nogil
char *pjsip_inv_state_name(pjsip_inv_state state) nogil
int pjsip_inv_reinvite(pjsip_inv_session *inv, pj_str_t *new_contact,
pjmedia_sdp_session *new_offer, pjsip_tx_data **p_tdata) nogil
# declarations
# core.util
cdef class frozenlist(object):
# attributes
cdef int initialized
cdef list list
cdef long hash
cdef class frozendict(object):
# attributes
cdef int initialized
cdef dict dict
cdef long hash
cdef class PJSTR(object):
# attributes
cdef pj_str_t pj_str
cdef object str
# core.lib
cdef class PJLIB(object):
# attributes
cdef int _init_done
cdef class PJCachingPool(object):
# attributes
cdef pj_caching_pool _obj
cdef int _init_done
cdef class PJSIPEndpoint(object):
# attributes
cdef pjsip_endpoint *_obj
cdef pj_pool_t *_pool
cdef pjsip_transport *_udp_transport
cdef pjsip_tpfactory *_tcp_transport
cdef pjsip_tpfactory *_tls_transport
cdef int _tls_verify_server
cdef PJSTR _tls_ca_file
cdef PJSTR _tls_cert_file
cdef PJSTR _tls_privkey_file
cdef object _local_ip_used
cdef int _tls_timeout
cdef object _tls_protocol
# private methods
cdef int _make_local_addr(self, pj_sockaddr_in *local_addr, object ip_address, int port) except -1
cdef int _start_udp_transport(self, int port) except -1
cdef int _stop_udp_transport(self) except -1
cdef int _start_tcp_transport(self, int port) except -1
cdef int _stop_tcp_transport(self) except -1
cdef int _start_tls_transport(self, port) except -1
cdef int _stop_tls_transport(self) except -1
cdef class PJMEDIAEndpoint(object):
# attributes
cdef pjmedia_endpt *_obj
cdef int _has_speex
cdef int _has_g722
cdef int _has_g711
cdef int _has_ilbc
cdef int _has_gsm
# private methods
cdef list _get_codecs(self)
cdef list _get_all_codecs(self)
cdef list _get_current_codecs(self)
cdef int _set_codecs(self, list req_codecs, int max_sample_rate) except -1
# core.helper
cdef class BaseCredentials(object):
# attributes
cdef pjsip_cred_info _credentials
# private methods
cdef pjsip_cred_info* get_cred_info(self)
cdef class Credentials(BaseCredentials):
# attributes
cdef str _username
cdef str _realm
cdef str _password
cdef class FrozenCredentials(BaseCredentials):
# attributes
cdef int initialized
cdef readonly str username
cdef readonly str realm
cdef readonly str password
cdef class BaseSIPURI(object):
pass
cdef class SIPURI(BaseSIPURI):
# attributes
cdef public str user
cdef public str password
cdef str _host
cdef object _port
cdef bint _secure
cdef dict _parameters
cdef dict _headers
cdef class FrozenSIPURI(BaseSIPURI):
# attributes
cdef int initialized
cdef readonly str user
cdef readonly str password
cdef readonly str host
cdef readonly object port
cdef readonly bint secure
cdef readonly frozendict parameters
cdef readonly frozendict headers
cdef SIPURI SIPURI_create(pjsip_sip_uri *base_uri)
cdef FrozenSIPURI FrozenSIPURI_create(pjsip_sip_uri *base_uri)
# core.headers
cdef class BaseHeader(object):
pass
cdef class Header(BaseHeader):
# attributes
cdef str _name
cdef str _body
cdef class FrozenHeader(BaseHeader):
# attributes
cdef readonly str name
cdef readonly str body
cdef class BaseContactHeader(object):
pass
cdef class ContactHeader(BaseContactHeader):
# attributes
cdef SIPURI _uri
cdef unicode _display_name
cdef dict _parameters
cdef class FrozenContactHeader(BaseContactHeader):
# attributes
cdef int initialized
cdef readonly FrozenSIPURI uri
cdef readonly unicode display_name
cdef readonly frozendict parameters
cdef class BaseIdentityHeader(object):
pass
cdef class IdentityHeader(BaseIdentityHeader):
# attributes
cdef SIPURI _uri
cdef public unicode display_name
cdef dict _parameters
cdef class FrozenIdentityHeader(BaseIdentityHeader):
# attributes
cdef int initialized
cdef readonly FrozenSIPURI uri
cdef readonly unicode display_name
cdef readonly frozendict parameters
cdef class FromHeader(IdentityHeader):
pass
cdef class FrozenFromHeader(FrozenIdentityHeader):
pass
cdef class ToHeader(IdentityHeader):
pass
cdef class FrozenToHeader(FrozenIdentityHeader):
pass
cdef class RouteHeader(IdentityHeader):
pass
cdef class FrozenRouteHeader(FrozenIdentityHeader):
pass
cdef class RecordRouteHeader(IdentityHeader):
pass
cdef class FrozenRecordRouteHeader(FrozenIdentityHeader):
pass
cdef class BaseRetryAfterHeader(object):
pass
cdef class RetryAfterHeader(BaseRetryAfterHeader):
# attributes
cdef public int seconds
cdef public str comment
cdef dict _parameters
cdef class FrozenRetryAfterHeader(BaseRetryAfterHeader):
# attributes
cdef int initialized
cdef readonly int seconds
cdef readonly str comment
cdef readonly frozendict parameters
cdef class BaseViaHeader(object):
pass
cdef class ViaHeader(BaseViaHeader):
# attributes
cdef str _transport
cdef str _host
cdef int _port
cdef dict _parameters
cdef class FrozenViaHeader(BaseViaHeader):
# attributes
cdef int initialized
cdef readonly str transport
cdef readonly str host
cdef readonly int port
cdef readonly frozendict parameters
cdef class BaseWarningHeader(object):
pass
cdef class WarningHeader(BaseWarningHeader):
# attributes
cdef int _code
cdef str _agent
cdef str _text
cdef class FrozenWarningHeader(BaseWarningHeader):
# attributes
cdef int initialized
cdef readonly int code
cdef readonly str agent
cdef readonly str text
cdef class BaseEventHeader(object):
pass
cdef class EventHeader(BaseEventHeader):
# attributes
cdef public event
cdef dict _parameters
cdef class FrozenEventHeader(BaseEventHeader):
# attributes
cdef int initialized
cdef readonly str event
cdef readonly frozendict parameters
cdef class BaseSubscriptionStateHeader(object):
pass
cdef class SubscriptionStateHeader(BaseSubscriptionStateHeader):
# attributes
cdef public state
cdef dict _parameters
cdef class FrozenSubscriptionStateHeader(BaseSubscriptionStateHeader):
# attributes
cdef int initialized
cdef readonly str state
cdef readonly frozendict parameters
cdef class BaseReasonHeader(object):
pass
cdef class ReasonHeader(BaseReasonHeader):
# attributes
cdef public str protocol
cdef public dict parameters
cdef class FrozenReasonHeader(BaseReasonHeader):
# attributes
cdef int initialized
cdef readonly str protocol
cdef readonly frozendict parameters
cdef Header Header_create(pjsip_generic_string_hdr *header)
cdef FrozenHeader FrozenHeader_create(pjsip_generic_string_hdr *header)
cdef ContactHeader ContactHeader_create(pjsip_contact_hdr *header)
cdef FrozenContactHeader FrozenContactHeader_create(pjsip_contact_hdr *header)
cdef FromHeader FromHeader_create(pjsip_fromto_hdr *header)
cdef FrozenFromHeader FrozenFromHeader_create(pjsip_fromto_hdr *header)
cdef ToHeader ToHeader_create(pjsip_fromto_hdr *header)
cdef FrozenToHeader FrozenToHeader_create(pjsip_fromto_hdr *header)
cdef RouteHeader RouteHeader_create(pjsip_routing_hdr *header)
cdef FrozenRouteHeader FrozenRouteHeader_create(pjsip_routing_hdr *header)
cdef RecordRouteHeader RecordRouteHeader_create(pjsip_routing_hdr *header)
cdef FrozenRecordRouteHeader FrozenRecordRouteHeader_create(pjsip_routing_hdr *header)
cdef RetryAfterHeader RetryAfterHeader_create(pjsip_retry_after_hdr *header)
cdef FrozenRetryAfterHeader FrozenRetryAfterHeader_create(pjsip_retry_after_hdr *header)
cdef ViaHeader ViaHeader_create(pjsip_via_hdr *header)
cdef FrozenViaHeader FrozenViaHeader_create(pjsip_via_hdr *header)
cdef EventHeader EventHeader_create(pjsip_event_hdr *header)
cdef FrozenEventHeader FrozenEventHeader_create(pjsip_event_hdr *header)
cdef SubscriptionStateHeader SubscriptionStateHeader_create(pjsip_sub_state_hdr *header)
cdef FrozenSubscriptionStateHeader FrozenSubscriptionStateHeader_create(pjsip_sub_state_hdr *header)
# core.util
cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1
cdef object _pj_str_to_str(pj_str_t pj_str)
cdef object _pj_status_to_str(int status)
cdef object _pj_status_to_def(int status)
cdef dict _pjsip_param_to_dict(pjsip_param *param_list)
cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool)
cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1
cdef int _is_valid_ip(int af, object ip) except -1
cdef int _get_ip_version(object ip) except -1
cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1
cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1
cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1
# core.ua
ctypedef int (*timer_callback)(object, object) except -1 with gil
cdef class Timer(object):
# attributes
cdef int _scheduled
cdef double schedule_time
cdef timer_callback callback
cdef object obj
# private methods
cdef int schedule(self, float delay, timer_callback callback, object obj) except -1
cdef int cancel(self) except -1
cdef int call(self) except -1
cdef class PJSIPThread(object):
# attributes
cdef pj_thread_t *_obj
cdef long _thread_desc[PJ_THREAD_DESC_SIZE]
cdef class PJSIPUA(object):
# attributes
cdef object _threads
cdef object _event_handler
cdef list _timers
cdef PJLIB _pjlib
cdef PJCachingPool _caching_pool
cdef PJSIPEndpoint _pjsip_endpoint
cdef PJMEDIAEndpoint _pjmedia_endpoint
cdef pjsip_module _module
cdef PJSTR _module_name
cdef pjsip_module _trace_module
cdef PJSTR _trace_module_name
cdef pjsip_module _ua_tag_module
cdef PJSTR _ua_tag_module_name
cdef pjsip_module _event_module
cdef PJSTR _event_module_name
cdef int _trace_sip
cdef int _ignore_missing_ack
cdef PJSTR _user_agent
cdef object _events
cdef object _sent_messages
cdef int _rtp_port_start
cdef int _rtp_port_count
cdef int _rtp_port_usable_count
cdef int _rtp_port_index
cdef pj_stun_config _stun_cfg
cdef int _fatal_error
cdef set _incoming_events
cdef set _incoming_requests
cdef pjmedia_audio_change_observer _audio_change_observer
cdef pj_rwmutex_t *audio_change_rwlock
cdef list old_devices
# private methods
cdef object _get_sound_devices(self, int is_output)
cdef int _poll_log(self) except -1
cdef int _handle_exception(self, int is_fatal) except -1
cdef int _check_self(self) except -1
cdef int _check_thread(self) except -1
cdef int _add_timer(self, Timer timer) except -1
cdef int _remove_timer(self, Timer timer) except -1
cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0
cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil
cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil
cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil
cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil
cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil
cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil
cdef PJSIPUA _get_ua()
cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil
# core.sound
cdef class AudioMixer(object):
# attributes
cdef int _disconnect_when_idle
cdef int _input_volume
cdef int _output_volume
cdef bint _muted
cdef pj_mutex_t *_lock
cdef pj_pool_t *_conf_pool
cdef pj_pool_t *_snd_pool
cdef pjmedia_conf *_obj
cdef pjmedia_master_port *_master_port
cdef pjmedia_port *_null_port
cdef pjmedia_snd_port *_snd
cdef list _connected_slots
cdef readonly int ec_tail_length
cdef readonly int sample_rate
cdef readonly int slot_count
cdef readonly int used_slot_count
- cdef readonly str input_device
- cdef readonly str output_device
- cdef readonly str real_input_device
- cdef readonly str real_output_device
+ cdef readonly unicode input_device
+ cdef readonly unicode output_device
+ cdef readonly unicode real_input_device
+ cdef readonly unicode real_output_device
# private methods
- cdef int _start_sound_device(self, PJSIPUA ua, str input_device, str output_device,
+ cdef int _start_sound_device(self, PJSIPUA ua, unicode input_device, unicode output_device,
int ec_tail_length, int revert_to_default) except -1
cdef int _stop_sound_device(self, PJSIPUA ua) except -1
cdef int _add_port(self, PJSIPUA ua, pj_pool_t *pool, pjmedia_port *port) except -1 with gil
cdef int _remove_port(self, PJSIPUA ua, unsigned int slot) except -1 with gil
cdef int _cb_postpoll_stop_sound(self, timer) except -1
cdef class ToneGenerator(object):
# attributes
cdef int _slot
cdef int _volume
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_port *_obj
cdef Timer _timer
cdef readonly AudioMixer mixer
# private methods
cdef PJSIPUA _get_ua(self, int raise_exception)
cdef int _stop(self, PJSIPUA ua) except -1
cdef int _cb_check_done(self, timer) except -1
cdef class RecordingWaveFile(object):
# attributes
cdef int _slot
cdef int _was_started
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_port *_port
cdef readonly str filename
cdef readonly AudioMixer mixer
# private methods
cdef PJSIPUA _check_ua(self)
cdef int _stop(self, PJSIPUA ua) except -1
cdef class WaveFile(object):
# attributes
cdef object __weakref__
cdef object weakref
cdef int _slot
cdef int _volume
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_port *_port
cdef readonly str filename
cdef readonly AudioMixer mixer
# private methods
cdef PJSIPUA _check_ua(self)
cdef int _stop(self, PJSIPUA ua, int notify) except -1
cdef int _cb_eof(self, timer) except -1
cdef class MixerPort(object):
cdef int _slot
cdef int _was_started
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_port *_port
cdef readonly AudioMixer mixer
# private methods
cdef PJSIPUA _check_ua(self)
cdef int _stop(self, PJSIPUA ua) except -1
cdef int _AudioMixer_dealloc_handler(object obj) except -1
cdef int cb_play_wav_eof(pjmedia_port *port, void *user_data) with gil
# core.event
cdef struct _core_event
cdef struct _handler_queue
cdef int _event_queue_append(_core_event *event)
cdef void _cb_log(int level, char_ptr_const data, int len)
cdef int _add_event(object event_name, dict params) except -1
cdef list _get_clear_event_queue()
cdef int _add_handler(int func(object obj) except -1, object obj, _handler_queue *queue) except -1
cdef int _remove_handler(object obj, _handler_queue *queue) except -1
cdef int _process_handler_queue(PJSIPUA ua, _handler_queue *queue) except -1
# core.request
cdef class EndpointAddress(object):
# attributes
cdef readonly str ip
cdef readonly int port
cdef class Request(object):
# attributes
cdef readonly object state
cdef PJSTR _method
cdef readonly EndpointAddress peer_address
cdef readonly FrozenCredentials credentials
cdef readonly FrozenFromHeader from_header
cdef readonly FrozenToHeader to_header
cdef readonly FrozenSIPURI request_uri
cdef readonly FrozenContactHeader contact_header
cdef readonly FrozenRouteHeader route_header
cdef PJSTR _call_id
cdef readonly int cseq
cdef readonly frozenlist extra_headers
cdef PJSTR _content_type
cdef PJSTR _content_subtype
cdef PJSTR _body
cdef pjsip_tx_data *_tdata
cdef pjsip_transaction *_tsx
cdef pjsip_auth_clt_sess _auth
cdef pjsip_route_hdr _route_header
cdef int _need_auth
cdef pj_timer_entry _timer
cdef int _timer_active
cdef int _expire_rest
cdef object _expire_time
cdef object _timeout
# private methods
cdef PJSIPUA _get_ua(self)
cdef int _cb_tsx_state(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1
cdef int _cb_timer(self, PJSIPUA ua) except -1
cdef class IncomingRequest(object):
# attributes
cdef readonly str state
cdef pjsip_transaction *_tsx
cdef pjsip_tx_data *_tdata
cdef readonly EndpointAddress peer_address
# methods
cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1
cdef void _Request_cb_tsx_state(pjsip_transaction *tsx, pjsip_event *event) with gil
cdef void _Request_cb_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) with gil
# core.subscription
cdef class Subscription(object):
# attributes
cdef pjsip_evsub *_obj
cdef pjsip_dialog *_dlg
cdef pjsip_route_hdr _route_header
cdef pj_list _route_set
cdef readonly object state
cdef pj_timer_entry _timeout_timer
cdef int _timeout_timer_active
cdef pj_timer_entry _refresh_timer
cdef int _refresh_timer_active
cdef readonly EndpointAddress peer_address
cdef readonly FrozenFromHeader from_header
cdef readonly FrozenToHeader to_header
cdef readonly FrozenContactHeader contact_header
cdef readonly object event
cdef readonly FrozenRouteHeader route_header
cdef readonly FrozenCredentials credentials
cdef readonly int refresh
cdef readonly frozenlist extra_headers
cdef readonly object body
cdef readonly object content_type
cdef pj_time_val _subscribe_timeout
cdef int _want_end
cdef int _term_code
cdef object _term_reason
cdef int _expires
# private methods
cdef PJSIPUA _get_ua(self)
cdef int _cancel_timers(self, PJSIPUA ua, int cancel_timeout, int cancel_refresh) except -1
cdef int _send_subscribe(self, PJSIPUA ua, int expires, pj_time_val *timeout,
object extra_headers, object content_type, object body) except -1
cdef int _cb_state(self, PJSIPUA ua, object state, int code, object reason, dict headers) except -1
cdef int _cb_got_response(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1
cdef int _cb_notify(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1
cdef int _cb_timeout_timer(self, PJSIPUA ua)
cdef int _cb_refresh_timer(self, PJSIPUA ua)
cdef class IncomingSubscription(object):
# attributes
cdef pjsip_evsub *_obj
cdef pjsip_dialog *_dlg
cdef readonly str state
cdef PJSTR _content_type
cdef PJSTR _content_subtype
cdef PJSTR _content
cdef pjsip_tx_data *_initial_response
cdef pjsip_transaction *_initial_tsx
cdef int _expires
cdef readonly str event
cdef readonly EndpointAddress peer_address
# TODO: add usefull attributes?
# methods
cdef int _set_state(self, str state) except -1
cdef PJSIPUA _get_ua(self, int raise_exception)
cdef int init(self, PJSIPUA ua, pjsip_rx_data *rdata, str event) except -1
cdef int _send_initial_response(self, int code) except -1
cdef int _send_notify(self, str reason=*) except -1
cdef int _terminate(self, PJSIPUA ua, str reason, int do_cleanup) except -1
cdef int _cb_rx_refresh(self, PJSIPUA ua, pjsip_rx_data *rdata) except -1
cdef int _cb_server_timeout(self, PJSIPUA ua) except -1
cdef int _cb_tsx(self, PJSIPUA ua, pjsip_event *event) except -1
cdef void _Subscription_cb_state(pjsip_evsub *sub, pjsip_event *event) with gil
cdef void _Subscription_cb_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code,
pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil
cdef void _Subscription_cb_refresh(pjsip_evsub *sub) with gil
cdef void _IncomingSubscription_cb_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata,
int *p_st_code, pj_str_t **p_st_text,
pjsip_hdr *res_hdr, pjsip_msg_body **p_body) with gil
cdef void _IncomingSubscription_cb_server_timeout(pjsip_evsub *sub) with gil
cdef void _IncomingSubscription_cb_tsx(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event) with gil
# core.sdp
cdef class BaseSDPConnection(object):
# attributes
cdef pjmedia_sdp_conn _sdp_connection
# private methods
cdef pjmedia_sdp_conn* get_sdp_connection(self)
cdef class SDPConnection(BaseSDPConnection):
# attributes
cdef str _address
cdef str _net_type
cdef str _address_type
cdef class FrozenSDPConnection(BaseSDPConnection):
# attributes
cdef int initialized
cdef readonly str address
cdef readonly str net_type
cdef readonly str address_type
cdef class SDPAttributeList(list):
pass
cdef class FrozenSDPAttributeList(frozenlist):
pass
cdef class BaseSDPSession(object):
# attributes
cdef pjmedia_sdp_session _sdp_session
# private methods
cdef pjmedia_sdp_session* get_sdp_session(self)
cdef class SDPSession(BaseSDPSession):
# attributes
cdef str _address
cdef str _user
cdef str _net_type
cdef str _address_type
cdef str _name
cdef str _info
cdef SDPConnection _connection
cdef list _attributes
cdef list _media
# private methods
cdef int _update(self) except -1
cdef class FrozenSDPSession(BaseSDPSession):
# attributes
cdef int initialized
cdef readonly str address
cdef readonly unsigned int id
cdef readonly unsigned int version
cdef readonly str user
cdef readonly str net_type
cdef readonly str address_type
cdef readonly str name
cdef readonly str info
cdef readonly FrozenSDPConnection connection
cdef readonly int start_time
cdef readonly int stop_time
cdef readonly FrozenSDPAttributeList attributes
cdef readonly frozenlist media
cdef class BaseSDPMediaStream(object):
# attributes
cdef pjmedia_sdp_media _sdp_media
# private methods
cdef pjmedia_sdp_media* get_sdp_media(self)
cdef class SDPMediaStream(BaseSDPMediaStream):
# attributes
cdef str _media
cdef str _transport
cdef list _formats
cdef list _codec_list
cdef str _info
cdef SDPConnection _connection
cdef SDPAttributeList _attributes
# private methods
cdef int _update(self, SDPMediaStream media) except -1
cdef class FrozenSDPMediaStream(BaseSDPMediaStream):
# attributes
cdef int initialized
cdef readonly str media
cdef readonly int port
cdef readonly str transport
cdef readonly int port_count
cdef readonly frozenlist formats
cdef readonly frozenlist codec_list
cdef readonly str info
cdef readonly FrozenSDPConnection connection
cdef readonly FrozenSDPAttributeList attributes
cdef class BaseSDPAttribute(object):
# attributes
cdef pjmedia_sdp_attr _sdp_attribute
# private methods
cdef pjmedia_sdp_attr* get_sdp_attribute(self)
cdef class SDPAttribute(BaseSDPAttribute):
# attributes
cdef str _name
cdef str _value
cdef class FrozenSDPAttribute(BaseSDPAttribute):
# attributes
cdef int initialized
cdef readonly str name
cdef readonly str value
cdef SDPSession SDPSession_create(pjmedia_sdp_session_ptr_const pj_session)
cdef FrozenSDPSession FrozenSDPSession_create(pjmedia_sdp_session_ptr_const pj_session)
cdef SDPMediaStream SDPMediaStream_create(pjmedia_sdp_media *pj_media)
cdef FrozenSDPMediaStream FrozenSDPMediaStream_create(pjmedia_sdp_media *pj_media)
cdef SDPConnection SDPConnection_create(pjmedia_sdp_conn *pj_conn)
cdef FrozenSDPConnection FrozenSDPConnection_create(pjmedia_sdp_conn *pj_conn)
cdef SDPAttribute SDPAttribute_create(pjmedia_sdp_attr *pj_attr)
cdef FrozenSDPAttribute FrozenSDPAttribute_create(pjmedia_sdp_attr *pj_attr)
# core.invitation
cdef class SDPPayloads:
# attributes
cdef readonly FrozenSDPSession proposed_local
cdef readonly FrozenSDPSession proposed_remote
cdef readonly FrozenSDPSession active_local
cdef readonly FrozenSDPSession active_remote
cdef class StateCallbackTimer(Timer):
# attributes
cdef object state
cdef object sub_state
cdef object rdata
cdef object tdata
cdef class SDPCallbackTimer(Timer):
# attributes
cdef int status
cdef class Invitation(object):
# attributes
cdef object __weakref__
cdef object weakref
cdef int _sdp_neg_status
cdef pj_list _route_set
cdef pj_mutex_t *_lock
cdef pjsip_inv_session *_invite_session
cdef pjsip_dialog *_dialog
cdef pjsip_route_hdr _route_header
cdef pjsip_transaction *_reinvite_transaction
cdef Timer _timer
cdef readonly str call_id
cdef readonly str direction
cdef readonly str remote_user_agent
cdef readonly str state
cdef readonly str sub_state
cdef readonly str transport
cdef readonly EndpointAddress peer_address
cdef readonly FrozenCredentials credentials
cdef readonly FrozenContactHeader local_contact_header
cdef readonly FrozenContactHeader remote_contact_header
cdef readonly FrozenFromHeader from_header
cdef readonly FrozenToHeader to_header
cdef readonly FrozenRouteHeader route_header
cdef readonly SDPPayloads sdp
# private methods
cdef int init_incoming(self, PJSIPUA ua, pjsip_rx_data *rdata, unsigned int inv_options) except -1
cdef PJSIPUA _check_ua(self)
cdef int _do_dealloc(self) except -1
cdef int _update_contact_header(self, BaseContactHeader contact_header) except -1
cdef int _fail(self, PJSIPUA ua) except -1
cdef int _cb_state(self, StateCallbackTimer timer) except -1
cdef int _cb_sdp_done(self, SDPCallbackTimer timer) except -1
cdef int _cb_timer_disconnect(self, timer) except -1
cdef int _cb_postpoll_fail(self, timer) except -1
cdef void _Invitation_cb_state(pjsip_inv_session *inv, pjsip_event *e) with gil
cdef void _Invitation_cb_sdp_done(pjsip_inv_session *inv, int status) with gil
cdef void _Invitation_cb_rx_reinvite(pjsip_inv_session *inv,
pjmedia_sdp_session_ptr_const offer, pjsip_rx_data *rdata) with gil
cdef void _Invitation_cb_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) with gil
cdef void _Invitation_cb_new(pjsip_inv_session *inv, pjsip_event *e) with gil
# core.mediatransport
cdef class RTPTransport(object):
# attributes
cdef object __weakref__
cdef object weakref
cdef int _af
cdef int _ice_active
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_transport *_obj
cdef pjmedia_transport *_wrapped_transport
cdef object _local_rtp_addr
cdef str _local_rtp_candidate_type
cdef str _remote_rtp_candidate_type
cdef readonly object ice_stun_address
cdef readonly object ice_stun_port
cdef readonly object remote_rtp_port_sdp
cdef readonly object remote_rtp_address_sdp
cdef readonly object remote_rtp_port_ice
cdef readonly object remote_rtp_address_ice
cdef readonly object srtp_forced
cdef readonly object state
cdef readonly object use_ice
cdef readonly object use_srtp
# private methods
cdef PJSIPUA _check_ua(self)
cdef int _get_info(self, pjmedia_transport_info *info) except -1
cdef int _update_local_sdp(self, SDPSession local_sdp, int sdp_index, pjmedia_sdp_session *remote_sdp) except -1
cdef class MediaCheckTimer(Timer):
# attributes
cdef int media_check_interval
cdef class AudioTransport(object):
# attributes
cdef object __weakref__
cdef object weakref
cdef int _is_offer
cdef int _is_started
cdef int _slot
cdef int _volume
cdef unsigned int _packets_received
cdef unsigned int _vad
cdef pj_mutex_t *_lock
cdef pj_pool_t *_pool
cdef pjmedia_sdp_media *_local_media
cdef pjmedia_stream *_obj
cdef pjmedia_stream_info _stream_info
cdef dict _cached_statistics
cdef Timer _timer
cdef readonly object direction
cdef readonly AudioMixer mixer
cdef readonly RTPTransport transport
# private methods
cdef PJSIPUA _check_ua(self)
cdef int _cb_check_rtp(self, MediaCheckTimer timer) except -1 with gil
cdef void _RTPTransport_cb_ice_complete(pjmedia_transport *tp, pj_ice_strans_op op, int status) with gil
cdef void _RTPTransport_cb_ice_candidates_chosen(pjmedia_transport *tp, int status, pj_ice_candidate_pair rtp_pair, pj_ice_candidate_pair rtcp_pair, char *duration, char *local_candidates, char *remote_candidates, char *valid_list) with gil
cdef void _RTPTransport_cb_ice_failure(pjmedia_transport *tp, char *reason) with gil
cdef void _RTPTransport_cb_ice_state(pjmedia_transport *tp, char *state) with gil
cdef void _AudioTransport_cb_dtmf(pjmedia_stream *stream, void *user_data, int digit) with gil
cdef dict _pj_math_stat_to_dict(pj_math_stat *stat)
cdef dict _pjmedia_rtcp_stream_stat_to_dict(pjmedia_rtcp_stream_stat *stream_stat)
diff --git a/sipsimple/core/_core.pyx b/sipsimple/core/_core.pyx
index bc3cc7cf..6fa20aeb 100644
--- a/sipsimple/core/_core.pyx
+++ b/sipsimple/core/_core.pyx
@@ -1,57 +1,57 @@
# Copyright (C) 2008-2010 AG Projects. See LICENSE for details.
#
# includes
include "_core.error.pxi"
include "_core.lib.pxi"
include "_core.sound.pxi"
include "_core.util.pxi"
include "_core.ua.pxi"
include "_core.event.pxi"
include "_core.request.pxi"
include "_core.helper.pxi"
include "_core.headers.pxi"
include "_core.subscription.pxi"
include "_core.invitation.pxi"
include "_core.sdp.pxi"
include "_core.mediatransport.pxi"
# constants
PJ_VERSION = pj_get_version()
PJ_SVN_REVISION = int(PJ_SVN_REV)
-CORE_REVISION = 128
+CORE_REVISION = 129
# exports
__all__ = ["PJ_VERSION", "PJ_SVN_REVISION", "CORE_REVISION",
"SIPCoreError", "PJSIPError", "PJSIPTLSError", "SIPCoreInvalidStateError",
"AudioMixer", "ToneGenerator", "RecordingWaveFile", "WaveFile", "MixerPort",
"sip_status_messages",
"BaseCredentials", "Credentials", "FrozenCredentials", "BaseSIPURI", "SIPURI", "FrozenSIPURI",
"BaseHeader", "Header", "FrozenHeader", "BaseContactHeader", "ContentType", "ContactHeader", "FrozenContactHeader",
"BaseIdentityHeader", "IdentityHeader", "FrozenIdentityHeader", "FromHeader", "FrozenFromHeader", "ToHeader", "FrozenToHeader",
"RouteHeader", "FrozenRouteHeader", "RecordRouteHeader", "FrozenRecordRouteHeader", "BaseRetryAfterHeader", "RetryAfterHeader", "FrozenRetryAfterHeader",
"BaseViaHeader", "ViaHeader", "FrozenViaHeader", "BaseWarningHeader", "WarningHeader", "FrozenWarningHeader",
"BaseEventHeader", "EventHeader", "FrozenEventHeader", "BaseSubscriptionStateHeader", "SubscriptionStateHeader", "FrozenSubscriptionStateHeader",
"BaseReasonHeader", "ReasonHeader", "FrozenReasonHeader",
"Request",
"Subscription",
"Invitation",
"SDPSession", "FrozenSDPSession", "SDPMediaStream", "FrozenSDPMediaStream", "SDPConnection", "FrozenSDPConnection", "SDPAttribute", "FrozenSDPAttribute",
"RTPTransport", "AudioTransport"]
# Initialize the GIL in the PyMODINIT function of the module.
# This is a hack because Cython does not support #ifdefs.
cdef extern from *:
cdef void emit_ifdef_with_thread "#ifdef WITH_THREAD //" ()
cdef void emit_endif "#endif //" ()
emit_ifdef_with_thread()
PyEval_InitThreads()
emit_endif()
diff --git a/sipsimple/core/_core.sound.pxi b/sipsimple/core/_core.sound.pxi
index 51643272..41890eba 100644
--- a/sipsimple/core/_core.sound.pxi
+++ b/sipsimple/core/_core.sound.pxi
@@ -1,1401 +1,1402 @@
# Copyright (C) 2008-2010 AG Projects. See LICENSE for details.
#
import platform
# classes
cdef class AudioMixer:
# properties
property input_volume:
def __get__(self):
return self._input_volume
def __set__(self, int value):
cdef int status
cdef int volume
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
pass
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
if value < 0:
raise ValueError("input_volume attribute cannot be negative")
if ua is not None:
volume = int(value * 1.28 - 128)
with nogil:
status = pjmedia_conf_adjust_rx_level(conf_bridge, 0, volume)
if status != 0:
raise PJSIPError("Could not set input volume of sound device", status)
if value > 0 and self._muted:
self._muted = False
self._input_volume = value
finally:
with nogil:
pj_mutex_unlock(lock)
property output_volume:
def __get__(self):
return self._output_volume
def __set__(self, int value):
cdef int status
cdef int volume
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
pass
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
if value < 0:
raise ValueError("output_volume attribute cannot be negative")
if ua is not None:
volume = int(value * 1.28 - 128)
with nogil:
status = pjmedia_conf_adjust_tx_level(conf_bridge, 0, volume)
if status != 0:
raise PJSIPError("Could not set output volume of sound device", status)
self._output_volume = value
finally:
with nogil:
pj_mutex_unlock(lock)
property muted:
def __get__(self):
return self._muted
def __set__(self, bint muted):
cdef int status
cdef int volume
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
pass
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
if muted == self._muted:
return
if ua is not None:
if muted:
volume = -128
else:
volume = int(self._input_volume * 1.28 - 128)
with nogil:
status = pjmedia_conf_adjust_rx_level(conf_bridge, 0, volume)
if status != 0:
raise PJSIPError("Could not set input volume of sound device", status)
self._muted = muted
finally:
with nogil:
pj_mutex_unlock(lock)
property connected_slots:
def __get__(self):
return sorted(self._connected_slots)
# public methods
def __cinit__(self, *args, **kwargs):
self._connected_slots = list()
if platform.system() == "Darwin":
# At some point Snow Leopard did not like this, but it works now 2010-09-05
# and not platform.mac_ver()[0].startswith("10.6"):
self._disconnect_when_idle = 1
else:
self._disconnect_when_idle = 0
self._input_volume = 100
self._output_volume = 100
pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "audio_mixer_lock", &self._lock)
- def __init__(self, str input_device, str output_device, int sample_rate,
+ def __init__(self, unicode input_device, unicode output_device, int sample_rate,
int ec_tail_length=200, int slot_count=254):
global _dealloc_handler_queue
cdef int status
cdef pj_pool_t *pool
cdef pjmedia_conf **conf_bridge_address
cdef pjsip_endpoint *endpoint
cdef str conf_pool_name
cdef PJSIPUA ua
ua = _get_ua()
conf_bridge_address = &self._obj
endpoint = ua._pjsip_endpoint._obj
if self._obj != NULL:
raise SIPCoreError("AudioMixer.__init__() was already called")
if sample_rate <= 0:
raise ValueError("sample_rate argument should be a non-negative integer")
if ec_tail_length < 0:
raise ValueError("ec_tail_length argument cannot be negative")
if sample_rate <= 0:
raise ValueError("sample_rate argument should be a non-negative integer")
if sample_rate % 50:
raise ValueError("sample_rate argument should be dividable by 50")
self.sample_rate = sample_rate
self.slot_count = slot_count
conf_pool_name = "AudioMixer_%d" % id(self)
with nogil:
pool = pjsip_endpt_create_pool(endpoint, conf_pool_name, 4096, 4096)
if pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._conf_pool = pool
with nogil:
status = pjmedia_conf_create(pool, slot_count+1, sample_rate, 1,
sample_rate / 50, 16, PJMEDIA_CONF_NO_DEVICE, conf_bridge_address)
if status != 0:
raise PJSIPError("Could not create audio mixer", status)
self._start_sound_device(ua, input_device, output_device, ec_tail_length, 0)
if self._disconnect_when_idle and not (input_device is None and output_device is None):
self._stop_sound_device(ua)
_add_handler(_AudioMixer_dealloc_handler, self, &_dealloc_handler_queue)
def __dealloc__(self):
global _dealloc_handler_queue
cdef PJSIPUA ua
cdef pjmedia_conf *conf_bridge
cdef pjsip_endpoint *endpoint
cdef pj_pool_t *pool
_remove_handler(self, &_dealloc_handler_queue)
try:
ua = _get_ua()
except:
return
conf_bridge = self._obj
endpoint = ua._pjsip_endpoint._obj
pool = self._conf_pool
self._stop_sound_device(ua)
if self._obj != NULL:
with nogil:
pjmedia_conf_destroy(conf_bridge)
self._obj = NULL
if self._conf_pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, pool)
self._conf_pool = NULL
pj_mutex_destroy(self._lock)
- def set_sound_devices(self, str input_device, str output_device, int ec_tail_length):
+ def set_sound_devices(self, unicode input_device, unicode output_device, int ec_tail_length):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
if ec_tail_length < 0:
raise ValueError("ec_tail_length argument cannot be negative")
self._stop_sound_device(ua)
self._start_sound_device(ua, input_device, output_device, ec_tail_length, 0)
if (self._disconnect_when_idle and self.used_slot_count == 0 and not
(input_device is None and output_device is None)):
self._stop_sound_device(ua)
finally:
with nogil:
pj_mutex_unlock(lock)
def connect_slots(self, int src_slot, int dst_slot):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef tuple connection
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
if src_slot < 0:
raise ValueError("src_slot argument cannot be negative")
if dst_slot < 0:
raise ValueError("d_slot argument cannot be negative")
connection = (src_slot, dst_slot)
if connection in self._connected_slots:
return
with nogil:
status = pjmedia_conf_connect_port(conf_bridge, src_slot, dst_slot, 0)
if status != 0:
raise PJSIPError("Could not connect slots on audio mixer", status)
self._connected_slots.append(connection)
finally:
with nogil:
pj_mutex_unlock(lock)
def disconnect_slots(self, int src_slot, int dst_slot):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef tuple connection
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
if src_slot < 0:
raise ValueError("src_slot argument cannot be negative")
if dst_slot < 0:
raise ValueError("d_slot argument cannot be negative")
connection = (src_slot, dst_slot)
if connection not in self._connected_slots:
return
with nogil:
status = pjmedia_conf_disconnect_port(conf_bridge, src_slot, dst_slot)
if status != 0:
raise PJSIPError("Could not disconnect slots on audio mixer", status)
self._connected_slots.remove(connection)
finally:
with nogil:
pj_mutex_unlock(lock)
# private methods
- cdef int _start_sound_device(self, PJSIPUA ua, str input_device, str output_device,
+ cdef int _start_sound_device(self, PJSIPUA ua, unicode input_device, unicode output_device,
int ec_tail_length, int revert_to_default) except -1:
+ global device_name_encoding
cdef int i
cdef int input_device_i = -2
cdef int output_device_i = -2
cdef int sample_rate = self.sample_rate
cdef int status
cdef pj_pool_t *conf_pool
cdef pj_pool_t *snd_pool
cdef pjmedia_conf *conf_bridge
cdef pjmedia_master_port **master_port_address
cdef pjmedia_port **null_port_address
cdef pjmedia_snd_dev_info_ptr_const dev_info
cdef pjmedia_snd_port **snd_port_address
cdef pjmedia_snd_stream_info snd_info
cdef pjsip_endpoint *endpoint
cdef str sound_pool_name
conf_bridge = self._obj
conf_pool = self._conf_pool
endpoint = ua._pjsip_endpoint._obj
master_port_address = &self._master_port
null_port_address = &self._null_port
sample_rate = self.sample_rate
snd_port_address = &self._snd
with nogil:
status = pj_rwmutex_lock_read(ua.audio_change_rwlock)
if status != 0:
raise SIPCoreError('Audio change lock could not be acquired for read', status)
try:
if pjmedia_snd_get_dev_count() == 0:
input_device = None
output_device = None
- if input_device == "system_default":
+ if input_device == u"system_default":
input_device_i = -1
- if output_device == "system_default":
+ if output_device == u"system_default":
output_device_i = -1
if ((input_device_i == -2 and input_device is not None) or
(output_device_i == -2 and output_device is not None)):
for i from 0 <= i < pjmedia_snd_get_dev_count():
dev_info = pjmedia_snd_get_dev_info(i)
if (input_device is not None and input_device_i == -2 and
- dev_info.input_count > 0 and dev_info.name == input_device):
+ dev_info.input_count > 0 and dev_info.name.decode(device_name_encoding) == input_device):
input_device_i = i
if (output_device is not None and output_device_i == -2 and
- dev_info.output_count > 0 and dev_info.name == output_device):
+ dev_info.output_count > 0 and dev_info.name.decode(device_name_encoding) == output_device):
output_device_i = i
if input_device_i == -2 and input_device is not None:
if revert_to_default:
input_device_i = -1
else:
raise SIPCoreError('Audio input device "%s" not found' % input_device)
if output_device_i == -2 and output_device is not None:
if revert_to_default:
output_device_i = -1
else:
raise SIPCoreError('Audio output device "%s" not found' % output_device)
if input_device is None and output_device is None:
with nogil:
status = pjmedia_null_port_create(conf_pool, sample_rate, 1,
sample_rate / 50, 16, null_port_address)
if status != 0:
raise PJSIPError("Could not create dummy audio port", status)
with nogil:
status = pjmedia_master_port_create(conf_pool, null_port_address[0],
pjmedia_conf_get_master_port(conf_bridge), 0, master_port_address)
if status != 0:
raise PJSIPError("Could not create master port for dummy sound device", status)
with nogil:
status = pjmedia_master_port_start(master_port_address[0])
if status != 0:
raise PJSIPError("Could not start master port for dummy sound device", status)
else:
snd_pool_name = "AudioMixer_snd_%d" % id(self)
with nogil:
snd_pool = pjsip_endpt_create_pool(endpoint, snd_pool_name, 4096, 4096)
if snd_pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._snd_pool = snd_pool
if input_device is None:
with nogil:
status = pjmedia_snd_port_create_player(snd_pool, output_device_i, sample_rate,
1, sample_rate / 50, 16, 0, snd_port_address)
elif output_device is None:
with nogil:
status = pjmedia_snd_port_create_rec(snd_pool, input_device_i, sample_rate,
1, sample_rate / 50, 16, 0, snd_port_address)
else:
with nogil:
status = pjmedia_snd_port_create(snd_pool, input_device_i, output_device_i,
sample_rate, 1, sample_rate / 50, 16, 0, snd_port_address)
if status == PJMEDIA_ENOSNDPLAY:
with nogil:
pjsip_endpt_release_pool(endpoint, snd_pool)
self._snd_pool = NULL
return self._start_sound_device(ua, input_device, None, ec_tail_length, revert_to_default)
elif status == PJMEDIA_ENOSNDREC:
with nogil:
pjsip_endpt_release_pool(endpoint, snd_pool)
self._snd_pool = NULL
return self._start_sound_device(ua, None, output_device, ec_tail_length, revert_to_default)
elif status != 0:
raise PJSIPError("Could not create sound device", status)
if input_device is not None and output_device is not None:
with nogil:
status = pjmedia_snd_port_set_ec(snd_port_address[0], snd_pool, ec_tail_length, 0)
if status != 0:
self._stop_sound_device(ua)
raise PJSIPError("Could not set echo cancellation", status)
with nogil:
status = pjmedia_snd_port_connect(snd_port_address[0], pjmedia_conf_get_master_port(conf_bridge))
if status != 0:
self._stop_sound_device(ua)
raise PJSIPError("Could not connect sound device", status)
if input_device_i == -1 or output_device_i == -1:
with nogil:
status = pjmedia_snd_stream_get_info(pjmedia_snd_port_get_snd_stream(snd_port_address[0]), &snd_info)
if status != 0:
self._stop_sound_device(ua)
raise PJSIPError("Could not get sounds device info", status)
if input_device_i == -1:
with nogil:
dev_info = pjmedia_snd_get_dev_info(snd_info.rec_id)
- self.real_input_device = dev_info.name
+ self.real_input_device = dev_info.name.decode(device_name_encoding)
if output_device_i == -1:
with nogil:
dev_info = pjmedia_snd_get_dev_info(snd_info.play_id)
- self.real_output_device = dev_info.name
+ self.real_output_device = dev_info.name.decode(device_name_encoding)
if input_device_i != -1:
self.real_input_device = input_device
if output_device_i != -1:
self.real_output_device = output_device
self.input_device = input_device
self.output_device = output_device
self.ec_tail_length = ec_tail_length
return 0
finally:
with nogil:
pj_rwmutex_unlock_read(ua.audio_change_rwlock)
cdef int _stop_sound_device(self, PJSIPUA ua) except -1:
cdef pj_pool_t *snd_pool
cdef pjmedia_master_port *master_port
cdef pjmedia_port *null_port
cdef pjmedia_snd_port *snd_port
cdef pjsip_endpoint *endpoint
endpoint = ua._pjsip_endpoint._obj
master_port = self._master_port
null_port = self._null_port
snd_pool = self._snd_pool
snd_port = self._snd
if self._snd != NULL:
with nogil:
pjmedia_snd_port_destroy(snd_port)
self._snd = NULL
if self._snd_pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, snd_pool)
self._snd_pool = NULL
if self._master_port != NULL:
with nogil:
pjmedia_master_port_destroy(master_port, 0)
self._master_port = NULL
if self._null_port != NULL:
with nogil:
pjmedia_port_destroy(null_port)
self._null_port = NULL
return 0
cdef int _add_port(self, PJSIPUA ua, pj_pool_t *pool, pjmedia_port *port) except -1 with gil:
cdef int input_device_i
cdef int output_device_i
cdef unsigned int slot
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf* conf_bridge
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
with nogil:
status = pjmedia_conf_add_port(conf_bridge, pool, port, NULL, &slot)
if status != 0:
raise PJSIPError("Could not add audio object to audio mixer", status)
self.used_slot_count += 1
if (self.used_slot_count == 1 and self._disconnect_when_idle and
not (self.input_device is None and self.output_device is None) and
self._snd == NULL):
self._start_sound_device(ua, self.input_device, self.output_device, self.ec_tail_length, 1)
return slot
finally:
with nogil:
pj_mutex_unlock(lock)
cdef int _remove_port(self, PJSIPUA ua, unsigned int slot) except -1 with gil:
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf* conf_bridge
cdef tuple connection
cdef Timer timer
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self._obj
with nogil:
status = pjmedia_conf_remove_port(conf_bridge, slot)
if status != 0:
raise PJSIPError("Could not remove audio object from audio mixer", status)
self._connected_slots = [connection for connection in self._connected_slots if slot not in connection]
self.used_slot_count -= 1
if (self.used_slot_count == 0 and self._disconnect_when_idle and
not (self.input_device is None and self.output_device is None)):
timer = Timer()
timer.schedule(0, <timer_callback>self._cb_postpoll_stop_sound, self)
return 0
finally:
with nogil:
pj_mutex_unlock(lock)
cdef int _cb_postpoll_stop_sound(self, timer) except -1:
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
if self.used_slot_count == 0:
self._stop_sound_device(ua)
finally:
with nogil:
pj_mutex_unlock(lock)
cdef class ToneGenerator:
# properties
property volume:
def __get__(self):
return self._volume
def __set__(self, value):
cdef int slot
cdef int volume
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef PJSIPUA ua
ua = self._get_ua(0)
if ua is not None:
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self.mixer._obj
slot = self._slot
if value < 0:
raise ValueError("volume attribute cannot be negative")
if ua is not None and self._slot != -1:
volume = int(value * 1.28 - 128)
with nogil:
status = pjmedia_conf_adjust_rx_level(conf_bridge, slot, volume)
if status != 0:
raise PJSIPError("Could not set volume of tone generator", status)
self._volume = value
finally:
if ua is not None:
with nogil:
pj_mutex_unlock(lock)
property slot:
def __get__(self):
self._get_ua(0)
if self._slot == -1:
return None
else:
return self._slot
property is_active:
def __get__(self):
self._get_ua(0)
return bool(self._slot != -1)
property is_busy:
def __get__(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_port *port
cdef PJSIPUA ua
ua = self._get_ua(0)
if ua is None:
return False
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
port = self._obj
if self._obj == NULL:
return False
with nogil:
status = pjmedia_tonegen_is_busy(port)
return bool(status)
finally:
with nogil:
pj_mutex_unlock(lock)
# public methods
def __cinit__(self, *args, **kwargs):
cdef pj_pool_t *pool
cdef pjsip_endpoint *endpoint
cdef str pool_name
cdef PJSIPUA ua
ua = _get_ua()
endpoint = ua._pjsip_endpoint._obj
pj_mutex_create_recursive(ua._pjsip_endpoint._pool, "tone_generator_lock", &self._lock)
pool_name = "ToneGenerator_%d" % id(self)
with nogil:
pool = pjsip_endpt_create_pool(endpoint, pool_name, 4096, 4096)
if pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._pool = pool
self._slot = -1
self._timer = None
self._volume = 100
def __init__(self, AudioMixer mixer):
cdef int sample_rate
cdef int status
cdef pj_pool_t *pool
cdef pjmedia_port **port_address
cdef PJSIPUA ua
ua = _get_ua()
pool = self._pool
port_address = &self._obj
sample_rate = mixer.sample_rate
if self._obj != NULL:
raise SIPCoreError("ToneGenerator.__init__() was already called")
if mixer is None:
raise ValueError("mixer argument may not be None")
self.mixer = mixer
with nogil:
status = pjmedia_tonegen_create(pool, sample_rate, 1,
sample_rate / 50, 16, 0, port_address)
if status != 0:
raise PJSIPError("Could not create tone generator", status)
def start(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._get_ua(1)
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
if self._slot != -1:
return
self._slot = self.mixer._add_port(ua, self._pool, self._obj)
if self._volume != 100:
self.volume = self._volume
finally:
with nogil:
pj_mutex_unlock(lock)
def stop(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._get_ua(0)
if ua is None:
return
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
if self._slot == -1:
return
self._stop(ua)
finally:
with nogil:
pj_mutex_unlock(lock)
def __dealloc__(self):
cdef pj_pool_t *pool
cdef pjmedia_port *port
cdef pjsip_endpoint *endpoint
cdef PJSIPUA ua
ua = self._get_ua(0)
if ua is None:
return
endpoint = ua._pjsip_endpoint._obj
pool = self._pool
port = self._obj
self._stop(ua)
if self._obj != NULL:
with nogil:
pjmedia_tonegen_stop(port)
self._obj = NULL
if self._pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, pool)
self._pool = NULL
pj_mutex_destroy(self._lock)
def play_tones(self, object tones):
cdef unsigned int count = 0
cdef int duration
cdef int freq1
cdef int freq2
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_port *port
cdef pjmedia_tone_desc tones_arr[PJMEDIA_TONEGEN_MAX_DIGITS]
cdef PJSIPUA ua
ua = self._get_ua(1)
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
port = self._obj
if self._slot == -1:
raise SIPCoreError("ToneGenerator has not yet been started")
for freq1, freq2, duration in tones:
if freq1 == 0 and count > 0:
tones_arr[count-1].off_msec += duration
else:
if count >= PJMEDIA_TONEGEN_MAX_DIGITS:
raise SIPCoreError("Too many tones")
tones_arr[count].freq1 = freq1
tones_arr[count].freq2 = freq2
tones_arr[count].on_msec = duration
tones_arr[count].off_msec = 0
tones_arr[count].volume = 0
tones_arr[count].flags = 0
count += 1
if count > 0:
with nogil:
status = pjmedia_tonegen_play(port, count, tones_arr, 0)
if status != 0 and status != PJ_ETOOMANY:
raise PJSIPError("Could not playback tones", status)
if self._timer is None:
self._timer = Timer()
self._timer.schedule(0.250, <timer_callback>self._cb_check_done, self)
finally:
with nogil:
pj_mutex_unlock(lock)
def play_dtmf(self, str digit):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_port *port
cdef pjmedia_tone_digit tone
cdef PJSIPUA ua
ua = self._get_ua(1)
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
port = self._obj
if self._slot == -1:
raise SIPCoreError("ToneGenerator has not yet been started")
tone.digit = ord(digit)
tone.on_msec = 200
tone.off_msec = 50
tone.volume = 0
with nogil:
status = pjmedia_tonegen_play_digits(port, 1, &tone, 0)
if status != 0 and status != PJ_ETOOMANY:
raise PJSIPError("Could not playback DTMF tone", status)
if self._timer is None:
self._timer = Timer()
self._timer.schedule(0.250, <timer_callback>self._cb_check_done, self)
finally:
with nogil:
pj_mutex_unlock(lock)
# private methods
cdef PJSIPUA _get_ua(self, int raise_exception):
cdef PJSIPUA ua
try:
ua = _get_ua()
except SIPCoreError:
self._obj = NULL
self._pool = NULL
self._slot = -1
self._timer = None
if raise_exception:
raise
else:
return None
else:
return ua
cdef int _stop(self, PJSIPUA ua) except -1:
if self._timer is not None:
self._timer.cancel()
self._timer = None
if self._slot != -1:
self.mixer._remove_port(ua, self._slot)
self._slot = -1
return 0
cdef int _cb_check_done(self, timer) except -1:
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_port *port
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
port = self._obj
with nogil:
status = pjmedia_tonegen_is_busy(port)
if status:
self._timer = Timer()
self._timer.schedule(0.250, <timer_callback>self._cb_check_done, self)
else:
self._timer = None
_add_event("ToneGeneratorDidFinishPlaying", dict(obj=self))
finally:
with nogil:
pj_mutex_unlock(lock)
cdef class RecordingWaveFile:
def __cinit__(self, *args, **kwargs):
pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "recording_wave_file_lock", &self._lock)
self._slot = -1
def __init__(self, AudioMixer mixer, str filename):
if self.filename is not None:
raise SIPCoreError("RecordingWaveFile.__init__() was already called")
if mixer is None:
raise ValueError("mixer argument may not be None")
if filename is None:
raise ValueError("filename argument may not be None")
self.mixer = mixer
self.filename = filename
cdef PJSIPUA _check_ua(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
return ua
except:
self._pool = NULL
self._port = NULL
self._slot = -1
return None
property is_active:
def __get__(self):
self._check_ua()
return self._slot != -1
property slot:
def __get__(self):
self._check_ua()
if self._slot == -1:
return None
else:
return self._slot
def start(self):
cdef char *filename
cdef int sample_rate
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pj_pool_t *pool
cdef pjmedia_port **port_address
cdef pjsip_endpoint *endpoint
cdef str pool_name
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
endpoint = ua._pjsip_endpoint._obj
filename = PyString_AsString(self.filename)
pool_name = "RecordingWaveFile_%d" % id(self)
port_address = &self._port
sample_rate = self.mixer.sample_rate
if self._was_started:
raise SIPCoreError("This RecordingWaveFile was already started once")
with nogil:
pool = pjsip_endpt_create_pool(endpoint, pool_name, 4096, 4096)
if pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._pool = pool
try:
with nogil:
status = pjmedia_wav_writer_port_create(pool, filename,
sample_rate, 1,
sample_rate / 50, 16,
PJMEDIA_FILE_WRITE_PCM, 0, port_address)
if status != 0:
raise PJSIPError("Could not create WAV file", status)
self._slot = self.mixer._add_port(ua, self._pool, self._port)
except:
self.stop()
raise
self._was_started = 1
finally:
with nogil:
pj_mutex_unlock(lock)
def stop(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._check_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
self._stop(ua)
finally:
with nogil:
pj_mutex_unlock(lock)
cdef int _stop(self, PJSIPUA ua) except -1:
cdef pj_pool_t *pool
cdef pjmedia_port *port
cdef pjsip_endpoint *endpoint
endpoint = ua._pjsip_endpoint._obj if ua is not None else NULL
pool = self._pool
port = self._port
if self._slot != -1:
self.mixer._remove_port(ua, self._slot)
self._slot = -1
if self._port != NULL:
with nogil:
pjmedia_port_destroy(port)
self._port = NULL
if self._pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, pool)
self._pool = NULL
return 0
def __dealloc__(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
except:
return
self._stop(ua)
pj_mutex_destroy(self._lock)
cdef class WaveFile:
def __cinit__(self, *args, **kwargs):
self.weakref = weakref.ref(self)
Py_INCREF(self.weakref)
pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "wave_file_lock", &self._lock)
self._slot = -1
self._volume = 100
def __init__(self, AudioMixer mixer, str filename):
if self.filename is not None:
raise SIPCoreError("WaveFile.__init__() was already called")
if mixer is None:
raise ValueError("mixer argument may not be None")
if filename is None:
raise ValueError("filename argument may not be None")
self.mixer = mixer
self.filename = filename
cdef PJSIPUA _check_ua(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
return ua
except:
self._pool = NULL
self._port = NULL
self._slot = -1
return None
property is_active:
def __get__(self):
self._check_ua()
return self._port != NULL
property slot:
def __get__(self):
self._check_ua()
if self._slot == -1:
return None
else:
return self._slot
property volume:
def __get__(self):
return self._volume
def __set__(self, value):
cdef int slot
cdef int status
cdef int volume
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_conf *conf_bridge
cdef PJSIPUA ua
ua = self._check_ua()
if ua is not None:
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
conf_bridge = self.mixer._obj
slot = self._slot
if value < 0:
raise ValueError("volume attribute cannot be negative")
if ua is not None and self._slot != -1:
volume = int(value * 1.28 - 128)
with nogil:
status = pjmedia_conf_adjust_rx_level(conf_bridge, slot, volume)
if status != 0:
raise PJSIPError("Could not set volume of .wav file", status)
self._volume = value
finally:
if ua is not None:
with nogil:
pj_mutex_unlock(lock)
def start(self):
cdef char *filename
cdef int status
cdef void *weakref
cdef pj_pool_t *pool
cdef pj_mutex_t *lock = self._lock
cdef pjmedia_port **port_address
cdef pjsip_endpoint *endpoint
cdef str pool_name
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
endpoint = ua._pjsip_endpoint._obj
filename = PyString_AsString(self.filename)
port_address = &self._port
weakref = <void *> self.weakref
if self._port != NULL:
raise SIPCoreError("WAV file is already playing")
pool_name = "WaveFile_%d" % id(self)
with nogil:
pool = pjsip_endpt_create_pool(endpoint, pool_name, 4096, 4096)
if pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._pool = pool
try:
with nogil:
status = pjmedia_wav_player_port_create(pool, filename, 0, PJMEDIA_FILE_NO_LOOP, 0, port_address)
if status != 0:
raise PJSIPError("Could not open WAV file", status)
with nogil:
status = pjmedia_wav_player_set_eof_cb(port_address[0], weakref, cb_play_wav_eof)
if status != 0:
raise PJSIPError("Could not set WAV EOF callback", status)
self._slot = self.mixer._add_port(ua, self._pool, self._port)
if self._volume != 100:
self.volume = self._volume
except:
self._stop(ua, 0)
raise
finally:
with nogil:
pj_mutex_unlock(lock)
cdef int _stop(self, PJSIPUA ua, int notify) except -1:
cdef int status
cdef int was_active
cdef pj_pool_t *pool
cdef pjmedia_port *port
cdef pjsip_endpoint *endpoint
endpoint = ua._pjsip_endpoint._obj
pool = self._pool
port = self._port
was_active = 0
if self._slot != -1:
was_active = 1
self.mixer._remove_port(ua, self._slot)
self._slot = -1
if self._port != NULL:
with nogil:
pjmedia_port_destroy(port)
self._port = NULL
was_active = 1
if self._pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, pool)
self._pool = NULL
if notify and was_active:
_add_event("WaveFileDidFinishPlaying", dict(obj=self))
def stop(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._check_ua()
if ua is None:
return
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
self._stop(ua, 1)
finally:
with nogil:
pj_mutex_unlock(lock)
def __dealloc__(self):
cdef PJSIPUA ua
cdef Timer timer
try:
ua = _get_ua()
except:
return
self._stop(ua, 0)
timer = Timer()
try:
timer.schedule(60, deallocate_weakref, self.weakref)
except SIPCoreError:
pass
pj_mutex_destroy(self._lock)
cdef int _cb_eof(self, timer) except -1:
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._check_ua()
if ua is None:
return 0
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
self._stop(ua, 1)
finally:
with nogil:
pj_mutex_unlock(lock)
cdef class MixerPort:
def __cinit__(self, *args, **kwargs):
pj_mutex_create_recursive(_get_ua()._pjsip_endpoint._pool, "mixer_port_lock", &self._lock)
self._slot = -1
def __init__(self, AudioMixer mixer):
if self.mixer is not None:
raise SIPCoreError("MixerPort.__init__() was already called")
if mixer is None:
raise ValueError("mixer argument may not be None")
self.mixer = mixer
cdef PJSIPUA _check_ua(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
return ua
except:
self._pool = NULL
self._port = NULL
self._slot = -1
return None
property is_active:
def __get__(self):
self._check_ua()
return self._slot != -1
property slot:
def __get__(self):
self._check_ua()
if self._slot == -1:
return None
else:
return self._slot
def start(self):
cdef int sample_rate
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef pj_pool_t *pool
cdef pjmedia_port **port_address
cdef pjsip_endpoint *endpoint
cdef str pool_name
cdef PJSIPUA ua
ua = _get_ua()
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
endpoint = ua._pjsip_endpoint._obj
pool_name = "MixerPort_%d" % id(self)
port_address = &self._port
sample_rate = self.mixer.sample_rate
if self._was_started:
raise SIPCoreError("This MixerPort was already started once")
with nogil:
pool = pjsip_endpt_create_pool(endpoint, pool_name, 4096, 4096)
if pool == NULL:
raise SIPCoreError("Could not allocate memory pool")
self._pool = pool
try:
with nogil:
status = pjmedia_mixer_port_create(pool, sample_rate, 1, sample_rate / 50, 16, port_address)
if status != 0:
raise PJSIPError("Could not create WAV file", status)
self._slot = self.mixer._add_port(ua, self._pool, self._port)
except:
self.stop()
raise
self._was_started = 1
finally:
with nogil:
pj_mutex_unlock(lock)
def stop(self):
cdef int status
cdef pj_mutex_t *lock = self._lock
cdef PJSIPUA ua
ua = self._check_ua()
if ua is None:
return
with nogil:
status = pj_mutex_lock(lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
self._stop(ua)
finally:
with nogil:
pj_mutex_unlock(lock)
cdef int _stop(self, PJSIPUA ua) except -1:
cdef pj_pool_t *pool
cdef pjmedia_port *port
cdef pjsip_endpoint *endpoint
endpoint = ua._pjsip_endpoint._obj if ua is not None else NULL
pool = self._pool
port = self._port
if self._slot != -1:
self.mixer._remove_port(ua, self._slot)
self._slot = -1
if self._port != NULL:
with nogil:
pjmedia_port_destroy(port)
self._port = NULL
if self._pool != NULL:
with nogil:
pjsip_endpt_release_pool(endpoint, pool)
self._pool = NULL
return 0
def __dealloc__(self):
cdef PJSIPUA ua
try:
ua = _get_ua()
except:
return
self._stop(ua)
pj_mutex_destroy(self._lock)
# callback functions
cdef int _AudioMixer_dealloc_handler(object obj) except -1:
cdef int status
cdef AudioMixer mixer = obj
cdef PJSIPUA ua
ua = _get_ua()
status = pj_mutex_lock(mixer._lock)
if status != 0:
raise PJSIPError("failed to acquire lock", status)
try:
mixer._stop_sound_device(ua)
mixer._connected_slots = list()
mixer.used_slot_count = 0
finally:
pj_mutex_unlock(mixer._lock)
cdef int cb_play_wav_eof(pjmedia_port *port, void *user_data) with gil:
cdef Timer timer
cdef WaveFile wav_file
wav_file = (<object> user_data)()
if wav_file is not None:
timer = Timer()
timer.schedule(0, <timer_callback>wav_file._cb_eof, wav_file)
# do not return PJ_SUCCESS because if you do pjsip will access the just deallocated port
return 1
diff --git a/sipsimple/core/_core.ua.pxi b/sipsimple/core/_core.ua.pxi
index adc2e991..e218fd6b 100644
--- a/sipsimple/core/_core.ua.pxi
+++ b/sipsimple/core/_core.ua.pxi
@@ -1,976 +1,978 @@
# Copyright (C) 2008-2009 AG Projects. See LICENSE for details.
#
# python imports
import random
import sys
import time
import traceback
import os
from errno import EBADF
# classes
cdef class Timer:
cdef int schedule(self, float delay, timer_callback callback, object obj) except -1:
cdef PJSIPUA ua = _get_ua()
if delay < 0:
raise ValueError("delay must be a non-negative number")
if callback == NULL:
raise ValueError("callback must be non-NULL")
if self._scheduled:
raise RuntimeError("already scheduled")
self.schedule_time = PyFloat_AsDouble(time.time() + delay)
self.callback = callback
self.obj = obj
ua._add_timer(self)
self._scheduled = 1
return 0
cdef int cancel(self) except -1:
cdef PJSIPUA ua = _get_ua()
if not self._scheduled:
return 0
ua._remove_timer(self)
self._scheduled = 0
return 0
cdef int call(self) except -1:
self._scheduled = 0
self.callback(self.obj, self)
def __richcmp__(self, other, op):
cdef double diff
if not isinstance(self, Timer) or not isinstance(other, Timer):
return NotImplemented
diff = (<Timer>self).schedule_time - (<Timer>other).schedule_time
if op == 0: # <
return diff < 0.0
elif op == 1: # <=
return diff <= 0.0
elif op == 2: # ==
return diff == 0.0
elif op == 3: # !=
return diff != 0.0
elif op == 4: # >
return diff > 0.0
elif op == 5: # >=
return diff >= 0.0
return
cdef class PJSIPUA:
def __cinit__(self, *args, **kwargs):
global _ua
if _ua != NULL:
raise SIPCoreError("Can only have one PJSUPUA instance at the same time")
_ua = <void *> self
self._threads = []
self._timers = list()
self._events = {}
self._incoming_events = set()
self._incoming_requests = set()
self._sent_messages = set()
def __init__(self, event_handler, *args, **kwargs):
global _event_queue_lock
cdef str event
cdef str method
cdef list accept_types
cdef int status
cdef PJSTR message_method = PJSTR("MESSAGE")
self._event_handler = event_handler
if kwargs["log_level"] < 0 or kwargs["log_level"] > PJ_LOG_MAX_LEVEL:
raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL)
pj_log_set_level(kwargs["log_level"])
pj_log_set_decor(PJ_LOG_HAS_YEAR | PJ_LOG_HAS_MONTH | PJ_LOG_HAS_DAY_OF_MON |
PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_SENDER)
pj_log_set_log_func(_cb_log)
self._pjlib = PJLIB()
pj_srand(random.getrandbits(32)) # rely on python seed for now
self._caching_pool = PJCachingPool()
self._pjmedia_endpoint = PJMEDIAEndpoint(self._caching_pool)
self._pjsip_endpoint = PJSIPEndpoint(self._caching_pool, kwargs["ip_address"], kwargs["udp_port"],
kwargs["tcp_port"], kwargs["tls_port"], kwargs["tls_protocol"],
kwargs["tls_verify_server"], kwargs["tls_ca_file"],
kwargs["tls_cert_file"], kwargs["tls_privkey_file"], kwargs["tls_timeout"])
status = pj_mutex_create_simple(self._pjsip_endpoint._pool, "event_queue_lock", &_event_queue_lock)
if status != 0:
raise PJSIPError("Could not initialize event queue mutex", status)
self.codecs = kwargs["codecs"]
self._module_name = PJSTR("mod-core")
self._module.name = self._module_name.pj_str
self._module.id = -1
self._module.priority = PJSIP_MOD_PRIORITY_APPLICATION
self._module.on_rx_request = _PJSIPUA_cb_rx_request
self._module.on_tsx_state = _Request_cb_tsx_state
status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._module)
if status != 0:
raise PJSIPError("Could not load application module", status)
status = pjsip_endpt_add_capability(self._pjsip_endpoint._obj, &self._module,
PJSIP_H_ALLOW, NULL, 1, &message_method.pj_str)
if status != 0:
raise PJSIPError("Could not add MESSAGE method to supported methods", status)
self._trace_sip = int(bool(kwargs["trace_sip"]))
self._ignore_missing_ack = int(bool(kwargs["ignore_missing_ack"]))
self._trace_module_name = PJSTR("mod-core-sip-trace")
self._trace_module.name = self._trace_module_name.pj_str
self._trace_module.id = -1
self._trace_module.priority = 0
self._trace_module.on_rx_request = _cb_trace_rx
self._trace_module.on_rx_response = _cb_trace_rx
self._trace_module.on_tx_request = _cb_trace_tx
self._trace_module.on_tx_response = _cb_trace_tx
status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._trace_module)
if status != 0:
raise PJSIPError("Could not load sip trace module", status)
self._ua_tag_module_name = PJSTR("mod-core-ua-tag")
self._ua_tag_module.name = self._ua_tag_module_name.pj_str
self._ua_tag_module.id = -1
self._ua_tag_module.priority = PJSIP_MOD_PRIORITY_TRANSPORT_LAYER+1
self._ua_tag_module.on_tx_request = _cb_add_user_agent_hdr
self._ua_tag_module.on_tx_response = _cb_add_server_hdr
status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._ua_tag_module)
if status != 0:
raise PJSIPError("Could not load User-Agent/Server header tagging module", status)
self._event_module_name = PJSTR("mod-core-events")
self._event_module.name = self._event_module_name.pj_str
self._event_module.id = -1
self._event_module.priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE
status = pjsip_endpt_register_module(self._pjsip_endpoint._obj, &self._event_module)
if status != 0:
raise PJSIPError("Could not load events module", status)
self._audio_change_observer.default_audio_change = _cb_default_audio_change
self._audio_change_observer.audio_devices_will_change = _cb_audio_devices_will_change
self._audio_change_observer.audio_devices_did_change = _cb_audio_devices_did_change
status = pjmedia_add_audio_change_observer(&self._audio_change_observer);
if status != 0:
raise PJSIPError("Could not set audio_change callbacks", status)
status = pj_rwmutex_create(self._pjsip_endpoint._pool, "ua_audio_change_rwlock", &self.audio_change_rwlock)
if status != 0:
raise PJSIPError("Could not initialize audio change rwmutex", status)
self._user_agent = PJSTR(kwargs["user_agent"])
for event, accept_types in kwargs["events"].iteritems():
self.add_event(event, accept_types)
for event in kwargs["incoming_events"]:
if event not in self._events.iterkeys():
raise ValueError('Event "%s" is not known' % event)
self._incoming_events.add(event)
for method in kwargs["incoming_requests"]:
method = method.upper()
if method in ("ACK", "BYE", "INVITE", "SUBSCRIBE"):
raise ValueError('Handling incoming "%s" requests is not allowed' % method)
self._incoming_requests.add(method)
self.rtp_port_range = kwargs["rtp_port_range"]
pj_stun_config_init(&self._stun_cfg, &self._caching_pool._obj.factory, 0,
pjmedia_endpt_get_ioqueue(self._pjmedia_endpoint._obj),
pjsip_endpt_get_timer_heap(self._pjsip_endpoint._obj))
property trace_sip:
def __get__(self):
self._check_self()
return bool(self._trace_sip)
def __set__(self, value):
self._check_self()
self._trace_sip = int(bool(value))
property ignore_missing_ack:
def __get__(self):
self._check_self()
return bool(self._ignore_missing_ack)
def __set__(self, value):
self._check_self()
self._ignore_missing_ack = int(bool(value))
property events:
def __get__(self):
self._check_self()
return self._events.copy()
def add_event(self, object event, list accept_types):
cdef pj_str_t event_pj
cdef pj_str_t accept_types_pj[PJSIP_MAX_ACCEPT_COUNT]
cdef int index
cdef object accept_type
cdef int accept_cnt = len(accept_types)
cdef int status
self._check_self()
if accept_cnt == 0:
raise SIPCoreError("Need at least one of accept_types")
if accept_cnt > PJSIP_MAX_ACCEPT_COUNT:
raise SIPCoreError("Too many accept_types")
_str_to_pj_str(event, &event_pj)
for index, accept_type in enumerate(accept_types):
_str_to_pj_str(accept_type, &accept_types_pj[index])
status = pjsip_evsub_register_pkg(&self._event_module, &event_pj, 3600, accept_cnt, accept_types_pj)
if status != 0:
raise PJSIPError("Could not register event package", status)
self._events[event] = accept_types[:]
property incoming_events:
def __get__(self):
self._check_self()
return self._incoming_events.copy()
def add_incoming_event(self, str event):
self._check_self()
if event not in self._events.iterkeys():
raise ValueError('Event "%s" is not known' % event)
self._incoming_events.add(event)
def remove_incoming_event(self, str event):
self._check_self()
if event not in self._events.iterkeys():
raise ValueError('Event "%s" is not known' % event)
self._incoming_events.discard(event)
property incoming_requests:
def __get__(self):
self._check_self()
return self._incoming_requests.copy()
def add_incoming_request(self, object value):
cdef str method
self._check_self()
method = value.upper()
if method in ("ACK", "BYE", "INVITE", "SUBSCRIBE"):
raise ValueError('Handling incoming "%s" requests is not allowed' % method)
self._incoming_requests.add(method)
def remove_incoming_request(self, object value):
cdef str method
self._check_self()
method = value.upper()
if method in ("ACK", "BYE", "INVITE", "SUBSCRIBE"):
raise ValueError('Handling incoming "%s" requests is not allowed' % method)
self._incoming_requests.discard(method)
cdef object _get_sound_devices(self, int is_output):
+ global device_name_encoding
cdef int i
cdef int count
cdef pjmedia_snd_dev_info_ptr_const info
cdef list retval = list()
cdef int status
with nogil:
status = pj_rwmutex_lock_read(self.audio_change_rwlock)
if status != 0:
raise SIPCoreError('Could not acquire audio_change_rwlock', status)
try:
for i from 0 <= i < pjmedia_snd_get_dev_count():
info = pjmedia_snd_get_dev_info(i)
if is_output:
count = info.output_count
else:
count = info.input_count
if count:
- retval.append(info.name)
+ retval.append(info.name.decode(device_name_encoding))
return retval
finally:
pj_rwmutex_unlock_read(self.audio_change_rwlock)
property output_devices:
def __get__(self):
self._check_self()
return self._get_sound_devices(1)
property input_devices:
def __get__(self):
self._check_self()
return self._get_sound_devices(0)
property sound_devices:
def __get__(self):
self._check_self()
+ global device_name_encoding
cdef int i
cdef int count
cdef pjmedia_snd_dev_info_ptr_const info
cdef list retval = list()
cdef int status
with nogil:
status = pj_rwmutex_lock_read(self.audio_change_rwlock)
if status != 0:
raise SIPCoreError('Could not acquire audio_change_rwlock', status)
try:
for i from 0 <= i < pjmedia_snd_get_dev_count():
with nogil:
info = pjmedia_snd_get_dev_info(i)
if info != NULL:
- retval.append(info.name)
+ retval.append(info.name.decode(device_name_encoding))
return retval
finally:
pj_rwmutex_unlock_read(self.audio_change_rwlock)
property available_codecs:
def __get__(self):
self._check_self()
return self._pjmedia_endpoint._get_all_codecs()
property codecs:
def __get__(self):
self._check_self()
return self._pjmedia_endpoint._get_current_codecs()
def __set__(self, value):
self._check_self()
self._pjmedia_endpoint._set_codecs(value, 32000)
property ip_address:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._udp_transport != NULL:
return _pj_str_to_str(self._pjsip_endpoint._udp_transport.local_name.host)
elif self._pjsip_endpoint._tcp_transport != NULL:
return _pj_str_to_str(self._pjsip_endpoint._tcp_transport.addr_name.host)
elif self._pjsip_endpoint._tls_transport != NULL:
return _pj_str_to_str(self._pjsip_endpoint._tls_transport.addr_name.host)
else:
return None
property udp_port:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._udp_transport == NULL:
return None
return self._pjsip_endpoint._udp_transport.local_name.port
def set_udp_port(self, value):
cdef int port
self._check_self()
if value is None:
if self._pjsip_endpoint._udp_transport == NULL:
return
self._pjsip_endpoint._stop_udp_transport()
else:
port = value
if not (0 <= port <= 65535):
raise ValueError("Not a valid UDP port: %d" % value)
if self._pjsip_endpoint._udp_transport != NULL:
if port == self._pjsip_endpoint._udp_transport.local_name.port:
return
self._pjsip_endpoint._stop_udp_transport()
self._pjsip_endpoint._start_udp_transport(port)
property tcp_port:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._tcp_transport == NULL:
return None
return self._pjsip_endpoint._tcp_transport.addr_name.port
def set_tcp_port(self, value):
cdef int port
self._check_self()
if value is None:
if self._pjsip_endpoint._tcp_transport == NULL:
return
self._pjsip_endpoint._stop_tcp_transport()
else:
port = value
if not (0 <= port <= 65535):
raise ValueError("Not a valid TCP port: %d" % value)
if self._pjsip_endpoint._tcp_transport != NULL:
if port == self._pjsip_endpoint._tcp_transport.addr_name.port:
return
self._pjsip_endpoint._stop_tcp_transport()
self._pjsip_endpoint._start_tcp_transport(port)
property tls_port:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._tls_transport == NULL:
return None
return self._pjsip_endpoint._tls_transport.addr_name.port
property rtp_port_range:
def __get__(self):
self._check_self()
return (self._rtp_port_start, self._rtp_port_start + self._rtp_port_count)
def __set__(self, value):
cdef int _rtp_port_start
cdef int _rtp_port_stop
cdef int _rtp_port_count
cdef int _rtp_port_usable_count
cdef int port
self._check_self()
for port in value:
if not (0 <= port <= 65535):
raise SIPCoreError("RTP port range values should be between 0 and 65535")
_rtp_port_start, _rtp_port_stop = value
_rtp_port_count = _rtp_port_stop - _rtp_port_start
_rtp_port_usable_count = _rtp_port_count - _rtp_port_count % 2 # we need an even number of ports, so we won't use the last one if an odd number is provided
if _rtp_port_usable_count < 2:
raise SIPCoreError("RTP port range should contain at least 2 ports")
self._rtp_port_start = _rtp_port_start
self._rtp_port_count = _rtp_port_count
self._rtp_port_usable_count = _rtp_port_usable_count
self._rtp_port_index = 0
property user_agent:
def __get__(self):
self._check_self()
return self._user_agent.str
def __set__(self, value):
self._check_self()
self._user_agent = PJSTR("value")
property log_level:
def __get__(self):
self._check_self()
return pj_log_get_level()
def __set__(self, value):
self._check_self()
if value < 0 or value > PJ_LOG_MAX_LEVEL:
raise ValueError("Log level should be between 0 and %d" % PJ_LOG_MAX_LEVEL)
pj_log_set_level(value)
property tls_protocol:
def __get__(self):
self._check_self()
return self._pjsip_endpoint._tls_protocol
property tls_verify_server:
def __get__(self):
self._check_self()
return bool(self._pjsip_endpoint._tls_verify_server)
property tls_ca_file:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._tls_ca_file is None:
return None
else:
return self._pjsip_endpoint._tls_ca_file.str
property tls_cert_file:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._tls_cert_file is None:
return None
else:
return self._pjsip_endpoint._tls_cert_file.str
property tls_privkey_file:
def __get__(self):
self._check_self()
if self._pjsip_endpoint._tls_privkey_file is None:
return None
else:
return self._pjsip_endpoint._tls_privkey_file.str
property tls_timeout:
def __get__(self):
self._check_self()
return self._pjsip_endpoint._tls_timeout
def set_tls_options(self, port=None, protocol="TLSv1", verify_server=False,
ca_file=None, cert_file=None, privkey_file=None, int timeout=1000):
global _tls_protocol_mapping
cdef int c_port
self._check_self()
if port is None:
if self._pjsip_endpoint._tls_transport == NULL:
return
self._pjsip_endpoint._stop_tls_transport()
else:
c_port = port
if not (0 <= c_port <= 65535):
raise ValueError("Not a valid TCP port: %d" % port)
if protocol not in _tls_protocol_mapping:
raise ValueError("Unknown TLS protocol: %s" % protocol)
if ca_file is not None and not os.path.isfile(ca_file):
raise ValueError("Cannot find the specified CA file: %s" % ca_file)
if cert_file is not None and not os.path.isfile(cert_file):
raise ValueError("Cannot find the specified certificate file: %s" % cert_file)
if privkey_file is not None and not os.path.isfile(privkey_file):
raise ValueError("Cannot find the specified private key file: %s" % privkey_file)
if timeout < 0:
raise ValueError("Invalid TLS timeout value: %d" % timeout)
if self._pjsip_endpoint._tls_transport != NULL:
self._pjsip_endpoint._stop_tls_transport()
self._pjsip_endpoint._tls_protocol = protocol
self._pjsip_endpoint._tls_verify_server = int(bool(verify_server))
if ca_file is None:
self._pjsip_endpoint._tls_ca_file = None
else:
self._pjsip_endpoint._tls_ca_file = PJSTR(ca_file.encode(sys.getfilesystemencoding()))
if cert_file is None:
self._pjsip_endpoint._tls_cert_file = None
else:
self._pjsip_endpoint._tls_cert_file = PJSTR(cert_file.encode(sys.getfilesystemencoding()))
if privkey_file is None:
self._pjsip_endpoint._tls_privkey_file = None
else:
self._pjsip_endpoint._tls_privkey_file = PJSTR(privkey_file.encode(sys.getfilesystemencoding()))
self._pjsip_endpoint._tls_timeout = timeout
self._pjsip_endpoint._start_tls_transport(c_port)
def detect_nat_type(self, stun_server_address, stun_server_port=PJ_STUN_PORT, object user_data=None):
cdef pj_str_t stun_server_address_pj
cdef pj_sockaddr_in stun_server
cdef int status
self._check_self()
if not _is_valid_ip(pj_AF_INET(), stun_server_address):
raise ValueError("Not a valid IPv4 address: %s" % stun_server_address)
_str_to_pj_str(stun_server_address, &stun_server_address_pj)
status = pj_sockaddr_in_init(&stun_server, &stun_server_address_pj, stun_server_port)
if status != 0:
raise PJSIPError("Could not init STUN server address", status)
status = pj_stun_detect_nat_type(&stun_server, &self._stun_cfg, <void *> user_data, _cb_detect_nat_type)
if status != 0:
raise PJSIPError("Could not start NAT type detection", status)
Py_INCREF(user_data)
def parse_sip_uri(self, uri_string):
# no need for self._check_self(), _get_ua() is called in the function
return SIPURI.parse(uri_string)
def __dealloc__(self):
self.dealloc()
def dealloc(self):
global _ua, _dealloc_handler_queue, _event_queue_lock
if _ua == NULL:
return
self._check_thread()
pj_rwmutex_destroy(self.audio_change_rwlock)
pjmedia_del_audio_change_observer(&self._audio_change_observer)
_process_handler_queue(self, &_dealloc_handler_queue)
if _event_queue_lock != NULL:
pj_mutex_lock(_event_queue_lock)
pj_mutex_destroy(_event_queue_lock)
_event_queue_lock = NULL
self._pjsip_endpoint = None
self._pjmedia_endpoint = None
self._caching_pool = None
self._pjlib = None
_ua = NULL
self._poll_log()
cdef int _poll_log(self) except -1:
cdef object event_name
cdef dict event_params
cdef list events
events = _get_clear_event_queue()
for event_name, event_params in events:
self._event_handler(event_name, **event_params)
def poll(self):
global _post_poll_handler_queue
cdef int status
cdef object retval = None
cdef float max_timeout
cdef pj_time_val pj_max_timeout
cdef list timers
cdef int processed_timers
cdef Timer timer
self._check_self()
if self._timers:
max_timeout = min(max((<Timer>self._timers[0]).schedule_time - time.time(), 0.001), 0.100)
pj_max_timeout.sec = int(max_timeout)
pj_max_timeout.msec = int(max_timeout * 1000) % 1000
else:
pj_max_timeout.sec = 0
pj_max_timeout.msec = 100
with nogil:
status = pjsip_endpt_handle_events(self._pjsip_endpoint._obj, &pj_max_timeout)
IF UNAME_SYSNAME == "Darwin":
if status not in [0, PJ_ERRNO_START_SYS + EBADF]:
raise PJSIPError("Error while handling events", status)
ELSE:
if status != 0:
raise PJSIPError("Error while handling events", status)
_process_handler_queue(self, &_post_poll_handler_queue)
timers = list()
processed_timers = 0
while processed_timers < len(self._timers):
timer = <Timer> self._timers[processed_timers]
if timer.schedule_time - time.time() > 0.001:
break
timers.append(timer)
processed_timers += 1
if processed_timers > 0:
del self._timers[:processed_timers]
for timer in timers:
timer.call()
self._poll_log()
if self._fatal_error:
return True
else:
return False
cdef int _handle_exception(self, int is_fatal) except -1:
cdef object exc_type
cdef object exc_val
cdef object exc_tb
exc_type, exc_val, exc_tb = sys.exc_info()
if is_fatal:
self._fatal_error = is_fatal
_add_event("SIPEngineGotException",
dict(type=exc_type, value=exc_val,
traceback="".join(traceback.format_exception(exc_type, exc_val, exc_tb))))
return 0
cdef int _check_self(self) except -1:
global _ua
if _ua == NULL:
raise SIPCoreError("The PJSIPUA is no longer running")
self._check_thread()
cdef int _check_thread(self) except -1:
if not pj_thread_is_registered():
self._threads.append(PJSIPThread())
return 0
cdef int _add_timer(self, Timer timer) except -1:
cdef int low
cdef int mid
cdef int high
low = 0
high = len(self._timers)
while low < high:
mid = (low+high)/2
if timer < self._timers[mid]:
high = mid
else:
low = mid+1
self._timers.insert(low, timer)
return 0
cdef int _remove_timer(self, Timer timer) except -1:
self._timers.remove(timer)
return 0
cdef int _cb_rx_request(self, pjsip_rx_data *rdata) except 0:
global _event_hdr_name
cdef int status
cdef int bad_request
cdef pjsip_tx_data *tdata = NULL
cdef pjsip_hdr_ptr_const hdr_add
cdef IncomingRequest request
cdef Invitation inv
cdef IncomingSubscription sub
cdef list extra_headers
cdef dict event_dict
cdef dict message_params
cdef pj_str_t tsx_key
cdef pjsip_via_hdr *top_via, *via
cdef pjsip_transaction *tsx = NULL
cdef unsigned int options = PJSIP_INV_SUPPORT_100REL
cdef pjsip_event_hdr *event_hdr
cdef object method_name = _pj_str_to_str(rdata.msg_info.msg.line.req.method.name)
# Temporarily trick PJSIP into believing the last Via header is actually the first
if method_name != "ACK":
top_via = via = rdata.msg_info.via
while True:
rdata.msg_info.via = via
via = <pjsip_via_hdr *> pjsip_msg_find_hdr(rdata.msg_info.msg, PJSIP_H_VIA, (<pj_list *> via).next)
if via == NULL:
break
status = pjsip_tsx_create_key(rdata.tp_info.pool, &tsx_key,
PJSIP_ROLE_UAC, &rdata.msg_info.msg.line.req.method, rdata)
rdata.msg_info.via = top_via
if status != 0:
raise PJSIPError("Could not generate transaction key for incoming request", status)
tsx = pjsip_tsx_layer_find_tsx(&tsx_key, 0)
if tsx != NULL:
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 482, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
elif method_name in self._incoming_requests:
request = IncomingRequest()
request.init(self, rdata)
elif method_name == "OPTIONS":
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
for hdr_type in [PJSIP_H_ALLOW, PJSIP_H_ACCEPT, PJSIP_H_SUPPORTED]:
hdr_add = pjsip_endpt_get_capability(self._pjsip_endpoint._obj, hdr_type, NULL)
if hdr_add != NULL:
pjsip_msg_add_hdr(tdata.msg, <pjsip_hdr *> pjsip_hdr_clone(tdata.pool, hdr_add))
elif method_name == "INVITE":
if self._ignore_missing_ack:
options |= PJSIP_INV_IGNORE_MISSING_ACK
status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, self._pjsip_endpoint._obj, &tdata)
if status == 0:
inv = Invitation()
inv.init_incoming(self, rdata, options)
elif method_name == "SUBSCRIBE":
event_hdr = <pjsip_event_hdr *> pjsip_msg_find_hdr_by_name(rdata.msg_info.msg, &_event_hdr_name.pj_str, NULL)
if event_hdr == NULL or _pj_str_to_str(event_hdr.event_type) not in self._incoming_events:
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 489, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
else:
sub = IncomingSubscription()
sub.init(self, rdata, _pj_str_to_str(event_hdr.event_type))
elif method_name == "MESSAGE":
bad_request = 0
extra_headers = list()
message_params = dict()
event_dict = dict()
_pjsip_msg_to_dict(rdata.msg_info.msg, event_dict)
message_params["request_uri"] = event_dict["request_uri"]
message_params["from_header"] = event_dict["headers"].get("From", None)
message_params["to_header"] = event_dict["headers"].get("To", None)
message_params["headers"] = event_dict["headers"]
message_params["body"] = event_dict["body"]
content_type, params = message_params["headers"].get("Content-Type", (None, None))
if content_type is not None:
message_params["content_type"] = ContentType(content_type)
if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None:
bad_request = 1
extra_headers.append(WarningHeader(399, "local", "Missing body"))
else:
message_params["content_type"] = None
if message_params["headers"].get("Content-Length", 0) > 0 and message_params["body"] is None:
bad_request = 1
extra_headers.append(WarningHeader(399, "local", "Missing Content-Type header"))
if bad_request:
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 400, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
_add_headers_to_tdata(tdata, extra_headers)
else:
_add_event("SIPEngineGotMessage", message_params)
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 200, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
elif method_name != "ACK":
status = pjsip_endpt_create_response(self._pjsip_endpoint._obj, rdata, 405, NULL, &tdata)
if status != 0:
raise PJSIPError("Could not create response", status)
if tdata != NULL:
status = pjsip_endpt_send_response2(self._pjsip_endpoint._obj, rdata, tdata, NULL, NULL)
if status != 0:
pjsip_tx_data_dec_ref(tdata)
raise PJSIPError("Could not send response", status)
return 1
cdef class PJSIPThread:
def __cinit__(self):
cdef object thread_name = "python_%d" % id(self)
cdef int status
status = pj_thread_register(thread_name, self._thread_desc, &self._obj)
if status != 0:
raise PJSIPError("Error while registering thread", status)
# callback functions
cdef void _cb_default_audio_change(void *user_data) with gil:
cdef PJSIPUA ua
cdef audio_change_type *type = <audio_change_type *>user_data
try:
ua = _get_ua()
except:
return
try:
event_dict = dict()
event_dict["changed_input"] = (type[0] == AUDIO_CHANGE_INPUT)
event_dict["changed_output"] = (type[0] == AUDIO_CHANGE_OUTPUT)
_add_event("DefaultAudioDeviceDidChange", event_dict)
except:
ua._handle_exception(1)
cdef void _cb_audio_devices_will_change(void *user_data) with gil:
cdef PJSIPUA ua
cdef pjmedia_audio_change_observer *observer = <pjmedia_audio_change_observer*> user_data
cdef int status
try:
ua = _get_ua()
except:
return
try:
ua.old_devices = ua.sound_devices
with nogil:
status = pj_rwmutex_lock_write(ua.audio_change_rwlock)
if status != 0:
raise SIPCoreError('Could not acquire audio_change_rwlock for writing', status)
except:
ua._handle_exception(1)
cdef void _cb_audio_devices_did_change(void *user_data) with gil:
cdef PJSIPUA ua
cdef pjmedia_audio_change_observer *observer = <pjmedia_audio_change_observer*> user_data
cdef int status
try:
ua = _get_ua()
except:
return
try:
with nogil:
status = pj_rwmutex_unlock_write(ua.audio_change_rwlock)
if status != 0:
raise SIPCoreError('Could not release the audio_change_rwlock', status)
event_dict = dict()
event_dict["old_devices"] = ua.old_devices
event_dict["new_devices"] = ua.sound_devices
_add_event("AudioDevicesDidChange", event_dict)
except:
ua._handle_exception(1)
cdef void _cb_detect_nat_type(void *user_data, pj_stun_nat_detect_result_ptr_const res) with gil:
cdef PJSIPUA ua
cdef dict event_dict
cdef object user_data_obj = <object> user_data
Py_DECREF(user_data_obj)
try:
ua = _get_ua()
except:
return
try:
event_dict = dict()
event_dict["succeeded"] = res.status == 0
event_dict["user_data"] = user_data_obj
if res.status == 0:
event_dict["nat_type"] = res.nat_type_name
else:
event_dict["error"] = res.status_text
_add_event("SIPEngineDetectedNATType", event_dict)
except:
ua._handle_exception(1)
cdef int _PJSIPUA_cb_rx_request(pjsip_rx_data *rdata) with gil:
cdef PJSIPUA ua
try:
ua = _get_ua()
except:
return 0
try:
return ua._cb_rx_request(rdata)
except:
ua._handle_exception(1)
cdef int _cb_trace_rx(pjsip_rx_data *rdata) with gil:
cdef PJSIPUA ua
try:
ua = _get_ua()
except:
return 0
try:
if ua._trace_sip:
_add_event("SIPEngineSIPTrace",
dict(received=True, source_ip=rdata.pkt_info.src_name, source_port=rdata.pkt_info.src_port,
destination_ip=_pj_str_to_str(rdata.tp_info.transport.local_name.host),
destination_port=rdata.tp_info.transport.local_name.port,
data=PyString_FromStringAndSize(rdata.pkt_info.packet, rdata.pkt_info.len),
transport=rdata.tp_info.transport.type_name))
except:
ua._handle_exception(1)
return 0
cdef int _cb_trace_tx(pjsip_tx_data *tdata) with gil:
cdef PJSIPUA ua
try:
ua = _get_ua()
except:
return 0
try:
if ua._trace_sip:
_add_event("SIPEngineSIPTrace",
dict(received=False,
source_ip=_pj_str_to_str(tdata.tp_info.transport.local_name.host),
source_port=tdata.tp_info.transport.local_name.port, destination_ip=tdata.tp_info.dst_name,
destination_port=tdata.tp_info.dst_port,
data=PyString_FromStringAndSize(tdata.buf.start, tdata.buf.cur - tdata.buf.start),
transport=tdata.tp_info.transport.type_name))
except:
ua._handle_exception(1)
return 0
cdef int _cb_add_user_agent_hdr(pjsip_tx_data *tdata) with gil:
cdef PJSIPUA ua
cdef pjsip_hdr *hdr
cdef void *found_hdr
try:
ua = _get_ua()
except:
return 0
try:
found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_user_agent_hdr_name.pj_str, NULL)
if found_hdr == NULL:
hdr = <pjsip_hdr *> pjsip_generic_string_hdr_create(tdata.pool, &_user_agent_hdr_name.pj_str,
&ua._user_agent.pj_str)
if hdr == NULL:
raise SIPCoreError('Could not add "User-Agent" header to outgoing request')
pjsip_msg_add_hdr(tdata.msg, hdr)
except:
ua._handle_exception(1)
return 0
cdef int _cb_add_server_hdr(pjsip_tx_data *tdata) with gil:
cdef PJSIPUA ua
cdef pjsip_hdr *hdr
cdef void *found_hdr
try:
ua = _get_ua()
except:
return 0
try:
found_hdr = pjsip_msg_find_hdr_by_name(tdata.msg, &_server_hdr_name.pj_str, NULL)
if found_hdr == NULL:
hdr = <pjsip_hdr *> pjsip_generic_string_hdr_create(tdata.pool, &_server_hdr_name.pj_str,
&ua._user_agent.pj_str)
if hdr == NULL:
raise SIPCoreError('Could not add "Server" header to outgoing response')
pjsip_msg_add_hdr(tdata.msg, hdr)
except:
ua._handle_exception(1)
return 0
# functions
cdef PJSIPUA _get_ua():
global _ua
cdef PJSIPUA ua
if _ua == NULL:
raise SIPCoreError("PJSIPUA is not instantiated")
ua = <object> _ua
ua._check_thread()
return ua
cdef int deallocate_weakref(object weak_ref, object timer) except -1 with gil:
Py_DECREF(weak_ref)
# globals
cdef void *_ua = NULL
cdef PJSTR _user_agent_hdr_name = PJSTR("User-Agent")
cdef PJSTR _server_hdr_name = PJSTR("Server")
cdef PJSTR _event_hdr_name = PJSTR("Event")
diff --git a/sipsimple/core/_core.util.pxi b/sipsimple/core/_core.util.pxi
index 95fd9833..5b7e9c3e 100644
--- a/sipsimple/core/_core.util.pxi
+++ b/sipsimple/core/_core.util.pxi
@@ -1,371 +1,386 @@
# Copyright (C) 2008-2009 AG Projects. See LICENSE for details.
#
# python imports
+import platform
import re
+import sys
+
+from application.version import Version
# classes
cdef class PJSTR:
def __cinit__(self, str):
self.str = str
_str_to_pj_str(str, &self.pj_str)
def __str__(self):
return self.str
cdef class SIPStatusMessages:
cdef object _default_status
def __cinit__(self, *args, **kwargs):
self._default_status = _pj_str_to_str(pjsip_get_status_text(0)[0])
def __getitem__(self, int val):
cdef object _status
_status = _pj_str_to_str(pjsip_get_status_text(val)[0])
if _status == self._default_status:
raise IndexError("Unknown SIP response code: %d" % val)
return _status
cdef class frozenlist:
def __cinit__(self, *args, **kw):
self.list = list()
self.initialized = 0
self.hash = 0
def __init__(self, *args, **kw):
if not self.initialized:
self.list = list(*args, **kw)
self.initialized = 1
self.hash = hash(tuple(self.list))
def __reduce__(self):
return (self.__class__.__name__, (self.list,), None)
def __repr__(self):
return "frozenlist(%r)" % self.list
def __len__(self):
return self.list.__len__()
def __hash__(self):
return self.hash
def __iter__(self):
return self.list.__iter__()
def __cmp__(self, frozenlist other):
return self.list.__cmp__(other.list)
def __richcmp__(frozenlist self, other, op):
if isinstance(other, frozenlist):
other = (<frozenlist>other).list
if op == 0:
return self.list.__cmp__(other) < 0
elif op == 1:
return self.list.__cmp__(other) <= 0
elif op == 2:
return self.list.__eq__(other)
elif op == 3:
return self.list.__ne__(other)
elif op == 4:
return self.list.__cmp__(other) > 0
elif op == 5:
return self.list.__cmp__(other) >= 0
else:
return NotImplemented
def __contains__(self, item):
return self.list.__contains__(item)
def __getitem__(self, key):
return self.list.__getitem__(key)
def __getslice__(self, i, j):
return self.list.__getslice__(i, j)
def __add__(first, second):
if isinstance(first, frozenlist):
first = (<frozenlist>first).list
if isinstance(second, frozenlist):
second = (<frozenlist>second).list
return frozenlist(first+second)
def __mul__(first, second):
if isinstance(first, frozenlist):
first = (<frozenlist>first).list
if isinstance(second, frozenlist):
second = (<frozenlist>second).list
return frozenlist(first*second)
def __reversed__(self):
return self.list.__reversed__()
def count(self, elem):
return self.list.count(elem)
def index(self, elem):
return self.list.index(elem)
cdef class frozendict:
def __cinit__(self, *args, **kw):
self.dict = dict()
self.initialized = 0
def __init__(self, *args, **kw):
if not self.initialized:
self.dict = dict(*args, **kw)
self.initialized = 1
self.hash = hash(tuple(self.dict.iteritems()))
def __reduce__(self):
return (self.__class__.__name__, (self.dict,), None)
def __repr__(self):
return "frozendict(%r)" % self.dict
def __len__(self):
return self.dict.__len__()
def __hash__(self):
return self.hash
def __iter__(self):
return self.dict.__iter__()
def __cmp__(self, frozendict other):
return self.dict.__cmp__(other.dict)
def __richcmp__(frozendict self, other, op):
if isinstance(other, frozendict):
other = (<frozendict>other).dict
if op == 0:
return self.dict.__cmp__(other) < 0
elif op == 1:
return self.dict.__cmp__(other) <= 0
elif op == 2:
return self.dict.__eq__(other)
elif op == 3:
return self.dict.__ne__(other)
elif op == 4:
return self.dict.__cmp__(other) > 0
elif op == 5:
return self.dict.__cmp__(other) >= 0
else:
return NotImplemented
def __contains__(self, item):
return self.dict.__contains__(item)
def __getitem__(self, key):
return self.dict.__getitem__(key)
def copy(self):
return self
def get(self, *args):
return self.dict.get(*args)
def has_key(self, key):
return self.dict.has_key(key)
def items(self):
return self.dict.items()
def iteritems(self):
return self.dict.iteritems()
def iterkeys(self):
return self.dict.iterkeys()
def itervalues(self):
return self.dict.itervalues()
def keys(self):
return self.dict.keys()
def values(self):
return self.dict.values()
# functions
cdef int _str_to_pj_str(object string, pj_str_t *pj_str) except -1:
pj_str.ptr = PyString_AsString(string)
pj_str.slen = len(string)
cdef object _pj_str_to_str(pj_str_t pj_str):
return PyString_FromStringAndSize(pj_str.ptr, pj_str.slen)
cdef object _pj_status_to_str(int status):
cdef char buf[PJ_ERR_MSG_SIZE]
return _pj_str_to_str(pj_strerror(status, buf, PJ_ERR_MSG_SIZE))
cdef object _pj_status_to_def(int status):
return _re_pj_status_str_def.match(_pj_status_to_str(status)).group(1)
cdef dict _pjsip_param_to_dict(pjsip_param *param_list):
cdef pjsip_param *param
cdef dict retval = dict()
param = <pjsip_param *> (<pj_list *> param_list).next
while param != param_list:
if param.value.slen == 0:
retval[_pj_str_to_str(param.name)] = None
else:
retval[_pj_str_to_str(param.name)] = _pj_str_to_str(param.value)
param = <pjsip_param *> (<pj_list *> param).next
return retval
cdef int _dict_to_pjsip_param(object params, pjsip_param *param_list, pj_pool_t *pool):
cdef pjsip_param *param = NULL
for name, value in params.iteritems():
param = <pjsip_param *> pj_pool_alloc(pool, sizeof(pjsip_param))
if param == NULL:
return -1
_str_to_pj_str(name, &param.name)
if value is None:
param.value.slen = 0
else:
_str_to_pj_str(value, &param.value)
pj_list_insert_after(<pj_list *> param_list, <pj_list *> param)
return 0
cdef int _pjsip_msg_to_dict(pjsip_msg *msg, dict info_dict) except -1:
cdef pjsip_msg_body *body
cdef pjsip_hdr *header
cdef pjsip_generic_array_hdr *array_header
cdef pjsip_ctype_hdr *ctype_header
cdef pjsip_cseq_hdr *cseq_header
cdef int i
headers = {}
header = <pjsip_hdr *> (<pj_list *> &msg.hdr).next
while header != &msg.hdr:
header_name = _pj_str_to_str(header.name)
header_data = None
multi_header = False
if header_name in ("Accept", "Allow", "Require", "Supported", "Unsupported", "Allow-Events"):
array_header = <pjsip_generic_array_hdr *> header
header_data = []
for i from 0 <= i < array_header.count:
header_data.append(_pj_str_to_str(array_header.values[i]))
elif header_name == "Contact":
multi_header = True
header_data = FrozenContactHeader_create(<pjsip_contact_hdr *> header)
elif header_name == "Content-Length":
header_data = (<pjsip_clen_hdr *> header).len
elif header_name == "Content-Type":
ctype_header = <pjsip_ctype_hdr *> header
header_data = ("%s/%s" % (_pj_str_to_str(ctype_header.media.type),
_pj_str_to_str(ctype_header.media.subtype)),
_pj_str_to_str(ctype_header.media.param))
elif header_name == "CSeq":
cseq_header = <pjsip_cseq_hdr *> header
header_data = (cseq_header.cseq, _pj_str_to_str(cseq_header.method.name))
elif header_name in ("Expires", "Max-Forwards", "Min-Expires"):
header_data = (<pjsip_generic_int_hdr *> header).ivalue
elif header_name == "From":
header_data = FrozenFromHeader_create(<pjsip_fromto_hdr *> header)
elif header_name == "To":
header_data = FrozenToHeader_create(<pjsip_fromto_hdr *> header)
elif header_name == "Route":
multi_header = True
header_data = FrozenRouteHeader_create(<pjsip_routing_hdr *> header)
elif header_name == "Reason":
value = _pj_str_to_str((<pjsip_generic_string_hdr *>header).hvalue)
protocol, sep, params_str = value.partition(';')
params = frozendict([(name, value or None) for name, sep, value in [param.partition('=') for param in params_str.split(';')]])
header_data = FrozenReasonHeader(protocol, params)
elif header_name == "Record-Route":
multi_header = True
header_data = FrozenRecordRouteHeader_create(<pjsip_routing_hdr *> header)
elif header_name == "Retry-After":
header_data = FrozenRetryAfterHeader_create(<pjsip_retry_after_hdr *> header)
elif header_name == "Via":
multi_header = True
header_data = FrozenViaHeader_create(<pjsip_via_hdr *> header)
elif header_name == "Warning":
match = _re_warning_hdr.match(_pj_str_to_str((<pjsip_generic_string_hdr *>header).hvalue))
if match is not None:
warning_params = match.groupdict()
warning_params['code'] = int(warning_params['code'])
header_data = FrozenWarningHeader(**warning_params)
elif header_name == "Event":
header_data = FrozenEventHeader_create(<pjsip_event_hdr *> header)
elif header_name == "Subscription-State":
header_data = FrozenSubscriptionStateHeader_create(<pjsip_sub_state_hdr *> header)
# skip the following headers:
elif header_name not in ("Authorization", "Proxy-Authenticate", "Proxy-Authorization", "WWW-Authenticate"):
header_data = FrozenHeader(header_name, _pj_str_to_str((<pjsip_generic_string_hdr *> header).hvalue))
if header_data is not None:
if multi_header:
headers.setdefault(header_name, []).append(header_data)
else:
if header_name not in headers:
headers[header_name] = header_data
header = <pjsip_hdr *> (<pj_list *> header).next
info_dict["headers"] = headers
body = msg.body
if body == NULL:
info_dict["body"] = None
else:
info_dict["body"] = PyString_FromStringAndSize(<char *> body.data, body.len)
if msg.type == PJSIP_REQUEST_MSG:
info_dict["method"] = _pj_str_to_str(msg.line.req.method.name)
# You need to call pjsip_uri_get_uri on the request URI if the message is for transmitting,
# but it isn't required if message is one received. Otherwise, a seg fault occurs. Don't ask.
info_dict["request_uri"] = FrozenSIPURI_create(<pjsip_sip_uri*>pjsip_uri_get_uri(msg.line.req.uri))
else:
info_dict["code"] = msg.line.status.code
info_dict["reason"] = _pj_str_to_str(msg.line.status.reason)
return 0
cdef int _is_valid_ip(int af, object ip) except -1:
cdef char buf[16]
cdef pj_str_t src
cdef int status
_str_to_pj_str(ip, &src)
status = pj_inet_pton(af, &src, buf)
if status == 0:
return 1
else:
return 0
cdef int _get_ip_version(object ip) except -1:
if _is_valid_ip(pj_AF_INET(), ip):
return pj_AF_INET()
elif _is_valid_ip(pj_AF_INET6(), ip):
return pj_AF_INET()
else:
return 0
cdef int _add_headers_to_tdata(pjsip_tx_data *tdata, object headers) except -1:
cdef object name, value
cdef pj_str_t name_pj, value_pj
cdef pjsip_hdr *hdr
for header in headers:
_str_to_pj_str(header.name, &name_pj)
_str_to_pj_str(header.body, &value_pj)
hdr = <pjsip_hdr *> pjsip_generic_string_hdr_create(tdata.pool, &name_pj, &value_pj)
pjsip_msg_add_hdr(tdata.msg, hdr)
cdef int _BaseSIPURI_to_pjsip_sip_uri(BaseSIPURI uri, pjsip_sip_uri *pj_uri, pj_pool_t *pool) except -1:
cdef pjsip_param *param
pjsip_sip_uri_init(pj_uri, uri.secure)
if uri.user:
_str_to_pj_str(uri.user, &pj_uri.user)
if uri.password:
_str_to_pj_str(uri.password, &pj_uri.passwd)
if uri.host:
_str_to_pj_str(uri.host, &pj_uri.host)
if uri.port:
pj_uri.port = uri.port
for name, value in uri.parameters.iteritems():
if name == "lr":
pj_uri.lr_param = 1
elif name == "maddr":
_str_to_pj_str(value, &pj_uri.maddr_param)
elif name == "method":
_str_to_pj_str(value, &pj_uri.method_param)
elif name == "transport":
_str_to_pj_str(value, &pj_uri.transport_param)
elif name == "ttl":
pj_uri.ttl_param = int(value)
elif name == "user":
_str_to_pj_str(value, &pj_uri.user_param)
else:
param = <pjsip_param *> pj_pool_alloc(pool, sizeof(pjsip_param))
_str_to_pj_str(name, &param.name)
if value is None:
param.value.slen = 0
else:
_str_to_pj_str(value, &param.value)
pj_list_insert_after(<pj_list *> &pj_uri.other_param, <pj_list *> param)
_dict_to_pjsip_param(uri.headers, &pj_uri.header_param, pool)
return 0
cdef int _BaseRouteHeader_to_pjsip_route_hdr(BaseIdentityHeader header, pjsip_route_hdr *pj_header, pj_pool_t *pool) except -1:
cdef pjsip_param *param
cdef pjsip_sip_uri *sip_uri
pjsip_route_hdr_init(NULL, <void *> pj_header)
sip_uri = <pjsip_sip_uri *> pj_pool_alloc(pool, sizeof(pjsip_sip_uri))
_BaseSIPURI_to_pjsip_sip_uri(header.uri, sip_uri, pool)
pj_header.name_addr.uri = <pjsip_uri *> sip_uri
if header.display_name:
_str_to_pj_str(header.display_name.encode('utf-8'), &pj_header.name_addr.display)
_dict_to_pjsip_param(header.parameters, &pj_header.other_param, pool)
return 0
+def _get_device_name_encoding():
+ if sys.platform == 'win32':
+ encoding = 'mbcs'
+ elif sys.platform.startswith('linux2') and Version.parse(platform.release()) < Version(2,6,31):
+ encoding = 'latin1'
+ else:
+ encoding = 'utf-8'
+ return encoding
+
# globals
cdef object _re_pj_status_str_def = re.compile("^.*\((.*)\)$")
cdef object _re_warning_hdr = re.compile('(?P<code>[0-9]{3}) (?P<agent>.*?) "(?P<text>.*?)"')
+device_name_encoding = _get_device_name_encoding()
sip_status_messages = SIPStatusMessages()
+

File Metadata

Mime Type
text/x-diff
Expires
Sat, Feb 1, 10:27 AM (20 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3487663
Default Alt Text
(212 KB)

Event Timeline