Synchronization of Multiple UPnP MediaRenderers
Synchronization of multiple UPnP renderers is one of the more common requests from owners of multiple playback devices who want synchronized, whole-house, audio playback. Unfortunately, the underlying protocol (HTTP-GET) used by the majority of UPnP servers and renderers (in the current generation) does not include the necessary timing information to implement this the 'correct' way. The good news is that there are well-established protocols that do provide better support for this (Real-Time Streaming Protocol for example), and UPnP itself is protocol-agnostic, so in the future this will likely be well supported by numerous UPnP devices.
So...what to do in the interim period before the commonly used UPnP protocols support 'true' synchronization? One solution for some users is to use the SlimServer software, since the Slimserver UDP protocol provides support for synchronization. If one wants to make use of UPnP HTTP-style servers, though, is there some cheesy synchronization method that is somewhat usable, if not perfect? Well,
maybe, if one is considering only syncing up devices of
the same type and firmware release. The basic idea is to feed the devices from a server that has some built-in intelligence and attempts to distribute the data to each renderer in a tightly-controlled, synchronized manner. That can be 'good enough' in some settings. The method relies on the fact that renderers of the same type have the same end-to-end buffering latency, so if the packets responsible for 'turning on the device' (the packets that cause the buffer to be considered full-enough to begin playback) arrive at the multiple devices at close to the same point in time, the devices will begin playback close enough togther in time to be perceived as being synchronized (20-40 milliseconds seems to be a good goal here). This assumes the renderers have little, if any, timing 'slop' in their primary packet ingest mechanism, such as is introduced if a polling loop with a sleep of more than 10ms is used rather than an interrupt-driven (i.e. blocking read) approach. This needs to be determined on a renderer-by-renderer basis.
In a favorable network environment (wired or wireless with good signal quality), this technique can be used with some success. Since none of the commonly available servers are (yet) capable of this, a 'proxy server' is required to perform the synchronization. This approach allows for content from any UPnP server to be played back in a synchronous manner, with the possible exception of some forms of DRM-protected content that aren't allowed to pass through a proxy even if the final destination device is DRM-capable.
The Cidero software bundle includes a proxy server that works in many
situations (but it should be stressed that your mileage may vary!). There are
two typical modes of usage. The first mode uses an instance of the proxy server built in to the controller. This is the default setup when the software is first installed, since it is the simplest to set up and use (i.e. run a quick-look test). This setup is illustrated below.
This is not an optimum configuration since a number of the controller threads running in the same Java Virtual Machine (JVM) may interfere with the timing of the proxy thread at the critical 'start of playback' time. Don't give up on the whole idea prematurely if it doesn't work very well for you in this configuration!
The second, preferred, usage is to install an external copy of the proxy server on the same machine as your primary UPnP media server, or if using a NAS for a server, another machine on your network that is hardwired to the network (this assumes that your controller will be used on a roving wireless laptop, and yes, this does reduce the benefit of running a NAS - the 'no running PC' benefit). The standalone version of the proxy server is called 'SyncProxy', with a startup script (.bat file or .sh file for UNIX) residing in the same place as the controller's startup script. When using this approach, the proxy setting for the controller should be modified to reflect the IP address of the machine on which the proxy is being run. By default, the port is set to 18081. If this is changed, it needs to be changed in both the controller UI (don't forget to do a 'File-Save Setup' after making the change) and in the properties file for the proxy (in
$USER_HOME/.cidero/SyncProxy/SyncProxy.properties on the machine in which you are running it). This preferred proxy setup is show in the figure below.
WMC requires that all devices accessing it be authorized. It authorizes by MAC address, so a good general rule of thumb is to run the controller on the same machine you are installing the proxy on, and making sure you can browse WMC. Once you authorize the controller, a proxy running on the same machine should be able to access WMC with no trouble.
Even if everything goes well and one achieves perfect synchronization at the start of playback, two (or more) renderers will tend to drift with respect to each other over time, due to small differences in the 'master clock' between devices. Measurements on 2 Roku Soundbridges (an admittedly small sample) found that
for these two devices, after 90 minutes or so, a small echo effect began to
become noticeable. This situation may be better for some device 'pairs' and worse for others.
To deal with the problem, the controller automatically resynchronizes the stream (stopping and restarting the renderers) periodically, in the interval between songs. In order to minimize disruption (playback gaps' within a single album, the synchronization does a simple check to see if a song is the first song in a series of songs from the same album. If so, it always resynchronizes at the beginning of the album, so the album can normally play all the way through with no need to be resychronized in the middle.
The resynchronization period and album check logic can be modified for a particular setup via the 'Options-Synchronization Options'
menu.
One small optimization that can be used is to figure out which of your devices plays faster than the others, by temporarily disabling the auto resync options and letting things run for 4 or 5 hours. If you detect no echo after that time, you are lucky and you don't have to do anything! If you do detect an echo, determine which device is 'ahead' the other. Put the device that runs the fastest in the location that is usually further away from the majority of listeners. When the devices first start a playback, there will a small echo effect (may not be noticeable if systems are within 50' each other) due to the normal sound propagation delay over the distance between your 2 systems. As time progresses, this echo should actual be
reduced in the primary listening location if the device in the 'more remote' location run faster. At some point, of course, the time drift of the faster device will exceed the normal sound propagation delay, and then the echo will begin to increase again. Nevertheless, this effectively doubles the time one can do playbacks without doing a resync, assuming there are no (or infrequent) listeners at the remote, secondary location. Hey, I
saidit was cheesy!
Trying it out
Ok, too much low-level information probably! Trying it out in the default configuration is reasonably simple. Bring up the renderer windows for 2 devices. Slave one of the renderers to the other using the 'link' button at the bottom right of the transport control panel. Add some content to the play queue, and hit Play as you normally would. If sync is lousy the first time, try it at least once more before giving up.
After that, I highly recommend configuring and running the proxy server on the same machine as your default media server (hopefully a hardwired machine). That generally improves the sync performance quite a bit, as does having the renderers themselves hardwired as opposed to wireless. Use the 'Options-Synchronization Options' dialog to configure the controller to use a remote proxy (remember to do a 'File-Save Setup' if you want to make the change permanent). Then simple run the 'SyncProxy' on the machine with the IP address you specified in the controller window
Have fun with it - I hope it works for ya!
Feedback always welcome.