Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/server/picture_encode.py : 27%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2010-2019 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
7from xpra.net import compression
8from xpra.codecs.loader import get_codec
9from xpra.util import envbool, first_time
10from xpra.codecs.rgb_transform import rgb_reformat
11from xpra.os_util import memoryview_to_bytes, bytestostr, monotonic_time
12from xpra.log import Logger
14#"pixels_to_bytes" gets patched up by the OSX shadow server
15pixels_to_bytes = memoryview_to_bytes
16try:
17 from xpra.net.mmap_pipe import mmap_write
18except ImportError:
19 mmap_write = None #no mmap
21log = Logger("window", "encoding")
23WEBP_PILLOW = envbool("XPRA_WEBP_PILLOW", False)
26def webp_encode(image, supports_transparency, quality, speed, content_type):
27 stride = image.get_rowstride()
28 pixel_format = image.get_pixel_format()
29 enc_webp = get_codec("enc_webp")
30 #log("WEBP_PILLOW=%s, enc_webp=%s, stride=%s, pixel_format=%s", WEBP_PILLOW, enc_webp, stride, pixel_format)
31 if not WEBP_PILLOW and enc_webp and stride>0 and stride%4==0 and pixel_format in ("BGRA", "BGRX", "RGBA", "RGBX"):
32 #prefer Cython module:
33 cdata, client_options = enc_webp.encode(image, quality, speed, supports_transparency, content_type)
34 return "webp", compression.Compressed("webp", cdata), client_options, image.get_width(), image.get_height(), 0, 24
35 #fallback using Pillow:
36 enc_pillow = get_codec("enc_pillow")
37 if enc_pillow:
38 if not WEBP_PILLOW:
39 log.warn("Warning: using PIL fallback for webp")
40 log.warn(" enc_webp=%s, stride=%s, pixel format=%s", enc_webp, stride, image.get_pixel_format())
41 for x in ("webp", "png"):
42 if x in enc_pillow.get_encodings():
43 return enc_pillow.encode(x, image, quality, speed, supports_transparency)
44 raise Exception("BUG: cannot use 'webp' encoding and none of the PIL fallbacks are available!")
47def rgb_encode(coding, image, rgb_formats, supports_transparency, speed, rgb_zlib=True, rgb_lz4=True, rgb_lzo=False):
48 pixel_format = bytestostr(image.get_pixel_format())
49 #log("rgb_encode%s pixel_format=%s, rgb_formats=%s",
50 # (coding, image, rgb_formats, supports_transparency, speed, rgb_zlib, rgb_lz4), pixel_format, rgb_formats)
51 if pixel_format not in rgb_formats:
52 log("rgb_encode reformatting because %s not in %s, supports_transparency=%s",
53 pixel_format, rgb_formats, supports_transparency)
54 if not rgb_reformat(image, rgb_formats, supports_transparency):
55 raise Exception("cannot find compatible rgb format to use for %s! (supported: %s)" % (
56 pixel_format, rgb_formats))
57 #get the new format:
58 pixel_format = bytestostr(image.get_pixel_format())
59 #switch encoding if necessary:
60 if len(pixel_format)==4:
61 coding = "rgb32"
62 elif len(pixel_format)==3:
63 coding = "rgb24"
64 else:
65 raise Exception("invalid pixel format %s" % pixel_format)
66 #we may still want to re-stride:
67 image.may_restride()
68 #always tell client which pixel format we are sending:
69 options = {"rgb_format" : pixel_format}
71 #compress here and return a wrapper so network code knows it is already zlib compressed:
72 pixels = image.get_pixels()
73 assert pixels, "failed to get pixels from %s" % image
74 width = image.get_width()
75 height = image.get_height()
76 stride = image.get_rowstride()
78 #compression stage:
79 level = 0
80 algo = "not"
81 l = len(pixels)
82 if l>=512 and speed<100:
83 if l>=4096:
84 #speed=99 -> level=1, speed=0 -> level=9
85 level = 1+max(0, min(8, int(100-speed)//12))
86 else:
87 #fewer pixels, make it more likely we won't bother compressing
88 #and use a lower level (max=5)
89 level = max(0, min(5, int(115-speed)//20))
90 if level>0:
91 cwrapper = compression.compressed_wrapper(coding, pixels, level=level,
92 zlib=rgb_zlib, lz4=rgb_lz4, lzo=rgb_lzo,
93 brotli=False, none=True)
94 algo = cwrapper.algorithm
95 if algo=="none" or len(cwrapper)>=(len(pixels)-32):
96 #no compression is enabled, or compressed is actually bigger!
97 #(fall through to uncompressed)
98 level = 0
99 else:
100 #add compressed marker:
101 options[algo] = level
102 #remove network layer compression marker
103 #so that this data will be decompressed by the decode thread client side:
104 cwrapper.level = 0
105 if level==0:
106 #can't pass a raw buffer to bencode / rencode,
107 #and even if we could, the image containing those pixels may be freed by the time we get to the encoder
108 algo = "not"
109 cwrapper = compression.Compressed(coding, pixels_to_bytes(pixels), True)
110 if pixel_format.find("A")>=0 or pixel_format.find("X")>=0:
111 bpp = 32
112 else:
113 bpp = 24
114 log("rgb_encode using level=%s for %5i bytes at %3i speed, %s compressed %4sx%-4s in %s/%s: %5s bytes down to %5s",
115 level, l, speed, algo, image.get_width(), image.get_height(), coding, pixel_format, len(pixels), len(cwrapper.data))
116 #wrap it using "Compressed" so the network layer receiving it
117 #won't decompress it (leave it to the client's draw thread)
118 return coding, cwrapper, options, width, height, stride, bpp
121def mmap_send(mmap, mmap_size, image, rgb_formats, supports_transparency):
122 if mmap_write is None:
123 if first_time("mmap_write missing"):
124 log.warn("Warning: cannot use mmap, no write method support")
125 return None
126 if image.get_pixel_format() not in rgb_formats:
127 if not rgb_reformat(image, rgb_formats, supports_transparency):
128 warning_key = "mmap_send(%s)" % image.get_pixel_format()
129 if first_time(warning_key):
130 log.warn("Waening: cannot use mmap to send %s" % image.get_pixel_format())
131 return None
132 start = monotonic_time()
133 data = image.get_pixels()
134 assert data, "failed to get pixels from %s" % image
135 mmap_data, mmap_free_size = mmap_write(mmap, mmap_size, data)
136 elapsed = monotonic_time()-start+0.000000001 #make sure never zero!
137 log("%s MBytes/s - %s bytes written to mmap in %.1f ms", int(len(data)/elapsed/1024/1024), len(data), 1000*elapsed)
138 if mmap_data is None:
139 return None
140 #replace pixels with mmap info:
141 return mmap_data, mmap_free_size, len(data)