From a5e1280af88fcc8de27e165826711b9063bf4353 Mon Sep 17 00:00:00 2001 From: Goffi Date: Thu, 6 Nov 2025 22:23:51 +0100 Subject: [PATCH] frontends (tools/webrtc): Fix pipeline for desktop sharing: Fix desktop sharing, and improve error handling. --- libervia/frontends/tools/webrtc.py | 85 +++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/libervia/frontends/tools/webrtc.py b/libervia/frontends/tools/webrtc.py index ca5b05c7..6e726b76 100644 --- a/libervia/frontends/tools/webrtc.py +++ b/libervia/frontends/tools/webrtc.py @@ -1502,27 +1502,87 @@ class WebRTC: log.debug(f"setting {source_elt} property: {key!r}={value!r}") desktop_src.set_property(key, value) video_convert = Gst.ElementFactory.make("videoconvert", "desktop_videoconvert") + video_rate = Gst.ElementFactory.make("videorate", "desktop_videorate") + video_scale = Gst.ElementFactory.make("videoscale", "desktop_videoscale") + + # Add capsfilter to ensure proper format matching with video_selector + capsfilter = Gst.ElementFactory.make("capsfilter", "desktop_capsfilter") + caps = Gst.Caps.from_string("video/x-raw,framerate=30/1") + capsfilter.set_property("caps", caps) + queue = Gst.ElementFactory.make("queue", "desktop_queue") queue.set_property("leaky", "downstream") + queue.set_property("max-size-buffers", 3) self.pipeline.add(desktop_src) self.pipeline.add(video_convert) + self.pipeline.add(video_rate) + self.pipeline.add(video_scale) + self.pipeline.add(capsfilter) self.pipeline.add(queue) - desktop_src.link(video_convert) - video_convert.link(queue) + # Link the elements: src → convert → rate → scale → capsfilter → queue + if not desktop_src.link(video_convert): + log.error("Failed to link desktop_src to videoconvert") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return + + if not video_convert.link(video_rate): + log.error("Failed to link videoconvert to videorate") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return + + if not video_rate.link(video_scale): + log.error("Failed to link videorate to videoscale") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return + + if not video_scale.link(capsfilter): + log.error("Failed to link videoscale to capsfilter") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return + + if not capsfilter.link(queue): + log.error("Failed to link capsfilter to queue") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return sink_pad_template = self.video_selector.get_pad_template("sink_%u") self.desktop_sink_pad = self.video_selector.request_pad( sink_pad_template, None, None ) queue_src_pad = queue.get_static_pad("src") - queue_src_pad.link(self.desktop_sink_pad) + link_ret = queue_src_pad.link(self.desktop_sink_pad) + if link_ret != Gst.PadLinkReturn.OK: + log.error(f"Failed to link queue to video_selector: {link_ret}") + self._cleanup_desktop_elements([desktop_src, video_convert, video_rate, + video_scale, capsfilter, queue]) + return desktop_src.sync_state_with_parent() video_convert.sync_state_with_parent() + video_rate.sync_state_with_parent() + video_scale.sync_state_with_parent() + capsfilter.sync_state_with_parent() queue.sync_state_with_parent() + log.info("Desktop source pipeline successfully created and linked") + + def _cleanup_desktop_elements(self, elements: list[Gst.Element]) -> None: + """Helper to cleanup elements on error. + + @param elements: List of elements to remove from pipeline + """ + for element in elements: + if element: + element.set_state(Gst.State.NULL) + self.pipeline.remove(element) + def _remove_desktop_source(self, desktop_src: Gst.Element) -> None: """Remove the desktop source from the pipeline. @@ -1530,19 +1590,18 @@ class WebRTC: """ # Remove elements for the desktop source video_convert = self.pipeline.get_by_name("desktop_videoconvert") + video_rate = self.pipeline.get_by_name("desktop_videorate") + video_scale = self.pipeline.get_by_name("desktop_videoscale") + capsfilter = self.pipeline.get_by_name("desktop_capsfilter") queue = self.pipeline.get_by_name("desktop_queue") - if video_convert: - video_convert.set_state(Gst.State.NULL) - desktop_src.unlink(video_convert) - self.pipeline.remove(video_convert) + # Unlink and remove in reverse order + elements = [queue, capsfilter, video_scale, video_rate, video_convert, desktop_src] - if queue: - queue.set_state(Gst.State.NULL) - self.pipeline.remove(queue) - - desktop_src.set_state(Gst.State.NULL) - self.pipeline.remove(desktop_src) + for element in elements: + if element: + element.set_state(Gst.State.NULL) + self.pipeline.remove(element) # Release the pad associated with the desktop source if self.desktop_sink_pad: