`import time import gi, glob gi.require_version('Gst', '1.0') from gi.repository import Gst, GLib from app.factories import OverlayFactory import os os.environ['GST_DEBUG'] = '3' class GstRunner: def init(self): self.vehicle_overlay = OverlayFactory.create() self.pipeline = None self.last_frame_time = 0 self.pad_udp = None self.pad_test = None self.selector = None def build_pipeline(self): self.pipeline = Gst.parse_launch( # === Shared processing === "input-selector name=selector ! " "videoconvert ! " "cairooverlay name=overlay ! " "videoconvert ! " "kmssink " # === UDP H264 input === "udpsrc port=5600 caps=application/x-rtp,media=video,encoding-name=H264,payload=96 ! " "rtpjitterbuffer latency=100 mode=1 max-misorder-time=300 max-dropout-time=200 max-rtcp-rtp-time-diff=150 ! " "queue max-size-buffers=5 max-size-bytes=0 max-size-time=0 ! " "rtph264depay ! " "h264parse ! " "avdec_h264 qos=false ! " "tee name=udpsrc_tee " "udpsrc_tee. ! queue ! selector.sink_0 " "udpsrc_tee. ! queue ! fakesink name=probesink sync=false " # === GL-accelerated Test source === "videotestsrc pattern=ball is-live=true ! " "videorate ! " "video/x-raw,width=1280,height=720,framerate=30/1 ! " "queue max-size-buffers=3 leaky=downstream ! " "videoconvert ! " # "glupload ! glcolorconvert ! " # "queue ! " # "glcolorconvert ! gldownload ! " "selector.sink_1" ) # output command # gst-launch-1.0 -v videotestsrc is-live=true pattern=ball ! video/x-raw,width=1280,height=720,framerate=30/1 ! videoconvert ! x264enc tune=zerolatency bitrate=1000 speed-preset=ultrafast key-int-max=30 ! rtph264pay config-interval=1 pt=96 mtu=1200 ! udpsink host=192.168.1.8 port=5600 self.selector = self.pipeline.get_by_name("selector") probesink = self.pipeline.get_by_name("probesink") overlay = self.pipeline.get_by_name("overlay") # Get input-selector sink pads self.pad_udp = self.selector.get_static_pad("sink_0") self.pad_test = self.selector.get_static_pad("sink_1") # Start with testsrc self.selector.set_property("active-pad", self.pad_test) # Watch for decoded frames def on_probe(pad, info): self.last_frame_time = GLib.get_monotonic_time() return Gst.PadProbeReturn.OK probesink.get_static_pad("sink").add_probe(Gst.PadProbeType.BUFFER, on_probe) # Overlay drawing def on_draw_callback(overlay, context, timestamp, duration): current_overlay = OverlayFactory.get_current() if current_overlay: current_overlay.on_draw(overlay, context, timestamp, duration) overlay.connect("draw", on_draw_callback) # Check video activity and switch source def check_video(): now = GLib.get_monotonic_time() age_sec = (now - self.last_frame_time) / 1_000_000 current = self.selector.get_property("active-pad") if age_sec < 2: if current != self.pad_udp: print("Switching to udpsrc") self.selector.set_property("active-pad", self.pad_udp) else: if current != self.pad_test: print("Switching to testsrc") self.selector.set_property("active-pad", self.pad_test) return True GLib.timeout_add_seconds(1, check_video) def start(self): self.build_pipeline() if not self.pipeline: raise RuntimeError("Pipeline build failed") result = self.pipeline.set_state(Gst.State.PLAYING) if result == Gst.StateChangeReturn.FAILURE: raise RuntimeError("Failed to set pipeline to PLAYING") def stop(self): if self.pipeline: def force_shutdown(): self.pipeline.set_state(Gst.State.NULL) GLib.timeout_add_seconds(1, force_shutdown) def detect_error_problem(self): bus = self.pipeline.get_bus() msg = bus.timed_pop_filtered( Gst.SECOND, Gst.MessageType.ERROR | Gst.MessageType.EOS ) if msg: if msg.type == Gst.MessageType.ERROR: err, debug = msg.parse_error() error_msg = f"Failed to start pipeline. Reason: {err.message} -> {debug.split(":")[-1]}" if "Could not get allowed GstCaps of device" == err.message: raise RuntimeError(f"{error_msg} -> {self._detect_monitor_vs_driver_issue()}") else: raise RuntimeError(error_msg) def _detect_monitor_vs_driver_issue(self): statuses = [] for path in glob.glob("/sys/class/drm/*/status"): try: with open(path) as f: statuses.append(f.read().strip()) except: pass if "connected" in statuses: return "driver issue" elif "disconnected" in statuses: return "monitor missing" return "unknown"` So, I got two pipeines (1st one is awaiting for input signal on 5600 port, 2nd one just has live test pattern) When input receives a signal , it works okay. And even when we lose a signal, the program switches to "reserve pipeline" and plays test pattern perfectly. The problem is when we have no image from 5600 port, the test pattern just freezes with only one frame renderred (so, Cairo can't refresh overlay). Any things with pushing frames to appsrc doesn't help. Does anybody faced this issue before? Tried to make appsrc to push frames Tried different approaches with diff caps. No result I have no errors. Just frozen image Continue reading...