Thursday, 7 August 2014

Sonos UPnP Development - Let's play some music!

So far we have seen how to discover media devices, how to monitor what is being played on them, and how to pause a media device.

I present here a way to play something on a Sonos Zoneplayer (or, for that matter, any other media renderer).

The code here is based on the code in my post Sonos UPnP Development - Controlling Playback which has been modified so that, instead of pausing the Zoneplayers, it now tries to play a file on one of them.

Firstly, an important point to consider here is that, unlike UPnPClient5 which searched for, and addressed all of the zoneplayers, this version needs to target a specific zoneplayer. We, therefore need a way to specify this.

We also need to specify a file to play. Browsing your Sonos library, looking for a specific file, selecting it, and playing it is a subject in it's own right and, hopefully, I'll get around to covering it eventually. I intend to keep this simple so I'm going to play a URI from the Internet which points to an MP3 file. So our program needs a way for use to specify this file.

The code below include these enhancements:


Rather than hardcode this information, I have made them parameters so you call the program thus:
./UPnPClient7 <zoneplayer> <uri>

The code to get these parameters is mostly at lines 128 to 135. These are held in variables declared at lines 18 and 20 along with another variable, rendererFound, which is used as a flag for error reporting.

As before in UPnPClient5 we discover all of the media render devices and create a list of them. The callback function device_proxy_available_cb is used for this as before.

However, in this case we do a test for the media renderer name. This is at lines 98 to 103. Not we are only matching on the first few characters, so we don't have to provide the full name of the ZP. If we are not careful, we could match multiple ZPs, and in this case the program will play the file on all of them. Note this is NOT the same as linking the zones.

When a match is made, we call a new utility function play_stream which is defined at lines 38 to 86. This takes two parameters: a pointer to the renderer, and the URI.

The instruction to set the AVTransport to use this URI is at lines 68 to 72. This is a call to the AVTransport service "SetAVTransportURI", which does exactly as it says on the tin. This requires a few parameters:

  • InstanceID - Always 0
  • CurrentURI - Our URI
  • CurrentURIMetadata - This describes the URI in more detail
The CurrentURIMetadata parameter is a string containing an encoded DIDL-Lite description of the file. I have hacked this into a couple of strings "metadata1" and "metadata2" and used strcat at lines 59 to 61 to create the final metadata string which contains our URL. This is a nasty, hacky way of doing this, but it works and it also exposes the raw data so you can see what it is.

We then set this as the CurrentURI for our AVTransport. If this is successful, we then instruct the renderer to play (lines 74 to 77).

Job done!

If I call this as follows:
./UPnPClient7 Office http://www.archive.org/download/jj2008-06-14.mk4/jj2008-06-14d1t03_64kb.mp3

I will have Jack Johnson start to play in my office.


Note that this is actually not added to the Sonos queue. It is played directly. Adding stuff to the Sonos Queue is something which involves some Sonos-specific capability. At the moment I'm concentrating on standard UPnP capability. I hope to cover the Sonos queue in a future article.

No comments:

Post a Comment