Unser als Küchenradio genutzes Noxon iRadio ist leider nicht geeignet, um in unserer UPnP-Künstlerliste zu navigieren. Es hat offenbar einen Fehler der dazu führt, daß man beim Navigieren nach 2-3 Bildschirmseiten (mit jeweils 5 Einträgen) wieder ins Hauptmenü zurückgeworfen wird, wenn man einen Ordner öffnen will. Bei einer Musiksammlung mit ~140 Künstlern nicht brauchbar.
Weiterhin ist es auch für Podcasts nicht brauchbar.
Zeit für ein neues Gerät.
Beim Kauf eines Satellitenreceivers für die Verwandtschaft hatte ich gesehen, daß Technisat auch Internetradios im Angebot hat. Ich hab mich dann durch deren Website geklickt und die Radios rausgesucht, die Ethernet, UPnP und DAB konnten.
Viel blieben nicht übrig, und dann bin ich ins lokale Conrad um die auszuprobieren. Beim DigitRadio 600 gibt es keinen Zurück-Knopf, der beim Navigieren im UPnP-Verzeichnisbaum immens wichtig ist. Er existiert nur auf der Ferbedienung.
Das DigitRadio 580 gabs im Conrad nicht, aber ich konnte es mir zur Ansicht in die Filiale liefern lassen und 2 Tage später ausprobieren.
Es passte alles, und ich habs für 270€ gekauft.
Zum Radio gibt es die App Technisat CONNECT, mit der man das Radio anschalten und komplett bedienen kann. Leider ist sie 40 MiB groß, was für ein Radiosteuerprogramm viel zu viel ist.
Um eine eigene App zu bauen oder das Radio in die Heimnetzsteuerung einzubinden braucht es aber Kenntnisse der Schnittstelle. Der Technisat-Support meinte nur lapidar:
Bitte haben Sie Verständnis dafür, dass wir Ihnen die Schnittstellendokumentation nicht zur Verfügung stellen können.
Das Verständnis fehlt mir hier völlig, aber ich gehe auch nicht davon aus, daß ich irgendeine Unterstützung seitens der Hersteller bekomme. Das ist ziemlich bedrückend.
Mir war unbekannt, wie die App mit dem Radio spricht. Also erstmal nen Portscan:
$ nmap -p1-65535 digira Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-13 21:40 CEST Nmap scan report for digira (192.168.3.46) Host is up (0.0043s latency). rDNS record for 192.168.3.46: digira.home.cweiske.de Not shown: 65530 closed ports PORT STATE SERVICE 80/tcp open http 514/tcp open shell 8080/tcp open http-proxy 10003/tcp open documentum_s 55299/tcp open unknown Nmap done: 1 IP address (1 host up) scanned in 333.03 seconds
Auf Port 80 läuft das Webinterface, mit dem man dem Radio einen neuen Namen geben kann. Der Rest ist unklar, aber Port 10003 könnte ein Fernsteuerungsport sein. Eine Telnetverbindung dahin brachte Binärzeugs, was auf eine Verschlüsselung hindeutet :/
Im vom UPnP verwendeten SSDP-Gerätfindeprotokoll meldet es sich neben den üblichen UPnP-Informationen so:
uuid:46C19E4A-472B-11E1-9F67-0022616E9730::urn:schemas-frontier-silicon-com:undok:fsaudsync:1
(via gssdp-discover)
Da ich die für die verschlüsselte Kommunikation nötigen Schlüssel und Algorithmen nicht erraten kann, beschloß ich in der App nachzusehen wie es gemacht wird.
Also die App per adb vom Handy ziehen:
$ adb root $ adb shell crackling:/ # pm path com.technisat.multiplay.main package:/mnt/expand/123457890/app/com.technisat.multiplay.main-1/base.apk $ exit $ adb pull /mnt/expand/123457890/app/com.technisat.multiplay.main-1/base.apk
Jetzt noch den Decompiler jadx-gui gestartet und ... rausgefunden, daß der Java-Code eigentlich nur nen dünner Wrapper ist, der die libMultiPlayAndroid.so (35 MiB) startet, eine binäre Bibliothek, die nicht so einfach zu dekompilieren ist :/
Der letzte Ausweg war jetzt der Netzwerksniffer Wireshark, mit dem ich die Netzwerkpakete zwischen App und Radio mitschneiden wollte. mitmproxy konnte ich nicht verwenden, weil der nur HTTP kann, ich aber diesen komischen 10003er Port belauschen muss.
Das machte das Setup etwas schwierig, denn das Konfigurieren des Gateways im DHCP-Server reicht leider nicht aus - die Handy und Radio kommunizieren nach den ersten Paketen dann doch lieber direkt.
Mir fiel eine Alternative ein: Laptop zum Access Point machen, Handy mit diesem verbinden und schwupps, schon hab ich den Traffic. Der im NetworkManager-Applet eingebaute Hotspotmodus funktionierte leider nicht, da dieser einen neuen IP-Bereich aufmachte, der nicht mit dem echten Heimnetzwerk sprechen wollte.
Die Lösung war dann create_ap, dem man direkt sagen kann, daß das W-LAN ins normale Netzwerk gebrückt werden soll:
$ create_ap -m bridge wlan0 eth0 myssid mypassword
Jetzt bekam ich den kompletten Handydatenverkehr mit!
Nach dem Starten der App werden erstmal UDP-Pakete an eine Multicastadresse geschickt, genauso wie SSDP-Suchpakete. Das Radio meldet sich dann direkt beim Handy, die App zeigt das Gerät dann an.
Und nun die Überraschung: Die App redete nicht wie vermutet über Port 10003, sondern über HTTP auf Port 80. Anstatt des Webinterfaces sprach es aber einen anderen Pfad an, nämlich /fsapi/.
Der mitmproxy hätte also doch geholfen, und ich hätte mir den ganzen Aufwand sparen können :/
Nachdem ich um die 30 verschiedene API-Aufrufe mitgeschnitten hatte suchte ich im Netz nach dem offensichtlichsten, dem An- und Ausschalten:
GET /fsapi/GET/netRemote.sys.power?pin=1234
... und es stellt sich raus, daß andere schon 2015 mit der API-Dokumentation angefangen haben:
Auch Implementierungen gibt es einige:
Dann gibt es noch Anbindungen an Heimautomatisierungssysteme:
Die Firmwareversion meines DigitRadio 580 ist
ir-mmi-FS2026-0500-0286_V2.11.12.EX65933-1A22
FS ist "Frontier Silicon", heißt jetzt Frontier Smart und liefert Chips, in denen DAB, FM, Internetradio, UPnP, CD und Bluetooth integriert ist, sowie die passende Software dazu.
Das 2026 in der Versionsnummer steht für das VENICE 6.5 FS2026-5 Internet radio/Network streaming/DAB/FM/iPhone audio module , auch "Neptune" genannt.
Wie es aussieht, nutzen sehr viele Gerätehersteller Chips dieses Zulieferers um ihre Radios zu bauen. Also keine Eigenentwicklungen.
Einige Radios von Hama und viele von Medion haben inzwischen FrontierSilicon-Chips drin.
Frontier Smart bietet auch gleich eine App an, und zwar UNDOK . Die macht genau dasselbe wie die Technisat-App, ist aber 10 MiB groß - 30 MiB gespart.
Scheint ein Debug-Port zu sein:
I tried telnet on port 514 and discovered what appears to be a debugging system log.
Das kann ich bestätigen.
Es gibt 2 änlich klingende API-Methoden, die die Ausgabe wahrscheinlich beeinflussen:
Update 2023-11: Ist wirklich so: Digitradio 580: <Empty> audio folders und Document debugging API endpoints #27.
Wird für UPnP-Informationen benutzt, wie die dd.xml.
Unbekannt.
Der Technisat-Support meinte, der Port ist für die Multiroom-Funktionalität
da.
Unbekannt. Das ist aber eine dynamische Portnummer, die sich ändern könnte.
After upgrading my UPnP/DLNA server software Gerbera to the latest release v1.12.1, the kitchen radio showed all audio album folders as <Leer> ("Empty"). Other UPnP clients showed the the audio files just fine and could play them without problems.
At first I thought it had something to do with transcoding (most of my audio files are ogg/vorbis, which need to be converted for the radio to play), but the radio showed folders with only mp3 files as empty, too.
I knew that the radio has a debug port, and I "only" needed to find out how to enable it. It turned out that the radio's netRemote.misc.fsDebug.component and netRemote.misc.fsDebug.traceLevel API endpoints are the key: component selects the part of the radio software whose debugging level shall be configured, and traceLevel then lets you query and set the debug level of the previously selected component.
After many tries and firmware crashes I finally knew that component 14 (UPnP browsing, "AVB") was the one I had been looking for and raising the log level from 2 to 5 gave me helpful log messages via telnet on port 514:
(Thread2): [ 87.986738] AVB (3): Browse for '34203', from 0, max 100 items, server 'f42aa029-7c99-47d2-b6ed-53b9e06cc3a7' (Thread2): [ 87.987678] AVB (4): Using sort criteria '+upnp:originalTrackNumber' (Thread2): [ 87.988558] AVB (4): Invoking AVB_REQTYPE_BROWSE request (b0b449e8 b0b449e8) (Thread2): [ 88.087921] AVB (5): (MS_BrowseResponded) errorCode=0 (Thread2): [ 88.099874] AVB (4): Resource: Not found (Thread2): [ 88.101841] AVB (4): 2: (0xa200) (68044) 2 Become 1(Single Version) (Thread2): [ 88.111814] AVB (4): Resource: Not found (Thread2): [ 88.111982] AVB (4): 2: (0xa200) (68054) 2 Become 1 (Orchestral Version) (Thread2): [ 88.119992] AVB (4): Resource: Not found (Thread2): [ 88.120341] AVB (4): 2: (0xa200) (68034) One Of These Girls (Thread2): [ 88.127823] AVB (4): Resource: Not found (Thread2): [ 88.127985] AVB (4): 2: (0xa200) (68024) Wannabe (Junior Vasquez Remix Edit) (Thread2): [ 88.129514] AVB (3): Browse OK - 4 matches, 4 returned, 4 copied (Thread2): [ 88.129649] AVB (3): TotalMatches = 4
The log messages told me that the radio actually got the list of
songs in the album folders, but rejected them because
Resource not found
- although it did not request the URLs.
For comparison I installed minidlna on my laptop; the radio could list and play all audio files without problems.
I captured DIDL-Lite XML responses with Wireshark from both minidlna and Gerbera, decoded and compared them with Meld - but I could not spot the problem :(
Then I remembered that I had tagged the old Gerbera version in the git clone on my home server. I installed it, recorded the UPnP server responses with Wireshark's SSH remote capture feature and compared them to the new ones:
The only relevant difference were the audio file URLs: Old gerbera from 2022-02 had the server's IP address, the new version used the domain name.
- http://192.168.3.3:49152/content/media/object_id/736/res_id/0/ext/file.ogg + http://mediatomb.home.cweiske.de:49152/content/media/object_id/51123/res_id/0/group/default/ext/file.ogg
So I removed the <virtualURL> setting from Gerbera's configuration .. and the radio showed all files again.
In my bug report Karl Straussberger told me that other devices had similar problems, and that I should use externalURI instead of virtualURL.
The whole thing took 4 hours distributed over 2 days. API documentation was another hour..
After years of using the unmaintained MediaTomb UPnP server, I switched to Gerbera. Gerbera builds on the MediaTomb codebase and adds new features like cover images, compiles on current distributions, and is maintained.
Our Dreambox satellite receiver is able to stream radio into the network, but only in the mpeg/ts2 format which no radio supports. I configured Gerbera to use vlc to automatically convert the TS2 streams into mp3 when a radio wants to play them, but after a software update on the home server transcoding ceased to work.
The new working configuration for Gerbera 1.2.0 and VLC 3.0.3-1-0+deb9u1:
audio/mpeg
yes
yes
no
]]>
Some parameters are the result of me trying to minimize the amount of lines that get written into the gerbera log file.
vlcpulse audio output error: PulseAudio server connection failure: Connection refused
Solution: --aout alsa.
dbus interface error: Failed to connect to the D-Bus session daemon: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11
Solution: --no-dbus
What stays are the following lines:
main interface error: no suitable interface module main libvlc error: interface "globalhotkeys,none" initialization failed dummy interface: using the dummy interface module... dummy mux: Open
Unfortunately, there is no way to get rid of those. Using --quiet also hides errors which is not something that I want.
That ticket #5047 also does not give me much hope that this will become any better in the future.
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.
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.
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.
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.
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.
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:
-
Feelin Blue
object.item.audioItem.musicTrack
Wonderwall
Witchcraft
2002-01-01
Pop
http://192.168.3.3:49152/content/media/object_id/20177/res_id/none/pr_name/audio2pcm/tr/1
]]>
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:
audio/L16
no
yes
yes
no
44100
2
]]>
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:
http://192.168.3.3:49152/content/media/object_id/20177/res_id/none/pr_name/audio2pcm/tr/1
http://192.168.3.3:49152/content/media/object_id/20177/res_id/0/ext/file.ogg
]]>
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.
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.
Meine eigene Noxon-Serversoftware hat ein viel übersichtlicheres und vor allem auf mich zugeschnittenes Podcastverzeichnis. Allerdings schaffe ich es meist nicht, einen ganzen 2h-Podcast am Stück zu hören und würde beim nächsten Mal gern wieder an der letzten Stelle weiterhören.
Beim Noxon iRadio und dem iRadio Cube kann man durch Gedrückthalten der vorheriges-Lied-Taste oder der nächstes-Lied-Taste spulen - und zwar mit 1.5facher Normalgeschwindigkeit. Das hilft so gar nicht wenn man eine ganze Stunde vorspulen möchte.
Die Geräte sind UPnP MediaRenderer und unterstützen das AVTransport:1-Protocol. Dieses definiert den Seek-Befehl mit dem man dem Abspielgerät sagen kann, dass es bitte mal spulen soll.
Die Spezifikation definiert verschiedene Spulmodi (A_ARG_TYPE_SeekMode), unter anderem springe-zu-zeitpunkt ABS_TIME und springe-relativ REL_TIME. Mit diesen beiden Modi könnte ich eine manuelle Spul-Spring-Steuerung über meine Noxon-Serversoftware nachrüsten.
Nachdem ich mich tief genug in die Spezifikation eingelesen hatte um das Protokoll gut zu verstehen, fand ich in der Servicebeschreibung der beiden Radios Folgendes:
A_ARG_TYPE_SeekMode
string
TRACK_NR
]]>
Der einzig erlaubte Spulmodus ist TRACK_NR - der einzige, der laut Spezifikation implementiert werden muss. Sendet man den Befehl, ist der einzig erlaubte Wert 0, also die aktuelle Datei.
Sinnlos.
Damit sind die Noxon iRadios nicht für Podcast-Hören zu gebrauchen.
Während des Stöberns in den Radiointerna fand ich noch etwas vermutlich Interessantes:
urn:schemas-upnp-org:service:HtmlPageHandler:1
urn:upnp-org:serviceId:HtmlPageServiceID
/HtmlPageHandler/desc.xml
/HtmlPageHandler/ctrl
/HtmlPageHandler/evt
]]>
Dieser Service definiert folgende Befehle:
actHtmlTcpip hat Parameter wie argHtmlIPAddress, argHtmlNetMask und argHtmlGateway was darauf schliessen lässt, daß man damit die Netzwerkeinstellungen ändern kann.
Das Ir in actHtmlIrControl steht vermutlich für Infrarot - was meinen Wunschgedanken nach bedeutet, dass ich eine Fernbedienung simulieren und per Netzwerk Befehle ans Radio schicken könnte.
Leider habe ich es nicht geschafft, auch nur einen einzigen der Befehle ohne Fehler auszuführen :/ Falls jemand mehr Infos hat, bitte her damit!
2008 begann ich damit, das Haus mit Internetradios auszustatten . Die Noxon iRadio-Geräte (Noxon iRadio und iRadio Cube) haben leider fest eingebaute Menüeinträge , die man nur mit Internetverbindung nutzen kann.
Diesmal habe ich das von den Radios gesprochene Protokoll analysiert, dokumentiert und meine eigene Serversoftware implementiert, mit der ich jetzt die Heizung steuern kann.
Die iRadio-Geräte haben einige sehr störende Bugs:
Die Noxon-Internetradios telefonieren regelmäßig nach Hause. Ich habe deshalb von Anfang an die angefragten Domains auf 127.0.0.1 gemappt.
Die Uhrzeit ist - vor allem auf dem iRadio Cube - falsch, obwohl NTP eingstellt ist. Das ganze hängt auch noch von der Sommerzeit/Winterzeit-Einstellung ab; im Sommer zeigt das Radio fast eine komplette Stunde falsch an, im Winter sind es ~10 Minuten.
Wenn das Radio einen Tag im Standby ist, verliert es den UPnP-Server - und findet ihn auch nicht mehr, wenn man es wieder aus dem Standby weckt. Man muss das Radio richtig hart neustarten, damit man es wieder nutzen kann.
In UPnP-Ordnern mit vielen Einträgen (gefühlt ab 50, ich habs aber auch schon bei welchen mit 10 erlebt) kann man nicht mehr in die Ordner reinwechseln.
Man scrollt runter zum gewünschten Ordner, drückt -> und ist wieder am Anfang der Liste.
Ganz krass ist es bei Ordnern mit 200+ Einträgen: Einfach nach oben scrollen, so dass man am Ende rauskommt und dann noch ein paar mal nach oben drücken - schon ist man komplett aus dem Menü raus und im Hauptmenü.
Damit ist das Radio bei größeren Albenlisten unbenutzbar.
Da Terratec vermutlich irgendwann die Server abschalten wird, habe ich mit mitmproxy den Datenverkehr der Radios mitgeschnitten, analysiert und das Protokoll dokumentiert . Mit der API-Doku konnte ich nun meinen eigenen Noxon-Server bauen.
Ich hatte mehrere Ziele:
Da die Radios fest eingebaute Menüeinträge Internetradio, Podcasts und My Noxon haben - die niemals verloren gehen - war die erste Idee, das Internetradio-Menü mit dem Inhalt des UPnP-Servers zu bestücken
Ich hatte 2008 mehr aus Spaß geschrieben:
Knowing this format, one could make the menu entries actually useful by creating a noxonXml-to-MediaTomb gateway with Services_MediaTomb :)
Genau das habe ich auch gemacht; wenn man das Internetradio-Menü betritt sieht man genau denselben Inhalt wie wenn man den UPnP-Server-Menüeintrag ausgewählt hätte.
Zwei Vorteile:
Das iRadio Cube unterstützt - im Gegensatz zum originalen iRadio - leider kein ogg/vorbis. Da der UPnP-Server jetzt nicht mehr direkt von den Radios angefragt wird, musste ich die Konvertierung von .ogg-Dateien nach .mp3 selbst implementieren.
Als die grundlegende API zum Ausspielen von Menüs implementiert war baute gleich ich noch einen Datei- und Verzeichnisansichtsmodus ein.
Damit werden die Menüs Podcasts und My Noxon jetzt auf Pfade im Dateisystem abgebildet: Wenn man My Noxon aufruft, wird der Inhalt des Verzeichnisses var/mynoxon aufgelistet.
Ordner und Textdateien werden im Radio als Ordner dargestellt. Navigiert man in eine Textdatei hinein, wird jede Zeile der Textdatei als nicht-navigierbarer Menüeintrag ans Radio ausgeliefert.
Für die Anzeige auf dem Fernseher habe ich schon solche Textdateien für die letzten Anrufe, die nächsten Geburtstage, den Kontostand und die Zimmertemperaturen.
Ich nutze zur Darstellung auf dem Fernseher meine Dreambox-Extension curlytx .
Schnell kam ich auf die Idee, ausführbare Dateien (chmod +x) auch als Ordner darzustellen. Navigiert man in diesen Ordner hinein, wird die Datei ausgeführt und die Ausgabe des Programms wie bei Textdateien zeilenweise ans Radio gesendet.
Schnell kamen noch Autostartscripte hinzu, die automatisch ausgeführt werden, wenn man einen Ordner auflistet, und deren Ausgabe in die Ordnerauflistung integriert.
Wir haben eine Wärmepumpe von Dimplex, die per Netzwerkmodul (NWPM) ansprechbar ist.
Über HTTP kann man die aktuellen Einstellungen auslesen und ändern, wie ich es mit meinen dimplex-tools mache:
#!/bin/sh
set -e
ipaddress=1.2.3.4
res=`curl -sS "$ipaddress/usr-cgi/xml.cgi?I|1|1"`
echo "$res"\
| grep VALUE\
| tr -cd [:digit:]
echo
Dieses Script liest die aktuelle Einstellung der manuellen Anpassung der Rücklaufsolltemperatur aus. Wenn man diese ändert, wird das Haus wärmer oder kälter.
Schnell waren ein paar Scripte geschrieben, eins das die aktuelle Einstellung der Wärmepumpe im Ordner "Heizung" anzeigt, und Scripte zum Wärmer- und Kälterstellen der Heizung:
Die resultierende Serversoftware heisst noxon-gateway und ist auf meinem Git-Server zu finden.
I installed the OpenVPN daemon a couple of days ago so that I'm able to connect into my home network from anywhere and route my traffic via this encrypted connection - helpful for public or hotel WiFi.
When I came home again, the UPnP radios (noxon iRadio and iRadio cube, Freecom Musicpal) did not find the MediaTomb UPnP server anymore.
Looking into the MediaTomb logs at /var/log/mediatomb.log told me that it used a different IP address - one from my VPN address space:
2015-06-30 23:03:08 INFO: Server bound to: 192.168.4.1
Since the netmasks of the radios are /24, they could not connect to the server anymore.
The .4.x addresses were on device tun0. I had to adjust /etc/default/mediatomb to force MediaTomb to use the correct network interface:
INTERFACE="eth0"
That's it. Radios play music again.
Many rooms in our house have an UPnP capable internet radio device, and I'm using Mediatomb to distribute music and radio to them.
Mediatomb automatically categorizes music files into different virtual folders:
UPnP directory root ├ Audio │ ├ Albums │ ├ All - full name │ ├ All Audio │ ├ Artists │ ├ Genres │ └ Year ├ PC Directory └ Photos
This is fine for music albums, but not for radio plays, podcasts, single tracks I don't have the full album for and kid's tales.
Luckily mediatomb allows one to completely customize how those virtual folders are setup.
Files for our kids fall in two categories: Music and tales/stories. Thus beside the "Audio" and "Photos" folder, there shall be a "Kid's Music" and "Kid's Stories" folder. They should not be indexed in the Audio folder.
I've got Podcasts and adult radio plays as well, and they should also not be in the Audio folder, but in their own ones.
Finally, single songs shall be put into "Audio/Singles" without any futher indexing.
UPnP directory root ├ Audio │ ├ Albums │ ├ All - full name │ ├ All Audio │ ├ Artists │ ├ Genres │ ├ Singles │ └ Year ├ Kid's Music ├ Kid's Stories ├ Podcasts ├ PC Directory ├ Radio plays └ Photos
I've already sorted them in the real directories that way:
$ ls -1 /var/spool/music Albums Singles Radio plays Kid's music Kid's tales Podcasts
With that, I only have to configure mediatomb to act a bit differently depending on the folder the audio file is in.
At first mediatomb has to use a custom import script. The documentation tells us that we have to use a virtual layout of type js, and can configure the import script there:
/etc/mediatomb/import-cweiske.js
]]>
import-cweiske.js is a copy of /usr/share/mediatomb/js/import.js with some small changes. The scripting docs helped here:
The adjustments to the addAudio function do the following:
I'm using mplayer to transcode the mpeg2 satellite streams from my Dreambox for the kitchen radio. Since the wav stream cannot be longer than 3:06:18, and Mediatomb fails to stop the mplayer process when the radio does not use the stream anymore, I decided to write a script that kills the mplayer process automatically when it has been run for a given time.
Here is the script in case someone else has similar needs:
Meine Telefonanlage, die Auerswald COMpact 3000 ISDN, spammt das Netzwerk mit regelmäßigen UPnP-Ankündigungspaketen zu, was beim Debuggen mit Wireshark ziemlich lästig ist.
Weder im Webinterface noch im Handbuch fand ich einen Hinweis darauf, wie man es deaktivieren kann - also schrieb ich an den Support:
Ich besitze eine Compact 3000 ISDN und benötige die UPnP-Funktionalität der Anlage nicht, weshalb ich sie gern abschalten würde. Leider habe ich weder durch Umherklicken noch im Handbuch einen Hinweis dazu gefunden, wie man den UPnP-Service der Telefonanlage deaktivieren könnte. Können Sie mir weiterhelfen?
Einen halben Tag später - also relativ flott - bekam ich die Antwort:
Der Zugriff zur Tk-Anlage COMpact 3000 ISDN erfolgt über die variable IP (im Auslieferzustand 192.168.0.240) oder über die feste IP 169.254.1.240. Diese feste IP kann immer genutzt werden, wenn vor Ort der Dienst UPnP aktiv ist und der genutzte PC keine feste IP hat. Hierbei handelt es sich um das APIPA (Automatic Private IP Addressing). UPnP und APIPA kann nicht ausgeschaltet werden.
Drecksteil.