1

I could not seem to link more than 3 elements in a gst pipeline in Python. For example, I try to implement the following cli command in Python.

gst-launch-1.0 filesrc location=cooldance.ogg ! oggdemux ! theoradec ! videoconvert ! autovideosink

Since there is no Python equivalence for the C function gst_element_link_many(), I try to connect them one by one:

import sys, os
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst

class TestPlayer(object):
    
    def __init__(self):
        
        self.player = Gst.Pipeline.new("player")

        source = Gst.ElementFactory.make("filesrc", "file-source")
        demux = Gst.ElementFactory.make("oggdemux", "demux1")
        decode = Gst.ElementFactory.make("theoradec", "decode1")
        sink = Gst.ElementFactory.make("autovideosink", "sink1")
        
        source.set_property("location", "/ione/gsttest/sample.ogg")

        # Try to implement this pipeline using multiple links.
        #self.player = Gst.parse_launch ("filesrc location=sample.ogg ! oggdemux ! theoradec ! autovideosink")
        
        self.player.add(source)
        self.player.add(demux)
        self.player.add(decode)
        self.player.add(sink)

        # link elements one by one.
        source.link(demux)
        demux.link(decode)
        decode.link(sink)
        
        ret = self.player.set_state(Gst.State.PLAYING)
        if ret == Gst.StateChangeReturn.FAILURE:
            print("ERROR: Unable to set the pipeline to the playing state")
            sys.exit(1)

        bus = self.player.get_bus()
        terminate = False
        while True:
            msg = bus.timed_pop_filtered(
                Gst.CLOCK_TIME_NONE,
                Gst.MessageType.EOS | Gst.MessageType.ERROR)  # listen on these types,.

            if not msg:
                continue

            t = msg.type
            if t == Gst.MessageType.ERROR:  # exit if error.
                err, dbg = msg.parse_error()
                print("ERROR:", msg.src.get_name(), " ", err.message)
                if dbg:
                    print("debugging info:", dbg)
                terminate = True
            elif t == Gst.MessageType.EOS:  # exit if EOS
                print("End-Of-Stream reached")
                terminate = True

            if terminate:
                break

        self.player.set_state(Gst.State.NULL)

if __name__ == '__main__':
    Gst.init(None)
    p = TestPlayer()

But the script fails with the following error messages:

ERROR: demux1   Internal data stream error.
debugging info: gstoggdemux.c(4961): gst_ogg_demux_loop (): /GstPipeline:player/GstOggDemux:demux1:
streaming stopped, reason not-linked (-1)

I found a few samples on github, but they all fail with similar error. Please advice.

Chu Bun
  • 513
  • 1
  • 5
  • 17
  • Check here: https://gitlab.freedesktop.org/gstreamer/gst-python/-/issues/43 – Florian Zwoch Mar 02 '22 at 17:42
  • It looks like I'll need to link using pads. Do you know any working sample code for that? None of the one I found seem to work at all (usually link the sink pad of the demuxer). – Chu Bun Mar 02 '22 at 19:21
  • There is a python link_many: https://gitlab.freedesktop.org/gstreamer/gst-python/-/blob/7a0decbec242b026391ff6504f0619259aa34721/gi/overrides/Gst.py#L55 – Carson Mar 15 '22 at 12:06

1 Answers1

2

Your issue stems from an improper understanding of delayed linking.

Pads in gstreamer can be linked if and only if they agree on their format. For some elements, all possible pad formats are known ahead-of-time. This means they can be linked ahead of time, with functions like link_may and link. For example, from gst-inspect-1.0 oggdemux In the pad section, we see this:

Pad Templates:
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      application/ogg
      audio/ogg
      video/ogg
      application/kate

This is a sink pad that can link statically. oggdemux will always have a sink pad, and it can be one of the 4 capabilities listed there.

However, the same cannot be said about it's src template(s):

  SRC template: 'src_%08x'
    Availability: Sometimes
    Capabilities:
      ANY

Because of ogg's internal structure, the exact amount of src pads cannot be known ahead of time. Instead, during pipeline function, ogg-demux adds pads as they are needed. Maybe it is given video and audio data, so it creates two pads on the fly, one for the audio and one for the video. Maybe at runtime it doesn't have any audio, so it only makes the video pad, or vice versa.

Because oggdemux dynamically allocates some of it's pads, you cannot statically link it ahead of time. You have two options for how to handle this.

Option 1:

switch to something that has only static pads. h264parse is an example of this. From gst-inspect-1.0 we see:

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-h264
                 parsed: true
          stream-format: { (string)avc, (string)avc3, (string)byte-stream }
              alignment: { (string)au, (string)nal }

  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-h264

It always has a src and a sink, and they always output the exact same format. This can be linked statically, ahead of time, without issues.

Option 2:

You can install callbacks on ogg-demux and wait for it to emit a pad-added signal. On this signal, you can have the callback link the new pad. This has the benefit of supporting dynamic pipelines, and avoiding allocations/links that don't need to be made. This is also what gst-launch-1.0 is doing internally. However, be warned. This is a lot of extra work, it's a lot more fragile, and it's probably not what you want. If you can get away with it, try to use option 1. However, lets say you don't care and want to do it anyways. Good luck! The gstreamer docs contain all the info you would need to implement this behavior.


Note: oggdemux may not be the only element in your pipeline with sometimes pads, make sure to check all elements

Note 2: If you set the enviroment variable GST_DEBUG high enough (probably 4, maybe 5?) and then run a pipeline with gst-launch-1.0, the log outputs will describe the dynamic pad linking in detail, which should be very helpful for understanding what your pipeline is actually doing.

Carson
  • 2,700
  • 11
  • 24
  • Thank you for the explanation. I'm new to GStreamer, but gradually have an idea how it works now. The sample codes floating out there that don't work really confuse me! – Chu Bun Mar 15 '22 at 16:53