Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F7232107
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
38 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/setup_pjsip.py b/setup_pjsip.py
index c463fe35..cd247365 100644
--- a/setup_pjsip.py
+++ b/setup_pjsip.py
@@ -1,256 +1,261 @@
import errno
import itertools
import os
import platform
import re
import shutil
import subprocess
import sys
if sys.platform.startswith('linux'):
sys_platform = 'linux'
elif sys.platform.startswith('freebsd'):
sys_platform = 'freebsd'
else:
sys_platform = sys.platform
# Hack to set environment variables before importing distutils
# modules that will fetch them and set the compiler and linker
# to be used. -Saul
if sys_platform == "darwin":
min_osx_version = "10.11"
try:
osx_sdk_path = subprocess.check_output(["xcodebuild", "-version", "-sdk", "macosx", "Path"]).decode().strip()
except subprocess.CalledProcessError as e:
raise RuntimeError("Could not locate SDK path: %s" % str(e))
# OpenSSL (installed with Homebrew)
ossl_cflags = "-I/usr/local/opt/openssl/include"
ossl_ldflags = "-L/usr/local/opt/openssl/lib"
# SQLite (installed with Homebrew)
sqlite_cflags = "-I/usr/local/opt/sqlite/include"
sqlite_ldflags = "-L/usr/local/opt/sqlite/lib"
# Opus flags (installed with Homebrew)
opus_cflags = "-I/usr/local/opt/opus/include"
opus_ldflags = "-L/usr/local/opt/opus/lib"
# VPX (installed with Homebrew)
vpx_cflags = "-I/usr/local/opt/libvpx/include"
vpx_ldflags = "-L/usr/local/opt/libvpx/lib"
# Prepare final flags
arch_flags = "-arch x86_64 -mmacosx-version-min=%s" % min_osx_version
local_cflags = " %s %s %s %s %s -mmacosx-version-min=%s -isysroot %s" % (arch_flags, ossl_cflags, sqlite_cflags, opus_cflags, vpx_cflags, min_osx_version, osx_sdk_path)
local_ldflags = " %s %s %s %s %s -isysroot %s" % (arch_flags, ossl_ldflags, sqlite_ldflags, opus_ldflags, vpx_ldflags, osx_sdk_path)
os.environ['CFLAGS'] = os.environ.get('CFLAGS', '') + local_cflags
os.environ['LDFLAGS'] = os.environ.get('LDFLAGS', '') + local_ldflags
os.environ['ARCHFLAGS'] = arch_flags
os.environ['MACOSX_DEPLOYMENT_TARGET'] = min_osx_version
from distutils import log
from distutils.dir_util import copy_tree
from distutils.errors import DistutilsError
from Cython.Distutils import build_ext
class PJSIP_build_ext(build_ext):
config_site = ["#define PJ_SCANNER_USE_BITWISE 0",
"#define PJSIP_SAFE_MODULE 0",
"#define PJSIP_MAX_PKT_LEN 262144",
"#define PJSIP_UNESCAPE_IN_PLACE 1",
"#define PJMEDIA_AUDIO_DEV_HAS_COREAUDIO %d" % (1 if sys_platform=="darwin" else 0),
"#define PJMEDIA_AUDIO_DEV_HAS_ALSA %d" % (1 if sys_platform=="linux" else 0),
"#define PJMEDIA_AUDIO_DEV_HAS_WMME %d" % (1 if sys_platform=="win32" else 0),
"#define PJMEDIA_HAS_SPEEX_AEC 0",
- "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 1",
+ "#define PJMEDIA_HAS_SPEEX_CODEC 1",
+ "#define PJMEDIA_HAS_GSM_CODEC 1",
+ "#define PJMEDIA_HAS_ILBC_CODEC 1",
"#define PJMEDIA_HAS_OPENCORE_AMRNB_CODEC 1",
+ "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 1",
"#define PJMEDIA_HAS_WEBRTC_AEC %d" % (1 if re.match('i\d86|x86|x86_64', platform.machine()) else 0),
"#define PJMEDIA_RTP_PT_TELEPHONE_EVENTS 101",
"#define PJMEDIA_RTP_PT_TELEPHONE_EVENTS_STR \"101\"",
"#define PJMEDIA_STREAM_ENABLE_KA PJMEDIA_STREAM_KA_EMPTY_RTP",
"#define PJMEDIA_STREAM_VAD_SUSPEND_MSEC 0",
"#define PJMEDIA_CODEC_MAX_SILENCE_PERIOD -1",
"#define PJ_ICE_MAX_CHECKS 256",
"#define PJ_LOG_MAX_LEVEL 6",
"#define PJ_IOQUEUE_MAX_HANDLES 1024",
"#define PJ_DNS_RESOLVER_MAX_TTL 0",
"#define PJ_DNS_RESOLVER_INVALID_TTL 0",
"#define PJSIP_TRANSPORT_IDLE_TIME 7200",
"#define PJ_ENABLE_EXTRA_CHECK 1",
"#define PJSIP_DONT_SWITCH_TO_TCP 1",
"#define PJMEDIA_VIDEO_DEV_HAS_SDL 0",
"#define PJMEDIA_VIDEO_DEV_HAS_AVI 0",
"#define PJMEDIA_VIDEO_DEV_HAS_FB 1",
"#define PJMEDIA_VIDEO_DEV_HAS_V4L2 %d" % (1 if sys_platform=="linux" else 0),
"#define PJMEDIA_VIDEO_DEV_HAS_AVF %d" % (1 if sys_platform=="darwin" else 0),
"#define PJMEDIA_VIDEO_DEV_HAS_DSHOW %d" % (1 if sys_platform=="win32" else 0),
"#define PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC 1",
"#define PJMEDIA_VIDEO_DEV_HAS_NULL 1"]
user_options = build_ext.user_options
user_options.extend([
("clean", None, "Clean PJSIP tree before compilation"),
("verbose", None, "Print output of PJSIP compilation process")
])
boolean_options = build_ext.boolean_options
boolean_options.extend(["clean", "verbose"])
@staticmethod
def distutils_exec_process(cmdline, silent=True, input=None, **kwargs):
"""Execute a subprocess and returns the returncode, stdout buffer and stderr buffer.
Optionally prints stdout and stderr while running."""
try:
sub = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)
stdout, stderr = sub.communicate(input=input)
returncode = sub.returncode
if not silent:
sys.stdout.write(stdout.decode())
sys.stderr.write(stderr.decode())
except OSError as e:
if e.errno == errno.ENOENT:
raise RuntimeError('"%s" is not present on this system' % cmdline[0])
else:
raise
if returncode != 0:
raise RuntimeError('Got return value %d while executing "%s", stderr output was:\n%s' % (returncode, " ".join(cmdline), stderr.decode()))
return stdout.decode()
@staticmethod
def get_make_cmd():
if sys_platform == "freebsd":
return "gmake"
else:
return "make"
@staticmethod
def get_opts_from_string(line, prefix):
"""Returns all options that have a particular prefix on a commandline"""
chunks = [chunk.strip() for chunk in line.split()]
return [chunk[len(prefix):] for chunk in chunks if chunk.startswith(prefix)]
@classmethod
def get_makefile_variables(cls, makefile, silent=True):
"""Returns all variables in a makefile as a dict"""
stdout = cls.distutils_exec_process([cls.get_make_cmd(), "-f", makefile, "-pR", makefile], silent=silent)
return dict(tup for tup in re.findall("(^[a-zA-Z]\w+)\s*:?=\s*(.*)$", stdout, re.MULTILINE))
@classmethod
def makedirs(cls, path):
try:
os.makedirs(path)
except OSError as e:
if e.errno==errno.EEXIST and os.path.isdir(path) and os.access(path, os.R_OK | os.W_OK | os.X_OK):
return
raise
def initialize_options(self):
build_ext.initialize_options(self)
self.clean = 0
self.verbose = 0
self.pjsip_dir = os.path.join(os.path.dirname(__file__), "deps", "pjsip")
def configure_pjsip(self, silent=True):
path = os.path.join(self.build_dir, "pjlib", "include", "pj", "config_site.h")
log.info("Configuring PJSIP in %s" % path)
with open(path, "w") as f:
s = "\n".join(self.config_site+[""])
f.write(s)
cflags = "-DNDEBUG -g -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -Wno-unused-label"
if self.debug or hasattr(sys, 'gettotalrefcount'):
log.info("PJSIP will be built without optimizations")
cflags += " -O0"
else:
cflags += " -O2"
env = os.environ.copy()
env['CFLAGS'] = ' '.join(x for x in (cflags, env.get('CFLAGS', None)) if x)
if sys_platform == "win32":
cmd = ["bash", "configure"]
else:
cmd = ["./configure"]
+
cmd.extend(["--disable-openh264", "--disable-l16-codec", "--disable-g7221-codec", "--disable-sdl"])
+ #cmd.extend(["--disable-ilbc-codec", "--disable-speex-codec", "--disable-gsm-codec"])
ffmpeg_path = env.get("SIPSIMPLE_FFMPEG_PATH", None)
if ffmpeg_path is not None:
cmd.append("--with-ffmpeg=%s" % os.path.abspath(os.path.expanduser(ffmpeg_path)))
libvpx_path = env.get("SIPSIMPLE_LIBVPX_PATH", None)
if libvpx_path is not None:
cmd.append("--with-vpx=%s" % os.path.abspath(os.path.expanduser(libvpx_path)))
amr_nb_path = env.get("SIPSIMPLE_AMR_NB_PATH", None)
if amr_nb_path is not None:
cmd.append("--with-opencore-amr=%s" % os.path.abspath(os.path.expanduser(amr_nb_path)))
amr_wb_path = env.get("SIPSIMPLE_AMR_WB_PATH", None)
if amr_wb_path is not None:
cmd.append("--with-opencore-amrwbenc=%s" % os.path.abspath(os.path.expanduser(amr_wb_path)))
if self.verbose:
log.info(" ".join(cmd))
self.distutils_exec_process(cmd, silent=not self.verbose, cwd=self.build_dir, env=env)
if "#define PJ_HAS_SSL_SOCK 1\n" not in open(os.path.join(self.build_dir, "pjlib", "include", "pj", "compat", "os_auto.h")).readlines():
os.remove(os.path.join(self.build_dir, "build.mak"))
raise DistutilsError("PJSIP TLS support was disabled, OpenSSL development files probably not present on this system")
def compile_pjsip(self):
log.info("Compiling PJSIP")
if self.verbose and sys_platform == "darwin":
log.info(os.environ['CFLAGS'])
log.info(os.environ['LDFLAGS'])
self.distutils_exec_process([self.get_make_cmd()], silent=not self.verbose, cwd=self.build_dir)
def clean_pjsip(self):
log.info("Cleaning PJSIP")
try:
shutil.rmtree(self.build_dir)
except OSError as e:
if e.errno == errno.ENOENT:
return
raise
def update_extension(self, extension, silent=True):
build_mak_vars = self.get_makefile_variables(os.path.join(self.build_dir, "build.mak"))
extension.include_dirs = self.get_opts_from_string(build_mak_vars["PJ_CFLAGS"], "-I")
extension.library_dirs = self.get_opts_from_string(build_mak_vars["PJ_LDFLAGS"], "-L")
extension.libraries = self.get_opts_from_string(build_mak_vars["PJ_LDLIBS"], "-l")
extension.define_macros = [tuple(define.split("=", 1)) for define in self.get_opts_from_string(build_mak_vars["PJ_CFLAGS"], "-D")]
extension.define_macros.append(("PJ_SVN_REVISION", open(os.path.join(self.build_dir, "base_rev"), "r").read().strip()))
#extension.define_macros.append(("__PYX_FORCE_INIT_THREADS", 1))
extension.extra_compile_args.append("-Wno-unused-function") # silence warning
if sys_platform == "darwin":
extension.define_macros.append(("MACOSX_DEPLOYMENT_TARGET", min_osx_version))
frameworks = re.findall("-framework (\S+)(?:\s|$)", build_mak_vars["PJ_LDLIBS"])
extension.extra_link_args = list(itertools.chain(*(("-framework", val) for val in frameworks)))
extension.extra_link_args.append("-mmacosx-version-min=%s" % min_osx_version)
extension.extra_compile_args.append("-mmacosx-version-min=%s" % min_osx_version)
extension.library_dirs.append("%s/usr/lib" % osx_sdk_path)
extension.include_dirs.append("%s/usr/include" % osx_sdk_path)
extension.depends = build_mak_vars["PJ_LIB_FILES"].split()
self.libraries = extension.depends[:]
def cython_sources(self, sources, extension, silent=True):
log.info("Compiling Cython extension %s" % extension.name)
if extension.name == "sipsimple.core._core":
self.build_dir = os.path.join(self.build_temp, "pjsip")
if self.clean:
self.clean_pjsip()
copy_tree(self.pjsip_dir, self.build_dir, verbose=0)
try:
if not os.path.exists(os.path.join(self.build_dir, "build.mak")):
self.configure_pjsip(silent=silent)
self.update_extension(extension, silent=silent)
self.compile_pjsip()
except RuntimeError as e:
log.info("Error building %s: %s" % (extension.name, str(e)))
return None
return build_ext.cython_sources(self, sources, extension)
diff --git a/sipsimple/configuration/datatypes.py b/sipsimple/configuration/datatypes.py
index 32b42259..1bbff280 100644
--- a/sipsimple/configuration/datatypes.py
+++ b/sipsimple/configuration/datatypes.py
@@ -1,664 +1,664 @@
"""Definitions of datatypes for use in configuration settings"""
__all__ = [
# Base datatypes
'List',
# Generic datatypes
'ContentType', 'ContentTypeList', 'CountryCode', 'NonNegativeInteger', 'PositiveInteger', 'SIPAddress',
# Custom datatypes
'PJSIPLogLevel',
# Audio datatypes
'AudioCodecList', 'SampleRate',
# Video datatypes
'H264Profile', 'VideoResolution', 'VideoCodecList',
# Address and transport datatypes
'Port', 'PortRange', 'Hostname', 'DomainList', 'EndpointAddress', 'EndpointIPAddress', 'MSRPRelayAddress',
'SIPProxyAddress', 'STUNServerAddress', 'STUNServerAddressList', 'XCAPRoot',
'MSRPConnectionModel', 'MSRPTransport', 'SIPTransport', 'SIPTransportList',
# SRTP encryption
'SRTPKeyNegotiation',
# Path datatypes
'Path']
import locale
import os
import re
import urllib.parse
from operator import itemgetter
# Base datatypes
class List(object):
type = str
def __init__(self, values=()):
self.values = [item if isinstance(item, self.type) else self.type(item) for item in values]
def __getstate__(self):
state = []
for item in self:
if item is None:
pass
elif issubclass(self.type, bool):
item = 'true' if item else 'false'
elif issubclass(self.type, (int, int, str)):
item = str(item)
elif hasattr(item, '__getstate__'):
item = item.__getstate__()
if type(item) is not str:
raise TypeError("Expected unicode type for list member, got %s" % item.__class__.__name__)
else:
item = str(item)
state.append(item)
return state
def __setstate__(self, state):
if not isinstance(state, list):
state = [state]
values = []
for item in state:
if item is None:
pass
elif issubclass(self.type, bool):
if item.lower() in ('true', 'yes', 'on', '1'):
item = True
elif item.lower() in ('false', 'no', 'off', '0'):
item = False
else:
raise ValueError("invalid boolean value: %s" % (item,))
elif issubclass(self.type, (int, int, str)):
item = self.type(item)
elif hasattr(self.type, '__setstate__'):
object = self.type.__new__(self.type)
object.__setstate__(item)
item = object
else:
item = self.type(item)
values.append(item)
self.values = values
def __add__(self, other):
if isinstance(other, List):
return self.__class__(self.values + other.values)
else:
return self.__class__(self.values + other)
def __radd__(self, other):
if isinstance(other, List):
return self.__class__(other.values + self.values)
else:
return self.__class__(other + self.values)
def __mul__(self, other):
return self.__class__(self.values * other)
def __rmul__(self, other):
return self.__class__(other * self.values)
def __eq__(self, other):
if isinstance(other, List):
return self.values == other.values
else:
return self.values == other
def __ne__(self, other):
return not self.__eq__(other)
__hash__ = None
def __iter__(self):
return iter(self.values)
def __contains__(self, value):
return value in self.values
def __getitem__(self, key):
return self.values[key]
def __len__(self):
return len(self.values)
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.values)
def __str__(self):
return ', '.join(str(item) for item in self)
def __unicode__(self):
return ', '.join(str(item) for item in self)
# Generic datatypes
class ContentType(str):
def __new__(cls, value):
value = str(value)
if value == '*':
return value
try:
type, subtype = value.split('/')
except ValueError:
raise ValueError("illegal content-type: %s" % value)
else:
if type == '*':
raise ValueError("illegal content-type: %s" % value)
return value
class ContentTypeList(List):
type = ContentType
class CountryCode(str):
code_pattern = re.compile(r'[1-9][0-9]*')
def __new__(cls, value):
value = str(value)
if cls.code_pattern.match(value) is None:
raise ValueError("illegal country code: %s" % value)
return value
class NonNegativeInteger(int):
def __new__(cls, value):
value = int(value)
if value < 0:
raise ValueError("non-negative int expected, found %d" % value)
return value
class PositiveInteger(int):
def __new__(cls, value):
value = int(value)
if value <= 0:
raise ValueError("positive int expected, found %d" % value)
return value
class SIPAddress(str):
def __new__(cls, address):
address = str(address)
address = address.replace('@', '%40', address.count('@')-1)
try:
username, domain = address.split('@')
Hostname(domain)
except ValueError:
raise ValueError("illegal SIP address: %s, must be in user@domain format" % address)
return super(SIPAddress, cls).__new__(cls, address)
username = property(lambda self: self.split('@')[0])
domain = property(lambda self: self.split('@')[1])
# Custom datatypes
class PJSIPLogLevel(int):
def __new__(cls, value):
value = int(value)
if not (0 <= value <= 5):
raise ValueError("expected an integer number between 0 and 5, found %d" % value)
return value
class CodecList(List):
type = str
available_values = None # to be defined in a subclass
@property
def values(self):
return self.__dict__['values']
@values.setter
def values(self, values):
if not set(values).issubset(self.available_values):
raise ValueError("illegal codec values: %s" % ', '.join(values))
self.__dict__['values'] = values
# Audio datatypes
class AudioCodecList(CodecList):
- available_values = {'opus', 'speex', 'G722', 'GSM', 'iLBC', 'PCMU', 'PCMA'}
+ available_values = {'opus', 'speex', 'G722', 'GSM', 'iLBC', 'PCMU', 'PCMA', 'AMR-WB', 'AMR-NB'}
class SampleRate(int):
valid_values = (16000, 32000, 44100, 48000)
def __new__(cls, value):
value = int(value)
if value not in cls.valid_values:
raise ValueError("illegal sample rate: %d" % value)
return value
# Video datatypes
class H264Profile(str):
valid_values = ('baseline', 'main', 'high')
def __new__(cls, value):
if value.lower() not in cls.valid_values:
raise ValueError('invalid value, must be one of: {}'.format(', '.join(cls.valid_values)))
return super(H264Profile, cls).__new__(cls, value.lower())
class VideoResolution(tuple):
width = property(itemgetter(0))
height = property(itemgetter(1))
def __new__(cls, value):
if isinstance(value, tuple):
width, height = value
elif isinstance(value, str):
width, height = value.split('x')
else:
raise ValueError('invalid value: %r' % value)
return super(VideoResolution, cls).__new__(cls, (int(width), int(height)))
def __repr__(self):
return '%s(%d, %d)' % (self.__class__.__name__, self.width, self.height)
def __str__(self):
return '%dx%d' % (self.width, self.height)
def __unicode__(self):
return '%dx%d' % (self.width, self.height)
class VideoCodecList(CodecList):
available_values = {'H264', 'VP8', 'VP9'}
# Address and transport datatypes
class Port(int):
def __new__(cls, value):
value = int(value)
if not (0 <= value <= 65535):
raise ValueError("illegal port value: %s" % value)
return value
class PortRange(object):
def __init__(self, start, end):
self.start = Port(start)
self.end = Port(end)
if self.start == 0:
raise ValueError("illegal port value: 0")
if self.end == 0:
raise ValueError("illegal port value: 0")
if self.start > self.end:
raise ValueError("illegal port range: start port (%d) cannot be larger than end port (%d)" % (self.start, self.end))
def __getstate__(self):
return str(self)
def __setstate__(self, state):
self.__init__(*state.split('-'))
def __eq__(self, other):
if isinstance(other, PortRange):
return self.start == other.start and self.end == other.end
else:
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
__hash__ = None
def __repr__(self):
return '%s(start=%r, end=%r)' % (self.__class__.__name__, self.start, self.end)
def __str__(self):
return '%d-%d' % (self.start, self.end)
def __unicode__(self):
return '%d-%d' % (self.start, self.end)
class Hostname(str):
_host_re = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*)$")
def __new__(cls, value):
value = str(value)
if not cls._host_re.match(value):
raise ValueError("illegal hostname or ip address: %s" % value)
return value
class IPAddress(str):
_ip_re = re.compile(r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$")
def __new__(cls, value):
value = str(value)
if not cls._ip_re.match(value):
raise ValueError("illegal IP address: %s" % value)
return value
class DomainList(List):
type = str
_domain_re = re.compile(r"^[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*$")
@property
def values(self):
return self.__dict__['values']
@values.setter
def values(self, values):
for value in values:
if self._domain_re.match(value) is None:
raise ValueError("illegal domain: %s" % value)
self.__dict__['values'] = values
class EndpointAddress(object):
_description_re = re.compile(r"^(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P<port>\d+))?$")
default_port = 0
def __init__(self, host, port=None):
self.host = Hostname(host)
self.port = Port(port if port is not None else self.default_port)
if self.port == 0:
raise ValueError("illegal port value: 0")
def __getstate__(self):
return str(self)
def __setstate__(self, state):
match = self._description_re.match(state)
if match is None:
raise ValueError("illegal endpoint address: %s" % state)
self.__init__(**match.groupdict())
def __eq__(self, other):
if isinstance(other, EndpointAddress):
return self.host == other.host and self.port == other.port
else:
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
__hash__ = None
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.host, self.port)
def __str__(self):
return '%s:%d' % (self.host, self.port)
def __unicode__(self):
return '%s:%d' % (self.host, self.port)
@classmethod
def from_description(cls, description):
if not description:
return None
match = cls._description_re.match(description)
if match is None:
raise ValueError("illegal endpoint address: %s" % description)
return cls(**match.groupdict())
class EndpointIPAddress(EndpointAddress):
_description_re = re.compile(r"^(?P<host>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:(?P<port>\d+))?$")
def __init__(self, host, port=None):
self.host = IPAddress(host)
self.port = Port(port if port is not None else self.default_port)
if self.port == 0:
raise ValueError("illegal port value: 0")
def __setstate__(self, state):
match = self._description_re.match(state)
if match is None:
raise ValueError("illegal value: %s, must be an IP address" % state)
self.__init__(**match.groupdict())
@classmethod
def from_description(cls, description):
if not description:
return None
match = cls._description_re.match(description)
if match is None:
raise ValueError("illegal value: %s, must be an IP address" % description)
return cls(**match.groupdict())
class MSRPRelayAddress(object):
_description_re = re.compile(r"^(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P<port>\d+))?(;transport=(?P<transport>.+))?$")
def __init__(self, host, port=2855, transport='tls'):
self.host = Hostname(host)
self.port = Port(port)
self.transport = MSRPTransport(transport)
def __getstate__(self):
return str(self)
def __setstate__(self, state):
match = self._description_re.match(state)
if match is None:
raise ValueError("illegal MSRP relay address: %s" % state)
self.__init__(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
def __eq__(self, other):
if isinstance(other, MSRPRelayAddress):
return self.host == other.host and self.port == other.port and self.transport == other.transport
else:
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
__hash__ = None
def __repr__(self):
return '%s(%r, port=%r, transport=%r)' % (self.__class__.__name__, self.host, self.port, self.transport)
def __str__(self):
return '%s:%d;transport=%s' % (self.host, self.port, self.transport)
def __unicode__(self):
return '%s:%d;transport=%s' % (self.host, self.port, self.transport)
@classmethod
def from_description(cls, description):
if not description:
return None
match = cls._description_re.match(description)
if match is None:
raise ValueError("illegal MSRP relay address: %s" % description)
return cls(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
class SIPProxyAddress(object):
_description_re = re.compile(r"^(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P<port>\d+))?(;transport=(?P<transport>.+))?$")
def __init__(self, host, port=5060, transport='udp'):
self.host = Hostname(host)
self.port = Port(port)
if self.port == 0:
raise ValueError("illegal port value: 0")
self.transport = SIPTransport(transport)
def __getstate__(self):
return str(self)
def __setstate__(self, state):
match = self._description_re.match(state)
if match is None:
raise ValueError("illegal SIP proxy address: %s" % state)
self.__init__(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
def __eq__(self, other):
if isinstance(other, SIPProxyAddress):
return self.host == other.host and self.port == other.port and self.transport == other.transport
else:
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
__hash__ = None
def __repr__(self):
return '%s(%r, port=%r, transport=%r)' % (self.__class__.__name__, self.host, self.port, self.transport)
def __str__(self):
return '%s:%d;transport=%s' % (self.host, self.port, self.transport)
def __unicode__(self):
return '%s:%d;transport=%s' % (self.host, self.port, self.transport)
@classmethod
def from_description(cls, description):
if not description:
return None
match = cls._description_re.match(description)
if match is None:
raise ValueError("illegal SIP proxy address: %s" % description)
return cls(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
class STUNServerAddress(object):
_description_re = re.compile(r"^(?P<host>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*))(:(?P<port>\d+))?$")
default_port = 3478
def __init__(self, host, port=default_port):
self.host = Hostname(host)
self.port = Port(port)
def __getstate__(self):
return str(self)
def __setstate__(self, state):
match = self._description_re.match(state)
if match is None:
raise ValueError("illegal STUN server address: %s" % state)
self.__init__(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
def __eq__(self, other):
if isinstance(other, STUNServerAddress):
return self.host == other.host and self.port == other.port
else:
return NotImplemented
def __ne__(self, other):
equal = self.__eq__(other)
return NotImplemented if equal is NotImplemented else not equal
__hash__ = None
def __repr__(self):
return '%s(%r, port=%r)' % (self.__class__.__name__, self.host, self.port)
def __str__(self):
return '%s:%d' % (self.host, self.port)
def __unicode__(self):
return '%s:%d' % (self.host, self.port)
@classmethod
def from_description(cls, description):
if not description:
return None
match = cls._description_re.match(description)
if match is None:
raise ValueError("illegal STUN server address: %s" % description)
return cls(**dict((k, v) for k, v in list(match.groupdict().items()) if v is not None))
class STUNServerAddressList(List):
type = STUNServerAddress
class XCAPRoot(str):
def __new__(cls, value):
value = str(value)
uri = urllib.parse.urlparse(value)
if uri.scheme not in ('http', 'https'):
raise ValueError("illegal XCAP root scheme (http and https only): %s" % uri.scheme)
if uri.params:
raise ValueError("XCAP root must not contain parameters: %s" % (uri.params,))
if uri.query:
raise ValueError("XCAP root must not contain query component: %s" % (uri.query,))
if uri.fragment:
raise ValueError("XCAP root must not contain fragment component: %s" % (uri.fragment,))
# check port and hostname
Hostname(uri.hostname)
if uri.port is not None:
port = Port(uri.port)
if port == 0:
raise ValueError("illegal port value: 0")
return value
class MSRPConnectionModel(str):
available_values = ('relay', 'acm')
def __new__(cls, value):
value = str(value)
if value not in cls.available_values:
raise ValueError("illegal value for MSRP NAT model: %s" % value)
return value
class MSRPTransport(str):
available_values = ('tls', 'tcp')
def __new__(cls, value):
value = str(value)
if value not in cls.available_values:
raise ValueError("illegal value for MSRP transport: %s" % value)
return value
class SIPTransport(str):
available_values = ('udp', 'tcp', 'tls')
def __new__(cls, value):
value = str(value)
if value not in cls.available_values:
raise ValueError("illegal value for SIP transport: %s" % value)
return value
class SIPTransportList(List):
type = SIPTransport
available_values = SIPTransport.available_values
class SRTPKeyNegotiation(str):
available_values = ('opportunistic', 'sdes_optional', 'sdes_mandatory', 'zrtp')
def __new__(cls, value):
value = str(value)
if value not in cls.available_values:
raise ValueError("illegal value for SRTP key negotiation: %s" % value)
return value
# Path datatypes
class Path(str):
def __new__(cls, path):
return super(Path, cls).__new__(cls, os.path.normpath(path))
@property
def normalized(self):
if not self.startswith('~'):
return self
encoding = locale.getpreferredencoding() or 'ascii'
return os.path.expanduser(self.encode(encoding)).decode(encoding)
diff --git a/sipsimple/configuration/settings.py b/sipsimple/configuration/settings.py
index 389f7e61..26fa6c71 100644
--- a/sipsimple/configuration/settings.py
+++ b/sipsimple/configuration/settings.py
@@ -1,109 +1,109 @@
"""
SIP SIMPLE settings.
Definition of general (non-account related) settings.
"""
from sipsimple import __version__
from sipsimple.configuration import CorrelatedSetting, RuntimeSetting, Setting, SettingsGroup, SettingsObject
from sipsimple.configuration.datatypes import NonNegativeInteger, PJSIPLogLevel
from sipsimple.configuration.datatypes import AudioCodecList, SampleRate, VideoCodecList
from sipsimple.configuration.datatypes import Port, PortRange, SIPTransportList
from sipsimple.configuration.datatypes import Path
from sipsimple.configuration.datatypes import H264Profile, VideoResolution
__all__ = ['SIPSimpleSettings']
class EchoCancellerSettings(SettingsGroup):
enabled = Setting(type=bool, default=True)
tail_length = Setting(type=NonNegativeInteger, default=2)
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)
sample_rate = Setting(type=SampleRate, default=44100)
muted = RuntimeSetting(type=bool, default=False)
silent = Setting(type=bool, default=False)
echo_canceller = EchoCancellerSettings
class H264Settings(SettingsGroup):
profile = Setting(type=H264Profile, default='baseline')
level = Setting(type=str, default='3.1')
class VideoSettings(SettingsGroup):
device = Setting(type=str, default='system_default', nillable=True)
resolution = Setting(type=VideoResolution, default=VideoResolution('1280x720'))
framerate = Setting(type=int, default=25)
max_bitrate = Setting(type=float, default=None, nillable=True)
muted = RuntimeSetting(type=bool, default=False)
h264 = H264Settings
class ChatSettings(SettingsGroup):
pass
class ScreenSharingSettings(SettingsGroup):
pass
class FileTransferSettings(SettingsGroup):
directory = Setting(type=Path, default=Path('~/Downloads'))
class LogsSettings(SettingsGroup):
trace_msrp = Setting(type=bool, default=False)
trace_sip = Setting(type=bool, default=False)
trace_pjsip = Setting(type=bool, default=False)
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(('opus', 'G722', 'PCMU', 'PCMA')))
+ audio_codec_list = Setting(type=AudioCodecList, default=AudioCodecList(('opus', 'G722', 'PCMU', 'PCMA', 'speex', 'iLBC', 'GSM', 'AMR-NB', 'AMR-WB')))
video_codec_list = Setting(type=VideoCodecList, default=VideoCodecList(('H264', 'VP8', 'VP9')))
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)
certificate = Setting(type=Path, default=None, nillable=True)
verify_server = Setting(type=bool, default=False)
class SIPSimpleSettings(SettingsObject):
__id__ = 'SIPSimpleSettings'
default_account = Setting(type=str, default='bonjour@local', nillable=True)
user_agent = Setting(type=str, default='sipsimple %s' % __version__)
instance_id = Setting(type=str, default='')
audio = AudioSettings
video = VideoSettings
chat = ChatSettings
screen_sharing = ScreenSharingSettings
file_transfer = FileTransferSettings
logs = LogsSettings
rtp = RTPSettings
sip = SIPSettings
tls = TLSSettings
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Dec 28, 10:54 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3454265
Default Alt Text
(38 KB)
Attached To
Mode
rPYNSIPSIMPLE python3-sipsimple
Attached
Detach File
Event Timeline
Log In to Comment