def threaded_setup(self): #load the slower codecs if "jpeg" in self.allowed_encodings: load_codec("enc_nvjpeg") #load video codecs: getVideoHelper().init() self.init_encodings()
def test_all_codecs_found(self): #the self tests would swallow the exceptions and produce a warning: loader.RUN_SELF_TESTS = False #loader.load_codecs() #test them all: for codec_name in TEST_CODECS: loader.load_codec(codec_name) codec = loader.get_codec(codec_name) if not codec: print("WARNING: codec '%s' not found" % (codec_name, )) continue try: #try to suspend error logging for full tests, #as those may cause errors log = getattr(codec, "log", None) if SUSPEND_CODEC_ERROR_LOGGING and log and not log.is_debug_enabled( ): log.logger.setLevel(logging.CRITICAL) init_module = getattr(codec, "init_module", None) #print("%s.init_module=%s" % (codec, init_module)) if init_module: try: init_module() except Exception as e: print("cannot initialize %s: %s" % (codec, e)) print(" test skipped") continue #print("found %s: %s" % (codec_name, codec)) selftest = getattr(codec, "selftest", None) #print("selftest(%s)=%s" % (codec_name, selftest)) if selftest: selftest(True) finally: if log: log.logger.setLevel(logging.DEBUG)
def init_video_decoders_options(self): log("init_video_decoders_options()") log(" will try video decoders: %s", csv(self.video_decoders)) for x in self.video_decoders: try: mod = get_decoder_module_name(x) load_codec(mod) self.init_video_decoder_option(mod) except Exception: log.warn("Warning: cannot add %s decoder", x, exc_info=True) log("found %s video decoder%s: %s", len(self._video_decoder_specs), engs(self._video_decoder_specs), csv(self._video_decoder_specs)) log("video decoder options: %s", self._video_decoder_specs)
def video_init(self): enclog("video_init() loading codecs") enclog("video_init() loading pillow encoder") load_codec("enc_pillow") enclog("video_init() will try video encoders: %s", csv(self.video_encoder_modules) or "none") 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! #we want to win scoring so we get used ahead of other encoders: spec_props["score_boost"] = 50 #limit to 3 video streams we proxy for (we really want 2, # but because of races with garbage collection, we need to allow more) spec_props["max_instances"] = 3 #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 = list(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", csv(self.video_encoder_types or [ "none", ])) self.timeout_add(VIDEO_TIMEOUT * 1000, self.timeout_video_encoders)
def init_csc_options(self): log("init_csc_options()") log(" will try csc modules: %s", csv(self.csc_modules)) for x in self.csc_modules: try: mod = get_csc_module_name(x) load_codec(mod) self.init_csc_option(mod) except Exception: log.warn("init_csc_options() cannot add %s csc", x, exc_info=True) log(" csc specs: %s", csv(self._csc_encoder_specs)) for src_format, d in sorted(self._csc_encoder_specs.items()): log(" %s - %s options:", src_format, len(d)) for dst_format, specs in sorted(d.items()): log(" * %7s via: %s", dst_format, csv(sorted(spec.codec_type for spec in specs))) log("csc options: %s", self._csc_encoder_specs)
def _test_csc(self, mod, width=16, height=16, in_csc="BGRX", out_csc="YUV420P", pixel="00000000", expected=()): csc_mod = loader.load_codec(mod) if not csc_mod: print("%s not found" % mod) return if in_csc not in csc_mod.get_input_colorspaces(): raise Exception("%s does not support %s as input" % (mod, in_csc)) if out_csc not in csc_mod.get_output_colorspaces(in_csc): raise Exception("%s does not support %s as output for %s" % (mod, out_csc, in_csc)) csc = csc_mod.ColorspaceConverter() csc.init_context(width, height, in_csc, width, height, out_csc) image = make_test_image(in_csc, width, height) size = image.get_rowstride()//4*image.get_height() bgrx = h2b(pixel)*size image.set_pixels(bgrx) out_image = csc.convert_image(image) csc.clean() assert out_image.get_planes()>=len(expected) #now verify the value for each plane specified: for i, v_str in enumerate(expected): plane = out_image.get_pixels()[i] #plane_stride = out_image.get_rowstride()[i] #assert len(plane)>=plane_stride*out_image.get_height() plane_bytes = memoryview_to_bytes(plane) v = h2b(v_str) if not cmpp(plane_bytes, v): raise Exception("%s: plane %s, expected %s but got %s" % ( mod, out_csc[i], v_str, hexstr(plane_bytes[:len(v)])))
def init_video_encoders_options(self): log("init_video_encoders_options()") log(" will try video encoders: %s", csv(self.video_encoders)) for x in self.video_encoders: try: mod = get_encoder_module_name(x) load_codec(mod) log(" encoder for %s: %s", x, mod) try: self.init_video_encoder_option(mod) except Exception as e: log(" init_video_encoder_option(%s) error", mod, exc_info=True) log.warn("Warning: cannot load %s video encoder:", mod) log.warn(" %s", e) del e except Exception as e: log("error on %s", x, exc_info=True) log.warn("Warning: cannot add %s encoder: %s", x, e) del e log("found %i video encoder%s: %s", len(self._video_encoder_specs), engs(self._video_encoder_specs), csv(self._video_encoder_specs))
def threaded_setup(self): #load video codecs: getVideoHelper().init() #and load the picture codecs: load_codec("enc_pillow") ae = self.allowed_encodings if "jpeg" in ae: #try to load the fast jpeg encoders: load_codec("enc_jpeg") load_codec("enc_nvjpeg") if "webp" in ae: #try to load the fast webp encoder: load_codec("enc_webp") self.init_encodings()
def test_YUV420P(self): for encoding, encoder_name, decoder_name in ( ("vp8", "enc_vpx", "dec_vpx"), ("vp9", "enc_vpx", "dec_vpx"), ("vp8", "enc_vpx", "dec_avcodec2"), ("vp9", "enc_vpx", "dec_avcodec2"), ("h264", "enc_x264", "dec_avcodec2"), #("h265", "enc_x265", "dec_avcodec2"), ): encoder = loader.load_codec(encoder_name) if not encoder: print("%s not found" % encoder_name) continue decoder = loader.load_codec(decoder_name) if not decoder: print("%s not found" % decoder_name) continue for colour, yuvdata in SAMPLE_YUV420P_IMAGES.items(): try: self._test_YUV420P(encoding, encoder, decoder, yuvdata) except Exception: print("error with %s %s image via %s and %s" % (colour, encoding, encoder_name, decoder_name)) raise
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 test_gl_client_window(gl_client_window_class, max_window_size=(1024, 1024), pixel_depth=24, show=False): #try to render using a temporary window: draw_result = {} window = None try: x, y = -100, -100 if show: x, y = 100, 100 w, h = 250, 250 from xpra.codecs.loader import load_codec load_codec("dec_pillow") from xpra.client.window_border import WindowBorder border = WindowBorder() default_cursor_data = None noclient = FakeClient() #test with alpha, but not on win32 #because we can't do alpha on win32 with opengl metadata = typedict({b"has-alpha": not WIN32}) class NoHeaderGLClientWindow(gl_client_window_class): def add_header_bar(self): pass def schedule_recheck_focus(self): pass window = NoHeaderGLClientWindow(noclient, None, None, 2**32 - 1, x, y, w, h, w, h, metadata, False, typedict({}), border, max_window_size, default_cursor_data, pixel_depth) window_backing = window._backing window_backing.idle_add = no_idle_add window_backing.timeout_add = no_timeout_add window_backing.source_remove = no_source_remove window.realize() window_backing.paint_screen = True pixel_format = "BGRX" bpp = len(pixel_format) options = typedict({"pixel_format": pixel_format}) stride = bpp * w coding = "rgb32" widget = window_backing._backing widget.realize() def paint_callback(success, message=""): log("paint_callback(%s, %s)", success, message) draw_result["success"] = success if message: draw_result["message"] = message.replace("\n", " ") log("OpenGL: testing draw on %s widget %s with %s : %s", window, widget, coding, pixel_format) pix = AtomicInteger(0x7f) REPAINT_DELAY = envint("XPRA_REPAINT_DELAY", int(show) * 16) gl_icon = get_icon_filename("opengl", ext="png") icon_data = None if os.path.exists(gl_icon): from PIL import Image img = Image.open(gl_icon) img.load() icon_w, icon_h = img.size icon_stride = icon_w * 4 noalpha = Image.new("RGB", img.size, (255, 255, 255)) noalpha.paste(img, mask=img.split()[3]) # 3 is the alpha channel buf = BytesIO() noalpha.save(buf, format="JPEG") icon_data = buf.getvalue() buf.close() icon_format = "jpeg" if not icon_data: icon_w = 32 icon_h = 32 icon_stride = icon_w * 4 icon_data = bytes([0]) * icon_stride * icon_h icon_format = "rgb32" def draw(): v = pix.increase() img_data = bytes([v % 256] * stride * h) options["flush"] = 1 window.draw_region(0, 0, w, h, coding, img_data, stride, v, options, [paint_callback]) options["flush"] = 0 mx = w // 2 - icon_w // 2 my = h // 2 - icon_h // 2 x = iround(mx * (1 + sin(v / 100))) y = iround(my * (1 + cos(v / 100))) window.draw_region(x, y, icon_w, icon_h, icon_format, icon_data, icon_stride, v, options, [paint_callback]) return REPAINT_DELAY > 0 #the paint code is actually synchronous here, #so we can check the present_fbo() result: if show: widget.show() window.show() from gi.repository import Gtk, GLib def window_close_event(*_args): Gtk.main_quit() noclient.window_close_event = window_close_event GLib.timeout_add(REPAINT_DELAY, draw) Gtk.main() else: draw() if window_backing.last_present_fbo_error: return { "success": False, "message": "failed to present FBO on screen: %s" % window_backing.last_present_fbo_error } finally: if window: window.destroy() log("test_gl_client_window(..) draw_result=%s", draw_result) return draw_result
def setup(self): #essential codecs, load them early: load_codec("enc_rgb") load_codec("enc_pillow") ae = self.allowed_encodings if "webp" in ae: #try to load the fast webp encoder: load_codec("enc_webp") if "png" in ae: #try to load the fast png encoder: load_codec("enc_spng") if "jpeg" in ae: #try to load the fast jpeg encoders: load_codec("enc_jpeg") if "avif" in ae: load_codec("enc_avif") self.init_encodings() self.add_init_thread_callback(self.reinit_encodings)
def setup(self): #always load pillow early, #so we have png and jpeg support before calling threaded_setup load_codec("enc_pillow") self.init_encodings()
def main(fmt="png", files=()): assert len(files) > 0, "specify images to benchmark" from xpra.net import compression compression.init_all() from xpra.codecs.loader import load_codec, get_codec encoders = [] for codec in CODECS: load_codec(codec) enc = get_codec(codec) if enc and (fmt == "all" or fmt in enc.get_encodings()): encoders.append(enc) from PIL import Image for f in files: img = Image.open(f) if img.mode not in ("RGBA", "RGB"): img = img.convert("RGB") pixel_format = img.mode w, h = img.size rgb_data = img.tobytes("raw") stride = w * len(pixel_format) print("%s : %s" % (f, img)) image = ImageWrapper(0, 0, w, h, rgb_data, pixel_format, len(pixel_format) * 8, stride, planes=ImageWrapper.PACKED, thread_safe=True) for enc in encoders: if fmt == "all": encodings = enc.get_encodings() else: encodings = (fmt, ) for encoding in encodings: size = 0 start = monotonic() for _ in range(N): try: r = enc.encode(encoding, image, options) except Exception: print("error on %s %s" % (enc.get_type(), enc.encode)) raise if not r: print("Error: no data for %s %s" % (enc.get_type(), enc.encode)) break size += len(r[1]) if not r: continue end = monotonic() cdata = r[1] ratio = 100 * len(cdata) / len(rgb_data) print( "%-10s %-10s : %10.1f MPixels/s size=%8iKB %3.1f%%" % (encoding, enc.get_type(), w * h * N / (end - start) / 1024 / 1024, size * N / 1024, ratio)) #verify that the png data is valid using pillow: if encoding not in ("rgb24", "rgb32", "avif"): from io import BytesIO buf = BytesIO(cdata.data) img = Image.open(buf)