UPNP/DLNA for IPS: basics and first how to

Hallo Gemeinde,

aufgrund mehrerer Anfragen stelle ich Euch heute meinen aktuellen Entwicklungsstand zu DLNA für IPS zur Verfügung. Diese ist zugegebenermassen sehr komplex und steckt noch in den Kinderschuhen, stellt aber schon einen rudimentären ControlPoint für UPNP/DLNA dar.

Zur Erklärung:

Ziel ist es einen UPNP/DLNA Control Point auf einem Server zu haben (der ehedem 24/7 läuft) und mir ein Web-basiertes Frontend zur Verfügung stellt, auf das ich mit meinen Tablets oder Smartphone sozusagen als Fernsteuerung zugreifen kann. Im Gegensatz zu einer Insellösung (d.h. Control Point auf jedem Gerät) können sich so die Geräte nicht ins Gehege kommen.
Ich habe vorher lange danach gesucht, aber leider gibt es zwar sehr taugliche stand alone Control Points (Kinsky, Asset Control, WMP Play To, Link 3+, etc.) aber eben nicht das, was ich will.

Lange Zeit trat ich auf der Stelle und bekam Rückkopplungen, dass es zu spezifisch und nur für meine Gerätekonfiguration ist. Das stimmte, da DLNA/UPNP eben nur ein grober Standard ist. Jeder Hersteller macht hier sein eigenes Ding und lässt die Geräte in seinem eigenen Slang plappern. Manchmal glaube ich, dass ist extra so gewollt, um bei einem Hersteller zu bleiben, da das einfach Probleme macht. So hatte ich erst alles auf meine Sony Audio-Geräte ausgerichtet.

Vor wenigen Wochen stieß ich dann endlich auf eine funktionierende Methode für PHP einen SSDP-Request ins Netzwerk zu senden und erhalte so alle Server und Renderer als Antwort, deren Spezifika (vorallem IP, Port und die Control-URL´s) ich weiter verarbeiten kann !!! :smiley: So hat man die Möglichkeit die Antworten der Devices (XML) auszulesen und die leidige händische Anpassung von Steuerungs-URL´s, IP´s und Port´s abzuschaffen.
Für alle die damit nichts anfangen können: per SSDP (Simple Service Discovery Protocol) schicke ich eine Nachricht ins Netzwerk, die man auf bestimmte Suchparameter einengen kann („ssdp:all“; „upnp:rootdevice“; „urn:dial-multiscreen-org:service:dial:1“; „urn:schemas-upnp-org:device:MediaServer:1“; „urn:schemas-upnp-org:service:ContentDirectory:1“; „urn:schemas-upnp-org:service:RenderingControl:1“; etc.).
Dieser sehr wichtige Teil steckt in UPNP_Discovery_Functions.ips.php. Ich hatte erst eine Version über einen Multicast-Socket, diese aber wieder verlassen. So ergab sich dann Eines zum Anderen. Eine Hürde war auch, warum manche Geräte nichts machen oder abstürzen (z.B. mein LG-TV). Hier war die Lösung, dass dort unbedingt die DIDL als Metadata übergeben werden muss.
Die einzelnen Scripte (insbesondere die UPNP_Functions_V2.3.ips.php als Kernstück) haben viele Evolutionsstufen durchlaufen.

Dies hier ist wie gesagt nur ein Anfang, der das Prinzip darstellt. UPNP hat viel Potential.

Was funktioniert ?

  • vorhandenes Netzwerk-Device selektieren
  • Netzwerk-Server mit Audio-Inhalt selektieren
  • durch dessen Ebenen browsen
  • ein selektieren eines Titels löst das Abspielen aus
  • mehrere Devices können gleichzeitig angesteuert werden
  • Lautstärke für das selektierte Device
  • Anzeige wesentlicher (fast aller) Daten zum Titel mit AlbumArt insofern vom Server bereitgestellt

Das Beispiel:

Im linken Teil des Screenshot stecken:

  • iFrame der Server_Suche: Diese wird von UPNP_Select_Server.ips.php erstellt, die IP, Port, Control-URL, Icon, etc. ausgelesen und mitgegeben. Sie wird minütlich ausgeführt.

  • iFrame der Device-Suche: Ähnlich der Server-Suche mit Ermittlung der notwendigen Parameter/Variablen durch UPNP_Select_Device.ips.php

  • iFrame Browse-Verlauf: So kann man auf verschieden Ebenen der Suche zurückspringen. Das Script steckt in Webfront/user/Browse/UPNP_Browse_Verlauf_WF.php

  • iFrame Browse Verzeichnisse/Dateien: Hier das eigentliche Browsen durch die Verzeichnisse (über BrowseDirectChildren, Suchparameter “*”). Hier ist die UPNP_Browse_Functions.ips.php essentiell.

Der rechte Teil ist, denke ich, selbsterklärend.

Kernstück ist die UPNP_Functions_V2.3.ips.php in der alle UPNP-Befehle stecken, zu denen die passenden Variablen (die Namen sind selbsterklärend) übergeben werden müssen.

Erfolgreich getestet habe ich bisher:

Server

  • Twonky
  • Asset UPNP
  • Sony Media Library
  • Cyberlink

Renderer (links zweite Zeile):

  • alle meine Sony Devices (STR-DN2010, SA-NS400, SA-NS410, SA-NS300, SA-NS310)
  • LG TV (LM660S)
  • PowerDVD (anderes Notebook)

Installation:

Ich habe wieder versucht, die Scripte/Variablen per Raketenschneckes ProjectExporter für Euch zusammenzustellen.

[ul]
[li]BITTE UNBEDINGT VORHER EIN BACKUP EURES IPS MACHEN. BESSER NOCH EIN TESTSYSTEM BENUTZEN. ICH KANN NICHT GEWÄHRLEISTEN, DASS GGF. EINE ID ÜBERSCHRIEBEN WIRD ![/li][li]Das File aus dem Projectexporter in ein leeres Script kopieren und starten (Zum Nachlesen: http://www.raketenschnecke.net/rs-projekte/rs-ips-project-exporter/)[/li][li]Die Scripte für das Webfront habe ich zusätzlich in die Objektbaum-Kategorie webfront/user/Browse/link bzw. webfront/user/Discovery/link gesteckt. Also das ZIP-File nach Webfront/user extrahieren (vgl. Browse.jpg) und dann die Scripte im Objectbaum unter “Scripte” per Hand in die jeweils gleichnamige Datei im Webfront-Verzeichnis kopieren. Das ist leider notwendig, da sonst zu viele Variablen nicht angepasst werden. Eine Installationsroutine wird irgendwann mal folgen [/li][li]Wenn alles gut geht wurden alle ID´s der Variablen übernommen. Wenn nicht, bitte bei eben diesen ID´s mit der Fehlersuche starten.[/li][li]Der Objektbaum sollte dann so aussehen wie in den Screenshots (ebenfalls die Struktur des WF bzw. so ähnlich[/li][li]Jeweils einmal die UPNP_Select_Device.ips.php und UPNP_Select_Server.ips.php ausführen und die Rückgabe ansehen – wenn etwas kommt (DeviceArrays) ist das die halbe Miete ![/li][li]Ich hoffe so lässt sich das für Euch portieren, wenn nicht muss ich mich doch über ein Install hermachen.[/li][/ul]

Die Screenshots des Objektbaumes und der Verzeichnisse liegen in Post 2, da ich hier auf 10 Anhänge limitiert bin.

Die Dateien:

Webfront-user.zip (19.9 KB)

UPNP-Install.zip (41.9 KB)

To do:

[ul]
[li]Bugs ausmerzen [/li][li]Die Scripte sind grob behauen und stecken noch voller Echos zur Fehlerkontrolle[/li][li]Derzeit wird testweise 1 Titel übergeben. Habe angefangen die Gesamtzeit und Restzeit abzufragen und will dann bei “=0” den nächsten Titel aus $Liste (das TitelArray) übergeben. Oder andere Ideen ?[/li][li]Momentan liest er WMP nicht aus. Ein $_GET parsed mir ein “+” in der Control-URL weg, welches ich dann wieder hinzufügte. Das funktioniert aber nicht wirklich anständig.[/li][li]Playlist-Erstellung z.B. mit Buttons an denen man ein Häkchen setzen kann. Hier vielleicht ein Input-Button über einem Select-Button ? [/li][li]Radio von Twonky funktioniert nicht – ich weiss noch nicht warum.[/li][li]Minütliche Server und Device - Suche ist sicher besser zu lösen, da die Anzeigen manchmal wechseln. Ich denke hier an ein stetiges horchen an Port 1900, um die vom Gerät gemeldeten “alive”-Meldungen abzufangen und so die Anzeige nur bei Änderungen zu erneuern.[/li][li]Geeignete Visualisierung [/li][li]Videos zum Laufen bringen[/li][li]Weitere Funktionen implementieren[/li][li]Ich habe in den letzten 1,5 Jahren mit IPS aus Spass an der Freude viel PHP und HTML in mich aufgesogen, bin aber weit von einem Programmierer entfernt. Es wäre also schön, wenn jemand, der richtig in der Materie steckt über die Codezeilen sehen könnte – hier ist einiges sicher besser zu lösen.[/li][/ul]

Da ich momentan beruflich sehr eingespannt bin, will ich Euch diesen Ansatz zumindest bereitstellen und muss dann erstmal eine Pause machen. Über Fragen und Anregungen würde ich mich freuen. Noch mehr aber über Leute, die sich auch dafür interessieren :slight_smile:

@ZipFam: entwickelst Du mit ?
@Tonic: besonders über Deinen Input würde ich mich freuen !
@BerndJ: versuchs mal damit
@NickBlue: wie hast Du LG und LAN gelöst. Gehen Videos ?

Viele Grüße

André

Was ist im Webfront ?

Browse-image.jpgDiscovery-image.jpg

Der Objectbaum:

Die von Hand in das Webfront zu kopierenden Dateien:

Die Kurz-Doku:

IPS Forum UPNP.pdf (350 KB)

Hallo Andre,

Super Klasse - Wirklich erstklassige Arbeit… Habe mich heute morgen schon an die Installation gewagt. Klappt soweit auch sehr gut.

Den ersten Unterschied habe ich entdeckt … bei Dir liegt der Hauptordner \IP-Symcon auf Laufwerk D:. Bei mir liegt er auf c:… Ergo habe ich sowohl upnp_browse_wf.ips.php und upnp_browse_verlauf_wf.ips.php als auch das Installations-Skript angepasst.

Nach der Installation noch Webfront anpassen und ausprobieren.

So, Devices und Server werden tadellos erkannt und die HMTLs angepasst.

Jetzt muss ich erst einmal los…

Bis später…

Klar helfe ich Dir, wenn ich kann (so wie es aussieht musst Du mir eher helfen…)

Grüße

Hi ZipFam,

schön zu hören, dass es nach kleineren Anpassungen klappt. Habe auch lang genug dazu gebraucht :smiley:

Habe gerade folgendes überlegt: Eigentlich sollten 2 Dinge oben auf to do stehen:

  1. Titelweiterschaltung bzw. Playlist

    Hier müsste eigentlich bei erreichter Restzeit = 0 die $Titellist um 1 hochgesetzt werden.
    Playlisterstellung sollte über eine XML gehen, die dann als $Result --> $liste verarbeitet werden kann

  2. Beim Umschalten auf ein anderes Device den aktuell abgespielten Titel anzeigen.

    Hier am ehesten nur über Getpositioninfo

Dann wäre das schon ein Control Point, der alles kann, was die „stand alones“ können.

Bin immer wieder fasziniert, wie schön man über DLNA Musik auf die Stereoanlage (da hängt mein Bravia auch dran) mit allem Drum und Dran wie Albumart, Titel etc. zaubern kann.
Super wären halt auch noch Videos. Die UPNP_Functions_V2.3.ips.php beinhaltet alle relevanten Befehle (und noch mehr), aber hinbekommen habe ich das noch nicht.

Gruß

André

Hallo André,

leider funktionier es zurzeit noch nicht 100%ig.

In der Zwischenzeit habe ich auch noch herausgefunden, dass man die Variablen in den Skripten anpassen muss. Bei der Installation mittels Skript wurden bei mir die Skripte angepasst und in den Installationsordner …/scripts mit ihrer Objekt-ID abgelegt. IPS ruft jedoch die Skripte aus den Ordnern …\webfront\user\browse\link … und …\webfront\user\discover\link\ ab. Also die richtigen Skripte aus dem Verzeichnis \scripts in die entsprechenden Ordner in webfront kopieren und umbenennen.

Die entsprechende Zuordnung findet Ihr beispielhaft hier:

So, nun bin ich ein ganzes Stück weitergekommen.

Leider bekomme ich immer noch eine Fehlermeldung beim Starten einer Musikdatei…

Später dazu mehr…

Grüße

Sascha

Hi Sascha,

werden die Variablen verändert ? Werden die Devices und Server erkannt ? Das bei Dir zum Laufen zu bringen hat erstmal Priorität !

Gruß

André

Hi Sascha,

es lässt mir keine Ruhe. Welche Fehlermeldung bekommst Du ?
Du hast ja auch Twonky: wird derselbe Titel auch über z.B. Kinsky abgespielt ?

Gruß

André

Hallo Andre, so nun werden Musk-Dateien gestreamt und richtig abgespielt… Echt super… Was noch nicht richtig funktioniert ist die Anzeige der Rest Länge und manchmal funktioniert die Auswahl der Devices oder der Server erst nach Neustart des Explorers… Morgen früh weiss ich vielleicht mehr…Grüße

Gesendet von meinem GT-I9505 mit Tapatalk 4

Hallo Sascha,

sehr gut. Freut mich, dass Du das reproduzieren konntest und es auch bei Dir funktioniert.

Was musstest du alles ändern ?

Die Server und Device-Abfrage minütlich sollte anders gelöst werden, da bei Traffic die Abfrage blockiert und dementsprechend die Anzeige vorübergehend verschwindet. Aber, und das ist wichtig, das Grundprinzip funktioniert.

Ich habe mich doch wieder an den Multicast-Socket gesetzt, da die Abfrage über einen offenen Socket mir stabiler vorkommt. Der Output $DeviceArray bleibt gleich, so dass wir das jederzeit austauschen können. Muss es ein bisschen beobachten.

Restzeit = 0 oder besser wenn $RelTime die $TrackDuration erreicht sollte einen Titel weiterschalten. Habe das aber noch nicht hinbekommen. Aktueller Versuch:

<?

//------------------------------------------------------------------------------
//UPNP_NextTitel.ips.php----------------------------------------------------
//------------------------------------------------------------------------------

//include Functions-------------------------------------------------------------

include ("UPNP_Functions_V2.3.ips.php");

//aktuellen Status abfragen-----------------------------------------------------

$RelTime = GetValue(13956 /*[Audio\Variablen\GetPositionInfo\RelTime]*/);
$TrackDuration = GetValue(19680 /*[Audio\Variablen\GetPositionInfo\TrackDuration]*/);
$Playing = GetValue(28600 /*[Audio\Variablen\GetTransportInfo\CurrentTransportState]*/);

$start_time_o  = $RelTime;
$end_time_o    = $TrackDuration;

$Progress = get_time_difference($start_time_o, $end_time_o);

function get_time_difference($start_time_o, $end_time_o)
	{
    $start_time = explode(":", $start_time_o);
	 print_r ($start_time);
    $end_time = explode(":", $end_time_o);
	 print_r ($end_time);

    $start_time_stamp = mktime($start_time[0], $start_time[1], $start_time[2], 0, 0, 0);
    $end_time_stamp = mktime($end_time[0], $end_time[1], $end_time[2], 0, 0, 0);

	 $Progress = round($start_time_stamp * 100 / $end_time_stamp);

	 echo ($Progress);
	}

SetValue(58154 /*[Audio\Browse\Progress]*/, $Progress);

if ($Playing == "PLAYING")
	{
	if ($RelTime == $TrackDuration)
		{
		$TitelListCurrent	= GetValue(30978 /*[Audio\Browse\Selected File]*/);
		$TitelListNext    = ($TitelListCurrent++);
		$_GET = "Number=$TitelListNext";
		include("D:/IP-Symcon/webfront/user/Browse/link/UPNP_Play_WF.php");
		}
	}

?>

Der Teil mit Progress soll mal einen Fortschrittsbalken füllen.

Man könnte vorher auch noch $SetNextAVTransportURI vorher einbauen, dann wäre das Abspielen Gap-less.

So, Schluss für heute

Gruß

André

… so Radio funktioniert jetzt.

Bereitgestellt von Asset-UPNP ! Das ist einfach der Beste UPNP AUDIO SERVER, den ich kenne. Viel zu konfigurieren und vor allem eine Anbindung an TuneIn - Radio.

Das WF ist nur Testweise zugegeben etwas unaufgeräumt :wink:
Der neue iFrame (senkrecht) soll mal die Playlist werden.

Dazu musste ich die UPNP_Browse_Functions.ips.php und UPNP_Browse_WF.php nochmal überarbeiten.

Hier die Neue UPNP_Browse_Functions_V1.6 (in user/Browse/link/UPNP_Browse_Functions.ips.php kopieren). Darin steckt die Abfrage an den Server als function ContentDirectory_Browse mit der Aufsplittung der zurückgegebenen XML welche zunächst das Array $Result ergibt. Daraus wird nach Auswertung der einzelnen Tags ein Array $liste mit allen verfügbaren Parametern (ID´s, Metadaten, Quelle, Quelle des AlbumArt, etc.)

<?

//------------------------------------------------------------------------------
//UPNP_Browse_Functions.ips.php-------------------------------------------------
//UPNP_Browse_Functions_V1.6----------------------------------------------------
//21.08.2013--------------------------------------------------------------------
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//function ContentDirectory_Browse----------------------------------------------
//------------------------------------------------------------------------------
//IN:   $ServerContentDirectory
//IN:   $ServerIP
//IN:   $ServerPort
//------------------------------------------------------------------------------
//IN:   $ObjectID
//IN:   $BrowseFlag
//IN:   $Filter
//IN:   $StartingIndex
//IN:   $RequestedCount
//IN:   $SortCriteria
//------------------------------------------------------------------------------
//OUT:  $Result
//OUT:  $NumberReturned
//OUT:  $TotalMatches
//OUT:  $TotalMatches
//------------------------------------------------------------------------------

function ContentDirectory_Browse ($ServerContentDirectory, $ServerIP, $ServerPort, $ObjectID, $BrowseFlag, $Filter, $StartingIndex, $RequestedCount, $SortCriteria)
{
$header='POST '.$ServerContentDirectory.' HTTP/1.1
HOST: '.$ServerIP.':'.$ServerPort.'
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
connection: close';

$xml='<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
   <s:Body>
      <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
         <ObjectID>'.$ObjectID.'</ObjectID>
         <BrowseFlag>'.$BrowseFlag.'</BrowseFlag>
         <Filter>'.$Filter.'</Filter>
         <StartingIndex>'.$StartingIndex.'</StartingIndex>
         <RequestedCount>'.$RequestedCount.'</RequestedCount>
         <SortCriteria>'.$SortCriteria.'</SortCriteria>
      </u:Browse>
   </s:Body>
</s:Envelope>';

$content_ContentDirectory_Browse = $header . '
Content-Length: '. strlen($xml) .'

'. $xml;

$fp = fsockopen ($ServerIP, $ServerPort, $errno, $errstr, 10);

fputs ($fp, $content_ContentDirectory_Browse);

		$buffer = stream_get_contents($fp, -1);

		echo $buffer;

		$vars = explode("

", $buffer); 						//Header abtrennen
		$header = $vars[0];                    					//Header
		$message = $vars[1];                   					//Message

		$handle = fopen('ContentDirectory_Browse.xml', "w"); 	//XML schreiben
		fwrite($handle, $message);
		fclose($handle);

fclose($fp);

//XML cutten und Variablen schreiben--------------------------------------------

		$vars1 = explode("<Result>", $message);
		$cutted1 = $vars1[0];
		$cutted2 = $vars1[1];
		$vars2 = explode("</Result>", $cutted2);
      $cutted3 = $vars2[1];
		$Result = $vars2[0];

		echo $Result;

	SetValue(42199 /*[Audio\Browse\Return\Result]*/, $Result);

		$vars1 = explode("<NumberReturned>", $message);
      $cutted1 = $vars1[0];
		$cutted2 = $vars1[1];
		$vars2 = explode("</NumberReturned>", $cutted2);
		$cutted3 = $vars2[1];
		$NumberReturned = $vars2[0];

		echo $NumberReturned;

	SetValue(26946 /*[Audio\Browse\Return\NumberReturned]*/, $NumberReturned);

		$vars1 = explode("<TotalMatches>", $message);
		$cutted1 = $vars1[0];
		$cutted2 = $vars1[1];
		$vars2 = explode("</TotalMatches>", $cutted2);
		$cutted3 = $vars2[1];
		$TotalMatches = $vars2[0];

		echo $TotalMatches;

	SetValue(52875 /*[Audio\Browse\Return\TotalMatches]*/, $TotalMatches);

		$vars1 = explode("<UpdateID>", $message);
      $cutted1 = $vars1[0];
		$cutted2 = $vars1[1];
		$vars2 = explode("</UpdateID>", $cutted2);
      $cutted3 = $vars2[1];
		$UpdateID = $vars2[0];

		echo $UpdateID;

	SetValue(53319 /*[Audio\Browse\Return\UpdateID]*/, $UpdateID);

Return ($Result);
}

//------------------------------------------------------------------------------
//function BrowseList($Result) --> $Results in Arrays ausgeben------------------
//------------------------------------------------------------------------------
//Sortierung:-------------------------------------------------------------------
//------------------------------------------------------------------------------
//01: container/item
//02: id
//03: refID
//04: parentid
//05: restricted
//06: artist
//07: album
//08: title
//09: resource
//10: duration
//11: size
//12: bitrate
//13: albumArtURI
//14: genre
//15: date
//16: originalTrackNumber
//17: class
//18: extension
//------------------------------------------------------------------------------

function BrowseList($Result)
{
$xmlParser = xml_parser_create();
$Result = substr($Result, stripos($Result, '<'));
$Result = substr($Result, 0, strrpos($Result, '>') + 4);
$Result = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $Result);

$xml = new SimpleXMLElement($Result);
$liste = array();

for($i=0,$size=count($xml);$i<$size;$i++)

	//Ereignisbaum Verzeichnis(container)/Musik(item)----------------------------
	//Wenn Container vorhanden, dann ist es ein Browse Element-------------------
	//Wenn Item vorhanden, dann ist es ein Song----------------------------------
	{
	if(isset($xml->container[$i]))
      {
		//Container vorhanden also Verzeichnis------------------------------------
		$aktrow = $xml->container[$i];
		$attr = $xml->container[$i]->attributes();
		$liste[$i]['typ'] = "container";
		}
	else if(isset($xml->item[$i]))
		{
		//Item vorhanden also nur noch Musik--------------------------------------
		$aktrow = $xml->item[$i];
		$attr = $xml->item[$i]->attributes();
		$liste[$i]['typ'] = "item";
		
		//MetaData für jeden Titel zusammenstellen--------------------------------
		$metadata_header 			= '<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/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">';
		$raw_metadata_string 	= $xml->item[$i]->asxml();
		$metadata_string 			= str_replace(array("<", ">"), array("<", ">"), $raw_metadata_string);
		$metadata_close  			= '</DIDL-Lite>';
		$metadata					= ("$metadata_header"."$metadata_string"."$metadata_close");

		$liste[$i]['metadata']	= $metadata;
		}
	else
		{
		//Fehler aufgetreten
   	return;
		}

//Inhalte auswerten-------------------------------------------------------------

if(isset($attr['id']) && !empty($attr['id']))
		{
		$id = $attr['id'];
		$liste[$i]['id']=(string)$id;
		}
	else
		{
		$liste[$i]['id']="leer";
		}
	
if(isset($attr['refID']) && !empty($attr['refID']))
		{
		$refid = $attr['refID'];
		$liste[$i]['refid']=(string)$refID;
		}
	else
		{
		$liste[$i]['refid']="leer";
		}

if(isset($attr['parentID']) && !empty($attr['parentID']))
		{
      $parentID = $attr['parentID'];
		$liste[$i]['parentid']=(string)$parentID;
		}
	else
		{
		$liste[$i]['parentid']="leer";
		}
		
if(isset($attr['restricted']) && !empty($attr['restricted']))
		{
		$restricted = $attr['restricted'];
		$liste[$i]['restricted']=(string)$restricted;
		}
	else
		{
		$liste[$i]['restricted']="leer";
		}

if($aktrow->xpath("dc:creator"))
		{
		$interpret = $aktrow->xpath("dc:creator");
		$liste[$i]['artist']=(string)$interpret[0];
		}
	else
		{
		$liste[$i]['artist']="leer";
		}
	
if($aktrow->xpath("upnp:album"))
		{
		$album = $aktrow->xpath("upnp:album");
		$liste[$i]['album']=(string)$album[0];
		}
	else
		{
		$liste[$i]['album']="leer";
		}

if($aktrow->xpath("dc:title"))//eigentlich immer vorhanden
		{
		$titel = $aktrow->xpath("dc:title");
		$liste[$i]['title']=(string)$titel[0];
		}
	else
		{
		$liste[$i]['title']="leer";
		}
	
if(isset($aktrow->res))
		{
		$res = (string)$aktrow->res;
		$liste[$i]['resource'] = $res;
		}
	else
		{
		$liste[$i]['resource'] = "leer";
		}
	 	
//Attribute auslesen------------------------------------------------------------
if(isset($aktrow->res->attributes))
		{
		$resattr = $aktrow->res->attributes();
		
		//wenn name ‘res’ und attribute ‘duration’ vorhanden
		if(isset($aktrow->res) and isset($resattr['duration']))
			{
			$liste[$i]['duration']=(string)$resattr['duration'];
			}
		else
			{
			$liste[$i]['duration']="leer";
			}
		
		//wenn name ‘res’ und attribute ‘size’ vorhanden
		if(isset($aktrow->res) and isset($resattr['size']))
			{
			$liste[$i]['size']=(string)$resattr['size'];
			}
		else
			{
			$liste[$i]['size']="leer";
			}
			
		//wenn name ‘res’ und attribute ‘bitrate’ vorhanden
		if(isset($aktrow->res) and isset($resattr['bitrate']))
			{
			$liste[$i]['bitrate']=(string)$resattr['bitrate'];
			}
		else
			{
			$liste[$i]['bitrate']="leer";
			}
		}
			
if($aktrow->xpath("upnp:albumArtURI"))
		{
		$albumart = $aktrow->xpath("upnp:albumArtURI");
		$liste[$i]['albumArtURI']=(string)$albumart[0];
		}
	else
		{
		$liste[$i]['albumArtURI'] ="leer";
		}
	
if($aktrow->xpath("upnp:genre"))
		{
		$genre = $aktrow->xpath("upnp:genre");
		$liste[$i]['genre']=(string)$genre[0];
		}
	else
		{
		$liste[$i]['genre']="leer";
		}
		
if($aktrow->xpath("dc:date"))
		{
		$date = $aktrow->xpath("dc:date");
		$liste[$i]['date']=(string)$date[0];
		}
	else
		{
		$liste[$i]['date']="leer";
		}
	
if($aktrow->xpath("upnp:originalTrackNumber"))
		{
		$originalTrackNumber = $aktrow->xpath("upnp:originalTrackNumber");
		$liste[$i]['originalTrackNumber']=(string) $originalTrackNumber[0];
		}
	else
		{
		$liste[$i]['originalTrackNumber']="leer";
		}
		
if($aktrow->xpath("upnp:class"))
		{
		$class = $aktrow->xpath("upnp:class");
		$liste[$i]['class']=(string)$class[0];
		}
	else
		{
		$liste[$i]['class']="leer";
		}
		
if($aktrow->xpath("extension"))
		{
		$extension = $aktrow->xpath("extension");
		$liste[$i]['extension']=(string)$extension[0];
		}
	else
		{
		$liste[$i]['extension']="leer";
		}

	}

return ($liste);

}

?>

Hier die neue UPNP_Browse_WF_V1.5 (in user/Browse/link/UPNP_Browse_WF.php kopieren). Sie ist die eigentliche Steuerung des browsens mit Buttonerstellung und Aktionsauslösung je nach Typ der Auswahl (Container → Verzeichnisse bzw. item → Titel). Die Buttons enthalten dann die spezifische Aktion (weiterbrowsen über die ID oder Play eines selektierten Titel)

<?

//------------------------------------------------------------------------------
//UPNP_Browse_WF.php------------------------------------------------------------
//V1.5
//22.08.2013--------------------------------------------------------------------
//------------------------------------------------------------------------------

$ObjectID = ($_GET["ObjectID"]);
SetValue(42159 /*[Audio\Browse\SelectID]*/, $ObjectID);

//include Functions-------------------------------------------------------------

include ("UPNP_Functions_V2.3.ips.php");
include ("UPNP_Browse_Functions.ips.php");

//IPAdresse und Port des Servers------------------------------------------------

$ServerContentDirectory = GetValue(21972 /*[Audio\Browse\Server:ContentDirectory]*/);
$ServerIP= GetValue(16642 /*[Audio\Browse\Server:IP]*/);
$ServerPort= GetValue(10238 /*[Audio\Browse\Server:Port]*/);

//max_execution_time auf 10 Minuten erhöhen-------------------------------------

ini_set('max_execution_time', 600);

//Suchvariablen-----------------------------------------------------------------

$BrowseFlag = "BrowseDirectChildren"; //GetValue();
$Filter = "*"; //GetValue();
$StartingIndex = "0"; //GetValue();
$RequestedCount = "0"; //GetValue();
$SortCriteria = ""; //GetValue();

//letzte ParentID merken-------------------------------------------------------------------------

$parentid = getvalue(41353 /*[Audio\Browse\DIDL-lite\ParentID [parentID]]*/);
$last_parentid = getvalue(41353 /*[Audio\Browse\DIDL-lite\ParentID [parentID]]*/);

// ContentDirectory_Browse aufrufen ? $Result / Message in Datei
$Result = ContentDirectory_Browse ($ServerContentDirectory, $ServerIP, $ServerPort, $ObjectID, $BrowseFlag, $Filter, $StartingIndex, $RequestedCount, $SortCriteria);

$liste = BrowseList($Result); //Daten aus $Result in Array ($liste) übertragen
print_r ($liste);

echo "

";
$size = count ($liste); //Zahl der Einträge im Array = NumberReturned
echo ($size);

//------------------------------------------------------------------------------
//Buttons erstellen-------------------------------------------------------------
//------------------------------------------------------------------------------

//Head und Buttons erstellen und in HTML-Box schreiben--------------------------

$head="<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
 <html xmlns='http://www.w3.org/1999/xhtml'>
  <head>
   <!--Variablenuebergabe per XMLHttpRequest / Auslesen per GET in php-->
	<script type=\"text/JavaScript\">

	 function sndReq(url)
	 {
	 var action;
	 action = new XMLHttpRequest;
	 action.open('get', url);
	 action.send(null);
	 <!-- alert(url); -->
	 }

	</script>

	<style type=\"text/css\">
	 body { font-family:Arial,sans-serif; }
	 input { color:#D8D8D8; width:200px; height:30px; background-color:#0000FF; font-color:#FFBF00; font-weight:bold; font-size:12px; border:3px blue;}
	 input { background-image:url(image/Button.png); background-repeat:no-repeat; }
	 input:link { color:#FFBF00; text-decoration:none; font-weight:bold; }
	 input:visited { color:#FFBF00; text-decoration:none; font-weight:bold; }
	 input:focus { color:#FFBF00; background-color:#FFFF77; font-weight:bold; }
	 input:hover { color:#FFBF00; text-decoration:none; background-color:#0000FF; font-weight:bold; }
	 input:active { color:#FFBF00; background-color:#FFBF00; font-weight:bold; }
	</style>

   </head>

	<body bgcolor=\"#0B243B\" text=\"#0000FF\" style=\"width:650px; height:1000px;\">
";

$buttons ="";

for($i=0,$size=count($liste);$i<$size;$i++)
{
$title = ($liste[$i]["title"]);
$id = ($liste[$i]['id']);
$artist = ($liste[$i]['artist']);
$res = ($liste[$i]['resource']);
$parentid = ($liste[$i]['parentid']);

//prüfen, ob Musik-Track --> wenn ja: Play-Aktion hinzufügen vorbereiten

if (($liste[$i]['typ']) == "item")
	{
	$play = (" sndReq('link/UPNP_Play_WF.php?Number=" . $i . "'); ");
	}
else
	{
	$play = "";
	}

$buttons .=("

  <input type=\"button\"
	name=\"" .$artist. " - " .$title. "\"
	value=\"" .$title. "\"
	id=\"" . $id . "\"
	alt=\"".$title."\"
	onclick=\"sndReq('link/UPNP_Browse_WF.php?ObjectID=" . $id . "');" . $play . "sndReq('link/UPNP_Browse_Verlauf_WF.php?Browse=2/" .$id. "/" .$title. "');\">
  </input>
");

}

echo $buttons;

//zusammensetzen und in HTML-Box schreiben-------------------------------------

$auswahl = "$head". "$buttons". "

 </body>
</html>
";

echo $auswahl;

SetValueString(11348 /*[Audio\Browse\Auswahl]*/, $auswahl);


if (isset($liste[0]['typ']) == "container")
	{
	setvaluestring(45881 /*[Audio\Browse\Eigenschaft]*/, "Verzeichnis");

	//partielle Website in D:/IP-Symcon/webfront/user/Browse/Browse.html erstellen--

		$handle = fopen("D:/IP-Symcon/webfront/user/Browse/Browse.html", "w"); 	//Browse schreiben
		fwrite($handle, $auswahl);
		fclose($handle);

	//iFrame auffrischen

	SetValue(40745 /*[Audio\Browse\Webfront\left\Browse_WF]*/, '<iframe src="../user/Browse/Browse.html" width="100%" height="500" frameborder="0" scrolling="yes"> </iframe>');

	SetValue(16067 /*[Audio\Browse\Array]*/, $Result);

	$TitelList="0";

	$id = ($liste[$TitelList]['id']);
	SetValue(38469 /*[Audio\Browse\DIDL-lite\ID [id]]*/, (string)$id);

	//letzte ParentID speichern--------------------------------------------------

	$parentid = $last_parentid;
	setvalue(32592 /*[Audio\Browse\Last_ParentID]*/, (string)$last_parentid);

	$parentid = $liste[$TitelList]['parentid']; // gibt parentid des X. Array aus
	setvalue(41353 /*[Audio\Browse\DIDL-lite\ParentID [parentID]]*/, (string)$parentid);
	}

if (isset($liste[0]['typ']) == "item")
	{
	setvaluestring(45881 /*[Audio\Browse\Eigenschaft]*/, "Musik");
	}

?>

Meine Devices (auch der LG) nehmen es klaglos an. Nur das Senderbild wird auf dem LG noch nicht angezeigt. WMP ist nach wie vor „hartleibig“.

Viele Grüße

André

PS: fast 400 Klicks in 1 Woche sagen mir zumindest, dass das zumindest nicht ganz uninteressant für Euch ist. Wenn Ihr also Fragen habt oder mitentwickeln könnt habe ich immer ein offenes Ohr ! Ich erforsche so etwas gern, tue mich aber mit Programmierungen als „Quereinsteiger“ eher schwer :wink:

… nur kurze Info:

Es ist mir gelungen, daß Musik, Bilder und auch Video gestreamt werden, insofern es der Server (hier Twonky) bereitstellt und der Renderer das Format anwenden kann. Jetzt wird es also richtig universell :slight_smile:

Test: Twonky --> MP3, MP4, MPG, JPG --> LG-TV

Der Windows Media Player macht jetzt auch mit.

Hat sich eventuell jemand ein bischen hineingedacht und eine Idee für eine sinnvolle Logik zum Auslesen einer Playlist (habe einen Ansatz, wo ich eine XML erstelle begonnen). Beim Absetzen von SetAVTransportURI(…) würde ich gleich SetNextAVTransportURI(…) mit übergeben (damit Gap-less). Aber in Abhängigkeit welcher Variablen setze ich nach Ablauf der der ersten URI dann wieder die Nächste ?

Gruß

André

Hallo,

mit großer Freude habe ich gesehen, dass auf diesem Gebiet Lösungen gesucht werden.
Ich habe also gleich mein Testsystem rausgeholt und instaliert. Leider bekomme ich folgende Fehler und es funzt erstmal gar nichts.

— Fehlermeldungen -----------------------------------------------------------------------
#3061 Media-Objekt AlbumArt.jpg, altID #30162 nicht angelegt (Option nicht implementiert)
— Fehlermeldungen Ende-------------------------------------------------------------------

+++++++++++++++++ WFC-Objekte installieren und verlinken ++++++++++++++++++++++++++++++++++++++++
	--- OK-Meldungen --------------------------------------------------------------------------
#6001 lade WFC-Objektbaum
	--- OK-Meldungen Ende ---------------------------------------------------------------------

	--- Kontrolle durch User erforderlich -----------------------------------------------------
#6000 WFCObjektbaum: keine Installation möglich (kein Quell-Inventory oder kein Ziel-WFE gefunden)
	--- Kontrolle durch User erforderlich Ende-------------------------------------------------

— Kontrolle durch User erforderlich -----------------------------------------------------
#5010 Script ID#UPNP_Devices.ips.php: Inhalt NICHT verändert: Zeile 16, alte Target-ID #52625 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Devices.ips.php: Inhalt NICHT verändert: Zeile 26, alte Target-ID #52471 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 16, alte Target-ID #12295 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 17, alte Target-ID #13578 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 18, alte Target-ID #55605 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 19, alte Target-ID #13500 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 20, alte Target-ID #23715 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 21, alte Target-ID #21338 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 22, alte Target-ID #26586 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 23, alte Target-ID #29377 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 26, alte Target-ID #13578 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 32, alte Target-ID #55605 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 38, alte Target-ID #13500 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 44, alte Target-ID #23715 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 50, alte Target-ID #26586 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 56, alte Target-ID #21338 nicht innerhalb des Projektes
#5010 Script ID#UPNP_Party_Room.ips.php: Inhalt NICHT verändert: Zeile 62, alte Target-ID #29377 nicht innerhalb des Projektes
— Kontrolle durch User erforderlich Ende-------------------------------------------------

— Kontrolle durch User erforderlich -----------------------------------------------------
#4040 Link-Objekt AlbumArt.jpg, ID #24650, NICHT verlinkt, kein Target innerhalb des Zielprojektes gefunden
— Kontrolle durch User erforderlich Ende-------------------------------------------------

	--- Fehlermeldungen -----------------------------------------------------------------------
#4010 Variablen-Objekt Auswahl, ID #17014, kein neues Actionscript zugewiesen (keine ID gefunden)
#4010 Variablen-Objekt Browse, ID #47787, kein neues Actionscript zugewiesen (keine ID gefunden)
	--- Fehlermeldungen Ende-------------------------------------------------------------------

Wäre schön, wenn mir da einer helfen könnte.
Habe die Installationsdateien aus dem Thread #23 genommen

Im Voraus schon vielen Dank aus Jena

Frank

Hallo Frank,

Danke für Dein Interesse. Das Interesse ist sonst spärlich.

Ich bin dabei alles umzustellen und Server-seitig unterzubringen, wobei ich jetzt Konstanten verwende, die man leichter überprüfen kann. Nebenbei habe ich Buttons mit Infos und AlbumArt eingefügt sowie Playlists (per Select) eingefügt. Gib mir bitte etwas Zeit das alles zu überarbeiten und dann online zu stellen.

Hier ein Vorgeschmack (alle letztlich iFrames, die in eine Site (am Besten als Accordeon-Menu) eingebunden werden sollen.

Bei der Gelegenheit: ich suche immer noch eine Möglichkeit auf dem Multicast zu horchen, um z.B. Alive-Meldungen filtern zu können. Das sollte dauerhaft über z.B. Socket --> Cutter erfolgen, nur eine solche Verbindung wird mir immer abgelehnt (Socket Error # 10049).

Viele Grüße

André

PS: das es nicht funktioniert wird daran liegen, dass meine Konfiguration auf D:/IP-Symcon/ liegt. Ich stelle das gerade ab.

Was ist übrigens Thread #23 ? Ich denke Du meinst den Post #1 ?
Die Party-Dateien kannst Du ignorieren - das ehedem nur für Sony.
Beim WFCObjektbaum bin ich momentan überfragt.

VG André

Das Interesse ist sonst spärlich.

Hallo Andre

ich denke nicht, dass das Interesse spärlich ist, aber die „Installation“ nach der Vorgabe wirft einen riesen Haufen Fehlermeldungen aus und das animiert irgendwie nicht zum weitermachen, da man aufgrund der zahlreichen Scripte nicht weiss, wo man die Fehler suchen soll (aus Sicht eines Copy-Paster).

… ich lasse von mir hören wenn ich das hin und her schaufeln auf ein anderes System im Griff habe :slight_smile:

Gruß

André

Hallo André,

meine Konfiguration liegt auch auf D:/IP-Symcon/
Das würde ich mal als Fehler ausschließen.

„kein Quell-Inventory oder kein Ziel-WFE gefunden“ - kannst Du mir erklären, was das Quell-Inventory ist?

Ansonsten sieht die neue Version ja super aus. Hoffentlich bekomme ich das auch bald hin.

Vielen Dank

Frank

… ich muss mal ein bisschen installieren, um herauszubekommen, was da schief geht. Wird denn der Rest installiert ?

Ansonsten zu WFC: http://www.ip-symcon.de/forum/threads/19528-Q-amp-A-IPS-RS-Project-Exporter?p=175826#post175826

Ich empfinde den ProjectExporter als Super Tool.

Und hat jemand eventuell einen Hinweis zu dem Problem auf 239.255.255.250:1900 den Traffic zu verfolgen? Pakete absetzen kann ich (z.B. Discovery mit SSDP) über UDP-Socket und angehängten Cutter und erhalte auch die Antworten der Geräte. Dies aber nur auf diese Anfrage. Meldungen dazwischen bekomme ich darüber nicht mit.
Oder verstehe ich da etwas generell falsch ? Die Geräte müssten doch ihre Präsenz in bestimmten Intervallen im Netzwerk nochmal bekanntgeben. Oder läuft das etwa nur über ein Event-Subscribe ? Hat jemand im Sonos-Bereich mal damit experimentiert ?

VG André

PS: ich melde mich, wenn ich mit dem sicheren Export weiter bin.

… da völlig neue Version habe ich einen neuen Thread eröffnet:

Ich hoffe das erleichtert Einiges für die

Copy-Paster
unter Euch :wink:

Viele Grüße

André