Intro And Musings
It’s not too often that I find a new docker application that really excites me and changes the way I do things, or fundamentally sets a concept on its head. Music Assistant is one of those. While it does have a very tight integration with Home Assistant, in this article we are going to install it in a standalone docker and then install it in Home Assistant via a proxy application someone made. I’ll go into why I made this decision. In order for me to really explain what I think sets this apart, we need to take a trip into the deep end first.
Castaway
I’ve had a love and hate relationship with Google casting over the years. Some applications do it bit differently, some hardware devices are more reliable than others. Wired cast targets vs wireless can present challenges as well. There’s an underlying technology at play here, mDNS. (Multicast DNS) It’s supposed to be fairly transparent and “just work” but…yea. Understanding better it is probably best.
Note: There’s also SSDP which is another service discovery protocol, but we’re focusing on mDNS here, googlecast specifically.
mDNS Intro
This protocol is a link local (layer 2 only) technology that can help applications resolve services. ARP (address resolution protocol) is another example of a discovery mechanism where mac addresses are mapped to IPs. mDNS can be thought of as “ARP for services.” Being layer 2, it cannot go beyond L3 boundaries but there are mechanisms available than can forward requests. I may cover that in another article.
In mDNS, when browsing for a service, the source sends a query like _googlecast._tcp.local,
and all devices on that segment that run that service respond with their records. Eventually those responses will time out, until the need is requested again. For example, when you go to use the cast feature in an app, the device sends out that query and waits for a response. That’s why you can see some of them “wink into existence” while waiting for your targets to show. There’s also a local cache which flushes after a time.
The query uses the special multicast address of 224.0.0.251:5353 that everyone running the service can reply to. This is different than a normal DNS lookup in that mDNS does not require a centralized resource to pull a record from. It pulls them dynamically from the devices providing the services.
Note: I know. There’s a lot of nuance missing here and other protocols, general purpose multicast, etc. Don’t forget, this is a primer so that I can explain Music Assistant better.
Before we go on, let’s have a talk. Is your local domain suffix still .local? You silly monkey, .local has been a suffix reserved for mDNS since 2013. If you want mDNS to work correctly, you’re going to have to change your domain. Go do that now. We’ll wait. … …. …. Ok all set? Great. Let’s get into it.
mDNS Deep Dive
Way more applications that you may realize use mDNS, but let’s focus on a phone casting to a target. I will also highlight some differences in consumer vs prosumer/enterprise gear in how this is handled.
As discussed, cast devices run services waiting for an opportunity to announce themselves on the network, Enterprise access points may store entries in a table or act as a “mDNS Gateway”, to forward between L3 segments. Like a bookie wanting collect a debt, the source announces it’s looking for them.
Once the end devices sees the cast targets, they can choose one. It then has a peer to peer connection via the access point, and then basically sends the URL to the resource, and the source becomes a fancy remote control.
Now, this is a wireless to wireless situation. The destination may be on the wired backhaul, but the actual mDNS announcements are coming from the wireless speaker so there’s no bridging between physical segments. Casting in these situations are usually pretty reliable and trouble free.
If the device sending mDNS is wired, then depending on the AP we can have an issue. Enterprise or even some Prosumer APs may not allow mDNS messages to be received from the wired side without additional configuration when mDNS forwarding is turned on . YMMV.
Such configurations may include adding the MAC of the sender to an approved list to allow the messages to forwarded on. I run Aruba wireless at my house, so I had some pretty frustrating moments trying to cast to my Nvidia Shield, Sony Receiver, etc until I learned how this works.
Spoiler: I had to create an Airgroup on Central to forward mDNS to other vlans then that blocked the wired mDNS. I then had to add the MAC addresses as servers on the config. SUCH…FUN! A buddy of mine told me about a VM he runs that does all this mDNS forwarding so I can turn all that off in the future, hopefully. Sounds like a future article. :)
Ok so that’s a mid-level explanation of the hows and whys. The con’s of this as I explained earlier:
device support can vary
applications can vary in quality of how they implement mDNS
unexpected hardware issues (see example above) can cause lots of frustration
audio quality can take a hit, relies on the end device to do most of the processing
can’t handle multiple streams
connecting to cast targets can be a PIFA.
Music Assistant fixes most, if not all of these issues.
Music Assistant Deep Dive
Ok, this is where things get exciting. With standard casting, as you’ve seen it’s a very "physical” process. MA takes this whole thing and abstracts it. It’s easier if I show you.
Basically, MA is the cast requester for all the services. And when you’re in the app, you’re streaming from the MA server itself, not casting traditionally. The server also manages signal processing, transcoding, etc. It’s pretty great. Look at these logs.
What’s great is that from the UI you can re-name the Players to so you know what they are. Very useful. I start a stream from my desktop at m.nscriven.net (proxy FTW) and we can see the stream. It plays via some new-ish browser API. It was just added in March, apparently. Lucky me!
If we take a look at the playback info:
It show that the server is transcoding down from FLAC to MP3, because that’s what the browser can support natively. And none of that process is happening on the end device it’s all on the server. You can really customize the player too.
Here we can see all the targets. I can make groups, I can cast different songs to different targets at the same time. It’s kind of amazing.
What about a dedicated device and not a web browser? Let’s see what happens when the target is a Nvidia Shield.
It plays as FLAC, but down samples to 48Khz/16bits. If we look at the advanced settings of the player, it’ll tell us what it supports.
If we look back at the browser player we see it doesn’t have an output codec. Let’s look at a Chromecast Speaker.
There’s some more features here, as well as the ability to turn off the MA registration and use traditional casting. Some devices may have issues that require this, thankfully I haven’t found one yet.
Let’s look at one final device, my Sony Receiver. I found something interesting. By default it wouldn’t play 24 bit, but I could change it! I scrolled further down and the receiver supported way more than what’s shown here.
Then on playback:
So as you can see there’s a LOT here.
And we can show the server is indeed transcoding. I started a playback and the CPU jumped up for a second and then settled down.
Ok, now that I’ve nerded out on the underlying tech, let’s move on.
Installation and Configuration
Ok, so this was really built for HA integration primarily. You can go to HA right now, find MA, and install it there. I didn’t go that route, because I didn’t want to be trapped in the HA app, very similar reasons why I separated out ZWave and Zigbee.
And just like those examples, someone made a proxy app for HA so I can get the best of both worlds. Let’s take a look at the Portainer Compose file I used. As always, there is an example at my GitHub.
You’ll notice that I’m using local bind mounts for my volumes. I have a NFS share mounted for media. The major deviation between this and my other docker containers is it’s the only one that uses the host networking mode. More details on why can be found here.
Now because we’re using the host mode you won’t see published ports on Portainer/Docker. The default web port is 8095, can be changed later per the documentation. TCP 8097 is used for the stream per the docs, but the app says its 8098.
When you go the site, you’ll notice there is no authentication mechanism right now, or separate user accounts . Be aware of that. Go to settings. I use the local filesystem option but look at all the other providers!
Next take a look at Player Providers. I use Chromecast, but look at the others! Wow.
Ok that’s about it for this, you really need to poke around and read the docs to really understand fully, but there’s not much more to cover here.
HA Proxy Install
Ok, so I installed outside of HA. I then send it through my Nginx server for a nice domain name experience. I can get to it really easy from my desktop, phone, etc. On On Android, I can even install as a PWA.
But If I want to access away from home (remember theres no auth) I can add a proxy into HA and access it from there via my Nabu Casa subscription (or any other reverse proxy you use for HA )
We need to add a repository into HA Add-ons.
Go to Settings>Add-On Store (lower right) and then on the upper right “Repositories”
Add this repo: https://github.com/Sarnog/Music-Assistant-Proxy
Once added, search for “Proxy”, and install. Once installed, enter config, enter IP and port of MA , start at boot, add to sidebar, profit.
That's it! This article took a long time to write, and my buddy Igor really helped me to clarify and better understand some misconceptions I had about how mDNS works. Confirmation bias can be a beast.
Until next time!