PyClutter video tutorial

By on February 9th, 2010 in Python Comments (3)

As usual, we’re learning together: today I decided that I would like to be able to play video in our Bazooka (PyClutter-based) framework, so I set out to write a Bazooka video class. The source code for Bazooka, including this video class, is in our git repository here.

First, make sure you have PyClutter 0.9.2 installed: Steve, being an altruist of epic proportions, has taken the time to create a MacPort for PyClutter running under Python 2.5 or Python 2.6. If you’re running Mac OSX and you have MacPorts and Python 2.5-2.6 installed (a plethora of ifs there), just type:

sudo port install py26-pyclutter

Or, if you’re running Python 2.5, change that 6 to a 5. If you’re running some version of Linux or Windows you might want to refer to Clutter’s installation instructions.
You may notice that we are installing PyClutter 0.9.2 when 1.0 is available at the time of this writing; this is because we need the pyclutter-gst bindings to play video. Make sure that everything has installed properly by running the Python interpreter and typing:

import clutter, gst, cluttergst

Clutter uses the C library Gstreamer to play video. Obtain the latest documentation here.

Note: I had some serious difficulties getting GStreamer running on Snow Leopard. In short, I had to ensure that the gst-ffmpeg plugin and all of the good, bad and ugly (not a joke, those are really their names) gst-plugins were installed under MacPorts. As a coup de grace, I even had to load a plist with launchctl (this one: org.freedesktop.dbus-session.plist). I won’t bore you with the extensive details; let me know if you have any issues, I’m happy to assist you in any way I can.

Step 1: Find a video.
I’m going to steal this guy’s 13 MB MP4 yoyo championship video because, let’s face it, this might be the most awesome thing I’ve ever seen. Go ahead, try to top yo-yo interpretive dance competition footage from 2001. I dare you.

Step 2: Research.
Let’s learn about GStreamer’s Python bindings first, because generally when Clutter incorporates other libraries (Cairo, Box2D, GTK), it does so by thinly covering their APIs. Read this great article: Getting started with GStreamer and Python. I’m as ADD as a pug at Thanksgiving dinner, so it took me about an hour and a half to get through this post, between watching random YouTube videos on ice skating and eating a popsicle. But now we’ve arrived, we’re children of the GStreamer gods and we can work with Clutter’s GST bindings with a mild sense of undeserved entitlement.

Step 3: Write a sample video script.
I can’t lie to you, my favorite blogoverse: Clutter is not particularly strong on tutorials. Or empathy for the weak (read: those who are not C programmers). In fact, you would do best to imagine these developers catching fish with their bare hands in an Alaskan stream, like grizzly bears. That said, the best way to understand this particular band of grizzlies is to read their code. So to figure out what Python classes we might use to stream our porn, we open the cluttergst.defs file within the PyClutter 0.9.2 source (ALWAYS get the source with this framework, the reference docs are decent (in C) and scarily uninformative (in Python)). The defs and overrides files define the mappings between the C and the Python Clutter code. And here we find some useful information:

(define-object VideoTexture
  (in-module "ClutterGst")
  (parent "ClutterTexture")
  (c-name "ClutterGstVideoTexture")
  (gtype-id "CLUTTER_GST_TYPE_VIDEO_TEXTURE")
)

Okay! There’s something called a VideoTexture object with a superclass of ClutterTexture. I find this very promising. Let’s use it! But how? In the 0.9 C docs, we read that this class implements ClutterMedia and ClutterScriptable, so now we know that we can get and set the URI, set it to play or to stop, seek, get the progress, get the filename, etc. So let’s write a test script completely in PyClutter to try this out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
import clutter, gst, cluttergst
 
stage = clutter.Stage()
stage.set_size(1024,768)
stage.connect('destroy', clutter.main_quit)
 
tx = cluttergst.VideoTexture()
tx.set_uri("file:///Users/cmerand1/Desktop/hindsightlabs/bazooka/examples/yu3.mp4")
tx.set_playing(True)
 
stage.add(tx)
stage.show_all()
clutter.main()

Step 3: Write our video class. What I’m most concerned with here is intuitiveness and usability: putting a video element on stage should be extremely simple, because I am lazy. I like to set everything in the constructor when I can, and I love properties. Here’s the Bazooka code for our little monster:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import clutter, gst, cluttergst
from bazooka.base import Widget
 
class Video(cluttergst.VideoTexture, Widget):
    '''
    Displays a video with controls.
    @param uri: The location of the video file, such as: file:///Users/me/Desktop/file.mp4
    @param size: An optional tuple of values (width, height) representing the video size
    @param play_now: Whether the video should begin playing immediately.
    '''
    def __init__(self, uri=None, size=None, play_now=True):
        Widget.__init__(self)
        cluttergst.VideoTexture.__init__(self)
 
        if size:
            self.set_size(*size)
 
        if uri:
            self.uri = uri
 
        if play_now:
            self.play()
 
 
    def _get_uri(self): return self.get_uri()
    def _set_uri(self, v): self.set_uri(v)
    uri = property(_get_uri, _set_uri, doc="Sets the URL of the video to play")
 
    def play(self):
        self.set_playing(True)
 
    def stop(self):
        self.set_playing(False)

A very barebones approach to video. We’ll add some more impressive features in my next post, I swear. Incidentally the order of multiple inheritance is important here; the cluttergst.VideoTexture must appear first or Clutter’s assertion of the object being ClutterMedia will fail.

Okay, we’re all exhausted, we’ve learned too much, and our brains are considering seceding from the union. One last thing: let’s take a look at our new, shiny Bazooka example code:

1
2
3
4
5
6
7
import bazooka
from bazooka.stage import Stage
from bazooka.video import Video
 
stage = Stage()
video = stage.add(Video(uri="file:///Users/cmerand1/Desktop/hindsightlabs/bazooka/examples/yu3.mp4", size=(1024,768)))
bazooka.main()

Three lines aside from imports; that’s right, bisnatch, that’s how we do here at Lazy Programmers International. Now, let’s go watch some more professional yo-yo footage.


  • Jimmy

    Hi,

    In the installation of another soft (empathy) I have the same kind of trouble you noticed and I probably have to load a plist (also this one: org.freedesktop.dbus-session.plist) with launchctcl but I don’t know how to do it. Could you give me more informations please?

  • Christine Meranda

    Sorry for the late reply! You’ve probably figured it out by now, but if not, here’s how to load that plist:
    launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist. I believe I loaded it both with my user and with sudo, but I can’t entirely remember, doh! Hope that works out for you.

  • http://www.anildewani.com programming blog

    Awesome tutorial. I am looking for the same…