Thursday 24 January 2019

Casting

Background 


For the past couple of years I have successfully used Pulse Jongo devices for playing music / radio on multiple devices.  By default you use the Pure Connect app to distribute sound but it has a useful feature using Caskeid which takes a bluetooth input to any Jongo and copies it other Jongos, thereby providing a proper multiroom system.  This formed the heart of my Multi-room Music Server (MuSe) which was based on a RPI streaming music via bluetooth to a Jongo and thence to the rest of the house.  However bluetooth has a short range and my (probably erroneous) perception was that is unreliable.

A second approach was to utilise RPIs in each room and use the multi-system networking capability of pulseaudio to send sound between them using Multicast RTP.  Each RPI is attached to a system via a 3.5mm jack plug interface.  This works ok but is restricted by the low-ish quality DAC provided on RPI.  A DAC HAT can be purchased to improve the quality (e.g. £12 from Pimoroni).  It would be preferable to stream directly from RPI to Pulse devices.  There is a much better solution for multi-room audio using multiple RPIs in the form of snapcast which can be installed through apt.  Snapserver is run on the source system and snapclient on recipient systems (which can include the source).  Music is streamed from a fifo audio output on mpd to //tmp/snapchat/fifo and then transmitted to clients and played through alsa or pulseaudio.  It was very well synchronised and sounded excellent.

Casting


I have been thinking for a while that it would be useful to cast to devices using something similar to ChromeCast audio (which has recently been discontinued).  It turns out this is really easy.

Phone-BubbleuPnP: this has a choice of where to cast music you are playing - Jongo devices such as JON and JOG are included in the list so you select the one you want and start playing music.  On a linux platform BubbleuPnP Server can be installed.  This makes it easier to connect to devices and also provides an alternative configuration called OpenHome which is a standard for devices.

Windows 10.  In the network folder Jongos are shown as media devices. Right clicking allows you to turn media streaming on for that device (or multiple devices).  You can then simply right-click on music and cast to a device, music plays straight away.

Note that uPnP, DLNA and casting are closely related.  What you need is to be able to send music to a device.  This requires an app which is DLNA or uPnP aware to initiate the communication.  We dont want client software.  It is a feature of some DLNA servers such as Windows Media Player.  It doesn't appear to work with vlc - usually my goto software for streaming functions.

This is a great solution - it is exactly what we want to do, casting from a variety of devices to Pure bluetooth/wireless Jongo connected speakers using wireless without needing to use Pure software to control them.

Linux Casting - pulseaudio-dlna

The last step is to cast from Linux to Pure devices. Music players such as mpg123 output to the alsa card by default we need to redirect the output to Jongo devices. I looked very quickly at using minidlna or servers like serviio but soon realised that pulseaudio casting is what I need.  There are two suitable solutions rygel and pulseaudio-dlna.  I successfully tried "apt-get install pulseaudio-dlna" so concentrated on its configuration.  Starting pulseaudio-dlna as user pi causes extra sinks to be added to pulseaudio.  You can then start music (e.g. using mpg123) and redirect output to the jongo.
Initially pulseaudio-dlna wouldn't connect to pulseaudio.  I put pulseaudio in system mode but then mpd would not connect to pulseaudio.  Messing around with groups and the load for module-native-protocol-tcp seemed to fix the problem and I was able to control pulseaudio in the usual way use Pulse Jongo devices.

This is an awesome result, we are using Pure devices as if they are speakers from linux and they fit perfectly to our web/mpd/pulseaudio solution avoiding the need to use bluetooth.  Much kudos is due to Massimo Mund (Masmu) for developing the pulseaudio-dlna add-on.

[update] After the euphoria of finding a neat solution using pulseaudio-dlna there was a show-stopper.  pulseaudio-dlna doesn't support the pulseaudio feature which allows you to send output to multiple devices. Pulseaudio provides module-combine-sink which permits a number of sinks to be combined and output can be redirected to all of them.  A similar effect is achievable using module loopback to "listen" to the sink.monitor stream.  Unfortunately pulseaudio-dlna doesn't support these "virtual" devices and I was unable to complete the installation.
Masmu did provide an experimental branch of pulseaudio-dlna on github which aimed to provide the necessary functionality for combined-sink but I didn't feel confident enough in the software to test it.

Thinking laterally, it occured that I could set up multiple mpd outputs to pulseaudio and route each to a single pulseaudio-dlna speaker.  It was a bit of a botch but did work except that output from the two speakers stuttered, so I didn't progress it further.

As an aside GMediaRender is of interest.  It can be installed with apt-get. When running on an RPI the RPI will show up as a media renderer in BubbleuPnP so that music can be played on the RPI speaker.  GMediaRender allows a wide variety of sinks to be used and I thought it might be possible to direct output accordingly.  I installed various gstreamer packages to identify and use sinks but nothing looked promising.  Gstreamer is an audio swiss army knife and other functions may be worth investigating in future, for example.

Linux casting part II - nodejs

There is an apparent dearth of software to send music to DLNA outputs in Linux.  Rygel appears to be Gnome GUI oriented, mkchromecast and castnow provide Chromecast output but don't recognise other DLNA devices. Simon Kusterer's castnow github page mentions another utility called dlnacast, which wouldn't work for me but it also mentioned a upnp/DLNA media-renderer client by Thibaut Seguy upon which it was based.
upnp-mediarenderer-client (UMC) is written in "server" Javascript which I haven't used before.  I installed nodejs and npm and was then able to install UMC.  Thibaut provides a sample node.js javascript program to read media and send it to DLNA. The DLNA device address is a SSDP (Simple Service Discovery Protocol) URL, which I have never seen or heard of.  Googling provided an answer so I installed gupnp-tools and ran gssdp-discover which provided a list of ssdp locations.  An SSDP location is a http URL for an XML file.  The URL includes the IP address UUID (available in windows properties) and a port number (which I couldn't find elsewhere).
UMC is based on upnp-device-client which provides the basic functions for device communication.

Once I substituted an SSDP location and a valid music URL in the sample node.js program I was able to play music on a Pure Jongo system.  Modifying the program to send output to two devices and sound came out from both simultaneously., it was a wonderful experience.  We now have a program which sends output wherever we want it.

[update]
Although our NodeJs program plays files either from a folder (e.g. /home/pi/music/runaway.mp3) or a web URL (e.g. http://rpip/music/runaway.mp3) if I tried to play an mpd stream output (http://localhost:8100/) I heard a snippet <1 second and no more.  I could tell the stream was running as it played correctly in a browser session.
I tried unsuccessfully to reconfigure MPD httpd output with different parameters.  I also tried but failed to setup shoutcast server output.
I tried to understand the UMC source code but it wasn't easy to guess its function.  I then investigated other npm audio packages in an attempt to shed light on playing / streaming music but they seemed somewhat flaky, nothing worked well so I abandoned node.js and investigated python instead.
It turns out that the node.js program works just fine.  I simply had to install icecast2 for MPD shoutcast to work and nodejs/client worked perfectly.





No comments:

Post a Comment