Skip to content

Commit

Permalink
#849: always call the sound via a subprocess, so we can use gstreamer…
Browse files Browse the repository at this point in the history
… 1.x with gtk2:

* add "_query_sound" subcommand
* blacklist "flac" with gstreamer 1.x to avoid errors (recent breakage with Fedora 22?)
* refactor the subprocess_wrapper code so we can re-use kwargs and env without subclassing

git-svn-id: https://xpra.org/svn/Xpra/trunk@9636 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jun 15, 2015
1 parent 909b5d5 commit 6038312
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 99 deletions.
17 changes: 9 additions & 8 deletions src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,16 @@ def __init__(self):
self.microphone_enabled = False
self.microphone_codecs = []
try:
from xpra.sound.gstreamer_util import has_gst, get_sound_codecs
from xpra.sound.gstreamer_util import has_gst
self.speaker_allowed = has_gst
if self.speaker_allowed:
self.speaker_codecs = get_sound_codecs(True, False)
self.speaker_allowed = len(self.speaker_codecs)>0
self.speaker_codecs = []
self.microphone_allowed = has_gst
self.microphone_enabled = False
self.microphone_codecs = []
if self.microphone_allowed:
self.microphone_enabled = False
if has_gst:
from xpra.sound.wrapper import get_sound_codecs
self.speaker_codecs = get_sound_codecs(True, False)
self.speaker_allowed = len(self.speaker_codecs)>0
self.microphone_codecs = get_sound_codecs(False, False)
self.microphone_allowed = len(self.microphone_codecs)>0
if has_gst:
Expand Down Expand Up @@ -266,7 +267,8 @@ def init(self, opts):
self.mmap_group = opts.mmap_group

try:
from xpra.sound.gstreamer_util import has_gst, get_sound_codecs
from xpra.sound.gstreamer_util import has_gst
from xpra.sound.wrapper import get_sound_codecs
except:
has_gst = False
self.sound_source_plugin = opts.sound_source
Expand All @@ -277,7 +279,6 @@ def init(self, opts):
self.speaker_codecs = opts.speaker_codec
if len(self.speaker_codecs)==0 and self.speaker_allowed:
assert has_gst
self.speaker_codecs = get_sound_codecs(True, False)
self.speaker_allowed = len(self.speaker_codecs)>0
self.microphone_codecs = opts.microphone_codec
if len(self.microphone_codecs)==0 and self.microphone_allowed:
Expand Down
48 changes: 27 additions & 21 deletions src/xpra/net/subprocess_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,31 @@ def process_packet(self, proto, packet):
glib.idle_add(method, *packet[1:])


def exec_kwargs():
if os.name=="posix":
return {"close_fds" : True}
elif sys.platform.startswith("win"):
if not WIN32_SHOWWINDOW:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
return {"startupinfo" : startupinfo}
return {}

def exec_env(blacklist=["LS_COLORS", ]):
env = os.environ.copy()
env["XPRA_SKIP_UI"] = "1"
#let's make things more complicated than they should be:
#on win32, the environment can end up containing unicode, and subprocess chokes on it
for k,v in env.items():
if k in blacklist:
continue
try:
env[k] = bytestostr(v.encode("utf8"))
except:
env[k] = bytestostr(v)
return env


class subprocess_caller(object):
"""
This is the caller side, wrapping the subprocess.
Expand Down Expand Up @@ -280,36 +305,17 @@ def make_protocol(self):


def exec_subprocess(self):
kwargs = self.exec_kwargs()
kwargs = exec_kwargs()
log("exec_subprocess() command=%s, kwargs=%s", self.command, kwargs)
proc = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr.fileno(), env=self.get_env(), **kwargs)
getChildReaper().add_process(proc, self.description, self.command, True, True, callback=self.subprocess_exit)
return proc

def get_env(self):
env = os.environ.copy()
env["XPRA_SKIP_UI"] = "1"
env = exec_env()
env["XPRA_LOG_PREFIX"] = "%s " % self.description
#let's make things more complicated than they should be:
#on win32, the environment can end up containing unicode, and subprocess chokes on it
for k,v in env.items():
try:
env[k] = bytestostr(v.encode("utf8"))
except:
env[k] = bytestostr(v)
return env

def exec_kwargs(self):
if os.name=="posix":
return {"close_fds" : True}
elif sys.platform.startswith("win"):
if not WIN32_SHOWWINDOW:
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
return {"startupinfo" : startupinfo}
return {}


def cleanup(self):
self.stop()

Expand Down
41 changes: 35 additions & 6 deletions src/xpra/scripts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,11 +736,10 @@ def legacy_bool_parse(optionname, newoptionname=None):
if cat=="help":
raise InitInfo("known logging filters (there may be others): %s" % ", ".join(KNOWN_FILTERS))
if options.sound_source=="help":
from xpra.sound.gstreamer_util import NAME_TO_INFO_PLUGIN
try:
#NOTE: this will import GTK, so we have to exit
#(we cannot start the server after this for example)
from xpra.sound.gstreamer_util import get_available_source_plugins, SRC_TO_NAME_PLUGIN, NAME_TO_INFO_PLUGIN
source_plugins = [SRC_TO_NAME_PLUGIN[p] for p in get_available_source_plugins()]
from xpra.sound.wrapper import query_sound_sources
source_plugins = query_sound_sources()
except Exception as e:
raise InitInfo(e)
source_plugins = []
Expand Down Expand Up @@ -814,11 +813,41 @@ def dump_frames(*arsg):
traceback.print_stack(frame)
print("")

def show_sound_codec_help(is_server, speaker_codecs, microphone_codecs):
from xpra.sound.gstreamer_util import has_gst
from xpra.sound.wrapper import get_sound_codecs
if not has_gst:
return "sound is not supported - gstreamer not present or not accessible"
info = []
all_speaker_codecs = get_sound_codecs(True, is_server)
invalid_sc = [x for x in speaker_codecs if x not in all_speaker_codecs]
hs = "help" in speaker_codecs
if hs:
info.append("speaker codecs available: %s" % (", ".join(all_speaker_codecs)))
elif len(invalid_sc):
info.append("WARNING: some of the specified speaker codecs are not available: %s" % (", ".join(invalid_sc)))
for x in invalid_sc:
speaker_codecs.remove(x)
elif len(speaker_codecs)==0:
speaker_codecs += all_speaker_codecs

all_microphone_codecs = get_sound_codecs(True, is_server)
invalid_mc = [x for x in microphone_codecs if x not in all_microphone_codecs]
hm = "help" in microphone_codecs
if hm:
info.append("microphone codecs available: %s" % (", ".join(all_microphone_codecs)))
elif len(invalid_mc):
info.append("WARNING: some of the specified microphone codecs are not available: %s" % (", ".join(invalid_mc)))
for x in invalid_mc:
microphone_codecs.remove(x)
elif len(microphone_codecs)==0:
microphone_codecs += all_microphone_codecs
return info


def configure_logging(options, mode):
if mode in ("start", "upgrade", "attach", "shadow", "proxy", "_sound_record", "_sound_play"):
if "help" in options.speaker_codec or "help" in options.microphone_codec:
from xpra.sound.gstreamer_util import show_sound_codec_help
info = show_sound_codec_help(mode!="attach", options.speaker_codec, options.microphone_codec)
raise InitInfo("\n".join(info))
else:
Expand Down Expand Up @@ -876,7 +905,7 @@ def run_mode(script_file, error_cb, options, args, mode, defaults):
elif mode in ("_proxy", "_proxy_start", "_shadow_start") and (supports_server or supports_shadow):
nox()
return run_proxy(error_cb, options, script_file, args, mode, defaults)
elif mode in ("_sound_record", "_sound_play"):
elif mode in ("_sound_record", "_sound_play", "_sound_query"):
if not has_sound_support:
error_cb("no sound support!")
from xpra.sound.wrapper import run_sound
Expand Down
64 changes: 13 additions & 51 deletions src/xpra/sound/gstreamer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,14 @@ def import_gst0_10():
#try gst1 first:
imports = import_options.reverse()
gst, vinfo = None, None
for import_function, v in import_options:
for import_function, MV in import_options:
try:
gst = import_function()
v = gst.version()
if v[-1]==0:
v = v[:-1]
vinfo = ".".join((str(x) for x in v))
_gst_major_version = MV
break
except Exception as e:
log("failed to import GStreamer %s: %s", vinfo, e)
Expand Down Expand Up @@ -240,12 +241,17 @@ def has_plugins(*names):
if encoding in CODECS:
#we already have one for this encoding
continue
elif sys.platform.startswith("win") and _gst_major_version==0 and encoding==FLAC:
#the gstreamer 0.10 builds on win32 use the outdated oss build,
#which includes outdated flac libraries with known CVEs,
#so avoid using those:
log("avoiding outdated flac module (likely buggy on win32 with gstreamer 0.10)")
continue
elif encoding==FLAC:
#flac problems:
if sys.platform.startswith("win") and _gst_major_version==0 and encoding==FLAC:
#the gstreamer 0.10 builds on win32 use the outdated oss build,
#which includes outdated flac libraries with known CVEs,
#so avoid using those:
log("avoiding outdated flac module (likely buggy on win32 with gstreamer 0.10)")
continue
elif _gst_major_version==1:
log("skipping flac with Gstreamer 1.x to avoid obscure 'not-neogtiated' errors I do not have time for")
continue
#verify we have all the elements needed:
if has_plugins(*elements[1:]):
#ie: FLAC, "flacenc", "oggmux", "flacdec", "oggdemux" = elements
Expand All @@ -255,49 +261,6 @@ def has_plugins(*names):
for k in [x for x in CODEC_ORDER if x in CODECS]:
log("* %s : %s", k, CODECS[k])

def get_sound_codecs(is_speaker, is_server):
global has_gst
if not has_gst:
return []
try:
if (is_server and is_speaker) or (not is_server and not is_speaker):
return can_encode()
else:
return can_decode()
except Exception as e:
log.warn("failed to get list of codecs: %s" % e)
return []

def show_sound_codec_help(is_server, speaker_codecs, microphone_codecs):
if not has_gst:
return "sound is not supported - gstreamer not present or not accessible"
info = []
all_speaker_codecs = get_sound_codecs(True, is_server)
invalid_sc = [x for x in speaker_codecs if x not in all_speaker_codecs]
hs = "help" in speaker_codecs
if hs:
info.append("speaker codecs available: %s" % (", ".join(all_speaker_codecs)))
elif len(invalid_sc):
info.append("WARNING: some of the specified speaker codecs are not available: %s" % (", ".join(invalid_sc)))
for x in invalid_sc:
speaker_codecs.remove(x)
elif len(speaker_codecs)==0:
speaker_codecs += all_speaker_codecs

all_microphone_codecs = get_sound_codecs(True, is_server)
invalid_mc = [x for x in microphone_codecs if x not in all_microphone_codecs]
hm = "help" in microphone_codecs
if hm:
info.append("microphone codecs available: %s" % (", ".join(all_microphone_codecs)))
elif len(invalid_mc):
info.append("WARNING: some of the specified microphone codecs are not available: %s" % (", ".join(invalid_mc)))
for x in invalid_mc:
microphone_codecs.remove(x)
elif len(microphone_codecs)==0:
microphone_codecs += all_microphone_codecs
return info


def get_encoder_formatter(name):
assert name in CODECS, "invalid codec: %s (should be one of: %s)" % (name, CODECS.keys())
encoder, formatter, _, _ = CODECS.get(name)
Expand Down Expand Up @@ -333,7 +296,6 @@ def can_encode():
def can_decode():
return [x for x in CODEC_ORDER if has_decoder(x)]


def plugin_str(plugin, options):
if plugin is None:
return None
Expand Down
Loading

0 comments on commit 6038312

Please sign in to comment.