Watching iView with Rygel

One of the features of Rygel that I found most interesting was the external media server support.  It looked like an easy way to publish information on the network without implementing a full UPnP/DLNA media server (i.e. handling the UPnP multicast traffic, transcoding to a format that the remote system can handle, etc). As a small test, I put together a server that exposes the ABC's iView service to UPnP media renderers.  The result is a bit rough around the edges, but the basic functionality works.  The source can be grabbed using Bazaar: bzr branch lp:~jamesh/+junk/rygel-iview It needs Python, Twisted, the Python bindings for D-Bus and rtmpdump to run.  The program exports the guide via D-Bus, and uses rtmpdump to stream the shows via HTTP.  Rygel then publishes the guide via the UPnP media server protocol and provides MPEG2 versions of the streams if clients need them. There are still a few rough edges though.  The video from iView comes as 640x480 with a 16:9 aspect ratio so has a 4:3 pixel aspect ratio, but there is nothing in the video file to indicate this (I am not sure if flash video supports this metadata). Getting Twisted and D-Bus to cooperate Since I'd decided to use Twisted, I needed to get it to cooperate with the D-Bus bindings for Python.  The first step here was to get both libraries using the same event loop.  This can be achieved by setting Twisted to use the glib2 reactor, and enabling the glib mainloop integration in the D-Bus bindings. Next was enabling asynchronous D-Bus method implementations.  There is support for this in the D-Bus bindings, but has quite a different (and less convenient) API compared to Twisted.  A small decorator was enough to overcome this impedence: from functools import wraps import dbus.service from twisted.internet import defer def dbus_deferred_method(*args, **kwargs): def decorator(function): function = dbus.service.method(*args, **kwargs)(function) @wraps(function) def wrapper(*args, **kwargs): dbus_callback = kwargs.pop('_dbus_callback') dbus_errback = kwargs.pop('_dbus_errback') d = defer.maybeDeferred(function, *args, **kwargs) d.addCallbacks( dbus_callback, lambda failure: dbus_errback(failure.value)) wrapper._dbus_async_callbacks = ('_dbus_callback', '_dbus_errback') return wrapper return decorator This decorator could then be applied to methods in the same way as the @dbus.service.method method, but it would correctly handle the case where the method returns a Deferred. Unfortunately it can't be used in conjunction with @defer.inlineCallbacks, since the D-Bus bindings don't handle varargs functions properly. You can of course call another function or method that uses @defer.inlineCallbacks though. The iView Guide After coding this, it became pretty obvious why it takes so long to load up the iView flash player: it splits the guide data over almost 300 XML files.  This might make sense if it relied on most of these files remaining unchanged and stored in cache, however it also uses a cache-busting technique when requesting them (adding a random query component to the URL). Most of these files are series description files (some for finished series with no published programs).  These files contain a title, a short description, the URL for a thumbnail image and the…