Search code examples
pythongstreamer

Identical Gstreamer pipeline leads to window with webcam feed being shown when pipeline is created with parse_launch, but not when manually created


I am trying to make a simple Gstreamer pipeline in python that will take input from the webcam and show it on a video window.

The following code works as expected:

import gi
gi.require_version("Gst", "1.0") 
from gi.repository import Gst, GLib
from threading import Thread
Gst.init()

main_loop = GLib.MainLoop()
pipeline = Gst.parse_launch("v4l2src ! decodebin ! videoconvert ! autovideosink")
main_loop_tread = Thread(target=main_loop.run)
main_loop_tread.start()

pipeline.set_state(Gst.State.PLAYING)

However, when I try to do the same thing with a manually created pipeline, the pipeline does not show any window. My camera light still turns on, but I get no video window popup. Also, if I reaplce the autovideo sink with an appsink, every buffer I get is None.

import gi
gi.require_version("Gst", "1.0") 
from gi.repository import Gst, GLib
from threading import Thread
Gst.init()

main_loop = GLib.MainLoop()
pipeline = Gst.Pipeline()
main_loop_tread = Thread(target=main_loop.run)
main_loop_tread.start()

src = Gst.ElementFactory.make("v4l2src")
decode = Gst.ElementFactory.make("decodebin")
convert = Gst.ElementFactory.make("videoconvert")
sink = Gst.ElementFactory.make("autovideosink")

pipeline.add(src)
pipeline.add(decode)
pipeline.add(convert)
pipeline.add(sink)

src.link(decode)
decode.link(convert)
convert.link(sink)

pipeline.set_state(Gst.State.PLAYING)

What is wrong with the second code example?


Solution

  • I ran your code with GST_DEBUG=3 flag and saw warnings

    $ GST_DEBUG=3 python3 python_gstreamer.py
    0:00:00.010488893 2604612 0x55d4f997cc00 FIXME                default gstutils.c:4025:gst_pad_create_stream_id_internal:<videotestsrc0:src> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
    0:00:00.011231276 2604612 0x55d4f997cc00 WARN                 basesrc gstbasesrc.c:3127:gst_base_src_loop:<videotestsrc0> error: Internal data stream error.
    0:00:00.011236130 2604612 0x55d4f997cc00 WARN                 basesrc gstbasesrc.c:3127:gst_base_src_loop:<videotestsrc0> error: streaming stopped, reason not-linked (-1)
    

    Then I ran it with GST_DEBUG=4 to find more details and I noticed that the decodebin element wasn't being linked to videoconvert element properly.

    $ GST_DEBUG=4 python3 python_gstreamer.py
    ...
    0:00:00.008857654 2606874 0x564edfcf68a0 INFO        GST_ELEMENT_PADS gstutils.c:1816:gst_element_link_pads_full: trying to link element decodebin0:(any) to element videoconvert0:(any)
    0:00:00.008860541 2606874 0x564edfcf68a0 INFO                GST_PADS gstpad.c:4357:gst_pad_peer_query:<videoconvert0:src> pad has no peer
    0:00:00.008934791 2606874 0x564edfcf68a0 INFO        GST_ELEMENT_PADS gstelement.c:1013:gst_element_get_static_pad: no such pad 'src_%u' in element "decodebin0"
    0:00:00.008941910 2606874 0x564edfcf68a0 INFO        GST_ELEMENT_PADS gstutils.c:1270:gst_element_get_compatible_pad:<decodebin0> Could not find a compatible pad to link to videoconvert0:sink
    ...
    

    Apparently, you have to handle the "pad-added" signal emitted by decodebin and then link the new pad to the sink pad of next element.

    import gi
    gi.require_version("Gst", "1.0") 
    from gi.repository import Gst, GLib
    from threading import Thread
    Gst.init()
    
    def pad_added_handler(src, new_pad, convert):
        sink_pad = convert.get_static_pad("sink")
        if sink_pad.is_linked():
            return
    
        new_pad.link(sink_pad)
    
    main_loop = GLib.MainLoop()
    pipeline = Gst.Pipeline()
    main_loop_tread = Thread(target=main_loop.run)
    main_loop_tread.start()
    
    src = Gst.ElementFactory.make("v4l2src")
    decode = Gst.ElementFactory.make("decodebin")
    convert = Gst.ElementFactory.make("videoconvert")
    sink = Gst.ElementFactory.make("autovideosink")
    
    pipeline.add(src)
    pipeline.add(decode)
    pipeline.add(convert)
    pipeline.add(sink)
    
    src.link(decode)
    convert.link(sink)
    
    decode.connect("pad-added", pad_added_handler, convert)
    
    pipeline.set_state(Gst.State.PLAYING)
    

    Note: I tested the code with videotestsrc element instead of v4l2src as I don't have usb camera.