Adding and removing audio sources to/from GStreamer pipeline on-the-go

NagyI picture NagyI · Oct 10, 2010 · Viewed 7k times · Source

I wrote a little Python script which uses an Adder plugin to mix two source streams together.

After starting the program, you hear a 1kHz tone generated by the audiotestsrc plugin. When you press Enter, an another 500Hz test tone is connected to the Adder so you hear them together. (By the way, i don't really get why should i set the pipeline again to playing state here to hear the mix. Is there any way i can plug in new sources without having to restart the pipeline?)

When you press Enter once again, the 1kHz tone should be removed from the mix and the 500Hz tone should keep playing, but instead i hear nothing anymore. I get a pulse pulsesink.c:528:gst_pulsering_stream_underflow_cb:<pulseaudio_output> Got underflow in the debug output as the last line. I don't really know what to try next.

Here is the full source code:

#!/usr/bin/python
# On-the-go source removal doesn't work this way with GStreamer. Why?

import gobject;
gobject.threads_init()
import gst;

if __name__ == "__main__":
    pipe = gst.Pipeline("mypipe")

    adder = gst.element_factory_make("adder","audiomixer")
    pipe.add(adder)

    buzzer = gst.element_factory_make("audiotestsrc","buzzer")
    buzzer.set_property("freq",1000)
    pipe.add(buzzer)

    pulse = gst.element_factory_make("pulsesink", "pulseaudio_output")
    pipe.add(pulse)

    buzzer.link(adder)
    adder.link(pulse)
    pipe.set_state(gst.STATE_PLAYING)

    raw_input("1kHz test sound. Press <ENTER> to continue.")

    buzzer2=gst.element_factory_make("audiotestsrc","buzzer2")
    buzzer2.set_property("freq",500)

    pipe.add(buzzer2)
    buzzer2.link(adder)
    pipe.set_state(gst.STATE_PLAYING)

    raw_input("1kHz + 500Hz test sound playing simoultenously. Press <ENTER> to continue.")

    buzzer.unlink(adder)
    pipe.set_state(gst.STATE_PLAYING)

    raw_input("Only 500Hz test sound. Press <ENTER> to stop.")

Answer

NagyI picture NagyI · Oct 10, 2010

I've found the solution on my own. I had to use request pads with Adder and use the pad blocking capability of GStreamer.

Here's the working source code with some descriptions:

#!/usr/bin/python

import gobject;
gobject.threads_init()
import gst;

if __name__ == "__main__":
    # First create our pipeline
    pipe = gst.Pipeline("mypipe")

    # Create a software mixer with "Adder"
    adder = gst.element_factory_make("adder","audiomixer")
    pipe.add(adder)

    # Gather a request sink pad on the mixer
    sinkpad1=adder.get_request_pad("sink%d")

    # Create the first buzzer..
    buzzer1 = gst.element_factory_make("audiotestsrc","buzzer1")
    buzzer1.set_property("freq",1000)
    pipe.add(buzzer1)
    # .. and connect it's source pad to the previously gathered request pad
    buzzersrc1=buzzer1.get_pad("src")
    buzzersrc1.link(sinkpad1)

    # Add some output
    output = gst.element_factory_make("autoaudiosink", "audio_out")
    pipe.add(output)
    adder.link(output)

    # Start the playback
    pipe.set_state(gst.STATE_PLAYING)

    raw_input("1kHz test sound. Press <ENTER> to continue.")

    # Get an another request sink pad on the mixer
    sinkpad2=adder.get_request_pad("sink%d")

    # Create an another buzzer and connect it the same way
    buzzer2 = gst.element_factory_make("audiotestsrc","buzzer2")
    buzzer2.set_property("freq",500)
    pipe.add(buzzer2)

    buzzersrc2=buzzer2.get_pad("src")
    buzzersrc2.link(sinkpad2)

    # Start the second buzzer (other ways streaming stops because of starvation)
    buzzer2.set_state(gst.STATE_PLAYING)

    raw_input("1kHz + 500Hz test sound playing simoultenously. Press <ENTER> to continue.")

    # Before removing a source, we must use pad blocking to prevent state changes
    buzzersrc1.set_blocked(True)
    # Stop the first buzzer
    buzzer1.set_state(gst.STATE_NULL)
    # Unlink from the mixer
    buzzersrc1.unlink(sinkpad1)
    # Release the mixers first sink pad
    adder.release_request_pad(sinkpad1)
    # Because here none of the Adder's sink pads block, streaming continues

    raw_input("Only 500Hz test sound. Press <ENTER> to stop.")