[Python] GSTREAMER+CAIRO issue with two pipelines render issue

Discussão em 'Python' iniciado por Stack, Agosto 10, 2025.

  1. Stack

    Stack Membro Participativo

    `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...

Compartilhe esta Página