Multimedia mit wxPython

Plattformunabhängige GUIs mit wxWidgets.
Antworten
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

Hallo!
Ich bin gerade dabei einen Audio-Player mit wxPython zu machen.
Ich habe mich im Inat mal nach Bibiliotheken zur Soundwiedergabe umgeschaut, habe auch was dazu gefunden, weiß aber nicht, welcher der Beste für meinen Zweck ist.
Ich möchte, dass man natürlich den Sound
pausieren und dann wieder starten bzw. stoppen kann
,dass man die Lautstärke ändern kann
,dass verschiedene Soundeigenschaften [optional: Geschwindigkeit, usw...] verstellen kann

Ich weiß nicht, ob das alles machbar ist, hier aber meine Suchergebnisse:

wx.Sound
wave, pymedia.audio.sound
MplayerCtrl

evt. noch etwas anderes

Das ganze soll sowohl auf Linux, als auch auf Windows funktionieren.

Ich badanke mich für alle Antworten schoneinmal im Vorraus!

akhof :D :D :D
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Definitiv MplayerCtrl - nein Spaß bei Seite (ich bin der Autor)

MplayerCtrtl:
  • ermöglicht dir das Abspielen so ziemlicher aller Formate, Audio wie Video
  • kompletter Funktionsumfang des MPlayers (schneller, langsamer, osd…)
  • Nachteil: benötigt eine Mplayer.exe
wx.media.MediaCtrl
  • wenige Formate
  • verwendet unter Linux gstreamer, unter Win WMP und unter MAC quicktime
  • wird mitgeliefert
  • wenige, bis keine Funktionen, wie Geschwindigkeit erhöhen
Wenn es nur um Sounds geht, gibt es noch diverse andere Python-Bibliotheken, allerdings kann ich zu denen nichts sagen. Mein persönlicher Favorit ist die MplayerCtrl, ich habe sie genau deswegen geschrieben, weil ich Probleme hatte mit wx.media.MediaCtrl verschiedene Formate abzuspielen (allerdings Videos) und weil mir wx.media.MediaCtrl zu wenig Funktion geboten hat.
the more they change the more they stay the same
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

Wow!
Vielen Dank für die schnelle Antowort!

Knnst du mir eine gute Dokumentation dazu empfehlen?

akhof :D :D :D
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

the more they change the more they stay the same
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

Danke! :D
Ich habe außerdem ein Beispiel gefunden: http://www.blog.pythonlibrary.org/2010/ ... ia-player/

leider kommt nun eine Fehlermeldung:

Code: Alles auswählen

TypeError
"in method 'Slider_SetRange', expected argument 3 of type 'int'"
Datei: /usr/lib64/python2.7/site-packages/wx-2.8-gtk2-unicode/wx/_controls.py, Zeile: 2692
->und dass, obwohl in der Datei [siehe unten] steht, dass nur zwei Argumente erwartet werden??


in Zeile 2692 steht folgendes: [falls damit jemand was anstllen kann :D]

Code: Alles auswählen

    def SetRange(*args, **kwargs):
        """SetRange(self, int minValue, int maxValue)"""
        return _controls_.Slider_SetRange(*args, **kwargs)
akhof :D :D :D
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Oh ja, von driscollis

Der ganze Traceback wäre interessant, besonders die Zeile im Skript.

Übrigens dein Code-Ausschnitt nimm beliebig viele Argumente entgegen, was du das siehst ist übrigens Teil des swig-Wrappers also uninteressant.
the more they change the more they stay the same
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

ok!
Ich kenn mich mit Python nicht so aus, aber die Fehlermeldung bedeutet doch, dass als 3. Element ein Int gebraucht wird.
Aber soll ich einfach ne 0 einsetzen??

akhof :D :D :D
BlackJack

@akhof: Nein Du solltest mal einen Traceback zeigen damit mit man sieht wie der Aufruf aussieht. Und vielleicht mal schauen welchen Typ das Argument hat was Du da übergibst.

Nachtrag: Ich habe es mal ausprobiert: das Argument ist ja *da*, aber es ist nicht vom Typ `int` sondern `None`.
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

Das Problem ist, dass ich nicht so genau weiß, wo dieser error passiert.
Auch beim Debugen ist nichts richtiges rausgekommen (nicht genau wo)

ich weiß (ist mir jetzt etwas peinlich :oops: ) nicht genau, wass Ihr mit traceback meint :K

akhof :D :D :D
BlackJack

@akhof: Traceback ist das wo "Traceback" am Anfang steht und wo Du uns nur die letzte Zeile von gezeigt hast:

Code: Alles auswählen

Traceback (most recent call last):
  File "mediaplayer.py", line 122, in on_add_file
    self.playbackSlider.SetRange(0, t_len)
  File "/usr/lib/python2.6/dist-packages/wx-2.8-gtk2-unicode/wx/_controls.py", line 2692, in SetRange
    return _controls_.Slider_SetRange(*args, **kwargs)
TypeError: in method 'Slider_SetRange', expected argument 3 of type 'int'
Da sieht man auch deutlich in welcher Zeile der Aufruf in der ``mediaplayer.py`` gemacht wird, der zu dieser Ausnahme führt. Und `t_len` ist an der Stelle `None`, sollte aber eine Zahl sein.
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

Achso! :D
Ich benutze eric, und da hab ich sowas noch nie gesehen, aber gut...

Code: Alles auswählen

/usr/lib64/python2.7/site-packages/wx-2.8-gtk2-unicode/wx/_core.py:14512: UserWarning: wxPython/wxWidgets release number mismatch
  warnings.warn("wxPython/wxWidgets release number mismatch")
Traceback (most recent call last):
  File "mediaplayer.py", line 122, in on_add_file
    self.playbackSlider.SetRange(0, t_len)
  File "/usr/lib64/python2.7/site-packages/wx-2.8-gtk2-unicode/wx/_controls.py", line 2692, in SetRange
    return _controls_.Slider_SetRange(*args, **kwargs)
TypeError: in method 'Slider_SetRange', expected argument 3 of type 'int'
->genau das ist es...
...ich habe aber keine ahnung, wieso, weshalb und warum
...auch, weil in der "Doku" nur steht, dass zwei Argumente benötigt werden ([self], min, max)


akhof :D :D :D

Danke nochmal für die schnellen Antworten!
BlackJack

@akhof: Es werden laut Doku drei Argumente erwartet. In der Zählung ist das `self` mit enthalten. Die Fehlermeldung sagt, dass das dritte Argument, also `max`, den falschen Typ hat. Und das hat es auch tatsächlich denn zu dem Zeitpunkt wo der Fehler auftritt ist `t_len` an `None` gebunden, weil das der Rückgabewert von ``self.mplayer.GetTimeLength()`` ist. Warum *das* so ist kann ich auch nur vermuten. Vielleicht kann man die Länge erst ermitteln wenn man angefangen hat die Datei abzuspielen und nicht schon wenn man sie geladen hat, keine Ahnung.
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

ich werde mal versuchen, die funktionenaufrufe etwas umzustellen!

aber danke! :D
BlackJack

Das Programm scheint insgesamt nicht wirklich zu funktionieren. `on_update_playback()` wird bei mir nicht aufgerufen, womit der Fortschritts-Slider sowieso nicht aktualisiert wird. Beziehungsweise erst wenn ich auf Pause klicke, dann aber in Eein-Sekunden-Zeitschritten in denen auch das Video immer ein ganz kleines Stück weiter läuft. Also ist Pause eher eine Slowmotion-Funktion. :-)

Die Event-Handler für die MplayerCtrl-Ereignisse werden auch nicht aufgerufen.
akhof
User
Beiträge: 104
Registriert: Mittwoch 11. Mai 2011, 19:07
Kontaktdaten:

ich hab mal ein bisschen rumprobiert, und habe dann mal, ein bisschen premitiv, einfach nen timer eingebaut, also den "sekunden-zähler" eine sckunde später gestartet, das hat auch soweit gefunkt.

Code: Alles auswählen

 def on_add_file(self, event):
        """
        Add a Movie and start playing it
        """
        wildcard = "Media Files (*.*)|*.*"
        dlg = wx.FileDialog(
            self, message="Choose a file",
            defaultDir=self.currentFolder, 
            defaultFile="",
            wildcard=wildcard,
            style=wx.OPEN | wx.CHANGE_DIR
            )
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.currentFolder = os.path.dirname(path[0])
            trackPath = '"%s"' % path.replace("\\", "/")
            self.mplayer.Loadfile(trackPath)
            
            self.timer = wx.Timer(self)
            self.Bind(wx.EVT_TIMER, self.timer_out, self.timer)
            self.timer.Start(1000)
            
            
    def timer_out(self, event):
        t_len = self.mplayer.GetTimeLength()
        self.playbackSlider.SetRange(0, t_len)
        self.playbackTimer.Start(100)

nun habe ich aber folgende probleme [lange liste]:
  • wenn ich die Wiedergabe pausiere, stottert der sound, auch probeweise das video weiter
  • ich kann niht die stelle angeben, die ich mir ausehen will
  • der sound-pegel kann nicht verstellt werden
ich hatte mir dieses thema schon sehr schwer vorgestelt, aber dass es so viele "schwere" Probleme gibt, damit hatte ich nicht gerechnet :D


akhof :D :D :D
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Der Code sagt leider nicht viel aus, außer, dass du den slider falsch verwendest.

Code: Alles auswählen

t_len = self.mplayer.GetTimeLength()
Das ist ein recht teurer Aufruf, den du alle 100ms absetzte, es müssen Daten in einen fd geschrieben werden, die Antwort vom Prozess abgewartet werden. Außerdem ändert sich die Länge des Videos/Lieds nicht, d.h. du musst die "Range" mit SetRange nur einmal setzten, zu Beginn des Videos. Updaten musst du mit .SetValue.
the more they change the more they stay the same
BlackJack

@Dav1d: Ab wann darf man denn `GetTimeLength()` aufrufen? akhof hat das ja hauptsächlich deswegen in den Timer-Handler verschoben, weil der Aufruf an der Originalstelle von dem Multimediaplayer `None` zurück gegeben hat. Kann es sein, dass das Video schon laufen muss bevor man die Methode aufrufen darf?
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ja, die Media-Datei muss schon gestartet sein, dafür lässt sich wunderbar das EVT_MEDIA_STARTED Event verwenden.

Kleines Beispiel, ändert die Lautstärke auf Tastendruck und zeigt die Länge und aktuelle Position an:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import MplayerCtrl as mpc
import youtube
import wx

#mpc.DEBUG = True

class Frame(wx.Frame):
    def __init__(self, parent, id, size=(-1,-1)):
        wx.Frame.__init__(self, parent, id, size=size)
        self.mpc = mpc.MplayerCtrl(self, -1, 'mplayer')

        self.Bind(mpc.EVT_MEDIA_STARTED, self.media_started)
        self.Bind(mpc.EVT_MEDIA_FINISHED, self.media_finished)
        self.Bind(mpc.EVT_PROCESS_STARTED, self.process_started)
        self.Bind(mpc.EVT_PROCESS_STOPPED, self.process_stopped)
        self.mpc.Bind(wx.EVT_KEY_DOWN, self.key_down)
        
        self.timer = wx.Timer()
        self.interval = 100
        self.timer.Bind(wx.EVT_TIMER, self.on_timer, self.timer)
        
        self.Center()
        self.Show()

    def media_started(self, evt):
        print '----------> Media started'
        print 'Länge:', self.mpc.GetTimeLength()
        self.timer.Start(self.interval)
    def media_finished(self, evt):
        print '----------> Media finished'
    def process_started(self, evt):
        print '----------> Process started'
        self.mpc.Loadfile('testmovie ü.mpg')
    def process_stopped(self, evt):
        print '----------> Process stopped'

    def on_timer(self, evt):
        if self.mpc.playing:
            print 'Pos:', self.mpc.GetTimePos()
            self.timer.Start(self.interval)

    def key_down(self, evt):
        k = evt.GetKeyCode()
        if k in (43, 45) and self.mpc.playing:
            volume = self.mpc.volume
            if k == 43:
                if not volume > 95:
                    self.mpc.volume += 5
            elif k == 45:
                if not volume <= 5:
                    self.mpc.volume -= 5
        evt.Skip()
            
    
if __name__ == '__main__':
    app = wx.App()
    f = Frame(None, -1)
    app.MainLoop()
the more they change the more they stay the same
BlackJack

@Dav1d: Wenn es denn funktioniert — wie gesagt bei dem oben angeführten Beispielprogramm `mediaplayer.py` werden die Handler nicht aufgerufen. Bei Deinem Beispiel funktioniert es.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

@BlackJack, du meinst den Blogbeitrag von driscollis? - Der sollte funktionieren (wobei der zu Zeiten von 0.1.3 entstanden ist, aktuell ist 0.3.1, erst letztens geupdated ;))
the more they change the more they stay the same
Antworten