Chromecast is one of those devices I just generally use a lot. They are small practical and enables me to stream video or music to my TV from multiple devices. But it also requires you to have a supported browser or video player. This is obviously a bit boring.
There has been multiple command line chromecast streamers through the years. But their ffmpeg usage has been shoddy at best with no hardware decoding support and usually quite bad implementations.
Instead of using these tools I have wound up with two bash scripts that pipes together a command line program to stream towards chromecasts and it works surprisingly well.
The first part of this is how we stream towards the chromecast. This is easily
solveable with vlc
luckily. You can just open vlc file://....
, select
Playback -> Renderer
and generally find the correct chromecast there. But this
is boring. We have to manually open the GUI and select the device!
I can peak into the app on my phone and learn that the local IP of my chromecast
is 192.168.1.231
. With clvc
you can also pass sout
and
sout-chromecast-ip
to auto-start a headless VLC players that will connect
towards your chromecast.
IP="192.168.1.231"
cvlc --sout "#chromecast" --sout-chromecast-ip="$IP" "file://...."
This works really well on it’s own. You can also pipe yt-dlp
into clvc
and
support most media websites like youtube or twitch. This allows you to quickly
write up some scripts to stream from these sites and is fairly flexible.
IP="192.168.1.231"
yt-dlp "https://twitch.tv/sovietwomble" -o - | \
cvlc --sout "#chromecast" --sout-chromecast-ip="$IP" -
There is one issue though, that IP is not static. If the chromecast reconnects to the network it might change it’s IP, and I’m never going to bother setting a static device.
Luckily chromecast announces itself over DNS Service Discovery
which allows devices to announce themself over the network through DNS names. To
query for these devices we will be using systemd-resolved
and resolvectl
.
You can probably use another DNS query tool, but systemd-resolved
also
supports mDNS
which is useful for finding services on the network.
First you need to enable systemd-resolved
through systemctl enable --now systemd-resolved
. Your mileage might wary but in some distributions this comes
enabled by default. It’s also important to note that you don’t need to make
resolved
your default stub resolver for any of this to work.
If you do read the resolvctl(1)
man page
you will see it has native support for dns-sd through resolvectl service
.
$ resolvectl service _googlecast._tcp local
However I have not gotten this to work as it seems like it will try to send a
combined DNS query to your network DNS resolver, and this doesn’t seem supported
depending on the router you have. So we have to implement this ourself. So we
will be using the PTR+SRV+TXT
process as outlined in RFC6763.
To find available chromecasts on the network we’ll first query for PTR records
on _googlecast._tcp.local
.
$ resolvectl --type=PTR query _googlecast._tcp.local
_googlecast._tcp.local IN PTR Chromecast-Ultra-13ccb56dd0ea6ce65dbe15b39c573856._googlecast._tcp.local
Here I see one Chromecast. Then we need to query for the service record it self, SRV is defined as part of RFC2782.
$ resolvectl --type=SRV query Chromecast-Ultra-13ccb56dd0ea6ce65dbe15b39c573856._googlecast._tcp.local
Chromecast-Ultra-13ccb56dd0ea6ce65dbe15b39c573856._googlecast._tcp.local IN SRV 0 0 8009 13ccb56d-d0ea-6ce6-5dbe-15b39c573856.local
And finally we can just query the TXT record of the service for the IP of the device itself, which is the correct IP we started out with :)!
$ resolvectl query 13ccb56d-d0ea-6ce6-5dbe-15b39c573856.local
192.168.1.231
To make this easier in the future we can easily parse this space limited output
with read
in a bash script1.
$ ~/.local/bin/dns-sd
#!/usr/bin/bash
set -e
read -r _ _ _ ptr _ < <(resolvectl --type=PTR query "$1")
read -r _ _ _ _ _ _ srv _ < <(resolvectl --type=SRV query "$ptr")
read -r _ ip _ < <(resolvectl query "$srv")
printf "$ip"
And we can make our chromecast
script as well which calls dns-sd
.
$ ~/.local/bin/chromecast
#!/usr/bin/bash
IP="$(dns-sd _googlecast._tcp.local)"
echo "Using $IP"
yt-dlp "$1" -o - | cvlc --sout "#chromecast" --sout-chromecast-ip="$IP" -
With this we have a neat way to stream web content to our chromecast without having to go through a GUI.
Bonus content: qutebrowser
If you, like me, enjoy qutebrowser but find the lack of chromecast support annoying we can resolve that with the script above.
We’ll use the current chromecast script from qutebrowser:
https://github.com/qutebrowser/qutebrowser/blob/main/misc/userscripts/cast
And apply the diff here, which just removes code we don’t really need.
diff --git a/misc/userscripts/cast b/misc/userscripts/cast
index ec703d5fb..57c8f95f9 100755
--- a/misc/userscripts/cast
+++ b/misc/userscripts/cast
@@ -139,37 +139,6 @@ printjs() {
}
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
-tmpdir=$(mktemp -d)
-file_to_cast=${tmpdir}/qutecast
-cast_program=$(command -v castnow)
-
-# pick a ytdl program
-for p in "$QUTE_CAST_YTDL_PROGRAM" yt-dlp youtube-dl; do
- ytdl_program=$(command -v -- "$p")
- [ "$ytdl_program" == "" ] || break
-done
-
-if [[ "${cast_program}" == "" ]]; then
- msg error "castnow can't be found"
- exit 1
-fi
-if [[ "${ytdl_program}" == "" ]]; then
- msg error "youtube-dl or a drop-in replacement can't be found in PATH, and no installed program " \
- "specified in QUTE_CAST_YTDL_PROGRAM (currently \"$QUTE_CAST_YTDL_PROGRAM\")"
- exit 1
-fi
-
-# kill any running instance of castnow
-pkill -f -- "${cast_program}"
-
-# start youtube download in stream mode (-o -) into temporary file
-"${ytdl_program}" -qo - "$1" > "${file_to_cast}" &
-ytdl_pid=$!
-
+pkill -f -- chromecast
msg info "Casting $1" >> "$QUTE_FIFO"
-# start castnow in stream mode to cast on ChromeCast
-tail -F "${file_to_cast}" | ${cast_program} -
-
-# cleanup remaining background process and file on disk
-kill ${ytdl_pid}
-rm -rf "${tmpdir}"
+chromecast "$1"
Then add a keybind into config.py
.
config.bind(',c', 'spawn --userscript cast')
Ta-da, chromecast support with our script.
-
I might have written all of this to show off the script. I just really like it. #NoShame ↩︎