The Coding Weasel

Tuesday, March 06, 2007

a theatrical open-source story, part one.

In what I laughingly refer to as "my spare time" I do lighting and sound for an amateur theatre company. The current play (plug: come see it - it's really, really good) has some fairly major tech behind it - particularly for a small amateur theatre (i.e. a miniscule budget).

The requirements include:
  • dual projectors behind stage, showing around 150 slides during the course of the show
  • a camera on stage at various points, going to the projectors. These then get news overlays on screen to make it look like a live news conference. The projectors are behind the screens, so the video needs to be flipped left-to-right.
  • a couple of dozen pieces of music to be played at various points - this needs to fade in and out cleanly.


I'm also operating the lighting desk while I do this, so whatever I do has to be incredibly easy to operate. Requiring me to use the mouse is just right out.

Oh, and of course I shouldn't forget that I really only had one or two evenings to put something together. The choice of hardware was pretty easy - clearly I'm going to need a couple of PCs - one backstage with the camera and the projectors, connected by wireless to a laptop in the lighting box running the whole show (and outputting audio to the sound system when needed). I grabbed a cheapo wireless card and a Radeon 9550 (with a DVI-to-VGA convertor, I get the two VGA outs that I need) for a spare desktop I had lying around and dropped a fresh install of Ubuntu Edgy on it.

Software - well, clearly this calls for some one-off custom software. Again, my choices were pretty easy. Python (duh), pygtk for the UI, gstreamer for the audio and video, and twisted to tie things together. Things like callLater and deferreds just make stuff too damn easy.

So how did I do? Well, here's a quick sampler (click the image for a larger version). I'll talk about it in more detail in a subsequent post. This was the result of a single Sunday evening's intense hacking, followed by a few hours over a couple of nights tweaking things after using it. All up, it's under 600 lines of code, plus a few tiny shell scripts.

Labels: , , ,

Tuesday, January 02, 2007

Bruce 1.2

Bruce 1.2 is out. This is the version resulting from the work Richard and I both did while preparing our talks for OSDC a month ago, and means you can run the presentations I linked to earlier, without needing to grab the SVN version of the code.

It works on MacOS X, Unix, and Windows - but note that some bits, such as SpawnPage and ShellInterpreterPage won't work currently on Windows. If someone wants to contribute patches to make these work (it shouldn't be hard - I just don't have a need or motivation to do it right now) they'll be gratefully accepted.

I've begun the work on what will become Bruce 2.0 - this will be a merge of the IronKant code, allowing Bruce to run on IronPython with SdlDotNet, and should hopefully also include a pyglet backend. Pyglet should allow for a whole host of shiny new possibilities.

Next up is a talk at LCA 2007's Speaker's Dinner in 2 weeks.

Labels: , ,

Wednesday, December 13, 2006

gstreamer and messages, pt 1

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: ,

Tuesday, December 12, 2006

some more on IronPython

Michael Foord has some notes on getting IronKant working with IronPython on Windows here

During my talk, I showed a slide that IronPython on .Net runs pybench at 41% of CPython 2.5's speed (71%, if you exclude the TryRaiseExcept case, where it totally loses), while Mono 1.1.17 gets around 14% of of CPython 2.5's speed. I finally got around to trying Mono 1.2.2 (I didn't have time before the talk). It's actually about 12% _slower_ than 1.1.17. This makes me sad. Very sad. I need to get pybench checked into IPCE and point the Mono and .Net folks at it.

It's pretty obvious that both .Net and Mono optimised for pystone - in both cases, IronPython is more than 20% faster than CPython. Unfortunately, pystones is a terrible benchmark. If they can optimise for pybench, most people's applications will see a real improvement.

Labels: , ,

Monday, December 11, 2006

talk #2: futurepython

The final talk from the three I did at OSDC is up now. This was the end of day talk on Thursday, "futurepython". I talked about IronPython and Python 3.0. Unlike the other two talks, this one wasn't done using Bruce. Instead, I did an extremely quick port of Bruce to run on top of IronPython and SdlDotNet. In keeping with the philosopher's naming scheme of Bruce, I called it "ironkant".

The talk is here, including the ironkant code. I should point out that ironkant almost certainly has bugs - I started it late Wednesday night after the conference dinner and finished it (and the talk) a good 5 minutes before I presented it the next afternoon. What can I say, I just thrive on the stress. Having the presentation running on top of IronPython was a pretty good demonstration of just how far along IronPython has come.

And sorry Jim for calling you a crazy man - I mean that with love and respect :-)

I was extremely impressed with how easy it is to grab a C# assembly, read the C# docs and get coding. I also only had to make one fix to a CPython standard library module to get the code to work - in this case, I had to fix codeop.py to not pass the DONT_IMPLY_DEDENT flag to the builtin compile() method. This flag is both undocumented and implemented in codeop.py in a most foul way - I've opened Python bug 1612012 about it. The fixed version of codeop.py is now in the SVN version of IPCE.

I started porting popen2 as well, that's not finished yet (but will be this week). For the moment, I'm just using os.spawnl() directly inside IronKant for spawning external programs.

The longer Windows.Forms example in the talk is from Michael Foord's excellent tutorial on the subject. If you're at all interested in producing native GUIs on both Windows and Unix, you should definitely check the tutorial out. And buy his book about IronPython when it's done!

Labels: , ,

Sunday, December 10, 2006

talk #3 - gstreamer and python

The third talk from OSDC is up as well, now. This was "GStreamer and Python". The talk itself can be found here, or via SVN with
"svn co http://codingweasel.googlecode.com/svn/trunk/talks/2006/gstpy"
Once again, it uses the current SVN version of Bruce, which you can get with
"svn co http://svn.bruce.python-hosting.com/trunk"
Unlike the previous talk, it's unlikely you'll be able to run it in it's full glory, because
a) it needs 3 sound cards :-)
b) I haven't checked in the many, many sound files (and single video file) I used for the talk, as they're all copyrighted to the original owners. The text and code should still be useful, though.


The goal is to turn the various examples from this talk into individual blog posts, with explanatory text. I mentioned during the talk a couple of posts by Jono Bacon that provide a good introduction - they can be found here and here.

Labels: , ,

Wednesday, December 06, 2006

What's New In Python, 2006

I've put the first of my OSDC 2006 talks up - What's New In Python 2006 Edition.

It can be fetched from http://codingweasel.googlecode.com/svn/trunk/talks/2006/whatsnew/
(or svn co thaturl)

This talk uses the Bruce presentation software, in particular the new 'socrates.py' script that takes a simple text file describing the presentation. At the moment, you'll need the current SVN version of Bruce. There will be a new release of that soon.

Alternately, you can just read the 'whatsnew.soc' file - it's a simple text file.

Update: You can get Bruce's current SVN with
"svn co http://svn.bruce.python-hosting.com/trunk"

Labels: ,