Audio usb speaker disconnection : increasing cpu and memory issue

Hello everyone,

I’ve noticed that when a USB audio speaker is disconnected while a pipeline is running, it results in increased CPU and memory usage.

With GST_DEBUG=3, I can see the following error that can be caught in a bus error handler:

alsa gstalsasink.c:1112_alsasink_write:<alsasink0> error: Error outputting to audio device. The device has been disconnected.

However, if I don’t stop the program at this point, the CPU and memory usage continue to increase. Assuming that I also have video content to display, I don’t want the program to stop if the USB speaker is disconnected. Instead, should I explicitly discard the audio samples in some way, or is there a memory leak that needs to be addressed?

Here’s the Python code I’m using to reproduce this issue. Run it and unplug your USB speaker to see the problem in action :slight_smile:

import gi

gi.require_version("Gst", "1.0")
from gi.repository import Gst


def main() -> None:
    Gst.init(None)

    pipeline = Gst.Pipeline.new()
    audio = Gst.ElementFactory.make("audiotestsrc")
    audio_sink = Gst.ElementFactory.make("alsasink")
    # change with your device id
    audio_sink.set_property("device", "hw:2,0")
    video = Gst.ElementFactory.make("videotestsrc")
    video_sink = Gst.ElementFactory.make("autovideosink")

    if not pipeline or not audio or not audio_sink or not video or not video_sink:
        print("Not all elements could be created.")
        exit(-1)

    pipeline.add(audio)
    pipeline.add(audio_sink)
    audio.link(audio_sink)
    pipeline.add(video)
    pipeline.add(video_sink)
    video.link(video_sink)

    ret = pipeline.set_state(Gst.State.PLAYING)
    if ret == Gst.StateChangeReturn.FAILURE:
        print("Error starting playback.")
        exit(-1)

    # Wait until error or EOS
    bus = pipeline.get_bus()
    try:
        while True:
            msg = bus.timed_pop_filtered(10 * Gst.MSECOND, Gst.MessageType.ANY)
            if msg:
                if msg.type == Gst.MessageType.ERROR:
                    err, debug = msg.parse_error()
                    if "The device has been disconnected" in str(err):
                        # print("device disconnected")
                        pass
                    else:
                        print(f"Error: {err}, {debug}")
                        break
                elif msg.type == Gst.MessageType.EOS:
                    print("End-Of-Stream reached.")
                    break
                # else:
                #    print(f"Message: {msg.type}")
    except KeyboardInterrupt:
        print("User exit")
    finally:
        pipeline.set_state(Gst.State.NULL)
        Gst.deinit()


if __name__ == "__main__":
    main()

Based on this post I used a valve to drop the audio packets, and then stopped the audio_sink.

When I detect that the usb device is back I can close the valve and reactivate the audio_sink. Sometimes it works, sometimes the sound is a bit “dirty”. I am not sure yet how to properly do this.