<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:admin="http://webns.net/mvcb/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:html="http://www.w3.org/1999/html" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>Ross Burton</title><link>http://www.burtonini.com/blog</link><description>A potted account of Ross' life</description><language>en</language><ttl>60</ttl><dc:creator>Ross Burton</dc:creator><admin:generatorAgent rdf:resource="http://pyblosxom.sourceforge.net/"/><admin:errorReportsTo rdf:resource="mailto:ross+web@burtonini.com"/><item><title>Experiments with GUPnP</title><guid isPermaLink="false">computers/gupnp-2007-10-09-17-50</guid><link>http://www.burtonini.com/blog/computers/gupnp-2007-10-09-17-50</link><description>Now that through black magic, voodoo, and a working network driver I have Avahi working on my NAS and UPnP ...</description><content:encoded><![CDATA[    <p>
      Now that through black magic, voodoo, and a working network driver I have
      Avahi working on my NAS <em>and</em> UPnP working on my SoundBridge, I can
      get back to the task of learning a bit about <a
      href="http://gupnp.org">GUPnP</a>, a GObject-based library for UPnP.  My
      first hack is a simple tool that pops up notification bubbles when the
      currently playing track changes, and it was surprisingly easy.
    </p>
    <p>
      First, wecreate a <cite>Control Point</cite> for the service we want to
      control.  This object will emit signals when devices on the network are
      discovered which provide this service, so by connecting to that signal all
      of the discovery is handled for us.
    </p>
    <pre>GUPnPContext *context = gupnp_context_new (NULL, NULL, 0, &amp;error);
GUPnPControlPoint *cp = gupnp_control_point_new (context, "urn:schemas-upnp-org:service:AVTransport:1");
g_signal_connect (cp, "service-proxy-available",
                  G_CALLBACK (service_proxy_available_cb), NULL);
gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);</pre>
    <p>
      Now in <tt>service_proxy_available_cb</tt> we are passed a <cite>Service
      Proxy</cite> object to which represents a device providing the service we
      asked for.  Service proxies can have <cite>actions</cite> invoked on them,
      or we can be notified when a <cite>State Variable</cite> change.  There
      is a state variable in AVTransport called <tt>CurrentTrackMetaData</tt>
      but don't let that fool you: for reasons too boring to detail that doesn't
      cause notifications.  The only interesting state variable in AVTransport
      is <tt>LastChange</tt>, which is basically a meta-variable which contains
      the name and value of the last variable which changed.  Madness, I agree.
      This is where we come to the problem with GUPnP: it makes the protocol
      seem so clean and simple that when warts like this become obvious, they
      stand out.
    </p>
    <p>
      Anyway, we want to listen for changes to the <tt>LastChange</tt> state
      variable. This involves adding a notify and then subscribing to the proxy.
      Luckily state variables emit their current value when we subscribe to
      them, so we never need to fetch the current value.
    </p>
    <pre>static void
service_proxy_available_cb (GUPnPControlPoint *cp, GUPnPServiceProxy *proxy)
{
    gupnp_service_proxy_add_notify (proxy, "LastChange",
                                    G_TYPE_STRING, notify_cb, NULL);
    gupnp_service_proxy_set_subscribed (proxy, TRUE);
}</pre>
    <p>
      Believe it or not, we're actually nearly finished.  Now to implement
      <tt>notify_cb</tt>.  This is passed the variable name and a
      <tt>GValue</tt> containing the new value.  A basic implementation is
      pretty simple.
    </p>
<pre>static void
notify_cb (GUPnPServiceProxy *proxy,
           const char        *variable,
           GValue            *value,
           gpointer           user_data)
{
    g_print ("%s changed to %s\n", variable, g_value_get_string (value));
}</pre>
    <p>
      And we're done!  When the program starts it discovers any devices on the
      network, creates proxies and watches the variables, printing them as they
      change.  Devices can come and go, GUPnP handles that automatically.  Easy
      as pie.
    </p>
    <p>
      Well, sort of.  The string is an XML document which could contain any
      variable, so we need to parse it looking for a
      <tt>CurrentTrackMetaData</tt> node.  On that node the <tt>val</tt>
      attribute contains an escaped XML document describing the metadata in the
      (and I kid you not) <cite>DIDL-Lite</cite> format.  The saving grace here
      is that at least its partially Dublin Core, but that needs to be parsed
      for the title and artist information.  This is all standard XML mojo, and
      quite boring.  The end result is that my first hack application is 150
      lines long, the first 100 of which are entirely devoted to XML parsing.
      The next step is to write a convenience library around DIDL to avoid
      having to parse it manually.  It's not exactly a complicated task, but
      quite tiresome.
    </p>
    <p>
      For the curious, the full source is in <a
      href="http://burtonini.com/bzr/tracknotify">this Bazaar branch</a>.
    </p>
    <p>
      <small>NP: <cite>Directions EP</cite>, Variou (from Acroplane I think)</small>
    </p>
]]></content:encoded><dc:date>2007-10-09T16:50:00Z</dc:date></item></channel></rss>