-
-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add boost score to codec_spec so we can favour the new proxy encoder * VideoHelper is no longer a singleton, so we can add encoders to it (and csc if we wanted to) for each client. Use this to add the proxy encoder when requested. * proxy encoder which just forwards the RGB pixel data with added metadata (scaling, etc) * proxy server sends the codec specs for the encodings it wants to handle itself (for now, use "h264" via both "x264" and "nvenc" to make it easier to test) * proxy server detects RGB proxy encoder frames, creates an encoder when required and uses it to encode them. Also keeps track of "lost-window" to free encoder context. git-svn-id: https://xpra.org/svn/Xpra/trunk@5286 3bb7dfac-3a0b-4e04-842a-767bc560f471
- Loading branch information
Showing
6 changed files
with
323 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# coding=utf8 | ||
# This file is part of Xpra. | ||
# Copyright (C) 2012, 2013 Antoine Martin <[email protected]> | ||
# Copyright (C) 2012-2014 Antoine Martin <[email protected]> | ||
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any | ||
# later version. See the file COPYING for details. | ||
|
||
|
@@ -55,6 +55,7 @@ def __init__(self, codec_class, codec_type="", encoding=None, | |
setup_cost=50, cpu_cost=100, gpu_cost=0, | ||
min_w=1, min_h=1, max_w=4*1024, max_h=4*1024, | ||
can_scale=False, | ||
score_boost=0, | ||
width_mask=0xFFFF, height_mask=0xFFFF): | ||
self.codec_class = codec_class #ie: xpra.codecs.enc_x264.encoder.Encoder | ||
self.codec_type = codec_type #ie: "nvenc" | ||
|
@@ -63,6 +64,7 @@ def __init__(self, codec_class, codec_type="", encoding=None, | |
self.setup_cost = setup_cost | ||
self.cpu_cost = cpu_cost | ||
self.gpu_cost = gpu_cost | ||
self.score_boost = score_boost | ||
self.min_w = min_w | ||
self.min_h = min_h | ||
self.max_w = max_w | ||
|
@@ -72,6 +74,16 @@ def __init__(self, codec_class, codec_type="", encoding=None, | |
self.can_scale = can_scale | ||
self.encoding = encoding #ie: "h264" | ||
|
||
def to_dict(self): | ||
d = {} | ||
for k in ("codec_class", "codec_type", "quality", "speed", | ||
"setup_cost", "cpu_cost", "gpu_cost", "score_boost", | ||
"min_w", "min_h", "max_w", "max_h", | ||
"width_mask", "height_mask", | ||
"can_scale", "encoding"): | ||
d[k] = getattr(self, k) | ||
return d | ||
|
||
def get_runtime_factor(self): | ||
#a cost multiplier that some encoder may want to override | ||
#1.0 means no change: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# This file is part of Xpra. | ||
# Copyright (C) 2014 Antoine Martin <[email protected]> | ||
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any | ||
# later version. See the file COPYING for details. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# This file is part of Xpra. | ||
# Copyright (C) 2014 Antoine Martin <[email protected]> | ||
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any | ||
# later version. See the file COPYING for details. | ||
|
||
import time | ||
|
||
from xpra.log import Logger, debug_if_env | ||
log = Logger() | ||
debug = debug_if_env(log, "XPRA_PROXYVIDEO_DEBUG") | ||
error = log.error | ||
|
||
from xpra.codecs.image_wrapper import ImageWrapper | ||
from xpra.deque import maxdeque | ||
|
||
|
||
def get_version(): | ||
return (0, 1) | ||
|
||
def get_type(): | ||
return "proxy" | ||
|
||
def get_info(): | ||
return {"version" : get_version()} | ||
|
||
def get_encodings(): | ||
return ["proxy"] | ||
|
||
def init_module(): | ||
#nothing to do! | ||
pass | ||
|
||
|
||
class Encoder(object): | ||
""" | ||
This is a "fake" encoder which just forwards | ||
the raw pixels and the metadata that goes with it. | ||
""" | ||
|
||
def init_context(self, width, height, src_format, encoding, quality, speed, scaling, options): | ||
self.encoding = encoding | ||
self.width = width | ||
self.height = height | ||
self.quality = quality | ||
self.speed = speed | ||
self.scaling = scaling | ||
self.src_format = src_format | ||
self.last_frame_times = maxdeque(200) | ||
self.frames = 0 | ||
self.time = 0 | ||
|
||
def get_info(self): #@DuplicatedSignature | ||
info = get_info() | ||
if self.src_format is None: | ||
return info | ||
info.update({"frames" : self.frames, | ||
"width" : self.width, | ||
"height" : self.height, | ||
"speed" : self.speed, | ||
"quality" : self.quality, | ||
"src_format": self.src_format, | ||
"version" : get_version()}) | ||
if self.scaling!=(1,1): | ||
info["scaling"] = self.scaling | ||
#calculate fps: | ||
now = time.time() | ||
last_time = now | ||
cut_off = now-10.0 | ||
f = 0 | ||
for v in list(self.last_frame_times): | ||
if v>cut_off: | ||
f += 1 | ||
last_time = min(last_time, v) | ||
if f>0 and last_time<now: | ||
info["fps"] = int(f/(now-last_time)) | ||
return info | ||
|
||
def __str__(self): | ||
if self.src_format is None: | ||
return "proxy_encoder(uninitialized)" | ||
return "proxy_encoder(%s - %sx%s)" % (self.src_format, self.width, self.height) | ||
|
||
def is_closed(self): | ||
return self.src_format is None | ||
|
||
def get_encoding(self): | ||
return self.encoding | ||
|
||
def get_width(self): | ||
return self.width | ||
|
||
def get_height(self): | ||
return self.height | ||
|
||
def get_type(self): #@DuplicatedSignature | ||
return "proxy" | ||
|
||
def get_src_format(self): | ||
return self.src_format | ||
|
||
def clean(self): #@DuplicatedSignature | ||
self.width = 0 | ||
self.height = 0 | ||
self.quality = 0 | ||
self.speed = 0 | ||
self.src_format = None | ||
|
||
def get_client_options(self, image, options): | ||
options = { | ||
"proxy" : True, | ||
"frame" : self.frames, | ||
#pass-through encoder options: | ||
"options" : options, | ||
#redundant metadata: | ||
#"width" : image.get_width(), | ||
#"height" : image.get_height(), | ||
"rowstride" : image.get_rowstride(), | ||
"depth" : image.get_depth(), | ||
"rgb_format": image.get_pixel_format(), | ||
} | ||
if self.scaling!=(1,1): | ||
options["scaling"] = self.scaling | ||
return options | ||
|
||
def compress_image(self, image, options={}): | ||
debug("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() | ||
pixels = str(image.get_pixels()) | ||
self.frames += 1 | ||
self.last_frame_times.append(time.time()) | ||
client_options = self.get_client_options(image, options) | ||
debug("compress_image(%s, %s) returning %s bytes and options=%s", image, options, len(pixels), client_options) | ||
return pixels, client_options | ||
|
||
def set_encoding_speed(self, pct): | ||
self.speed = min(100, max(0, pct)) | ||
|
||
def set_encoding_quality(self, pct): | ||
self.quality = min(100, max(0, pct)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# coding=utf8 | ||
# This file is part of Xpra. | ||
# Copyright (C) 2013 Antoine Martin <[email protected]> | ||
# Copyright (C) 2013, 2014 Antoine Martin <[email protected]> | ||
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any | ||
# later version. See the file COPYING for details. | ||
|
||
|
@@ -17,15 +17,21 @@ | |
class VideoHelper(object): | ||
|
||
def __init__(self): | ||
global singleton | ||
assert singleton is None | ||
self._video_encoder_specs = {} | ||
self._csc_encoder_specs = {} | ||
#bits needed to ensure we can initialize just once | ||
#even when called from multiple threads: | ||
self._initialized = False | ||
self._lock = Lock() | ||
|
||
def clone(self): | ||
assert self._initialized | ||
clone = VideoHelper() | ||
clone._video_encoder_specs = self._video_encoder_specs.copy() | ||
clone._csc_encoder_specs = self._csc_encoder_specs.copy() | ||
clone._initialized = True | ||
return clone | ||
|
||
def get_info(self): | ||
d = {} | ||
for in_csc, specs in self._csc_encoder_specs.items(): | ||
|
@@ -52,6 +58,9 @@ def may_init(self): | |
finally: | ||
self._lock.release() | ||
|
||
def get_encodings(self): | ||
return self._video_encoder_specs.keys() | ||
|
||
def get_encoder_specs(self, encoding): | ||
return self._video_encoder_specs.get(encoding, []) | ||
|
||
|
@@ -93,10 +102,13 @@ def init_video_encoder_option(self, encoder_name): | |
encodings = encoder_module.get_encodings() | ||
debug("init_video_encoder_option(%s) %s encodings=%s", encoder_module, encoder_type, encodings) | ||
for encoding in encodings: | ||
encoder_specs = self._video_encoder_specs.setdefault(encoding, {}) | ||
for colorspace in colorspaces: | ||
spec = encoder_module.get_spec(encoding, colorspace) | ||
encoder_specs.setdefault(colorspace, []).append(spec) | ||
self.add_encoder_spec(encoding, colorspace, spec) | ||
|
||
def add_encoder_spec(self, encoding, colorspace, spec): | ||
self._video_encoder_specs.setdefault(encoding, {}).setdefault(colorspace, []).append(spec) | ||
|
||
|
||
def init_csc_options(self): | ||
try: | ||
|
@@ -137,13 +149,16 @@ def init_csc_option(self, csc_name): | |
return | ||
in_cscs = csc_module.get_input_colorspaces() | ||
for in_csc in in_cscs: | ||
csc_specs = self._csc_encoder_specs.setdefault(in_csc, []) | ||
out_cscs = csc_module.get_output_colorspaces(in_csc) | ||
debug("init_csc_option(..) %s.get_output_colorspaces(%s)=%s", csc_module.get_type(), in_csc, out_cscs) | ||
for out_csc in out_cscs: | ||
spec = csc_module.get_spec(in_csc, out_csc) | ||
item = out_csc, spec | ||
csc_specs.append(item) | ||
self.add_csc_spec(in_csc, out_csc, spec) | ||
|
||
def add_csc_spec(self, in_csc, out_csc, spec): | ||
item = out_csc, spec | ||
self._csc_encoder_specs.setdefault(in_csc, []).append(item) | ||
|
||
|
||
singleton = VideoHelper() | ||
def getVideoHelper(): | ||
|
Oops, something went wrong.