From fa59906d11adf6cd784478febd5dcca3ee7c1c3e Mon Sep 17 00:00:00 2001 From: totaam Date: Wed, 11 Oct 2023 23:32:05 +0700 Subject: [PATCH] #3964 run some basic pipelines for testing --- xpra/gtk/configure/gstreamer.py | 106 +++++++++++++++++++++++++++++-- xpra/gtk/configure/main.py | 16 ++++- xpra/gtk/dialogs/session_info.py | 23 +------ xpra/gtk/widget.py | 31 ++++++++- 4 files changed, 147 insertions(+), 29 deletions(-) diff --git a/xpra/gtk/configure/gstreamer.py b/xpra/gtk/configure/gstreamer.py index 762272d019..efaf8bfc18 100644 --- a/xpra/gtk/configure/gstreamer.py +++ b/xpra/gtk/configure/gstreamer.py @@ -3,16 +3,20 @@ # 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 gi +import shlex from textwrap import wrap +from subprocess import Popen, PIPE +from xpra.util.types import AtomicInteger +from xpra.os_util import is_X11, is_gnome, OSX, WIN32 from xpra.gtk.dialogs.base_gui_window import BaseGUIWindow -from xpra.gtk.widget import label +from xpra.gtk.widget import label, slabel, title_box from xpra.platform.paths import get_image from xpra.log import Logger +import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk +from gi.repository import Gtk, GLib log = Logger("gstreamer", "util") @@ -34,7 +38,7 @@ def __init__(self, parent:Gtk.Window|None=None): self.warning_pixbuf = get_image("warning.png") size = (800, 554) if self.warning_pixbuf: - size = self.warning_pixbuf.get_width(), self.warning_pixbuf.get_height() + size = self.warning_pixbuf.get_width()+20, self.warning_pixbuf.get_height()+20 super().__init__( "Configure Xpra's GStreamer Codecs", "gstreamer.png", @@ -52,11 +56,105 @@ def populate(self): self.populate_with_warning() else: self.add_widget(label("Configure Xpra GStreamer Codecs", font="sans 20")) + grid = Gtk.Grid() + grid.set_row_homogeneous(False) + grid.set_column_homogeneous(True) + hbox = Gtk.HBox() + hbox.add(slabel("Please select the viewing element")) + videosink = "ximagesink" + self.add_widget(hbox) + self.add_widget(grid) + grid.attach(title_box("Mode"), 0, 0, 1, 1) + grid.attach(title_box("GStreamer Elements"), 1, 0, 1, 1) + grid.attach(title_box("Test"), 2, 0, 1, 1) + grid.attach(title_box("Result"), 3, 0, 1, 1) + row = AtomicInteger(1) + + WIDTH = 640 + HEIGHT = 480 + FRAMERATE = 10 + + def lal(text:str, element:str, test="") -> None: + r = int(row) + grid.attach(title_box(text), 0, r, 1, 1) + lbl = slabel(element) + al = Gtk.Alignment(xalign=0, yalign=0.5, xscale=0.0, yscale=0.0) + al.add(lbl) + grid.attach(al, 1, r, 1, 1) + if test: + btn = Gtk.Button(label="Run") + grid.attach(btn, 2, r, 1, 1) + lbl = slabel() + al = Gtk.Alignment(xalign=0, yalign=0.5, xscale=0.0, yscale=0.0) + al.add(lbl) + grid.attach(al, 3, r, 1, 1) + def run_test(btn, lbl): + log(f"run_test({btn}, {lbl})") + lbl.set_label(f"Testing {element}") + from xpra.util.thread import start_thread + start_thread(self.test_element, f"test {element}", True, (lbl, element, test)) + btn.connect("clicked", run_test, lbl) + row.increase() + if is_X11(): + lal("X11 Capture", "ximagesrc", + f"ximagesrc use-damage=0 ! video/x-raw,framerate={FRAMERATE}/1 ! videoscale method=0 ! video/x-raw,width={WIDTH},height={HEIGHT} ! {videosink}") + if is_gnome(): + lal("pipewire ScreenCast", "pipewiresrc") + lal("pipewire RemoteDesktop", "pipewiresrc") + def testencoder(element, fmt) -> str: + return f"videotestsrc num-buffers=50 ! 'video/x-raw,format=(string)NV12,width={WIDTH},height={HEIGHT},framerate=(fraction){FRAMERATE}/1' ! videoconvert ! {element} ! avdec_{fmt} ! videoconvert ! {videosink}" + def encoder_option(text:str, element:str, fmt:str): + lal(text, element, testencoder(element, fmt)) + + for sw in ("x264", "vp8", "vp9", "av1"): + fmt = {"x264" : "h264"}.get(sw, sw) + encoder_option(f"software {sw} encoder", f"{sw}enc", fmt) + for fmt in ("h264", "h265", "jpeg", "vp8", "vp9"): + encoder_option(f"vaapi {fmt} encoder", f"vaapi{fmt}enc", fmt) + encoder_option(f"libva {fmt} encoder", f"va{fmt}enc", fmt) + if WIN32: + for fmt in ("h264", "h265"): + encoder_option(f"NVidia D3D11 {fmt}", f"nvd3d11{fmt}enc", fmt) + for fmt in ("h264", "h265"): + encoder_option(f"NVENC {fmt}", f"nv{fmt}enc", fmt) + if not (OSX or WIN32): + for fmt in ("h264", "h265"): + lal(f"AMD AMF {fmt}", f"amf{fmt}enc", fmt) self.vbox.show_all() + def test_element(self, lbl, element, test): + def set_label(message="OK"): + GLib.timeout_add(1000, lbl.set_label, message) + def run_test_cmd(cmd): + try: + log.info(f"running {cmd!r}") + proc = Popen(cmd, text=True, stdout=PIPE, stderr=PIPE) + out, err = proc.communicate(None) + if proc.returncode==0: + return True + log.error(f"Error: {cmd!r} returned {proc.returncode}") + set_label(f"Error: test pipeline returned {proc.returncode}") + except OSError as e: + log.error(f"Error running {cmd!r}: {e}") + set_label(f"Error: {e}") + return False + + #first make sure that the element exists: + if not run_test_cmd(["gst-inspect-1.0", element]): + return + set_label("Element found") + if not run_test_cmd(["gst-launch-1.0"]+shlex.split(test)): + return + set_label("Test pipeline worked") + + def populate_with_warning(self): layout = Gtk.Layout() + layout.set_margin_top(0) + layout.set_margin_bottom(0) + layout.set_margin_start(0) + layout.set_margin_end(0) self.vbox.add(layout) if self.warning_pixbuf: image = Gtk.Image.new_from_pixbuf(self.warning_pixbuf) diff --git a/xpra/gtk/configure/main.py b/xpra/gtk/configure/main.py index fe067b0197..905948594f 100644 --- a/xpra/gtk/configure/main.py +++ b/xpra/gtk/configure/main.py @@ -8,6 +8,7 @@ from xpra.scripts.config import InitExit from xpra.exit_codes import ExitCode +from xpra.util.parsing import parse_simple_dict from xpra.gtk.dialogs.base_gui_window import BaseGUIWindow from xpra.gtk.widget import label from xpra.log import Logger @@ -66,10 +67,23 @@ def run_gui(gui_class=ConfigureGUI) -> int: return 0 -def get_user_config_file(): +def get_user_config_file() -> str: from xpra.platform.paths import get_user_conf_dirs return os.path.join(get_user_conf_dirs()[0], "99_configure_tool.conf") +def parse_user_config_file() -> dict: + filename = get_user_config_file() + if not os.path.exists(filename): + return {} + with open(filename, "r", encoding="utf8") as f: + return parse_simple_dict(f.read()) + +def save_user_config_file(options:dict) -> None: + filename = get_user_config_file() + with open(filename, "w", encoding="utf8") as f: + for k,v in options.items(): + f.write(f"{k} = {v}") + def main(args) -> ExitCode: if args: diff --git a/xpra/gtk/dialogs/session_info.py b/xpra/gtk/dialogs/session_info.py index 92d61bf900..a92647d3d2 100644 --- a/xpra/gtk/dialogs/session_info.py +++ b/xpra/gtk/dialogs/session_info.py @@ -25,7 +25,7 @@ from xpra.client.base.gobject_client import InfoTimerClient from xpra.gtk.gtk_util import add_close_accel from xpra.gtk.graph import make_graph_imagesurface -from xpra.gtk.widget import imagebutton, label, modify_fg, IgnoreWarningsContext +from xpra.gtk.widget import imagebutton, title_box, slabel from xpra.gtk.pixbuf import get_icon_pixbuf from xpra.log import Logger @@ -47,18 +47,6 @@ def make_datetime(date, time) -> str: return bytestostr(date or "") return "%s %s" % (bytestostr(date), bytestostr(time)) -def title_box(label_str:str, tooltip="") -> Gtk.EventBox: - eb = Gtk.EventBox() - lbl = slabel(label_str, tooltip=tooltip) - modify_fg(lbl, Gdk.Color(red=48*256, green=0, blue=0)) - al = Gtk.Alignment(xalign=0.0, yalign=0.5, xscale=0.0, yscale=0.0) - al.set_margin_start(10) - al.set_margin_end(10) - al.add(lbl) - eb.add(al) - with IgnoreWarningsContext(): - eb.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(red=219*256, green=226*256, blue=242*256)) - return eb def bool_icon(image, on_off:bool): if on_off: @@ -134,15 +122,6 @@ def newdictlook(d, parts, fallback=None): return fallback return v -def slabel(text:str="", tooltip:str="", font:str="") -> Gtk.Label: - l = label(text, tooltip, font) - l.set_margin_start(5) - l.set_margin_end(5) - l.set_margin_top(2) - l.set_margin_bottom(2) - l.set_selectable(True) - l.set_line_wrap(True) - return l def image_label_hbox(): hbox = Gtk.HBox(homogeneous=False, spacing=10) diff --git a/xpra/gtk/widget.py b/xpra/gtk/widget.py index ce8597c13f..e979f53a77 100644 --- a/xpra/gtk/widget.py +++ b/xpra/gtk/widget.py @@ -8,9 +8,11 @@ from contextlib import AbstractContextManager import gi + gi.require_version("Gtk", "3.0") # @UndefinedVariable +gi.require_version("Gdk", "3.0") # @UndefinedVariable gi.require_version("Pango", "1.0") # @UndefinedVariable -from gi.repository import Gtk, Pango #@UnresolvedImport +from gi.repository import Gtk, Gdk, Pango #@UnresolvedImport from xpra.log import Logger log = Logger("gtk", "util") @@ -152,4 +154,29 @@ def choose_file(parent_window, title, action=Gtk.FileChooserAction.OPEN, action_ def pack_start(self, child, expand=True, fill=True, padding=0): orig_pack_start(self, child, expand, fill, padding) -Gtk.Box.pack_start = pack_start \ No newline at end of file +Gtk.Box.pack_start = pack_start + + +def slabel(text:str="", tooltip:str="", font:str="") -> Gtk.Label: + l = label(text, tooltip, font) + l.set_margin_start(5) + l.set_margin_end(5) + l.set_margin_top(2) + l.set_margin_bottom(2) + l.set_selectable(True) + l.set_line_wrap(True) + return l + + +def title_box(label_str:str, tooltip="") -> Gtk.EventBox: + eb = Gtk.EventBox() + lbl = slabel(label_str, tooltip=tooltip) + modify_fg(lbl, Gdk.Color(red=48*256, green=0, blue=0)) + al = Gtk.Alignment(xalign=0.0, yalign=0.5, xscale=0.0, yscale=0.0) + al.set_margin_start(10) + al.set_margin_end(10) + al.add(lbl) + eb.add(al) + with IgnoreWarningsContext(): + eb.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(red=219*256, green=226*256, blue=242*256)) + return eb