Thursday 7 August 2014

UPnP Discovery with Sonos, event driven

Following on from my post UPnP Discovery with Sonos Players I present a revised version which doesn't add much in terms of functionality, but which is a bit tidier and has some changes to how we handle discovery.

In the last versions, we simply printed out whatever we discovered. Processing discovery on-the-fly like this will not suit a lot of real-world programs, especially if it involves something like a GUI. What we need to do is to store the results of out discovery so that we can reference them whenever we want.

This raises an important point about discovery. UPnP discovery is not a "one shot" activity. It is a continuous processes. You start it, and it then runs continuously until you stop it. There is no magic flag that says "discovery has finished".  Normally the start and end of discovery is dictated by the start and end of your program. In my program, discovery (and the rest of the program) is stopped by me running a 2 second timer, after which time discovery is stopped and the program exits.

This is a very important concept to understand as, during the continuous discovery process devices can come and go from the network. Your program has to be able to accommodate devices appearing and disappearing. Of course, we are used to that with the Sonos controllers: if we add a new player to the system it magically appears in the zone menu. If you disconnect power from a Zoneplayer it (eventually) disappears.

So what we need to do in any long-lived UPnP program is use the discovery to maintain a local copy of what is currently on the network.

This next piece of code does that. It is based on the previous versions but with some significant changes. I am using a GHashTable from the Glib collections library. Once again this is common on Linux systems. In fact it's a dependency for GUPnP so it will be there if GUPnP is.

GHashTable gives us a nice key-value table with the ability to easily add and remove items.

Here is the code:


Firstly, I have added some new functions as follows:

device_info (lines 19-21) is a simple print utility function which, given a key value and a renderer reference, prints out some information about that renderer. This is used by main_loop_timeout (lines 26-34) which has been altered to print out the contents of the GHashTable. Remember, main_loop_timeout is called to signal the end of the program.

We already had the callback function device_proxy_available_cb (lines 40-50) but now we have added a new callback function device_proxy_unavailable_cb. These now respectively add or remove the discovered renderer device to the GHashTable. What this means is that at any time our GHashTable should contain a current list of the available devices in the network.

(Note that a device which has its power removed will not normally announce it's departure from the network, so this list is not 100% correct).

These callback functions also print data about the added or removed device to the screen, so you can see what it's doing.

To accommodate these changes, we need to make some simple changes to the main program. Firstly we need to create our empty GHashTable, which I have called renderers (line 79). Then we need to register our interest in the device-proxy-unavailable signal which occurs when a device leaves the network, and point it to use our new callback handler (lines 97-99).

I think the only other change worth mentioning is that I have made the MediaRenderer URN into a constant (line 10 and line 87) as it's better coding practice.

On running this, you should get something similar to the following:

The first block shows the devices first being discovered. Then the 2 second timeout kicks in and this prints out the current contents of the renderers list before stopping discovery. Note that before it stops discovery the system automatically tidies up for us, removing all of the discovered devices (the final block).

If you wish, change the timeout value at line 106 from 2 seconds to something longer if you want to make this more obvious.

Also note that, in this case, a new media renderer showed up on my list "Intel AV Renderer (laptop)". This is the Intel AV Renderer which is part of their UPnP developer toolkit. I ran this on my Windows laptop to show that this is a generic UPnP AV capability that's being used.

If you set the timeout to a longer value, you can launch and close the Intel AV renderer and see it being added to, and removed from the list of devices.

As you can imagine, this could be wrapped in some sort of GUI to give a constantly updating list of the Media Renderer devices on the network, similar to the Zone list on the Sonos controller.

No comments:

Post a Comment