Banana Pi as UPnP music player

Last week I read a blog post by Stefan Kraus: Raspberry Pi als DLNA Renderer / Client mit Fernsteuerung which describes how to setup a Raspberry Pi mini computer as UPnP media renderer that can be controlled from any UPnP control point. ​

It uses gmrender-resurrect, a continuation of the gmediarender code that was published many years ago. It runs as daemon on a Linux machine and implements all the necessities to make it a usable part of the UPnP infrastructure. ​

Banana Pi​

I have a Banana Pi which basically is a copy of the Raspberry with a bit better hardware specs - and which I didn't know what to use it for. On that hardware I run Bananian, a pre-configured image of Debian 8 made for the Banana Pi. ​

I followed the installation steps on Stefan Kraus' blog post, and at the end I remembered that gstreamer 0.10 is old, and version 1.0 is already available. So I uninstalled the gstreamer0.10 packages and installed the gstreamer1.0 ones. ​

ALSA​

I decided that ALSA as sound system would suffice, and started gmediarender accordingly: ​

$ gmediarender -f Stereoanlage -u ... --gstout-audiosink=alsasink --gstout-audiodevice=sysdefault

Playing sound did not work; the log (--logfile=/dev/stdout) told this: ​

INFO  [date | gstreamer] Setting audio sink to alsasink; device=sysdefault
ERROR [date | gstreamer] Couldn't create sink 'alsasink'
INFO  [date | gstreamer] Set mute to off
INFO  [date | gstreamer] Set volume fraction to 0.316228​

This error comes from the following code: ​

sink = gst_element_factory_make (audio_sink, "sink");
if (sink == NULL) {
    Log_error("gstreamer", "Couldn't create sink '%s'", audio_sink);

Search engines did not really help here, but then I had an idea and looked for the gstreamer packages I had installed. In my uninstall and reinstall orgy I forgot gstreamer1.0-alsa. After installing that package I got sound. ​

ogg/vorbis​

Most of my music library is encoded as ogg/vorbis files, and that is the source of the next trouble: Playing .mp3 files works fine, but playing .ogg files results in an error: ​

** (gmediarender:2418): WARNING **: conv: size 121 is not a multiple of unit size 4
ERROR [date | gstreamer] source: Error: Internal data flow error.
 (Debug: gstbasesrc.c(2933): gst_base_src_loop ():
 /GstPlayBin:play/GstURIDecodeBin:uridecodebin1/GstSoupHTTPSrc:source:​

It turns out that my MediaTomb UPnP server gives out non-mp3 files as audio/L16. (I don't know yet why; my suspicion is that gupnp-av-cp does not send MIME types supported by the selected renderer along with the requests, so that MediTomb falls back to the basics) See a helping hand. ​

Another gmediarender user had the same problem, and this leads deep down to a bug in gstreamer on ARM machines.. ​

So I'm stuck here for now until the gstreamer developers fix that bug. Or I transcode all my .ogg files to .mp3, which I won't do. ​

A helping hand​

Two weeks later I joined the #gstreamer IRC channel and asked whom I should give money to get the bug fixed. Tim-Philipp Müller encouraged me to add a nice "me, too" comment to the bug report since most of the gstreamer devs work by mail. ​

One of the people who noticed that was Jens Georg of the Rygel UPnP project. He wrote me that gupnp-av-cp tries to match the offered streams of the media server and the supported stream types of the media renderer as good as possible, and that this probably failed - thus gmediarender would get the transcoded audio/L16 data instead of the original .ogg file. ​

Collecting information​

At first I used gupnp-universal-cp to obtain the list of formats supported by the media renderer: Open the renderer, open urn:upnp-org:serviceId:urn:schemas-upnp-org:service:ConnectionManager then double-click GetProtocolInfo and run the command. In out-arguments, the Sink field contained the following list: ​

http-get:*:audio/x-private1-lpcm:*,http-get:*:raw/x-pcap:*,http-get:*:application/x-3gp:*,http-get:*:audio/mp4:*,http-get:*:audio/m4a:*,http-get:*:audio/x-m4a:*,http-get:*:video/mj2:*,http-get:*:video/quicktime:*,http-get:*:video/x-qt-part:*,http-get:*:video/x-mp4-part:*,http-get:*:application/vnd.ms-sstr+xml:*,http-get:*:audio/x-smpte-302m:*,http-get:*:audio/alac:*,http-get:*:audio/x-alac:*,http-get:*:audio/x-ffmpeg-parsed-ape:*,http-get:*:audio/x-vnd.sony.atrac1:*,http-get:*:audio/x-imc:*,http-get:*:audio/x-mlp:*,http-get:*:audio/x-gst-av-mp3adu:*,http-get:*:audio/x-gst-av-mp3on4:*,http-get:*:audio/x-ffmpeg-parsed-musepack:*,http-get:*:audio/qcelp:*,http-get:*:audio/x-qdm2:*,http-get:*:audio/x-shorten:*,http-get:*:audio/x-sipro:*,http-get:*:audio/x-true-hd:*,http-get:*:audio/x-truespeech:*,http-get:*:audio/x-twin-vq:*,http-get:*:audio/x-gst-av-vmdaudio:*,http-get:*:audio/x-wms:*,http-get:*:audio/x-gst-av-ws_snd1:*,http-get:*:audio/x-dpcm:*,http-get:*:video/x-aasc:*,http-get:*:video/x-apple-intermediate-codec:*,http-get:*:video/x-amv:*,http-get:*:video/x-asus:*,http-get:*:video/x-gst-av-avs:*,http-get:*:video/x-gst-av-cavs:*,http-get:*:video/x-cinepak:*,http-get:*:video/x-cirrus-logic-accupak:*,http-get:*:video/x-camstudio:*,http-get:*:video/x-compressed-yuv:*,http-get:*:video/x-dnxhd:*,http-get:*:video/x-gst-av-8bps:*,http-get:*:video/x-ffv:*,http-get:*:video/x-gst-av-ffvhuff:*,http-get:*:video/x-gst-av-flic:*,http-get:*:video/x-fraps:*,http-get:*:video/x-h261:*,http-get:*:video/x-intel-h263:*,http-get:*:video/x-gst-av-idcinvideo:*,http-get:*:video/x-indeo:*,http-get:*:video/x-gst-av-interplayvideo:*,http-get:*:video/x-kmvc:*,http-get:*:video/x-lagarith:*,http-get:*:video/x-loco:*,http-get:*:video/x-gst-av-mdec:*,http-get:*:video/x-mjpeg-b:*,http-get:*:video/x-gst-av-mmvideo:*,http-get:*:video/x-msvideocodec:*,http-get:*:video/x-mszh:*,http-get:*:video/x-nuv:*,http-get:*:image/pbm:*,http-get:*:video/x-gst-av-pgmyuv:*,http-get:*:image/ppm:*,http-get:*:video/x-prores:*,http-get:*:video/x-qdrw:*,http-get:*:video/x-gst-av-qpeg:*,http-get:*:video/x-rle:*,http-get:*:video/x-gst-av-roqvideo:*,http-get:*:video/x-apple-video:*,http-get:*:image/x-sgi:*,http-get:*:video/x-smc:*,http-get:*:video/sp5x:*,http-get:*:video/x-truemotion:*,http-get:*:video/x-camtasia:*,http-get:*:video/x-tscc:*,http-get:*:video/x-ultimotion:*,http-get:*:video/x-ati-vcr:*,http-get:*:video/x-gst-av-vmdvideo:*,http-get:*:video/x-vmnc:*,http-get:*:video/x-vp3:*,http-get:*:video/x-vp5:*,http-get:*:video/x-vp6:*,http-get:*:video/x-gst-av-vqavideo:*,http-get:*:video/x-gst-av-wnv1:*,http-get:*:video/x-xan:*,http-get:*:video/x-gst-av-xl:*,http-get:*:unknown/unknown:*,http-get:*:video/x-zlib:*,http-get:*:video/x-zmbv:*,http-get:*:application/x-ape:*,http-get:*:application/x-gst-av-avs:*,http-get:*:application/x-gst-av-daud:*,http-get:*:application/x-gst-av-ea:*,http-get:*:video/x-4xm:*,http-get:*:application/gxf:*,http-get:*:application/x-gst-av-idcin:*,http-get:*:application/x-gst-av-ipmovie:*,http-get:*:application/x-gst-av-mm:*,http-get:*:application/x-gst-av-mmf:*,http-get:*:audio/x-musepack:*,http-get:*:video/x-nsv:*,http-get:*:application/x-gst-av-nut:*,http-get:*:application/x-gst-av-nuv:*,http-get:*:video/x-pva:*,http-get:*:application/x-gst-av-film_cpk:*,http-get:*:application/x-gst-av-smk:*,http-get:*:application/x-gst-av-sol:*,http-get:*:application/x-gst-av-psxstr:*,http-get:*:audio/x-ttafile:*,http-get:*:application/x-gst-av-vmd:*,http-get:*:application/x-gst-av-wc3movie:*,http-get:*:application/x-gst-av-wsaud:*,http-get:*:application/x-gst-av-wsvqa:*,http-get:*:video/x-svq:*,http-get:*:audio/x-mace:*,http-get:*:audio/x-vnd.sony.atrac3:*,http-get:*:audio/x-vgm:*,http-get:*:audio/x-spc:*,http-get:*:audio/x-sap:*,http-get:*:audio/x-nsf:*,http-get:*:audio/x-kss:*,http-get:*:audio/x-hes:*,http-get:*:audio/x-gym:*,http-get:*:audio/x-gbs:*,http-get:*:audio/x-ay:*,http-get:*:application/vnd.rn-realmedia:*,http-get:*:application/x-pn-realaudio:*,http-get:*:application/x-rdt:*,http-get:*:application/mxf:*,http-get:*:video/x-cdxa:*,http-get:*:audio/x-stm:*,http-get:*:audio/x-s3m:*,http-get:*:audio/x-it:*,http-get:*:audio/x-xm:*,http-get:*:audio/x-mod:*,http-get:*:application/x-id3:*,http-get:*:application/sdp:*,http-get:*:application/x-gdp:*,http-get:*:audio/riff-midi:*,http-get:*:audio/midi:*,http-get:*:application/x-hls:*,http-get:*:application/kate:*,http-get:*:text/x-cmml:*,http-get:*:video/x-daala:*,http-get:*:video/x-smoke:*,http-get:*:application/x-ogm-audio:*,http-get:*:application/x-ogm-video:*,http-get:*:application/x-ogm-text:*,http-get:*:application/x-ogg-avi:*,http-get:*:application/x-subtitle-lrc:*,http-get:*:application/x-subtitle-qttext:*,http-get:*:application/x-subtitle-dks:*,http-get:*:application/x-subtitle-mpl2:*,http-get:*:application/x-subtitle-tmplayer:*,http-get:*:application/x-subtitle-sami:*,http-get:*:application/x-subtitle:*,http-get:*:video/x-sonix:*,http-get:*:video/x-pwc2:*,http-get:*:video/x-pwc1:*,http-get:*:video/x-bayer:*,http-get:*:image/x-exr:*,http-get:*:image/webp:*,http-get:*:video/x-ivf:*,http-get:*:video/x-matroska-3d:*,http-get:*:video/x-matroska:*,http-get:*:audio/x-matroska:*,http-get:*:audio/x-pn-realaudio:*,http-get:*:audio/x-tta:*,http-get:*:video/x-pn-realvideo:*,http-get:*:application/x-subtitle-unknown:*,http-get:*:application/x-usf:*,http-get:*:application/x-ass:*,http-get:*:application/x-ssa:*,http-get:*:audio/x-midi-event:*,http-get:*:audio/ms-gsm:*,http-get:*:image/x-pcx:*,http-get:*:image/x-tga:*,http-get:*:image/x-bitmap:*,http-get:*:image/vnd.wap.wbmp:*,http-get:*:image/x-MS-bmp:*,http-get:*:image/x-bmp:*,http-get:*:image/bmp:*,http-get:*:image/x-portable-pixmap:*,http-get:*:image/x-portable-graymap:*,http-get:*:image/x-portable-bitmap:*,http-get:*:image/x-portable-anymap:*,http-get:*:image/tiff:*,http-get:*:image/x-pixmap:*,http-get:*:image/x-sun-raster:*,http-get:*:image/x-cmu-raster:*,http-get:*:application/x-navi-animation:*,http-get:*:image/x-icon:*,http-get:*:image/gif:*,http-get:*:text/plain:*,http-get:*:image/svg:*,http-get:*:image/svg+xml:*,http-get:*:video/x-mimic:*,http-get:*:application/x-kate:*,http-get:*:subtitle/x-kate:*,http-get:*:audio/x-sid:*,http-get:*:video/x-vp9:*,http-get:*:text/x-raw:*,http-get:*:video/x-msvideo:*,http-get:*:video/x-huffyuv:*,http-get:*:video/x-msmpeg:*,http-get:*:application/x-subtitle-avi:*,http-get:*:image/jp2:*,http-get:*:image/x-j2c:*,http-get:*:subpicture/x-pgs:*,http-get:*:subpicture/x-dvd:*,http-get:*:application/x-apetag:*,http-get:*:audio/x-xi:*,http-get:*:audio/x-w64:*,http-get:*:audio/x-voc:*,http-get:*:audio/x-svx:*,http-get:*:audio/x-sds:*,http-get:*:audio/x-rf64:*,http-get:*:audio/x-paris:*,http-get:*:audio/x-nist:*,http-get:*:audio/x-ircam:*,http-get:*:audio/x-au:*,http-get:*:audio/aiff:*,http-get:*:audio/x-aiff:*,http-get:*:application/x-bzip:*,http-get:*:video/x-fli:*,http-get:*:application/x-yuv4mpeg:*,http-get:*:video/x-h265:*,http-get:*:image/png:*,http-get:*:application/dash+xml:*,http-get:*:video/x-wmv:*,http-get:*:audio/x-wma:*,http-get:*:video/x-ms-asf:*,http-get:*:multipart/x-mixed-replace:*,http-get:*:audio/x-amr-wb-sh:*,http-get:*:audio/x-amr-nb-sh:*,http-get:*:audio/x-private1-ac3:*,http-get:*:audio/x-eac3:*,http-get:*:audio/x-private1-dts:*,http-get:*:audio/x-flac:*,http-get:*:audio/x-wavpack:*,http-get:*:audio/x-wav:*,http-get:*:video/x-flv:*,http-get:*:audio/x-nellymoser:*,http-get:*:video/x-vp6-alpha:*,http-get:*:video/x-vp6-flash:*,http-get:*:video/x-flash-screen:*,http-get:*:video/x-flash-video:*,http-get:*:audio/ac3:*,http-get:*:audio/x-bv:*,http-get:*:audio/x-celt:*,http-get:*:video/x-dv:*,http-get:*:audio/x-iLBC:*,http-get:*:audio/G722:*,http-get:*:audio/G723:*,http-get:*:audio/x-adpcm:*,http-get:*:audio/G729:*,http-get:*:audio/x-gsm:*,http-get:*:audio/AMR-WB:*,http-get:*:audio/AMR:*,http-get:*:video/x-h263:*,http-get:*:image/x-jpc:*,http-get:*:video/x-jpeg:*,http-get:*:image/jpeg:*,http-get:*:video/mpegts:*,http-get:*:video/x-divx:*,http-get:*:audio/x-sbc:*,http-get:*:audio/x-siren:*,http-get:*:audio/x-speex:*,http-get:*:audio/x-vorbis:*,http-get:*:video/x-vp8:*,http-get:*:application/x-srtcp:*,http-get:*:application/x-srtp:*,http-get:*:application/x-rtcp:*,http-get:*:application/x-srtcp-stream:*,http-get:*:application/x-srtp-stream:*,http-get:*:application/x-rtcp-stream:*,http-get:*:application/x-rtp-stream:*,http-get:*:application/x-teletext:*,http-get:*:subpicture/x-dvb:*,http-get:*:audio/x-dts:*,http-get:*:audio/x-ac3:*,http-get:*:audio/x-lpcm:*,http-get:*:video/x-h264:*,http-get:*:video/x-dirac:*,http-get:*:video/mpeg:*,http-get:*:application/x-rtp:*,http-get:*:audio/x-opus:*,http-get:*:audio/x-mulaw:*,http-get:*:video/x-theora:*,http-get:*:audio/webm:*,http-get:*:video/webm:*,http-get:*:video/ogg:*,http-get:*:audio/ogg:*,http-get:*:application/ogg:*,http-get:*:application/x-icy:*,http-get:*:audio/L16;rate=44100;channels=2:*,http-get:*:audio/x-scpls:*,http-get:*:audio/x-mpeg:*,http-get:*:audio/mpeg:*,http-get:*:video/x-raw:*,http-get:*:audio/x-raw:*,http-get:*:audio/*:*,http-get:*:audio/x-alaw:*​

What I saw there was that the audio/ogg MIME type came before audio/L16 and thus is preferred by the player. ​

Then I used gupnp-av-cp to get the DIDL-Lite information of a song offered by the server: Browse to a song, then right-click and select Fetch DIDL-Lite. It looked like that: ​

<?xml version="1.0" encoding="UTF-8"?>
<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/">
  <item id="20177" parentID="20140" restricted="1">
    <dc:title>Feelin Blue</dc:title>
    <upnp:class>object.item.audioItem.musicTrack</upnp:class>
    <upnp:artist>Wonderwall</upnp:artist>
    <upnp:album>Witchcraft</upnp:album>
    <dc:date>2002-01-01</dc:date>
    <upnp:genre>Pop</upnp:genre>
    <res duration="00:03:17" sampleFrequency="44100" nrAudioChannels="2" protocolInfo="http-get:*:audio/L16;rate=44100;channels=2:*">http://192.168.3.3:49152/content/media/object_id/20177/res_id/none/pr_name/audio2pcm/tr/1</res>
  </item>
</DIDL-Lite>

Missing ogg​

Apparent in the DIDL-Lite is that there is no mention of audio/ogg at all. I looked up the UPnP specification, the XSD in UPnP-av-ContentDirectory-v1-Service.pdf told me that the <res> tag is allowed several times. ​

My MediaTomb 0.12.1 server had the following transcoding configuration for ogg/vorbis files: ​

<!-- ogg to wav #2 -->
<profile name="audio2pcm" enabled="yes" type="external">
  <mimetype>audio/L16</mimetype>
  <accept-url>no</accept-url>
  <first-resource>yes</first-resource>
  <hide-original-resource>yes</hide-original-resource>
  <accept-ogg-theora>no</accept-ogg-theora>
  <sample-frequency>44100</sample-frequency>
  <audio-channels>2</audio-channels>
  <agent command="ffmpeg" arguments="-i %in -acodec pcm_s16be -ab 192k -ar 44100 -ac 2 -f s16be -y %out"/>
  <buffer size="1048576" chunk-size="131072" fill-size="262144"/>
</profile>

hide-original-resource caused the original .ogg file to not to be listed at all, so I changed it to no and got two resources now: ​

<res duration="00:03:17" sampleFrequency="44100" nrAudioChannels="2" protocolInfo="http-get:*:audio/L16;rate=44100;channels=2:*">http://192.168.3.3:49152/content/media/object_id/20177/res_id/none/pr_name/audio2pcm/tr/1</res>
<res protocolInfo="http-get:*:audio/ogg:*" size="4940995" bitrate="24576" duration="00:03:17" sampleFrequency="44100" nrAudioChannels="2">http://192.168.3.3:49152/content/media/object_id/20177/res_id/0/ext/file.ogg</res>

Transcoding flag​

Unfortunately the L16 stream was still handed to the player. Jens Georg explained to me that gupnp-av-cp used the first resource since the order of appearance is important when streams are equal. Resources listed first are seen as preferred by the server. ​

Apart from reordering, the audio/L16 resources can be marked as transcoded, and in this case gupnp-av-cp would actually prefer the original stream. DLNA provides a <res> attribute to indicate a transcoded stream: ​

7.4.1.3.23 MM ci-param (conversion indicator flag)​

7.4.1.3.23.1​

[GUIDELINE] The syntax definition of ci-param shall be as follows: ​

  • ci-param = ci-param-delim "DLNA.ORG_CI=" ci-value​
  • ci-param-delim = ";"​
  • ci-value = Boolean​
  • Boolean = "1" | "0"​

If the context of the protocolInfo involves a content binary that is converted from a different content binary, then ci-value is "1". Otherwise, the ci-value is "0". (See 7.4.1.3.12.5 for determining an appropriate context.) ​

DLNA Guidelines March 2014 - Part 1-1 Architectures and Protocols 201412....pdf ​

​

MediaTomb's source code contains a reference to the attribute in common.h , but it is wrapped in an #ifdef: ​

D_CONVERSION_INDICATOR "DLNA.ORG_CI"

I guess on Debian 8 this is no defined, and thus this attribute does not get added to transcoded resources. ​

Jin^ELD, one of the previous maintainers, told me that MediaTomb is realistically dead; nobody had been doing any work on it since 7 years. ​

Reordering​

Now that I could not get the transcoding flag, the only option left was to change the order of the res tags. The transcoding configuration has a first-resource tag for renderers that don't do stream matching, and it was activated on my server. ​

Deactivating it made the audio/ogg resource appear before the audio/L16 one, and gupnp-av-cp finally handed the ogg file to gmediarender which played it fine. ​

Written by Christian Weiske.

Comments? Please send an e-mail.