Posts Tagged ‘python’

More Video Goodness with Python and GStreamer

Sunday, May 6th, 2007

Further to my previous post, Video goodness with Python and GStreamer, I’ve now mastered playback of video along with audio using Python and GStreamer. This is my code to play back an MPEG2 transport stream:

def gstInit(self):
  # declare our pipeline and GST elements
  self.pipeline = gst.Pipeline(“mypipeline”)
  self.src = gst.element_factory_make(“filesrc”, “src”);
  self.src.set_property(“location”, “file.mpeg”)
  self.demux = gst.element_factory_make(“ffdemux_mpegts”, “demux”)
  self.queue1 = gst.element_factory_make(“queue”, “queue1″)
  self.queue2 = gst.element_factory_make(“queue”, “queue2″)
  self.deinterlace = gst.element_factory_make(“ffdeinterlace”, “deinterlace”)
  self.vdecode = gst.element_factory_make(“mpeg2dec”, “vdecode”)
  self.adecode = gst.element_factory_make(“mad”, “adecode”)
  self.vsink = gst.element_factory_make(“xvimagesink”, “vsink”)
  self.asink = gst.element_factory_make(“alsasink”, “asink”)
  
  # add elements to the pipeline
  self.pipeline.add(self.src)
  self.pipeline.add(self.demux)
  self.pipeline.add(self.queue1)
  self.pipeline.add(self.queue2)
  self.pipeline.add(self.vdecode)
  self.pipeline.add(self.deinterlace)
  self.pipeline.add(self.adecode)
  self.pipeline.add(self.vsink)
  self.pipeline.add(self.asink)

  # we can’t link demux until the audio and video pads are added
  # we need to listen for ‘pad-added’ signals
  self.demux.connect(‘pad-added’, self.on_pad_added)

  # link all elements apart from demux
  gst.element_link_many(self.src, self.demux)
  gst.element_link_many(self.queue1, self.vdecode, self.deinterlace, self.vsink)
  gst.element_link_many(self.queue2, self.adecode, self.asink)

def on_pad_added(self, element, src_pad):
  caps = src_pad.get_caps()
  name = caps[0].get_name()
  # link demux to vdecode when video/mpeg pad added to demux
  if name == ‘video/mpeg’:
    sink_pad = self.queue1.get_pad(’sink’)
  elif name == ‘audio/mpeg’:
    sink_pad = self.queue2.get_pad(’sink’)
  else:
    return
  if not sink_pad.is_linked():
    src_pad.link(sink_pad)

def startPlayback(self):
  # start playback
  self.pipeline.set_state(gst.STATE_PLAYING)

Naturally you begin by declaring your pipeline and elements (for an introduction to GStreamer, see my previous post linked above and the link to Jono Bacon’s post included in that post).
Then you link your ‘filesrc’ to your demux element (variable depending on the type of content you’re playing).

Now, you cannot link anything to your demux element until you’ve started playback – this is because your demux element hasn’t yet looked at the data its being given, so it doesn’t know whether it should have audio and/or video pads. So you need a local function to handle the ‘pad-added’ signal which is sent when, you guessed it, a pad is added to an element – we only care about this signal when it relates to the demux element, so we connect that signal from that element to a local function. In this function we first need to determine what type of pad has been added – audio, video, or something else. In my case, because I’m playing MPEG, I know that I’m waiting for audio/mpeg and video/mpeg pads. The precise pads you need to wait for will depend on the content you’re playing.

When each pad is added, you link both the video and audio pads to separate ‘queue’ elements. Queue elements are essentially buffers and you need these for video and audio playback to work at the same time. You can link the audio queue element to your audio decoder and the decoder to your audio sink during initialisation – you don’t need to wait until you can link in the demux element. Likewise for the video queue element.

The equivalent if you want to launch playback from the command-line would be:

gst-launch filesrc location=file.mpeg ! ffdemux_mpegts name=demux ! { queue ! mpeg2dec ! xvimagesink } { demux. ! queue ! mad ! alsasink }

Things become simpler if you’re using a ‘decodebin’ or ‘playbin’ element rather than specifying your elements individually (which is what you’ll do if you want to play more than one type of content). In this case with a playbin you can simply add that one element to your pipeline, after giving it a URI to play, without worrying about what the content is.

def gstInit(self):
  # declare our pipeline and GST elements
  self.pipeline = gst.Pipeline(“mypipeline”)
  self.pbin = gst.element_factory_make(“playbin”, “pbin”);
  self.pbin.set_property(“uri”, “file:///home/user/file.mpeg”)
  
  # add elements to the pipeline
  self.pipeline.add(self.pbin)

def startPlayback(self):
  # start playback
  self.pipeline.set_state(gst.STATE_PLAYING)

And on the command line:

gst-launch playbin uri=file:///home/user/file.mpeg

You’re probably wondering why I went through all the complicated code at the beginning of this post, when you can just use a playbin as above? Well, if you are only going to be playing one type of content you can make initialisation of GStreamer quicker and more efficient if you specify your elements manually rather than asking GStreamer to detect which elements it needs and then set them up. If you are in the position of being able to specify your elements manually, you should do so. If not, then you should aim to use a decodebin element to automatically detect the decoders needed for the content and specify the src and sink element(s) manually.

I haven’t given an example of using a decodebin, because I couldn’t make it work (GStreamer continues to be pretty buggy – you may well find that you need to use development versions of plugins or even core libraries in order to make things work as they should).

Edit: I’ve put together a quick, fairly simplified PlayBin example for those wanting something downloadable they can play with. playbin-example.py.

Setting lots of permissions

Friday, January 5th, 2007

I thought I’d share a quick script I’ve written to set the permissions for a directory tree. Of course it’s easy to do in a single command if you want the same permissions for directories and files, but that’s almost never the case. In home directories, I need the permissions for files to be 0600 (so the owner only has permission to read and write, but not execute). However for directories I want 0700 (same as before, but with the execute bit set) otherwise the owner won’t be able to enter the directory. I could find no quick or easy way to do this.

I initially tried to write this script in BASH, by essentially tying together ‘find’ and ‘chmod’ but it just wasn’t having it. I needed to read the output of find into an array, but it was interpreting spaces in filenames as separators and so was messing things up – nothing I tried could make it only interpret newlines as separators. So I gave up and wrote it in Python – here it is:

#!/usr/bin/python

from os import chmod
from sys import argv
from os.path import walk
from os import access
from os import W_OK

# get the target directory from the command line
targetdir = argv[1]

def setperms(arg, dirname, names):
  writable = access(dirname, W_OK)
  if not writable:
    print “Ignoring directory “+dirname+”; not writable.”
  else:
    # set the perms of the directory
    chmod(dirname, 0700)
    
    # set the perms of files in the directory
    for item in names:
      # generate the full relative path
      item = dirname+”/”+item
      # can we write the individual file?
      writable = access(item, W_OK)
      if not writable:
        print “Ignoring file “+item+”; not writable.”
      else:
        chmod(item, 0600)

# walk the target directory, calling our function as we go
walk(targetdir, setperms, 0)

Download this code

Simply edit the file to set the permissions you’d like setting – the first (0700) is for directories and the second (0600) is for files. Then call it with the starting path as the first (and only) argument, e.g.:
./setperms.py ./home

The first person to give me the 5-line BASH equivalent gets a punch in the face ;-)

GStreamer rocks!

Wednesday, October 4th, 2006

It’s dull and takes a long time to do anything? No, quite the opposite actually. I was rather dismayed to recently find that GStreamer was unable to play an MPEG2 transport stream, as neither of the two plugins that are supposed to be able to do this (ffdemux_mpegts which is part of GStreamer and flutsdemux which is produced by Fluendo) were working. So this morning I reported a bug against ffdemux_mpegts, mentioning in passing that the Fluendo plugin didn’t work either. Three hours later, both plugins have been fixed.

Rock on GStreamer devs!

Video goodness with Python and GStreamer

Thursday, September 14th, 2006

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.

Squeezed by a Python

Sunday, November 6th, 2005

No I haven’t been wandering around a zoo, I am of course going to write about Python the programming language.

I’ve been using PHP for web development for a number of years and am quite happy with it, but I keep hearing how wonderful Python is. Having used it for writing the odd GTK app, I’d be inclined to agree, but I was wondering how it compares when used under mod_python for web development.

So I decided to have a go at writing a simple script to display a row of data from a MySQL table. Here’s the PHP code I was trying to re-create (I wrote the same code in PHP so I could compare number of lines/complexity/etc):

<?php

echo "<html><head><title>PHP example</title></head>\n";
echo "<body>\n";

$conn = mysql_connect("localhost", "username", "password");
$db = mysql_select_db("databasename", $conn);

$result = mysql_query("SELECT * FROM pages;", $conn);

$row = mysql_fetch_array($result);

for($x = 0; $x <= count($row)-1; $x++) {
  echo $row[$x];
  echo "<br />";
}

echo "</body></html>\n";

?>

As usual when working with Python I found that there was little documentation available, but I didn’t let it deter me. Here is the equivalent Python code to the above:

<%

import MySQLdb

req.write("<html><head><title>mod_python example</title></head>\n")
req.write("<body>\n")

conn = MySQLdb.connect(host="localhost",user="username",passwd="password",db="databasename")
cursor = conn.cursor()
cursor.execute("SELECT * FROM pages")

datareturned = cursor.fetchone()

for value in datareturned:
  req.write(str(value))
  req.write("<br />")

req.write("</body></html>\n")

%>

It took about an hour to write that code and make it work. Note that the indentation is important, as if you incorrectly indent something Python will die with a syntax error which will keep you busy for an hour until you spot the mistake ;-)
What I mainly wanted to find out was whether Python would make it possible to do things such as interacting with a database with less code. But as you can see, both the PHP and Python files are 13 lines long, so Python offers no advantage there. Both implementations have their annoyances as far as complexity of code is concerned. In the PHP implementation, I should have been able to use:

foreach($row as $value) {
  echo $value;
  echo "<br />";
}

But for some reason that didn’t work as expected and printed each line of data twice, even though the data in $row was correct, so I had to use a full for loop.
In Python you have to convert everything to a string before it can be displayed, hence the addition of the str(). I mean come on – I thought high-level languages had left this nonsense behind? Surely if I tell a language to display a variable and the only way to display that variable is to cast it to a string, it makes sense for the language to handle that automatically?

I think the power of Python is in the high level functions that are available – e.g. you can do network socket operations using only a few lines of code. The things I do most in PHP are simple database operations, e-mail sending, image manipulation and a bit of XML parsing. I very rarely do anything complicated. Python just doesn’t seem to make these things any simpler, so unless anyone can convince me otherwise, I think I’ll stick with PHP for now…