From 0c3b4c70ba999540971ebaca2dce767b0dd75ac5 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 3 Feb 2015 12:37:44 +0000 Subject: [PATCH] #796: * refactor all encoders so we pass in both the quality and speed to use (very much like the other encoding functions) so that we can deal with both at once * re-init code for nvenc4 so we can switch to/from yuv444 mode git-svn-id: https://xpra.org/svn/Xpra/trunk@8606 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/codecs/enc_proxy/encoder.py | 14 +- src/xpra/codecs/enc_x264/encoder.pyx | 68 +++---- src/xpra/codecs/enc_x265/encoder.pyx | 9 +- src/xpra/codecs/nvenc3/encoder.pyx | 9 +- src/xpra/codecs/nvenc4/constants.txt | 1 + src/xpra/codecs/nvenc4/encoder.pyx | 236 ++++++++++++++-------- src/xpra/codecs/vpx/encoder.pyx | 6 +- src/xpra/server/proxy_instance_process.py | 7 +- src/xpra/server/window_source.py | 1 + src/xpra/server/window_video_source.py | 4 +- 10 files changed, 196 insertions(+), 159 deletions(-) diff --git a/src/xpra/codecs/enc_proxy/encoder.py b/src/xpra/codecs/enc_proxy/encoder.py index e364804e58..39070b6852 100755 --- a/src/xpra/codecs/enc_proxy/encoder.py +++ b/src/xpra/codecs/enc_proxy/encoder.py @@ -117,10 +117,12 @@ def clean(self): #@DuplicatedSignature self.time = 0 self.first_frame_timestamp = 0 - def compress_image(self, image, options={}): + def compress_image(self, image, quality=-1, speed=-1, options={}): log("compress_image(%s, %s)", image, options) #pass the pixels as they are assert image.get_planes()==ImageWrapper.PACKED, "invalid number of planes: %s" % image.get_planes() + self.quality = quality + self.speed = speed pixels = str(image.get_pixels()) #info used by proxy encoder: client_options = { @@ -132,8 +134,8 @@ def compress_image(self, image, options={}): #redundant metadata: #"width" : image.get_width(), #"height" : image.get_height(), - "quality" : options.get("quality", self.quality), - "speed" : options.get("speed", self.speed), + "quality" : quality, + "speed" : speed, "timestamp" : image.get_timestamp(), "rowstride" : image.get_rowstride(), "depth" : image.get_depth(), @@ -150,9 +152,3 @@ def compress_image(self, image, options={}): self.last_frame_times.append(time.time()) self.frames += 1 return pixels, client_options - - def set_encoding_speed(self, pct): - self.speed = int(min(100, max(0, pct))) - - def set_encoding_quality(self, pct): - self.quality = int(min(100, max(0, pct))) diff --git a/src/xpra/codecs/enc_x264/encoder.pyx b/src/xpra/codecs/enc_x264/encoder.pyx index 9d35987ed0..951125ba28 100644 --- a/src/xpra/codecs/enc_x264/encoder.pyx +++ b/src/xpra/codecs/enc_x264/encoder.pyx @@ -424,7 +424,7 @@ cdef class Encoder: return profile - def compress_image(self, image, options={}): + def compress_image(self, image, quality=-1, speed=-1, options={}): cdef x264_nal_t *nals = NULL cdef int i_nals = 0 cdef x264_picture_t pic_out @@ -435,26 +435,16 @@ cdef class Encoder: cdef Py_ssize_t pic_buf_len = 0 cdef char *out - cdef int quality_override = options.get("quality", -1) - cdef int speed_override = options.get("speed", -1) - cdef int saved_quality = self.quality - cdef int saved_speed = self.speed cdef int i #@DuplicatedSignature start = time.time() if self.frames==0: self.first_frame_timestamp = image.get_timestamp() - #deal with overriden speed or quality settings: - #(temporarily change the settings for this encode only): - speed = self.speed - if speed_override>=0 and saved_speed!=speed_override: - self.set_encoding_speed(speed_override) - speed = speed_override - quality = self.quality - if quality_override>=0 and saved_quality!=quality_override: - self.set_encoding_quality(quality_override) - quality = quality_override + if speed>=0 and abs(self.speed-speed)>5: + self.set_encoding_speed(speed) + if quality>=0 and abs(self.quality-quality)>5: + self.set_encoding_quality(quality) assert self.context!=NULL pixels = image.get_pixels() istrides = image.get_rowstride() @@ -482,32 +472,26 @@ cdef class Encoder: pic_in.img.i_plane = 3 pic_in.i_pts = image.get_timestamp()-self.first_frame_timestamp - try: - with nogil: - frame_size = x264_encoder_encode(self.context, &nals, &i_nals, &pic_in, &pic_out) - if frame_size < 0: - log.error("x264 encoding error: frame_size is invalid!") - return None - out = nals[0].p_payload - cdata = out[:frame_size] - self.bytes_out += frame_size - #info for client: - client_options = { - "frame" : self.frames, - "pts" : pic_out.i_pts, - "quality" : min(99, quality), - "speed" : speed} - #accounting: - end = time.time() - self.time += end-start - self.frames += 1 - self.last_frame_times.append((start, end)) - return cdata, client_options - finally: - if speed_override>=0 and saved_speed!=speed_override: - self.set_encoding_speed(saved_speed) - if quality_override>=0 and saved_quality!=quality_override: - self.set_encoding_quality(saved_quality) + with nogil: + frame_size = x264_encoder_encode(self.context, &nals, &i_nals, &pic_in, &pic_out) + if frame_size < 0: + log.error("x264 encoding error: frame_size is invalid!") + return None + out = nals[0].p_payload + cdata = out[:frame_size] + self.bytes_out += frame_size + #info for client: + client_options = { + "frame" : self.frames, + "pts" : pic_out.i_pts, + "quality" : min(99, quality), + "speed" : speed} + #accounting: + end = time.time() + self.time += end-start + self.frames += 1 + self.last_frame_times.append((start, end)) + return cdata, client_options def set_encoding_speed(self, int pct): @@ -560,7 +544,7 @@ def selftest(): e.init_context(w, h, "YUV420P", ["YUV420P"], encoding, w, h, (1,1), {}) from xpra.codecs.image_wrapper import ImageWrapper image = ImageWrapper(0, 0, w, h, [y, u ,v], "YUV420P", 32, [w, w/2, w/2], planes=ImageWrapper.PACKED, thread_safe=True) - c = e.compress_image(image, {}) + c = e.compress_image(image) #import binascii #print("compressed data(%s)=%s" % (encoding, binascii.hexlify(str(c)))) finally: diff --git a/src/xpra/codecs/enc_x265/encoder.pyx b/src/xpra/codecs/enc_x265/encoder.pyx index 0e4fe68448..f2297fa867 100644 --- a/src/xpra/codecs/enc_x265/encoder.pyx +++ b/src/xpra/codecs/enc_x265/encoder.pyx @@ -406,7 +406,7 @@ cdef class Encoder: return self.src_format - def compress_image(self, image, options={}): + def compress_image(self, image, quality=-1, speed=-1, options={}): cdef x265_nal *nal cdef uint32_t nnal = 0 cdef int r = 0 @@ -486,10 +486,3 @@ cdef class Encoder: self.frames += 1 log("x265 compressed data size: %s, client options=%s", frame_size, client_options) return "".join(data), client_options - - - def set_encoding_speed(self, int pct): - pass - - def set_encoding_quality(self, int pct): - pass diff --git a/src/xpra/codecs/nvenc3/encoder.pyx b/src/xpra/codecs/nvenc3/encoder.pyx index bfa8d10235..77e475eec1 100644 --- a/src/xpra/codecs/nvenc3/encoder.pyx +++ b/src/xpra/codecs/nvenc3/encoder.pyx @@ -1701,13 +1701,6 @@ cdef class Encoder: def get_src_format(self): return self.src_format - def set_encoding_speed(self, speed): - self.speed = max(0, min(100, speed)) - self.update_bitrate() - - def set_encoding_quality(self, quality): - self.quality = max(0, min(100, quality)) - def update_bitrate(self): #use an exponential scale so for a 1Kx1K image (after scaling), roughly: #speed=0 -> 1Mbit/s @@ -1736,7 +1729,7 @@ cdef class Encoder: r = self.functionList.nvEncEncodePicture(self.context, &picParams) raiseNVENC(r, "flushing encoder buffer") - def compress_image(self, image, options={}, retry=0): + def compress_image(self, image, quality=-1, speed=-1, options={}, retry=0): self.cuda_context.push() try: try: diff --git a/src/xpra/codecs/nvenc4/constants.txt b/src/xpra/codecs/nvenc4/constants.txt index 5f113c955a..a6145a7eb4 100644 --- a/src/xpra/codecs/nvenc4/constants.txt +++ b/src/xpra/codecs/nvenc4/constants.txt @@ -61,3 +61,4 @@ NV_ENC_ERR_RESOURCE_NOT_MAPPED NV_ENC_CAPS_MB_PER_SEC_MAX NV_ENC_CAPS_SUPPORT_YUV444_ENCODE NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE +NV_ENC_RECONFIGURE_PARAMS_VER \ No newline at end of file diff --git a/src/xpra/codecs/nvenc4/encoder.pyx b/src/xpra/codecs/nvenc4/encoder.pyx index b044bb0cfb..53b66557ee 100644 --- a/src/xpra/codecs/nvenc4/encoder.pyx +++ b/src/xpra/codecs/nvenc4/encoder.pyx @@ -35,7 +35,7 @@ DESIRED_PRESET = os.environ.get("XPRA_NVENC_PRESET", "") #NVENC requires compute capability value 0x30 or above: MIN_COMPUTE = 0x30 -YUV444_THRESHOLD = int(os.environ.get("XPRA_NVENC_YUV444_THRESHOLD", "80")) +YUV444_THRESHOLD = int(os.environ.get("XPRA_NVENC_YUV444_THRESHOLD", "85")) LOSSLESS_THRESHOLD = int(os.environ.get("XPRA_NVENC_LOSSLESS_THRESHOLD", "100")) QP_MAX_VALUE = 51 #newer versions of ffmpeg can decode up to 63 @@ -1132,6 +1132,7 @@ cdef class Encoder: cdef int encoder_width cdef int encoder_height cdef object src_format + cdef object dst_formats cdef object scaling cdef int speed cdef int quality @@ -1155,6 +1156,7 @@ cdef class Encoder: #NVENC: cdef NV_ENCODE_API_FUNCTION_LIST *functionList #@DuplicatedSignature cdef void *context + cdef GUID codec cdef NV_ENC_REGISTERED_PTR inputHandle cdef object inputBuffer cdef object cudaInputBuffer @@ -1179,11 +1181,15 @@ cdef class Encoder: cdef object __weakref__ - cdef GUID get_codec(self): + cdef GUID init_codec(self): codecs = self.query_codecs() #codecs={'H264': '6BC82762-4E63-4CA4-AA85-1E50F321F6BF'} assert self.codec_name in codecs, "%s not supported!?" % self.codec_name - return c_parseguid(codecs.get(self.codec_name)) + self.codec = c_parseguid(codecs.get(self.codec_name)) + return self.codec + + cdef GUID get_codec(self): + return self.codec cdef GUID get_preset(self, GUID codec): presets = self.query_presets(codec) @@ -1233,6 +1239,7 @@ cdef class Encoder: self.encoder_width = roundup(width*v/u, 32) self.encoder_height = roundup(height*v/u, 32) self.src_format = src_format + self.dst_formats = dst_formats self.codec_name = "H264" self.preset_name = None self.frames = 0 @@ -1243,20 +1250,14 @@ cdef class Encoder: self.update_bitrate() start = time.time() - global YUV444P_ENABLED, LOSSLESS_ENABLED - if YUV444P_ENABLED and "YUV444P" in dst_formats and ((quality>=YUV444_THRESHOLD and v==1 and u==1) or ("YUV420P" not in dst_formats)): - self.pixel_format = "YUV444P" - #3 full planes: - plane_size_div = 1 - if LOSSLESS_ENABLED and quality>=LOSSLESS_THRESHOLD: - self.lossless = 1 - elif "YUV420P" in dst_formats: - self.pixel_format = "NV12" + plane_size_div = 1 + if not YUV444P_ENABLED or "YUV444P" not in dst_formats: + #we don't need as much memory reserved with NV12 / YUV420: #1 full Y plane and 2 U+V planes subsampled by 4: plane_size_div = 2 - else: - raise Exception("no compatible formats found!") + self.pixel_format = self.get_target_pixel_format(self.quality) + self.lossless = self.get_target_lossless(self.pixel_format, self.quality) self.cuda_device_id, self.cuda_device = select_device(options.get("cuda_device", -1), min_compute=MIN_COMPUTE) log("using pixel format %s with device %s", self.pixel_format, device_info(self.cuda_device)) try: @@ -1275,6 +1276,23 @@ cdef class Encoder: end = time.time() log("init_context%s took %1.fms", (width, height, src_format, quality, speed, options), (end-start)*1000.0) + def get_target_pixel_format(self, quality): + global YUV444P_ENABLED, LOSSLESS_ENABLED + x,y = self.scaling + if YUV444P_ENABLED and ("YUV444P" in self.dst_formats) and ((quality>=YUV444_THRESHOLD and x==1 and y==1) or ("YUV420P" not in self.dst_formats)): + return "YUV444P" + elif "YUV420P" in self.dst_formats: + return "NV12" + else: + raise Exception("no compatible formats found!") + + def get_target_lossless(self, pixel_format, quality): + if pixel_format!="YUV444": + return False + if LOSSLESS_ENABLED and quality>=LOSSLESS_THRESHOLD: + return True + return False + cdef init_cuda(self): cdef int plane_size_div cdef int max_input_stride @@ -1349,38 +1367,59 @@ cdef class Encoder: self.cuda_context.pop() cdef init_nvenc(self): - cdef GUID codec - cdef GUID preset cdef NV_ENC_INITIALIZE_PARAMS *params = NULL - cdef NV_ENC_CONFIG *config = NULL - cdef NV_ENC_PRESET_CONFIG *presetConfig = NULL #@DuplicatedSignature - cdef NV_ENC_REGISTER_RESOURCE registerResource - cdef NV_ENC_CREATE_INPUT_BUFFER createInputBufferParams - cdef NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBufferParams - cdef long resource - cdef Py_ssize_t size - cdef unsigned char* cptr = NULL cdef NVENCSTATUS r self.functionList = malloc(sizeof(NV_ENCODE_API_FUNCTION_LIST)) memset(self.functionList, 0, sizeof(NV_ENCODE_API_FUNCTION_LIST)) self.open_encode_session() - codec = self.get_codec() - preset = self.get_preset(codec) + + cdef GUID codec = self.init_codec() + params = malloc(sizeof(NV_ENC_INITIALIZE_PARAMS)) + assert params!=NULL + try: + self.init_params(codec, params) + log("nvEncInitializeEncoder using encode=%s", codecstr(codec)) + with nogil: + r = self.functionList.nvEncInitializeEncoder(self.context, params) + raiseNVENC(r, "initializing encoder") + log("NVENC initialized with '%s' codec and '%s' preset" % (self.codec_name, self.preset_name)) + + self.dump_caps(codec) + self.init_buffers() + finally: + if params.encodeConfig!=NULL: + free(params.encodeConfig) + free(params) + + cdef dump_caps(self, GUID codec): + #test all caps: + for cap, descr in CAPS_NAMES.items(): + if cap!=NV_ENC_CAPS_EXPOSED_COUNT: + v = self.query_encoder_caps(codec, cap) + log("%s=%s", descr, v) + + cdef NV_ENC_INITIALIZE_PARAMS *init_params(self, GUID codec, NV_ENC_INITIALIZE_PARAMS *params): + #caller must free the config! + cdef GUID preset + cdef NV_ENC_CONFIG *config = NULL + cdef NV_ENC_PRESET_CONFIG *presetConfig = NULL #@DuplicatedSignature + + preset = self.get_preset(self.codec) self.preset_name = CODEC_PRESETS_GUIDS.get(guidstr(preset), guidstr(preset)) + log("init_params(%s) using preset=%s", codecstr(codec), presetstr(preset)) input_format = BUFFER_FORMAT[self.bufferFmt] input_formats = self.query_input_formats(codec) assert input_format in input_formats, "%s does not support %s (only: %s)" % (self.codec_name, input_format, input_formats) - try: - #TODO: undo malloc and use local var - params = malloc(sizeof(NV_ENC_INITIALIZE_PARAMS)) - assert memset(params, 0, sizeof(NV_ENC_INITIALIZE_PARAMS))!=NULL - config = malloc(sizeof(NV_ENC_CONFIG)) - presetConfig = self.get_preset_config(self.preset_name, codec, preset) - assert presetConfig!=NULL, "could not find preset %s" % self.preset_name + assert memset(params, 0, sizeof(NV_ENC_INITIALIZE_PARAMS))!=NULL + config = malloc(sizeof(NV_ENC_CONFIG)) + + presetConfig = self.get_preset_config(self.preset_name, codec, preset) + assert presetConfig!=NULL, "could not find preset %s" % self.preset_name + try: assert memcpy(config, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG))!=NULL params.version = NV_ENC_INITIALIZE_PARAMS_VER @@ -1425,55 +1464,49 @@ cdef class Encoder: #config.encodeCodecConfig.h264Config.h264VUIParameters.colourPrimaries = 1 #AVCOL_PRI_BT709 ? #config.encodeCodecConfig.h264Config.h264VUIParameters.transferCharacteristics = 1 #AVCOL_TRC_BT709 ? #config.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = 1 - - log("nvEncInitializeEncoder using encode=%s, preset=%s", codecstr(codec), presetstr(preset)) - with nogil: - r = self.functionList.nvEncInitializeEncoder(self.context, params) - raiseNVENC(r, "initializing encoder") - log("NVENC initialized with '%s' codec and '%s' preset" % (self.codec_name, self.preset_name)) - - #test all caps: - for cap, descr in CAPS_NAMES.items(): - if cap!=NV_ENC_CAPS_EXPOSED_COUNT: - v = self.query_encoder_caps(codec, cap) - log("%s=%s", descr, v) - - #register CUDA input buffer: - memset(®isterResource, 0, sizeof(NV_ENC_REGISTER_RESOURCE)) - registerResource.version = NV_ENC_REGISTER_RESOURCE_VER - registerResource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR - resource = int(self.cudaOutputBuffer) - registerResource.resourceToRegister = resource - registerResource.width = self.encoder_width - registerResource.height = self.encoder_height - registerResource.pitch = self.outputPitch - registerResource.bufferFormat = self.bufferFmt - log("nvEncRegisterResource(%#x)", ®isterResource) - with nogil: - r = self.functionList.nvEncRegisterResource(self.context, ®isterResource) - raiseNVENC(r, "registering CUDA input buffer") - self.inputHandle = registerResource.registeredResource - log("input handle for CUDA buffer: %#x", self.inputHandle) - - #allocate output buffer: - memset(&createBitstreamBufferParams, 0, sizeof(NV_ENC_CREATE_BITSTREAM_BUFFER)) - createBitstreamBufferParams.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER - #this is the uncompressed size - must be big enough for the compressed stream: - createBitstreamBufferParams.size = min(1024*1024*2, self.encoder_width*self.encoder_height*3/2) - createBitstreamBufferParams.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED - log("nvEncCreateBitstreamBuffer(%#x)", &createBitstreamBufferParams) - with nogil: - r = self.functionList.nvEncCreateBitstreamBuffer(self.context, &createBitstreamBufferParams) - raiseNVENC(r, "creating output buffer") - self.bitstreamBuffer = createBitstreamBufferParams.bitstreamBuffer - log("output bitstream buffer=%#x", self.bitstreamBuffer) + return params finally: if presetConfig!=NULL: free(presetConfig) - if config!=NULL: - free(config) - if params!=NULL: - free(params) + + def init_buffers(self): + cdef NV_ENC_REGISTER_RESOURCE registerResource + cdef NV_ENC_CREATE_INPUT_BUFFER createInputBufferParams + cdef NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBufferParams + cdef long resource + cdef Py_ssize_t size + cdef unsigned char* cptr = NULL + cdef NVENCSTATUS r # + #register CUDA input buffer: + memset(®isterResource, 0, sizeof(NV_ENC_REGISTER_RESOURCE)) + registerResource.version = NV_ENC_REGISTER_RESOURCE_VER + registerResource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR + resource = int(self.cudaOutputBuffer) + registerResource.resourceToRegister = resource + registerResource.width = self.encoder_width + registerResource.height = self.encoder_height + registerResource.pitch = self.outputPitch + registerResource.bufferFormat = self.bufferFmt + log("nvEncRegisterResource(%#x)", ®isterResource) + with nogil: + r = self.functionList.nvEncRegisterResource(self.context, ®isterResource) + raiseNVENC(r, "registering CUDA input buffer") + self.inputHandle = registerResource.registeredResource + log("input handle for CUDA buffer: %#x", self.inputHandle) + + #allocate output buffer: + memset(&createBitstreamBufferParams, 0, sizeof(NV_ENC_CREATE_BITSTREAM_BUFFER)) + createBitstreamBufferParams.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER + #this is the uncompressed size - must be big enough for the compressed stream: + createBitstreamBufferParams.size = min(1024*1024*2, self.encoder_width*self.encoder_height*3/2) + createBitstreamBufferParams.memoryHeap = NV_ENC_MEMORY_HEAP_SYSMEM_CACHED + log("nvEncCreateBitstreamBuffer(%#x)", &createBitstreamBufferParams) + with nogil: + r = self.functionList.nvEncCreateBitstreamBuffer(self.context, &createBitstreamBufferParams) + raiseNVENC(r, "creating output buffer") + self.bitstreamBuffer = createBitstreamBufferParams.bitstreamBuffer + log("output bitstream buffer=%#x", self.bitstreamBuffer) + def get_info(self): #@DuplicatedSignature cdef double pps @@ -1558,6 +1591,7 @@ cdef class Encoder: self.encoder_width = 0 self.encoder_height = 0 self.src_format = "" + self.dst_formats = [] self.scaling = None self.speed = 0 self.quality = 0 @@ -1648,11 +1682,45 @@ cdef class Encoder: return self.src_format def set_encoding_speed(self, speed): - self.speed = max(0, min(100, speed)) - self.update_bitrate() + if self.speed!=speed: + self.speed = speed + self.update_bitrate() def set_encoding_quality(self, quality): - self.quality = max(0, min(100, quality)) + cdef NV_ENC_RECONFIGURE_PARAMS reconfigure_params + if self.quality!=quality: + if quality &pic_buf, &pic_buf_len)==0 pic_in[i] = pic_buf strides[i] = istrides[i] + self.set_encoding_speed(speed) + self.set_encoding_quality(quality) return self.do_compress_image(pic_in, strides), {"frame" : self.frames, "quality" : min(99, self.quality), "speed" : self.speed} @@ -483,7 +485,7 @@ def selftest(): e.init_context(w, h, "YUV420P", ["YUV420P"], encoding, w, h, (1,1), {}) from xpra.codecs.image_wrapper import ImageWrapper image = ImageWrapper(0, 0, w, h, [y, u ,v], "YUV420P", 32, [w, w/2, w/2], planes=ImageWrapper.PACKED, thread_safe=True) - c = e.compress_image(image, {}) + c = e.compress_image(image) #import binascii #print("compressed data(%s)=%s" % (encoding, binascii.hexlify(str(c)))) finally: diff --git a/src/xpra/server/proxy_instance_process.py b/src/xpra/server/proxy_instance_process.py index e6a41167e5..5da228201e 100644 --- a/src/xpra/server/proxy_instance_process.py +++ b/src/xpra/server/proxy_instance_process.py @@ -668,14 +668,9 @@ def passthrough(): 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 - else: - if quality>=0: - ve.set_encoding_quality(quality) - if speed>=0: - ve.set_encoding_speed(speed) #actual video compression: log("proxy compression using %s with quality=%s, speed=%s", ve, quality, speed) - data, client_options = ve.compress_image(image, encoder_options) + data, client_options = ve.compress_image(image, quality, speed, encoder_options) self.video_encoders_last_used_time[wid] = time.time() return send_updated(ve.get_encoding(), Compressed(encoding, data), client_options) diff --git a/src/xpra/server/window_source.py b/src/xpra/server/window_source.py index 3f3d98141e..f864c1dabe 100644 --- a/src/xpra/server/window_source.py +++ b/src/xpra/server/window_source.py @@ -741,6 +741,7 @@ def get_speed(self, coding): def update_quality(self): + log("update_quality() suspended=%s, mmap=%s, encoding=%s", self.suspended, self._mmap, self.encoding) if self.suspended or self._mmap: return if self.encoding in ("rgb", "png", "png/P", "png/L"): diff --git a/src/xpra/server/window_video_source.py b/src/xpra/server/window_video_source.py index 92c81c5560..c1867d9228 100644 --- a/src/xpra/server/window_video_source.py +++ b/src/xpra/server/window_video_source.py @@ -1239,7 +1239,9 @@ def video_encode(self, encoding, image, options): csc_image, csc, enc_width, enc_height = self.csc_image(image, width, height) start = time.time() - ret = self._video_encoder.compress_image(csc_image, options) + quality = max(0, min(100, self._current_quality)) + speed = max(0, min(100, self._current_speed)) + ret = self._video_encoder.compress_image(csc_image, quality, speed, options) if ret is None: log.error("video_encode: ouch, %s compression failed", encoding) return None