Search code examples
pythonpython-3.xgstreamerspeexpygst

Problems with using speex in gstreamer in python


I'm trying to experiment how to use gstreamer in python 3.x for audio encoding and later for streaming, but got stuck unfortunately.

I've found this simple audio player, which works fine:

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

# Initializing threads used by the Gst various elements
GObject.threads_init()
#Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Gst.init(None)

class Main:
    def __init__(self):
        self.mainloop = GObject.MainLoop()
        #Creating the gst pipeline we're going to add elements to and use to play the file
        self.pipeline = Gst.Pipeline()

        #creating the filesrc element, and adding it to the pipeline
        self.filesrc = Gst.ElementFactory.make("filesrc", "filesrc")
        self.filesrc.set_property("location", "beatles.mp3")
        self.pipeline.add(self.filesrc)

        #creating and adding the decodebin element , an "automagic" element able to configure itself to decode pretty much anything
        self.decode = Gst.ElementFactory.make("decodebin", "decode")
        self.pipeline.add(self.decode)
        #connecting the decoder's "pad-added" event to a handler: the decoder doesn't yet have an output pad (a source), it's created at runtime when the decoders starts receiving some data
        self.decode.connect("pad-added", self.decode_src_created)

        #setting up (and adding) the alsasin, which is actually going to "play" the sound it receives
        self.sink = Gst.ElementFactory.make("alsasink", "sink")
        self.pipeline.add(self.sink)

        #linking elements one to another (here it's just the filesrc - > decoder link , the decoder -> sink link's going to be set up later)
        self.filesrc.link(self.decode)

    #handler taking care of linking the decoder's newly created source pad to the sink
    def decode_src_created(self, element, pad):
        pad.link(self.sink.get_static_pad("sink"))

    #running the shit
    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        self.mainloop.run()

start=Main()
start.run()

Then I've tried to add a speexenc and a speexdec element in-between. See 6 lines indicated by "# ADDED" and 1 line "# MODIFIED". I know that in this particular case the encoding and decoding via speex makes no real sense, but due to lack of tutorials and examples in this matter in python 3.x, I wanted to start experimenting somehow after reading the basic gstreamer documents and FAQs. There're are no error messages, but ultimately it plays nothing, and speaker is dead silent:

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

# Initializing threads used by the Gst various elements
GObject.threads_init()
#Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins.
Gst.init(None)

class Main:
    def __init__(self):
        self.mainloop = GObject.MainLoop()
        #Creating the gst pipeline we're going to add elements to and use to play the file
        self.pipeline = Gst.Pipeline()

        #creating the filesrc element, and adding it to the pipeline
        self.filesrc = Gst.ElementFactory.make("filesrc", "filesrc")
        self.filesrc.set_property("location", "beatles.mp3")
        self.pipeline.add(self.filesrc)

        #creating and adding the decodebin element , an "automagic" element able to configure itself to decode pretty much anything
        self.decode = Gst.ElementFactory.make("decodebin", "decode")
        self.pipeline.add(self.decode)
        #connecting the decoder's "pad-added" event to a handler: the decoder doesn't yet have an output pad (a source), it's created at runtime when the decoders starts receiving some data
        self.decode.connect("pad-added", self.decode_src_created)

        self.speexenc = Gst.ElementFactory.make("speexenc", "speexenc") # ADDED
        self.pipeline.add(self.speexenc) # ADDED

        self.speexdec = Gst.ElementFactory.make("speexdec", "speexdec") # ADDED
        self.pipeline.add(self.speexdec) # ADDED

        #setting up (and adding) the alsasin, which is actually going to "play" the sound it receives
        self.sink = Gst.ElementFactory.make("alsasink", "sink")
        self.pipeline.add(self.sink)

        #linking elements one to another
        self.filesrc.link(self.decode)
        self.speexenc.link(self.speexdec) # ADDED
        self.speexdec.link(self.sink) # ADDED

    #handler taking care of linking the decoder's newly created source pad to the speexenc
    def decode_src_created(self, element, pad):
        pad.link(self.speexenc.get_static_pad("sink")) # MODIFIED

    #running the shit
    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        self.mainloop.run()

start=Main()
start.run()

Any proposals to make it finally work would be highly appreciated! Many thanks in advance!

EDIT: Output of GST_DEBUG="*:3"

** (GsTest04.py:2500): WARNING **: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files
0:00:00.310035438  2500  0x25b7290 FIXME                  id3v2      gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.310425594  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.310597677  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.310753458  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.310931687  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.311063614  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.311322155  2500  0x25b7290 FIXME                  id3v2 gstid3tag.c:142:gst_tag_from_id3_tag: Cannot map ID3v2 tag 'PRIV' to GStreamer tag
0:00:00.376435699  2500 0x741014f0 WARN                    alsa conf.c:4544:parse_args: alsalib error: Unknown parameter AES0
0:00:00.376732365  2500 0x741014f0 WARN                    alsa conf.c:4704:snd_config_expand: alsalib error: Parse arguments error: No such file or directory
0:00:00.376897261  2500 0x741014f0 WARN                    alsa pcm.c:2217:snd_pcm_open_noupdate: alsalib error: Unknown PCM default:{AES0 0x02 AES1 0x82 AES2 0x00 AES3 0x02}
0:00:00.378137573  2500 0x741014f0 WARN               baseparse gstbaseparse.c:3188:gst_base_parse_loop:<mpegaudioparse0> error: streaming stopped, reason not-linked

Solution

  • I can't see anything obviously wrong. I suggest to run you program using GST_DEBUG="*:3" to see if there are any warnings in the log. You might also want to double-check that all api calls worked. (e.g. that

    self.speexenc = Gst.ElementFactory.make(...)
    

    did not result in None. I'd also check the return value for pad.link() in decode_src_created. If the raw audio produced by the decoder is not what speexenc likes linking will fail. Try adding an audioconvert element before your speexenc.