Live-Streaming von DVB-Streams

DVB Radio Live Webstreaming

Hat man sich zuhause Squeezebox Mediaplayer aufgebaut und einen Logitechmediaserver im Haus kommt auch der Wunsch auf, damit Radio empfangen zu können. Problemlos kann man verschiedene Webstreams einrichten, aber eigentlich kommen auch über Satellit und im Kabelfernsehen etliche Radioprogramme. Diese DVB Datenströme haben (technisch) eine hohe Qualität und es skaliert sehr gut. Das bedeutet, daß auch zu Stoßzeiten der Empfang sichergestellt ist, da es sich eigentlich um Radioempfang handelt. Allerdings werden die Radioprogramme bei DVB-S und DVB-C als MPEG Transportströme übertragen.

[Ansicht des Players]
DVB Stream als (Shoutcast kompatibler) Datenstrom empfangen. Darstellung auf dem Player, aktuell läuft SWR1 BW "Der Nachmittag".

Mit MPEG Transportströmen kommt die Squeezebox und die meisten Internet- und Webradios allerdings nicht zurecht und demzufolge ist eine Konvertierung erforderlich. Mit dieser Konvertierung kann man DVB -C/-S Radioprogramme ganz normal wie einen normalen Webstream mit einer Squeezebox oder einem beliebigen Streaming Client empfangen.

Da man für die Nutzung einer TV Karte eine spezialisierte Applikation braucht verwende ich für den eigentlichen Empfang tvheadend. Die Einrichtung empfand ich persönlich als nicht trivial und man braucht auch Zusatzsoftware die es ohne weiteres nicht gibt um erweiterte Programminformationen aus dem MPEG Transportstrom im Radioprogramm breitzustellen (aus dem MPEG "EPG"). Prinzipiell kann auch tvheadend die Konvertierung des MPEG Transportstromes in "nackte" MPEG Audiodaten übernehmen, mit meiner unten beschriebenen, spezialisierten Applikation können aber gleich auch noch Informationen über den Stream übertragen werden.

TV Karte

Zum Empfang der DVB Datenströme wird bei mir eine Digital Devices Cine-C2 verwendet. Das Linux-Kernelmodule ddbridge darf bei mir zumindest nicht mit MSI-X Interrupts geladen werden, da es ansonsten zu I2C Bus-Timeouts kommt. Das ist auch bei Digital Devices in der FAQ beschrieben. Ich habe daher die folgenden Optionen angegeben:

server:/etc>cat modules |grep ddbridge
ddbridge msi=0 
server:/etc>cat modprobe.d/ddbridge.conf
options ddbridge msi=0

tvheadend

Nachdem die Low-Level-Module problemlos geladen werden, kann man sich an die Installation der tvheadend Middleware machen. Ich nutze tvheadend, aktuell in der Version 4.3-1133 auf einem Debian Jessie. Das ist eine Beta-Version die nicht im Debian Jessie Standard-Repository ist (die aber Transcoding-Module schon mitbringt). Für nähere Infos hierzu bitte die entsprechende Wiki-Seite des tvheadend Projektes besuchen. Für das im folgende beschriebene Setup braucht man diese spezielle Version aber gar nicht, sondern kann das Debian Jessie Standardpaket verwenden. Nach der Installation von tvheadend fällt auf, daß die Konfiguration etwas ungewöhnlich ist. Alles wird über das Webfrontend eingestellt. Es gibt keine zentrale Konfigurationsdatei sondern eher ein Konfigurationsverzeichnis welches sich dann in /home/hts/.hts/ befindet. Eher Linux-unüblich ist eine manuelle Konfiguration eher nicht möglich sondern alle Einstellungen müssen über das Webfrontend gemacht werden. Im weiteren gehe ich davon aus, daß tvheadend schon soweit eingerichtet ist, daß man z.B. mit vlc oder kodi TV gucken bzw. Radio hören kann.

Um die Streams mit der Squeezebox anhören zu können muß man den MPEG Transportstrom in etwas konvertieren was die Squeezebox versteht. Hierzu verwende ich die von mir angepasste Software ts2shout. Diese konvertiert einen MPEG Transportstrom in einen Shoutcast- oder, alternativ, einen nativen MPEG Audiodatenstrom.

Fürs Streaming der Radioprogramme aus dem Kabel verwende ich das "Stream profile" "pass" welches in tvheadend schon als Standard vorbereitet ist.

ts2shout

Die Konvertierung erledigt apache2 zusammen mit einem CGI- und Filterprogramm, welches ich hier abgelegt habe:

ts2shout ist absichtlich minimalistisch, aber autokonfigurierend. Im MPEG Transportstrom wird nach einem passenden Audio-MPEG Kanal gesucht und dieser und die EPG Information über die aktuelle Sendung extrahiert. Dieser wird konvertiert ins Shoutcast Format (ohne Parameter gestartet) bzw. in einen nackten mpeg Audiodatenstrom (mit Parameter noshout aufgerufen). Die crc32 Checks über die MPEG Controlframes werden gemacht. Einen "nackten" mpeg Audiodatenstrom kann man auch mit tvheadend direkt erzeugen, indem man das entsprechende Transcoding modul verwendet. ts2shout braucht man also nur, wenn man einen "Shoutcast"-kompatiblen Audiostrom möchte, der Sendungsinformationen über die aktuelle Radiosendung aus dem EPG im Datenstrom transportiert. Dadurch kann man dann auf dem Display direkt die Radiosendung erkennen.

ts2shout ... als filter

ts2shout wird defaultmässig nach /usr/local/bin installiert. Es handelt sich um eine "Dual-Use" Applikation. Einerseits kann man diese regulär auf der Kommandozeile als "klassischen" Unix Filter verwenden, d.h. auf stdin (Standardeingabe) wird ein MPEG-Transportstrom gelesen und auf stdout (Standardausgabe) wird der nackte MPEG Audiostrom (oder eben Shoutcast-Medienstrom) ausgegeben. Typischerweise verwendet man diesen Modus zum Testen. Es ist auch möglich die Applikation auf diese Art als mod_ext_filter in Apache2 einzurichten. Der Startup ist dann etwas schneller als als CGI Programm, da es dann nicht mehr nötig ist auf ein SDT MPEG Frame zu warten, welches den Stationsnamen usw. enthält.

ts2shout ... als CGI applikation

Das ist die Hauptanwendung. Bei der Verwendung als CGI Applikation werden die relevanten Streamparameter direkt aus dem MPEG Frames gelesen und - sowie sie zusammengesammelt wurden - "Shoutcast-Kompatibel" als HTTP Header ausgegeben. Danach beginnt dann das reguläre Streaming der MPEG Daten. Im CGI Modus wird der Eingangsdatenstrom mit Hilfe von libcurl von tvheadend gelesen und wie bei CGI Programmen üblich auf stdout geschrieben.

Es sind einige Hacks im Code enthalten, damit die Anzeige auf einer Squeezebox oder auch auf einem kleinen Webradio sinnvoll wird, aber trotzdem viele Informationen über die aktuelle Sendung eingeblendet werden. Zum Teil sind die EPG Informationen über die aktuelle Sendung über 600 Zeichen lang (gerne in den "Kulturprogrammen" z.B. SWR2 oder Bayern 4 Klassik). Die Squeezebox(-nachbauten) rollen den Text durch und pausieren am Textanfang eine kurze Zeit

[LMS Ansicht eines langen Titels] [LMS Ansicht eines langen Titels]
DVB Stream empfangen. Darstellung im LMS Webinterface, aktuell laufen "SWR2 Feature" bzw. "Bayern 4 Jazztime".

Einrichten von apache2 fürs Radiostreaming

Ich verwende apache2 in Zusammenspiel mit "mod_action" als Webserver für das CGI Programm. Folgende Änderung in /etc/apache2/sites-available/000-default.conf ist nach der Installation des Modules mod_action (a2enmod action) erforderlich. Wie man sich vorstellen kann, muß dieser Konfigurationsabschnitt natürlich nicht genau an dieser Stelle sein. Die Konfiguration muß eben dort eingefügt werden wo es einem für den virtuellen Host und (virtuellem) Unterverzeichnis passt. In /etc/apache2/sites-available/000-default.conf liegt eine minimale Serverkonfiguration in der Debian Defaultinstallation, dort kann man es im Normalfall einfügen. /cgi-bin/ wird bei Debian defaultmässig in /usr/lib/cgi-bin/ gesucht. Ich habe bei mir einen Symlink angelegt ln -s /usr/local/bin/ts2shout /usr/lib/cgi-bin/

Nun aber der erforderliche Konfigurationsabschnitt.

<Location /radio>
 Order Deny,Allow
 Deny from all
 Allow from 172.16.0.0/24
 Allow from ::1
 Allow from 127.0.0.1
 <If "%{HTTP:Icy-MetaData} in {'1'}">
  SetEnv "MetaData" "1"
 </If>
 SetEnv TVHEADEND "http://localhost:9981/stream/channelnumber"
 # Die Radioprogramme, die URLs lauten dann
 # /radio/swr1bw (z.B.)
 SetEnvIf REQUEST_URI "swr1bw$" PROGRAMMNO=813
 SetEnvIf REQUEST_URI "swr2$" PROGRAMMNO=815
 SetEnvIf REQUEST_URI "drs2$" PROGRAMMNO=913
 SetEnvIf REQUEST_URI "drs3$" PROGRAMMNO=914
 # Beliebig viele Radiostation auf diese Art hinzufügen
 # weiter unten gibt es eine fertige Konfiguration für Unitymedia zum Download
 Options +ExecCGI
 # Das Keyword virtual ist wichtig, da ansonsten auf die Existenz von Dateien
 # geprüft wird (die es natürlich nicht gibt)
 Action ts2shout /cgi-bin/ts2shout virtual
 SetHandler ts2shout
</Location>

Dieser Konfigurationsblock sorgt für folgendes: Apache wird angewiesen für alle Zugriffe auf URLs unterhalb von /radio die CGI Applikation ts2shout aufzurufen. Bei einem Aufruf werden die Environmentvariablen TVHEADEND und MetaData und bei einem "gültigen" Programm PROGRAMMNO gesetzt. Diese werden von ts2shout ausgewertet! Die URL $TVHEADEND/$PROGRAMMNO wird von ts2shout mittels libcurl von tvendend geladen. Es wird im empfangenden MPEG Transportstrom nach MPEG Streaminfos (Streamrate, Bitrate) und dem Stationsnamen gesucht. Wenn dieser empfangen wurde, werden die HTTP Header auf stdout an Apache gesendet und ab da dann der MPEG Datenstrom (im Shoutcastmodus oder eben nicht, abhängig vom evtl. empfangendem Icy-MetaData HTTP Header). Es dauert also ungefähr immer ca. eine halbe Sekunde bis der Stream losgeht, da vorher das CGI Programm noch nicht alle Informationen zusammengesammelt hat.

Durch das Parsen von Icy-MetaData im Request und das setzen der korrekten Stream- und Bitrate sollten die Streams weitestgehend zu vielen Webradios (nicht nur zur Squeezebox) kompatibel sein.

Eventuelle Fehler werden auf stderr und damit ins Apache Errorlog geloggt - normalerweise in /var/log/apache2/error.log zu finden.

Wenn alles richtig eingerichtet und konfiguriert ist, wird folgendes von ts2shout ins apache Error log geschrieben.

[Tue Apr 03 22:04:48.860204 2018] [ts2shout:info] [pid 19210] add_channel(): Subscribing to MPEG PID 0 (Type CHANNEL_TYPE_PAT)
[Tue Apr 03 22:04:48.860247 2018] [ts2shout:info] [pid 19210] add_channel(): Subscribing to MPEG PID 18 (Type CHANNEL_TYPE_EIT)
[Tue Apr 03 22:04:48.860259 2018] [ts2shout:info] [pid 19210] add_channel(): Subscribing to MPEG PID 17 (Type CHANNEL_TYPE_SDT)
[Tue Apr 03 22:04:48.860271 2018] [ts2shout:info] [pid 19210] Streaming with shoutcast StreamTitles in CGI mode.
[Tue Apr 03 22:04:48.887428 2018] [ts2shout:info] [pid 19210] add_channel(): Subscribing to MPEG PID 4020 (Type CHANNEL_TYPE_PMT)
[Tue Apr 03 22:04:48.966817 2018] [ts2shout:info] [pid 19210] add_channel(): Subscribing to MPEG PID 4021 (Type CHANNEL_TYPE_PAYLOAD)
[Tue Apr 03 22:04:49.026457 2018] [ts2shout:info] [pid 19210] Synced to MPEG audio in PID 4021 stream: 0xc0
[Tue Apr 03 22:04:49.026488 2018] [ts2shout:info] [pid 19210]    MPEG-1 layer 2, 320 kbps, 48000 Hz, Stereo
[Tue Apr 03 22:04:49.125773 2018] [ts2shout:info] [pid 19210] EIT: Current transmission `SWR Bestenliste'
[Tue Apr 03 22:04:50.238429 2018] [ts2shout:info] [pid 19210] SDT: Stream is station SWR2 from network ARD SW.
[...Nach dem Stoppen des Streams...]
[Tue Apr 03 22:08:36.314708 2018] [cgid:error] [pid 16643:tid 140296390149888] (104)Connection reset by peer: [client 172.16.0.13:46970] AH02550: Failed to flush CGI output to client
[Tue Apr 03 22:08:36.394020 2018] [ts2shout:info] [pid 19210] write_callback (curl): Terminated after fetching 25.33 MB and writing 8.90 MB. Exiting.

Die ersten 4 Zeilen werden direkt mit Programmstart geschrieben. Aus der PAT (Programme association table) wird die Datenstrom-ID der PMT (programm map table) ausgelesen in der wiederrum die Datenstrom-ID der eigentlichen "Audio-Payload" übertragen wird. Dies wird automatisch aus dem Datenstrom herausgelesen und wenn die Daten aus PAT und PMT verarbeitet wurden wird ab da der Audiodatenstrom ausgegeben. Sobald "Synced to MPEG audio" und die "SDT:" Zeile geloggt wurde, wird der Audiostrom im CGI weitergeleitet. Zusätzlich zu den Audiodaten werden noch Daten aus der EIT (Event information table, besser bekannt unter dem Namen EPG) herausgelesen um die aktuelle Sendung zu bestimmen.

Die öfter auftauchende Zeile beim Beenden des Streaming [cgid:error] [pid 16643:tid 140296390149888] (104)Connection reset by peer: [client 172.16.0.13:46970] AH02550: Failed to flush CGI output to client ist kein Fehler im engen Sinne, sondern rührt daher, wie das Streaming von den Clients abgebrochen wird. Es wird versucht möglichst sauber das Streaming von tvheadend zu stoppen.

Vorbereiten von tvheadend

[Bitte Bild herunterladen]
Editieren eines Benutzers im Webfrontend von tvheadend

Wichtig: Allowed networks auf jeden Fall an die eigene Installation anpassen. Das ist die IP-(Range) von der aus der Client, in unserem Fall der Apache reverse proxy, zugreifen. Das ist jetzt in unserem Fall Localhost, aber der Apache muß sich nicht zwingend auf dem Server befinden auf dem tvheadend läuft. Eben entsprechend konfigurieren, seit der Version 4.3-1159 muß offensichtlich Digest-Authentisierung genutzt werden, deshalb habe ich das bei mir erstmal herausgenommen.

Nun kann das ganze schonmal getestet werden. Bei mir befindet sich LMS und tvheadend auf dem gleichen Rechner, das muß aber nicht zwingend so sein.

Im Channel-Dialog von tvheadend muß man sich jetzt die Kanalnummern heraussuchen die die gewünschten Radioprogramme verwenden. Diese muß man in die obige Apache Konfiguration übertragen, für jedes Programm das man Streamen möchte muß sinngemäß die oben beschriebenen Abschnitte übertragen. Ich habe schonmal eine komplettes Konfigurationssnippet für Unitymedia (April 2018) vorbereitet.

Mittels wget http://localhost:9981/playlist kann man aus tvheadend schon einen ganz guten Start für den erforderlichen Konfigurationsabschnitt herunterladen.

[Bitte Bild herunterladen]
Ansicht der tvheadend Kanalliste

Für einen Stream von DRS3 (bzw. SRF3, Unitymedia ist in der Kabel-BW Zeit stehengeblieben) muß man Channel-Nummer 914 aussuchen. Das ist auch die Programmnummer im Unitymedia-Netz von BW.

Squeezebox

Nun wechselt man in die Weboberfläche des LMS und editiert die Favoriten (Da habe ich die Radioprogramme hingelegt, dann kann man denen auch einfach ein Logo zuordnen).

[Ansicht der LMS Oberfläche] [Ansicht des Players]
Ansicht der Favoriten in der LMS Oberfläche und Darstellung auf dem Player, aktuell läuft SWR1 BW "Der Nachmittag".

Da mehrere Radioprogramme manuell einzugeben etwas umständlich ist und man insbesondere kein Logo zuordnen kann, habe ich direkt die Favoriten editiert und den LMS restartet. Hierzu muß man die Datei /var/lib/squeezeboxserver/prefs/favorites.opml auf dem LMS editieren. Will man, daß die Änderungen aktiv werden muß man den LMS restarten, vielleicht geht es auch einfacher, allerdings macht man das ja nicht so oft. Ein Eintrag in den Favoriten sieht dann z.B. wie folgt aus. Usernamen oder Passwörter braucht man nicht, das wird vom Apache rein über eine Accessliste gemacht

<outline URL="http://172.16.0.4/radio/swr1bw" icon="http://172.16.0.4/logos/SWR1-BW.png" text="SWR1 BW" type="audio" />

Die Logos habe ich mir direkt von den jeweiligen Infoseiten über das Radioprogramm bei der Wikipedia als png Datei heruntergeladen. Die können über den Apache mit ausgeliefert werden. Damit die Logos richtig funktionieren muß in den Defaulteinstellungen des LMS die IP Adresse der Icon-URL hart hereingeschrieben werden. Ansonsten wird der "Imageproxy" von mysqueezebox verwendet. Der kommt aber an einen Webserver den es nur im LAN gibt gar nicht heran. Im nächsten Abschnitt beschreibe ich eine Lösung hierfür, dann werden die Senderlogos auch in der Auswahl viel schneller dargestellt. Achtung: Wenn man sich in der favorites.opml vertippt, dann wird einem die Datei bei Start des logitechmediaservers zurückgesetzt. Schaltet man das Debugging für die favoriten an, kann man im Logfile sehen, welche Zeile ihm nicht passt.

Senderlogos für tvheadend und Squeezebox

Damit die Senderlogos gut sowohl in den Squeezeboxen als auch über tvheadend z.B. in kodi genutzt werden können sind noch folgende Einstellungen im tvheadend und im LMS sinnvoll Configuration->General (tvheadend) bzw. Einstellungen->Erweitert->Leistung (LMS):

[picon Dialog tvheadend] [LMS Einstellungen]
Einrichten der picons - für vlc, kodi etc.;Einrichten des lokalen Imageproxies im LMS

Ich habe ziemlich lange herumgerätselt was ein picon ist, wer es bereitstellt und wie das eingerichtet werden soll, bin aber der Meinung das es auf einem getrennten Webserver mit Channel-Namen benannt am besten übergreifend funktioniert.

Damit die piCore Player ("Squeezeboxen") die Icons in der Favoritenauswahl schnell laden können ist es sinnvoll den lokalen "Imageproxy" zu verwenden. Im Einstellungsdialog heißt der Punkt Plattenhüllen Grössenanpassung und sollte so eingestellt werden wie oben im Screenshot gezeigt. Leider hat die Android App Squeezer einen Bug wodurch die Bilder der Favoriten (also in meinem Falle die Senderlogos) dann nicht mehr auf dem Smartphone angezeigt werden. Mit folgendem Workaround im LMS (Version 7.9.0) werden auch in der App die Senderlogos angezeigt. Der Bug in der App besteht darin das "//imageproxy" aufgerufen wird, anstatt "/imageproxy" (mit einem Slash).

Vorteile dieses Setup sind folgende

Viel Erfolg