diff --git a/debian/control b/debian/control index e86af261..726fe029 100644 --- a/debian/control +++ b/debian/control @@ -1,45 +1,47 @@ Source: python3-sipsimple Section: python Priority: optional Maintainer: Adrian Georgescu Uploaders: Tijmen de Mes Build-Depends: debhelper (>= 11), python3, dh-python, python3-all-dev, cython3, libasound2-dev, libssl-dev, libv4l-dev, libavcodec-dev, libavformat-dev, + libopencore-amrnb-dev, + libopencore-amrwb-dev, libavutil-dev, libswscale-dev, libx264-dev, libvpx-dev, libopus-dev, libsqlite3-dev, pkg-config, uuid-dev Standards-Version: 4.5.0 Homepage: https://sipsimpleclient.org Package: python3-sipsimple Architecture: any Depends: ${python3:Depends}, ${shlibs:Depends}, ${misc:Depends}, python3-application, python3-dateutil, python3-dnspython, python3-eventlib, python3-gevent, python3-gnutls, python3-lxml, python3-msrplib, python3-otr, python3-twisted, python3-xcaplib Suggests: libavahi-compat-libdnssd1 Provides: ${python3:Provides} Description: SIP SIMPLE SDK is a Python library for desktop operating systems designed for the development of real time communications applications based on SIP protocol and related media like audio, video, messaging, file transfers, desktop sharing and presence. diff --git a/setup_pjsip.py b/setup_pjsip.py index cd247365..a831b59b 100644 --- a/setup_pjsip.py +++ b/setup_pjsip.py @@ -1,261 +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_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_OPENCORE_AMRNB_CODEC 0", + "#define PJMEDIA_HAS_OPENCORE_AMRWB_CODEC 0", "#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/settings.py b/sipsimple/configuration/settings.py index 85e0f091..f4fd1af5 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', 'speex', 'iLBC', 'GSM', 'AMR-WB'))) + audio_codec_list = Setting(type=AudioCodecList, default=AudioCodecList(('opus', 'G722', 'PCMU', 'PCMA', 'speex', 'iLBC', 'GSM'))) 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