98_DLNARenderer.pm (UPnP) (zuvor 98_DLNAClient.pm)

Begonnen von dominik, 04 August 2015, 20:23:38

Vorheriges Thema - Nächstes Thema

Brockmann

Wie wird denn das Attribut ignoreUDNs richtig verwendet? Ich habe zwei UDNs mit einem Komma getrennt angegeben, aber zumindest das zweite Device taucht trotzdem wieder auf. Muss mit einem "|" getrennt werden?
Vielleicht sollte diese Information bei Gelegenheit auch in der Commandref ergänzt werden.

Weissbrotgrill

#376
Hallo Dominik,

nachdem ich gestern mehrere Stunden nach dem Problem gesucht und dann endlich die Lösung gefunden hatte konnte ich diese unmöglich für mich behalten.  :-)

Habe die neue Version getestet, funktionienrt. Allerdings erst nachdem ich meinen eigenen Tippfehler korrigiert habe:
http://relay3.slayradio.org:8000/
=> acht tausend, nicht achzig/achzig  ;)

Schön das du die Anpassungen der Channels übernommen hast. Denn dadurch lassen sich Album, Artist, Titel und sogar Albumcover mit setzen. Leider aktualisiert mein Yamaha dieses Infos nichts aus dem laufenden Stream. Im Display am Gerät steht alles ordentlich, aber via UPnP scheint er das nicht zu setzen. Auch BubbleUPnP zeigt da nichts atuelles an und mit den GUPnP-Tools gibt es auch keine aktuellen infos.. Naja, zum Glück zeigt YAMAHA_AVR diese fehlenden Infos an, so kann ich mir doch noch irgend wie eine nette Statusanzeige zusammenbauen.  ;)

Gruß
Christian

Weissbrotgrill

Hallo Brockmann,

Zitat von: Brockmann am 23 Juli 2016, 12:56:00
Wie wird denn das Attribut ignoreUDNs richtig verwendet?

Da ich gerade sowieso im Quellcode unterwegs bin habe ich dort mal nachgesehen.
Es muss die exakte UDN des Gerätes eingegeben werden. Trennzeichen ist offenbar egal. Die UDN ist eine UUID, sieht also z.B. wie folgt aus:
uuid:53dba25b-4e84-4705-9958-fa916a362146

Sie kann in den angelgeten Devices unter Internal nachgelesen werden. Ich habe dies eben mit zwei Geräten erfolgreich getestet. Als Trennzeichen habe ich einfach ein Leerzeichen genommen. Auch mit Komma als Trennzeichen werden die Geräte nicht angelegt.

@Dominik:
Wenn ein Gerät in ignoreUDNs steht gibt es im Verbose Log ein paar Fehler sobald dieses nicht mehr erreichbar ist:

2016.07.23 13:33:48 4: DLNARenderer: deviceRemoved, BubbleUPnP (SGP511)
2016.07.23 13:33:48 1: ERROR: empty name in readingsBeginUpdate
2016.07.23 13:33:48 3: stacktrace:
2016.07.23 13:33:48 3:     main::readingsBeginUpdate           called by fhem.pl (4186)
2016.07.23 13:33:48 3:     main::readingsSingleUpdate          called by ./FHEM/98_DLNARenderer.pm (1367)
2016.07.23 13:33:48 3:     main::DLNARenderer_removedDevice    called by ./FHEM/98_DLNARenderer.pm (1189)
2016.07.23 13:33:48 3:     main::DLNARenderer_discoverCallback called by ./FHEM/98_DLNARenderer.pm (1153)
2016.07.23 13:33:48 3:     main::__ANON__                      called by FHEM/lib/UPnP/ControlPoint.pm (972)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::Search::deviceRemoved called by FHEM/lib/UPnP/ControlPoint.pm (363)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::_deviceRemoved  called by FHEM/lib/UPnP/ControlPoint.pm (469)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::_receiveSSDPEvent called by FHEM/lib/UPnP/ControlPoint.pm (165)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::handleOnce      called by ./FHEM/98_DLNARenderer.pm (224)
2016.07.23 13:33:48 3:     (eval)                              called by ./FHEM/98_DLNARenderer.pm (223)
2016.07.23 13:33:48 3:     main::DLNARenderer_Read             called by fhem.pl (3199)
2016.07.23 13:33:48 3:     main::CallFn                        called by fhem.pl (667)
2016.07.23 13:33:48 1: readingsUpdate(,presence,offline) missed to call readingsBeginUpdate first.
2016.07.23 13:33:48 1: ERROR: empty name in readingsBeginUpdate
2016.07.23 13:33:48 3: stacktrace:
2016.07.23 13:33:48 3:     main::readingsBeginUpdate           called by fhem.pl (4186)
2016.07.23 13:33:48 3:     main::readingsSingleUpdate          called by ./FHEM/98_DLNARenderer.pm (1368)
2016.07.23 13:33:48 3:     main::DLNARenderer_removedDevice    called by ./FHEM/98_DLNARenderer.pm (1189)
2016.07.23 13:33:48 3:     main::DLNARenderer_discoverCallback called by ./FHEM/98_DLNARenderer.pm (1153)
2016.07.23 13:33:48 3:     main::__ANON__                      called by FHEM/lib/UPnP/ControlPoint.pm (972)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::Search::deviceRemoved called by FHEM/lib/UPnP/ControlPoint.pm (363)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::_deviceRemoved  called by FHEM/lib/UPnP/ControlPoint.pm (469)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::_receiveSSDPEvent called by FHEM/lib/UPnP/ControlPoint.pm (165)
2016.07.23 13:33:48 3:     UPnP::ControlPoint::handleOnce      called by ./FHEM/98_DLNARenderer.pm (224)
2016.07.23 13:33:48 3:     (eval)                              called by ./FHEM/98_DLNARenderer.pm (223)
2016.07.23 13:33:48 3:     main::DLNARenderer_Read             called by fhem.pl (3199)
2016.07.23 13:33:48 3:     main::CallFn                        called by fhem.pl (667)
2016.07.23 13:33:48 1: readingsUpdate(,state,offline) missed to call readingsBeginUpdate first.


Sieht so aus als sollen hier ein paar Readings aktualisiert werden die nicht existieren.

Gruß
Christian

Weissbrotgrill

Hallo Dominik,

bei deiner Änderung das nun die DIDL-Lite Metadaten bei "set ... stream ..." generiert werden gibt es ein Problem.
Das sind Metadaten einer MPEG Audiodatei. Selbstverständlich gibt es dann Probleme mit allen anderen Dateitypen wie AAC, OGG, MPEG, MP4, MKV, ....

Um das also richtig ordentlich zu machen müsste das Modul die URL aufrufen und die Metadaten ermitteln um diese dann zu übergeben.
Ich habe ein wenig mit LWP::Useragent experimentiert und denke das damit ausreichend Informationen abrufbar sind. Aber es gibt hoffentlich schon eine Perl Klasse für sowas ;)

Für eine Videodatei müssten in der DIDL-Lite folgende Änderungen vorgenommen werden:
<upnp:class>object.item.videoItem</upnp:class>

Und das XML Element "res" ändert sich (Beispiel einer Matroska Datei):
<res protocolInfo="http-get:*:video/x-matroska:DLNA.ORG_FLAGS=01700000000000000000000000000000;DLNA.ORG_OP=01">URL entfernt</res>

Gruß
Christian

dominik

Hallo Christian,

die Analyse des Streams muss ich mir noch genauer überlegen wie man das vernünftig löst. FFmpeg hätte ich als Library gefunden die passen könnte. Wie greift BubbleUPnP auf die Radio Streams zu? Über Shoutcast?

Werde mal nur die Änderung über channel_X einchecken und die Option mit "set ... stream ..." vorerst entfernen.

Gruß,
Dominik
fhempy -  https://github.com/fhempy/fhempy: GoogleCast, Tuya, UPnP, Ring, EQ3BT, Nespresso, Xiaomi, Spotify, Object Detection, ...
Kaffeespende: https://paypal.me/todominik

Weissbrotgrill

Hallo Dominik,

wenn ich in BubbleUPnP eine HTTP Stream-URL zur Playliste hinzufüge ruft BubbleUPnP diese sofort ab. Neben den Headern werden noch ein paar Stream-Daten abgerufen und dann bricht BubbleUPnP ab. Der Zugriff erfolgt über simples HTTP.

Ich bin mir nicht sicher ob der Stream wirklich mit ffmpeg analysiert werden muss. Evtl. reichte es aus den Content-Type aus dem HTTP Responce Header zu nehmen.
Hier ein kleines Testscript um die DIDL-Lite Metadaten aus den HTTP Responce Headern zu generieren. Einfach die Stream-URL als Übergabeparameter mitgeben.

#!/usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;

#my $stream = "http://relay4.slayradio.org:8000/";
my $stream = $ARGV[0];

my $ua = new LWP::UserAgent;
$ua->max_size(0);
my $resp = $ua->get($stream);
print $resp->headers_as_string, "\n";

my $didl_header = '<DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/" xmlns:sec="http://www.sec.co.kr/"><item id="-1" parentID="parent" restricted="1">';
my $didl_footer = '</item></DIDL-Lite>';

$stream =~ s/&/&amp;/g;

my $size = "";
my $protocolInfo = "";
my $album = $stream;
my $title = $stream;
my $meta = "";

if (defined($resp->header('content-length'))) {
  $size = ' size="'.$resp->header('content-length').'"';
}

if (defined($resp->header('contentfeatures.dlna.org'))) {
  $protocolInfo = "http-get:*:".$resp->header('content-type').":".$resp->header('contentfeatures.dlna.org');
} else {
  $protocolInfo = "http-get:*:".$resp->header('content-type').":DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000";
}

if (defined($resp->header('icy-name'))) {
  $album = $resp->header('icy-name');
}

if (defined($resp->header('icy-genre'))) {
  $title = $resp->header('icy-genre');
}

if (substr($resp->header('content-type'),0,5) eq "audio") {
  $meta = $didl_header.'<upnp:class>object.item.audioItem.musicTrack</upnp:class><dc:title>'.$title.'</dc:title><upnp:album>'.$album.'</upnp:album><res protocolInfo="'.$protocolInfo.'"'.$size.'>'.$stream.'</res>'.$didl_footer;
} elsif (substr($resp->header('content-type'),0,5) eq "video") {
  $meta = $didl_header.'<upnp:class>object.item.videoItem</upnp:class><dc:title>'.$title.'</dc:title><upnp:album>'.$album.'</upnp:album><res protocolInfo="'.$protocolInfo.'"'.$size.'>'.$stream.'</res>'.$didl_footer;
} else {
  $meta = "";
}

print $meta, "\n";


Bisher akzeptiert mein Panasonic TV und mein Yamaha AVR jede damit generierte DIDL-Lite Metadaten.

  • AVR: MP3 Datei vom lokalen Webserver
  • AVR: diverse MP3 Streams
  • TV: MP4 Datei von einem Minidlna Server
  • TV: MPEG Datei von einem Minidlna Server
  • TV: MKV Datei von einem Minidlna Server
  • TV: MP4 Internet Stream

Beim Generieren der XML Daten sollte ggf. ein passendes Perl Modul verwendet werden damit ein korrektes XML heraus kommt. So muss z.B: aus "&" ein "&amp;" werden.

Gruß
Christian

dominik

Genial Christian! :)

Werde das mal bei mir für ein paar Stream probieren. Wenn es klappt, übernehme ich gerne deinen Testcode ins Modul.

Gruß,
Dominik
fhempy -  https://github.com/fhempy/fhempy: GoogleCast, Tuya, UPnP, Ring, EQ3BT, Nespresso, Xiaomi, Spotify, Object Detection, ...
Kaffeespende: https://paypal.me/todominik

Weissbrotgrill

Hallo Dominik,

freut mich das es dir gefällt.

Bitte zwei Dinge beachten:

  • Der HTTP Aufruf dauert eine gewisse Zeit, ich hoffe das blockiert den FHEM nicht.
  • Werte in die XML Struktur bitte mittels encode_entities() schreiben.
Gruß
Christian

Brockmann

Zitat von: Weissbrotgrill am 23 Juli 2016, 13:41:14
Es muss die exakte UDN des Gerätes eingegeben werden. Trennzeichen ist offenbar egal. Die UDN ist eine UUID, sieht also z.B. wie folgt aus:
uuid:53dba25b-4e84-4705-9958-fa916a362146
Ich habe es nochmal probiert. Wichtig ist, dass man nicht die reine ID nimmt, sondern das "uuid:" davor belässt. Dann klappt es auch mit dem Ignorieren. Das war mein Fehler bzw. meine Fehlannahme, weshalb es nicht auf Anhieb geklappt hat.

DefaultRoom funktioniert übrigens prima, vielen Dank dafür!

Aber mal eine Frage zu den Attributen generell: Warum werden DefaultRoom und ignoreUDNs als userattr definiert? Das Modul DLNARenderer kann doch seine eigenen Attribute festlegen. userattr werden nach meinem Verständnis vom Benutzer angelegt, entweder global oder nur für bestimmte Devices. Also es funktioniert so natürlich auch, aber es scheint mir etwas unkonventionell zu sein.

dominik

@Brockmann
Das mit "uuid:" nehme ich in die CommandRef auf.

Ich habe die Attribute in userattr aufgenommen, da ich sonst keine Möglichkeit gefunden habe die Attribute nur im Main Device anzuzeigen und nicht in den Play Devices. Wenn du eine andere Möglichkeit kennst, baue ich das gerne an, da mir die aktuelle Lösung auch nicht gefällt.

@Christian
Kannst du mal deinen Player mit folgender URI (http://mp3-live.swr3.de/swr3_m.m3u) und die aus deinem Testscript generierte DIDL-Lite XML versorgen? Da kommt dann nämlich audio/x-mpegurl als MIME Type. Bin mir nicht sicher ob die Player damit umgehen können. Des Weiteren dürften die Attribute für Icy-* nur bei Icecast Servern vorliegen. Wenn also kein Icecast Server verwendet wird, bleibt Title/Album = URI, wäre für mich auch ok.
fhempy -  https://github.com/fhempy/fhempy: GoogleCast, Tuya, UPnP, Ring, EQ3BT, Nespresso, Xiaomi, Spotify, Object Detection, ...
Kaffeespende: https://paypal.me/todominik

Weissbrotgrill

Hallo Dominik,

Zitat von: dominik am 25 Juli 2016, 18:43:17
Kannst du mal deinen Player mit folgender URI (http://mp3-live.swr3.de/swr3_m.m3u) und die aus deinem Testscript generierte DIDL-Lite XML versorgen? Da kommt dann nämlich audio/x-mpegurl als MIME Type. Bin mir nicht sicher ob die Player damit umgehen können.
Ich bin schwer überrascht, das funktioniert. Damit habe ich wirklich nicht gerechnet.
Das ist natürlich nur ein einzelner Test mit einem Endgerät und somit nicht unbedingt aussagekräftig.

Zitat von: dominik am 25 Juli 2016, 18:43:17
Des Weiteren dürften die Attribute für Icy-* nur bei Icecast Servern vorliegen. Wenn also kein Icecast Server verwendet wird, bleibt Title/Album = URI, wäre für mich auch ok.
Immer noch besser als nicht :-)

Gruß
Christian

dominik

Hi Christian,

kannst du die Version anbei mal bei dir mit ein paar URIs testen? Habe die DIDL-Lite Generierung mit einem BlockingCall implementiert um sicher zu gehen, dass FHEM nicht blockiert wird.

Gruß,
Dominik
fhempy -  https://github.com/fhempy/fhempy: GoogleCast, Tuya, UPnP, Ring, EQ3BT, Nespresso, Xiaomi, Spotify, Object Detection, ...
Kaffeespende: https://paypal.me/todominik

Brockmann

Zitat von: dominik am 25 Juli 2016, 18:43:17
Ich habe die Attribute in userattr aufgenommen, da ich sonst keine Möglichkeit gefunden habe die Attribute nur im Main Device anzuzeigen und nicht in den Play Devices. , bleibt Title/Album = URI, wäre für mich auch ok.
Ah, verstehe das Problem. Nee, dafür gibt es in der Tat keine (einfache) andere Lösung. Im Grunde genommen müsstest Du trennen zwischen dem zentralen Device, das die DLNA-Geräte sucht und erstellt und den DLNA-Renderern selbst. Also quasi ein Modul DLNARendererCtrl für die zentrale Instanz und ein Modul DLNARenderer für die erstellten Geräte. Aber der Aufwand wäre natürlich Quatsch, nur um die Attribute aufzuhübschen. Sind ja wirklich nur "Schönheitsfehler".

Weissbrotgrill

Hallo,

alle Tests mit "set player stream http....".

mehrere MP3 Stream URIs => OK
mehrere MP3 Stream URIs in M3U Dateien => OK
mehrere AAC Stream URIs => will mein Yamaha nicht
mehrere AAC Stream URIs in M3U Dateien => OK, verrückt
PLS Dateien => mag mein Yamaha nicht
OGG Vorbis => OK, nachdem ich "application/ogg" hinzugefügt habe

Bei den Test sind mir ein paar Kleinigkeiten aufgefallen die ich dann korrigiert habe:

1460d1459
<   print $resp->headers_as_string, "\n";
1476a1476,1478
>   my @header = split /;/, $resp->header('content-type');
>   my $contenttype = $header[0];
>
1478c1480
<     $protocolInfo = "http-get:*:".$resp->header('content-type').":".$resp->header('contentfeatures.dlna.org');
---
>     $protocolInfo = "http-get:*:".$contenttype.":".$resp->header('contentfeatures.dlna.org');
1480c1482
<     $protocolInfo = "http-get:*:".$resp->header('content-type').":DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000";
---
>     $protocolInfo = "http-get:*:".$contenttype.":DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01700000000000000000000000000000";
1484c1486
<     $album = $resp->header('icy-name');
---
>     $album = encode_entities($resp->header('icy-name'));
1488c1490
<     $title = $resp->header('icy-genre');
---
>     $title = encode_entities($resp->header('icy-genre'));
1491c1493
<   if (substr($resp->header('content-type'),0,5) eq "audio") {
---
>   if (substr($contenttype,0,5) eq "audio" or $contenttype eq "application/ogg") {
1493c1495
<   } elsif (substr($resp->header('content-type'),0,5) eq "video") {
---
>   } elsif (substr($contenttype,0,5) eq "video") {


  • Debugausgabe headers_as_string entfernt
  • Ogg Vorbis (application/ogg) als Audio hinzugefügt
  • Album und Titel aus Icy Metatags auch per encode_entities
  • Der Header content-type kann auch den verwendeten Zeichensatz enthalten, dies führt im DIDL-Lite aber zu Fehlern

Danach habe ich noch diverse andere Streams ausprobiert und konnte bisher keine Probleme mehr erkennen.

Gruß
Christian

alpha1974

Werte FHEMler,

kann es sein, dass das Modul den gesamten FHEM-Server gelegentlich blockiert und deshalb u.a. die Web-Oberfläche vorübergehend nicht erreichbar ist?

Hintergrund meiner Frage: Ich habe das zum Modul gehörende Device im Urlaub eingerichtet (via VPN) und es lief seitdem alles problemlos. Unter Unsorted wurde der einzige DLNA-Server in meinem Netzwerk gefunden und eingerichtet.

Aus dem Urlaub zurück starte ich meinen Laptop mit Windows 10 mit aktivierter Medienfreigabe und seitdem "hängt" FHEM gelegentlich.
Im Log erscheint:
PERL WARNING: Loading device description failed with error: 500 Can't connect to 192.168.178.23:2869 at ./FHEM/98_DLNARenderer.pm line 220.

Meine Vermutung: Der Laptop, auf dem die Medienfreigabe aktiviert ist (nunmehr: war), schickte ein DLNA-Advertising ins lokale Netzwerk und das DLNARenderer-Modul versuchte, darauf zu reagieren. Allerdings konnte der Raspberry, auf dem FHEM läuft, den Laptop nicht aktiv erreichen, weil beide in unterschiedlichen Netzen sind und die Firewall dafür sorgt, dass nur der Laptop (= DLNA-Server) in das Subnetz des Raspberry kommt, aber nicht umgekehrt.

Kann das dazu führen, dass FHEM geblockt wird? Ich habe jetzt auf dem Win10-Laptop die Medienfreigabe und die Netzwerkerkennung deaktiviert und seitdem kommt die o.g. Fehlermeldung nicht mehr und die FHEM-Weboberfläche ist auch durchgehend erreichbar.

Gruß,
alpha1974
FHEM/Z-Wave USB-Dongle + div. Devices