def __init__(self, src_type=None, src_options={}, codecs=get_codecs(), codec_options={}, volume=1.0): if not src_type: from xpra.sound.pulseaudio_util import get_pa_device_options monitor_devices = get_pa_device_options(True, False) log.info("found pulseaudio monitor devices: %s", monitor_devices) if len(monitor_devices)==0: log.warn("could not detect any pulseaudio monitor devices") log.warn(" a test source will be used instead") src_type = "audiotestsrc" default_src_options = {"wave":2, "freq":100, "volume":0.4} else: monitor_device = monitor_devices.items()[0][0] log.info("using pulseaudio source device:") log.info(" '%s'", monitor_device) src_type = "pulsesrc" default_src_options = {"device" : monitor_device} src_options = default_src_options if src_type not in get_source_plugins(): raise InitExit(1, "invalid source plugin '%s', valid options are: %s" % (src_type, ",".join(get_source_plugins()))) matching = [x for x in CODEC_ORDER if (x in codecs and x in get_codecs())] log("SoundSource(..) found matching codecs %s", matching) if not matching: raise InitExit(1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_codecs().keys()))) codec = matching[0] encoder, fmt = get_encoder_formatter(codec) SoundPipeline.__init__(self, codec) self.src_type = src_type source_str = plugin_str(src_type, src_options) #FIXME: this is ugly and relies on the fact that we don't pass any codec options to work! encoder_str = plugin_str(encoder, codec_options or ENCODER_DEFAULT_OPTIONS.get(encoder, {})) fmt_str = plugin_str(fmt, MUXER_DEFAULT_OPTIONS.get(fmt, {})) pipeline_els = [source_str] if encoder in ENCODER_NEEDS_AUDIOCONVERT or src_type in SOURCE_NEEDS_AUDIOCONVERT: pipeline_els += ["audioconvert"] pipeline_els.append("volume name=volume volume=%s" % volume) pipeline_els += [encoder_str, fmt_str, APPSINK] self.setup_pipeline_and_bus(pipeline_els) self.volume = self.pipeline.get_by_name("volume") self.sink = self.pipeline.get_by_name("sink") try: if get_gst_version()<(1,0): self.sink.set_property("enable-last-buffer", False) else: self.sink.set_property("enable-last-sample", False) except Exception as e: log("failed to disable last buffer: %s", e) self.caps = None self.skipped_caps = set() if JITTER>0: self.jitter_queue = Queue() try: #Gst 1.0: self.sink.connect("new-sample", self.on_new_sample) self.sink.connect("new-preroll", self.on_new_preroll1) except: #Gst 0.10: self.sink.connect("new-buffer", self.on_new_buffer) self.sink.connect("new-preroll", self.on_new_preroll0)
def do_wrap_socket(tcp_socket): assert tcp_socket ssllog("do_wrap_socket(%s)", tcp_socket) if WIN32: #on win32, setting the tcp socket to blocking doesn't work? #we still hit the following errors that we need to retry: from xpra.net import bytestreams bytestreams.CAN_RETRY_EXCEPTIONS = (ssl.SSLWantReadError, ssl.SSLWantWriteError) else: tcp_socket.setblocking(True) try: ssl_sock = wrap_socket(tcp_socket, **kwargs) except Exception as e: ssllog.debug("wrap_socket(%s, %s)", tcp_socket, kwargs, exc_info=True) SSLEOFError = getattr(ssl, "SSLEOFError", None) if SSLEOFError and isinstance(e, SSLEOFError): return None raise InitExit(EXIT_SSL_FAILURE, "Cannot wrap socket %s: %s" % (tcp_socket, e)) from None if not server_side: try: ssl_sock.do_handshake(True) except Exception as e: ssllog.debug("do_handshake", exc_info=True) SSLEOFError = getattr(ssl, "SSLEOFError", None) if SSLEOFError and isinstance(e, SSLEOFError): return None status = EXIT_SSL_FAILURE SSLCertVerificationError = getattr(ssl, "SSLCertVerificationError", None) if SSLCertVerificationError and isinstance( e, SSLCertVerificationError): try: msg = e.args[1].split(":", 2)[2] except (ValueError, IndexError): msg = str(e) status = EXIT_SSL_CERTIFICATE_VERIFY_FAILURE #ssllog.warn("host failed SSL verification: %s", msg) else: msg = str(e) raise InitExit(status, "SSL handshake failed: %s" % msg) from None return ssl_sock
def get_encryption_key(self): conn = self._protocol._conn keydata = parse_encoded_bin_data(conn.options.get("keydata", None)) cryptolog("get_encryption_key() connection options keydata=%s", ellipsizer(keydata)) if keydata: return keydata keyfile = conn.options.get("encryption-keyfile") or conn.options.get("keyfile") or self.encryption_keyfile if keyfile: if not os.path.isabs(keyfile): keyfile = os.path.abspath(keyfile) if os.path.exists(keyfile): key = filedata_nocrlf(keyfile) if key: cryptolog("get_encryption_key() loaded %i bytes from '%s'", len(key or ""), keyfile) return key else: cryptolog("get_encryption_key() keyfile '%s' is empty", keyfile) else: cryptolog("get_encryption_key() file '%s' does not exist", keyfile) XPRA_ENCRYPTION_KEY = "XPRA_ENCRYPTION_KEY" key = strtobytes(os.environ.get(XPRA_ENCRYPTION_KEY, '')) cryptolog("get_encryption_key() got %i bytes from '%s' environment variable", len(key or ""), XPRA_ENCRYPTION_KEY) if key: return key.strip(b"\n\r") raise InitExit(EXIT_ENCRYPTION, "no encryption key")
def get_encryption_key(self): conn = self._protocol._conn key = conn.options.get("keydata", None) cryptolog("get_encryption_key() connection options keydata=%s", key) if key: #may be specified as hex: if key.lower().startswith("0x"): import binascii return binascii.unhexlify(key[2:]) return strtobytes(key) keyfile = conn.options.get("encryption-keyfile") or conn.options.get( "keyfile") or self.encryption_keyfile if keyfile: if os.path.exists(keyfile): key = filedata_nocrlf(keyfile) cryptolog("get_encryption_key() loaded %i bytes from '%s'", len(key or ""), keyfile) return key cryptolog("get_encryption_key() file '%s' does not exist", keyfile) XPRA_ENCRYPTION_KEY = "XPRA_ENCRYPTION_KEY" key = strtobytes(os.environ.get(XPRA_ENCRYPTION_KEY, '')) cryptolog( "get_encryption_key() got %i bytes from '%s' environment variable", len(key or ""), XPRA_ENCRYPTION_KEY) if key: return key.strip(b"\n\r") raise InitExit(1, "no encryption key")
def ssl_handshake(ssl_sock): from xpra.log import Logger ssllog = Logger("ssl") try: ssl_sock.do_handshake(True) ssllog.info("SSL handshake complete, %s", ssl_sock.version()) log_ssl_info(ssl_sock) except Exception as e: ssllog("do_handshake", exc_info=True) log_ssl_info(ssl_sock) import ssl SSLEOFError = getattr(ssl, "SSLEOFError", None) if SSLEOFError and isinstance(e, SSLEOFError): return None status = EXIT_SSL_FAILURE SSLCertVerificationError = getattr(ssl, "SSLCertVerificationError", None) if SSLCertVerificationError and isinstance(e, SSLCertVerificationError): verify_code = getattr(e, "verify_code", 0) ssllog("verify_code=%s", SSL_VERIFY_CODES.get(verify_code, verify_code)) try: msg = getattr(e, "verify_message") or (e.args[1].split(":", 2)[2]) except (ValueError, IndexError): msg = str(e) status = EXIT_SSL_CERTIFICATE_VERIFY_FAILURE ssllog("host failed SSL verification: %s", msg) raise SSLVerifyFailure(status, msg, verify_code, ssl_sock) from None raise InitExit(status, "SSL handshake failed: %s" % str(e)) from None return ssl_sock
def handle_socket_error(sockpath, sperms, e): log = get_network_logger() log("socket creation error", exc_info=True) if sockpath.startswith("/var/run/xpra") or sockpath.startswith("/run/xpra"): log.info("cannot create group socket '%s'", sockpath) log.info(" %s", e) dirname = sockpath[:sockpath.find("xpra")+len("xpra")] if not os.path.exists(dirname): log.info(" %s does not exist", dirname) #only show extra information if the socket permissions #would have been accessible by the group: elif POSIX and (sperms & 0o40): uid = getuid() username = get_username_for_uid(uid) groups = get_groups(username) log.info(" user '%s' is a member of groups: %s", username, csv(groups) or "no groups!") if "xpra" not in groups: log.info(" add 'xpra' group membership to enable group socket sharing") for x in path_permission_info(dirname): log.info(" %s", x) elif sockpath.startswith("/var/run/user") or sockpath.startswith("/run/user"): log.warn("Warning: cannot create socket '%s':", sockpath) log.warn(" %s", e) run_user = sockpath.split("/user")[0]+"/user" if not os.path.exists(run_user): log.warn(" %s does not exist", run_user) else: log.warn(" ($XDG_RUNTIME_DIR has not been created?)") else: log.error("Error: failed to create socket '%s':", sockpath) log.error(" %s", e) raise InitExit(EXIT_SOCKET_CREATION_ERROR, "failed to create socket %s" % sockpath)
def get_encryption_key(self): key = load_binary_file(self.encryption_keyfile) if not key: key = os.environ.get('XPRA_ENCRYPTION_KEY') if not key: raise InitExit(1, "no encryption key") return key.strip("\n\r")
def checkstate(sockpath, state): if state not in (DotXpra.DEAD, DotXpra.UNKNOWN): if state==DotXpra.INACCESSIBLE: raise InitException("An xpra server is already running at %s\n" % (sockpath,)) raise InitExit(EXIT_SERVER_ALREADY_EXISTS, "You already have an xpra server running at %s\n" " (did you want 'xpra upgrade'?)" % (sockpath,))
def set_autostart(enabled): target = get_autostart_file() if enabled: #find the file to copy there: autostart = os.path.join(do_get_resources_dir(), "autostart.desktop") if not os.path.exists(autostart): raise InitExit(EXIT_FILE_NOT_FOUND, "%s file not found" % autostart) copy2(autostart, target) else: os.unlink(target)
def get_encryption_key(self): key = None if os.path.exists(self.encryption_keyfile): key = load_binary_file(self.encryption_keyfile) cryptolog("get_encryption_key() loaded %i bytes from '%s'", len(key or ""), self.encryption_keyfile) if not key: XPRA_ENCRYPTION_KEY = "XPRA_ENCRYPTION_KEY" key = strtobytes(os.environ.get(XPRA_ENCRYPTION_KEY, '')) cryptolog("get_encryption_key() got %i bytes from '%s' environment variable", len(key or ""), XPRA_ENCRYPTION_KEY) if not key: raise InitExit(1, "no encryption key") return key.strip(b"\n\r")
def __init__(self): #sanity check: image = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault) if image is None: from xpra.scripts.config import InitExit log("cannot grab test screenshot - maybe you need to run this command whilst logged in via the UI") raise InitExit(1, "cannot grab pixels from the screen, make sure this command is launched from a GUI session") patch_picture_encode() self.refresh_count = 0 self.refresh_rectangle_count = 0 self.refresh_registered = False GTKShadowServerBase.__init__(self)
def setup_vsock_socket(cid, iport): log = get_network_logger() try: from xpra.net.vsock import bind_vsocket #@UnresolvedImport vsock_socket = bind_vsocket(cid=cid, port=iport) except Exception as e: raise InitExit(EXIT_SOCKET_CREATION_ERROR, "failed to setup vsock socket on %s:%s %s" % (cid, iport, e)) from None def cleanup_vsock_socket(): log.info("closing vsock socket %s:%s", cid, iport) try: vsock_socket.close() except OSError: pass return "vsock", vsock_socket, (cid, iport), cleanup_vsock_socket
def get_encryption_key(self): key = load_binary_file(self.encryption_keyfile) if not key: key = os.environ.get('XPRA_ENCRYPTION_KEY') if not key and self.password_file: key = load_binary_file(self.password_file) if key: netlog("used password file as encryption key") if not key: key = os.environ.get('XPRA_PASSWORD') if key: netlog("used XPRA_PASSWORD as encryption key") if key is None: raise InitExit(1, "no encryption key") return key.strip("\n\r")
def __init__(self): #sanity check: check_display() image = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault) if image is None: log("cannot grab test screenshot - maybe you need to run this command whilst logged in via the UI") raise InitExit(EXIT_FAILURE, "cannot grab pixels from the screen, make sure this command is launched from a GUI session") patch_pixels_to_bytes() self.refresh_count = 0 self.refresh_rectangle_count = 0 self.refresh_registered = False super().__init__()
def run_pinentry(extra_args): messages = list(extra_args) def get_input(): if not messages: return None return messages.pop(0) def process_output(message, line): if line.startswith(b"ERR "): log.error("Error: pinentry responded to '%s' with:", message) log.error(" %s", line.rstrip(b"\n\r").decode()) else: log("pinentry sent %r", line) pinentry_cmd = get_pinentry_command() or "pinentry" proc = popen_pinentry(pinentry_cmd) if not proc: raise InitExit(EXIT_UNSUPPORTED, "cannot run pinentry") return do_run_pinentry(proc, get_input, process_output)
def setup_udp_socket(host, iport, socktype="udp"): log = get_network_logger() try: udp_socket = create_udp_socket(host, iport) except Exception as e: log("create_udp_socket%s", (host, iport), exc_info=True) raise InitExit(EXIT_SOCKET_CREATION_ERROR, "failed to setup %s socket on %s:%s %s" % (socktype, host, iport, e)) from None def cleanup_udp_socket(): log.info("closing %s socket %s:%s", socktype, host, iport) try: udp_socket.close() except OSError: pass if iport==0: iport = udp_socket.getsockname()[1] log.info("allocated UDP port %i for %s", iport, host) log("%s: %s:%s : %s", socktype, host, iport, socket) log.info("created UDP socket %s:%s", host, iport) return socktype, udp_socket, (host, iport), cleanup_udp_socket
def do_wrap_socket(tcp_socket, context, **kwargs): wrap_socket = context.wrap_socket assert tcp_socket from xpra.log import Logger ssllog = Logger("ssl") ssllog("do_wrap_socket(%s, %s)", tcp_socket, context) import ssl if WIN32: #on win32, setting the tcp socket to blocking doesn't work? #we still hit the following errors that we need to retry: from xpra.net import bytestreams bytestreams.CAN_RETRY_EXCEPTIONS = (ssl.SSLWantReadError, ssl.SSLWantWriteError) else: tcp_socket.setblocking(True) try: ssl_sock = wrap_socket(tcp_socket, **kwargs) except Exception as e: ssllog.debug("wrap_socket(%s, %s)", tcp_socket, kwargs, exc_info=True) SSLEOFError = getattr(ssl, "SSLEOFError", None) if SSLEOFError and isinstance(e, SSLEOFError): return None raise InitExit(EXIT_SSL_FAILURE, "Cannot wrap socket %s: %s" % (tcp_socket, e)) from None return ssl_sock
def __init__(self, sink_type=None, sink_options={}, codecs=get_decoders(), codec_options={}, volume=1.0): if not sink_type: sink_type = get_default_sink() if sink_type not in get_sink_plugins(): raise InitExit(1, "invalid sink: %s" % sink_type) matching = [ x for x in CODEC_ORDER if (x in codecs and x in get_decoders()) ] log("SoundSink(..) found matching codecs %s", matching) if not matching: raise InitExit( 1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_decoders().keys()))) codec = matching[0] decoder, parser, stream_compressor = get_decoder_elements(codec) SoundPipeline.__init__(self, codec) self.container_format = (parser or "").replace("demux", "").replace("depay", "") self.sink_type = sink_type self.stream_compressor = stream_compressor log("container format=%s, stream_compressor=%s, sink type=%s", self.container_format, self.stream_compressor, self.sink_type) self.levels = deque(maxlen=100) self.volume = None self.src = None self.queue = None self.normal_volume = volume self.target_volume = volume self.volume_timer = 0 self.overruns = 0 self.underruns = 0 self.overrun_events = deque(maxlen=100) self.queue_state = "starting" self.last_data = None self.last_underrun = 0 self.last_overrun = 0 self.refill = True self.last_max_update = monotonic_time() self.last_min_update = monotonic_time() self.level_lock = Lock() pipeline_els = [] appsrc_el = [ "appsrc", #"do-timestamp=1", "name=src", "emit-signals=0", "block=0", "is-live=0", "stream-type=%s" % STREAM_TYPE, "format=%s" % BUFFER_FORMAT ] pipeline_els.append(" ".join(appsrc_el)) if parser: pipeline_els.append(parser) if decoder: decoder_str = plugin_str(decoder, codec_options) pipeline_els.append(decoder_str) pipeline_els.append("audioconvert") pipeline_els.append("audioresample") if QUEUE_TIME > 0: pipeline_els.append(" ".join([ "queue", "name=queue", "min-threshold-time=0", "max-size-buffers=0", "max-size-bytes=0", "max-size-time=%s" % QUEUE_TIME, "leaky=%s" % QUEUE_LEAK ])) pipeline_els.append("volume name=volume volume=0") sink_attributes = SINK_SHARED_DEFAULT_ATTRIBUTES.copy() #anything older than this may cause problems (ie: centos 6.x) #because the attributes may not exist sink_attributes.update(SINK_DEFAULT_ATTRIBUTES.get(sink_type, {})) get_options_cb = DEFAULT_SINK_PLUGIN_OPTIONS.get( sink_type.replace("sink", "")) if get_options_cb: v = get_options_cb() log("%s()=%s", get_options_cb, v) sink_attributes.update(v) sink_attributes.update(sink_options) sink_str = plugin_str(sink_type, sink_attributes) pipeline_els.append(sink_str) if not self.setup_pipeline_and_bus(pipeline_els): return self.volume = self.pipeline.get_by_name("volume") self.src = self.pipeline.get_by_name("src") self.queue = self.pipeline.get_by_name("queue") if self.queue: if QUEUE_SILENT: self.queue.set_property("silent", False) else: self.queue.connect("overrun", self.queue_overrun) self.queue.connect("underrun", self.queue_underrun) self.queue.connect("running", self.queue_running) self.queue.connect("pushing", self.queue_pushing)
def __init__(self, src_type=None, src_options=None, codecs=(), codec_options=None, volume=1.0): if not src_type: try: from xpra.sound.pulseaudio.pulseaudio_util import get_pa_device_options monitor_devices = get_pa_device_options(True, False) log.info("found pulseaudio monitor devices: %s", monitor_devices) except ImportError as e: log.warn("Warning: pulseaudio is not available!") log.warn(" %s", e) monitor_devices = [] if not monitor_devices: log.warn("could not detect any pulseaudio monitor devices") log.warn(" a test source will be used instead") src_type = "audiotestsrc" default_src_options = {"wave": 2, "freq": 100, "volume": 0.4} else: monitor_device = monitor_devices.items()[0][0] log.info("using pulseaudio source device:") log.info(" '%s'", monitor_device) src_type = "pulsesrc" default_src_options = {"device": monitor_device} src_options = default_src_options if src_type not in get_source_plugins(): raise InitExit( 1, "invalid source plugin '%s', valid options are: %s" % (src_type, ",".join(get_source_plugins()))) matching = [ x for x in CODEC_ORDER if (x in codecs and x in get_encoders()) ] log("SoundSource(..) found matching codecs %s", matching) if not matching: raise InitExit( 1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_encoders().keys()))) codec = matching[0] encoder, fmt, stream_compressor = get_encoder_elements(codec) super().__init__(codec) self.queue = None self.caps = None self.volume = None self.sink = None self.src = None self.src_type = src_type self.timestamp = None self.min_timestamp = 0 self.max_timestamp = 0 self.pending_metadata = [] self.buffer_latency = True self.jitter_queue = None self.container_format = (fmt or "").replace("mux", "").replace("pay", "") self.stream_compressor = stream_compressor if src_options is None: src_options = {} src_options["name"] = "src" source_str = plugin_str(src_type, src_options) #FIXME: this is ugly and relies on the fact that we don't pass any codec options to work! pipeline_els = [source_str] log("has plugin(timestamp)=%s", has_plugins("timestamp")) if has_plugins("timestamp"): pipeline_els.append("timestamp name=timestamp") if SOURCE_QUEUE_TIME > 0: queue_el = [ "queue", "name=queue", "min-threshold-time=0", "max-size-buffers=0", "max-size-bytes=0", "max-size-time=%s" % (SOURCE_QUEUE_TIME * MS_TO_NS), "leaky=%s" % GST_QUEUE_LEAK_DOWNSTREAM ] pipeline_els += [" ".join(queue_el)] if encoder in ENCODER_NEEDS_AUDIOCONVERT or src_type in SOURCE_NEEDS_AUDIOCONVERT: pipeline_els += ["audioconvert"] if CUTTER_THRESHOLD > 0 and encoder not in ENCODER_CANNOT_USE_CUTTER and not fmt: pipeline_els.append( "cutter threshold=%.4f run-length=%i pre-length=%i leaky=false name=cutter" % (CUTTER_THRESHOLD, CUTTER_RUN_LENGTH * MS_TO_NS, CUTTER_PRE_LENGTH * MS_TO_NS)) if encoder in CUTTER_NEEDS_CONVERT: pipeline_els.append("audioconvert") if encoder in CUTTER_NEEDS_RESAMPLE: pipeline_els.append("audioresample") pipeline_els.append("volume name=volume volume=%s" % volume) if encoder: encoder_str = plugin_str( encoder, codec_options or get_encoder_default_options(encoder)) pipeline_els.append(encoder_str) if fmt: fmt_str = plugin_str(fmt, MUXER_DEFAULT_OPTIONS.get(fmt, {})) pipeline_els.append(fmt_str) pipeline_els.append(APPSINK) if not self.setup_pipeline_and_bus(pipeline_els): return self.timestamp = self.pipeline.get_by_name("timestamp") self.volume = self.pipeline.get_by_name("volume") self.sink = self.pipeline.get_by_name("sink") if SOURCE_QUEUE_TIME > 0: self.queue = self.pipeline.get_by_name("queue") if self.queue: try: self.queue.set_property("silent", True) except Exception as e: log("cannot make queue silent: %s", e) self.sink.set_property("enable-last-sample", False) self.skipped_caps = set() if JITTER > 0: self.jitter_queue = Queue() #Gst 1.0: self.sink.connect("new-sample", self.on_new_sample) self.sink.connect("new-preroll", self.on_new_preroll) self.src = self.pipeline.get_by_name("src") for x in ("actual-buffer-time", "actual-latency-time"): try: gstlog("initial %s: %s", x, self.src.get_property(x)) except Exception as e: gstlog("no %s property on %s: %s", x, self.src, e) self.buffer_latency = False #if the env vars have been set, try to honour the settings: global BUFFER_TIME, LATENCY_TIME if BUFFER_TIME > 0: if BUFFER_TIME < LATENCY_TIME: log.warn( "Warning: latency (%ims) must be lower than the buffer time (%ims)", LATENCY_TIME, BUFFER_TIME) else: log( "latency tuning for %s, will try to set buffer-time=%i, latency-time=%i", src_type, BUFFER_TIME, LATENCY_TIME) def settime(attr, v): try: cval = self.src.get_property(attr) gstlog("default: %s=%i", attr, cval // 1000) if v >= 0: self.src.set_property(attr, v * 1000) gstlog("overriding with: %s=%i", attr, v) except Exception as e: log.warn("source %s does not support '%s': %s", self.src_type, attr, e) settime("buffer-time", BUFFER_TIME) settime("latency-time", LATENCY_TIME) self.init_file(codec)
def __init__(self, src_type=None, src_options={}, codecs=get_encoders(), codec_options={}, volume=1.0): if not src_type: try: from xpra.sound.pulseaudio.pulseaudio_util import get_pa_device_options monitor_devices = get_pa_device_options(True, False) log.info("found pulseaudio monitor devices: %s", monitor_devices) except ImportError as e: log.warn("Warning: pulseaudio is not available!") log.warn(" %s", e) monitor_devices = [] if len(monitor_devices) == 0: log.warn("could not detect any pulseaudio monitor devices") log.warn(" a test source will be used instead") src_type = "audiotestsrc" default_src_options = {"wave": 2, "freq": 100, "volume": 0.4} else: monitor_device = monitor_devices.items()[0][0] log.info("using pulseaudio source device:") log.info(" '%s'", monitor_device) src_type = "pulsesrc" default_src_options = {"device": monitor_device} src_options = default_src_options if src_type not in get_source_plugins(): raise InitExit( 1, "invalid source plugin '%s', valid options are: %s" % (src_type, ",".join(get_source_plugins()))) matching = [ x for x in CODEC_ORDER if (x in codecs and x in get_encoders()) ] log("SoundSource(..) found matching codecs %s", matching) if not matching: raise InitExit( 1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_encoders().keys()))) codec = matching[0] encoder, fmt, stream_compressor = get_encoder_elements(codec) SoundPipeline.__init__(self, codec) self.queue = None self.caps = None self.volume = None self.sink = None self.src = None self.src_type = src_type self.pending_metadata = [] self.buffer_latency = True self.jitter_queue = None self.file = None self.container_format = (fmt or "").replace("mux", "").replace("pay", "") self.stream_compressor = stream_compressor src_options["name"] = "src" source_str = plugin_str(src_type, src_options) #FIXME: this is ugly and relies on the fact that we don't pass any codec options to work! pipeline_els = [source_str] if SOURCE_QUEUE_TIME > 0: queue_el = [ "queue", "name=queue", "min-threshold-time=0", "max-size-buffers=0", "max-size-bytes=0", "max-size-time=%s" % (SOURCE_QUEUE_TIME * MS_TO_NS), "leaky=%s" % GST_QUEUE_LEAK_DOWNSTREAM ] pipeline_els += [" ".join(queue_el)] if encoder in ENCODER_NEEDS_AUDIOCONVERT or src_type in SOURCE_NEEDS_AUDIOCONVERT: pipeline_els += ["audioconvert"] pipeline_els.append("volume name=volume volume=%s" % volume) if encoder: encoder_str = plugin_str( encoder, codec_options or get_encoder_default_options(encoder)) pipeline_els.append(encoder_str) if fmt: fmt_str = plugin_str(fmt, MUXER_DEFAULT_OPTIONS.get(fmt, {})) pipeline_els.append(fmt_str) pipeline_els.append(APPSINK) if not self.setup_pipeline_and_bus(pipeline_els): return self.volume = self.pipeline.get_by_name("volume") self.sink = self.pipeline.get_by_name("sink") if SOURCE_QUEUE_TIME > 0: self.queue = self.pipeline.get_by_name("queue") if self.queue: try: self.queue.set_property("silent", True) except Exception as e: log("cannot make queue silent: %s", e) try: if get_gst_version() < (1, 0): self.sink.set_property("enable-last-buffer", False) else: self.sink.set_property("enable-last-sample", False) except Exception as e: log("failed to disable last buffer: %s", e) self.skipped_caps = set() if JITTER > 0: self.jitter_queue = Queue() try: #Gst 1.0: self.sink.connect("new-sample", self.on_new_sample) self.sink.connect("new-preroll", self.on_new_preroll1) except: #Gst 0.10: self.sink.connect("new-buffer", self.on_new_buffer) self.sink.connect("new-preroll", self.on_new_preroll0) self.src = self.pipeline.get_by_name("src") try: for x in ("actual-buffer-time", "actual-latency-time"): #don't comment this out, it is used to verify the attributes are present: try: gstlog("initial %s: %s", x, self.src.get_property(x)) except Exception as e: self.buffer_latency = False except Exception as e: log.info( "source %s does not support 'buffer-time' or 'latency-time':", self.src_type) log.info(" %s", e) else: #if the env vars have been set, try to honour the settings: global BUFFER_TIME, LATENCY_TIME if BUFFER_TIME > 0: if BUFFER_TIME < LATENCY_TIME: log.warn( "Warning: latency (%ims) must be lower than the buffer time (%ims)", LATENCY_TIME, BUFFER_TIME) else: log( "latency tuning for %s, will try to set buffer-time=%i, latency-time=%i", src_type, BUFFER_TIME, LATENCY_TIME) def settime(attr, v): try: cval = self.src.get_property(attr) gstlog("default: %s=%i", attr, cval // 1000) if v >= 0: self.src.set_property(attr, v * 1000) gstlog("overriding with: %s=%i", attr, v) except Exception as e: log.warn("source %s does not support '%s': %s", self.src_type, attr, e) settime("buffer-time", BUFFER_TIME) settime("latency-time", LATENCY_TIME) gen = generation.increase() if SAVE_TO_FILE is not None: parts = codec.split("+") if len(parts) > 1: filename = SAVE_TO_FILE + str( gen) + "-" + parts[0] + ".%s" % parts[1] else: filename = SAVE_TO_FILE + str(gen) + ".%s" % codec self.file = open(filename, 'wb') log.info("saving %s stream to %s", codec, filename)
def setup_local_sockets(bind, socket_dir, socket_dirs, display_name, clobber, mmap_group="auto", socket_permissions="600", username="", uid=0, gid=0): log = get_network_logger() log("setup_local_sockets%s", (bind, socket_dir, socket_dirs, display_name, clobber, mmap_group, socket_permissions, username, uid, gid)) if not bind: return {} if not socket_dir and (not socket_dirs or (len(socket_dirs) == 1 and not socket_dirs[0])): if WIN32: socket_dirs = [""] else: raise InitExit( EXIT_SOCKET_CREATION_ERROR, "at least one socket directory must be set to use unix domain sockets" ) from xpra.platform.dotxpra import DotXpra, norm_makepath dotxpra = DotXpra(socket_dir or socket_dirs[0], socket_dirs, username, uid, gid) if display_name is not None and not WIN32: display_name = normalize_local_display_name(display_name) defs = {} try: sockpaths = {} log("setup_local_sockets: bind=%s, dotxpra=%s", bind, dotxpra) for b in bind: if b in ("none", ""): continue parts = b.split(",") sockpath = parts[0] options = {} if len(parts) == 2: options = parse_simple_dict(parts[1]) if sockpath == "auto": assert display_name is not None for sockpath in dotxpra.norm_socket_paths(display_name): sockpaths[sockpath] = options log("sockpaths(%s)=%s (uid=%i, gid=%i)", display_name, sockpaths, uid, gid) else: sockpath = dotxpra.osexpand(sockpath) if os.path.isabs(sockpath): pass elif sockpath.endswith("/") or (os.path.exists(sockpath) and os.path.isdir(sockpath)): assert display_name is not None sockpath = os.path.abspath(sockpath) if not os.path.exists(sockpath): os.makedirs(sockpath) sockpath = norm_makepath(sockpath, display_name) else: sockpath = dotxpra.socket_path(sockpath) sockpaths[sockpath] = options assert sockpaths, "no socket paths to try for %s" % b #expand and remove duplicate paths: tmp = {} for tsp, options in sockpaths.items(): sockpath = dotxpra.osexpand(tsp) if sockpath in tmp: log.warn("Warning: skipping duplicate bind path %s", sockpath) continue tmp[sockpath] = options sockpaths = tmp log("sockpaths=%s", sockpaths) #create listeners: if WIN32: from xpra.platform.win32.namedpipes.listener import NamedPipeListener from xpra.platform.win32.dotxpra import PIPE_PATH for sockpath, options in sockpaths.items(): npl = NamedPipeListener(sockpath) ppath = sockpath if ppath.startswith(PIPE_PATH): ppath = ppath[len(PIPE_PATH):] log.info("created named pipe '%s'", ppath) defs[("named-pipe", npl, sockpath, npl.stop)] = options else: def checkstate(sockpath, state): if state not in (DotXpra.DEAD, DotXpra.UNKNOWN): if state == DotXpra.INACCESSIBLE: raise InitException( "An xpra server is already running at %s\n" % (sockpath, )) raise InitExit( EXIT_SERVER_ALREADY_EXISTS, "You already have an xpra server running at %s\n" " (did you want 'xpra upgrade'?)" % (sockpath, )) #remove exisiting sockets if clobber is set, #otherwise verify there isn't a server already running #and create the directories for the sockets: unknown = [] for sockpath in sockpaths: if clobber and os.path.exists(sockpath): os.unlink(sockpath) else: state = dotxpra.get_server_state(sockpath, 1) log("state(%s)=%s", sockpath, state) checkstate(sockpath, state) if state == dotxpra.UNKNOWN: unknown.append(sockpath) d = os.path.dirname(sockpath) try: kwargs = {} if d in ("/var/run/xpra", "/run/xpra"): #this is normally done by tmpfiles.d, #but we may need to do it ourselves in some cases: kwargs["mode"] = SOCKET_DIR_MODE xpra_gid = get_group_id(SOCKET_DIR_GROUP) if xpra_gid > 0: kwargs["gid"] = xpra_gid log("creating sockdir=%s, kwargs=%s" % (d, kwargs)) dotxpra.mksockdir(d, **kwargs) log("%s permission mask: %s", d, oct(os.stat(d).st_mode)) except Exception as e: log.warn("Warning: failed to create socket directory '%s'", d) log.warn(" %s", e) del e #wait for all the unknown ones: log("sockets in unknown state: %s", unknown) if unknown: #re-probe them using threads so we can do them in parallel: threads = [] def timeout_probe(sockpath): #we need a loop because "DEAD" sockets may return immediately #(ie: when the server is starting up) start = monotonic_time() while monotonic_time() - start < WAIT_PROBE_TIMEOUT: state = dotxpra.get_server_state( sockpath, WAIT_PROBE_TIMEOUT) log("timeout_probe() get_server_state(%s)=%s", sockpath, state) if state not in (DotXpra.UNKNOWN, DotXpra.DEAD): break sleep(1) log.warn( "Warning: some of the sockets are in an unknown state:") for sockpath in unknown: log.warn(" %s", sockpath) t = start_thread(timeout_probe, "probe-%s" % sockpath, daemon=True, args=(sockpath, )) threads.append(t) log.warn( " please wait as we allow the socket probing to timeout") #wait for all the threads to do their job: for t in threads: t.join(WAIT_PROBE_TIMEOUT + 1) if sockpaths: #now we can re-check quickly: #(they should all be DEAD or UNKNOWN): for sockpath in sockpaths: state = dotxpra.get_server_state(sockpath, 1) log("state(%s)=%s", sockpath, state) checkstate(sockpath, state) try: if os.path.exists(sockpath): os.unlink(sockpath) except OSError: pass #socket permissions: if mmap_group.lower() in TRUE_OPTIONS: #when using the mmap group option, use '660' sperms = 0o660 else: #parse octal mode given as config option: try: if isinstance(socket_permissions, int): sperms = socket_permissions else: #assume octal string: sperms = int(socket_permissions, 8) assert 0 <= sperms <= 0o777, "invalid socket permission value %s" % oct( sperms) except ValueError: raise ValueError("invalid socket permissions " + "(must be an octal number): '%s'" % socket_permissions) from None #now try to create all the sockets: for sockpath, options in sockpaths.items(): #create it: try: sock, cleanup_socket = create_unix_domain_socket( sockpath, sperms) log.info("created unix domain socket '%s'", sockpath) defs[("unix-domain", sock, sockpath, cleanup_socket)] = options except Exception as e: handle_socket_error(sockpath, sperms, e) del e except Exception: for sock, cleanup_socket in defs.items(): try: cleanup_socket() except Exception as e: log.error("Error cleaning up socket %s:", sock) log.error(" %s", e) del e raise return defs
def __init__(self, sink_type=None, sink_options={}, codecs=get_codecs(), codec_options={}, volume=1.0): if not sink_type: sink_type = DEFAULT_SINK if sink_type not in SINKS: raise InitExit(1, "invalid sink: %s" % sink_type) matching = [x for x in CODEC_ORDER if (x in codecs and x in get_codecs())] log("SoundSink(..) found matching codecs %s", matching) if not matching: raise InitExit(1, "no matching codecs between arguments '%s' and supported list '%s'" % (csv(codecs), csv(get_codecs().keys()))) codec = matching[0] decoder, parser = get_decoder_parser(codec) SoundPipeline.__init__(self, codec) self.sink_type = sink_type self.levels = deque(maxlen=100) decoder_str = plugin_str(decoder, codec_options) pipeline_els = [] appsrc_el = ["appsrc", "do-timestamp=1", "name=src", "emit-signals=0", "block=0", "is-live=0", "stream-type=stream", "format=%s" % GST_FORMAT_BUFFERS] pipeline_els.append(" ".join(appsrc_el)) pipeline_els.append(parser) pipeline_els.append(decoder_str) pipeline_els.append("audioconvert") pipeline_els.append("audioresample") pipeline_els.append("volume name=volume volume=%s" % volume) queue_el = ["queue", "name=queue", "min-threshold-time=0", "max-size-buffers=0", "max-size-bytes=0", "max-size-time=%s" % QUEUE_TIME, "leaky=%s" % QUEUE_LEAK] if QUEUE_SILENT: queue_el.append("silent=%s" % QUEUE_SILENT) pipeline_els.append(" ".join(queue_el)) sink_attributes = SINK_SHARED_DEFAULT_ATTRIBUTES.copy() from xpra.sound.gstreamer_util import gst_major_version, get_gst_version #anything older than this may cause problems (ie: centos 6.x) #because the attributes may not exist if get_gst_version()>=(0, 10, 36): sink_attributes.update(SINK_DEFAULT_ATTRIBUTES.get(gst_major_version, {}).get(sink_type, {})) sink_attributes.update(sink_options) sink_str = plugin_str(sink_type, sink_attributes) pipeline_els.append(sink_str) self.setup_pipeline_and_bus(pipeline_els) self.volume = self.pipeline.get_by_name("volume") self.src = self.pipeline.get_by_name("src") self.queue = self.pipeline.get_by_name("queue") self.overruns = 0 self.underruns = 0 self.overrun_events = deque(maxlen=100) self.underrun_events = deque(maxlen=100) self.queue_state = "starting" self.last_underrun = 0 self.last_overrun = 0 self.last_max_update = time.time() self.level_lock = Lock() if QUEUE_SILENT==0: self.queue.connect("overrun", self.queue_overrun) self.queue.connect("underrun", self.queue_underrun) self.queue.connect("running", self.queue_running) self.queue.connect("pushing", self.queue_pushing)