def test_main(self): compression.init_all() assert compression.use("zlib") assert compression.get_compression_caps() assert compression.get_enabled_compressors() for x in compression.get_enabled_compressors(): assert compression.get_compressor(x)
def get_rgb_compression_options(): from xpra.net import compression compressors = compression.get_enabled_compressors() RGB_COMP_OPTIONS = ["Raw RGB"] if compressors: RGB_COMP_OPTIONS += ["/".join(compressors)] return RGB_COMP_OPTIONS
def get_encoding_help(encoding): from xpra.net import compression compressors = compression.get_enabled_compressors() return { "auto": "automatic mode (recommended)", "h264": "H.264 video codec", "h265": "H.265 (HEVC) video codec (slow and buggy - do not use!)", "vp8": "VP8 video codec", "vp9": "VP9 video codec", "mpeg4": "MPEG-4 video codec", "png": "Portable Network Graphics (lossless, 24bpp or 32bpp for transparency)", "png/P": "Portable Network Graphics (lossy, 8bpp colour)", "png/L": "Portable Network Graphics (lossy, 8bpp grayscale)", "webp": "WebP compression (supports lossless and lossy modes)", "jpeg": "JPEG lossy compression", "jpeg2000": "JPEG 2000 lossy compression (slow)", "rgb": "Raw RGB pixels, lossless, compressed using %s (24bpp or 32bpp for transparency)" % (" or ".join(compressors)), }.get(encoding)
def get_control_commands_caps(self): caps = ["show_session_info", "show_bug_report", "debug"] for x in compression.get_enabled_compressors(): caps.append("enable_" + x) for x in packet_encoding.get_enabled_encoders(): caps.append("enable_" + x) log("get_control_commands_caps()=%s", caps) return {"": caps}
def control_command_compression(self, compression): c = compression.lower() from xpra.net import compression opts = compression.get_enabled_compressors() #ie: [lz4, lzo, zlib] if c not in opts: raise ControlError("compressor argument must be one of: %s" % csv(opts)) for cproto in tuple(self._server_sources.keys()): cproto.enable_compressor(c) self.all_send_client_command("enable_%s" % c) return "compressors set to %s" % compression
def enable_compressor_from_caps(self, caps): if self.compression_level==0: self.enable_compressor("none") return opts = compression.get_enabled_compressors(order=compression.PERFORMANCE_ORDER) log("enable_compressor_from_caps(..) options=%s", opts) for c in opts: #ie: [zlib, lz4, lzo] if caps.boolget(c): self.enable_compressor(c) return log.warn("compression disabled: no matching compressor found") self.enable_compressor("none")
def enable_compressor_from_caps(self, caps): if self.compression_level == 0: self.enable_compressor("none") return opts = compression.get_enabled_compressors(order=compression.PERFORMANCE_ORDER) log("enable_compressor_from_caps(..) options=%s", opts) for c in opts: # ie: [zlib, lz4, lzo] if caps.boolget(c): self.enable_compressor(c) return log.warn("compression disabled: no matching compressor found") self.enable_compressor("none")
def enable_compressor_from_caps(self, caps): if self.compression_level == 0: self.enable_compressor("none") return opts = compression.get_enabled_compressors( order=compression.PERFORMANCE_ORDER) defaults = {"zlib": True} for c in opts: #ie: [zlib, lz4, lzo] if caps.boolget(c, defaults.get(c)): self.enable_compressor(c) return log.warn("compression disabled: no matching compressor found") self.enable_compressor("none")
def _process_control(self, packet): command = packet[1] if command == "show_session_info": args = packet[2:] log("calling show_session_info%s on server request", args) self.show_session_info(*args) elif command == "show_bug_report": self.show_bug_report() elif command in ("enable_%s" % x for x in compression.get_enabled_compressors()): compressor = command.split("_")[1] log.info("switching to %s on server request", compressor) self._protocol.enable_compressor(compressor) elif command in ("enable_%s" % x for x in packet_encoding.get_enabled_encoders()): pe = command.split("_")[1] log.info("switching to %s on server request", pe) self._protocol.enable_encoder(pe) elif command == "name": assert len(args) >= 3 self.server_session_name = args[2] log.info("session name updated from server: %s", self.server_session_name) #TODO: reset tray tooltip, session info title, etc.. elif command == "debug": args = packet[2:] if len(args) < 2: log.warn("not enough arguments for debug control command") return log_cmd = args[0] if log_cmd not in ("enable", "disable"): log.warn( "invalid debug control mode: '%s' (must be 'enable' or 'disable')", log_cmd) return categories = args[1:] from xpra.log import add_debug_category, add_disabled_category, enable_debug_for, disable_debug_for if log_cmd == "enable": add_debug_category(*categories) loggers = enable_debug_for(*categories) else: assert log_cmd == "disable" add_disabled_category(*categories) loggers = disable_debug_for(*categories) log.info("%sd debugging for: %s", log_cmd, loggers) return else: log.warn("received invalid control command from server: %s", command)
def get_network_caps(): try: from xpra.net.mmap_pipe import can_use_mmap mmap = can_use_mmap() except: mmap = False caps = { "digest" : ("hmac", "xor"), "compressors" : compression.get_enabled_compressors(), "encoders" : packet_encoding.get_enabled_encoders(), "mmap" : mmap, } caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_network_caps(): try: from xpra.net.mmap_pipe import can_use_mmap mmap = can_use_mmap() except: mmap = False caps = { "digest": ("hmac", "xor"), "compressors": compression.get_enabled_compressors(), "encoders": packet_encoding.get_enabled_encoders(), "mmap": mmap, } caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_network_caps() -> dict: from xpra.net.digest import get_digests from xpra.net.crypto import get_crypto_caps from xpra.net.compression import get_enabled_compressors, get_compression_caps from xpra.net.packet_encoding import get_enabled_encoders, get_packet_encoding_caps digests = get_digests() #"hmac" is the legacy name, "xor" and "des" should not be used for salt: salt_digests = tuple(x for x in digests if x not in ("hmac", "xor", "des")) caps = { "digest": digests, "salt-digest": salt_digests, "compressors": get_enabled_compressors(), "encoders": get_enabled_encoders(), } caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_network_caps(legacy=True): try: from xpra.net.mmap_pipe import can_use_mmap mmap = can_use_mmap() except: mmap = False caps = { "digest": ("hmac", "xor"), "compressors": compression.get_enabled_compressors(), "encoders": packet_encoding.get_enabled_encoders(), "mmap": mmap, } if legacy: #for backwards compatibility only: caps.update({"raw_packets": True, "chunked_compression": True}) caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_network_caps(): try: from xpra.net.mmap_pipe import can_use_mmap mmap = can_use_mmap() except: mmap = False digests = get_digests() #"hmac" is the legacy name, "xor" should not be used for salt: salt_digests = [x for x in digests if x not in ("hmac", "xor")] caps = { "digest": digests, "salt-digest": salt_digests, "compressors": compression.get_enabled_compressors(), "encoders": packet_encoding.get_enabled_encoders(), "mmap": mmap, } caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_network_caps(legacy=True): try: from xpra.net.mmap_pipe import can_use_mmap mmap = can_use_mmap() except: mmap = False caps = { "digest": ("hmac", "xor"), "compressors": compression.get_enabled_compressors(), "encoders": packet_encoding.get_enabled_encoders(), "mmap": mmap, } if legacy: # for backwards compatibility only: caps.update({"raw_packets": True, "chunked_compression": True}) caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
def get_encoding_help(encoding): from xpra.net import compression compressors = compression.get_enabled_compressors() compressors = [x for x in compressors if x!="brotli"] return { "auto" : "automatic mode (recommended)", "grayscale" : "same as 'auto' but in grayscale mode", "h264" : "H.264 video codec", "h265" : "H.265 (HEVC) video codec (not recommended)", "vp8" : "VP8 video codec", "vp9" : "VP9 video codec", "mpeg4" : "MPEG-4 video codec", "png" : "Portable Network Graphics (lossless, 24bpp or 32bpp for transparency)", "png/P" : "Portable Network Graphics (lossy, 8bpp colour)", "png/L" : "Portable Network Graphics (lossy, 8bpp grayscale)", "webp" : "WebP compression (supports lossless and lossy modes)", "jpeg" : "JPEG lossy compression", "rgb" : "Raw RGB pixels, lossless," +" compressed using %s (24bpp or 32bpp for transparency)" % (" or ".join(compressors)), "scroll" : "motion vectors, supplemented with picture codecs", }.get(encoding)
def get_network_caps(): try: from xpra.platform.features import MMAP_SUPPORTED except: MMAP_SUPPORTED = False from xpra.net.crypto import get_digests, get_crypto_caps from xpra.net.compression import get_enabled_compressors, get_compression_caps from xpra.net.packet_encoding import get_enabled_encoders, get_packet_encoding_caps digests = get_digests() #"hmac" is the legacy name, "xor" and "des" should not be used for salt: salt_digests = [x for x in digests if x not in ("hmac", "xor", "des")] caps = { "digest": digests, "salt-digest": salt_digests, "compressors": get_enabled_compressors(), "encoders": get_enabled_encoders(), "mmap": MMAP_SUPPORTED, } caps.update(get_crypto_caps()) caps.update(get_compression_caps()) caps.update(get_packet_encoding_caps()) return caps
"png/L", "webp", "rgb", "rgb24", "rgb32", "jpeg", "h265", "vp9", ] # encoding order for edges (usually one pixel high or wide): EDGE_ENCODING_ORDER = ["rgb24", "rgb32", "jpeg", "png", "webp", "png/P", "png/L", "rgb"] from xpra.net import compression RGB_COMP_OPTIONS = ["Raw RGB"] if compression.get_enabled_compressors(): RGB_COMP_OPTIONS += ["/".join(compression.get_enabled_compressors())] ENCODINGS_TO_NAME = { "h264": "H.264", "h265": "H.265", "vp8": "VP8", "vp9": "VP9", "png": "PNG (24/32bpp)", "png/P": "PNG (8bpp colour)", "png/L": "PNG (8bpp grayscale)", "webp": "WebP", "jpeg": "JPEG", "rgb": " + ".join(RGB_COMP_OPTIONS) + " (24/32bpp)", }
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 enable_default_compressor(self): opts = compression.get_enabled_compressors() if len(opts) > 0: self.enable_compressor(opts[0]) else: self.enable_compressor("none")
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
ALL_CODECS = tuple(set(CSC_CODECS + ENCODER_CODECS + DECODER_CODECS)) #note: this is just for defining the order of encodings, #so we have both core encodings (rgb24/rgb32) and regular encodings (rgb) in here: PREFERED_ENCODING_ORDER = [ "h264", "vp9", "vp8", "mpeg4", "png", "png/P", "png/L", "webp", "rgb", "rgb24", "rgb32", "jpeg", "h265" ] #encoding order for edges (usually one pixel high or wide): EDGE_ENCODING_ORDER = [ "rgb24", "rgb32", "jpeg", "png", "webp", "png/P", "png/L", "rgb" ] from xpra.net import compression RGB_COMP_OPTIONS = ["Raw RGB"] if compression.get_enabled_compressors(): RGB_COMP_OPTIONS += ["/".join(compression.get_enabled_compressors())] ENCODINGS_TO_NAME = { "auto": "automatic", "h264": "H.264", "h265": "H.265", "mpeg4": "MPEG4", "vp8": "VP8", "vp9": "VP9", "png": "PNG (24/32bpp)", "png/P": "PNG (8bpp colour)", "png/L": "PNG (8bpp grayscale)", "webp": "WebP", "jpeg": "JPEG", "rgb": " + ".join(RGB_COMP_OPTIONS) + " (24/32bpp)",
CSC_CODECS = "csc_swscale", "csc_libyuv" ENCODER_CODECS = "enc_pillow", "enc_vpx", "enc_x264", "enc_x265", "nvenc7", "enc_ffmpeg" DECODER_CODECS = "dec_pillow", "dec_vpx", "dec_avcodec2" ALL_CODECS = tuple(set(CSC_CODECS + ENCODER_CODECS + DECODER_CODECS)) #note: this is just for defining the order of encodings, #so we have both core encodings (rgb24/rgb32) and regular encodings (rgb) in here: PREFERED_ENCODING_ORDER = ["h264", "vp9", "vp8", "mpeg4", "mpeg4+mp4", "h264+mp4", "mpeg4+mp4", "vp8+webm", "vp9+webm", "png", "png/P", "png/L", "rgb", "rgb24", "rgb32", "jpeg", "h265"] #encoding order for edges (usually one pixel high or wide): EDGE_ENCODING_ORDER = ["rgb24", "rgb32", "jpeg", "png", "png/P", "png/L", "rgb"] from xpra.net import compression RGB_COMP_OPTIONS = ["Raw RGB"] if compression.get_enabled_compressors(): RGB_COMP_OPTIONS += ["/".join(compression.get_enabled_compressors())] ENCODINGS_TO_NAME = { "auto" : "automatic", "h264" : "H.264", "h265" : "H.265", "mpeg4" : "MPEG4", "vp8" : "VP8", "vp9" : "VP9", "png" : "PNG (24/32bpp)", "png/P" : "PNG (8bpp colour)", "png/L" : "PNG (8bpp grayscale)", "jpeg" : "JPEG", "rgb" : " + ".join(RGB_COMP_OPTIONS) + " (24/32bpp)", }
def _process_control(self, packet): command = bytestostr(packet[1]) if command == "show_session_info": args = packet[2:] log("calling %s%s on server request", self.show_session_info, args) self.show_session_info(*args) elif command == "show_bug_report": self.show_bug_report() elif command in ("enable_%s" % x for x in compression.get_enabled_compressors()): compressor = command.split("_")[1] log.info("switching to %s on server request", compressor) self._protocol.enable_compressor(compressor) elif command in ("enable_%s" % x for x in packet_encoding.get_enabled_encoders()): pe = command.split("_")[1] log.info("switching to %s on server request", pe) self._protocol.enable_encoder(pe) elif command == "name": assert len(args) >= 3 self.server_session_name = args[2] log.info("session name updated from server: %s", self.server_session_name) #TODO: reset tray tooltip, session info title, etc.. elif command == "debug": args = packet[2:] if not args: log.warn("not enough arguments for debug control command") return from xpra.log import ( add_debug_category, add_disabled_category, enable_debug_for, disable_debug_for, get_all_loggers, ) log_cmd = bytestostr(args[0]) if log_cmd == "status": dloggers = [ x for x in get_all_loggers() if x.is_debug_enabled() ] if dloggers: log.info("logging is enabled for:") for l in dloggers: log.info(" - %s", l) else: log.info("logging is not enabled for any loggers") return log_cmd = bytestostr(args[0]) if log_cmd not in ("enable", "disable"): log.warn( "invalid debug control mode: '%s' (must be 'enable' or 'disable')", log_cmd) return if len(args) < 2: log.warn( "not enough arguments for '%s' debug control command" % log_cmd) return loggers = [] #each argument is a group groups = [bytestostr(x) for x in args[1:]] for group in groups: #and each group is a list of categories #preferably separated by "+", #but we support "," for backwards compatibility: categories = [ v.strip() for v in group.replace("+", ",").split(",") ] if log_cmd == "enable": add_debug_category(*categories) loggers += enable_debug_for(*categories) else: assert log_cmd == "disable" add_disabled_category(*categories) loggers += disable_debug_for(*categories) if not loggers: log.info("%s debugging, no new loggers matching: %s", log_cmd, csv(groups)) else: log.info("%sd debugging for:", log_cmd) for l in loggers: log.info(" - %s", l) else: log.warn("received invalid control command from server: %s", command)
CSC_CODECS = "csc_swscale", "csc_cython", "csc_opencl", "csc_libyuv", "csc_opencv" ENCODER_CODECS = "enc_pillow", "enc_vpx", "enc_webp", "enc_x264", "enc_x265", "nvenc7", "enc_xvid", "enc_ffmpeg" DECODER_CODECS = "dec_pillow", "dec_vpx", "dec_webp", "dec_avcodec2" ALL_CODECS = tuple(set(CSC_CODECS + ENCODER_CODECS + DECODER_CODECS)) #note: this is just for defining the order of encodings, #so we have both core encodings (rgb24/rgb32) and regular encodings (rgb) in here: PREFERED_ENCODING_ORDER = ["h264", "vp9", "vp8", "mpeg4", "mpeg4+mp4", "h264+mp4", "mpeg4+mp4", "vp8+webm", "vp9+webm", "png", "png/P", "png/L", "webp", "rgb", "rgb24", "rgb32", "jpeg", "h265"] #encoding order for edges (usually one pixel high or wide): EDGE_ENCODING_ORDER = ["rgb24", "rgb32", "jpeg", "png", "webp", "png/P", "png/L", "rgb"] from xpra.net import compression RGB_COMP_OPTIONS = ["Raw RGB"] if compression.get_enabled_compressors(): RGB_COMP_OPTIONS += ["/".join(compression.get_enabled_compressors())] ENCODINGS_TO_NAME = { "auto" : "automatic", "h264" : "H.264", "h265" : "H.265", "mpeg4" : "MPEG4", "vp8" : "VP8", "vp9" : "VP9", "png" : "PNG (24/32bpp)", "png/P" : "PNG (8bpp colour)", "png/L" : "PNG (8bpp grayscale)", "webp" : "WebP", "jpeg" : "JPEG", "rgb" : " + ".join(RGB_COMP_OPTIONS) + " (24/32bpp)",