def get_pulse_device(device_name_match=None, want_monitor_device=True, input_or_output=None, remote=None, env_device_name=None): """ choose the device to use """ log("get_pulse_device%s", (device_name_match, want_monitor_device, input_or_output, remote, env_device_name)) try: from xpra.sound.pulseaudio.pulseaudio_util import ( has_pa, get_pa_device_options, get_default_sink, get_pactl_server, get_pulse_id, ) if not has_pa(): log.warn("Warning: pulseaudio is not available!") return None except ImportError as e: log.warn("Warning: pulseaudio is not available!") log.warn(" %s", e) return None pa_server = get_pactl_server() log("get_pactl_server()=%s", pa_server) if remote: log("start sound, remote pulseaudio server=%s, local pulseaudio server=%s", remote.pulseaudio_server, pa_server) #only worth comparing if we have a real server string #one that starts with {UUID}unix:/.. if pa_server and pa_server.startswith("{") and \ remote.pulseaudio_server and remote.pulseaudio_server==pa_server: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio server '%s'", pa_server) return None pa_id = get_pulse_id() log("start sound, client id=%s, server id=%s", remote.pulseaudio_id, pa_id) if remote.pulseaudio_id and remote.pulseaudio_id==pa_id: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio ID '%s'", pa_id) return None device_type_str = "" if input_or_output is not None: device_type_str = "input" if input_or_output else "output" if want_monitor_device: device_type_str += " monitor" #def get_pa_device_options(monitors=False, input_or_output=None, ignored_devices=["bell-window-system"]) devices = get_pa_device_options(want_monitor_device, input_or_output) log("found %i pulseaudio %s device%s: %s", len(devices), device_type_str, engs(devices), devices) ignore = () if input_or_output is True: ignore = IGNORED_INPUT_DEVICES elif input_or_output is False: ignore = IGNORED_OUTPUT_DEVICES else: ignore = IGNORED_INPUT_DEVICES+IGNORED_OUTPUT_DEVICES if ignore and devices: #filter out the ignore list: filtered = {} for k,v in devices.items(): kl = bytestostr(k).strip().lower() vl = bytestostr(v).strip().lower() if kl not in ignore and vl not in ignore: filtered[k] = v devices = filtered if not devices: log.error("Error: sound forwarding is disabled") log.error(" could not detect any Pulseaudio %s devices", device_type_str) return None env_device = None if env_device_name: env_device = os.environ.get(env_device_name) #try to match one of the devices using the device name filters: if len(devices)>1: filters = [] matches = [] for match in (device_name_match, env_device): if not match: continue if match!=env_device: filters.append(match) match = match.lower() log("trying to match '%s' in devices=%s", match, devices) matches = dict((k,v) for k,v in devices.items() if (bytestostr(k).strip().lower().find(match)>=0 or bytestostr(v).strip().lower().find(match)>=0)) #log("matches(%s, %s)=%s", devices, match, matches) if len(matches)==1: log("found name match for '%s': %s", match, tuple(matches.items())[0]) break elif len(matches)>1: log.warn("Warning: Pulseaudio %s device name filter '%s'", device_type_str, match) log.warn(" matched %i devices", len(matches)) if filters or matches: if not matches: log.warn("Warning: Pulseaudio %s device name filter%s:", device_type_str, engs(filters)) log.warn(" %s", csv("'%s'" % x for x in filters)) log.warn(" did not match any of the devices found:") for k,v in devices.items(): log.warn(" * '%s'", k) log.warn(" '%s'", v) return None devices = matches #still have too many devices to choose from? if len(devices)>1: if want_monitor_device: #use the monitor of the default sink if we find it: default_sink = get_default_sink() default_monitor = default_sink+".monitor" if default_monitor in devices: device_name = devices.get(default_monitor) log.info("using monitor of default sink: %s", device_name) return default_monitor global WARNED_MULTIPLE_DEVICES if not WARNED_MULTIPLE_DEVICES: WARNED_MULTIPLE_DEVICES = True dtype = "audio" if want_monitor_device: dtype = "output monitor" elif input_or_output is False: dtype = "audio output" elif input_or_output is True: dtype = "audio input" log.info("found %i %s devices:", len(devices), dtype) for k,v in devices.items(): log.info(" * %s", bytestostr(v)) log.info(" %s", bytestostr(k)) if not env_device: #used already! log.info(" to select a specific one,") log.info(" use the environment variable '%s'", env_device_name) #default to first one: if USE_DEFAULT_DEVICE: log.info("using default pulseaudio device") return None #default to first one: device, device_name = tuple(devices.items())[0] log.info("using pulseaudio device:") log.info(" '%s'", bytestostr(device_name)) return device
def __init__(self, src_type=None, src_options={}, codecs=get_codecs(), 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_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) self.queue = None self.caps = None self.volume = None self.sink = None self.src = None self.src_type = src_type self.buffer_latency = False self.jitter_queue = None self.file = None SoundPipeline.__init__(self, codec) 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! encoder_str = plugin_str(encoder, codec_options or get_encoder_default_options(encoder)) fmt_str = plugin_str(fmt, MUXER_DEFAULT_OPTIONS.get(fmt, {})) 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) pipeline_els += [encoder_str, fmt_str, 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: gstlog("initial %s: %s", x, self.src.get_property(x)) self.buffer_latency = True 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 get_pulse_device(device_name_match=None, want_monitor_device=True, input_or_output=None, remote=None, xpra_device_name=None): """ choose the device to use """ try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa, get_pa_device_options, get_default_sink, get_pulse_server, get_pulse_id if not has_pa(): log.warn("Warning: pulseaudio is not available!") return None except ImportError as e: log.warn("Warning: pulseaudio is not available!") log.warn(" %s", e) return None pa_server = get_pulse_server() if remote: log("start sound, remote pulseaudio server=%s, local pulseaudio server=%s", remote.pulseaudio_server, pa_server) #only worth comparing if we have a real server string #one that starts with {UUID}unix:/.. if pa_server and pa_server.startswith("{") and \ remote.pulseaudio_server and remote.pulseaudio_server==pa_server: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio server '%s'", pa_server) return None pa_id = get_pulse_id() log("start sound, client id=%s, server id=%s", remote.pulseaudio_id, pa_id) if remote.pulseaudio_id and remote.pulseaudio_id==pa_id: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio ID '%s'", pa_id) return None device_type_str = "" if input_or_output is not None: device_type_str = ["output", "input"][input_or_output] if want_monitor_device: device_type_str += " monitor" #def get_pa_device_options(monitors=False, input_or_output=None, ignored_devices=["bell-window-system"], log_errors=True) devices = get_pa_device_options(want_monitor_device, input_or_output) log("found %i pulseaudio %s device%s: %s", len(devices), device_type_str, engs(devices), devices) if len(devices)==0: log.error("Error: sound forwarding is disabled") log.error(" could not detect any Pulseaudio %s devices", device_type_str) return None #try to match one of the devices using the device name filters: if len(devices)>1: filters = [] matches = [] for match in (device_name_match, PULSEAUDIO_DEVICE_NAME, xpra_device_name): if not match: continue if match!=xpra_device_name: filters.append(match) match = match.lower() matches = dict((k,v) for k,v in devices.items() if k.lower().find(match)>=0 or v.lower().find(match)>=0) #log("matches(%s, %s)=%s", devices, match, matches) if len(matches)==1: log("found name match for '%s': %s", match, matches.items()[0]) break elif len(matches)>1: log.warn("Warning: Pulseaudio %s device name filter '%s'", device_type_str, match) log.warn(" matched %i devices", len(matches)) if filters or len(matches)>0: if len(matches)==0: log.warn("Warning: Pulseaudio %s device name filter%s:", device_type_str, engs(filters)) log.warn(" %s", csv("'%s'" % x for x in filters)) log.warn(" did not match any of the devices found:") for k,v in devices.items(): log.warn(" * '%s'", k) log.warn(" '%s'", v) return None devices = matches #still have too many devices to choose from? if len(devices)>1: if want_monitor_device: #use the monitor of the default sink if we find it: default_sink = get_default_sink() default_monitor = default_sink+".monitor" if default_monitor in devices: device_name = devices.get(default_monitor) log.info("using monitor of default sink: %s", device_name) return default_monitor global WARNED_MULTIPLE_DEVICES if not WARNED_MULTIPLE_DEVICES: WARNED_MULTIPLE_DEVICES = True if not PULSEAUDIO_DEVICE_NAME: #warned already dtype = "audio" if want_monitor_device: dtype = "output monitor" elif input_or_output is False: dtype = "audio input" elif input_or_output is True: dtype = "audio output" log.warn("Warning: found %i %s devices:", len(devices), dtype) for k,v in devices.items(): log.warn(" * %s", v) log.warn(" %s", k) if not PULSEAUDIO_DEVICE_NAME: #used already! log.warn(" to select a specific one,") log.warn(" use the environment variable XPRA_PULSEAUDIO_DEVICE_NAME") #default to first one: if USE_DEFAULT_DEVICE: log.info("using default pulseaudio device") return None #default to first one: device, device_name = devices.items()[0] log.info("using pulseaudio device:") log.info(" '%s'", device_name) return device
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 get_pulse_defaults(device_name_match=None, want_monitor_device=True, input_or_output=None, remote=None, xpra_device_name=None): """ choose the device to use """ try: from xpra.sound.pulseaudio.pulseaudio_util import has_pa, get_pa_device_options, get_default_sink from xpra.sound.pulseaudio.pulseaudio_util import get_pulse_server, get_pulse_id, set_source_mute, set_sink_mute if not has_pa(): log.warn("Warning: pulseaudio is not available!") return None except ImportError as e: log.warn("Warning: pulseaudio is not available!") log.warn(" %s", e) return None pa_server = get_pulse_server() if remote: log( "start sound, remote pulseaudio server=%s, local pulseaudio server=%s", remote.pulseaudio_server, pa_server) #only worth comparing if we have a real server string #one that starts with {UUID}unix:/.. if pa_server and pa_server.startswith("{") and \ remote.pulseaudio_server and remote.pulseaudio_server==pa_server: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio server '%s'", pa_server) return None pa_id = get_pulse_id() log("start sound, client id=%s, server id=%s", remote.pulseaudio_id, pa_id) if remote.pulseaudio_id and remote.pulseaudio_id == pa_id: log.error("Error: sound is disabled to prevent a sound loop") log.error(" identical Pulseaudio ID '%s'", pa_id) return None device_type_str = "" if input_or_output is not None: device_type_str = ["output", "input"][input_or_output] if want_monitor_device: device_type_str += " monitor" #def get_pa_device_options(monitors=False, input_or_output=None, ignored_devices=["bell-window-system"], log_errors=True) devices = get_pa_device_options(want_monitor_device, input_or_output) log("found %i pulseaudio %s device%s: %s", len(devices), device_type_str, engs(devices), devices) if len(devices) == 0: log.error("Error: sound forwarding is disabled") log.error(" could not detect any Pulseaudio %s devices", device_type_str) return None if len(devices) > 1: filters = [] matches = [] for match in (device_name_match, PULSEAUDIO_DEVICE_NAME, xpra_device_name): if not match: continue if match != xpra_device_name: filters.append(match) match = match.lower() matches = dict( (k, v) for k, v in devices.items() if k.lower().find(match) >= 0 or v.lower().find(match) >= 0) #log("matches(%s, %s)=%s", devices, match, matches) if len(matches) == 1: log("found name match for '%s': %s", match, matches.items()[0]) break elif len(matches) > 1: log.warn("Warning: Pulseaudio %s device name filter '%s'", device_type_str, match) log.warn(" matched %i devices", len(matches)) if filters or len(matches) > 0: if len(matches) == 0: log.warn("Warning: Pulseaudio %s device name filter%s:", device_type_str, engs(filters)) log.warn(" %s", csv("'%s'" % x for x in filters)) log.warn(" did not match the devices found:") for k, v in devices.items(): log.warn(" * '%s'", k) log.warn(" '%s'", v) return None devices = matches #default to first one: device, device_name = devices.items()[0] if len(devices) > 1: default_sink = get_default_sink() default_monitor = default_sink + ".monitor" global WARNED_MULTIPLE_DEVICES if not WARNED_MULTIPLE_DEVICES: WARNED_MULTIPLE_DEVICES = True if not PULSEAUDIO_DEVICE_NAME: #warned already log.warn("Warning: found %i audio devices:", len(devices)) for k, v in devices.items(): log.warn(" * %s", v) log.warn(" %s", k) if not PULSEAUDIO_DEVICE_NAME: #used already! log.warn(" to select a specific one,") log.warn( " use the environment variable XPRA_PULSEAUDIO_DEVICE_NAME" ) if default_monitor in devices: device = default_monitor device_name = devices.get(default_monitor) if not WARNED_MULTIPLE_DEVICES: log.warn("using monitor of default sink: %s", device_name) else: if not WARNED_MULTIPLE_DEVICES: log.warn("using the first device") log.info("using pulseaudio device:") log.info(" '%s'", device_name) #make sure it is not muted: if input_or_output is True or want_monitor_device: set_source_mute(device, mute=False) elif input_or_output is False: set_sink_mute(device, mute=False) return {"device": device}
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)