示例#1
0
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
示例#2
0
文件: src.py 项目: svn2github/Xpra
 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)
示例#3
0
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
示例#4
0
    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)
示例#5
0
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}
示例#6
0
文件: src.py 项目: frostbane/xpra
    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)