示例#1
0
 def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     if USE_PIL and has_codec("PIL"):
         return GTKWindowBacking.paint_image(self, coding, img_data, x, y, width, height, options, callbacks)
     # gdk needs UI thread:
     gobject.idle_add(self.paint_pixbuf_gdk, coding, img_data, x, y, width, height, options, callbacks)
     return False
示例#2
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
示例#3
0
 def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     if USE_PIL and has_codec("dec_pillow"):
         return GTKWindowBacking.paint_image(self, coding, img_data, x, y, width, height, options, callbacks)
     #gdk needs UI thread:
     self.idle_add(self.paint_pixbuf_gdk, coding, img_data, x, y, width, height, options, callbacks)
     return  False
示例#4
0
def webm_encode(image, quality):
    assert enc_webm and webp_handlers, "webp components are missing"

    BitmapHandler = webp_handlers.BitmapHandler
    handler_encs = {
                "RGB" : (BitmapHandler.RGB,     "EncodeRGB",  "EncodeLosslessRGB",  False),
                "BGR" : (BitmapHandler.BGR,     "EncodeBGR",  "EncodeLosslessBGR",  False),
                "RGBA": (BitmapHandler.RGBA,    "EncodeRGBA", "EncodeLosslessRGBA", True),
                "RGBX": (BitmapHandler.RGBA,    "EncodeRGBA", "EncodeLosslessRGBA", False),
                "BGRA": (BitmapHandler.BGRA,    "EncodeBGRA", "EncodeLosslessBGRA", True),
                "BGRX": (BitmapHandler.BGRA,    "EncodeBGRA", "EncodeLosslessBGRA", False),
                }
    pixel_format = image.get_pixel_format()
    h_e = handler_encs.get(pixel_format)
    assert h_e is not None, "cannot handle rgb format %s with webp!" % pixel_format
    bh, lossy_enc, lossless_enc, has_alpha = h_e
    q = max(1, quality)
    enc = None
    if q>=100 and has_codec("enc_webm_lossless"):
        enc = getattr(enc_webm, lossless_enc)
        kwargs = {}
        client_options = {}
        log("webm_encode(%s, %s) using lossless encoder=%s for %s", image, enc, pixel_format)
    if enc is None:
        enc = getattr(enc_webm, lossy_enc)
        kwargs = {"quality" : q}
        client_options = {"quality" : q}
        log("webm_encode(%s, %s) using lossy encoder=%s with quality=%s for %s", image, enc, q, pixel_format)
    handler = BitmapHandler(image.get_pixels(), bh, image.get_width(), image.get_height(), image.get_rowstride())
    bpp = 24
    if has_alpha:
        client_options["has_alpha"] = True
        bpp = 32
    return "webp", Compressed("webp", str(enc(handler, **kwargs).data)), client_options, image.get_width(), image.get_height(), 0, bpp
示例#5
0
 def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     use_PIL = has_codec("PIL") and os.environ.get("XPRA_USE_PIL", "1")=="1"
     if use_PIL:
         return GTKWindowBacking.paint_image(self, coding, img_data, x, y, width, height, options, callbacks)
     #gdk needs UI thread:
     gobject.idle_add(self.paint_pixbuf_gdk, coding, img_data, x, y, width, height, options, callbacks)
     return  False
示例#6
0
 def paint_image(self, coding, img_data, x, y, width, height, options, callbacks):
     """ can be called from any thread """
     use_PIL = has_codec("PIL") and os.environ.get("XPRA_USE_PIL", "1")=="1"
     if use_PIL:
         return GTKWindowBacking.paint_image(self, coding, img_data, x, y, width, height, options, callbacks)
     #gdk needs UI thread:
     gobject.idle_add(self.paint_pixbuf_gdk, coding, img_data, x, y, width, height, options, callbacks)
     return  False
示例#7
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)
示例#8
0
    def get_encodings_caps(self):
        if B_FRAMES:
            video_b_frames = ["h264"]  #only tested with dec_avcodec2
        else:
            video_b_frames = []
        caps = {
            "flush":
            PAINT_FLUSH,
            "scaling.control":
            self.video_scaling,
            "client_options":
            True,
            "csc_atoms":
            True,
            #TODO: check for csc support (swscale only?)
            "video_reinit":
            True,
            "video_scaling":
            True,
            "video_b_frames":
            video_b_frames,
            "webp_leaks":
            False,
            "transparency":
            self.has_transparency(),
            "rgb24zlib":
            True,
            "max-soft-expired":
            MAX_SOFT_EXPIRED,
            "send-timestamps":
            SEND_TIMESTAMPS,
            "supports_delta":
            tuple(x for x in ("png", "rgb24", "rgb32")
                  if x in self.get_core_encodings()),
        }
        if self.encoding:
            caps[""] = self.encoding
        for k, v in codec_versions.items():
            caps["%s.version" % k] = v
        if self.quality > 0:
            caps["quality"] = self.quality
        if self.min_quality > 0:
            caps["min-quality"] = self.min_quality
        if self.speed >= 0:
            caps["speed"] = self.speed
        if self.min_speed >= 0:
            caps["min-speed"] = self.min_speed

        #generic rgb compression flags:
        for x in compression.ALL_COMPRESSORS:
            caps["rgb_%s" % x] = x in compression.get_enabled_compressors()
        #these are the defaults - when we instantiate a window,
        #we can send different values as part of the map event
        #these are the RGB modes we want (the ones we are expected to be able to paint with):
        rgb_formats = ["RGB", "RGBX", "RGBA"]
        caps["rgb_formats"] = rgb_formats
        #figure out which CSC modes (usually YUV) can give us those RGB modes:
        full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(
            *rgb_formats)
        if has_codec("dec_webp"):
            if self.opengl_enabled:
                full_csc_modes["webp"] = ("BGRX", "BGRA", "RGBX", "RGBA")
            else:
                full_csc_modes["webp"] = (
                    "BGRX",
                    "BGRA",
                )
        log("supported full csc_modes=%s", full_csc_modes)
        caps["full_csc_modes"] = full_csc_modes

        if "h264" in self.get_core_encodings():
            # some profile options: "baseline", "main", "high", "high10", ...
            # set the default to "high10" for I420/YUV420P
            # as the python client always supports all the profiles
            # whereas on the server side, the default is baseline to accomodate less capable clients.
            # I422/YUV422P requires high422, and
            # I444/YUV444P requires high444,
            # so we don't bother specifying anything for those two.
            for old_csc_name, csc_name, default_profile in (("I420", "YUV420P",
                                                             "high10"),
                                                            ("I422", "YUV422P",
                                                             ""), ("I444",
                                                                   "YUV444P",
                                                                   "")):
                profile = default_profile
                #try with the old prefix (X264) as well as the more correct one (H264):
                for H264_NAME in ("X264", "H264"):
                    profile = os.environ.get(
                        "XPRA_%s_%s_PROFILE" % (H264_NAME, old_csc_name),
                        profile)
                    profile = os.environ.get(
                        "XPRA_%s_%s_PROFILE" % (H264_NAME, csc_name), profile)
                if profile:
                    #send as both old and new names:
                    for h264_name in ("x264", "h264"):
                        caps["%s.%s.profile" %
                             (h264_name, old_csc_name)] = profile
                        caps["%s.%s.profile" % (h264_name, csc_name)] = profile
            log(
                "x264 encoding options: %s",
                str([(k, v) for k, v in caps.items()
                     if k.startswith("x264.")]))
        log("encoding capabilities: %s", caps)
        return caps
示例#9
0
    def get_encodings_caps(self) -> dict:
        if B_FRAMES:
            video_b_frames = ("h264", ) #only tested with dec_avcodec2
        else:
            video_b_frames = ()
        caps = {
            "flush"                     : PAINT_FLUSH,      #v4 servers assume this is available
            "video_scaling"             : True,             #v4 servers assume this is available
            "video_b_frames"            : video_b_frames,
            "video_max_size"            : self.video_max_size,
            "max-soft-expired"          : MAX_SOFT_EXPIRED,
            "send-timestamps"           : SEND_TIMESTAMPS,
            }
        if self.video_scaling is not None:
            caps["scaling.control"] = self.video_scaling
        if self.encoding:
            caps[""] = self.encoding
        for k,v in codec_versions.items():
            caps["%s.version" % k] = v
        if self.quality>0:
            caps["quality"] = self.quality
        if self.min_quality>0:
            caps["min-quality"] = self.min_quality
        if self.speed>=0:
            caps["speed"] = self.speed
        if self.min_speed>=0:
            caps["min-speed"] = self.min_speed

        #generic rgb compression flags:
        for x in compression.ALL_COMPRESSORS:
            caps["rgb_%s" % x] = x in compression.get_enabled_compressors()
        #these are the defaults - when we instantiate a window,
        #we can send different values as part of the map event
        #these are the RGB modes we want (the ones we are expected to be able to paint with):
        rgb_formats = ["RGB", "RGBX", "RGBA"]
        caps["rgb_formats"] = rgb_formats
        #figure out which CSC modes (usually YUV) can give us those RGB modes:
        full_csc_modes = getVideoHelper().get_server_full_csc_modes_for_rgb(*rgb_formats)
        if has_codec("dec_webp"):
            if self.opengl_enabled:
                full_csc_modes["webp"] = ("BGRX", "BGRA", "RGBX", "RGBA")
            else:
                full_csc_modes["webp"] = ("BGRX", "BGRA", )
        if has_codec("dec_jpeg") or has_codec("dec_pillow"):
            full_csc_modes["jpeg"] = ("BGRX", "BGRA", "YUV420P")
        if has_codec("dec_jpeg"):
            full_csc_modes["jpega"] = ("BGRA", "RGBA", )
        log("supported full csc_modes=%s", full_csc_modes)
        caps["full_csc_modes"] = full_csc_modes

        if "h264" in self.get_core_encodings():
            # some profile options: "baseline", "main", "high", "high10", ...
            # set the default to "high10" for YUV420P
            # as the python client always supports all the profiles
            # whereas on the server side, the default is baseline to accomodate less capable clients.
            # YUV422P requires high422, and
            # YUV444P requires high444,
            # so we don't bother specifying anything for those two.
            h264_caps = {}
            for csc_name, default_profile in (
                        ("YUV420P", "high"),
                        ("YUV422P", ""),
                        ("YUV444P", "")):
                profile = os.environ.get("XPRA_H264_%s_PROFILE" % (csc_name), default_profile)
                if profile:
                    h264_caps["%s.profile" % (csc_name)] = profile
            h264_caps["fast-decode"] = envbool("XPRA_X264_FAST_DECODE", False)
            log("x264 encoding options: %s", h264_caps)
            updict(caps, "h264", h264_caps)
        log("encoding capabilities: %s", caps)
        return caps
示例#10
0
    def init_encodings(self):
        encs, core_encs = [], []
        log("init_encodings() allowed_encodings=%s", self.allowed_encodings)

        def add_encoding(encoding):
            log("add_encoding(%s)", encoding)
            e = {"rgb32": "rgb", "rgb24": "rgb"}.get(encoding, encoding)
            if self.allowed_encodings is not None:
                if e not in self.allowed_encodings and encoding not in self.allowed_encodings:
                    #not in whitelist (if it exists)
                    return
            if e not in encs:
                encs.append(e)
            if encoding not in core_encs:
                core_encs.append(encoding)

        def add_encodings(*encodings):
            log("add_encodings%s", encodings)
            for encoding in encodings:
                add_encoding(encoding)

        add_encodings("rgb24", "rgb32", "scroll")
        lossless = []
        if "scroll" in self.allowed_encodings and "scroll" not in self.lossless_mode_encodings:
            #scroll is lossless, but it also uses other picture codecs
            #and those allow changes in quality
            lossless.append("scroll")

        #video encoders (empty when first called - see threaded_init)
        ve = getVideoHelper().get_encodings()
        log("init_encodings() adding video encodings: %s", ve)
        add_encodings(*ve)  #ie: ["vp8", "h264"]
        #Pithon Imaging Libary:
        enc_pillow = get_codec("enc_pillow")
        log("enc_pillow=%s", enc_pillow)
        if enc_pillow:
            pil_encs = enc_pillow.get_encodings()
            log("pillow encodings: %s", pil_encs)
            for encoding in pil_encs:
                if encoding != "webp":
                    add_encoding(encoding)
            #Note: webp will only be enabled if we have a Python-PIL fallback
            #(either "webp" or "png")
            if has_codec("enc_webp") and ("webp" in pil_encs
                                          or "png" in pil_encs):
                add_encodings("webp")
                if "webp" not in lossless:
                    lossless.append("webp")
        for codec_name in ("avif", "enc_jpeg", "enc_nvjpeg"):
            codec = get_codec(codec_name)
            if codec:
                add_encodings(*codec.get_encodings())
        #look for video encodings with lossless mode:
        for e in ve:
            for colorspace, especs in getVideoHelper().get_encoder_specs(
                    e).items():
                for espec in especs:
                    if espec.has_lossless_mode:
                        if e not in lossless:
                            log(
                                "found lossless mode for encoding %s with %s and colorspace %s",
                                e, espec, colorspace)
                            lossless.append(e)
                            break
        #now update the variables:
        encs.append("grayscale")
        self.encodings = encs
        self.core_encodings = tuple(core_encs)
        self.lossless_mode_encodings = tuple(lossless)
        self.lossless_encodings = tuple(
            x for x in self.core_encodings
            if (x.startswith("png") or x.startswith("rgb") or x == "webp"))
        log(
            "allowed encodings=%s, encodings=%s, core encodings=%s, lossless encodings=%s",
            self.allowed_encodings, encs, core_encs, self.lossless_encodings)
        pref = [x for x in PREFERRED_ENCODING_ORDER if x in self.encodings]
        if pref:
            self.default_encoding = pref[0]
        else:
            self.default_encoding = None
        #default encoding:
        if not self.encoding or str(self.encoding).lower() in ("auto", "none"):
            self.default_encoding = None
        elif self.encoding in self.encodings:
            self.default_encoding = self.encoding
        else:
            log.warn("ignored invalid default encoding option: %s",
                     self.encoding)
示例#11
0
    def parse_encoding_caps(self, c):
        self.set_encoding(c.strget("encoding", None), None)
        #encoding options (filter):
        #1: these properties are special cased here because we
        #defined their name before the "encoding." prefix convention,
        #or because we want to pass default values (zlib/lz4):
        for k,ek in {"initial_quality"          : "initial_quality",
                     "quality"                  : "quality",
                     }.items():
            if k in c:
                self.encoding_options[ek] = c.intget(k)
        for k,ek in {"zlib"                     : "rgb_zlib",
                     "lz4"                      : "rgb_lz4",
                     }.items():
            if k in c:
                self.encoding_options[ek] = c.boolget(k)
        #2: standardized encoding options:
        for k in c.keys():
            #yaml gives us str..
            k = bytestostr(k)
            if k.startswith("theme.") or k.startswith("encoding.icons."):
                self.icons_encoding_options[k.replace("encoding.icons.", "").replace("theme.", "")] = c.get(k)
            elif k.startswith("encoding."):
                stripped_k = k[len("encoding."):]
                if stripped_k in ("transparency",
                                  "rgb_zlib", "rgb_lz4",
                                  ):
                    v = c.boolget(k)
                elif stripped_k in ("initial_quality", "initial_speed",
                                    "min-quality", "quality",
                                    "min-speed", "speed"):
                    v = c.intget(k)
                else:
                    v = c.get(k)
                self.encoding_options[stripped_k] = v
        log("encoding options: %s", self.encoding_options)
        log("icons encoding options: %s", self.icons_encoding_options)

        #handle proxy video: add proxy codec to video helper:
        pv = self.encoding_options.boolget("proxy.video")
        proxylog("proxy.video=%s", pv)
        if pv:
            #enabling video proxy:
            try:
                self.parse_proxy_video()
            except Exception:
                proxylog.error("failed to parse proxy video", exc_info=True)

        sc = self.encoding_options.get("scaling.control", self.scaling_control)
        if sc is not None:
            #"encoding_options" are exposed via "xpra info",
            #so we can't have None values in there (bencoder would choke)
            self.default_encoding_options["scaling.control"] = sc
        q = self.encoding_options.intget("quality", self.default_quality)         #0.7 onwards:
        if q>0:
            self.default_encoding_options["quality"] = q
        mq = self.encoding_options.intget("min-quality", self.default_min_quality)
        if mq>0 and (q<=0 or q>mq):
            self.default_encoding_options["min-quality"] = mq
        s = self.encoding_options.intget("speed", self.default_speed)
        if s>0:
            self.default_encoding_options["speed"] = s
        ms = self.encoding_options.intget("min-speed", self.default_min_speed)
        if ms>0 and (s<=0 or s>ms):
            self.default_encoding_options["min-speed"] = ms
        log("default encoding options: %s", self.default_encoding_options)
        self.auto_refresh_delay = c.intget("auto_refresh_delay", 0)

        #are we going to need a cuda context?
        if getattr(self, "mmap_enabled", False):
            #not with mmap!
            return
        common_encodings = tuple(x for x in self.encodings if x in self.server_encodings)
        from xpra.codecs.loader import has_codec
        if (has_codec("nvenc") and "h264" in self.core_encodings or "h265" in self.core_encodings) \
        or ("jpeg" in common_encodings and has_codec("enc_nvjpeg")):
            cudalog = Logger("cuda")
            try:
                from xpra.codecs.cuda_common.cuda_context import get_device_context
                self.cuda_device_context = get_device_context(self.encoding_options)
                cudalog("cuda_device_context=%s", self.cuda_device_context)
            except Exception as e:
                cudalog("failed to get a cuda device context using encoding options %s",
                    self.encoding_options, exc_info=True)
                cudalog.error("Error: failed to allocate a CUDA context:")
                cudalog.error(" %s", e)
                cudalog.error(" NVJPEG and NVENC will not be available")