GStreamer Dynamic Pads, Explained

(for an intro to GStreamer, see my previous article)

You know, one thing that gets people confused in GStreamer is the idea of Dynamic Pads. Some elements, most notably the decodebin and gnlcomposition only have certain pads at certain times. This is because they figure out what kind of content they are processing, and apply the relevant pads where needed. This is often told to new users, but then they wonder how exactly these dynamic pads are handled.

Here is how it works, using decodebin as an example:

  • When the pipeline is run, the decodebin generates a signal that says a new pad has been added. In the case of decodebin this is the new-decoded-pad signal, in the case of gnlcomposition this is a pad-added signal.
  • You hook this signal up to your own callback, such as OnDecodedPad().
  • The signal passes your callback the pad that was added and some other stuff. You then link this pad to the next element along in the pipeline. So if your pipeline is filesrc ! decodebin ! audioconvert ! alsasink, the pad passed to your OnDecodeBin() method would be linked to the audioconvert.

To outline this, I have written a sample script to show how it works:

#!/usr/bin/python

import pygst
pygst.require("0.10")
import gst
import pygtk
import gtk

class Main:
    def __init__(self):
        self.pipeline = gst.Pipeline("mypipeline")

        self.filesrc = gst.element_factory_make("filesrc", "source")
        self.pipeline.add(self.filesrc)
        self.filesrc.set_property("location", "/home/jono/Desktop/jonobacon-voicesoffreedom.ogg")

        self.decode = gst.element_factory_make("decodebin", "decode")
        self.decode.connect("new-decoded-pad", self.OnDynamicPad)
        self.pipeline.add(self.decode)

        self.filesrc.link(self.decode)

        self.convert = gst.element_factory_make("audioconvert", "convert")
        self.pipeline.add(self.convert)

        self.sink = gst.element_factory_make("alsasink", "sink")
        self.pipeline.add(self.sink)

        self.convert.link(self.sink)

        self.pipeline.set_state(gst.STATE_PLAYING)

    def OnDynamicPad(self, dbin, pad, islast):
        print "OnDynamicPad Called!"
        pad.link(self.convert.get_pad("sink"))


start=Main()
gtk.main()

You can grab the script from here. Hope this helps some of you. :)

  • http://www.lovesunix.net/blog David Nielsen

    In the example pipeline don’t you mean filesrc not filesink… otherwise with my meager understanding you’d have two sinks and no source.. right?

  • jono

    Fixed! Thanks David. :)

  • http://progbox.co.uk/wordpress cbx33

    Dude you more than rock – thank you for your help with this :mrgreen:

  • http://www.lovesunix.net/blog David Nielsen

    Not being a big Python fan this strikes me as inconsistent: self.pipeline.add(self.filesrc)

    when everywhere else you use the passed name from the factory_make function I would have though it should be: self.pipeline.add(self.source)

  • jono

    David – wha?

  • http://www.lovesunix.net/blog David Nielsen

    okay, I’ll try slower for your sake Jono:

    shouldn’t: self.filesrc = gst.element_factory_make(“filesrc”, “source”) self.pipeline.add(self.filesrc)

    be: self.filesrc = gst.element_factory_make(“filesrc”, “source”) self.pipeline.add(self.source)

  • Jonathan Hitchcock

    Insert “not a gstreamer fundi” disclaimer here, but doesn’t your decodebin element want to link to the ‘convert’ element, and not to the ‘sink’ element?

    In other words, your callback:

    def OnDynamicPad(self, dbin, pad, islast): print “OnDynamicPad Called!” pad.link(self.convert.get_pad(“sink”))

    Should be, if I’m right: def OnDynamicPad(self, dbin, pad, islast): print “OnDynamicPad Called!” pad.link(self.convert.get_pad(“convert”))

    Not so?

    Still, we get the point – thanks for the explanation!

  • http://www.lovesunix.net/blog David Nielsen

    well at least we are all learning from this.. I had never touched GStreamer code before but now I’m getting into it by reading Jono’ examples..

  • Kushal Das

    while running the above example follwing error is coming:

    python: Python/ceval.c:2531: PyEval_EvalCodeEx: Assertion `tstate != ((void *)0)’ failed. Aborted

  • http://tristanb.net Tristan

    David,

    I see where you’re coming from.

    Here, self.filesrc is a GStreamer filesrc element, which is a Python object. This object has various properties, one of which is a “name” field, which in this case is equal to “source”. This name is how GStreamer refers to the element.

    I agree that it’s a bit confusing, that an element can have a (Python) object name that’s different from its GStreamer name; I tend to set them to be the same whenever I create an element, so I don’t get mixed up :)

  • http://www.jonobacon.org/?p=851 jonobacon@home » Using Gnonlin with GStreamer and Python

    [...] In GStreamer there is a concept called Dynamic Pads. What this means is that with some objects in GStreamer, pads only get added when other things have been processed, and one such item is the gnlcomposition. Before the gnlcomposition can know what pads should be added, it needs to check which gnlfilesources it has and automatically figure out which elements are required to process the media that you want to play. All of this happens automatically, but it means that you cannot assume that it will have a pad – once the gnlcomposition has figured out what it is doing with its gnlfilesources it will then provide a pad. See GStreamer Dynamic Pads, Explained for more details on this. [...]

  • http://www.jejik.com/articles/2007/01/streaming_audio_over_tcp_with_python-gstreamer Lone Wolves – Web, game, and open source development

    [...] Anoher thing I wanted an excuse for was learning gstreamer, so naturally I picked python-gstreamer. Sadly there’s a huge lack of documentation for it. There are only a few good tutorials around, first and foremost Jona Bacon’s excellent tutorials [1][2][3]. I wanted my hack to work over TCP as well, because I have multiple music libraries. One on my home server, one on my desktop, a bit more on my laptop, etcetera. I want to be able to stream from other machines to my server which I’ll hook up to my stereo. I could not find any tutorials on using tcpserversource and tcpclientsink elements, so honoring Jona’s request that everyone “should write an article about something that you have discovered that isn’t particularly well documented”, here’s mine about tcpserversource and tcpclientsink. [...]

  • Kyle

    How would you set up a queue to play many files sequentially or randomly? Would you have a pipeline in your play pipeline or just have a boolean test after it is done playing and if it isn’t the last in the a queue list and then play that file? Thanks for nay response.

  • http://livesets.com GroteBozeWolf

    I would like to create a never stopping stream of data (to send to an icecast server, with shout2send..). In my case, i want to play files, using a playlist. But i don’t understand how to make gstreamer aware of loading the next file, if the other one is ended. I understand i have to change something in the pipeline.

    The pipeline for one file is: filesrc file1.mp3 ! decodebin ! audioconvert ! lame ! etc.

    After file1.mp3 is finished, i have to make filesrc aware to load a new file, or i have to cut filesrc file1 from the pipeline and reconnect anew filesrc element. But how do i do that?

  • Christian Schaller

    Hi Jono, I am actually using your tutorials to teach myself some basic python/gstreamer hacking. One thing you might want to change in your example is naming your alsasink element ‘sink’ as it confused the hell out of me when I then saw ‘sink’ again in the pad.link argument.

  • http://blog.abourget.net/2009/6/14/gstreamer-rtp-and-live-streaming GStreamer, RTP and live streaming — Alexandre Bourget