Example #1
0
 def __init__(self, wid : int, window_alpha : bool):
     load_csc_options()
     load_video_decoders()
     self.wid = wid
     self.size = 0, 0
     self.render_size = 0, 0
     #padding between the window contents and where we actually draw the backing
     #(ie: if the window is bigger than the backing,
     # we may be rendering the backing in the center of the window)
     self.offsets = 0, 0, 0, 0       #top,left,bottom,right
     self.gravity = 0
     self._alpha_enabled = window_alpha
     self._backing = None
     self._video_decoder = None
     self._csc_decoder = None
     self._decoder_lock = Lock()
     self._PIL_encodings = []
     self.default_paint_box_line_width = PAINT_BOX or 1
     self.paint_box_line_width = PAINT_BOX
     self.pointer_overlay = None
     self.cursor_data = None
     self.default_cursor_data = None
     self.jpeg_decoder = None
     self.webp_decoder = None
     self.pil_decoder = get_codec("dec_pillow")
     if self.pil_decoder:
         self._PIL_encodings = self.pil_decoder.get_encodings()
     self.jpeg_decoder = get_codec("dec_jpeg")
     self.webp_decoder = get_codec("dec_webp")
     self.draw_needs_refresh = True
     self.repaint_all = REPAINT_ALL
     self.mmap = None
     self.mmap_enabled = False
Example #2
0
 def __init__(self, wid, window_alpha):
     load_csc_options()
     load_video_decoders()
     self.wid = wid
     self.size = 0, 0
     self.render_size = 0, 0
     self.offsets = 0, 0, 0, 0       #top,left,bottom,right
     self._alpha_enabled = window_alpha
     self._backing = None
     self._delta_pixel_data = [None for _ in range(DELTA_BUCKETS)]
     self._video_decoder = None
     self._csc_decoder = None
     self._decoder_lock = Lock()
     self._PIL_encodings = []
     self.default_paint_box_line_width = PAINT_BOX or 1
     self.paint_box_line_width = PAINT_BOX
     self.pointer_overlay = None
     self.cursor_data = None
     self.default_cursor_data = None
     self.jpeg_decoder = None
     self.webp_decoder = None
     if is_loaded():
         PIL = get_codec("dec_pillow")
         if PIL:
             self._PIL_encodings = PIL.get_encodings()
         self.jpeg_decoder = get_codec("dec_jpeg")
         self.webp_decoder = get_codec("dec_webp")
     self.draw_needs_refresh = True
     self.mmap = None
     self.mmap_enabled = False
Example #3
0
def webp_encode(coding, image, supports_transparency, quality, speed, options):
    stride = image.get_rowstride()
    enc_webp = get_codec("enc_webp")
    if enc_webp and stride>0 and stride%4==0 and image.get_pixel_format() in ("BGRA", "BGRX"):
        #prefer Cython module:
        alpha = supports_transparency and image.get_pixel_format().find("A")>=0
        w = image.get_width()
        h = image.get_height()
        if quality==100:
            #webp lossless is unbearibly slow for only marginal compression improvements,
            #so force max speed:
            speed = 100
        else:
            #normalize speed for webp: avoid low speeds!
            speed = int(sqrt(speed) * 10)
        speed = max(0, min(100, speed))
        cdata = enc_webp.compress(image.get_pixels(), w, h, stride=stride/4, quality=quality, speed=speed, has_alpha=alpha)
        client_options = {"speed" : speed}
        if quality>=0 and quality<=100:
            client_options["quality"] = quality
        if alpha:
            client_options["has_alpha"] = True
        return "webp", Compressed("webp", cdata), client_options, image.get_width(), image.get_height(), 0, 24
    enc_webm = get_codec("enc_webm")
    webp_handlers = get_codec("webm_bitmap_handlers")
    if enc_webm and webp_handlers:
        return webm_encode(image, quality)
    #fallback to PIL
    return PIL_encode(coding, image, quality, speed, supports_transparency)
Example #4
0
def webp_encode(image, supports_transparency, quality, speed, content_type):
    stride = image.get_rowstride()
    pixel_format = image.get_pixel_format()
    enc_webp = get_codec("enc_webp")
    #log("WEBP_PILLOW=%s, enc_webp=%s, stride=%s, pixel_format=%s", WEBP_PILLOW, enc_webp, stride, pixel_format)
    if not WEBP_PILLOW and enc_webp and stride > 0 and stride % 4 == 0 and pixel_format in (
            "BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        cdata, client_options = enc_webp.encode(image, quality, speed,
                                                supports_transparency,
                                                content_type)
        return "webp", compression.Compressed(
            "webp", cdata), client_options, image.get_width(
            ), image.get_height(), 0, 24
    #fallback using Pillow:
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        if not WEBP_PILLOW:
            log.warn("Warning: using PIL fallback for webp")
            log.warn(" enc_webp=%s, stride=%s, pixel format=%s", enc_webp,
                     stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed,
                                         supports_transparency)
    raise Exception(
        "BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!"
    )
Example #5
0
def webp_encode(coding, image, rgb_formats, supports_transparency, quality,
                speed, options):
    pixel_format = image.get_pixel_format()
    #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats)
    if pixel_format not in rgb_formats:
        if not rgb_reformat(image, rgb_formats, supports_transparency):
            raise Exception(
                "cannot find compatible rgb format to use for %s! (supported: %s)"
                % (pixel_format, rgb_formats))
        #get the new format:
        pixel_format = image.get_pixel_format()
    stride = image.get_rowstride()
    enc_webp = get_codec("enc_webp")
    #log("webp_encode%s stride=%s, enc_webp=%s", (coding, image, rgb_formats, supports_transparency, quality, speed, options), stride, enc_webp)
    if enc_webp and stride > 0 and stride % 4 == 0 and image.get_pixel_format(
    ) in ("BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        alpha = supports_transparency and image.get_pixel_format().find(
            "A") >= 0
        w = image.get_width()
        h = image.get_height()
        if quality == 100:
            #webp lossless is unbearibly slow for only marginal compression improvements,
            #so force max speed:
            speed = 100
        else:
            #normalize speed for webp: avoid low speeds!
            speed = int(sqrt(speed) * 10)
        speed = max(0, min(100, speed))
        pixels = image.get_pixels()
        assert pixels, "failed to get pixels from %s" % image
        cdata = enc_webp.compress(pixels,
                                  w,
                                  h,
                                  stride=stride / 4,
                                  quality=quality,
                                  speed=speed,
                                  has_alpha=alpha)
        client_options = {"speed": speed, "rgb_format": pixel_format}
        if quality >= 0 and quality <= 100:
            client_options["quality"] = quality
        if alpha:
            client_options["has_alpha"] = True
        return "webp", compression.Compressed(
            "webp", cdata), client_options, image.get_width(
            ), image.get_height(), 0, 24
    #fallback to PIL
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        log(
            "using PIL fallback for webp: enc_webp=%s, stride=%s, pixel format=%s",
            enc_webp, stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed,
                                         supports_transparency)
    raise Exception(
        "BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!"
    )
Example #6
0
 def init_video_encoder_option(self, encoder_name):
     encoder_module = get_codec(encoder_name)
     log("init_video_encoder_option(%s)", encoder_name)
     log(" module=%s", encoder_module)
     if not encoder_module:
         log(" video encoder '%s' could not be loaded:", encoder_name)
         log(" %s", get_codec_error(encoder_name))
         return
     encoder_type = encoder_module.get_type()
     try:
         encoder_module.init_module()
         self._cleanup_modules.append(encoder_module)
     except Exception as e:
         log(" exception in %s module %s initialization %s: %s",
             encoder_type,
             encoder_module.__name__,
             encoder_module.init_module,
             e,
             exc_info=True)
         raise
     encodings = encoder_module.get_encodings()
     log(" %s encodings=%s", encoder_type, csv(encodings))
     for encoding in encodings:
         colorspaces = encoder_module.get_input_colorspaces(encoding)
         log(" %s input colorspaces for %s: %s", encoder_type, encoding,
             csv(colorspaces))
         for colorspace in colorspaces:
             spec = encoder_module.get_spec(encoding, colorspace)
             self.add_encoder_spec(encoding, colorspace, spec)
Example #7
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     log("init_csc_option(%s)", csc_name)
     log(" module=%s", csc_module)
     if csc_module is None:
         log(" csc module %s could not be loaded:", csc_name)
         log(" %s", get_codec_error(csc_name))
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
         self._cleanup_modules.append(csc_module)
     except Exception as e:
         log("exception in %s module initialization %s: %s",
             csc_type,
             csc_module.init_module,
             e,
             exc_info=True)
         log.warn("Warning: cannot use %s module %s: %s", csc_type,
                  csc_module, e)
         return
     in_cscs = csc_module.get_input_colorspaces()
     for in_csc in in_cscs:
         out_cscs = csc_module.get_output_colorspaces(in_csc)
         log("%s output colorspaces for %s: %s", csc_module.get_type(),
             in_csc, csv(out_cscs))
         for out_csc in out_cscs:
             spec = csc_module.get_spec(in_csc, out_csc)
             self.add_csc_spec(in_csc, out_csc, spec)
Example #8
0
 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
Example #9
0
 def paint_image(self, coding, img_data, x, y, width, height, options,
                 callbacks):
     """ can be called from any thread """
     #log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
     PIL = get_codec("PIL")
     assert PIL, "PIL not found"
     buf = BytesIOClass(img_data)
     img = PIL.Image.open(buf)
     assert img.mode in ("L", "P", "RGB",
                         "RGBA"), "invalid image mode: %s" % img.mode
     if img.mode in ("P", "L"):
         transparency = options.get("transparency", -1)
         if transparency >= 0:
             img = img.convert("RGBA")
         else:
             img = img.convert("RGB")
     raw_data = img.tostring("raw", img.mode)
     if img.mode == "RGB":
         #PIL flattens the data to a continuous straightforward RGB format:
         rowstride = width * 3
         img_data = self.process_delta(raw_data, width, height, rowstride,
                                       options)
         self.idle_add(self.do_paint_rgb24, img_data, x, y, width, height,
                       rowstride, options, callbacks)
     elif img.mode == "RGBA":
         rowstride = width * 4
         img_data = self.process_delta(raw_data, width, height, rowstride,
                                       options)
         self.idle_add(self.do_paint_rgb32, img_data, x, y, width, height,
                       rowstride, options, callbacks)
     return False
Example #10
0
 def paint_webp(self, img_data, x, y, width, height, options, callbacks):
     dec_webp = get_codec("dec_webp")
     if dec_webp:
         return self.paint_webp_using_cwebp(img_data, x, y, width, height,
                                            options, callbacks)
     return self.paint_webp_using_webm(img_data, x, y, width, height,
                                       options, callbacks)
Example #11
0
 def test_all_codecs_found(self):
     from xpra.codecs import loader
     #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 loader.ALL_CODECS:
         codec = loader.get_codec(codec_name)
         if not codec:
             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:
                 import logging
                 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)
Example #12
0
 def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     #log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
     PIL = get_codec("PIL")
     assert PIL, "PIL not found"
     buf = BytesIOClass(img_data)
     img = PIL.Image.open(buf)
     assert img.mode in ("L", "P", "RGB", "RGBA"), "invalid image mode: %s" % img.mode
     if img.mode in ("P", "L"):
         transparency = options.get("transparency", -1)
         if transparency>=0:
             img = img.convert("RGBA")
         else:
             img = img.convert("RGB")
     raw_data = img.tostring("raw", img.mode)
     if img.mode=="RGB":
         #PIL flattens the data to a continuous straightforward RGB format:
         rowstride = width*3
         img_data = self.process_delta(raw_data, width, height, rowstride, options)
         self.idle_add(self.do_paint_rgb24, img_data, x, y, width, height, rowstride, options, callbacks)
     elif img.mode=="RGBA":
         rowstride = width*4
         img_data = self.process_delta(raw_data, width, height, rowstride, options)
         self.idle_add(self.do_paint_rgb32, img_data, x, y, width, height, rowstride, options, callbacks)
     return False
Example #13
0
def load_csc_options():
    global CSC_OPTIONS
    if CSC_OPTIONS is None:
        CSC_OPTIONS = {}
        opts = [x.strip() for x in XPRA_CLIENT_CSC.split(",")]
        log("load_csc_options() module options=%s", opts)
        for opt in opts:
            csc_module = get_codec("csc_%s" % opt)
            if not csc_module:
                log.warn("csc module %s not found", opt)
                continue
            log("csc_module(%s)=%s", opt, csc_module)
            try:
                in_cscs = csc_module.get_input_colorspaces()
                log("input colorspaces(%s)=%s", csc_module, in_cscs)
                for in_csc in in_cscs:
                    in_opts = CSC_OPTIONS.setdefault(in_csc, {})
                    out_cscs = csc_module.get_output_colorspaces(in_csc)
                    log("output colorspaces(%s, %s)=%s", csc_module, in_csc,
                        out_cscs)
                    for out_csc in out_cscs:
                        spec = csc_module.get_spec(in_csc, out_csc)
                        specs = in_opts.setdefault(out_csc, [])
                        specs.append(spec)
                        log("specs(%s, %s)=%s", in_csc, out_csc, specs)
            except:
                log.warn("failed to load csc module %s",
                         csc_module,
                         exc_info=True)
Example #14
0
 def init_video_decoder_option(self, decoder_name):
     decoder_module = get_codec(decoder_name)
     log("init_video_decoder_option(%s)", decoder_name)
     log(" module=%s", decoder_module)
     if not decoder_module:
         log(" video decoder %s could not be loaded:", decoder_name)
         log(" %s", get_codec_error(decoder_name))
         return
     decoder_type = decoder_module.get_type()
     try:
         decoder_module.init_module()
         self._cleanup_modules.append(decoder_module)
     except Exception as e:
         log("exception in %s module initialization %s: %s", decoder_type, decoder_module.init_module, e, exc_info=True)
         log.warn("Warning: cannot use %s module %s: %s", decoder_type, decoder_module, e, exc_info=True)
         return
     encodings = decoder_module.get_encodings()
     log(" %s encodings=%s", decoder_type, csv(encodings))
     for encoding in encodings:
         colorspaces = decoder_module.get_input_colorspaces(encoding)
         log(" %s input colorspaces for %s: %s", decoder_type, encoding, csv(colorspaces))
         for colorspace in colorspaces:
             output_colorspace = decoder_module.get_output_colorspace(encoding, colorspace)
             log(" %s output colorspace for %s/%s: %s", decoder_type, encoding, colorspace, output_colorspace)
             try:
                 assert decoder_module.Decoder
                 self.add_decoder(encoding, colorspace, decoder_name, decoder_module)
             except Exception as e:
                 log.warn("failed to add decoder %s: %s", decoder_module, e)
                 del e
Example #15
0
def load_csc_options():
    global CSC_OPTIONS
    if CSC_OPTIONS is None:
        CSC_OPTIONS = {}
        opts = [x.strip() for x in XPRA_CLIENT_CSC.split(",")]
        log("load_csc_options() module options=%s", opts)
        for opt in opts:
            csc_module = get_codec("csc_%s" % opt)
            if not csc_module:
                log.warn("csc module %s not found", opt)
                continue
            log("csc_module(%s)=%s", opt, csc_module)
            try:
                in_cscs = csc_module.get_input_colorspaces()
                log("input colorspaces(%s)=%s", csc_module, in_cscs)
                for in_csc in in_cscs:
                    in_opts = CSC_OPTIONS.setdefault(in_csc, {})
                    out_cscs = csc_module.get_output_colorspaces(in_csc)
                    log("output colorspaces(%s, %s)=%s", csc_module, in_csc, out_cscs)
                    for out_csc in out_cscs:
                        spec = csc_module.get_spec(in_csc, out_csc)
                        specs = in_opts.setdefault(out_csc, [])
                        specs.append(spec)
                        log("specs(%s, %s)=%s", in_csc, out_csc, specs)
            except:
                log.warn("failed to load csc module %s", csc_module, exc_info=True)
Example #16
0
 def do_send_cursor(self, delay):
     self.cursor_timer = None
     cd = self.get_cursor_data_cb()
     if cd and cd[0]:
         cursor_data = list(cd[0])
         cursor_sizes = cd[1]
         #skip first two fields (if present) as those are coordinates:
         if self.last_cursor_sent and self.last_cursor_sent[
                 2:9] == cursor_data[2:9]:
             cursorlog(
                 "do_send_cursor(..) cursor identical to the last one we sent, nothing to do"
             )
             return
         self.last_cursor_sent = cursor_data[:9]
         w, h, _xhot, _yhot, serial, pixels, name = cursor_data[2:9]
         #compress pixels if needed:
         encoding = None
         if pixels is not None:
             #convert bytearray to string:
             cpixels = strtobytes(pixels)
             if "png" in self.cursor_encodings:
                 from xpra.codecs.loader import get_codec
                 PIL = get_codec("PIL")
                 assert PIL
                 cursorlog(
                     "do_send_cursor() loading %i bytes of cursor pixel data for %ix%i cursor named '%s'",
                     len(cpixels), w, h, name)
                 img = PIL.Image.frombytes("RGBA", (w, h), cpixels, "raw",
                                           "BGRA", w * 4, 1)
                 buf = BytesIOClass()
                 img.save(buf, "PNG")
                 pngdata = buf.getvalue()
                 buf.close()
                 cpixels = Compressed("png cursor",
                                      pngdata,
                                      can_inline=True)
                 encoding = "png"
                 if SAVE_CURSORS:
                     with open("raw-cursor-%#x.png" % serial, "wb") as f:
                         f.write(pngdata)
             elif len(cpixels) >= 256 and ("raw" in self.cursor_encodings
                                           or not self.cursor_encodings):
                 cpixels = self.compressed_wrapper("cursor", pixels)
                 cursorlog("do_send_cursor(..) pixels=%s ", cpixels)
                 encoding = "raw"
             cursor_data[7] = cpixels
         cursorlog(
             "do_send_cursor(..) %sx%s %s cursor name='%s', serial=%#x with delay=%s (cursor_encodings=%s)",
             w, h, (encoding or "empty"), name, serial, delay,
             self.cursor_encodings)
         args = list(cursor_data[:9]) + [cursor_sizes[0]] + list(
             cursor_sizes[1])
         if self.cursor_encodings and encoding:
             args = [encoding] + args
     else:
         cursorlog("do_send_cursor(..) sending empty cursor with delay=%s",
                   delay)
         args = [""]
         self.last_cursor_sent = None
     self.send_more("cursor", *args)
Example #17
0
 def test_all_codecs_found(self):
     from xpra.codecs import loader
     #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 loader.ALL_CODECS:
         codec = loader.get_codec(codec_name)
         if not codec:
             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:
                 import logging
                 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)
Example #18
0
 def init_video_decoder_option(self, decoder_name):
     decoder_module = get_codec(decoder_name)
     log("init_video_decoder_option(%s)", decoder_name)
     log(" module=%s", decoder_module)
     if not decoder_module:
         log(" video decoder %s could not be loaded:", decoder_name)
         log(" %s", get_codec_error(decoder_name))
         return
     decoder_type = decoder_module.get_type()
     encodings = decoder_module.get_encodings()
     log(" %s encodings=%s", decoder_type, csv(encodings))
     for encoding in encodings:
         colorspaces = decoder_module.get_input_colorspaces(encoding)
         log(" %s input colorspaces for %s: %s", decoder_type, encoding,
             csv(colorspaces))
         for colorspace in colorspaces:
             output_colorspace = decoder_module.get_output_colorspace(
                 encoding, colorspace)
             log(" %s output colorspace for %5s/%7s: %s", decoder_type,
                 encoding, colorspace, output_colorspace)
             try:
                 assert decoder_module.Decoder
                 self.add_decoder(encoding, colorspace, decoder_name,
                                  decoder_module)
             except Exception as e:
                 log.warn("failed to add decoder %s: %s", decoder_module, e)
                 del e
Example #19
0
 def init_video_decoder_option(self, decoder_name):
     decoder_module = get_codec(decoder_name)
     log("init_video_decoder_option(%s)", decoder_name)
     log(" module=%s", decoder_module)
     if not decoder_module:
         log(" video decoder %s could not be loaded:", decoder_name)
         log(" %s", get_codec_error(decoder_name))
         return
     decoder_type = decoder_module.get_type()
     try:
         decoder_module.init_module()
         self._cleanup_modules.append(decoder_module)
     except Exception as e:
         log("exception in %s module initialization %s: %s", decoder_type, decoder_module.init_module, e, exc_info=True)
         log.warn("Warning: cannot use %s module %s: %s", decoder_type, decoder_module, e, exc_info=True)
         return
     encodings = decoder_module.get_encodings()
     log(" %s encodings=%s", decoder_type, csv(encodings))
     for encoding in encodings:
         colorspaces = decoder_module.get_input_colorspaces(encoding)
         log(" %s input colorspaces for %s: %s", decoder_type, encoding, csv(colorspaces))
         for colorspace in colorspaces:
             output_colorspace = decoder_module.get_output_colorspace(encoding, colorspace)
             log(" %s output colorspace for %s/%s: %s", decoder_type, encoding, colorspace, output_colorspace)
             try:
                 assert decoder_module.Decoder
                 self.add_decoder(encoding, colorspace, decoder_name, decoder_module)
             except Exception as e:
                 log.warn("failed to add decoder %s: %s", decoder_module, e)
Example #20
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y,
                             width, height, rowstride, rgb_format):
     log("nasty_rgb_via_png_paint%s",
         (cairo_format, has_alpha, len(img_data), x, y, width, height,
          rowstride, rgb_format))
     #PIL fallback
     PIL = get_codec("PIL")
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     img = PIL.Image.frombytes(oformat, (width, height), bdata, "raw",
                               rgb_format, rowstride, 1)
     #This is insane, the code below should work, but it doesn't:
     # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
     # pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
     # success = self.cairo_paint_pixbuf(pixbuf, x, y)
     #So we still rountrip via PNG:
     png = BytesIOClass()
     img.save(png, format="PNG")
     reader = BytesIOClass(png.getvalue())
     png.close()
     img = cairo.ImageSurface.create_from_png(reader)
     self.cairo_paint_surface(img, x, y)
     return True
Example #21
0
 def nasty_rgb_via_png_paint(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, rgb_format):
     log("nasty_rgb_via_png_paint%s", (cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, rgb_format))
     #PIL fallback
     PIL = get_codec("PIL")
     if has_alpha:
         oformat = "RGBA"
     else:
         oformat = "RGB"
     #use frombytes rather than frombuffer to be compatible with python3 new-style buffers
     #this is slower, but since this codepath is already dreadfully slow, we don't care
     bdata = strtobytes(memoryview_to_bytes(img_data))
     try:
         img = PIL.Image.frombytes(oformat, (width,height), bdata, "raw", rgb_format.replace("X", "A"), rowstride, 1)
     except ValueError as e:
         raise Exception("failed to parse raw %s data to %s: %s" % (rgb_format, oformat, e))
     #This is insane, the code below should work, but it doesn't:
     # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
     # pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
     # success = self.cairo_paint_pixbuf(pixbuf, x, y)
     #So we still rountrip via PNG:
     png = BytesIOClass()
     img.save(png, format="PNG")
     reader = BytesIOClass(png.getvalue())
     png.close()
     img = cairo.ImageSurface.create_from_png(reader)
     self.cairo_paint_surface(img, x, y)
     return True
Example #22
0
    def paint_image(self, coding, img_data, x, y, width, height, options,
                    callbacks):
        """ can be called from any thread """
        #log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
        PIL = get_codec("PIL")
        assert PIL, "PIL not found"
        buf = BytesIOClass(img_data)
        img = PIL.Image.open(buf)
        assert img.mode in ("L", "P", "RGB",
                            "RGBA"), "invalid image mode: %s" % img.mode
        transparency = options.get("transparency", -1)
        if img.mode == "P":
            if transparency >= 0:
                #this deals with alpha without any extra work
                img = img.convert("RGBA")
            else:
                img = img.convert("RGB")
        elif img.mode == "L":
            if transparency >= 0:
                #why do we have to deal with alpha ourselves??
                def mask_value(a):
                    if a != transparency:
                        return 255
                    return 0

                mask = PIL.Image.eval(img, mask_value)
                mask = mask.convert("L")

                def nomask_value(a):
                    if a != transparency:
                        return a
                    return 0

                img = PIL.Image.eval(img, nomask_value)
                img = img.convert("RGBA")
                img.putalpha(mask)
            else:
                img = img.convert("RGB")

        #use tobytes() if present, fallback to tostring():
        data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
        raw_data = data_fn("raw", img.mode)
        paint_options = typedict(options)
        if img.mode == "RGB":
            #PIL flattens the data to a continuous straightforward RGB format:
            rowstride = width * 3
            paint_options["rgb_format"] = "RGB"
            img_data = self.process_delta(raw_data, width, height, rowstride,
                                          options)
            self.idle_add(self.do_paint_rgb24, img_data, x, y, width, height,
                          rowstride, paint_options, callbacks)
        elif img.mode == "RGBA":
            rowstride = width * 4
            paint_options["rgb_format"] = "RGBA"
            img_data = self.process_delta(raw_data, width, height, rowstride,
                                          options)
            self.idle_add(self.do_paint_rgb32, img_data, x, y, width, height,
                          rowstride, paint_options, callbacks)
        return False
Example #23
0
def rgb_reformat(image, rgb_formats, supports_transparency):
    """ convert the RGB pixel data into a format supported by the client """
    #need to convert to a supported format!
    PIL = get_codec("PIL")
    pixel_format = image.get_pixel_format()
    pixels = image.get_pixels()
    assert pixels, "failed to get pixels from %s" % image
    if not PIL or pixel_format == "r210":
        #try to fallback to argb module
        #(required for r210 which is not handled by PIL directly)
        log("rgb_reformat: using argb_swap for %s", image)
        return argb_swap(image, rgb_formats, supports_transparency)
    if supports_transparency:
        modes = PIL_conv.get(pixel_format)
    else:
        modes = PIL_conv_noalpha.get(pixel_format)
    assert modes, "no PIL conversion from %s" % (pixel_format)
    target_rgb = [(im, om) for (im, om) in modes if om in rgb_formats]
    if len(target_rgb) == 0:
        log("rgb_reformat: no matching target modes for converting %s to %s",
            image, rgb_formats)
        #try argb module:
        if argb_swap(image, rgb_formats, supports_transparency):
            return True
        warning_key = "rgb_reformat(%s, %s, %s)" % (pixel_format, rgb_formats,
                                                    supports_transparency)
        warn_encoding_once(
            warning_key,
            "cannot convert %s to one of: %s" % (pixel_format, rgb_formats))
        return False
    input_format, target_format = target_rgb[0]
    start = time.time()
    w = image.get_width()
    h = image.get_height()
    #PIL cannot use the memoryview directly:
    if _memoryview and isinstance(pixels, _memoryview):
        pixels = pixels.tobytes()
    log("rgb_reformat: converting %s from %s to %s using PIL", image,
        input_format, target_format)
    img = PIL.Image.frombuffer(target_format, (w, h), pixels, "raw",
                               input_format, image.get_rowstride())
    rowstride = w * len(
        target_format)  #number of characters is number of bytes per pixel!
    #use tobytes() if present, fallback to tostring():
    data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
    data = data_fn("raw", target_format)
    assert len(
        data) == rowstride * h, "expected %s bytes in %s format but got %s" % (
            rowstride * h, len(data))
    image.set_pixels(data)
    image.set_rowstride(rowstride)
    image.set_pixel_format(target_format)
    end = time.time()
    log(
        "rgb_reformat(%s, %s, %s) converted from %s (%s bytes) to %s (%s bytes) in %.1fms, rowstride=%s",
        image, rgb_formats, supports_transparency, pixel_format, len(pixels),
        target_format, len(data), (end - start) * 1000.0, rowstride)
    return True
Example #24
0
    def _do_paint_rgb(self, cairo_format, has_alpha, img_data, x, y, width,
                      height, rowstride, options):
        """ must be called from UI thread """
        log("cairo._do_paint_rgb(%s, %s, %s bytes,%s,%s,%s,%s,%s,%s)",
            cairo_format, has_alpha, len(img_data), x, y, width, height,
            rowstride, options)
        rgb_format = options.strget("rgb_format", "RGB")
        if _memoryview and isinstance(img_data, _memoryview):
            #Pixbuf cannot use the memoryview directly:
            img_data = img_data.tobytes()
        #"cairo.ImageSurface.create_for_data" is not implemented in GTK3! ARGH!
        # http://cairographics.org/documentation/pycairo/3/reference/surfaces.html#cairo.ImageSurface.create_for_data
        # "Not yet available in Python 3"
        #
        #It is available in the cffi cairo bindings, which can be used instead of pycairo
        # but then we can't use it from the draw callbacks:
        # https://mail.gnome.org/archives/python-hackers-list/2011-December/msg00004.html
        # "PyGObject just lacks the glue code that allows it to pass the statically-wrapped
        # cairo.Pattern to introspected methods"

        if not is_gtk3() and rgb_format in ("ARGB", "XRGB"):
            #the pixel format is also what cairo expects
            #maybe we should also check that the stride is acceptable for cairo?
            #cairo_stride = cairo.ImageSurface.format_stride_for_width(cairo_format, width)
            #log("cairo_stride=%s, stride=%s", cairo_stride, rowstride)
            img_surface = cairo.ImageSurface.create_for_data(
                img_data, cairo_format, width, height, rowstride)
            return self.cairo_paint_surface(img_surface, x, y)

        if not is_gtk3() and rgb_format in ("RGBA", "RGBX"):
            #with GTK2, we can use a pixbuf from RGB(A) pixels
            if rgb_format == "RGBA":
                #we have to unpremultiply for pixbuf!
                img_data = self.unpremultiply(img_data)
            pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, has_alpha,
                                          8, width, height, rowstride)
            return self.cairo_paint_pixbuf(pixbuf, x, y)

        #PIL fallback
        PIL = get_codec("PIL")
        if has_alpha:
            oformat = "RGBA"
        else:
            oformat = "RGB"
        img = PIL.Image.frombuffer(oformat, (width, height), img_data, "raw",
                                   rgb_format, rowstride, 1)
        #This is insane, the code below should work, but it doesn't:
        # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
        # pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
        # success = self.cairo_paint_pixbuf(pixbuf, x, y)
        #So we still rountrip via PNG:
        png = BytesIOClass()
        img.save(png, format="PNG")
        reader = BytesIOClass(png.getvalue())
        png.close()
        img = cairo.ImageSurface.create_from_png(reader)
        return self.cairo_paint_surface(img, x, y)
Example #25
0
def get_DEFAULT_CSC_MODULES():
    """ returns all the csc modules installed """
    csc = []
    for x in tuple(ALL_CSC_MODULE_OPTIONS):
        mod = get_csc_module_name(x)
        c = get_codec(mod)
        if c:
            csc.append(x)
    return csc
Example #26
0
def get_DEFAULT_VIDEO_ENCODERS():
    """ returns all the video encoders installed """
    encoders = []
    for x in list(ALL_VIDEO_ENCODER_OPTIONS):
        mod = get_encoder_module_name(x)
        c = get_codec(mod)
        if c:
            encoders.append(x)
    return encoders
Example #27
0
    def init_encodings(self):
        load_codecs(decoders=False)
        encs, core_encs = [], []
        def add_encodings(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)
Example #28
0
def get_DEFAULT_VIDEO_DECODERS():
    """ returns all the video decoders installed """
    decoders = []
    for x in tuple(ALL_VIDEO_DECODER_OPTIONS):
        mod = get_decoder_module_name(x)
        c = get_codec(mod)
        if c:
            decoders.append(x)
    return decoders
Example #29
0
def get_DEFAULT_VIDEO_ENCODERS():
    """ returns all the video encoders installed """
    encoders = []
    for x in list(ALL_VIDEO_ENCODER_OPTIONS):
        mod = get_encoder_module_name(x)
        c = get_codec(mod)
        if c:
            encoders.append(x)
    return encoders
Example #30
0
def get_DEFAULT_CSC_MODULES():
    """ returns all the csc modules installed """
    csc = []
    for x in list(ALL_CSC_MODULE_OPTIONS):
        mod = get_csc_module_name(x)
        c = get_codec(mod)
        if c:
            csc.append(x)
    return csc
Example #31
0
def rgb_reformat(image, rgb_formats, supports_transparency):
    """ convert the RGB pixel data into a format supported by the client """
    # need to convert to a supported format!
    PIL = get_codec("PIL")
    pixel_format = image.get_pixel_format()
    pixels = image.get_pixels()
    assert pixels, "failed to get pixels from %s" % image
    if not PIL or pixel_format == "r210":
        # try to fallback to argb module
        # (required for r210 which is not handled by PIL directly)
        log("rgb_reformat: using argb_swap for %s", image)
        return argb_swap(image, rgb_formats, supports_transparency)
    if supports_transparency:
        modes = PIL_conv.get(pixel_format)
    else:
        modes = PIL_conv_noalpha.get(pixel_format)
    assert modes, "no PIL conversion from %s" % (pixel_format)
    target_rgb = [(im, om) for (im, om) in modes if om in rgb_formats]
    if len(target_rgb) == 0:
        log("rgb_reformat: no matching target modes for converting %s to %s", image, rgb_formats)
        # try argb module:
        if argb_swap(image, rgb_formats, supports_transparency):
            return True
        warning_key = "rgb_reformat(%s, %s, %s)" % (pixel_format, rgb_formats, supports_transparency)
        warn_encoding_once(warning_key, "cannot convert %s to one of: %s" % (pixel_format, rgb_formats))
        return False
    input_format, target_format = target_rgb[0]
    start = time.time()
    w = image.get_width()
    h = image.get_height()
    # PIL cannot use the memoryview directly:
    if isinstance(pixels, memoryview):
        pixels = pixels.tobytes()
    log("rgb_reformat: converting %s from %s to %s using PIL", image, input_format, target_format)
    img = PIL.Image.frombuffer(target_format, (w, h), pixels, "raw", input_format, image.get_rowstride())
    rowstride = w * len(target_format)  # number of characters is number of bytes per pixel!
    data = img.tobytes("raw", target_format)
    assert len(data) == rowstride * h, "expected %s bytes in %s format but got %s" % (rowstride * h, len(data))
    image.set_pixels(data)
    image.set_rowstride(rowstride)
    image.set_pixel_format(target_format)
    end = time.time()
    log(
        "rgb_reformat(%s, %s, %s) converted from %s (%s bytes) to %s (%s bytes) in %.1fms, rowstride=%s",
        image,
        rgb_formats,
        supports_transparency,
        pixel_format,
        len(pixels),
        target_format,
        len(data),
        (end - start) * 1000.0,
        rowstride,
    )
    return True
Example #32
0
def webp_encode(coding, image, rgb_formats, supports_transparency, quality, speed, options):
    pixel_format = image.get_pixel_format()
    #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats)
    if pixel_format not in rgb_formats:
        if not rgb_reformat(image, rgb_formats, supports_transparency):
            raise Exception("cannot find compatible rgb format to use for %s! (supported: %s)" % (pixel_format, rgb_formats))
        #get the new format:
        pixel_format = image.get_pixel_format()
    stride = image.get_rowstride()
    enc_webp = get_codec("enc_webp")
    #log("webp_encode%s stride=%s, enc_webp=%s", (coding, image, rgb_formats, supports_transparency, quality, speed, options), stride, enc_webp)
    if enc_webp and stride>0 and stride%4==0 and image.get_pixel_format() in ("BGRA", "BGRX", "RGBA", "RGBX"):
        #prefer Cython module:
        alpha = supports_transparency and image.get_pixel_format().find("A")>=0
        w = image.get_width()
        h = image.get_height()
        if quality==100:
            #webp lossless is unbearibly slow for only marginal compression improvements,
            #so force max speed:
            speed = 100
        else:
            #normalize speed for webp: avoid low speeds!
            speed = int(sqrt(speed) * 10)
        speed = max(0, min(100, speed))
        pixels = image.get_pixels()
        assert pixels, "failed to get pixels from %s" % image
        cdata = enc_webp.compress(pixels, w, h, stride=stride/4, quality=quality, speed=speed, has_alpha=alpha)
        client_options = {"speed"       : speed,
                          "rgb_format"  : pixel_format}
        if quality>=0 and quality<=100:
            client_options["quality"] = quality
        if alpha:
            client_options["has_alpha"] = True
        return "webp", compression.Compressed("webp", cdata), client_options, image.get_width(), image.get_height(), 0, 24
    #fallback to PIL
    enc_pillow = get_codec("enc_pillow")
    if enc_pillow:
        log("using PIL fallback for webp: enc_webp=%s, stride=%s, pixel format=%s", enc_webp, stride, image.get_pixel_format())
        for x in ("webp", "png"):
            if x in enc_pillow.get_encodings():
                return enc_pillow.encode(x, image, quality, speed, supports_transparency)
    raise Exception("BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!")
Example #33
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     debug("init_csc_option(%s) module=%s", csc_name, csc_module)
     if csc_module is None:
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
     except Exception, e:
         log.warn("cannot use %s module %s: %s", csc_type, csc_module, e, exc_info=True)
         return
Example #34
0
def get_DEFAULT_VIDEO_ENCODERS():
    """ returns all the video encoders installed """
    encoders = []
    for x in tuple(ALL_VIDEO_ENCODER_OPTIONS):
        mods = get_encoder_module_names(x)
        for mod in mods:
            c = get_codec(mod)
            if c:
                encoders.append(x)
                break
    return encoders
Example #35
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     debug("init_csc_option(%s) module=%s", csc_name, csc_module)
     if csc_module is None:
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
     except Exception, e:
         log.warn("cannot use %s module %s: %s", csc_type, csc_module, e, exc_info=True)
         return
Example #36
0
 def init_video_encoder_option(self, encoder_name):
     encoder_module = get_codec(encoder_name)
     debug("init_video_encoder_option(%s) module=%s", encoder_name, encoder_module)
     if not encoder_module:
         return
     encoder_type = encoder_module.get_type()
     try:
         encoder_module.init_module()
     except Exception, e:
         log.warn("cannot use %s module %s: %s", encoder_type, encoder_module, e, exc_info=True)
         return
Example #37
0
 def init_video_encoder_option(self, encoder_name):
     encoder_module = get_codec(encoder_name)
     debug("init_video_encoder_option(%s) module=%s", encoder_name, encoder_module)
     if not encoder_module:
         return
     encoder_type = encoder_module.get_type()
     try:
         encoder_module.init_module()
     except Exception, e:
         log.warn("cannot use %s module %s: %s", encoder_type, encoder_module, e, exc_info=True)
         return
Example #38
0
 def bgr_to_rgb(self, img_data, width, height, rowstride, rgb_format, target_format):
     if not rgb_format.startswith("BGR"):
         return img_data, rowstride
     from xpra.codecs.loader import get_codec
     #use an rgb format name that PIL will recognize:
     in_format = rgb_format.replace("X", "A")
     PIL = get_codec("PIL")
     img = PIL.Image.frombuffer(target_format, (width, height), img_data, "raw", in_format, rowstride)
     img_data = img.tobytes("raw", target_format)
     log.warn("%s converted to %s", rgb_format, target_format)
     return img_data, width*len(target_format)
Example #39
0
 def bgr_to_rgb(self, img_data, width, height, rowstride, rgb_format, target_format):
     if not rgb_format.startswith("BGR"):
         return img_data, rowstride
     from xpra.codecs.loader import get_codec
     #use an rgb format name that PIL will recognize:
     in_format = rgb_format.replace("X", "A")
     PIL = get_codec("PIL")
     img = PIL.Image.frombuffer(target_format, (width, height), img_data, "raw", in_format, rowstride)
     img_data = img.tobytes("raw", target_format)
     log.warn("%s converted to %s", rgb_format, target_format)
     return img_data, width*len(target_format)
Example #40
0
    def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
        """ can be called from any thread """
        # log("paint_image(%s, %s bytes, %s, %s, %s, %s, %s, %s)", coding, len(img_data), x, y, width, height, options, callbacks)
        PIL = get_codec("PIL")
        assert PIL.Image, "PIL.Image not found"
        buf = BytesIOClass(img_data)
        img = PIL.Image.open(buf)
        assert img.mode in ("L", "P", "RGB", "RGBA"), "invalid image mode: %s" % img.mode
        transparency = options.get("transparency", -1)
        if img.mode == "P":
            if transparency >= 0:
                # this deals with alpha without any extra work
                img = img.convert("RGBA")
            else:
                img = img.convert("RGB")
        elif img.mode == "L":
            if transparency >= 0:
                # why do we have to deal with alpha ourselves??
                def mask_value(a):
                    if a != transparency:
                        return 255
                    return 0

                mask = PIL.Image.eval(img, mask_value)
                mask = mask.convert("L")

                def nomask_value(a):
                    if a != transparency:
                        return a
                    return 0

                img = PIL.Image.eval(img, nomask_value)
                img = img.convert("RGBA")
                img.putalpha(mask)
            else:
                img = img.convert("RGB")

        # use tobytes() if present, fallback to tostring():
        data_fn = getattr(img, "tobytes", getattr(img, "tostring", None))
        raw_data = data_fn("raw", img.mode)
        paint_options = typedict(options)
        rgb_format = img.mode
        if rgb_format == "RGB":
            # PIL flattens the data to a continuous straightforward RGB format:
            rowstride = width * 3
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        elif rgb_format == "RGBA":
            rowstride = width * 4
            img_data = self.process_delta(raw_data, width, height, rowstride, options)
        else:
            raise Exception("invalid image mode: %s" % img.mode)
        paint_options["rgb_format"] = rgb_format
        self.idle_add(self.do_paint_rgb, rgb_format, img_data, x, y, width, height, rowstride, paint_options, callbacks)
        return False
Example #41
0
def load_video_decoders():
    global VIDEO_DECODERS
    if VIDEO_DECODERS is None:
        VIDEO_DECODERS = {}
        for codec in ("vp8", "vp9", "h264"):
            #prefer native vpx ahead of avcodec:
            for module in ("dec_vpx", "dec_avcodec", "dec_avcodec2"):
                decoder = get_codec(module)
                if decoder and (codec in decoder.get_encodings()):
                    VIDEO_DECODERS[codec] = module
                    break
    log("video decoders: %s", VIDEO_DECODERS)
Example #42
0
def load_video_decoders():
    global VIDEO_DECODERS
    if VIDEO_DECODERS is None:
        VIDEO_DECODERS = {}
        for codec in ("vp8", "vp9", "h264"):
            #prefer native vpx ahead of avcodec:
            for module in ("dec_vpx", "dec_avcodec", "dec_avcodec2"):
                decoder = get_codec(module)
                if decoder and (codec in decoder.get_encodings()):
                    VIDEO_DECODERS[codec] = module
                    break
    log("video decoders: %s", VIDEO_DECODERS)
Example #43
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     log("init_csc_option(%s) module=%s", csc_name, csc_module)
     if csc_module is None:
         log.warn("csc module %s could not be loaded: %s", csc_name, get_codec_error(csc_name))
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
         self._cleanup_modules.append(csc_module)
     except Exception, e:
         log.warn("cannot use %s module %s: %s", csc_type, csc_module, e)
         return
Example #44
0
 def init_video_decoder_option(self, decoder_name):
     decoder_module = get_codec(decoder_name)
     log("init_video_decoder_option(%s) module=%s", decoder_name, decoder_module)
     if not decoder_module:
         log.warn("video decoder %s could not be loaded: %s", decoder_name, get_codec_error(decoder_name))
         return
     encoder_type = decoder_module.get_type()
     try:
         decoder_module.init_module()
         self._cleanup_modules.append(decoder_module)
     except Exception, e:
         log.warn("cannot use %s module %s: %s", encoder_type, decoder_module, e, exc_info=True)
         return
Example #45
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     log("init_csc_option(%s) module=%s", csc_name, csc_module)
     if csc_module is None:
         log.warn("csc module %s could not be loaded: %s", csc_name, get_codec_error(csc_name))
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
         self._cleanup_modules.append(csc_module)
     except Exception, e:
         log.warn("cannot use %s module %s: %s", csc_type, csc_module, e)
         return
Example #46
0
 def paint_webp_using_cwebp(self, img_data, x, y, width, height, options, callbacks):
     dec_webp = get_codec("dec_webp")
     has_alpha = options.get("has_alpha", False)
     buffer_wrapper, width, height, stride, has_alpha, rgb_format = dec_webp.decompress(img_data, has_alpha)
     options["rgb_format"] = rgb_format
     def free_buffer(*args):
         buffer_wrapper.free()
     callbacks.append(free_buffer)
     data = buffer_wrapper.get_pixels()
     if has_alpha:
         return self.paint_rgb32(data, x, y, width, height, stride, options, callbacks)
     else:
         return self.paint_rgb24(data, x, y, width, height, stride, options, callbacks)
Example #47
0
 def init_video_decoder_option(self, decoder_name):
     decoder_module = get_codec(decoder_name)
     log("init_video_decoder_option(%s) module=%s", decoder_name, decoder_module)
     if not decoder_module:
         log.warn("video decoder %s could not be loaded: %s", decoder_name, get_codec_error(decoder_name))
         return
     encoder_type = decoder_module.get_type()
     try:
         decoder_module.init_module()
         self._cleanup_modules.append(decoder_module)
     except Exception, e:
         log.warn("cannot use %s module %s: %s", encoder_type, decoder_module, e, exc_info=True)
         return
Example #48
0
    def _do_paint_rgb(self, cairo_format, has_alpha, img_data, x, y, width, height, rowstride, options):
        """ must be called from UI thread """
        log("cairo._do_paint_rgb(%s, %s, %s bytes,%s,%s,%s,%s,%s,%s)", cairo_format, has_alpha, len(img_data), x, y, width, height, rowstride, options)
        rgb_format = options.strget("rgb_format", "RGB")
        if _memoryview and isinstance(img_data, _memoryview):
            #Pixbuf cannot use the memoryview directly:
            img_data = img_data.tobytes()
        #"cairo.ImageSurface.create_for_data" is not implemented in GTK3! ARGH!
        # http://cairographics.org/documentation/pycairo/3/reference/surfaces.html#cairo.ImageSurface.create_for_data
        # "Not yet available in Python 3"
        #
        #It is available in the cffi cairo bindings, which can be used instead of pycairo
        # but then we can't use it from the draw callbacks:
        # https://mail.gnome.org/archives/python-hackers-list/2011-December/msg00004.html
        # "PyGObject just lacks the glue code that allows it to pass the statically-wrapped
        # cairo.Pattern to introspected methods"

        if not is_gtk3() and rgb_format in ("ARGB", "XRGB"):
            #the pixel format is also what cairo expects
            #maybe we should also check that the stride is acceptable for cairo?
            #cairo_stride = cairo.ImageSurface.format_stride_for_width(cairo_format, width)
            #log("cairo_stride=%s, stride=%s", cairo_stride, rowstride)
            img_surface = cairo.ImageSurface.create_for_data(img_data, cairo_format, width, height, rowstride)
            return self.cairo_paint_surface(img_surface, x, y)

        if not is_gtk3() and rgb_format in ("RGBA", "RGBX"):
            #with GTK2, we can use a pixbuf from RGB(A) pixels
            if rgb_format=="RGBA":
                #we have to unpremultiply for pixbuf!
                img_data = self.unpremultiply(img_data)
            pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, has_alpha, 8, width, height, rowstride)
            return self.cairo_paint_pixbuf(pixbuf, x, y)

        #PIL fallback
        PIL = get_codec("PIL")
        if has_alpha:
            oformat = "RGBA"
        else:
            oformat = "RGB"
        img = PIL.Image.frombuffer(oformat, (width,height), img_data, "raw", rgb_format, rowstride, 1)
        #This is insane, the code below should work, but it doesn't:
        # img_data = bytearray(img.tostring('raw', oformat, 0, 1))
        # pixbuf = pixbuf_new_from_data(img_data, COLORSPACE_RGB, True, 8, width, height, rowstride)
        # success = self.cairo_paint_pixbuf(pixbuf, x, y)
        #So we still rountrip via PNG:
        png = BytesIOClass()
        img.save(png, format="PNG")
        reader = BytesIOClass(png.getvalue())
        png.close()
        img = cairo.ImageSurface.create_from_png(reader)
        return self.cairo_paint_surface(img, x, y)
Example #49
0
 def paint_webp(self, img_data, x, y, width, height, options, callbacks):
     dec_webp = get_codec("dec_webp")
     if not dec_webp or WEBP_PILLOW:
         #if webp is enabled, then Pillow should be able to take care of it:
         return self.paint_image("webp", img_data, x, y, width, height, options, callbacks)
     has_alpha = options.get("has_alpha", False)
     buffer_wrapper, width, height, stride, has_alpha, rgb_format = dec_webp.decompress(img_data, has_alpha, options.get("rgb_format"))
     #replace with the actual rgb format we get from the decoder:
     options["rgb_format"] = rgb_format
     def free_buffer(*args):
         buffer_wrapper.free()
     callbacks.append(free_buffer)
     data = buffer_wrapper.get_pixels()
     if len(rgb_format)==4:
         return self.paint_rgb32(data, x, y, width, height, stride, options, callbacks)
     else:
         return self.paint_rgb24(data, x, y, width, height, stride, options, callbacks)
Example #50
0
 def paint_webp(self, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     dec_webp = get_codec("dec_webp")
     assert dec_webp is not None, "webp decoder not found"
     if options.get("has_alpha", False):
         decode = dec_webp.DecodeRGBA
         rowstride = width*4
         paint_rgb = self.do_paint_rgb32
     else:
         decode = dec_webp.DecodeRGB
         rowstride = width*3
         paint_rgb = self.do_paint_rgb24
     log("paint_webp(%s) using decode=%s, paint=%s",
          ("%s bytes" % len(img_data), x, y, width, height, options, callbacks), decode, paint_rgb)
     rgb_data = decode(img_data)
     pixels = str(rgb_data.bitmap)
     self.idle_add(paint_rgb, pixels, x, y, width, height, rowstride, options, callbacks)
     return  False
Example #51
0
 def do_paint_rgb24(self, img_data, x, y, width, height, rowstride, options, callbacks):
     """ must be called from UI thread """
     log("cairo.do_paint_rgb24(..,%s,%s,%s,%s,%s,%s,%s)", x, y, width, height, rowstride, options, callbacks)
     if self._backing is None:
         fire_paint_callbacks(callbacks, False)
         return  False
     PIL = get_codec("PIL")
     assert PIL, "cannot paint without PIL!"
     if rowstride==0:
         rowstride = width * 3
     im = PIL.Image.frombuffer("RGB", (width, height), img_data, "raw", "RGB", rowstride)
     buf = BytesIOClass()
     im.save(buf, "PNG")
     data = buf.getvalue()
     buf.close()
     img_data = BytesIOClass(data)
     self.do_paint_png(img_data, x, y, width, height, rowstride, options, callbacks)
     return  False
Example #52
0
 def __init__(self, wid, window_alpha, idle_add):
     load_csc_options()
     load_video_decoders()
     self.wid = wid
     self.size = 0, 0
     self.idle_add = idle_add
     self._alpha_enabled = window_alpha
     self._backing = None
     self._delta_pixel_data = [None for _ in range(DELTA_BUCKETS)]
     self._video_decoder = None
     self._csc_decoder = None
     self._decoder_lock = Lock()
     self._PIL_encodings = []
     PIL = get_codec("dec_pillow")
     if PIL:
         self._PIL_encodings = PIL.get_encodings()
     self.draw_needs_refresh = True
     self.mmap = None
     self.mmap_enabled = False
Example #53
0
 def init_csc_option(self, csc_name):
     csc_module = get_codec(csc_name)
     log("init_csc_option(%s)", csc_name)
     log(" module=%s", csc_module)
     if csc_module is None:
         log(" csc module %s could not be loaded:", csc_name)
         log(" %s", get_codec_error(csc_name))
         return
     csc_type = csc_module.get_type()
     try:
         csc_module.init_module()
         self._cleanup_modules.append(csc_module)
     except Exception as e:
         log("exception in %s module initialization %s: %s", csc_type, csc_module.init_module, e, exc_info=True)
         log.warn("Warning: cannot use %s module %s: %s", csc_type, csc_module, e)
         return
     in_cscs = csc_module.get_input_colorspaces()
     for in_csc in in_cscs:
         out_cscs = csc_module.get_output_colorspaces(in_csc)
         log("%s output colorspaces for %s: %s", csc_module.get_type(), in_csc, csv(out_cscs))
         for out_csc in out_cscs:
             spec = csc_module.get_spec(in_csc, out_csc)
             self.add_csc_spec(in_csc, out_csc, spec)
Example #54
0
 def init_video_encoder_option(self, encoder_name):
     encoder_module = get_codec(encoder_name)
     log("init_video_encoder_option(%s)", encoder_name)
     log(" module=%s", encoder_module)
     if not encoder_module:
         log(" video encoder '%s' could not be loaded:", encoder_name)
         log(" %s", get_codec_error(encoder_name))
         return
     encoder_type = encoder_module.get_type()
     try:
         encoder_module.init_module()
         self._cleanup_modules.append(encoder_module)
     except Exception as e:
         log(" exception in %s module %s initialization %s: %s", encoder_type, encoder_module.__name__, encoder_module.init_module, e, exc_info=True)
         raise
     encodings = encoder_module.get_encodings()
     log(" %s encodings=%s", encoder_type, csv(encodings))
     for encoding in encodings:
         colorspaces = encoder_module.get_input_colorspaces(encoding)
         log(" %s input colorspaces for %s: %s", encoder_type, encoding, csv(colorspaces))
         for colorspace in colorspaces:
             spec = encoder_module.get_spec(encoding, colorspace)
             self.add_encoder_spec(encoding, colorspace, spec)
Example #55
0
 def test_all_codecs_found(self):
     from xpra.codecs import loader
     #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 loader.ALL_CODECS:
         codec = loader.get_codec(codec_name)
         if not codec:
             continue
         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)
Example #56
0
    from xpra.codecs.argb.argb import bgra_to_rgb, bgra_to_rgba, argb_to_rgb, argb_to_rgba   #@UnresolvedImport
except Exception, e:
    log("cannot load argb module: %s", e)
    bgra_to_rgb, bgra_to_rgba, argb_to_rgb, argb_to_rgba = (None,)*4
from xpra.codecs.codec_constants import get_PIL_encodings
from xpra.os_util import StringIOClass, memoryview_to_bytes
from xpra.codecs.loader import get_codec, get_codec_version
from xpra.os_util import builtins
_memoryview = builtins.__dict__.get("memoryview")
try:
    from xpra.net.mmap_pipe import mmap_write
except:
    mmap_write = None               #no mmap


PIL = get_codec("PIL")
PIL_VERSION = get_codec_version("PIL")
PIL_can_optimize = PIL_VERSION>="2.2"

#give warning message just once per key then ignore:
encoding_warnings = set()
def warn_encoding_once(key, message):
    global encoding_warnings
    if key not in encoding_warnings:
        log.warn("Warning: "+message)
        encoding_warnings.add(key)


def webp_encode(coding, image, rgb_formats, supports_transparency, quality, speed, options):
    pixel_format = image.get_pixel_format()
    #log("rgb_encode%s pixel_format=%s, rgb_formats=%s", (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats)
Example #57
0
    def paint_with_video_decoder(self, decoder_name, coding, img_data, x, y, width, height, options, callbacks):
        assert x==0 and y==0
        decoder_module = get_codec(decoder_name)
        assert decoder_module, "decoder module not found for %s" % decoder_name
        assert hasattr(decoder_module, "Decoder"), "decoder module %s does not have 'Decoder' factory function!" % decoder_module
        assert hasattr(decoder_module, "get_colorspaces"), "decoder module %s does not have 'get_colorspaces' function!" % decoder_module
        factory = getattr(decoder_module, "Decoder")
        get_colorspaces = getattr(decoder_module, "get_colorspaces")
        try:
            self._decoder_lock.acquire()
            if self._backing is None:
                log("window %s is already gone!", self.wid)
                fire_paint_callbacks(callbacks, False)
                return  False
            enc_width, enc_height = options.get("scaled_size", (width, height))
            input_colorspace = options.get("csc")
            if not input_colorspace:
                # Backwards compatibility with pre 0.10.x clients
                # We used to specify the colorspace as an avutil PixelFormat constant
                old_csc_fmt = options.get("csc_pixel_format")
                input_colorspace = get_colorspace_from_avutil_enum(old_csc_fmt)
                if input_colorspace is None:
                    #completely broken and out of date clients (ie: v0.3.x):
                    log.debug("csc was not specified and we cannot find a colorspace from csc_pixel_format=%s, assuming it is an old client and using YUV420P", old_csc_fmt)
                    input_colorspace = "YUV420P"

            #do we need a prep step for decoders that cannot handle the input_colorspace directly?
            decoder_colorspaces = get_colorspaces()
            decoder_colorspace = input_colorspace
            if input_colorspace not in decoder_colorspaces:
                log("colorspace not supported by %s directly", decoder_module)
                assert input_colorspace in ("BGRA", "BGRX"), "colorspace %s cannot be handled directly or via a csc preparation step!" % input_colorspace
                decoder_colorspace = "YUV444P"
                if self._csc_prep:
                    if self._csc_prep.get_src_format()!=input_colorspace:
                        #this should not happen!
                        log.warn("input colorspace has changed from %s to %s", self._csc_prep.get_src_format(), input_colorspace)
                        self.do_clean_csc_prep()
                    elif self._csc_prep.get_dst_format() not in decoder_colorspaces:
                        #this should not happen!
                        log.warn("csc prep colorspace %s is now invalid!?", self._csc_prep.get_dst_format())
                        self.do_clean_csc_prep()
                    elif self._csc_prep.get_src_width()!=enc_width or self._csc_prep.get_src_height()!=enc_height:
                        log("csc prep dimensions have changed from %s to %s", (self._csc_prep.get_src_width(), self._csc_prep.get_src_height()), (enc_width, enc_height))
                        self.do_clean_csc_prep()
                if self._csc_prep is None:
                    csc_speed = 0   #always best quality
                    self._csc_prep = self.make_csc(enc_width, enc_height, input_colorspace,
                                           width, height, [decoder_colorspace], csc_speed)
                    log("csc preparation step: %s", self._csc_prep)
            elif self._csc_prep:
                #no longer needed?
                self.do_clean_csc_prep()

            if self._video_decoder:
                if self._video_decoder.get_encoding()!=coding:
                    if DRAW_DEBUG:
                        log.info("paint_with_video_decoder: encoding changed from %s to %s", self._video_decoder.get_encoding(), coding)
                    self.do_clean_video_decoder()
                elif self._video_decoder.get_width()!=enc_width or self._video_decoder.get_height()!=enc_height:
                    if DRAW_DEBUG:
                        log.info("paint_with_video_decoder: window dimensions have changed from %s to %s", (self._video_decoder.get_width(), self._video_decoder.get_height()), (enc_width, enc_height))
                    self.do_clean_video_decoder()
                elif self._video_decoder.get_colorspace()!=decoder_colorspace:
                    if DRAW_DEBUG:
                        log.info("paint_with_video_decoder: colorspace changed from %s to %s", self._video_decoder.get_colorspace(), decoder_colorspace)
                    self.do_clean_video_decoder()
                elif options.get("frame")==0:
                    if DRAW_DEBUG:
                        log.info("paint_with_video_decoder: first frame of new stream")
                    self.do_clean_video_decoder()
            if self._video_decoder is None:
                if DRAW_DEBUG:
                    log.info("paint_with_video_decoder: new %s(%s,%s,%s)", factory, width, height, decoder_colorspace)
                self._video_decoder = factory()
                self._video_decoder.init_context(coding, enc_width, enc_height, decoder_colorspace)
                if DRAW_DEBUG:
                    log.info("paint_with_video_decoder: info=%s", self._video_decoder.get_info())

            img = self._video_decoder.decompress_image(img_data, options)
            if not img:
                raise Exception("paint_with_video_decoder: wid=%s, %s decompression error on %s bytes of picture data for %sx%s pixels using %s, options=%s" % (
                      self.wid, coding, len(img_data), width, height, self._video_decoder, options))
            self.do_video_paint(img, x, y, enc_width, enc_height, width, height, options, callbacks)
        finally:
            self._decoder_lock.release()
            if self._backing is None:
                self.close_decoder(True)
        return  False
Example #58
0
try:
    from xpra.codecs.argb.argb import bgra_to_rgb, bgra_to_rgba, argb_to_rgb, argb_to_rgba   #@UnresolvedImport
except Exception, e:
    log("cannot load argb module: %s", e)
    bgra_to_rgb, bgra_to_rgba, argb_to_rgb, argb_to_rgba = (None,)*4
from xpra.os_util import StringIOClass
from xpra.codecs.loader import get_codec, get_codec_version, has_codec
from xpra.os_util import builtins
_memoryview = builtins.__dict__.get("memoryview")
try:
    from xpra.net.mmap_pipe import mmap_write
except:
    mmap_write = None               #no mmap


PIL = get_codec("PIL")
PIL_VERSION = get_codec_version("PIL")
enc_webm = get_codec("enc_webm")
webp_handlers = get_codec("webm_bitmap_handlers")
PIL_can_optimize = PIL_VERSION>="2.2"

#give warning message just once per key then ignore:
encoding_warnings = set()
def warn_encoding_once(key, message):
    global encoding_warnings
    if key not in encoding_warnings:
        log.warn("Warning: "+message)
        encoding_warnings.add(key)


def webp_encode(coding, image, supports_transparency, quality, speed, options):
Example #59
0
    def process_draw(self, packet):
        wid, x, y, width, height, encoding, pixels, _, rowstride, client_options = packet[1:11]
        #never modify mmap packets
        if encoding=="mmap":
            return True

        #we have a proxy video packet:
        rgb_format = client_options.get("rgb_format", "")
        enclog("proxy draw: client_options=%s", client_options)

        def send_updated(encoding, compressed_data, updated_client_options):
            #update the packet with actual encoding data used:
            packet[6] = encoding
            packet[7] = compressed_data
            packet[10] = updated_client_options
            enclog("returning %s bytes from %s, options=%s", len(compressed_data), len(pixels), updated_client_options)
            return (wid not in self.lost_windows)

        def passthrough(strip_alpha=True):
            enclog("proxy draw: %s passthrough (rowstride: %s vs %s, strip alpha=%s)", rgb_format, rowstride, client_options.get("rowstride", 0), strip_alpha)
            if strip_alpha:
                #passthrough as plain RGB:
                Xindex = rgb_format.upper().find("X")
                if Xindex>=0 and len(rgb_format)==4:
                    #force clear alpha (which may be garbage):
                    newdata = bytearray(pixels)
                    for i in range(len(pixels)/4):
                        newdata[i*4+Xindex] = chr(255)
                    packet[9] = client_options.get("rowstride", 0)
                    cdata = bytes(newdata)
                else:
                    cdata = pixels
                new_client_options = {"rgb_format" : rgb_format}
            else:
                #preserve
                cdata = pixels
                new_client_options = client_options
            wrapped = Compressed("%s pixels" % encoding, cdata)
            #FIXME: we should not assume that rgb32 is supported here...
            #(we may have to convert to rgb24..)
            return send_updated("rgb32", wrapped, new_client_options)

        proxy_video = client_options.get("proxy", False)
        if PASSTHROUGH and (encoding in ("rgb32", "rgb24") or proxy_video):
            #we are dealing with rgb data, so we can pass it through:
            return passthrough(proxy_video)
        elif not self.video_encoder_types or not client_options or not proxy_video:
            #ensure we don't try to re-compress the pixel data in the network layer:
            #(re-add the "compressed" marker that gets lost when we re-assemble packets)
            packet[7] = Compressed("%s pixels" % encoding, packet[7])
            return True

        #video encoding: find existing encoder
        ve = self.video_encoders.get(wid)
        if ve:
            if ve in self.lost_windows:
                #we cannot clean the video encoder here, there may be more frames queue up
                #"lost-window" in encode_loop will take care of it safely
                return False
            #we must verify that the encoder is still valid
            #and scrap it if not (ie: when window is resized)
            if ve.get_width()!=width or ve.get_height()!=height:
                enclog("closing existing video encoder %s because dimensions have changed from %sx%s to %sx%s", ve, ve.get_width(), ve.get_height(), width, height)
                ve.clean()
                ve = None
            elif ve.get_encoding()!=encoding:
                enclog("closing existing video encoder %s because encoding has changed from %s to %s", ve.get_encoding(), encoding)
                ve.clean()
                ve = None
        #scaling and depth are proxy-encoder attributes:
        scaling = client_options.get("scaling", (1, 1))
        depth   = client_options.get("depth", 24)
        rowstride = client_options.get("rowstride", rowstride)
        quality = client_options.get("quality", -1)
        speed   = client_options.get("speed", -1)
        timestamp = client_options.get("timestamp")

        image = ImageWrapper(x, y, width, height, pixels, rgb_format, depth, rowstride, planes=ImageWrapper.PACKED)
        if timestamp is not None:
            image.set_timestamp(timestamp)

        #the encoder options are passed through:
        encoder_options = client_options.get("options", {})
        if not ve:
            #make a new video encoder:
            spec = self._find_video_encoder(encoding, rgb_format)
            if spec is None:
                #no video encoder!
                enc_pillow = get_codec("enc_pillow")
                if not enc_pillow:
                    from xpra.server.picture_encode import warn_encoding_once
                    warn_encoding_once("no-video-no-PIL", "no video encoder found for rgb format %s, sending as plain RGB!" % rgb_format)
                    return passthrough(True)
                enclog("no video encoder available: sending as jpeg")
                coding, compressed_data, client_options, _, _, _, _ = enc_pillow.encode("jpeg", image, quality, speed, False)
                return send_updated(coding, compressed_data, client_options)

            enclog("creating new video encoder %s for window %s", spec, wid)
            ve = spec.make_instance()
            #dst_formats is specified with first frame only:
            dst_formats = client_options.get("dst_formats")
            if dst_formats is not None:
                #save it in case we timeout the video encoder,
                #so we can instantiate it again, even from a frame no>1
                self.video_encoders_dst_formats = dst_formats
            else:
                assert self.video_encoders_dst_formats, "BUG: dst_formats not specified for proxy and we don't have it either"
                dst_formats = self.video_encoders_dst_formats
            ve.init_context(width, height, rgb_format, dst_formats, encoding, quality, speed, scaling, {})
            self.video_encoders[wid] = ve
            self.video_encoders_last_used_time[wid] = time.time()       #just to make sure this is always set
        #actual video compression:
        enclog("proxy compression using %s with quality=%s, speed=%s", ve, quality, speed)
        data, out_options = ve.compress_image(image, quality, speed, encoder_options)
        #pass through some options if we don't have them from the encoder
        #(maybe we should also use the "pts" from the real server?)
        for k in ("timestamp", "rgb_format", "depth", "csc"):
            if k not in out_options and k in client_options:
                out_options[k] = client_options[k]
        self.video_encoders_last_used_time[wid] = time.time()
        return send_updated(ve.get_encoding(), Compressed(encoding, data), out_options)