Skip to content

Commit

Permalink
#3337 decide when to use lossless mode in one place
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Nov 8, 2021
1 parent 947ddd3 commit dcbb495
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 26 deletions.
5 changes: 1 addition & 4 deletions xpra/codecs/webp/encoder.pyx
Expand Up @@ -19,10 +19,7 @@ log = Logger("encoder", "webp")
cdef int SAVE_TO_FILE = envbool("XPRA_SAVE_TO_FILE")
cdef int LOG_CONFIG = envbool("XPRA_WEBP_LOG_CONFIG", False)
cdef int WEBP_THREADING = envbool("XPRA_WEBP_THREADING", True)
cdef int LOSSLESS_THRESHOLD = envint("XPRA_WEBP_LOSSLESS_THRESHOLD", 75)
cdef int SUBSAMPLING_THRESHOLD = envint("XPRA_WEBP_SUBSAMPLING_THRESHOLD", 40)
assert SUBSAMPLING_THRESHOLD<=LOSSLESS_THRESHOLD, "lossless threshold must be higher than subsampling threshold"
assert LOSSLESS_THRESHOLD>=0 and LOSSLESS_THRESHOLD<=100, "invalid lossless threshold: %i" % LOSSLESS_THRESHOLD

cdef inline int MIN(int a, int b):
if a<=b:
Expand Down Expand Up @@ -420,7 +417,7 @@ def encode(image, int quality=50, int speed=50, supports_alpha=False, content_ty
raise Exception("failed to set webp preset")

#tune it:
config.lossless = quality>=(LOSSLESS_THRESHOLD+threshold_delta)
config.lossless = quality==100
if config.lossless:
#not much to gain from setting a high quality here,
#the latency will be higher for a negligible compression gain:
Expand Down
40 changes: 21 additions & 19 deletions xpra/server/window/window_source.py
Expand Up @@ -835,9 +835,6 @@ def update_encoding_selection(self, encoding=None, exclude=(), init=False):
self.update_encoding_options()
self.update_refresh_attributes()

def _more_lossless(self):
return False

def update_encoding_options(self, force_reload=False):
self._want_alpha = self.is_tray or (self.has_alpha and self.supports_transparency)
ww, wh = self.window_dimensions
Expand All @@ -847,8 +844,7 @@ def update_encoding_options(self, force_reload=False):
if r.contains(0, 0, ww, wh):
#window is fully opaque
self._want_alpha = False
ml = self._more_lossless()
self._lossless_threshold_base = min(90-10*ml, 60-ml*20+self._current_speed//5)
self._lossless_threshold_base = min(90, 60+self._current_speed//5)
if self.content_type=="text" or self.is_shadow:
self._lossless_threshold_base -= 20
self._lossless_threshold_pixel_boost = max(5, 20-self._current_speed//5)
Expand All @@ -866,7 +862,8 @@ def update_encoding_options(self, force_reload=False):
bwl = self.bandwidth_limit
if bwl:
max_rgb_threshold = min(max_rgb_threshold, max(bwl//1000, 1024))
v = int(MAX_PIXELS_PREFER_RGB * pcmult * smult * qmult * (1 + int(self.is_OR or self.is_tray or self.is_shadow)*2))
weight = 1 + int(self.is_OR or self.is_tray or self.is_shadow)*2
v = int(MAX_PIXELS_PREFER_RGB * pcmult * smult * qmult * weight)
crs = self.client_render_size
if crs and DOWNSCALE:
if crs[0]<ww or crs[1]<wh:
Expand All @@ -875,8 +872,12 @@ def update_encoding_options(self, force_reload=False):
max_rgb_threshold = 1024
self._rgb_auto_threshold = min(max_rgb_threshold, max(min_rgb_threshold, v))
self.assign_encoding_getter()
log("update_encoding_options(%s) wid=%i, want_alpha=%s, speed=%i, quality=%i, bandwidth-limit=%i, lossless threshold: %s / %s, rgb auto threshold=%i (min=%i, max=%i), get_best_encoding=%s",
force_reload, self.wid, self._want_alpha, self._current_speed, self._current_quality, bwl, self._lossless_threshold_base, self._lossless_threshold_pixel_boost, self._rgb_auto_threshold, min_rgb_threshold, max_rgb_threshold, self.get_best_encoding)
log("update_encoding_options(%s) wid=%i, want_alpha=%s, speed=%i, quality=%i",
force_reload, self.wid, self._want_alpha, self._current_speed, self._current_quality)
log("lossless threshold: %s / %s, rgb auto threshold=%i (min=%i, max=%i)",
self._lossless_threshold_base, self._lossless_threshold_pixel_boost,
self._rgb_auto_threshold, min_rgb_threshold, max_rgb_threshold, self.get_best_encoding)
log("bandwidth-limit=%i, get_best_encoding=%s", bwl, self.get_best_encoding)

def assign_encoding_getter(self):
self.get_best_encoding = self.get_best_encoding_impl()
Expand Down Expand Up @@ -1803,17 +1804,22 @@ def assign_sq_options(self, options, speed_delta=0, quality_delta=0):
packets_backlog = self.get_packets_backlog()
quality = max(1, self._fixed_min_quality, quality-packets_backlog*20+quality_delta)
eoptions = dict(options)
options["quality"] = quality
eoptions["quality"] = quality
eoptions["speed"] = speed
return eoptions

def do_send_delayed_regions(self, damage_time, regions, coding, options, exclude_region=None, get_best_encoding=None):
def do_send_delayed_regions(self, damage_time, regions, coding, options,
exclude_region=None, get_best_encoding=None):
ww,wh = self.window_dimensions
options = self.assign_sq_options(options)
speed = options.get("speed", 0)
quality = options.get("quality", 0)
get_best_encoding = get_best_encoding or self.get_best_encoding
def get_encoding(w, h):
speed = options.get("speed", 0)
quality = options.get("quality", 0)
if quality<100:
lossless_q = self._lossless_threshold_base + self._lossless_threshold_pixel_boost * w*h // (ww*wh)
if quality>=lossless_q:
quality = 100
return get_best_encoding(w, h, speed, quality, coding)

def send_full_window_update(cause):
Expand Down Expand Up @@ -2565,6 +2571,7 @@ def make_draw_packet(self, x, y, outw, outh, coding, data, outstride, client_opt


def webp_encode(self, coding, image, options):
assert coding=="webp"
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
pixel_format = image.get_pixel_format()
Expand All @@ -2589,6 +2596,7 @@ def no_r210(self, image, rgb_formats):
argb_swap(image, rgb_formats, self.supports_transparency)

def jpeg_encode(self, coding, image, options):
assert coding=="jpeg"
self.no_r210(image, ["RGB"])
q = options.get("quality", self._current_quality)
s = options.get("speed", self._current_speed)
Expand Down Expand Up @@ -2629,9 +2637,7 @@ def fallback(reason):
self.enc_nvjpeg = None
return fallback("nvjpeg is now disabled")
data, w, h, stride = r
cpixels = Compressed(coding, data, False)
#cpixels = Compressed(coding, data.tobytes(), True)
return "jpeg", cpixels, {}, w, h, stride, 24
return "jpeg", Compressed(coding, data, False), {}, w, h, stride, 24

def pillow_encode(self, coding, image, options):
#for more information on pixel formats supported by PIL / Pillow, see:
Expand All @@ -2651,10 +2657,6 @@ def pillow_encode(self, coding, image, options):
if ww-crsw>DOWNSCALE_THRESHOLD and wh-crsh>DOWNSCALE_THRESHOLD:
#keep the same proportions:
resize = w*crsw//ww, h*crsh//wh
else:
lossless_q = int(self._lossless_threshold_base + self._lossless_threshold_pixel_boost * w*h // (ww*wh))
if q>=lossless_q:
q = 100
return self.enc_pillow.encode(coding, image, q, s, transparency, grayscale, resize)

def mmap_encode(self, coding, image, _options):
Expand Down
3 changes: 0 additions & 3 deletions xpra/server/window/window_video_source.py
Expand Up @@ -1048,9 +1048,6 @@ def encode_from_queue(self):
first_due, still_due, self.av_sync_delay, av_delay, self.wid)
self.idle_add(self.schedule_encode_from_queue, first_due)

def _more_lossless(self):
return self.subregion_is_video()

def update_encoding_options(self, force_reload=False):
"""
This is called when we want to force a full re-init (force_reload=True)
Expand Down

0 comments on commit dcbb495

Please sign in to comment.