Monday, July 9, 2012

Streaming Internet radio from Raspberry Pi to Samsung TV

Not much related to programming, but I spent almost a week trying to make it work, so I decided to share my experience in hope someone may find it useful. You've probably heard already about Raspberry Pi - I think it's a great project and I admire thousands of people from all over the world who contribute to its development, but it's still in its experimental stage and has some quirks which sometimes make it very hard to use. One of them is using it as a DLNA server for streaming media straight from the Internet right to your home theater.

I have a Samsung TV with DLNA client on board, which works well with Serviio installed on my laptop, but I didn't feel like turning on a computer just to listen to my favourite online radio station, once I got my hands on Raspberry Pi. So I installed SqueezePlug on an SD card and started to play with it. Unfortunately, it turned out that none of the four pre-installed media servers could provide me with what I needed: MiniDLNA did not support transcoding of live streams (it needed some patches that were not applied), TVMobili was not recognized by my TV, Twonky said that its license expired before I even managed to configure it, and Logitech Media Server simply didn't work (it had some problems with the GUI - it called an AJAX script which always responded with an empty contents).

The only option I was left with was MediaTomb, which fortunately has a very good documentation and a lot of supportive users across different forums. The first thing I learned was that Samsung needs some special headers to be sent by MediaTomb, otherwise it always responds with "file format not recognized" message. Very informative from Samsung I must say - it took me a lot of time just to discover that it was the response header from DLNA server that was not recognized, not the data itself. Next step was to read the radio stream from the Internet and pass it to the TV. I spent next few evenings analyzing why I can stream media from a laptop and I can't from Raspberry Pi using ffmpeg for transcoding, despite using exactly the same version of MediaTomb and the same configuration file. As it turned out, my TV has issues with streams encoded by ffmpeg on ARM CPU (they differ substantially from those produced by ffmpeg on Intel), and it seems that ffmpeg compilation for ARM is simply broken. If you try ffmpeg on different distributions of Debian for Raspberry Pi you will learn that it either crashes with core dump or "illegal instruction" message or produces mpeg audio streams full of bad bytes, which can be corrected by PC players like vlc or foobar, but apparently not by a TV built-in software. So I tried other transcoders, like mencoder (provided by mplayer), until I realized that I need no transcoding at all - it's enough just to pass the original http stream to the output file just as it is, and wget is actually everything that I needed.

Still, one problem remained. After a few seconds of playing the connection between the TV and MediaTomb was breaking up. It was driving me nuts and it took another few hours to discover that MediaTomb sends "Connection: close" header to the client, while the online radio streams use "Connection: keep-alive", which prevents client from disconnecting when there is no data in the stream for a while. I found out that the solution is to use apropriate buffer size for the radio stream - it should be fairly small so that it never stops serving data even for a while and thus prevents the TV from disconnecting, but not too small because it will make the ARM to choke.

So, if you have a Samsung TV and want to listen to Internet radio stations served by a Raspberry Pi, here is my recommended configuration:

1. Install Debian Squeeze following the instructions on Raspberry Pi download page. Then log in to it and install MediaTomb server:
sudo apt-get update
sudo apt-get install mediatomb
2. Modifiy the /etc/mediatomb/config.xml file: Enable web user interface by modifying <ui> entry in the <server> section:
<ui enabled="yes" show-tooltips="yes">
  <accounts enabled="yes" session-timeout="300">
    <account user="mediatomb" password="mediatomb"/>
  </accounts>
</ui>
Add the following entry to the <server> section (as described here):
<custom-http-headers>
   <add header="transferMode.dlna.org: Streaming"/>
   <add header="contentFeatures.dlna.org: 
DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=017000 
00000000000000000000000000"/>
</custom-http-headers>
Change:
<transcoding enabled="no">
to:
<transcoding enabled="yes">
Just below it, in <mimetype-profile-mappings> section add the following entry:
<transcode mimetype="audio/x-shoutcast" using="streaming"/>
Also, add the following section to <profiles>:
<profile name="streaming" enabled="yes" type="external">
  <mimetype>audio/mpeg</mimetype>
  <accept-url>yes</accept-url>
  <first-resource>yes</first-resource>
  <use-chunked-encoding>no</use-chunked-encoding>
  <agent command="wget" arguments="%in -O %out"/>
  <buffer size="8192" chunk-size="1024" fill-size="2048"/>
</profile>
3. Restart MediaTomb server:
sudo service mediatomb restart
Now log in to MediaTomb interface using a web browser (it will be listening at port 49152, so assuming that your Raspberry Pi has IP address 192.168.1.10 you should go to http://192.168.1.10:49152/) with user and password defined in beforementioned <accounts> section (in my example it's mediatomb/mediatomb) and add a new stream:
Type: External Link (URL)
Title: Your radio station title
URL: Radio URL (for example http://nl2.ah.fm:9000)
Protocol: http-get
Class: object.item.audioItem
Mimetype: audio/x-shoutcast
Choose DLNA (MediaTomb) as input source on your TV and enjoy listening to your favourite radio.