I’ve been working on a project for some time which involves using Python and GStreamer to display some video. The lack of documentation has been a PITA and I’ve been trying for some time to figure out how you get GStreamer to display its video output within a GTK widget. Well today, I figured it out. Thanks to this blog post by Jono Bacon I learned a lot more about GStreamer which motivated me to start looking at the problem again. His post urges everyone who can to document what they’re doing in Python and GStreamer, so here’s some example code to display some video within a GTK widget. The code is based on Jono’s example for an audio player.
#!/usr/bin/python
import pygst
pygst.require(“0.10″)
import gst
import pygtk
import gtk
class Main:
def __init__(self):
# Create GUI objects
self.window = gtk.Window()
self.vbox = gtk.VBox()
self.da = gtk.DrawingArea()
self.bb = gtk.HButtonBox()
self.da.set_size_request(300,150)
self.playButton = gtk.Button(stock=’gtk-media-play’)
self.playButton.connect(“clicked”, self.OnPlay)
self.stopButton = gtk.Button(stock=’gtk-media-stop’)
self.stopButton.connect(“clicked”, self.OnStop)
self.quitButton = gtk.Button(stock=’gtk-quit’)
self.quitButton.connect(“clicked”, self.OnQuit)
self.vbox.pack_start(self.da)
self.bb.add(self.playButton)
self.bb.add(self.stopButton)
self.bb.add(self.quitButton)
self.vbox.pack_start(self.bb)
self.window.add(self.vbox)
# Create GStreamer pipeline
self.pipeline = gst.Pipeline(“mypipeline”)
# Set up our video test source
self.videotestsrc = gst.element_factory_make(“videotestsrc”, “video”)
# Add it to the pipeline
self.pipeline.add(self.videotestsrc)
# Now we need somewhere to send the video
self.sink = gst.element_factory_make(“xvimagesink”, “sink”)
# Add it to the pipeline
self.pipeline.add(self.sink)
# Link the video source to the sink – xv
self.videotestsrc.link(self.sink)
self.window.show_all()
def OnPlay(self, widget):
print “play”
# Tell the video sink to display the output in our DrawingArea
self.sink.set_xwindow_id(self.da.window.xid)
self.pipeline.set_state(gst.STATE_PLAYING)
def OnStop(self, widget):
print “stop”
self.pipeline.set_state(gst.STATE_READY)
def OnQuit(self, widget):
gtk.main_quit()
start=Main()
gtk.main()
Download this code
We start off by creating our objects. Unlike Jono I’m not using glade to create the GUI because, for some reason, it won’t allow me to access the full range of functions/properties associated with the objects – perhaps my own fault. This part should be pretty self-explanatory for anyone who’s done GUI programming in other languages such as Java.
To understand the basics of GStreamer and pipelines, sources, sinks etc. refer to Jono’s post.
Here I’m using the videotestsrc which produces a testcard. To decode an actual file, you’d instead use a filesrc and decodebin and link them together. I then create an xvimagesink which will display the video using the xv functionality of X. There is also a sdlvideosink and a glvideosink – which you use will depend on what’s installed on the system of the user, but generally xvimagesink is the best choice. By default, any of the video/image sinks will simply create a new window in which to display their output, but we want to display it in the DrawingArea we created earlier.
This is achieved in the OnPlay function, where we get the DrawingArea’s ID using the window.xid property and pass it to the video sink we created earlier using the set_xwindow_id function. This tells the video sink to not create a new window, but use the DrawingArea instead. Note that we can’t get the window.xid property until after the window has been created, which is why we do it in the OnPlay function rather than before when we’re setting up the pipeline. Once that’s done, we can start the playback and enjoy the GStreamer video goodness. Note that this only displays video though – any audio content will be ignored. Getting audio and video playback working together is my next task and I shall endeavour to document that here once I figure it out.
Unfortunately I can’t show you a screenshot of the working script due to the way xv works using overlay – if you take a screen shot, you just get nothingness where the video is.