Hi there,
My team and I are working on a VLC based Raspberry Pi 4 media player. We’re using the VLC bindings for Python module to download/cache a playlist of 1080p movies, and post playback stats to a RabbitMQ message broker.
We were hoping to use Balena to deploy hundreds of these players but have run into a problem.
Running our software on a Raspbian Buster with desktop image, it’s able to run solidly for a week+.
Running the identical software on a BalenaOS balenalib/raspberrypi3:buster
image works for n
loops of the playlist before hanging at the end of a video, and providing no error logs, warnings, or anything that we’ve been able to find.
It fails in this state:
- The Balena Cloud
Reboot/Restart
buttons don’t reboot or restart the device. - We can still interact with the Host and app container running the media player via the Balena Cloud Terminal, but
free -m
reports 288MB free ram, andtop
shows nothing using any significant CPU. - Attempting to
restart
the media player container via the Balena CloudServices
tab results in the Host reporting it has Killed the service but it doesn’t ever restart it. - Checking the supervisor logs
journalctl -f -a -u resin-supervisor
shows that it thinks it’s healthy.
Questions
- Have you noticed this behaviour before in other Balena apps?
- Do you have any ideas how we can debug this?
- Could you check over our Dockerfile.template & start script for obvious errors?
- Are there any Device Variables we should be setting differently?
If anyone has any ideas how to debug further that’d be great!
Cheers, Simon.
Technical details
Device Configuration
- Define device GPU memory in megabytes: 512
- Define DT parameters: “i2c_arm=on”,“spi=on”,“audio=on”
- RESIN_HOST_CONFIG_arm_64bit: 1
- RESIN_HOST_CONFIG_avoid_warnings: 1
- RESIN_HOST_CONFIG_dtoverlay: “vc4-fkms-v3d”
Dockerfile.template
# Force Raspberry Pi 3 for 32-bit X
FROM balenalib/raspberrypi3:buster
# Use `install_packages` for dependencies
RUN install_packages vlc vlc-plugin-* g++ python3-pip python3-setuptools python3-dev build-essential \
xserver-xorg-core \
xinit lxsession desktop-file-utils \
raspberrypi-ui-mods rpd-icons \
gtk2-engines-clearlookspix \
matchbox-keyboard \
# For system volume
libasound2-dev \
# Audio
alsa-utils \
# Remove cursor
unclutter
# disable lxpolkit popup warning
RUN mv /usr/bin/lxpolkit /usr/bin/lxpolkit.bak
# Set wallpaper
COPY /conf/desktop-items-0.conf /root/.config/pcmanfm/LXDE-pi/
# Autohide desktop panel
COPY /conf/panel /root/.config/lxpanel/LXDE-pi/panels/
# Hide desktop panel completely
COPY /conf/autostart /etc/xdg/lxsession/LXDE-pi/
COPY /conf/autostart /root/.config/lxsession/LXDE-pi/
# Disable screen from turning it off
RUN echo "#!/bin/bash" > /etc/X11/xinit/xserverrc \
&& echo "" >> /etc/X11/xinit/xserverrc \
&& echo 'exec /usr/bin/X -s 0 dpms -nolisten tcp "$@"' >> /etc/X11/xinit/xserverrc
# Enable udevd so that plugged dynamic hardware devices show up in our container.
ENV UDEV 1
# Install Python modules
COPY ./requirements/base.txt /code/requirements/base.txt
COPY ./requirements/prod.txt /code/requirements/prod.txt
RUN pip3 install -Ur /code/requirements/prod.txt
COPY . /code/
WORKDIR /code/
# pi.sh will run when the container starts up on the device
CMD ["bash","scripts/pi.sh"]
Start-up script
#!/bin/bash
# Allow VLC to run as root
sed -i 's/geteuid/getppid/' /usr/bin/vlc
# Remove the X server lock file so ours starts cleanly
rm /tmp/.X0-lock &>/dev/null || true
# Set the display to use
export DISPLAY=:0
# Set the DBUS address for sending around system messages
export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
# Start the desktop manager
echo "STARTING X"
startx -- -nocursor &
# TODO: work out how to detect X has started
sleep 5
# Hide the cursor
unclutter -display :0 -idle 0.1 &
# Start the VLC media player
python3 media_player.py
Media Player
The core Python code looks something like this:
import vlc
class MediaPlayer():
"""
A media player that communicates with our CMS to download resources
and update the message broker with its playback status.
"""
def __init__(self):
self.playlist = []
self.vlc = {
'instance': None,
'player': None,
'list_player': None,
'playlist': None,
}
self.init_vlc()
def init_vlc(self):
"""
Initialise the VLC variables:
- The VLC instance
- A MediaListPlayer for playing playlists
- A MediaPlayer for controlling playback
- A MediaList to load in the MediaListPlayer
Documentation for these can be found here:
http://www.olivieraubert.net/vlc/python-ctypes/doc/
"""
flags = ['--quiet']
self.vlc['instance'] = vlc.Instance(flags)
self.vlc['list_player'] = self.vlc['instance'].media_list_player_new()
self.vlc['player'] = self.vlc['list_player'].get_media_player()
self.vlc['playlist'] = self.vlc['instance'].media_list_new()
self.vlc['player'].set_fullscreen(True)
self.vlc['list_player'].set_playback_mode(vlc.PlaybackMode.loop)
if __name__ == "__main__":
media_player = MediaPlayer()
media_player.download_playlist() # Adds media to self.vlc['list_player']
media_player.vlc['list_player'].play()