IPTV Adventures
by arjunI really enjoy the game show Jeopardy1.
When I was still at home in Pennsylvania, my family and I would usually watch it via the ABC affiliate in Philadelphia, using our cable subscription. My family doesn’t have a cable subscription anymore, and has switched fully to using digital over-the-air TV antennas to watch broadcast television, along with streaming subscriptions like Netflix, Hulu, etc.
For the most part, this digital over-the-air TV setup was limited to just the TVs within the house, but I run a small server at home (really an old desktop PC with Nextcloud on it), and I thought about using it to watch TV instead. I picked up a TV tuner stick and installed TVHeadEnd, which is a software package that can take decoded over-the-air TV signals from the tuner stick and transform them into stable M3U8 media streams. These streams then can be fed into media server software like Jellyfin, for a UX that approximates a cable box’s TV guide feature. Once the antenna cable was plugged into the tuner, I could reliably watch local news over the web (with the help of a Wireguard-based VPN2), both when I was at home and when I was on campus at UMD.
One small problem with this setup: my family lives in a valley. This is fine for local stations that broadcast within the valley (e.g. WFMZ for local news, WLVT for PBS), but we can’t reliably receive signals from Philadelphia, which is the nearest city with a “Big 3” (ABC, NBC, or CBS) network affiliate that would air Jeopardy. We’ve tried to address this problem by retuning each of the TVs, shifting the antenna position in the attic, and adding an amplifier to try and boost the signal, but we haven’t had much luck with improving reception.
The lack of reliable “Big 3” network reception wasn’t much of an issue for me at UMD, since I could easily watch shows like Jeopardy via the cable TV subscription university housing provided on-campus students. Once I moved out to Santa Cruz though, I didn’t have a cable subscription, and I couldn’t rely on the home setup for watching Jeopardy.
For my new setup, I picked up another TV tuner stick from Amazon, along with an antenna and amplifier combination from Microcenter3. I installed TVHeadEnd on an old Raspberry Pi I had laying around, and with some more configuration-wrangling, I could finally watch Jeopardy via the local NBC affiliate.
Unfortunately, Jellyfin seems to have some issues running on recent version of Raspbian, and so I couldn’t get the neat TV guide style integration that I had with my PA-based TV setup. TVHeadEnd itself provides M3U8 streaming manifests for both live and recorded shows, and these could be used with a media player like mpv or VLC to watch the stream. This has pretty bad UX though, especially on mobile, for a couple of reasons:
- Stream manifests, by default, are downloadable M3U8 text files. You can open this up in a media player and start watching the stream, but this introduces more friction since I now need to fetch the streaming file and pull it into my phone’s media player app.
- TVHeadEnd generally requires authentication. There are different ways to implement this (I think OAuth authentication is an option), but for simplicity’s sake I opted for old-school, HTTP plaintext authentication when I was setting up TVHeadEnd. Authentication information like this can be embedded into the URL listed in the M3U8 stream manifest, but it’s clunky and requires manual intervention to prepend the username/password combination:
http://[user]@[pass]:raspberrypi.lan/<stream URL>
- TVHeadEnd seems to implement some sort of stream caching behavior that makes it basically impossible to seek more than two minutes forward/backward in a recording without causing the media player software to crash. This might also be due to mpv just not handling stalled media streams correctly, but I’m not entirely sure. Either way, it requires me to reopen the stream manifest and navigate through my phone’s file manager again, or manually copy/paste + type the URL into mpv.
My solution to these problems was to write a very small Android app that uses TVHeadEnd’s JSON API and lists the recordings present, along with Intent-laden buttons that automatically open the stream in mpv. It’s not perfect - for one, a more robust solution would be to use something other than basic HTTP authentication, and it still doesn’t solve the seeking issue (that probably involves playing around with TVHeadEnd’s configuration). It also still relies on mpv (it doesn’t ship with its own M3U8/streaming client), but it works well enough on my home network, and has made listening to or watching Jeopardy while I cook dinner a part of my evening routine.
I still haven’t exactly figured out how to relay the streams or episodes back to my family in PA. Theoretically this app and mpv should work for mobile and desktop/laptop-based viewing, but it’s nowhere near as smooth UX-wise as using Jellyfin on the TV. One idea I had was to routinely sync recordings between here in Santa Cruz with the server in PA, (thus resulting in a distributed setup that would be even more over-engineered than what I’m currently running), but if my family wanted to watch anything live this wouldn’t be an option due to the three hour time difference.
If you want to try out my app (TVManifest), you can find it on GitHub here. As I mentioned above, it is not very robust, and shouldn’t really be used outside of home networks or (private) VPNs.
One final note: if the US could have something like the CBC’s nationwide public web streams, that would be great4. PBS kind of has this, but even a lot of its content is geolocked and based on what your local station broadcasts.
A rant: the term “VPN” seems to be used very misleadingly these days. The Wireguard VPN that I’m mentioning here is to provide a secure tunnel to devices on my home networks without opening a public-facing port that anyone could theoretically use.
Unless you have reason to suspect that your service provider is watching and cares about which addresses you connect to, public VPN services don’t really do much. See Tom Scott’s video on VPN marketing.
For a suspiciously cheap $1. I guess no one else bothers with over the air TV these days.
Ici Radio-Canada, the CBC’s French language service, doesn’t even require an account to watch its news stream, but its livestream is embedded into the front page and so I couldn’t link it here directly.