Skip to content

Commit

Permalink
#3872 dumb capture and encode streaming mode
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Jun 9, 2023
1 parent 9a1ed42 commit 057e240
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 6 deletions.
19 changes: 13 additions & 6 deletions xpra/server/window/window_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -1630,14 +1630,18 @@ def do_damage(self, ww : int, wh : int, x : int, y : int, w : int, h : int, opti
def may_update_window_dimensions(self) -> Tuple[int,int]:
ww, wh = self.window.get_dimensions()
if self.window_dimensions != (ww, wh):
now = monotonic()
self.statistics.last_resized = now
self.statistics.resize_events.append(now)
log("window dimensions changed from %s to %s", self.window_dimensions, (ww, wh))
self.window_dimensions = ww, wh
self.encode_queue_max_size = max(2, min(30, MAX_SYNC_BUFFER_SIZE//(ww*wh*4)))
self.update_window_dimensions(ww, wh)
return ww, wh

def update_window_dimensions(self, ww, wh):
now = monotonic()
self.statistics.last_resized = now
self.statistics.resize_events.append(now)
log("window dimensions changed from %s to %s", self.window_dimensions, (ww, wh))
self.window_dimensions = ww, wh
self.encode_queue_max_size = max(2, min(30, MAX_SYNC_BUFFER_SIZE//(ww*wh*4)))


def get_packets_backlog(self) -> int:
s = self.statistics
gs = self.global_statistics
Expand Down Expand Up @@ -2183,6 +2187,9 @@ def schedule_auto_refresh(self, packet : Tuple, options) -> None:

def do_schedule_auto_refresh(self, encoding : str, data, region, client_options, options) -> None:
assert data
if self.encoding=="stream":
#streaming mode doesn't use refresh
return
if encoding.startswith("png"):
actual_quality = 100
lossy = self.image_depth>32 or self.image_depth==30
Expand Down
45 changes: 45 additions & 0 deletions xpra/server/window/window_video_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def parse_scaling_options_str(scaling_options_str) -> Tuple:
SCROLL_MIN_PERCENT = max(1, min(100, envint("XPRA_SCROLL_MIN_PERCENT", 30)))
MIN_SCROLL_IMAGE_SIZE = envint("XPRA_MIN_SCROLL_IMAGE_SIZE", 128)

STREAM_MODE = os.environ.get("XPRA_STREAM_MODE", "")

SAVE_VIDEO_PATH = os.environ.get("XPRA_SAVE_VIDEO_PATH", "")
SAVE_VIDEO_STREAMS = envbool("XPRA_SAVE_VIDEO_STREAMS", False)
SAVE_VIDEO_FRAMES = os.environ.get("XPRA_SAVE_VIDEO_FRAMES")
Expand Down Expand Up @@ -159,6 +161,7 @@ def init_vars(self) -> None:
self.encode_from_queue_due = 0
self.scroll_data = None
self.last_scroll_time = 0
self.gstreamer_pipeline = None

self._csc_encoder = None
self._video_encoder = None
Expand Down Expand Up @@ -336,6 +339,7 @@ def suspend(self) -> None:
def cleanup(self) -> None:
super().cleanup()
self.cleanup_codecs()
self.stop_gstreamer_pipeline()

def cleanup_codecs(self) -> None:
""" Video encoders (x264, nvenc and vpx) and their csc helpers
Expand Down Expand Up @@ -597,6 +601,12 @@ def get_best_nonvideo_encoding(self, ww : int, wh : int, options : Dict,


def do_damage(self, ww : int, wh : int, x : int, y : int, w : int, h : int, options):
if ww>=64 and wh>=64 and self.encoding=="stream" and STREAM_MODE=="gstreamer" and self.common_video_encodings:
#in this mode, we start a pipeline once
#and let it submit packets, bypassing all the usual logic:
if not self.gstreamer_pipeline:
self.start_gstreamer_pipeline()
return
vs = self.video_subregion
if vs:
r = vs.rectangle
Expand All @@ -605,6 +615,40 @@ def do_damage(self, ww : int, wh : int, x : int, y : int, w : int, h : int, opti
vs.cancel_refresh_timer()
super().do_damage(ww, wh, x, y, w, h, options)

def start_gstreamer_pipeline(self):
from xpra.gst_common import plugin_str
from xpra.codecs.gstreamer.capture import CaptureAndEncode
attrs = {
"show-pointer" : False,
"do-timestamp" : True,
"use-damage" : True,
}
try:
xid = self.window.get_property("xid")
except (TypeError, AttributeError):
xid = 0
if xid:
attrs["xid"] = xid
element = plugin_str("ximagesrc", attrs)
encoding = self.common_video_encodings[0]
w, h = self.window_dimensions
self.gstreamer_pipeline = CaptureAndEncode(element, encoding, w, h)
self.gstreamer_pipeline.connect("new-image", self.new_gstreamer_frame)
self.gstreamer_pipeline.start()

def stop_gstreamer_pipeline(self):
gp = self.gstreamer_pipeline
if gp:
self.gstreamer_pipeline = None
gp.stop()

def new_gstreamer_frame(self, capture, coding:str, data, client_info:Dict) -> None:
self.direct_queue_draw(coding, data, client_info)

def update_window_dimensions(self, ww, wh):
super().update_window_dimensions(ww, wh)
self.stop_gstreamer_pipeline()


def cancel_damage(self, limit : int=0):
self.cancel_encode_from_queue()
Expand All @@ -615,6 +659,7 @@ def cancel_damage(self, limit : int=0):
self.free_scroll_data()
self.last_scroll_time = 0
super().cancel_damage(limit)
self.stop_gstreamer_pipeline()
#we must clean the video encoder to ensure
#we will resend a key frame because we may be missing a frame
self.cleanup_codecs()
Expand Down

0 comments on commit 057e240

Please sign in to comment.