Webserver und Qt im Zusammenspiel

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@anogayales:
Was passiert Deiner Meinung nach hier?

Code: Alles auswählen

@route('/api/youtube/:id')
def youtube(id):
    import webbrowser
    youtube_url = "http://www.youtube.com/watch?v=%s" % id
    webbrowser.open(youtube_url)
Da spielt nicht bottle das Video ab, sondern bottle startet den Browser, der als Webclient das Video holt. Wenn Du jetzt einen Schritt weiter gehst und bottle selbst die Videodaten holen lässt, dann ist bottle gegenüber youtube Client und nicht Server.
Ich glaube die Kommunikation scheitert hier gerade an diesen Begrifflichkeiten.

Wozu Du bottle zwischen Qt und youtube brauchst, ist mir schleierhaft. Soll das eine Art Proxysystem werden?
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Ich will die Musik in meinem Wohnheim fernsteuern. Dabei soll es möglich sein, über eine Server URL ein entsprechendes Video zu laden (via Smartphone) und dieses wird dann vom Hostrechner im Wohnheim mittels Lautsprecher ausgestrahlt.

Ich glaub auch, dass es an den Begrifflichkeiten von Server/Client scheitert bzw. mitlerweile habe ich wohl euer Problem verstanden. Ich sehe mittlerweile auch das der Bottle Ansatz nicht funktioniert. Bottle hat keine Möglichkeit auf Signale von Qt zu reagieren.

Wäre hier ein QTcpServer von Qt vielleicht besser? Hier kann ich nämlich bequem Signale und Slots benutzen. Nachteil ist wohl, dass man nicht über HTTP direkt Daten einschleusen könnte ohne viel Aufwand (parsen) zu betreiben?

Grüße,
anogayales
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

anogayales hat geschrieben:Ich will die Musik in meinem Wohnheim fernsteuern. Dabei soll es möglich sein, über eine Server URL ein entsprechendes Video zu laden (via Smartphone) und dieses wird dann vom Hostrechner im Wohnheim mittels Lautsprecher ausgestrahlt.
Ich verstehe das so:
1. Der Hostrechner soll youtube-Videos abspielen und den Sound ausgeben.
Dafür kannst Du QWebkit mit Flash resp. HTML5 nutzen oder Phonon direkt. Für HTML5 bzw. Phonon solltest Du die Links für die Mpeg-Version nehmen, nicht die Flash-Links (worauf das x-flv hindeutet). Wobei ich nicht weiss, ob Phonon .flv mit ensprechendem Plugin versteht.

2. Der Player soll programmatisch steuerbar sein.
Das geht bei QWebkit über JS und bei Phonon direkt.

3. Die Steuerung soll über eine HTTP-Schnittstelle erreichbar sein.
Hierfür kannst Du in der Tat bottle verwenden.

Ich gehe davon aus, dass der Player nur eine Instanz haben soll. Ausgehend von QWebkit und Flash (womit Du den youtube-Standard- oder chromeless-Player verwenden kannst) strickst Du Dir einfach eine Qt-Klasse, die eine Video-URL entgegen nehmen kann und daraufhin abspielt (über QWebView). Die Steuerung bindest Du an die JS-API des Players an und stellt entsprechende Methoden wie skip, stop etc zur Verfügung. Diese Klasse packst Du in einen XML-RPC-Server mit entsprechenden Request-Methoden (Achtung: auf Threadsicherheit achten!)
Dann baust Du Dir eine bottle-Umgebung mit den Unterseiten für die Steuerung. In den einzelnen Requesthandlern verweisst Du auf die entsprechende Methode des XML-RPC-Servers.
Das schöne an der XML-RPC-Lösung ist die einfache Erweiterbarkeit. So könntest Du z.B. einen XML-RPC-Client fürs Smartphone bauen, der direkt mit dem Player ohne Umweg übers Web interagiert. (Der bottle-HTTP-Server wäre dann nur ein möglicher Client des XML-RPC-Servers.)

Mit einigen Verrenkungen könntest Du bottle und den Player auch direkt verheiraten. Allerdings dürfte der Aufwand, das globale Playerobjekt threadsicher über die Requesthandler zu steuern ungemein höher sein und schränkt zusätzlich die Wahl der HTTP-Server für bottle ein.

Last but not least kannst Du mit QTcpServer die Serverfunktionalität selbst vorhalten. Das ist dann ziemlich low level aber vllt. ausreichend, falls Du nur den Player steuern und keine aufwendigen Seiten ausliefern willst.
deets

anogayales hat geschrieben:Ich will die Musik in meinem Wohnheim fernsteuern. Dabei soll es möglich sein, über eine Server URL ein entsprechendes Video zu laden (via Smartphone) und dieses wird dann vom Hostrechner im Wohnheim mittels Lautsprecher ausgestrahlt.
Haleluja! Nach nur 15 posts endlich mal ne Ansage, worum es ueberhaupt geht...

Was du willst, ist also ein fernsteuerbarer Videoplayer. Ok. Also baust du zuerstmal einen Videoplayer. ZB mit Qt + der eingebetteten VLC-Komponente oder was auch immer Youtube videos spielen kann. Und der hat einen SLOT, wenn man da ein youtube-video-link reinjagd, dann spielt der den ab.

Und dann kannst du natuerlich schon bottle in einem eigenen Thread verwenden, um diesen Player - der dann aber *IMMER* laeuft - auch noch eine Webapp hosten zu lassen, die dann per smartphone aufgerufen werden kann. Und wenn du da ein URL eingibst oder wie auch immer, dann kann natuerlich bottle das via SIGNAL an den Qt-Prozess melden, an den oben beschrieben SLOT.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Vielen Dank für eurer Antworten. Da werde ich ein Weilchen beschäftigt sein.

Mir sagt momentan die Phonon Variante zu. Habe auch rausgefunden wie man an den MP4 Stream kommt. Leider hat damit mein Phonon auf Windows Probleme. Der Player geht immer in den Phonon::ErrorState http://developer.qt.nokia.com/doc/qt-4. ... State-enum nachdem ich meine Youtube mp4 Datei abgespielt habe, sowohl mit der komplett runtergeladenen Datei als auch mit dem http Link. Im VLC und Windows Media Player klappt das Abspielen problemlos. Dummerweise kann man nicht so einfach das Phononbackend in Windows wechseln, obwohl mp4 eindeutig von Phonon unterstützt wird.

Ein

Code: Alles auswählen

    for mime in Phonon.BackendCapabilities.availableMimeTypes():
        print mime
gibt

Code: Alles auswählen

application/vnd.ms-wpl
application/x-mplayer2
...
audio/mp4
...
video/mp4
...
video/x-msvideo
vnd.ms.wmhtml
zurück. Alles also seeehhhhr seltsam.

Eine ffmpeg info Abfrage verrät mir:

Code: Alles auswählen

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'videoplayback.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isomavc1mp42
    creation_time   : 2010-12-29 07:58:20
  Duration: 00:04:23.20, start: 0.000000, bitrate: 656 kb/s
    Stream #0:0(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, s16, 114
 kb/s
    Metadata:
      creation_time   : 2010-12-29 07:58:20
      handler_name    : (C) 2007 Google Inc. v08.13.2007.
    Stream #0:1(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yu
v420p, 480x360 [SAR 1:1 DAR 4:3], 539 kb/s, 25 fps, 25 tbr, 25k tbn, 50 tbc
    Metadata:
      creation_time   : 2010-12-29 07:58:20
      handler_name    :
Edit: Noch eine Frage:
deets hat geschrieben:dann kann natuerlich bottle das via SIGNAL an den Qt-Prozess melden, an den oben beschrieben SLOT.
Wie kann ich Signale über Prozessgrenzen senden?

Grüße,
anogayales
deets

Kannst du nicht, und das habe ich auch nicht gesagt. Sondern du sollst bottle in einem eigenen Thread starten, im *selben* Prozess.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Alles klar.

Mitlerweile bin ich von phonon auf den vlc player umgestiegen, der selbst eine remote control anbietet, die man über tcp ansprechen kann.

Grüße,
anogayales
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Ein simples Bsp. mit QTcpServer, QWebKit und dem chromeless-Player-Bsp. von youtube --> http://paste.pocoo.org/show/532176/

Die Netzwerksteuerung könntest Du auf HTTP hieven, indem Du dem TcpServer das Protokoll beibringst oder halt ein Webrahmenwerk ala bottle hierfür nutzt. Der Server hat gerade den Vorteil, nur eine Verbindung entgegen zu nehmen - bei den Rahmenwerken musst Du auf Threadsicherheit und mögliche Race-Conditions von Request-Threads zu Player-Hauptthread achten.

@vlc-Player:
Damit kommst Du wahrscheinlich schneller und einfacher zum Ziel.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Vielen Dank für deine Beispiel Implementierung. Ähnlich sieht es bei mir auch schon aus.

Ein Problem habe ich noch. Ich verbinde mich über den VLC Player über TCP und lese die Antwort des VLC Players in einem Zuge aus:

Code: Alles auswählen

    def _execute_command(self, command):
        self.socket.write("%s \n" % command)
        self.socket.waitForBytesWritten()
        self.socket.waitForReadyRead()
        return self.socket.readAll()
Die Methode _execute_command wird von einem anderen Thread aufgerufen. Diese sieht so aus:

Code: Alles auswählen

@route('/api/player/<command:re:[a-z 0-9]+>')
def player(command):
    return player._execute_command(command)
Das Problem ist, dass immer nur ein Teil der Nachricht übermittelt wird. Als wenn die Methode readAll die Daten zurück gibt ohne darauf zu warten ob vielleicht noch was kommt. Ich weiß so ist TCP spezifiziert und so sollte es auch sein. Wie kann ich dennoch warten bis WIRKLICH nichts mehr kommt, über einen Timer vielleicht?

Mit self.socket.readyRead.connect(handle) kann ich leider nicht arbeiten, denn bottle brauch ja die Daten sofort. Auf Threadsichereit hab ich auch noch nicht geachtet. Ich muss sicherstellen, dass das socket Objekt nur einmal benutzt wird, oder?

Hilft mir http://bottlepy.org/docs/dev/async.html vielleicht weiter?

Grüße,
anogayales
lunar

@anogayales: Gar nicht, denn Du kannst letztlich nie wissen, ob wirklich gar nichts mehr kommt, oder ob die Gegenstelle nicht einfach gerade nur etwas länger braucht, um weitere Daten zu senden. Daher spezifiziert ein Netzwerk-Protokoll immer, wann eine Datenübertragung abgeschlossen ist, e.g. beispielsweise wenn ein bestimmtes Steuerzeichen übertragen wurde, oder die Gegenstelle die Verbindung schließt.

Wie lange Du Daten lesen musst, hängt also vom konkreten Protokoll ab, dass VLC zur Kommunikation nutzt. Dazu müsstest Du die VLC-Dokumentation konsultieren.
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Leider ist die VLC Doku da überhaupt nicht ausführlich. Zumindest finde ich gar keine Doku, eher eine tutorialartige Beschreibung. Auf http://www.videolan.org/doc/ steht was ich vermutet habe:

Code: Alles auswählen

Due to lack of volunteer technical writers, we are unable to provide a proper up-to-date online documentation until further notice.
Sorry for the inconvenience.
Edit: Hab das CLI gefunden:
http://www.videolan.org/developers/vlc/ ... tf/cli.lua oder auch https://github.com/pyrot/vlc/blob/maste ... ntrol/rc.c

Bei ausgewählten Befehlen habe ich durch Testen Schemata festellen können:

Code: Alles auswählen

+----[ Stream 0 ] .... +----[ end of stream info ] 
beinhaltet ein "Paket".

So, es klappt. Leider nur für explizit definierte return messages:

Code: Alles auswählen

        start_time =  QtCore.QTime().currentTime()
        timeout = lambda : (QtCore.QTime().currentTime().msecsTo(start_time)) > -50
        while timeout():
            if command == "info":
                     data = ""
                     while "end of stream info" not in data and timeout():
                         data += self.socket.readAll()
                     if timeout():
                        return data
        return "error"
Hier werden maximal 50 ms gewartet, sonst wird "error" ausgegebn. Wie sinnvoll das ist, muss ich noch testen. :)

Zu Testzwecken hab ich mal das ausprobiert: http://bottlepy.org/docs/dev/async.html

Code: Alles auswählen

def player(command):
    socket = player._execute_command(command) #gibt wirklich ein socket zurück
    body = queue.Queue()
    socket.readyRead.connect(lambda chunk: body.put(chunk))
    return body
Scheintert leider, mit einem

Code: Alles auswählen

NotImplementedError('gevent is only usable from a single thread',)
Grüße,
anogayales
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Die Krux der low level-Implementierung...
Aber mal ehrlich - für Dein beschriebenes Szenario, Player innerhalb des Wohnheims, steuerbar über ein Webfrontend, ist doch der low-level-Kram völlig unerheblich, da Skalierbarkeit, Performance etc. keine Rolle spielen. Über meinen oben beschriebenen Weg per XML-RPC ist die Sache, zugegeben inperformant, relativ schnell zu lösen, und Du musst Dich nicht um einzelne Buckets der TCP-Übertragung scheren:
- Player mit vlc oder QtWebKit-App in Haupt-Thread des XML-RPC-Servers
- API des Players exponiert über XML-RPC
- HTTP-Rahmenwerk genutzt für Webclient oder native XML-RPC-Clients für smartphones gebastelt

Ich glaube, dass Du Dir das Problem unnötig verkomplizierst. Ansonsten ist es natürlich löblich, die Untiefen des Netzwerkstacks durchschauen zu wollen ;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und darf ich fragen warum du nicht ein bestehendes, per Smartphone fernsteuerbares Projekt nutzt, sondern das Rad neu erfindest? Ich wollte letztens meinen Musikplayer, Quod Libet via Android-Phone fernsteuern, damit ich bequem im bett liegen kann um Tracks zu überspringen. Eine einfache Google-Suche später war das Problem durch eine entsprechende Software gelöst. Programmieraufwand: 0.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Antworten