From 09a63b931b444a0edf9a47aca31a7f8037dfe72d Mon Sep 17 00:00:00 2001 From: Bharat Date: Fri, 14 Jun 2024 22:57:00 +0530 Subject: [PATCH 1/4] draft: Port 'Screencast' demo to Vala The current code does not work, Gstreamer Pipeline is causing issues. --- src/Screencast/main.vala | 81 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/Screencast/main.vala diff --git a/src/Screencast/main.vala b/src/Screencast/main.vala new file mode 100644 index 00000000..e1734785 --- /dev/null +++ b/src/Screencast/main.vala @@ -0,0 +1,81 @@ +#! /usr/bin/env -S vala workbench.vala --pkg libadwaita-1 --pkg libportal-gtk4 --pkg gstreamer-1.0 + +private Gtk.Picture output; + +async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) { + try { + Xdp.Session session = yield portal.create_screencast_session (Xdp.OutputType.MONITOR, + Xdp.ScreencastFlags.NONE, + Xdp.CursorMode.EMBEDDED, + Xdp.PersistMode.TRANSIENT, + null, + null); + + if (session == null) { + message ("Permission denied"); + return; + } + + var success = yield session.start (parent, null); + + if (!success) { + message ("Could not start session"); + return; + } + var pw_remote = session.open_pipewire_remote (); + var pipeline = new Gst.Pipeline (""); + var source = Gst.ElementFactory.make ("pipewiresrc", "source"); + var queue = Gst.ElementFactory.make ("queue", "queue"); // add a queue element + var paintable_sink = Gst.ElementFactory.make ( + "gtk4paintablesink", + "paintable_sink" + ); + var glsinkbin = Gst.ElementFactory.make ("glsinkbin", "glsinkbin"); + + // Set up and Link Pipeline + source.set_property ("fd", pw_remote); // pw_remote is the file descriptor obtained from libportal + + // Obtain the node id from the screencast session + var streamss = session.get_streams (); + var node_id = streamss.get_child_value (0); + + if (node_id == null) { + stderr.printf ("No available node id\n"); + return; + } + + source.set_property ("path", node_id); + glsinkbin.set_property ("sink", paintable_sink); + + pipeline.add (source); + pipeline.add (queue); + pipeline.add (glsinkbin); + source.link (queue); + queue.link (glsinkbin); + + var paintable = GLib.Value (typeof (Gdk.Paintable)); + + paintable_sink.get_property ("paintable", ref paintable); + output.paintable = (Gdk.Paintable) paintable.get_object (); + + // Start the pipeline + pipeline.set_state (Gst.State.PLAYING); + + // Handle cleanup on application exit + output.destroy.connect (() => { + pipeline.set_state (Gst.State.NULL); + }); + } catch (Error e) { + message (@"$(e.message)"); + } +} + +public void main (string[] args) { + // Gst.init (ref args); + var portal = new Xdp.Portal (); + var parent = Xdp.parent_new_gtk (workbench.window); + output = (Gtk.Picture) workbench.builder.get_object ("output"); + var button = (Gtk.Button) workbench.builder.get_object ("button"); + + button.clicked.connect (() => { start_screencast_session.begin (portal, parent); }); +} From da12ddaeb139069aa8e7ad641c7dfc0fc7215664 Mon Sep 17 00:00:00 2001 From: Bharat Date: Fri, 21 Jun 2024 11:27:43 +0530 Subject: [PATCH 2/4] Minor changes: Explicit variable declaration Changed the way node_id is accessed with a more proper way to get elements from the touple Skip Gst.state enum --- src/Screencast/main.vala | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Screencast/main.vala b/src/Screencast/main.vala index e1734785..09bb2fdf 100644 --- a/src/Screencast/main.vala +++ b/src/Screencast/main.vala @@ -4,42 +4,42 @@ private Gtk.Picture output; async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) { try { - Xdp.Session session = yield portal.create_screencast_session (Xdp.OutputType.MONITOR, - Xdp.ScreencastFlags.NONE, - Xdp.CursorMode.EMBEDDED, - Xdp.PersistMode.TRANSIENT, - null, - null); + Xdp.Session session = yield portal.create_screencast_session (MONITOR, // Output Type + NONE, // Screencast Flags + EMBEDDED, // Cursor Mode + TRANSIENT, // Persist Mode + null, // Restore token + null); // Cancellable if (session == null) { message ("Permission denied"); return; } - var success = yield session.start (parent, null); + bool success = yield session.start (parent, null); if (!success) { message ("Could not start session"); return; } - var pw_remote = session.open_pipewire_remote (); + int pw_remote = session.open_pipewire_remote (); var pipeline = new Gst.Pipeline (""); - var source = Gst.ElementFactory.make ("pipewiresrc", "source"); - var queue = Gst.ElementFactory.make ("queue", "queue"); // add a queue element - var paintable_sink = Gst.ElementFactory.make ( - "gtk4paintablesink", - "paintable_sink" + Gst.Element source = Gst.ElementFactory.make ("pipewiresrc", "source"); + Gst.Element queue = Gst.ElementFactory.make ("queue", "queue"); // add a queue element + Gst.Element paintable_sink = Gst.ElementFactory.make ( + "gtk4paintablesink", + "paintable_sink" ); - var glsinkbin = Gst.ElementFactory.make ("glsinkbin", "glsinkbin"); + Gst.Element glsinkbin = Gst.ElementFactory.make ("glsinkbin", "glsinkbin"); // Set up and Link Pipeline source.set_property ("fd", pw_remote); // pw_remote is the file descriptor obtained from libportal // Obtain the node id from the screencast session - var streamss = session.get_streams (); - var node_id = streamss.get_child_value (0); + Variant streams = session.get_streams (); + uint node_id = streams.get_child_value (0).get_child_value (0).get_uint32 (); - if (node_id == null) { + if (node_id == 0) { stderr.printf ("No available node id\n"); return; } @@ -53,17 +53,17 @@ async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) { source.link (queue); queue.link (glsinkbin); - var paintable = GLib.Value (typeof (Gdk.Paintable)); + var paintable = Value (typeof (Gdk.Paintable)); paintable_sink.get_property ("paintable", ref paintable); output.paintable = (Gdk.Paintable) paintable.get_object (); // Start the pipeline - pipeline.set_state (Gst.State.PLAYING); + pipeline.set_state (PLAYING); // Handle cleanup on application exit output.destroy.connect (() => { - pipeline.set_state (Gst.State.NULL); + pipeline.set_state (NULL); }); } catch (Error e) { message (@"$(e.message)"); From 0529cbc9e339665c5e5ff858342f59d337a85178 Mon Sep 17 00:00:00 2001 From: Bharat Date: Fri, 21 Jun 2024 12:11:14 +0530 Subject: [PATCH 3/4] Improvements Replace enclosing whole method in try catch Throw an error from start_screencast_session() Declare portal and parent as global for easier access --- src/Screencast/main.vala | 140 ++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 67 deletions(-) diff --git a/src/Screencast/main.vala b/src/Screencast/main.vala index 09bb2fdf..9702e4d3 100644 --- a/src/Screencast/main.vala +++ b/src/Screencast/main.vala @@ -1,81 +1,87 @@ #! /usr/bin/env -S vala workbench.vala --pkg libadwaita-1 --pkg libportal-gtk4 --pkg gstreamer-1.0 - +public errordomain MessageError { + FAILED; +} private Gtk.Picture output; +private Xdp.Portal portal; +private Xdp.Parent parent; + +async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) throws Error { + + Xdp.Session session = yield portal.create_screencast_session (MONITOR, // Output Type + NONE, // Screencast Flags + EMBEDDED, // Cursor Mode + TRANSIENT, // Persist Mode + null, // Restore token + null); // Cancellable + + if (session == null) { + throw new MessageError.FAILED (@"Permission denied"); + } + + bool success = yield session.start (parent, null); + + if (!success) { + throw new MessageError.FAILED (@"Could not start session"); + } + int pw_remote = session.open_pipewire_remote (); + var pipeline = new Gst.Pipeline (""); + Gst.Element source = Gst.ElementFactory.make ("pipewiresrc", "source"); + Gst.Element queue = Gst.ElementFactory.make ("queue", "queue"); // add a queue element + Gst.Element paintable_sink = Gst.ElementFactory.make ( + "gtk4paintablesink", + "paintable_sink" + ); + Gst.Element glsinkbin = Gst.ElementFactory.make ("glsinkbin", "glsinkbin"); + + // Set up and Link Pipeline + source.set_property ("fd", pw_remote); // pw_remote is the file descriptor obtained from libportal + + // Obtain the node id from the screencast session + Variant streams = session.get_streams (); + uint node_id = streams.get_child_value (0).get_child_value (0).get_uint32 (); + if (node_id == 0) { + throw new MessageError.FAILED (@"No available node"); + // stderr.printf ("No available node id\n"); + } + + source.set_property ("path", node_id); + glsinkbin.set_property ("sink", paintable_sink); + + pipeline.add (source); + pipeline.add (queue); + pipeline.add (glsinkbin); + source.link (queue); + queue.link (glsinkbin); + + var paintable = Value (typeof (Gdk.Paintable)); + + paintable_sink.get_property ("paintable", ref paintable); + output.paintable = (Gdk.Paintable) paintable.get_object (); + + // Start the pipeline + pipeline.set_state (PLAYING); + + // Handle cleanup on application exit + output.destroy.connect (() => { + pipeline.set_state (NULL); + }); +} -async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) { +async void on_button_clicked () { try { - Xdp.Session session = yield portal.create_screencast_session (MONITOR, // Output Type - NONE, // Screencast Flags - EMBEDDED, // Cursor Mode - TRANSIENT, // Persist Mode - null, // Restore token - null); // Cancellable - - if (session == null) { - message ("Permission denied"); - return; - } - - bool success = yield session.start (parent, null); - - if (!success) { - message ("Could not start session"); - return; - } - int pw_remote = session.open_pipewire_remote (); - var pipeline = new Gst.Pipeline (""); - Gst.Element source = Gst.ElementFactory.make ("pipewiresrc", "source"); - Gst.Element queue = Gst.ElementFactory.make ("queue", "queue"); // add a queue element - Gst.Element paintable_sink = Gst.ElementFactory.make ( - "gtk4paintablesink", - "paintable_sink" - ); - Gst.Element glsinkbin = Gst.ElementFactory.make ("glsinkbin", "glsinkbin"); - - // Set up and Link Pipeline - source.set_property ("fd", pw_remote); // pw_remote is the file descriptor obtained from libportal - - // Obtain the node id from the screencast session - Variant streams = session.get_streams (); - uint node_id = streams.get_child_value (0).get_child_value (0).get_uint32 (); - - if (node_id == 0) { - stderr.printf ("No available node id\n"); - return; - } - - source.set_property ("path", node_id); - glsinkbin.set_property ("sink", paintable_sink); - - pipeline.add (source); - pipeline.add (queue); - pipeline.add (glsinkbin); - source.link (queue); - queue.link (glsinkbin); - - var paintable = Value (typeof (Gdk.Paintable)); - - paintable_sink.get_property ("paintable", ref paintable); - output.paintable = (Gdk.Paintable) paintable.get_object (); - - // Start the pipeline - pipeline.set_state (PLAYING); - - // Handle cleanup on application exit - output.destroy.connect (() => { - pipeline.set_state (NULL); - }); + yield start_screencast_session (portal, parent); } catch (Error e) { - message (@"$(e.message)"); + critical (@"$(e.message)"); } } public void main (string[] args) { // Gst.init (ref args); - var portal = new Xdp.Portal (); - var parent = Xdp.parent_new_gtk (workbench.window); + portal = new Xdp.Portal (); + parent = Xdp.parent_new_gtk (workbench.window); output = (Gtk.Picture) workbench.builder.get_object ("output"); var button = (Gtk.Button) workbench.builder.get_object ("button"); - button.clicked.connect (() => { start_screencast_session.begin (portal, parent); }); + button.clicked.connect (on_button_clicked); } From 8934253cc299a002501e6c8c173eb2f780d1a7a2 Mon Sep 17 00:00:00 2001 From: Bharat Date: Wed, 9 Oct 2024 22:00:30 +0530 Subject: [PATCH 4/4] minor changes: Workaround as suggested by @Diego-Ivan Demo functions as expected --- src/Screencast/main.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Screencast/main.vala b/src/Screencast/main.vala index 9702e4d3..7f0bb496 100644 --- a/src/Screencast/main.vala +++ b/src/Screencast/main.vala @@ -42,7 +42,6 @@ async void start_screencast_session (Xdp.Portal portal, Xdp.Parent parent) throw uint node_id = streams.get_child_value (0).get_child_value (0).get_uint32 (); if (node_id == 0) { throw new MessageError.FAILED (@"No available node"); - // stderr.printf ("No available node id\n"); } source.set_property ("path", node_id); @@ -77,7 +76,8 @@ async void on_button_clicked () { } public void main (string[] args) { - // Gst.init (ref args); + unowned string[] arguments = null; + Gst.init (ref arguments); portal = new Xdp.Portal (); parent = Xdp.parent_new_gtk (workbench.window); output = (Gtk.Picture) workbench.builder.get_object ("output");