If you take a simple GStreamer pipeline (for instance, something based on the first piece of code in this post) and run it, one of the first things you'll notice is that it doesn't exit when the file finishes playing. To do this, you'll want to use messages. Here's a simple example showing this. This example sets up a filesrc, connects it to a 'mad' element (which decodes MP3s) and then dumps out to ALSA via an alsasink.
import pygst
pygst.require("0.10")
import gst
import gobject
class Playback:
def __init__(self, filename):
self.pipeline = gst.Pipeline('player')
# Get the message bus from the pipeline
self.bus = self.pipeline.get_bus()
# register a watch function - self.message
self.bus.add_watch(self.message)
# create a filesrc->mad->alsasink pipeline
filesrc = gst.element_factory_make("filesrc", "input")
filesrc.set_property('location', filename)
converter = gst.element_factory_make("mad", "converter")
output = gst.element_factory_make("alsasink", "output")
output.set_property('device', 'plughw:0')
# Add them all to the pipeline with one call.
self.pipeline.add(filesrc, converter, output)
# Link them all together in one call.
gst.element_link_many(filesrc, converter, output)
self.pipeline.set_state(gst.STATE_PLAYING)
def message(self, bus, message):
# Check for an 'End Of Stream' message
if message.type == gst.MESSAGE_EOS:
print "Got EOS, quitting mainloop"
self.quit()
# We want to continue receiving messages, so return True
# If you return False, this function is removed from the list
# of functions that receive bus messages.
return True
def run(self):
self.mainloop = gobject.MainLoop()
self.mainloop.run()
def quit(self, *args):
self.mainloop.quit()
if __name__ == "__main__":
import sys
player=Playback(sys.argv[1])
player.run()
Let's look at this in more detail. After we create the pipeline, we get the message bus associated with it.
# Get the message bus from the pipeline
self.bus = self.pipeline.get_bus()
We then add a 'watch' function to the pipeline. This function will receive all messages on the bus. You can't add more than one watch function to a pipeline (yet).
# register a watch function - self.message
self.bus.add_watch(self.message)
The watch function itself is pretty simple - it takes the bus and the message as arguments, and returns True or False. The return value indicates whether we want to keep receiving messages. If you return False, this watch function is removed from the bus.
def message(self, bus, message):
# Check for an 'End Of Stream' message
if message.type == gst.MESSAGE_EOS:
print "Got EOS, quitting mainloop"
self.mainloop.quit()
# We want to continue receiving messages, so return True
# If you return False, this function is removed from the list
# of functions that receive bus messages.
return True
We compare the message.type
with the singleton gst.MESSAGE_EOS
. If it's true, we stop the mainloop and exit.
There's many other messages you can receive, not just end-of-stream. If we put a "print message.type"
statement before the message.type check in the message()
method, you'll see something like this:
flags GST_MESSAGE_CLOCK_PROVIDE of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_TAG of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_NEW_CLOCK of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_STATE_CHANGED of type GstMessageType
flags GST_MESSAGE_EOS of type GstMessageType
If you look a bit closer, you'll see there's a couple of sets of messages. First off, there's 4 STATE_CHANGED messages - one from each element, and from the pipeline itself. These are going from a state of VOID to PAUSED, as the pipeline starts up. There's another set of 4 going from PAUSED to PLAYING, and a final set of 4 going from PLAYING to VOID when the pipeline finishes. There's also a couple about the pipeline clock (which I'll touch on in a future piece) and a MESSAGE_TAG message. That's from the 'mad' converter, and if we print it, we'll see:
<gst.Message taglist, layer=(guint)3, mode=(string)joint,
emphasis=(string)none, audio-codec=(string)"MPEG-1\ layer\ 3",
bitrate=(guint)128000 from converter at 0x81cae98>
Note that these messages are from the pipeline as a whole, not from individual elements. If you want to know about when a particular element runs out of data, you use pad.add_event_probe()
and look for EVENT_EOS
. More on that in a future post.
That's enough for one starting post.
Please note - I'm still learning about gstreamer as I go. If you see something wrong, or unclear, please let me know in the comments. Also let me know if blogger has messed up the formatting - I think I've got it OK now.
Labels: gstreamer, python