def init_state(self): self.wants_encodings = False self.wants_features = False #contains default values, some of which may be supplied by the client: self.default_batch_config = batch_config.DamageBatchConfig() self.global_batch_config = self.default_batch_config.clone() #global batch config self.supports_transparency = False self.encoding = None #the default encoding for all windows self.encodings = () #all the encodings supported by the client self.core_encodings = () self.encodings_packet = False #supports delayed encodings initialization? self.window_icon_encodings = ["premult_argb32"] self.rgb_formats = ("RGB",) self.encoding_options = typedict() self.icons_encoding_options = typedict() self.default_encoding_options = typedict() self.auto_refresh_delay = 0 self.zlib = True self.lz4 = use("lz4") #for managing the recalculate_delays work: self.calculate_window_pixels = {} self.calculate_window_ids = set() self.calculate_timer = 0 self.calculate_last_time = 0 #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original #which may be used by other clients (other ServerSource instances) self.video_helper = getVideoHelper().clone() self.cuda_device_context = None
def __init__(self, core_encodings, encodings, default_encoding, scaling_control, default_quality, default_min_quality, default_speed, default_min_speed): log("ServerSource%s", (core_encodings, encodings, default_encoding, scaling_control, default_quality, default_min_quality, default_speed, default_min_speed)) self.server_core_encodings = core_encodings self.server_encodings = encodings self.default_encoding = default_encoding self.scaling_control = scaling_control self.default_quality = default_quality #default encoding quality for lossy encodings self.default_min_quality = default_min_quality #default minimum encoding quality self.default_speed = default_speed #encoding speed (only used by x264) self.default_min_speed = default_min_speed #default minimum encoding speed self.default_batch_config = DamageBatchConfig( ) #contains default values, some of which may be supplied by the client self.global_batch_config = self.default_batch_config.clone( ) #global batch config self.vrefresh = -1 self.supports_transparency = False self.encoding = None #the default encoding for all windows self.encodings = () #all the encodings supported by the client self.core_encodings = () self.window_icon_encodings = ["premult_argb32"] self.rgb_formats = ("RGB", ) self.encoding_options = typedict() self.icons_encoding_options = typedict() self.default_encoding_options = typedict() self.auto_refresh_delay = 0 self.zlib = True self.lz4 = use_lz4 self.lzo = use_lzo #for managing the recalculate_delays work: self.calculate_window_pixels = {} self.calculate_window_ids = set() self.calculate_timer = 0 self.calculate_last_time = 0 #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original #which may be used by other clients (other ServerSource instances) self.video_helper = getVideoHelper().clone() # the queues of damage requests we work through: self.encode_work_queue = Queue( ) #holds functions to call to compress data (pixels, clipboard) #items placed in this queue are picked off by the "encode" thread, #the functions should add the packets they generate to the 'packet_queue' self.packet_queue = deque( ) #holds actual packets ready for sending (already encoded) #these packets are picked off by the "protocol" via 'next_packet()' #format: packet, wid, pixels, start_send_cb, end_send_cb #(only packet is required - the rest can be 0/None for clipboard packets) self.encode_thread = start_thread(self.encode_loop, "encode")
def get_full_csc_modes(self): #initialize just once per class if GLClientWindow.full_csc_modes is None: GLClientWindow.full_csc_modes = getVideoHelper( ).get_server_full_csc_modes("YUV420P", "YUV422P", "YUV444P", "GBRP") return GLClientWindow.full_csc_modes
def do_get_core_encodings(self): """ This method returns the actual encodings supported. ie: ["rgb24", "vp8", "webp", "png", "png/L", "png/P", "jpeg", "jpeg2000", "h264", "vpx"] It is often overriden in the actual client class implementations, where extra encodings can be added (generally just 'rgb32' for transparency), or removed if the toolkit implementation class is more limited. """ #we always support rgb: core_encodings = ["rgb24", "rgb32"] for codec in ("dec_pillow", "dec_webp"): if has_codec(codec): c = get_codec(codec) for e in c.get_encodings(): if e not in core_encodings: core_encodings.append(e) #we enable all the video decoders we know about, #what will actually get used by the server will still depend on the csc modes supported video_decodings = getVideoHelper().get_decodings() log("video_decodings=%s", video_decodings) for encoding in video_decodings: if encoding not in core_encodings: core_encodings.append(encoding) #remove duplicates and use prefered encoding order: core_encodings = [ x for x in PREFERED_ENCODING_ORDER if x in set(core_encodings) and x in self.allowed_encodings ] return core_encodings
def __init__(self, *args): WindowSource.__init__(self, *args) #client uses uses_swscale (has extra limits on sizes) self.uses_swscale = self.encoding_options.get("uses_swscale", True) self.uses_csc_atoms = self.encoding_options.get("csc_atoms", False) self.video_scaling = self.encoding_options.get("video_scaling", False) self.video_reinit = self.encoding_options.get("video_reinit", False) if not self.encoding_client_options: #old clients can only use 420P: def_csc_modes = ("YUV420P") else: #default for newer clients that don't specify "csc_modes": def_csc_modes = ("YUV420P", "YUV422P", "YUV444P") #0.10 onwards should have specified csc_modes: self.csc_modes = self.encoding_options.get("csc_modes", def_csc_modes) self.video_encodings = ("vp8", "vp9", "h264") for x in self.video_encodings: if x in self.server_core_encodings: self._encoders[x] = self.video_encode #these constraints get updated with real values #when we construct the video pipeline: self.min_w = 1 self.min_h = 1 self.max_w = 16384 self.max_h = 16384 self.width_mask = 0xFFFF self.height_mask = 0xFFFF self.actual_scaling = (1, 1) self._csc_encoder = None self._video_encoder = None self._lock = Lock() #to ensure we serialize access to the encoder and its internals self.last_pipeline_params = None self.last_pipeline_scores = [] self.video_helper = getVideoHelper() if self.encoding_options.get("proxy.video", False): #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original: self.video_helper = getVideoHelper().clone(deep=True) #enabling video proxy: try: self.parse_proxy_video() except: log.error("failed to parse proxy video", exc_info=True)
def load_csc_options(): global CSC_OPTIONS if CSC_OPTIONS is None: CSC_OPTIONS = {} vh = getVideoHelper() for csc_in in vh.get_csc_inputs(): CSC_OPTIONS[csc_in] = vh.get_csc_specs(csc_in) return CSC_OPTIONS
def get_info(self, _proto): info = { "encodings": self.get_encoding_info(), "video": getVideoHelper().get_info(), } for k, v in codec_versions.items(): info.setdefault("encoding", {}).setdefault(k, {})["version"] = v return info
def _get_full_csc_modes(self, rgb_modes): #calculate the server CSC modes the server is allowed to use #based on the client CSC modes we can convert to in the backing class we use #and trim the transparency if we cannot handle it target_rgb_modes = list(rgb_modes) if not self._alpha_enabled: target_rgb_modes = [x for x in target_rgb_modes if x.find("A")<0] full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes) log("full csc modes (%s)=%s", target_rgb_modes, full_csc_modes) return full_csc_modes
def _get_full_csc_modes(self, rgb_modes): #calculate the server CSC modes the server is allowed to use #based on the client CSC modes we can convert to in the backing class we use #and trim the transparency if we cannot handle it target_rgb_modes = list(rgb_modes) if not self._alpha_enabled: target_rgb_modes = [x for x in target_rgb_modes if x.find("A")<0] full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes) log("_get_full_csc_modes(%s)=%s (target_rgb_modes=%s)", rgb_modes, full_csc_modes, target_rgb_modes) return full_csc_modes
def test_load(): #NOTE: we assume that the scores: # - make nvenc less desirable at HQ (maybe not?) # - make cuda csc beat swscale vep = getVideoHelper() try: vep.cleanup() except Exception: pass vep.init()
def load_codecs(self): log("load_codecs()") from xpra.codecs.video_helper import getVideoHelper, NO_GFX_CSC_OPTIONS #pylint: disable=import-outside-toplevel vh = getVideoHelper() vh.set_modules(video_decoders=self.session_options.video_decoders, csc_modules=self.session_options.csc_modules or NO_GFX_CSC_OPTIONS) vh.init() from xpra.codecs.loader import load_codecs, show_codecs #pylint: disable=import-outside-toplevel load_codecs() show_codecs() log("load_codecs() done")
def video_helper_init(self): #all threads will use just use the same settings anyway, #so don't re-initialize the video helper: self.video_helper = getVideoHelper() #only use video encoders (no CSC supported in proxy) try: self.video_helper.set_modules(video_encoders=self.video_encoder_modules) except AssertionError as e: log("video_helper_init() ignored: %s", e) else: self.video_helper.init()
def get_full_csc_modes(self): #initialize just once per class if ClientWindow.full_csc_modes is None: #plain GTK2 window needs to use CSC modules to paint video #so calculate the server CSC modes the server is allowed to use #based on the client CSC modes we can convert to RGB(A): target_rgb_modes = ["RGB", "RGBX"] if HAS_ALPHA: target_rgb_modes.append("RGBA") ClientWindow.full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes) log("full csc modes (%s)=%s", target_rgb_modes, ClientWindow.full_csc_modes) return ClientWindow.full_csc_modes
def video_init(self): enclog("video_init() loading codecs") load_codecs(decoders=False) enclog("video_init() will try video encoders: %s", csv(self.video_encoder_modules) or "none") self.video_helper = getVideoHelper() #only use video encoders (no CSC supported in proxy) self.video_helper.set_modules( video_encoders=self.video_encoder_modules) self.video_helper.init() self.video_encoding_defs = {} self.video_encoders = {} self.video_encoders_dst_formats = [] self.video_encoders_last_used_time = {} self.video_encoder_types = [] #figure out which encoders we want to proxy for (if any): encoder_types = set() for encoding in self.video_helper.get_encodings(): colorspace_specs = self.video_helper.get_encoder_specs(encoding) for colorspace, especs in colorspace_specs.items(): if colorspace not in ("BGRX", "BGRA", "RGBX", "RGBA"): #only deal with encoders that can handle plain RGB directly continue for spec in especs: #ie: video_spec("x264") spec_props = spec.to_dict() del spec_props["codec_class"] #not serializable! spec_props[ "score_boost"] = 50 #we want to win scoring so we get used ahead of other encoders spec_props[ "max_instances"] = 3 #limit to 3 video streams we proxy for (we really want 2, # but because of races with garbage collection, we need to allow more) #store it in encoding defs: self.video_encoding_defs.setdefault( encoding, {}).setdefault(colorspace, []).append(spec_props) encoder_types.add(spec.codec_type) enclog("encoder types found: %s", tuple(encoder_types)) #remove duplicates and use preferred order: order = PREFERRED_ENCODER_ORDER[:] for x in tuple(encoder_types): if x not in order: order.append(x) self.video_encoder_types = [x for x in order if x in encoder_types] enclog.info("proxy video encoders: %s", ", ".join(self.video_encoder_types or [ "none", ]))
def get_full_csc_modes(self): #initialize just once per class if ClientWindow.full_csc_modes is None: #plain GTK2 window needs to use CSC modules to paint video #so calculate the server CSC modes the server is allowed to use #based on the client CSC modes we can convert to RGB(A): target_rgb_modes = ["RGB", "RGBX"] if HAS_ALPHA: target_rgb_modes.append("RGBA") ClientWindow.full_csc_modes = getVideoHelper( ).get_server_full_csc_modes_for_rgb(*target_rgb_modes) log("full csc modes (%s)=%s", target_rgb_modes, ClientWindow.full_csc_modes) return ClientWindow.full_csc_modes
def _get_full_csc_modes(self, rgb_modes): #calculate the server CSC modes the server is allowed to use #based on the client CSC modes we can convert to in the backing class we use #and trim the transparency if we cannot handle it target_rgb_modes = tuple(rgb_modes) if not self._alpha_enabled: target_rgb_modes = tuple(x for x in target_rgb_modes if x.find("A")<0) full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*target_rgb_modes) full_csc_modes["webp"] = [x for x in rgb_modes if x in ("BGRX", "BGRA", "RGBX", "RGBA")] videolog("_get_full_csc_modes(%s) with target_rgb_modes=%s", rgb_modes, target_rgb_modes) for e in sorted(full_csc_modes.keys()): modes = full_csc_modes.get(e) videolog(" * %s : %s", e, modes) return full_csc_modes
def load_video_decoders(): global VIDEO_DECODERS if VIDEO_DECODERS is None: VIDEO_DECODERS = {} vh = getVideoHelper() for encoding in vh.get_decodings(): specs = vh.get_decoder_specs(encoding) for colorspace, decoders in specs.items(): log("%s decoders for %s: %s", encoding, colorspace, decoders) assert len(decoders)>0 #use the first one: _, decoder_module = decoders[0] VIDEO_DECODERS[encoding] = decoder_module log("video decoders: %s", VIDEO_DECODERS) return VIDEO_DECODERS
def load_video_decoders(): global VIDEO_DECODERS if VIDEO_DECODERS is None: VIDEO_DECODERS = {} vh = getVideoHelper() for encoding in vh.get_decodings(): specs = vh.get_decoder_specs(encoding) for colorspace, decoders in specs.items(): log("%-5s decoders for %7s: %s", encoding, colorspace, csv([d.get_type() for _, d in decoders])) assert len(decoders) > 0 # use the first one: _, decoder_module = decoders[0] VIDEO_DECODERS[encoding] = decoder_module log("video decoders: %s", dict((e, d.get_type()) for e, d in VIDEO_DECODERS.items())) return VIDEO_DECODERS
def load_video_decoders(): global VIDEO_DECODERS if VIDEO_DECODERS is None: VIDEO_DECODERS = {} vh = getVideoHelper() for encoding in vh.get_decodings(): specs = vh.get_decoder_specs(encoding) for colorspace, decoders in specs.items(): log("%-5s decoders for %7s: %s", encoding, colorspace, csv([d.get_type() for _,d in decoders])) assert decoders #use the first one: _, decoder_module = decoders[0] VIDEO_DECODERS[encoding] = decoder_module log("video decoders: %s", dict((e,d.get_type()) for e,d in VIDEO_DECODERS.items())) return VIDEO_DECODERS
def load_video_decoders(): global VIDEO_DECODERS if VIDEO_DECODERS is None: VIDEO_DECODERS = {} vh = getVideoHelper() for encoding in vh.get_decodings(): specs = vh.get_decoder_specs(encoding) for colorspace, decoders in specs.items(): log("%s decoders for %s: %s", encoding, colorspace, decoders) assert len(decoders) > 0 #use the first one: _, decoder_module = decoders[0] VIDEO_DECODERS[encoding] = decoder_module log("video decoders: %s", VIDEO_DECODERS) return VIDEO_DECODERS
def init(self, opts): self.allowed_encodings = opts.encodings self.encoding = opts.encoding self.video_scaling = parse_bool_or_int("video-scaling", opts.video_scaling) self.quality = opts.quality self.min_quality = opts.min_quality self.speed = opts.speed self.min_speed = opts.min_speed #until we add the ability to choose decoders, use all of them: #(and default to non grahics card csc modules if not specified) load_codecs(encoders=False) vh = getVideoHelper() vh.set_modules(video_decoders=opts.video_decoders, csc_modules=opts.csc_modules or NO_GFX_CSC_OPTIONS) vh.init()
def __init__(self): self.server_core_encodings = [] self.server_encodings = [] self.default_encoding = None self.scaling_control = None self.default_quality = 40 #default encoding quality for lossy encodings self.default_min_quality = 10 #default minimum encoding quality self.default_speed = 40 #encoding speed (only used by x264) self.default_min_speed = 10 #default minimum encoding speed self.default_batch_config = DamageBatchConfig( ) #contains default values, some of which may be supplied by the client self.global_batch_config = self.default_batch_config.clone( ) #global batch config self.vrefresh = -1 self.supports_transparency = False self.encoding = None #the default encoding for all windows self.encodings = () #all the encodings supported by the client self.core_encodings = () self.window_icon_encodings = ["premult_argb32"] self.rgb_formats = ("RGB", ) self.encoding_options = typedict() self.icons_encoding_options = typedict() self.default_encoding_options = typedict() self.auto_refresh_delay = 0 self.zlib = True self.lz4 = use_lz4 self.lzo = use_lzo #for managing the recalculate_delays work: self.calculate_window_pixels = {} self.calculate_window_ids = set() self.calculate_timer = 0 self.calculate_last_time = 0 #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original #which may be used by other clients (other ServerSource instances) self.video_helper = getVideoHelper().clone()
def video_init(self): enclog("video_init() loading codecs") load_codecs(decoders=False) enclog("video_init() will try video encoders: %s", csv(self.video_encoder_modules) or "none") self.video_helper = getVideoHelper() #only use video encoders (no CSC supported in proxy) self.video_helper.set_modules(video_encoders=self.video_encoder_modules) self.video_helper.init() self.video_encoding_defs = {} self.video_encoders = {} self.video_encoders_dst_formats = [] self.video_encoders_last_used_time = {} self.video_encoder_types = [] #figure out which encoders we want to proxy for (if any): encoder_types = set() for encoding in self.video_helper.get_encodings(): colorspace_specs = self.video_helper.get_encoder_specs(encoding) for colorspace, especs in colorspace_specs.items(): if colorspace not in ("BGRX", "BGRA", "RGBX", "RGBA"): #only deal with encoders that can handle plain RGB directly continue for spec in especs: #ie: video_spec("x264") spec_props = spec.to_dict() del spec_props["codec_class"] #not serializable! spec_props["score_boost"] = 50 #we want to win scoring so we get used ahead of other encoders spec_props["max_instances"] = 3 #limit to 3 video streams we proxy for (we really want 2, # but because of races with garbage collection, we need to allow more) #store it in encoding defs: self.video_encoding_defs.setdefault(encoding, {}).setdefault(colorspace, []).append(spec_props) encoder_types.add(spec.codec_type) enclog("encoder types found: %s", tuple(encoder_types)) #remove duplicates and use preferred order: order = PREFERRED_ENCODER_ORDER[:] for x in list(encoder_types): if x not in order: order.append(x) self.video_encoder_types = [x for x in order if x in encoder_types] enclog.info("proxy video encoders: %s", ", ".join(self.video_encoder_types))
def init(self, opts): self.allowed_encodings = opts.encodings self.encoding = opts.encoding if opts.video_scaling.lower() in ("auto", "on"): self.video_scaling = None else: self.video_scaling = parse_bool_or_int("video-scaling", opts.video_scaling) self.quality = opts.quality self.min_quality = opts.min_quality self.speed = opts.speed self.min_speed = opts.min_speed load_codec("dec_pillow") ae = self.allowed_encodings if "jpeg" in ae: #try to load the fast jpeg encoder: load_codec("dec_jpeg") if "webp" in ae: #try to load the fast webp encoder: load_codec("dec_webp") vh = getVideoHelper() vh.set_modules(video_decoders=opts.video_decoders, csc_modules=opts.csc_modules or NO_GFX_CSC_OPTIONS) vh.init()
def filter_client_caps(self, caps): fc = self.filter_caps(caps, ("cipher", "digest", "aliases", "compression", "lz4")) #update with options provided via config if any: fc.update(self.session_options) if self.video_encoder_types: #pass list of encoding specs to client: from xpra.codecs.video_helper import getVideoHelper self.video_helper = getVideoHelper() self.video_helper.init() #serialize encodings defs into a dict: encoding_defs = {} e_found = [] #encoding: "h264" or "vp8", etc for encoding in self.video_helper.get_encodings(): #ie: colorspace_specs = {"BGRX" : [codec_spec("x264"), codec_spec("nvenc")], "YUV422P" : ... colorspace_specs = self.video_helper.get_encoder_specs(encoding) #ie: colorspace="BGRX", especs=[codec_spec("x264"), codec_spec("nvenc")] for colorspace, especs in colorspace_specs.items(): if colorspace not in ("BGRX", "BGRA", "RGBX", "RGBA"): #don't bother with formats that require a CSC step for now continue for spec in especs: #ie: codec_spec("x264") if spec.codec_type not in self.video_encoder_types: debug("skipping encoder %s", spec.codec_type) continue spec_props = spec.to_dict() del spec_props["codec_class"] #not serializable! spec_props["score_boost"] = 50 #we want to win scoring so we get used ahead of other encoders #store it in encoding defs: encoding_defs.setdefault(encoding, {}).setdefault(colorspace, []).append(spec_props) e_found.append(spec.codec_type) missing = [x for x in self.video_encoder_types if x not in e_found] if len(missing)>0: log.warn("the following proxy encoders were not found or did not match: %s", ", ".join(missing)) fc["encoding.proxy.video.encodings"] = encoding_defs fc["encoding.proxy.video"] = True return fc
def run_server(error_cb, opts, mode, xpra_file, extra_args): try: cwd = os.getcwd() except: cwd = os.path.expanduser("~") sys.stderr.write("current working directory does not exist, using '%s'\n" % cwd) if opts.encoding and opts.encoding=="help": #avoid errors and warnings: opts.encoding = "" opts.clipboard = False opts.notifications = False print("xpra server supports the following encodings:") print("(please wait, encoder initialization may take a few seconds)") #disable info logging which would be confusing here from xpra.log import get_all_loggers, set_default_level import logging set_default_level(logging.WARN) logging.root.setLevel(logging.WARN) for x in get_all_loggers(): x.logger.setLevel(logging.WARN) from xpra.server.server_base import ServerBase sb = ServerBase() sb.init(opts) #ensures that the threaded video helper init has completed #(by running it again, which will block on the init lock) from xpra.codecs.video_helper import getVideoHelper getVideoHelper().init() sb.init_encodings() from xpra.codecs.loader import encoding_help for e in sb.encodings: print(" * %s" % encoding_help(e)) return 0 assert mode in ("start", "upgrade", "shadow", "proxy") starting = mode == "start" upgrading = mode == "upgrade" shadowing = mode == "shadow" proxying = mode == "proxy" clobber = upgrading or opts.use_display if upgrading or shadowing: #there should already be one running opts.pulseaudio = False #get the display name: if shadowing and len(extra_args)==0: if sys.platform.startswith("win") or sys.platform.startswith("darwin"): #just a virtual name for the only display available: display_name = ":0" else: from xpra.scripts.main import guess_X11_display display_name = guess_X11_display(opts.socket_dir) elif upgrading and len(extra_args)==0: from xpra.scripts.main import guess_xpra_display display_name = guess_xpra_display(opts.socket_dir) else: if len(extra_args) > 1: error_cb("too many extra arguments: only expected a display number") if len(extra_args) == 1: display_name = extra_args[0] if not shadowing and not proxying: display_name_check(display_name) else: if proxying: error_cb("you must specify a free virtual display name to use with the proxy server") if not opts.displayfd: error_cb("displayfd support is not enabled on this system, you must specify the display to use") if opts.use_display: #only use automatic guess for xpra displays and not X11 displays: from xpra.scripts.main import guess_xpra_display #@Reimport display_name = guess_xpra_display(opts.socket_dir) else: # We will try to find one automaticaly # Use the temporary magic value 'S' as marker: display_name = 'S' + str(os.getpid()) if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child: error_cb("--exit-with-children specified without any children to spawn; exiting immediately") atexit.register(run_cleanups) #the server class will usually override those: signal.signal(signal.SIGINT, deadly_signal) signal.signal(signal.SIGTERM, deadly_signal) dotxpra = DotXpra(opts.socket_dir) # Generate the script text now, because os.getcwd() will # change if/when we daemonize: script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir) stdout = sys.stdout stderr = sys.stderr # Daemonize: if opts.daemon: #daemonize will chdir to "/", so try to use an absolute path: if opts.password_file: opts.password_file = os.path.abspath(opts.password_file) # At this point we may not know the display name, # so log_filename0 may point to a temporary file which we will rename later log_filename0 = select_log_file(dotxpra, opts.log_file, display_name) logfd = open_log_file(log_filename0) assert logfd > 2 stdout, stderr = daemonize(logfd) try: stderr.write("Entering daemon mode; " + "any further errors will be reported to:\n" + (" %s\n" % log_filename0)) except: #this can happen if stderr is closed by the caller already pass if os.name=="posix": # Write out a shell-script so that we can start our proxy in a clean # environment: write_runner_shell_script(dotxpra, script) from xpra.log import Logger log = Logger("server") mdns_recs = [] sockets = [] try: # Initialize the TCP sockets before the display, # That way, errors won't make us kill the Xvfb # (which may not be ours to kill at that point) bind_tcp = parse_bind_tcp(opts.bind_tcp) for host, iport in bind_tcp: socket = setup_tcp_socket(host, iport) sockets.append(socket) if opts.mdns: mdns_recs.append(("tcp", bind_tcp)) except Exception as e: log.error("cannot start server: failed to setup sockets: %s", e) return 1 # Do this after writing out the shell script: if display_name[0] != 'S': os.environ["DISPLAY"] = display_name sanitize_env() configure_imsettings_env(opts.input_method) # Start the Xvfb server first to get the display_name if needed xvfb = None xvfb_pid = None if not shadowing and not proxying and not clobber: try: xvfb, display_name = start_Xvfb(opts.xvfb, display_name) except OSError as e: log.error("Error starting Xvfb: %s\n", e) return 1 xvfb_pid = xvfb.pid #always update as we may now have the "real" display name: os.environ["DISPLAY"] = display_name if opts.daemon: log_filename1 = select_log_file(dotxpra, opts.log_file, display_name) if log_filename0 != log_filename1: # we now have the correct log filename, so use it: os.rename(log_filename0, log_filename1) stderr.write("Actual log file name is now: %s\n" % log_filename1) stdout.close() stderr.close() if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 #setup unix domain socket: socket, cleanup_socket = setup_local_socket(dotxpra, display_name, clobber, opts.mmap_group, opts.socket_permissions) if socket: #win32 returns None! sockets.append(socket) if opts.mdns: ssh_port = get_ssh_port() if ssh_port: mdns_recs.append(("ssh", [("", ssh_port)])) #publish mdns records: if opts.mdns: from xpra.platform.info import get_username mdns_info = {"display" : display_name, "username": get_username()} if opts.session_name: mdns_info["session"] = opts.session_name for mode, listen_on in mdns_recs: mdns_publish(display_name, mode, listen_on, mdns_info) if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 display = None if not sys.platform.startswith("win") and not sys.platform.startswith("darwin") and not proxying: display = verify_display_ready(xvfb, display_name, shadowing) if not display: return 1 elif not proxying: assert "gtk" not in sys.modules import gtk #@Reimport assert gtk if shadowing: from xpra.platform.shadow_server import ShadowServer app = ShadowServer() info = "shadow" elif proxying: from xpra.server.proxy_server import ProxyServer app = ProxyServer() info = "proxy" else: assert starting or upgrading from xpra.x11.gtk_x11 import gdk_display_source assert gdk_display_source #(now we can access the X11 server) if clobber: #get the saved pid (there should be one): xvfb_pid = get_xvfb_pid() elif xvfb_pid is not None: #save the new pid (we should have one): save_xvfb_pid(xvfb_pid) #check for an existing window manager: from xpra.x11.gtk_x11.wm import wm_check if not wm_check(display, opts.wm_name, upgrading): return 1 try: # This import is delayed because the module depends on gtk: from xpra.x11.server import XpraServer from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport X11Window = X11WindowBindings() except ImportError as e: log.error("Failed to load Xpra server components, check your installation: %s" % e) return 1 if not X11Window.displayHasXComposite(): log.error("Xpra is a compositing manager, it cannot use a display which lacks the XComposite extension!") return 1 app = XpraServer(clobber) info = "xpra" try: app.exec_cwd = cwd app.init(opts) except Exception as e: log.error("Error: cannot start the %s server", info, exc_info=True) log.error(str(e)) log.info("") return 1 #honour start child, html webserver, and setup child reaper if os.name=="posix" and not proxying and not upgrading and not shadowing: # start websockify? try: start_websockify(app.child_reaper, opts, bind_tcp) #websockify overrides the tcp proxy, so we must re-set it: app._tcp_proxy = opts.tcp_proxy except Exception as e: error_cb("failed to setup websockify html server: %s" % e) if opts.exit_with_children: assert opts.start_child, "exit-with-children was specified but start-child is missing!" if opts.start: for x in opts.start: if x: app.start_child(x, x, True) if opts.start_child: for x in opts.start_child: if x: app.start_child(x, x, False) log("%s(%s)", app.init_sockets, sockets) app.init_sockets(sockets) log("%s(%s)", app.init_when_ready, _when_ready) app.init_when_ready(_when_ready) #we got this far so the sockets have initialized and #the server should be able to manage the display #from now on, if we exit without upgrading we will also kill the Xvfb def kill_xvfb(): # Close our display(s) first, so the server dying won't kill us. log.info("killing xvfb with pid %s" % xvfb_pid) import gtk #@Reimport for display in gtk.gdk.display_manager_get().list_displays(): display.close() os.kill(xvfb_pid, signal.SIGTERM) if xvfb_pid is not None and not opts.use_display and not shadowing: _cleanups.append(kill_xvfb) try: log("running %s", app.run) e = app.run() log("%s()=%s", app.run, e) except KeyboardInterrupt: log.info("stopping on KeyboardInterrupt") e = 0 except Exception as e: log.error("server error", exc_info=True) e = -128 if e>0: # Upgrading/exiting, so leave X server running if kill_xvfb in _cleanups: _cleanups.remove(kill_xvfb) from xpra.server.server_core import ServerCore if e==ServerCore.EXITING_CODE: log.info("exiting: not cleaning up Xvfb") elif cleanup_socket in _cleanups: log.info("upgrading: not cleaning up Xvfb or socket") # don't delete the new socket (not ours) _cleanups.remove(cleanup_socket) log("cleanups=%s", _cleanups) e = 0 return e
def cleanup(self): try: getVideoHelper().cleanup() except: log.error("error on video cleanup", exc_info=True)
def run_server(parser, opts, mode, xpra_file, extra_args): if opts.encoding and opts.encoding=="help": #avoid errors and warnings: opts.encoding = "" opts.clipboard = False opts.notifications = False print("xpra server supports the following encodings:") print("(please wait, encoder initialization may take a few seconds)") #disable info logging which would be confusing here from xpra.log import get_all_loggers, set_default_level import logging set_default_level(logging.WARN) logging.root.setLevel(logging.WARN) for x in get_all_loggers(): x.logger.setLevel(logging.WARN) from xpra.server.server_base import ServerBase sb = ServerBase() sb.init(opts) #ensures that the threaded video helper init has completed #(by running it again, which will block on the init lock) from xpra.codecs.video_helper import getVideoHelper getVideoHelper().init() sb.init_encodings() from xpra.codecs.loader import encoding_help for e in sb.encodings: print(" * %s" % encoding_help(e)) return 0 assert mode in ("start", "upgrade", "shadow", "proxy") starting = mode == "start" upgrading = mode == "upgrade" shadowing = mode == "shadow" proxying = mode == "proxy" clobber = upgrading or opts.use_display #get the display name: if shadowing and len(extra_args)==0: from xpra.scripts.main import guess_X11_display display_name = guess_X11_display() else: if len(extra_args) != 1: parser.error("need exactly 1 extra argument") display_name = extra_args.pop(0) if not shadowing and not proxying: display_name_check(display_name) if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child: sys.stderr.write("--exit-with-children specified without any children to spawn; exiting immediately") return 1 atexit.register(run_cleanups) #the server class will usually override those: signal.signal(signal.SIGINT, deadly_signal) signal.signal(signal.SIGTERM, deadly_signal) dotxpra = DotXpra(opts.socket_dir) # Generate the script text now, because os.getcwd() will # change if/when we daemonize: script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir) # Daemonize: if opts.daemon: #daemonize will chdir to "/", so try to use an absolute path: if opts.password_file: opts.password_file = os.path.abspath(opts.password_file) logfd = open_log_file(dotxpra, opts.log_file, display_name) assert logfd > 2 daemonize(logfd) # Write out a shell-script so that we can start our proxy in a clean # environment: write_runner_shell_script(dotxpra, script) from xpra.log import Logger log = Logger("server") try: # Initialize the sockets before the display, # That way, errors won't make us kill the Xvfb # (which may not be ours to kill at that point) bind_tcp = parse_bind_tcp(opts.bind_tcp) sockets = [] mdns_info = {"display" : display_name, "username": getpass.getuser()} if opts.session_name: mdns_info["session"] = opts.session_name #tcp: for host, iport in bind_tcp: socket = setup_tcp_socket(host, iport) sockets.append(socket) #unix: socket, cleanup_socket = setup_local_socket(dotxpra, display_name, clobber, opts.mmap_group) if socket: #win32 returns None! sockets.append(socket) if opts.mdns: ssh_port = get_ssh_port() if ssh_port: mdns_publish(display_name, "ssh", [("", ssh_port)], mdns_info) if opts.mdns: mdns_publish(display_name, "tcp", bind_tcp, mdns_info) except Exception, e: log.error("cannot start server: failed to setup sockets: %s", e) return 1
def get_encodings_caps(self): if B_FRAMES: video_b_frames = ["h264"] #only tested with dec_avcodec2 else: video_b_frames = [] caps = { "flush": PAINT_FLUSH, "scaling.control": self.video_scaling, "client_options": True, "csc_atoms": True, #TODO: check for csc support (swscale only?) "video_reinit": True, "video_scaling": True, "video_b_frames": video_b_frames, "webp_leaks": False, "transparency": self.has_transparency(), "rgb24zlib": True, "max-soft-expired": MAX_SOFT_EXPIRED, "send-timestamps": SEND_TIMESTAMPS, "supports_delta": tuple(x for x in ("png", "rgb24", "rgb32") if x in self.get_core_encodings()), } if self.encoding: caps[""] = self.encoding for k, v in codec_versions.items(): caps["%s.version" % k] = v if self.quality > 0: caps["quality"] = self.quality if self.min_quality > 0: caps["min-quality"] = self.min_quality if self.speed >= 0: caps["speed"] = self.speed if self.min_speed >= 0: caps["min-speed"] = self.min_speed #generic rgb compression flags: for x in compression.ALL_COMPRESSORS: caps["rgb_%s" % x] = x in compression.get_enabled_compressors() #these are the defaults - when we instantiate a window, #we can send different values as part of the map event #these are the RGB modes we want (the ones we are expected to be able to paint with): rgb_formats = ["RGB", "RGBX", "RGBA"] caps["rgb_formats"] = rgb_formats #figure out which CSC modes (usually YUV) can give us those RGB modes: full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb( *rgb_formats) if has_codec("dec_webp"): if self.opengl_enabled: full_csc_modes["webp"] = ("BGRX", "BGRA", "RGBX", "RGBA") else: full_csc_modes["webp"] = ( "BGRX", "BGRA", ) log("supported full csc_modes=%s", full_csc_modes) caps["full_csc_modes"] = full_csc_modes if "h264" in self.get_core_encodings(): # some profile options: "baseline", "main", "high", "high10", ... # set the default to "high10" for I420/YUV420P # as the python client always supports all the profiles # whereas on the server side, the default is baseline to accomodate less capable clients. # I422/YUV422P requires high422, and # I444/YUV444P requires high444, # so we don't bother specifying anything for those two. for old_csc_name, csc_name, default_profile in (("I420", "YUV420P", "high10"), ("I422", "YUV422P", ""), ("I444", "YUV444P", "")): profile = default_profile #try with the old prefix (X264) as well as the more correct one (H264): for H264_NAME in ("X264", "H264"): profile = os.environ.get( "XPRA_%s_%s_PROFILE" % (H264_NAME, old_csc_name), profile) profile = os.environ.get( "XPRA_%s_%s_PROFILE" % (H264_NAME, csc_name), profile) if profile: #send as both old and new names: for h264_name in ("x264", "h264"): caps["%s.%s.profile" % (h264_name, old_csc_name)] = profile caps["%s.%s.profile" % (h264_name, csc_name)] = profile log( "x264 encoding options: %s", str([(k, v) for k, v in caps.items() if k.startswith("x264.")])) log("encoding capabilities: %s", caps) return caps
def get_encodings_caps(self) -> dict: if B_FRAMES: video_b_frames = ("h264", ) #only tested with dec_avcodec2 else: video_b_frames = () caps = { "flush" : PAINT_FLUSH, #v4 servers assume this is available "video_scaling" : True, #v4 servers assume this is available "video_b_frames" : video_b_frames, "video_max_size" : self.video_max_size, "max-soft-expired" : MAX_SOFT_EXPIRED, "send-timestamps" : SEND_TIMESTAMPS, } if self.video_scaling is not None: caps["scaling.control"] = self.video_scaling if self.encoding: caps[""] = self.encoding for k,v in codec_versions.items(): caps["%s.version" % k] = v if self.quality>0: caps["quality"] = self.quality if self.min_quality>0: caps["min-quality"] = self.min_quality if self.speed>=0: caps["speed"] = self.speed if self.min_speed>=0: caps["min-speed"] = self.min_speed #generic rgb compression flags: for x in compression.ALL_COMPRESSORS: caps["rgb_%s" % x] = x in compression.get_enabled_compressors() #these are the defaults - when we instantiate a window, #we can send different values as part of the map event #these are the RGB modes we want (the ones we are expected to be able to paint with): rgb_formats = ["RGB", "RGBX", "RGBA"] caps["rgb_formats"] = rgb_formats #figure out which CSC modes (usually YUV) can give us those RGB modes: full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*rgb_formats) if has_codec("dec_webp"): if self.opengl_enabled: full_csc_modes["webp"] = ("BGRX", "BGRA", "RGBX", "RGBA") else: full_csc_modes["webp"] = ("BGRX", "BGRA", ) if has_codec("dec_jpeg") or has_codec("dec_pillow"): full_csc_modes["jpeg"] = ("BGRX", "BGRA", "YUV420P") if has_codec("dec_jpeg"): full_csc_modes["jpega"] = ("BGRA", "RGBA", ) log("supported full csc_modes=%s", full_csc_modes) caps["full_csc_modes"] = full_csc_modes if "h264" in self.get_core_encodings(): # some profile options: "baseline", "main", "high", "high10", ... # set the default to "high10" for YUV420P # as the python client always supports all the profiles # whereas on the server side, the default is baseline to accomodate less capable clients. # YUV422P requires high422, and # YUV444P requires high444, # so we don't bother specifying anything for those two. h264_caps = {} for csc_name, default_profile in ( ("YUV420P", "high"), ("YUV422P", ""), ("YUV444P", "")): profile = os.environ.get("XPRA_H264_%s_PROFILE" % (csc_name), default_profile) if profile: h264_caps["%s.profile" % (csc_name)] = profile h264_caps["fast-decode"] = envbool("XPRA_X264_FAST_DECODE", False) log("x264 encoding options: %s", h264_caps) updict(caps, "h264", h264_caps) log("encoding capabilities: %s", caps) return caps
def cleanup(self): try: getVideoHelper().cleanup() except Exception: # pragma: no cover log.error("error on video cleanup", exc_info=True)
def cleanup(self): getVideoHelper().cleanup()
def do_start_XpraClient(self, conn, display_desc={}): log("do_start_XpraClient(%s, %s) client=%s", conn, display_desc, self.client) self.client.encoding = self.config.encoding self.client.display_desc = display_desc self.client.setup_connection(conn) #we have already initialized it, #but calling client.init will do it again - so we have to clear it: from xpra.codecs.video_helper import getVideoHelper getVideoHelper().cleanup() self.client.init(self.config) self.client.init_ui(self.config) log("start_XpraClient() client initialized") if self.config.password: #pass the password to the class directly: def load_password(): return self.config.password self.client.password_file = "FAKE-PASSWORD-FILE-FOR-LAUNCHER" self.client.load_password = load_password #override exit code: warn_and_quit_save = self.client.warn_and_quit quit_save = self.client.quit def do_quit(*_args): self.client.warn_and_quit = warn_and_quit_save self.client.quit = quit_save self.client.cleanup() self.destroy() gtk.main_quit() def warn_and_quit_override(exit_code, warning): log("warn_and_quit_override(%s, %s)", exit_code, warning) if self.exit_code == None: self.exit_code = exit_code password_warning = warning.find("invalid password")>=0 if password_warning: self.password_warning() err = exit_code!=0 or password_warning if not self.current_error: self.current_error = warning self.set_info_color(err) self.set_info_text(warning) if err: def ignore_further_quit_events(*args): pass if self.client: self.client.cleanup() self.client.warn_and_quit = ignore_further_quit_events self.client.quit = ignore_further_quit_events w = self.window if w: self.set_sensitive(True) self.reset_client() glib.idle_add(w.show) else: do_quit() def quit_override(exit_code): log("quit_override(%s)", exit_code) if self.exit_code == None: self.exit_code = exit_code self.client.cleanup() if self.exit_code==0: do_quit() else: self.reset_client() self.client.warn_and_quit = warn_and_quit_override self.client.quit = quit_override try: self.client.run() except Exception as e: log.error("client error", exc_info=True) self.handle_exception(e)
def threaded_setup(self): getVideoHelper().init() #re-init encodings now that we have video: self.init_encodings()
def run_server(error_cb, opts, mode, xpra_file, extra_args): try: cwd = os.getcwd() except: cwd = os.path.expanduser("~") sys.stderr.write("current working directory does not exist, using '%s'\n" % cwd) if opts.encoding and opts.encoding == "help": # avoid errors and warnings: opts.encoding = "" opts.clipboard = False opts.notifications = False print("xpra server supports the following encodings:") print("(please wait, encoder initialization may take a few seconds)") # disable info logging which would be confusing here from xpra.log import get_all_loggers, set_default_level import logging set_default_level(logging.WARN) logging.root.setLevel(logging.WARN) for x in get_all_loggers(): x.logger.setLevel(logging.WARN) from xpra.server.server_base import ServerBase sb = ServerBase() sb.init(opts) # ensures that the threaded video helper init has completed # (by running it again, which will block on the init lock) from xpra.codecs.video_helper import getVideoHelper getVideoHelper().init() sb.init_encodings() from xpra.codecs.loader import encoding_help for e in sb.encodings: print(" * %s" % encoding_help(e)) return 0 assert mode in ("start", "upgrade", "shadow", "proxy") starting = mode == "start" upgrading = mode == "upgrade" shadowing = mode == "shadow" proxying = mode == "proxy" clobber = upgrading or opts.use_display # get the display name: if shadowing and len(extra_args) == 0: if sys.platform.startswith("win") or sys.platform.startswith("darwin"): # just a virtual name for the only display available: display_name = ":0" else: from xpra.scripts.main import guess_X11_display display_name = guess_X11_display(opts.socket_dir) elif upgrading and len(extra_args) == 0: from xpra.scripts.main import guess_xpra_display display_name = guess_xpra_display(opts.socket_dir) else: if len(extra_args) > 1: error_cb("too many extra arguments: only expected a display number") if len(extra_args) == 1: display_name = extra_args[0] if not shadowing and not proxying: display_name_check(display_name) else: if proxying: error_cb("you must specify a free virtual display name to use with the proxy server") if not opts.displayfd: error_cb("displayfd support is not enabled on this system, you must specify the display to use") if opts.use_display: # only use automatic guess for xpra displays and not X11 displays: from xpra.scripts.main import guess_xpra_display # @Reimport display_name = guess_xpra_display(opts.socket_dir) else: # We will try to find one automaticaly # Use the temporary magic value 'S' as marker: display_name = "S" + str(os.getpid()) if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child: error_cb("--exit-with-children specified without any children to spawn; exiting immediately") atexit.register(run_cleanups) # the server class will usually override those: signal.signal(signal.SIGINT, deadly_signal) signal.signal(signal.SIGTERM, deadly_signal) dotxpra = DotXpra(opts.socket_dir) # Generate the script text now, because os.getcwd() will # change if/when we daemonize: script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir) stdout = sys.stdout stderr = sys.stderr # Daemonize: if opts.daemon: # daemonize will chdir to "/", so try to use an absolute path: if opts.password_file: opts.password_file = os.path.abspath(opts.password_file) # At this point we may not know the display name, # so log_filename0 may point to a temporary file which we will rename later log_filename0 = select_log_file(dotxpra, opts.log_file, display_name) logfd = open_log_file(log_filename0) assert logfd > 2 stdout, stderr = daemonize(logfd) try: stderr.write( "Entering daemon mode; " + "any further errors will be reported to:\n" + (" %s\n" % log_filename0) ) except: # this can happen if stderr is closed by the caller already pass # Write out a shell-script so that we can start our proxy in a clean # environment: write_runner_shell_script(dotxpra, script) from xpra.log import Logger log = Logger("server") mdns_recs = [] sockets = [] try: # Initialize the TCP sockets before the display, # That way, errors won't make us kill the Xvfb # (which may not be ours to kill at that point) bind_tcp = parse_bind_tcp(opts.bind_tcp) for host, iport in bind_tcp: socket = setup_tcp_socket(host, iport) sockets.append(socket) if opts.mdns: mdns_recs.append(("tcp", bind_tcp)) except Exception, e: log.error("cannot start server: failed to setup sockets: %s", e) return 1
def init_encodings(self): encs, core_encs = [], [] log("init_encodings() allowed_encodings=%s", self.allowed_encodings) def add_encoding(encoding): log("add_encoding(%s)", encoding) e = {"rgb32": "rgb", "rgb24": "rgb"}.get(encoding, encoding) if self.allowed_encodings is not None: if e not in self.allowed_encodings and encoding not in self.allowed_encodings: #not in whitelist (if it exists) return if e not in encs: encs.append(e) if encoding not in core_encs: core_encs.append(encoding) def add_encodings(*encodings): log("add_encodings%s", encodings) for encoding in encodings: add_encoding(encoding) add_encodings("rgb24", "rgb32", "scroll") lossless = [] if "scroll" in self.allowed_encodings and "scroll" not in self.lossless_mode_encodings: #scroll is lossless, but it also uses other picture codecs #and those allow changes in quality lossless.append("scroll") #video encoders (empty when first called - see threaded_init) ve = getVideoHelper().get_encodings() log("init_encodings() adding video encodings: %s", ve) add_encodings(*ve) #ie: ["vp8", "h264"] #Pithon Imaging Libary: enc_pillow = get_codec("enc_pillow") log("enc_pillow=%s", enc_pillow) if enc_pillow: pil_encs = enc_pillow.get_encodings() log("pillow encodings: %s", pil_encs) for encoding in pil_encs: if encoding != "webp": add_encoding(encoding) #Note: webp will only be enabled if we have a Python-PIL fallback #(either "webp" or "png") if has_codec("enc_webp") and ("webp" in pil_encs or "png" in pil_encs): add_encodings("webp") if "webp" not in lossless: lossless.append("webp") for codec_name in ("avif", "enc_jpeg", "enc_nvjpeg"): codec = get_codec(codec_name) if codec: add_encodings(*codec.get_encodings()) #look for video encodings with lossless mode: for e in ve: for colorspace, especs in getVideoHelper().get_encoder_specs( e).items(): for espec in especs: if espec.has_lossless_mode: if e not in lossless: log( "found lossless mode for encoding %s with %s and colorspace %s", e, espec, colorspace) lossless.append(e) break #now update the variables: encs.append("grayscale") self.encodings = encs self.core_encodings = tuple(core_encs) self.lossless_mode_encodings = tuple(lossless) self.lossless_encodings = tuple( x for x in self.core_encodings if (x.startswith("png") or x.startswith("rgb") or x == "webp")) log( "allowed encodings=%s, encodings=%s, core encodings=%s, lossless encodings=%s", self.allowed_encodings, encs, core_encs, self.lossless_encodings) pref = [x for x in PREFERRED_ENCODING_ORDER if x in self.encodings] if pref: self.default_encoding = pref[0] else: self.default_encoding = None #default encoding: if not self.encoding or str(self.encoding).lower() in ("auto", "none"): self.default_encoding = None elif self.encoding in self.encodings: self.default_encoding = self.encoding else: log.warn("ignored invalid default encoding option: %s", self.encoding)
def do_start_XpraClient(self, conn, display_desc={}): log("do_start_XpraClient(%s, %s) client=%s", conn, display_desc, self.client) self.client.encoding = self.config.encoding self.client.display_desc = display_desc self.client.setup_connection(conn) #we have already initialized it, #but calling client.init will do it again - so we have to clear it: from xpra.codecs.video_helper import getVideoHelper getVideoHelper().cleanup() self.client.init(self.config) self.client.init_ui(self.config) log("start_XpraClient() client initialized") if self.config.password: #pass the password to the class directly: def load_password(): return self.config.password self.client.password_file = "FAKE-PASSWORD-FILE-FOR-LAUNCHER" self.client.load_password = load_password #override exit code: warn_and_quit_save = self.client.warn_and_quit quit_save = self.client.quit def do_quit(*args): self.client.warn_and_quit = warn_and_quit_save self.client.quit = quit_save self.client.cleanup() self.destroy() gtk.main_quit() def warn_and_quit_override(exit_code, warning): log("warn_and_quit_override(%s, %s)", exit_code, warning) if self.exit_code == None: self.exit_code = exit_code password_warning = warning.find("invalid password")>=0 if password_warning: self.password_warning() err = exit_code!=0 or password_warning if not self.current_error: self.current_error = warning self.set_info_color(err) self.set_info_text(warning) if err: def ignore_further_quit_events(*args): pass if self.client: self.client.cleanup() self.client.warn_and_quit = ignore_further_quit_events self.client.quit = ignore_further_quit_events self.set_sensitive(True) self.reset_client() glib.idle_add(self.window.show) else: do_quit() def quit_override(exit_code): log("quit_override(%s)", exit_code) if self.exit_code == None: self.exit_code = exit_code self.client.cleanup() if self.exit_code==0: do_quit() else: self.reset_client() self.client.warn_and_quit = warn_and_quit_override self.client.quit = quit_override try: self.client.run() except Exception as e: log.error("client error", exc_info=True) self.handle_exception(e)
def run_server(error_cb, opts, mode, xpra_file, extra_args): try: cwd = os.getcwd() except: cwd = os.path.expanduser("~") sys.stderr.write( "current working directory does not exist, using '%s'\n" % cwd) if opts.encoding and opts.encoding == "help": #avoid errors and warnings: opts.encoding = "" opts.clipboard = False opts.notifications = False print("xpra server supports the following encodings:") print("(please wait, encoder initialization may take a few seconds)") #disable info logging which would be confusing here from xpra.log import get_all_loggers, set_default_level import logging set_default_level(logging.WARN) logging.root.setLevel(logging.WARN) for x in get_all_loggers(): x.logger.setLevel(logging.WARN) from xpra.server.server_base import ServerBase sb = ServerBase() sb.init(opts) #ensures that the threaded video helper init has completed #(by running it again, which will block on the init lock) from xpra.codecs.video_helper import getVideoHelper getVideoHelper().init() sb.init_encodings() from xpra.codecs.loader import encoding_help for e in sb.encodings: print(" * %s" % encoding_help(e)) return 0 assert mode in ("start", "upgrade", "shadow", "proxy") starting = mode == "start" upgrading = mode == "upgrade" shadowing = mode == "shadow" proxying = mode == "proxy" clobber = upgrading or opts.use_display start_vfb = not shadowing and not proxying and not clobber if upgrading or shadowing: #there should already be one running opts.pulseaudio = False #get the display name: if shadowing and len(extra_args) == 0: if sys.platform.startswith("win") or sys.platform.startswith("darwin"): #just a virtual name for the only display available: display_name = ":0" else: from xpra.scripts.main import guess_X11_display display_name = guess_X11_display(opts.socket_dir, opts.socket_dirs) elif upgrading and len(extra_args) == 0: display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs) else: if len(extra_args) > 1: error_cb( "too many extra arguments: only expected a display number") if len(extra_args) == 1: display_name = extra_args[0] if not shadowing and not proxying: display_name_check(display_name) else: if proxying: error_cb( "you must specify a free virtual display name to use with the proxy server" ) if not opts.displayfd: error_cb( "displayfd support is not enabled on this system, you must specify the display to use" ) if opts.use_display: #only use automatic guess for xpra displays and not X11 displays: display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs) else: # We will try to find one automaticaly # Use the temporary magic value 'S' as marker: display_name = 'S' + str(os.getpid()) if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child: error_cb( "--exit-with-children specified without any children to spawn; exiting immediately" ) atexit.register(run_cleanups) #the server class will usually override those: #SIGINT breaks GTK3.. (but there are no py3k servers!) signal.signal(signal.SIGINT, deadly_signal) signal.signal(signal.SIGTERM, deadly_signal) # Generate the script text now, because os.getcwd() will # change if/when we daemonize: script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir) if start_vfb or opts.daemon: #we will probably need a log dir #either for the vfb, or for our own log file log_dir = os.path.expanduser(opts.log_dir) if not os.path.exists(log_dir): try: os.mkdir(log_dir, 0o700) except OSError as e: raise InitException( "failed to create the Xorg log directory '%s': %s" % (xorg_log_dir, e)) stdout = sys.stdout stderr = sys.stderr # Daemonize: if opts.daemon: #daemonize will chdir to "/", so try to use an absolute path: if opts.password_file: opts.password_file = os.path.abspath(opts.password_file) # At this point we may not know the display name, # so log_filename0 may point to a temporary file which we will rename later log_filename0 = select_log_file(log_dir, opts.log_file, display_name) logfd = open_log_file(log_filename0) assert logfd > 2 stdout, stderr = daemonize(logfd) try: stderr.write("Entering daemon mode; " + "any further errors will be reported to:\n" + (" %s\n" % log_filename0)) except: #this can happen if stderr is closed by the caller already pass if os.name == "posix": # Write out a shell-script so that we can start our proxy in a clean # environment: write_runner_shell_script(script) from xpra.log import Logger log = Logger("server") #warn early about this: if starting: de = os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get( "SESSION_DESKTOP") if de and (opts.pulseaudio or opts.notifications): log.warn( "Warning: xpra start from an existing '%s' desktop session", de) log.warn(" pulseaudio and notifications forwarding may not work") log.warn( " try using a clean environment, a dedicated user, or turn off those options" ) mdns_recs = [] sockets = [] # Initialize the TCP sockets before the display, # That way, errors won't make us kill the Xvfb # (which may not be ours to kill at that point) bind_tcp = parse_bind_tcp(opts.bind_tcp) for host, iport in bind_tcp: socket = setup_tcp_socket(host, iport) sockets.append(socket) if opts.mdns: rec = "tcp", [(host, iport)] mdns_recs.append(rec) # Do this after writing out the shell script: if display_name[0] != 'S': os.environ["DISPLAY"] = display_name sanitize_env() configure_imsettings_env(opts.input_method) # Start the Xvfb server first to get the display_name if needed xvfb = None xvfb_pid = None if start_vfb: try: xvfb, display_name = start_Xvfb(opts.xvfb, display_name) except OSError as e: log.error("Error starting Xvfb: %s\n", e) return 1 xvfb_pid = xvfb.pid #always update as we may now have the "real" display name: os.environ["DISPLAY"] = display_name if opts.daemon: log_filename1 = select_log_file(log_dir, opts.log_file, display_name) if log_filename0 != log_filename1: # we now have the correct log filename, so use it: os.rename(log_filename0, log_filename1) stderr.write("Actual log file name is now: %s\n" % log_filename1) stdout.close() stderr.close() if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 #setup unix domain socket: socket, cleanup_socket = setup_local_socket(opts.socket_dir, opts.socket_dirs, display_name, clobber, opts.mmap_group, opts.socket_permissions) if socket: #win32 returns None! sockets.append(socket) if opts.mdns: ssh_port = get_ssh_port() if ssh_port: mdns_recs.append(("ssh", [("", ssh_port)])) #publish mdns records: if opts.mdns: from xpra.platform.info import get_username mdns_info = {"display": display_name, "username": get_username()} if opts.session_name: mdns_info["session"] = opts.session_name for mode, listen_on in mdns_recs: mdns_publish(display_name, mode, listen_on, mdns_info) if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 display = None if not sys.platform.startswith("win") and not sys.platform.startswith( "darwin") and not proxying: display = verify_display_ready(xvfb, display_name, shadowing) if not display: return 1 elif not proxying: no_gtk() import gtk #@Reimport assert gtk if shadowing: from xpra.platform.shadow_server import ShadowServer app = ShadowServer() info = "shadow" elif proxying: from xpra.server.proxy.proxy_server import ProxyServer app = ProxyServer() info = "proxy" else: assert starting or upgrading from xpra.x11.gtk2 import gdk_display_source assert gdk_display_source #(now we can access the X11 server) if clobber: #get the saved pid (there should be one): xvfb_pid = get_xvfb_pid() elif xvfb_pid is not None: #save the new pid (we should have one): save_xvfb_pid(xvfb_pid) #check for an existing window manager: from xpra.x11.gtk2.wm import wm_check if not wm_check(display, opts.wm_name, upgrading): return 1 try: # This import is delayed because the module depends on gtk: from xpra.x11.server import XpraServer from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport X11Window = X11WindowBindings() except ImportError as e: log.error( "Failed to load Xpra server components, check your installation: %s" % e) return 1 if not X11Window.displayHasXComposite(): log.error( "Xpra is a compositing manager, it cannot use a display which lacks the XComposite extension!" ) return 1 log("XShape=%s", X11Window.displayHasXShape()) app = XpraServer(clobber) info = "xpra" #we got this far so the sockets have initialized and #the server should be able to manage the display #from now on, if we exit without upgrading we will also kill the Xvfb def kill_xvfb(): # Close our display(s) first, so the server dying won't kill us. log.info("killing xvfb with pid %s" % xvfb_pid) import gtk #@Reimport for display in gtk.gdk.display_manager_get().list_displays(): display.close() os.kill(xvfb_pid, signal.SIGTERM) if xvfb_pid is not None and not opts.use_display and not shadowing: _cleanups.append(kill_xvfb) try: app.exec_cwd = cwd app.init(opts) except InitException as e: log.error("xpra server initialization error:") log.error(" %s", e) return 1 except Exception as e: log.error("Error: cannot start the %s server", info, exc_info=True) log.error(str(e)) log.info("") return 1 #honour start child, html webserver, and setup child reaper if os.name == "posix" and not proxying and not upgrading and not shadowing: # start websockify? try: start_websockify(app.child_reaper, opts, bind_tcp) #websockify overrides the tcp proxy, so we must re-set it: app._tcp_proxy = opts.tcp_proxy except Exception as e: error_cb("failed to setup websockify html server: %s" % e) if opts.exit_with_children: assert opts.start_child, "exit-with-children was specified but start-child is missing!" if opts.start: for x in opts.start: if x: app.start_child(x, x, True) if opts.start_child: for x in opts.start_child: if x: app.start_child(x, x, False) log("%s(%s)", app.init_sockets, sockets) app.init_sockets(sockets) log("%s(%s)", app.init_when_ready, _when_ready) app.init_when_ready(_when_ready) try: log("running %s", app.run) e = app.run() log("%s()=%s", app.run, e) except KeyboardInterrupt: log.info("stopping on KeyboardInterrupt") e = 0 except Exception as e: log.error("server error", exc_info=True) e = -128 if e > 0: # Upgrading/exiting, so leave X server running if kill_xvfb in _cleanups: _cleanups.remove(kill_xvfb) from xpra.server.server_core import ServerCore if e == ServerCore.EXITING_CODE: log.info("exiting: not cleaning up Xvfb") elif cleanup_socket in _cleanups: log.info("upgrading: not cleaning up Xvfb or socket") # don't delete the new socket (not ours) _cleanups.remove(cleanup_socket) log("cleanups=%s", _cleanups) e = 0 return e
def init_encodings(self): encs, core_encs = [], [] log("init_encodings() allowed_encodings=%s", self.allowed_encodings) def add_encodings(encodings): log("add_encodings(%s)", encodings) for ce in encodings: e = {"rgb32": "rgb", "rgb24": "rgb"}.get(ce, ce) if self.allowed_encodings is not None and e not in self.allowed_encodings: #not in whitelist (if it exists) continue if e not in encs: encs.append(e) if ce not in core_encs: core_encs.append(ce) add_encodings(["rgb24", "rgb32"]) #video encoders (empty when first called - see threaded_init) ve = getVideoHelper().get_encodings() log("init_encodings() adding video encodings: %s", ve) add_encodings(ve) #ie: ["vp8", "h264"] #Pithon Imaging Libary: enc_pillow = get_codec("enc_pillow") if enc_pillow: pil_encs = enc_pillow.get_encodings() add_encodings(x for x in pil_encs if x != "webp") #Note: webp will only be enabled if we have a Python-PIL fallback #(either "webp" or "png") if has_codec("enc_webp") and ("webp" in pil_encs or "png" in pil_encs): add_encodings(["webp"]) if "webp" not in self.lossless_mode_encodings: self.lossless_mode_encodings.append("webp") #look for video encodings with lossless mode: for e in ve: for colorspace, especs in getVideoHelper().get_encoder_specs( e).items(): for espec in especs: if espec.has_lossless_mode: if e not in self.lossless_mode_encodings: log( "found lossless mode for encoding %s with %s and colorspace %s", e, espec, colorspace) self.lossless_mode_encodings.append(e) break #now update the variables: self.encodings = encs self.core_encodings = core_encs self.lossless_encodings = [ x for x in self.core_encodings if (x.startswith("png") or x.startswith("rgb") or x == "webp") ] log( "allowed encodings=%s, encodings=%s, core encodings=%s, lossless encodings=%s", self.allowed_encodings, encs, core_encs, self.lossless_encodings) pref = [x for x in PREFERED_ENCODING_ORDER if x in self.encodings] if pref: self.default_encoding = pref[0] else: self.default_encoding = None #default encoding: if not self.encoding or str(self.encoding).lower() in ("auto", "none"): self.default_encoding = None elif self.encoding in self.encodings: self.default_encoding = self.encoding else: log.warn("ignored invalid default encoding option: %s", self.encoding)
def test_load(): #NOTE: we assume that the scores: # - make nvenc less desirable at HQ (maybe not?) # - make cuda csc beat swscale vep = getVideoHelper() vep.may_init()
def video_helper_init(self): self.video_helper = getVideoHelper() #only use video encoders (no CSC supported in proxy) self.video_helper.set_modules( video_encoders=self.video_encoder_modules) self.video_helper.init()