Frage zu gstreamer bzw. gst-python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Vortex
User
Beiträge: 16
Registriert: Sonntag 13. August 2006, 14:12
Kontaktdaten:

Hallo zusammen.

Ich habe mir das gst-python-Tutorial durchgelesen und habe eine Frage (das Tutorial findet man hier http://pygstdocs.berlios.de/pygst-tutorial/index.html).

Nehmen wir zum Beispiel mal diese Code-Beispiel (das erste Example aus dem Tutorial):

Code: Alles auswählen

#!/usr/bin/env python

import sys, os, os.path
import pygtk, gtk, gobject
import pygst
pygst.require("0.10")
import gst

class GTK_Main:
	
	def __init__(self):
		window = gtk.Window(gtk.WINDOW_TOPLEVEL)
		window.set_title("Audio-Player")
		window.set_default_size(400, 300)
		window.connect("destroy", gtk.main_quit, "WM destroy")
		vbox = gtk.VBox()
		window.add(vbox)
		self.entry = gtk.Entry()
		vbox.pack_start(self.entry, False, True)
		self.button = gtk.Button("Start")
		self.button.connect("clicked", self.start_stop)
		vbox.add(self.button)
		window.show_all()
		
		self.player = gst.element_factory_make("playbin", "player")
		fakesink = gst.element_factory_make('fakesink', "my-fakesink")
		self.player.set_property("video-sink", fakesink)
		bus = self.player.get_bus()
		bus.add_signal_watch()
		bus.connect('message', self.on_message)
		
	def start_stop(self, w):
		if self.button.get_label() == "Start":
			filepath = self.entry.get_text()
			if os.path.exists(filepath):
				self.button.set_label("Stop")
				self.player.set_property('uri', "file://" + filepath)
				self.player.set_state(gst.STATE_PLAYING)
		else:
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")
						
	def on_message(self, bus, message):
		t = message.type
		if t == gst.MESSAGE_EOS:
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")
		elif t == gst.MESSAGE_ERROR:
			self.player.set_state(gst.STATE_NULL)
			self.button.set_label("Start")

gtk.gdk.threads_init()
GTK_Main()
gtk.main()
Wie man sieht, muss man hier Signale von "bus" abfangen. "bus" ist ein gObject, das heißt es ist erforderlich, dass ein gObject Event-Loop läuft (richtig?).

Ich würde jedoch gerne eine GUI-Anwendung in Qt4 schreiben, die Gstreamer benutzt. Jetzt habe ich aber das Problem, dass sämtliche gstreamer-Objekte gObjects sind und ich zum Arbeiten mit deren Signalen einen gObject Event-Loop bräuchte.

Wenn ich z.B. einfach so in meiner Qt-Anwendung bus.add_signal_watch() aufrufe, dann Segfaultet sie mir sofort.

Kann ich irgendwie einen gObject Event-Loop in meine Qt-Anwendung einbinden? Ist es nicht ein bisschen unpraktisch, einen bestimmten Event-Loop vorauszusetzen?

Ich habe Versucht mit QThread einen neuen Thread zu erstellen und in diesem Thread dann einen gObject Event-Loop laufen zu lassen. Aber sobald ich in dem Thread gobject.MainLoop.run() aufrufe, friert meine Anwendung ein.

Ich merke, das die Sache ziemlich kompliziert ist, aber ich hoffe mir kann trotzdem jemand helfen.

Vielen Dank schonmal im Voraus!
dev
User
Beiträge: 49
Registriert: Montag 23. Januar 2006, 09:52
Kontaktdaten:

Hi,
Vortex hat geschrieben: Wie man sieht, muss man hier Signale von "bus" abfangen. "bus" ist ein gObject, das heißt es ist erforderlich, dass ein gObject Event-Loop läuft (richtig?).
jein, Du kannst auch pollen.

Schau mal, wie ich es im Player des Coherence MediaRenderer gemacht habe, vielleicht hilft Dir das ja weiter.

Ciao,
dev
Vortex
User
Beiträge: 16
Registriert: Sonntag 13. August 2006, 14:12
Kontaktdaten:

Okay, aber das ist erstens keine sehr schöne Methode und zweitens glaube ich nicht, dass das auch mit den "sync messages" aus dem zweiten Beispiel funktioniert.

Wäre es denn prinzipiell möglich in einem Thread einen anderen Event-Loop laufen zu lassen? Ich kann (laut der Qt-Dokumentation) problemlos mehrere Qt-Loops in unterschiedlichen Threads laufen lassen. Aber da steht leider kein Wort darüber ob man da auch andere Event-Loops laufen lassen kann oder ob die sich dann in die quere kommen.

Das hier funktioniert jedenfalls nicht. Es braucht 100% CPU-Zeit und müllt den Speicher zu (besser nicht ausführen):

Code: Alles auswählen

#!/usr/bin/env python

import sys, os
import gobject

from PyQt4.QtCore import *
from PyQt4.QtGui import*

class SignalThread(QThread):
    def run(self):
        loop = gobject.MainLoop()
        loop.run()

class Main(QApplication):
    def __init__(self):
        QApplication.__init__(self, sys.argv)
        self.window = QWidget()
        self.window.setWindowTitle("Test-Window")
        self.window.setLayout(QVBoxLayout())
        self.window.layout().setMargin(2)
        self.window.layout().addWidget(QLabel("Test-Label", self.window))
        self.window.resize(self.window.minimumSizeHint().expandedTo(QSize(400, 300)))
        self.window.show()
        
        self.signalThread = SignalThread(self)
        self.signalThread.start()
        
main = Main()
main.exec_()
EDIT: Ich merke gerade, dass die Anwendung sich unterschiedlich verhalten kann. Mal passiert gar nichts, mal klappt das Fenster auf und friert dann ein und manchmal läuft die Anwendung, müllt dann aber den Speicher zu.
Vortex
User
Beiträge: 16
Registriert: Sonntag 13. August 2006, 14:12
Kontaktdaten:

Ich habs jetzt hingekriegt. :D

Anstatt einen zweiten MainLoop im Thread zu starten, reicht es, wenn man gobject.threads_init() dort aufruft. Fragt mich aber nicht warum das funktioniert, ich verstehs nicht...

Ich hab mal den Video-Player aus dem Tutorial nach Qt4 umgeschrieben:

Code: Alles auswählen

#!/usr/bin/env python

import sys, os, os.path
import pygst
pygst.require("0.10")
import gst
import gobject

from PyQt4.QtCore import *
from PyQt4.QtGui import*

class SignalThread(QThread):
    def __init__(self, parent, player):
        QThread.__init__(self, parent)
        self.player = player
        
    def run(self):
        gobject.threads_init()
        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.enable_sync_message_emission()
        bus.connect('message', self.on_message)
        bus.connect('sync-message::element', self.on_sync_message)
        
    def on_message(self, bus, message):
        self.emit(SIGNAL("message"), bus, message)
        
    def on_sync_message(self, bus, message):
        self.emit(SIGNAL("sync-message"), bus, message)

class Main(QApplication):
    def __init__(self):
        QApplication.__init__(self, sys.argv)
        
        self.window = QWidget()
        self.window.setWindowTitle("Video-Player")
        self.window.setLayout(QVBoxLayout())
        self.window.layout().setMargin(2)
        self.window.layout().setSpacing(0)
        
        upper = QWidget(self.window)
        upper.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
        self.window.layout().addWidget(upper)
        upper.setLayout(QHBoxLayout())
        upper.layout().setMargin(0)
        
        self.entry = QLineEdit(upper)
        upper.layout().addWidget(self.entry)
        
        self.button = QPushButton("Start", upper)
        upper.layout().addWidget(self.button)
        self.connect(self.button, SIGNAL("clicked()"), self.start_stop)
        
        self.movie_window = QWidget(self.window)
        self.window.layout().addWidget(self.movie_window)
        
        self.window.resize(self.window.minimumSizeHint().expandedTo(QSize(600, 400)))
        self.window.show()
        
        self.player = gst.element_factory_make("playbin", "player")
        self.signalThread = SignalThread(self, self.player)
        
        self.connect(self.signalThread, SIGNAL("message"), self.on_message)
        self.connect(self.signalThread, SIGNAL("sync-message"), self.on_sync_message)
        
        self.signalThread.start()
        
    def start_stop(self):
        if self.button.text() == "Start":
            filepath = unicode(self.entry.text())
            self.button.setText("Stop")
            self.player.set_property('uri', "file://" + filepath)
            self.player.set_state(gst.STATE_PLAYING)
        else:
            self.player.set_state(gst.STATE_NULL)
            self.button.setText("Start")
            
    def on_message(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            self.player.set_state(gst.STATE_NULL)
            self.button.setText("Start")
        elif t == gst.MESSAGE_ERROR:
            self.player.set_state(gst.STATE_NULL)
            self.button.setText("Start")
    
    def on_sync_message(self, bus, message):
        if message.structure is None:
            return
        message_name = message.structure.get_name()
        print message_name
        if message_name == 'prepare-xwindow-id':
            imagesink = message.src
            imagesink.set_property('force-aspect-ratio', True)
            imagesink.set_xwindow_id(self.movie_window.winId())
            
main = Main()
main.exec_()
Der SignalThread wandelt die gobject-Signale in Qt-Signale um. Das funktioniert im Prinzip ganz gut, lediglich die "sync-messages" werden jetzt nicht mehr ganz synchron abgearbeitet. Für diese muss ich noch irgend nen workaround finden.
Antworten