Stream audio on a RaspberryPi for playback on HiFi

By: Sam Light
On: 31st May 2017

Backstory

I decided to buy myself a HiFi AMP and speakers for my home. I chose this over some kind of all in one, wifi/Bluetooth type speaker system as I believe that separate speaker amp setups sound better. The only catch is I feel like I'm missing out on the ability to control my music wirelessly.

There are off the shelf solutions for enabling this functionality but some can be expensive and I had a strong concern that they may not be very user friends. There is Sonos, this can be hooked up to an amplifier, but I have used it and you are stuck within their ecosystem using their apps. This is limited and just doesn't sit well with me.

What I wanted is a way to play music with any application on my computer whether it is a music player application or a web browser playing youtube. I use Linux as my main Operating system and use PulseAudio to manage my audio. This gives me the option to pick different audio output devices.

Now what I wanted was a way to make the Raspberry Pi A DLNA media renderer to stream audio too. I then wanted a way to create a PulseAudio output device that would stream to the DLNA renderer.

There is gmrender-resurrect that will do the media renderer. I compiled this on the PI. It seemed to work.

I then found PulseAudio DLNA. I initially found this worked but then I had issues with it not connecting properly, then working for a while, then disconnecting. Sometimes I just couldn't get it to work at all.

Solution

Make a new way to stream PulseAudio to the PI. This really started out with me just trying to figure out all the basics to get this working. How could I create a PulseAudio sink, then how could I capture the audio from it then, reencode the audio for streaming. I started off using a FIFO sink then passing the output of that to FFmpeg for encoding. This didn't work so well. I then realized that people used a null sink and then use parecord to get the output from that sink. Parecord is interesting as it just outputs the raw audio to stdout. So I could pipe this or write it to my own FIFO file. This allowed me to pass the raw data to FFmpeg then capture the output from that. This gives me a way to get the audio that I now need to stream over the network.

This was the tricky bit. It has crossed my mind at this point I could make a little program on PI that I can just run in SSH to connect to my laptop and play the stream. I didn't want to do this though, I wanted a system where the PI is discoverable then I can connect to it from my laptop. To do this I decided to keep using gmediarenderer on the PI to act as a DLNA renderer. This meant that I had to create a DLNA server and controller.

I implement the UPNP discovery service and filtered it to just get devices that have DLNA rendering functionality.  This would allow me to create a PulseAudio null sink to send out to any of the DLNA devices on my network. Then when one of these sinks becomes active my application will then tell the corresponding renderer to connect to the UPNP server on my laptop. This basically just means the server just needs to be an HTTP server that will send the data of the audio stream on request.

Result

This system works reasonably well. There are a few bugs and issues I'd one day like to iron out. One thing is occasionally the audio breaks up and you have to kill the stream and reconnect to get it to go clear again. I also only have the ability to stream in mp3 right now. I would like to do FLAC as if I playing a lossy file it will come out of parecord in lossless raw audio then get re-encoded back into a lossy compression format. In my knowledge, that means it's going to lose even more audio fidelity.

With the above issues in mind, I still feel like this works better than the pulseaudio-dnla application that already existed. It works mostly in the same way. Although this works better for me with my DLNA renderer, the raspberry pi with gmediarenderer, I don't think I have implemented enough of UPnP protocol to work on all devices. This is noticeable when trying to connect to my Samsung TV. This is something I would like to fix but it's not a priority for me.

What's next

I would like to get ways to achieve the same result on Windows and Mac. It would also be pretty cool if I could find a way to stream from mobile devices. I myself, don't really do that anymore though.

Links

MY CODE

Leave a comment

Your email address will not be published. Required fields are marked *