Finally after a very long time I’ve found the motivation to write a new post, yay :)

I’ve gotten myself a RPI a long long time ago with the primary target to use it as a htpc (Desktops are powerful, but also loud and power-hungry. ARM-SoC are the exact opposite.)
I’ve since used various setups to get the maximum performance out of this very nifty but also really slow device.
If you are a beginner and not that experienced with linux in general, I’d recommend Sam Nazarkos Raspbmc. Sam frequently adds new tweaks to the distribution and keeps the xbmc-builds updated.

But being an ArchLinux fanatic I just couldn’t stand the fact that there is an ArchLinuxARM version available for the Raspberry and not having it installed on mine. Also installing Arch-ARM is even easier than installing the ‘normal’ Arch (you just dd the image to the sd-card).
But after you have installed ‘xbmc-rbp’ from the repos you end up with a system that is too slow to play DTS tracks, doesn’t start xbmc at boot, has a slower GUI than raspbmc and boots slower than raspbmc. (You can fix the DTS-problem easily by using an AV-Receiver which does the DTS-Decoding, but as I don’t have such a device at the moment I had to find another solution…)
This is why im going to point out the modifications I’ve done to my system to make it as awesome as a £27.84 arm-development board could possibly be :)

Warning: I’ve noticed, that this post is still relevant and gets read a lot. I do not use my raspberry for xbmc anymore as I have bought a wandboard quad now (see some of the other posts here). Nearly all of the advice given in this blog is still totally valid, but the parts where I mention very specific build dates or versions should be taken with a grain of salt. Just keep in mind that most likely a lot has changed during those few months and the specific problems with specific versions I mention are most likely gone.

Changelog
20130707 – initial post.
20130816 – some updates, including kernel 3.10 and xbmc-rpb-git.
20130912 – updates to the f2fs section.
20130913 – updated python script.
20131020 – update f2fs section, added custom xbmc build
20131023 – added information about sqlite (see xbmc-rbp-git section)
20140125 – added notes about the wandboard

Overclocking
The most powerful and easiest solution to boost the performance is to run the device at a higher speed. In this post it even souns like if the Raspberry Foundation kinda ‘recommends’ overclocking. As far as I know (im not responsible for your raspberry overheating and creating another human-like species which threatens the life on our planet) there are no down-sides to the stability (provided you have a strong powersupply) or the lifetime of the device. (Using the this settings doesn’t even void your warranty).

All you have to do is include this snippet in your /boot/config.txt

arm_freq=1000
core_freq=500
sdram_freq=500
over_voltage=6
force_turbo=0

Using a modified version of memory-functions like memcpy
This is a library that implements standard system-functions like memcpy/memset/strcpy (and even more) in hand-written assembler. This improves general performance and made a noteable difference in playback-performance (at least for me).
On Arch-ARM all you have to do is install ‘arm-mem-git’ and reboot.

Using kernel 3.9 3.10 3.11 and f2fs
While f2fs does not affect playback performance (at least not over the network) it does greatly improve the booting-speed and speeds up the general performance.
F2FS (Flash-friendly FileSystem) is a file-system designed by Samsung specifically for flash-drives and is said to improve filesystem performance and is available since kernel 3.8.
To use it on the raspberry you have to install ‘linux-raspberrypi-latest’ and ‘linux-headers-raspberrypi-latest’ on the pi and then recreate your filesystems.
I did that on my archlinux-desktop (you have to use a recent kernel and have to have mkfs.f2fs, packaged in ‘f2fs-tools’). Just cp -a the contents of the /-partition to a temporary directory (don’t use a ntfs-volume, you’ll have to preserve the file-attributes), mkfs.f2fs on the partition and then cp -a it on the new filesystem. The only thing thats left to do now is to change the parameter “ext4” in /boot/cmdline.txt to “f2fs” and its going to boot off your new shiny f2fs.
Do not downgrade your kernel afterwards as 3.6 does not include f2fs.

Update: This does also work fine with kernel 3.10. Sadly, the current (3.10.6-1, 20130816) kernel does not boot on the raspi so you have to stick to 3.10.4-2 posted here. 3.10 does bring some changes to f2fs, the newest version of 3.10 (3.10.6) should also improve overall performance. Sadly its not available yet but it sure will be soon :)

Update: I’ve had some recent issues with f2fs like total corruption to the filesystem after a hardreset (On 3.10). I’ve therefore switched back to ext4 (you do notice the slower performance) :/ I have yet to try f2fs with 3.11.

Update: Back to f2fs, it just feels much faster, especially while navigating with the shell. I’ve not found anything to counter corruption, it seems to happen more or less randomly. I just keep a backup of my root-filesystem and write it over as soon as it corrupts :/

Use kernel-space nfs implementation with big buffers
While xbmc does have the feature to use nfs shares, I’d recommend using mount.nfs4 for maximum performance. I’ve played around with the values a bit and this is what my /etc/fstab looks like:

192.168.178.200:/media /media/ nfs4 ro,auto,rsize=32768,udp,noatime 0 0

Using udp and rsize=32768 gave me the best results in raw transfer performance.
Also, nfs uses less CPU than smb which frees capacity for those DTS-tracks

Update: I switched back to tcp because udp seems to be a bit unreliable, even over very reliable networks (around 2m cable distance, 1 gigabit switch). TCP is still fast enough to play the samples I am using.

Use xbmc-rbp-git
The newest versions of xbmc (Gotham alpha 7 or later builds or builds off the master-branch) include a sick patch for the dts decoder. According to the pull request, some guys hand-optimized the dts-decoder for the SoC of the raspberry in assembly which in turn runs 36% faster now! On archlinux, all you have to do is ‘pacman -S xbmc-rbp-git’.

According to the database table versions 12.2 and 13 alpha7 do not share the same database version, this means you should backup your .xbmc-folder in case you want to switch back later.

Update: xbmc-rbp-git has not been updated for a very long time (nearly 2 months now). I’ve chosen to compile my own build and got it working by modifyin the PKGBUILD slighty (issue is filed, info supplied to upstream) and im going to provide the binaries here!
You’ll have to download the tar file, extract it and install all the packages I’ve provided. The new firmware is necessary because xbmc relies on a never version of the userspace-libraries than the one which is provided in the repos (surely they will update it some time in the future…).
As always: back up your .xbmc folder :)

Update: I’ve submitted my changes to archlinux PKBUILDs master. The firmware was updated and xbmc should also be built but I’ve not been able to verify it yet).

Download:
20131019 – (xbmc at 37a0959099ce6589221c10097f2161558d1710ce, firmware at 4c1456944b5f6cc9e5141077ed4e158398811fc1)

Altough this build technically works, I’ve experienced some issues such as tv shows not being listed etc. I’ll provide a new build as soon as this changes :)
Update: According to the archlinux bug-tracker and the xbmc bug-tracke the problem with the library seems to be related the the 3.8.1 version of sqlite. As a workaround, you can downgrade to the previous version of sqlite until this is fixed.

Autostart xbmc and change governors on the fly
Most likely, you’ll want to have xbmc autostarted at boot, so you have to create a service for starting it.
I’ve added a small wrapper script and some python code to change the cpu-governor to RoundRobin and change the io scheduler of the xbmc performance as soon as you hit play.
The Round-Robin governor does improve the performance on playback, but decreases it if you want to browse through the library, thats why I decided to switch it on demand.
The python-script does only wirk with python 2.7, not with python 3 or higher.

The bash start-script does also stop policy-kit which uses a few megabytes of ram if its running. You can stop it and reboot/shutdown in xbmc still works, I’ve yet to discover any downsides.

I don’t know if changing the governor to performance is equivalent to force_turbo=1 which (if you used my overclocking settings) does void your warranty!. If you don’t want the script to change the governor, remove the appropriate lines in the python-file.

Im running xbmc as user ‘xbmc’ in ‘/home/xbmc’

#Start my pthon-script with 15 sec delay
(sleep 15 && python2.7 /home/xbmc/watchdog.py) &
#stop policy-kit after 15 sec
(sleep 15 && systemctl stop polkit) &
#Chmod the tty0 input, fixes problems with keyboard input in xbmc
chmod 0777 /dev/tty0
#start xbmc as user xbmc
su - xbmc -c "LD_LIBRARY_PATH=/opt/vc/lib/ xbmc-standalone"
#!/usr/bin/python2

# this script is supposed to connect to xbmc on port 9090 and wait for incoming event-notifications
# on the onPlay-Event it changes the xbmc-process-priority to 10 and its scheduler to SHED_RR. It also changes the cpu-governor to performance.
# on the onStop-Event it reverts the process-priority to its default state and changes the cpu-governor to ondemand.
#
# coded by CruX (crux@project-insanity.org)

import socket
import time
import subprocess

def countOpen (input):
        return input.count('{')

def countClose (input):
        return input.count('}')

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
elapsed = 0

def loop():
        pid = int(subprocess.check_output(["/bin/pidof", "xbmc.bin"]))
        print pid
        while True:
                opening = 0
                closing = 0
                s.setblocking(1)
                data = ""
                tmp = s.recv(8)
                if (tmp == ""):
                        break
                opening += countOpen(tmp)
                closing += countClose(tmp)
                data += tmp

                s.setblocking(0)
                while True:
                        tmp = bytearray(128)
                        count = s.recv_into(tmp, 128)
                        opening += countOpen(tmp[:count])
                        closing += countClose(tmp[:count])
                        data += tmp[:count].decode("utf-8")

                        if opening == closing:
                                break

                        time.sleep(.1)

                if (data.count("\"type\":\"movie\"") > 0 or data.count("\"type\":\"episode\"") > 0):
                        if (data.count("\"method\":\"Player.OnPlay\"") > 0):
                                subprocess.check_output(["/usr/bin/chrt", "-a", "-p", "10", str(pid)])
                                gov = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "w")
                                gov.write("performance")
                                gov.close()
                        elif (data.count("\"method\":\"Player.OnStop\"") > 0):
                                subprocess.check_output(["/usr/bin/chrt", "-a", "-o", "-p", "0", str(pid)])
                                gov = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor", "w")
                                gov.write("ondemand")
                                gov.close()
        s.close()

while elapsed < 120:
        try:
                s.connect(("127.0.0.1", 9090))
                loop()
                elapsed = 0
        except socket.error:
                time.sleep(1)
                elapsed += 1
[Unit]
Description = Starts instance of XBMC
After = remote-fs.target

[Service]
Type = simple
ExecStart = /bin/bash /home/xbmc/xbmc.sh
Restart = always

[Install]
WantedBy = multi-user.target

While you should definitely not start to learn python off this code, it does work as expected and I have no intents to refactor it :)

Update: I’ve been having some weird issues with xbmc-git lately where it does only listen on ipv6, not on ipv4 which means 127.0.0.1 doesn’t work there. You can see if xbmc sends its event via the socket by telnetting to 127.0.0.1:9090. If you get permission denied or similar, try ::1:9090. If it works change the address in the script aswell as the socket to AF_INET6.

Update: I’ve updated the script and fixed an issue where the script did not change governors if you playback a ‘episode’ rather than a ‘movie’. Earlier xbmc versions did not make a difference here.
Thinking about rewriting the whole thing in go, not sure what the benefits would be…

These modifications greatly increase booting speed, render the xbmc-ui useable and improve the playback-performance in xbmc. I’ve some mkv Files which are about 20g in size and have 3 different DTS-tracks at around 1500kbit/s and they play smoothly (played over nfs of course :))

I can’t remember any more modifications I applied, but if I find something else I’ll keep this post updated!
If you know of any other things I can do to improve performance, I’d be more than happy to read about them in the comments :)

💬 Are you interested in our work or have some questions? Join us in our public Signal chat pi crew 👋
🪙 If you like our work or want to supprot us, you can donate MobileCoins to our address.