Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/codecs/loader.py : 49%
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#!/usr/bin/env python
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.
7import sys
8import os.path
10from xpra.util import envbool, csv
11from xpra.log import Logger
12log = Logger("codec", "loader")
15#these codecs may well not load because we
16#do not require the libraries to be installed
17NOWARN = ["nvenc", "enc_x265", "enc_ffmpeg"]
19SELFTEST = envbool("XPRA_CODEC_SELFTEST", True)
20FULL_SELFTEST = envbool("XPRA_CODEC_FULL_SELFTEST", False)
22CODEC_FAIL_IMPORT = os.environ.get("XPRA_CODEC_FAIL_IMPORT", "").split(",")
23CODEC_FAIL_SELFTEST = os.environ.get("XPRA_CODEC_FAIL_SELFTEST", "").split(",")
25log("codec loader settings: SELFTEST=%s, FULL_SELFTEST=%s, CODEC_FAIL_IMPORT=%s, CODEC_FAIL_SELFTEST=%s",
26 SELFTEST, FULL_SELFTEST, CODEC_FAIL_IMPORT, CODEC_FAIL_SELFTEST)
28codec_errors = {}
29codecs = {}
30def codec_import_check(name, description, top_module, class_module, classnames):
31 log("%s:", name)
32 log(" codec_import_check%s", (name, description, top_module, class_module, classnames))
33 try:
34 try:
35 if name in CODEC_FAIL_IMPORT:
36 raise ImportError("codec found in fail import list")
37 __import__(top_module, {}, {}, [])
38 except ImportError as e:
39 log("failed to import %s (%s)", description, name)
40 log("", exc_info=True)
41 codec_errors[name] = str(e)
42 return None
43 except Exception as e:
44 log.warn(" cannot load %s (%s):", name, description, exc_info=True)
45 codec_errors[name] = str(e)
46 return None
47 classname = None
48 try:
49 #module is present
50 try:
51 log(" %s found, will check for %s in %s", top_module, classnames, class_module)
52 ic = __import__(class_module, {}, {}, classnames)
53 try:
54 #run init_module?
55 init_module = getattr(ic, "init_module", None)
56 log("%s: init_module=%s", class_module, init_module)
57 if init_module:
58 init_module()
60 if classnames:
61 for classname in classnames:
62 clazz = getattr(ic, classname)
63 log("%s: %s=%s", class_module, classname, clazz)
65 selftest = getattr(ic, "selftest", None)
66 log("%s.selftest=%s", name, selftest)
67 if SELFTEST and selftest:
68 if name in CODEC_FAIL_SELFTEST:
69 raise ImportError("codec found in fail selftest list")
70 try:
71 selftest(FULL_SELFTEST)
72 except Exception as e:
73 log.warn("Warning: %s failed its self test", name)
74 for x in str(e).splitlines():
75 log.warn(" %s", x)
76 log("%s failed", selftest, exc_info=True)
77 return None
78 finally:
79 cleanup_module = getattr(ic, "cleanup_module", None)
80 log("%s: cleanup_module=%s", class_module, cleanup_module)
81 if cleanup_module:
82 cleanup_module()
83 #log.warn("codec_import_check(%s, ..)=%s" % (name, ic))
84 log(" found %s : %s", name, ic)
85 codecs[name] = ic
86 return ic
87 except ImportError as e:
88 codec_errors[name] = str(e)
89 l = log.error
90 if name in NOWARN:
91 l = log.debug
92 l("Error importing %s (%s)", description, name)
93 l(" %s", e)
94 log("", exc_info=True)
95 except Exception as e:
96 codec_errors[name] = str(e)
97 if classname:
98 log.warn(" cannot load %s (%s): %s missing from %s",
99 name, description, classname, class_module, exc_info=True)
100 else:
101 log.warn(" cannot load %s (%s)",
102 name, description, exc_info=True)
103 return None
104codec_versions = {}
105def add_codec_version(name, top_module, version="get_version()", alt_version="__version__"):
106 try:
107 fieldnames = [x for x in (version, alt_version) if x is not None]
108 for fieldname in fieldnames:
109 f = fieldname
110 if f.endswith("()"):
111 f = version[:-2]
112 module = __import__(top_module, {}, {}, [f])
113 if not hasattr(module, f):
114 continue
115 v = getattr(module, f)
116 if fieldname.endswith("()") and v:
117 v = v()
118 global codec_versions
119 codec_versions[name] = v
120 #optional info:
121 if hasattr(module, "get_info"):
122 info = getattr(module, "get_info")
123 log(" %s %s.%s=%s", name, top_module, info, info())
124 return v
125 if name in codecs:
126 log.warn(" cannot find %s in %s", " or ".join(fieldnames), module)
127 else:
128 log(" no version information for missing codec %s", name)
129 except ImportError as e:
130 #not present
131 log(" cannot import %s: %s", name, e)
132 log("", exc_info=True)
133 except Exception as e:
134 log.warn("error during codec import: %s", e)
135 log.warn("", exc_info=True)
136 return None
138def xpra_codec_import(name, description, top_module, class_module, classname):
139 xpra_top_module = "xpra.codecs.%s" % top_module
140 xpra_class_module = "%s.%s" % (xpra_top_module, class_module)
141 if codec_import_check(name, description, xpra_top_module, xpra_class_module, classname):
142 version_name = name
143 if name.startswith("enc_") or name.startswith("dec_") or name.startswith("csc_"):
144 version_name = name[4:]
145 add_codec_version(version_name, xpra_class_module)
148CODEC_OPTIONS = {
149 #encoders:
150 "enc_pillow" : ("Pillow encoder", "pillow", "encoder", "encode"),
151 "enc_webp" : ("webp encoder", "webp", "encoder", "encode"),
152 "enc_jpeg" : ("JPEG encoder", "jpeg", "encoder", "encode"),
153 #video encoders:
154 "enc_vpx" : ("vpx encoder", "vpx", "encoder", "Encoder"),
155 "enc_x264" : ("x264 encoder", "enc_x264", "encoder", "Encoder"),
156 "enc_x265" : ("x265 encoder", "enc_x265", "encoder", "Encoder"),
157 "nvenc" : ("nvenc encoder", "nvenc", "encoder", "Encoder"),
158 "enc_ffmpeg" : ("ffmpeg encoder", "enc_ffmpeg", "encoder", "Encoder"),
159 #csc:
160 "csc_swscale" : ("swscale colorspace conversion", "csc_swscale", "colorspace_converter", "ColorspaceConverter"),
161 "csc_libyuv" : ("libyuv colorspace conversion", "csc_libyuv", "colorspace_converter", "ColorspaceConverter"),
162 "csc_cython" : ("cython colorspace conversion", "csc_cython", "colorspace_converter", "ColorspaceConverter"),
163 #decoders:
164 "dec_pillow" : ("Pillow decoder", "pillow", "decoder", "decompress"),
165 "dec_webp" : ("webp decoder", "webp", "decoder", "decompress"),
166 "dec_jpeg" : ("JPEG decoder", "jpeg", "decoder", "decompress_to_rgb", "decompress_to_yuv"),
167 #video decoders:
168 "dec_vpx" : ("vpx decoder", "vpx", "decoder", "Decoder"),
169 "dec_avcodec2" : ("avcodec2 decoder", "dec_avcodec2", "decoder", "Decoder"),
170 }
172def load_codec(name):
173 if has_codec(name):
174 return
175 try:
176 option = CODEC_OPTIONS[name]
177 description, top_module, class_module = option[:3]
178 classnames = option[3:]
179 except KeyError:
180 log("load_codec(%s)", name, exc_info=True)
181 log.error("Error: invalid codec name '%s'", name)
182 else:
183 xpra_codec_import(name, description, top_module, class_module, classnames)
186def load_codecs(encoders=True, decoders=True, csc=True, video=True):
187 show = []
188 log("loading codecs")
190 def load(*names):
191 for name in names:
192 load_codec(name)
194 if encoders:
195 show += list(ENCODER_CODECS)
196 load(*ENCODER_CODECS)
197 if video:
198 show += list(ENCODER_VIDEO_CODECS)
199 load(*ENCODER_VIDEO_CODECS)
200 if csc and video:
201 show += list(CSC_CODECS)
202 load(*CSC_CODECS)
203 if decoders:
204 show += list(DECODER_CODECS)
205 load(*DECODER_CODECS)
206 if video:
207 show += list(DECODER_VIDEO_CODECS)
208 load(*DECODER_VIDEO_CODECS)
210 log("done loading codecs")
211 log("found:")
212 #print("codec_status=%s" % codecs)
213 for name in sorted(ALL_CODECS):
214 log("* %s : %s %s" % (name.ljust(20), str(name in codecs).ljust(10), codecs.get(name, "")))
215 log("codecs versions:")
216 for name, version in codec_versions.items():
217 log("* %s : %s" % (name.ljust(20), version))
220def get_codec_error(name):
221 return codec_errors.get(name)
223def get_codec(name):
224 return codecs.get(name)
226def get_codec_version(name):
227 return codec_versions.get(name)
229def has_codec(name) -> bool:
230 return name in codecs
233CSC_CODECS = "csc_swscale", "csc_cython", "csc_libyuv"
234ENCODER_CODECS = "enc_pillow", "enc_webp", "enc_jpeg"
235ENCODER_VIDEO_CODECS = "enc_vpx", "enc_x264", "enc_x265", "nvenc", "enc_ffmpeg"
236DECODER_CODECS = "dec_pillow", "dec_webp", "dec_jpeg"
237DECODER_VIDEO_CODECS = "dec_vpx", "dec_avcodec2"
239ALL_CODECS = tuple(set(CSC_CODECS + ENCODER_CODECS + ENCODER_VIDEO_CODECS + DECODER_CODECS + DECODER_VIDEO_CODECS))
242def get_rgb_compression_options():
243 from xpra.net import compression
244 compressors = compression.get_enabled_compressors()
245 compressors = [x for x in compressors if x!="brotli"]
246 RGB_COMP_OPTIONS = ["Raw RGB"]
247 if compressors:
248 RGB_COMP_OPTIONS += ["/".join(compressors)]
249 return RGB_COMP_OPTIONS
251def get_encoding_name(encoding):
252 ENCODINGS_TO_NAME = {
253 "auto" : "automatic",
254 "h264" : "H.264",
255 "h265" : "H.265",
256 "mpeg4" : "MPEG4",
257 "vp8" : "VP8",
258 "webp" : "WebP",
259 "vp9" : "VP9",
260 "png" : "PNG (24/32bpp)",
261 "png/P" : "PNG (8bpp colour)",
262 "png/L" : "PNG (8bpp grayscale)",
263 "jpeg" : "JPEG",
264 "rgb" : " + ".join(get_rgb_compression_options()) + " (24/32bpp)",
265 }
266 return ENCODINGS_TO_NAME.get(encoding, encoding)
268def get_encoding_help(encoding):
269 from xpra.net import compression
270 compressors = compression.get_enabled_compressors()
271 compressors = [x for x in compressors if x!="brotli"]
272 return {
273 "auto" : "automatic mode (recommended)",
274 "grayscale" : "same as 'auto' but in grayscale mode",
275 "h264" : "H.264 video codec",
276 "h265" : "H.265 (HEVC) video codec (not recommended)",
277 "vp8" : "VP8 video codec",
278 "vp9" : "VP9 video codec",
279 "mpeg4" : "MPEG-4 video codec",
280 "png" : "Portable Network Graphics (lossless, 24bpp or 32bpp for transparency)",
281 "png/P" : "Portable Network Graphics (lossy, 8bpp colour)",
282 "png/L" : "Portable Network Graphics (lossy, 8bpp grayscale)",
283 "webp" : "WebP compression (supports lossless and lossy modes)",
284 "jpeg" : "JPEG lossy compression",
285 "rgb" : "Raw RGB pixels, lossless,"
286 +" compressed using %s (24bpp or 32bpp for transparency)" % (" or ".join(compressors)),
287 "scroll" : "motion vectors, supplemented with picture codecs",
288 }.get(encoding)
291def encodings_help(encodings):
292 from xpra.codecs.codec_constants import HELP_ORDER
293 h = []
294 for e in HELP_ORDER:
295 if e in encodings:
296 h.append(encoding_help(e))
297 return h
299def encoding_help(encoding):
300 ehelp = get_encoding_help(encoding) or ""
301 return encoding.ljust(12) + ehelp
305def main():
306 from xpra.platform import program_context
307 from xpra.log import enable_color, LOG_FORMAT, NOPREFIX_FORMAT
308 from xpra.util import print_nested_dict, pver
309 with program_context("Loader", "Encoding Info"):
310 verbose = "-v" in sys.argv or "--verbose" in sys.argv
311 format_string = NOPREFIX_FORMAT
312 if verbose:
313 format_string = LOG_FORMAT
314 log.enable_debug()
315 enable_color(format_string)
317 load_codecs()
318 #not really a codec, but gets used by codecs, so include version info:
319 add_codec_version("numpy", "numpy")
320 print("codecs and csc modules found:")
321 #print("codec_status=%s" % codecs)
322 for name in sorted(ALL_CODECS):
323 mod = codecs.get(name, "")
324 f = mod
325 if mod and hasattr(mod, "__file__"):
326 f = mod.__file__
327 if f.startswith(os.getcwd()):
328 f = f[len(os.getcwd()):]
329 if f.startswith(os.path.sep):
330 f = f[1:]
331 print("* %s : %s" % (name.ljust(20), f))
332 if mod and verbose:
333 try:
334 if name.find("csc")>=0:
335 cs = list(mod.get_input_colorspaces())
336 for c in list(cs):
337 cs += list(mod.get_output_colorspaces(c))
338 print(" colorspaces: %s" % csv(list(set(cs))))
339 elif name.find("enc")>=0 or name.find("dec")>=0:
340 encodings = mod.get_encodings()
341 print(" encodings: %s" % csv(encodings))
342 try:
343 i = mod.get_info()
344 for k,v in sorted(i.items()):
345 print(" %s = %s" % (k,v))
346 except Exception:
347 pass
348 except Exception as e:
349 print("error getting extra information on %s: %s" % (name, e))
350 print("")
351 print("codecs versions:")
352 def forcever(v):
353 return pver(v, numsep=".", strsep=".").lstrip("v")
354 print_nested_dict(codec_versions, vformat=forcever)
355 return 0
358if __name__ == "__main__":
359 main()