Question

I have a music application that uses PyQt4 and Phonon (with the gstreamer0.1 backend). I want to add some audio analysis using some GStreamer code. Both sets of code work fine independently, but when I try to combine them in the same application I get the following errors and the application hangs up. Test code is below. Any ideas?

> (python3:11922): GStreamer-WARNING **: Element factory metadata for 'bin' has no valid long-name field 

> phonon_gstreamer.py:110: Warning: cannot register existing type `GstObject'                            
  self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)

> phonon_gstreamer.py:110: Warning: g_once_init_leave: assertion `result != 0' failed
  self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)

> phonon_gstreamer.py:110: Warning: g_type_register_static: assertion `parent_type > 0' failed
  self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)

> phonon_gstreamer.py:110: Warning: g_object_newv: assertion `G_TYPE_IS_OBJECT (object_type)' failed
  self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)


from PyQt4 import QtGui
from PyQt4.phonon import Phonon

import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst

class AudioAnalysis(Gst.Bin):
    def __init__(self):
        super().__init__()

        # Create elements
        q1 = Gst.ElementFactory.make('queue', None)        

        convert = Gst.ElementFactory.make("audioconvert", None)
        resample = Gst.ElementFactory.make("audioresample", None)        
        self.analysis = Gst.ElementFactory.make("rganalysis", None)
        self.analysis.set_property("num-tracks", 1)

        q2 = Gst.ElementFactory.make('queue', None)

        # Add elements to Bin
        self.add(q1)
        self.add(convert)
        self.add(resample)
        self.add(self.analysis)
        self.add(q2)

        # Link elements
        q1.link(convert)
        convert.link(resample)
        resample.link(self.analysis)
        self.analysis.link(q2)

        # Add Ghost Pads
        self.add_pad(
            Gst.GhostPad.new('sink', q1.get_static_pad('sink'))
        )
        self.add_pad(
            Gst.GhostPad.new('src', q2.get_static_pad('src'))
        )

    def bus_message_tag(self, bus, message):
        pass

class Example:
    def __init__(self):        
        self.mainloop = GObject.MainLoop()
        self.pipeline = Gst.Pipeline()

        # Create elements
        self.src = Gst.ElementFactory.make('filesrc', None)
        self.dec = Gst.ElementFactory.make('decodebin', None)
        self.audio = AudioAnalysis()        
        self.sink =  Gst.ElementFactory.make('fakesink', None)

        # Add elements to pipeline      
        self.pipeline.add(self.src)
        self.pipeline.add(self.dec)
        self.pipeline.add(self.audio)
        self.pipeline.add(self.sink)

        # Set properties
        self.src.set_property('location', 'foo.mp3')

        # Connect signal handlers
        self.dec.connect('pad-added', self.on_pad_added)
        self.dec.connect('pad-removed', self.removed_decoded_pad)

        # Link elements
        self.src.link(self.dec)
        self.audio.link(self.sink)

        self.bus = self.pipeline.get_bus()
        self.bus.add_signal_watch()
        self.bus.connect('message::eos', self.on_eos)
        self.bus.connect('message::error', self.on_error)
        self.bus.connect("message::tag", self.audio.bus_message_tag)       

    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        self.mainloop.run()

    def kill(self):
        self.pipeline.set_state(Gst.State.NULL)
        self.mainloop.quit()

    def removed_decoded_pad(self, dbin, pad):
        pad.unlink(self.audio.get_static_pad("sink"))

    def on_pad_added(self, element, pad):
        string = pad.query_caps(None).to_string()
        if string.startswith('audio/'):
            pad.link(self.audio.get_static_pad('sink'))

    def on_eos(self, bus, msg):
        self.kill()

    def on_error(self, bus, msg):
        print('on_error():', msg.parse_error())
        self.kill()

#example = Example()
#example.run()

class Window(QtGui.QPushButton):
    def __init__(self):
        QtGui.QPushButton.__init__(self, '')
        self.mediaObject = Phonon.MediaObject(self)
        self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
        Phonon.createPath(self.mediaObject, self.audioOutput)
        self.mediaObject.stateChanged.connect(self.handleStateChanged)
        self.mediaObject.setCurrentSource(Phonon.MediaSource(sys.argv[1]))
        self.mediaObject.play()

    def handleStateChanged(self, newstate, oldstate):
        if newstate == Phonon.PlayingState:
            self.setText('Playing')
        elif newstate == Phonon.StoppedState:
            self.setText('Stopped')
        elif newstate == Phonon.ErrorState:
            source = self.mediaObject.currentSource().fileName()
            print('ERROR: could not play:', source.toLocal8Bit().data())

if __name__ == '__main__':

    GObject.threads_init()
    Gst.init(None)

    import sys
    app = QtGui.QApplication(sys.argv)
    app.setApplicationName('Phonon')
    win = Window()
    win.resize(200, 100)
    win.show()
    sys.exit(app.exec_())
Was it helpful?

Solution

It's impossible. Gstreamer 0.10 and 1.x is not parallel linkable.

Either try to use this branch: https://projects.kde.org/projects/kdesupport/phonon/phonon-gstreamer/repository/show?rev=1.0-porting (unstable AFAIK)

Or use a non-gstreamer phonon backend. or you can implement the analysis code in separete process and don't call it directly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top