Loading...
View related media

Media Player

m3u8-tweaks on GitHub

Project type

Personal

Platform

Raspberry Pi

Technology used

OS
Linux (Headless raspbian)
Software
MPD, VLC, Syncthing
Linux specific
alsa, cifs, dd, fstab, systemd
Custom VLC remote
Git, Nginx, TypeScript, React

Production date

Several days (2023)

Description


As a lot of people with technical interests, I find joy in automating things around the house. In this particular example, it's about my home media setup, which plays audio and video from my local library. The goal is to be able to control the player remotely, through my android phone.

I rarely stream music from cloud services, instead I enjoy my good old mp3 and flac files. That makes it a lot more challenging to listen to my collection from locations other than my desktop PC. Hence I came up with this solution, which is for the living room but is also relatively portable which allows me to deploy it elsewhere too.

Occasionally, I also want to view some media on the big TV in the living room, but the content is only on my PC in the other room. I've made sure I can access that content too, and stream it through VLC.


My responsibilities


Building a music pipeline

As mentioned, my music collection is simply a whole bunch of files. I manage these files with Winamp, where I create playlists for each album, or sometimes a collection of albums or random tracks.

The goals are fairly straightforward:

  • Have my music available in the living room
  • Control playback remotely, using my phone
  • Able to use the existing playlists that I already have in Winamp
  • Automatically synchronise whenever I add to my music collection / playlists

Let's start at the music files. These should be available locally on the Raspberry Pi, so I can listen to it without having to depend on a network or remote storage location. This is trivial thanks to Syncthing, which synchronizes files with another location, in this case my desktop PC.

Next, the playback. I've opted to use Music Player Daemon (MPD), as it best fits my requirements. I've configured it to output to both the RCA and 3.5mm outputs of the HifiBerry board (through ALSA).
MPD has an open protocol which allows various clients to be used. I personally use MPDroid, which is a bit outdated and has some quirks, but it works well enough. I can fully control playback and select anything from my library.

Finally, playlists. This was the biggest hurdle, as the playlists used in Winamp are not compatible with MPD. Without an easy way to convert them, I did what every software engineer would do in this situation: Write my own conversion program!
The conversion is relatively simple, from m3u8 to m3u. Here's how it works in a couple of steps:

  • Strip the first line from each playlist file and change the file extension to match the m3u format.
  • MPD uses the filename of the playlist as the title, opposed to Winamp which stores it in a separate XML file. As such, I'm also parsing the XML and applying the names to the playlist file names.
  • Change the root path of the listed audio files to match the folder structure on the Raspberry Pi.
  • Run the conversion script periodically and sync the result with Syncthing once more.

With that, I've accomplished my goals! I've hooked it up to my media receiver and am able to enjoy all the music from the comfort of my couch!

Viewing local network media

Accessing storage on the network is fairly trivial. Even when my target machine is running windows, using CIFS to mount the drive is pretty easy. I've also automated it through fstab.

However, being able to browse directories and playing back media using my phone as a remote is a bit more tricky. I've opted to use VLC as it's capable of processing a lot of different media formats. It's also capable of remote control through various means, including HTTP.

Unfortunately the HTTP interface is, in my humble opinion, quite awful. Especially on mobile it's nigh impossible to use.
Hence, I created my own interface! With a bit of network routing trickery using Nginx, it's possible to forward API calls from my own interface to the HTTP server of VLC, without CORS errors. There was barely any documentation on the VLC http API as it is technically only available for internal (within VLC) use. With the little documentation that was available and some reverse engineering, I managed to get the most important functions working!


Retrospective


While this system works alright, there are still some flaws. Here is a list of things I would like to improve in the future:

  • The Raspberry Pi is quite sensitive to corruption due to power loss. This makes a proper back-up solution necessary. I've done some manual backups using dd, but this is rather slow, both in terms of storing and recovering. And it is not automated, which could result in losing weeks or months of data depending on the frequency of the manual backup.
  • Requiring to power down the Pi before disconnecting it makes it less portable. It also has no internal battery. I would love to make it into an overkill mp3-player for enjoying my music while out and about!
  • The receiver does not turn on automatically when playback is occuring from the RPI. I am considering activation through HDMI CEC signals.
  • In some cases, viewing images through VLC causes them to appear with a blue tint, indicating some technical issue. I have been unable to find the cause so far.
    Loading images is also quite slow, which makes me consider moving away from VLC, at least for images.
  • The http API of VLC is a bit slow and janky at times. I've made an attempt to use the rc interface instead, using Rust to create a lightweight http server which forwards commands to the rc interface. However, that was practically my first attempt at writing something in Rust, which turned out to be a bit too complex for the effort I wanted to put into it at that time.