Issue getting any QT application with a GUI to play nice with pulseaudio
-
I have an embedded linux device that is running linux kernel 4.4 and QT5.9.1 with busybox. I have two applications written in QT, one with a GUI and another backend service also written in QT but has no GUI or widgets whatsoever, console only. I've recently been trying to pair and connect bluetooth audio devices to record and playback audio using pulseaudio and bluez. I also have a camera that records live video displayed on the GUI while recording audio from the bluetooth headset. This is done using gstreamer in the wirelesscontroller service.
This all works fine, but when going to play back saved videos with the audio I use the Video widget in the GUI to simplify things. Unfortunately it doesnt recognize the default audio device from pulseaudio as being the bluetooth headset/speaker and instead always forces it out of the 3.5mm audio jack. I tried using this code to print out the list of available audio devices as well as what QT thinks is the default audio device:
const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); for (const QAudioDeviceInfo &deviceInfo : deviceInfos) qDebug() << "Device name: " << deviceInfo.deviceName(); qWarning() << "default device: " << QAudioDeviceInfo::defaultOutputDevice().deviceName();
I originally tried this in the backend wirelesscontroller service and it prints this:
[M] qWirelessController WirelessControllerMain::init - Device name: "default" [M] qWirelessController WirelessControllerMain::init - Device name: "default:CARD=rockchipwitorch" [M] qWirelessController WirelessControllerMain::init - Device name: "sysdefault:CARD=rockchipwitorch" [M] qWirelessController WirelessControllerMain::init - Device name: "bluez_sink.0C_A6_94_E4_22_B9.headset_head_unit" [M] qWirelessController WirelessControllerMain::init - Device name: "alsa_output.0.stereo-fallback" [W] qWirelessController WirelessControllerMain::init - default device: "bluez_sink.0C_A6_94_E4_22_B9.headset_head_unit"
So the backend service has no problem recognizing the default audio device. I tried adding this same code snippet to my GUI application and I get this error message:
[W] MyGUI - PulseAudioService: pa_context_connect() failed with return: -1 [M] MyGUI HMIMain::init - Device name: "default" [M] MyGUI HMIMain::init - Device name: "default:CARD=rockchipwitorch" [M] MyGUI HMIMain::init - Device name: "sysdefault:CARD=rockchipwitorch" [M] MyGUI HMIMain::init - Device name: "" [W] MyGUI HMIMain::init - default device: ""
It errors out trying to connect to the pulseaudio service. Ive searched a lot on this error and so far nothing ive found is either relevant to my setup or has fixed my issue. Ive also found that this error occurs on ALL GUI applications. I built a number of QT examples with and without GUIs and put that code snippet in them. All of the GUI applications give me the same pa_context_connect() error whereas all of the console based applications have no problem connecting with the pulse audio service.
From what I can tell, it does not matter how many QT console applications are running, they all are able to connect to the pulseaudio service. I've also made sure that no other user applications are running when trying to get my GUI application to connect with pulseaudio. Currently I'm starting pulseaudio with these parameters:
/usr/bin/pulseaudio -D --exit-idle-time=-1
I have tried launching it with the --system parameter as well as setting the environment variable PULSE_SERVER=localhost and both of those result in me getting the following error:
"PulseAudioService: Connection failure: Connection refused"
If I run pulseaudio in the background (not as a daemon) and --vvvv I see it print out a lot of information when my backend service connects to it but naturally, I see absolutely nothing when my GUI application attempts to connect to it.
I have also tried removing all my pulseaudio cookies in the .config/pulse/ folder as one thread suggested but that also didnt change anything.
As far as the configuration files in /etc/pulse/ I have reverted them all to stock. However, the only change I've had to make was adding "load-module module-stream-restore restore_device=false" to default.pa to get the default sink to maintain.
I really dont want to add more gstreamer code to play back videos so I would really like to figure out what is going on here.
-
Hi and welcome to devnet,
You should start your GUI app with the QT_DEBUG_PLUGINS environment variable set to 1 to see what is going on with the multimedia backend.
-
@SGaist said in Issue getting any QT application with a GUI to play nice with pulseaudio:
QT_DEBUG_PLUGINS
I have not seen this environment variable before so thank you!
i set the environment variable and launched my application again and this is all the audio stuff i see. if theres more information that you think is pertinent let me know. but the vast majority of it is the eglfs plugins, touchscreen, SQL, etc loading.
[M] MyGUI - QFactoryLoader::QFactoryLoader() checking directory path "/usr/lib/qt/plugins/audio" ... [M] MyGUI - QFactoryLoader::QFactoryLoader() looking at "/usr/lib/qt/plugins/audio/libqtaudio_alsa.so" [W] MyGUI - Found metadata in lib /usr/lib/qt/plugins/audio/libqtaudio_alsa.so, metadata= { "IID": "org.qt-project.qt.audiosystemfactory/5.0", "MetaData": { "Keys": [ "alsa" ] }, "className": "QAlsaPlugin", "debug": false, "version": 329988 } [M] MyGUI - Got keys from plugin meta data ("alsa") [M] MyGUI - QFactoryLoader::QFactoryLoader() looking at "/usr/lib/qt/plugins/audio/libqtmedia_pulse.so" [W] MyGUI - Found metadata in lib /usr/lib/qt/plugins/audio/libqtmedia_pulse.so, metadata= { "IID": "org.qt-project.qt.audiosystemfactory/5.0", "MetaData": { "Keys": [ "default" ] }, "className": "QPulseAudioPlugin", "debug": false, "version": 329988 } [M] MyGUI - Got keys from plugin meta data ("default") [M] MyGUI - QFactoryLoader::QFactoryLoader() checking directory path "/mnt/app/bin/audio" ... [M] MyGUI - loaded library "/usr/lib/qt/plugins/audio/libqtaudio_alsa.so" [M] MyGUI - loaded library "/usr/lib/qt/plugins/audio/libqtmedia_pulse.so" [W] MyGUI - PulseAudioService: pa_context_connect() failed with return: -1 [M] MyGUI HMIMain::init - Device name: "default" [M] MyGUI HMIMain::init - Device name: "default:CARD=rockchipwitorch" [M] MyGUI HMIMain::init - Device name: "sysdefault:CARD=rockchipwitorch" [M] MyGUI HMIMain::init - Device name: "" [W] MyGUI HMIMain::init - default device: ""
-
Are you building Qt yourself ?
-
yes, I build QT with buildroot for my embedded system. its weird though that it works for non GUI applications just fine.
-
@rabidmonkeyman said in Issue getting any QT application with a GUI to play nice with pulseaudio:
pa_context_connect
I'm a sucker for a good mystery. This thread has got my rapt attention!
It certainly seems like the failed call to
pa_context_connect
is at the root of all this. My gut tells me that if the call would succeed in the GUI application, then all else would run as desired after that.So why is it failing? I don't have an answer now. Not sure when I might carve out some free time, but I might try this myself (but I am on Ubuntu 18 using Qt 5.15.0 so my results might differ).
An early rough idea:
- could a different copy of
libpulse
be in memory for each app? Your GUI loading one copy oflibpulse
and the headless process loading another?
I just tried to figure out if Qt even links against
libpulse
, and on my system I see that it does:$ export MY_QT_INSTALL_ROOT=$PWD $ cd ${MY_QT_INSTALL_ROOT}/lib $ ls -F | grep '\*' | awk -F '\*' '{print $1}' | xargs -l grep -r pa_context_connect awk: warning: escape sequence `\*' treated as plain `*' Binary file libQt5Multimedia.so.5.15.0 matches Binary file libQt5WebEngineCore.so.5.15.0 matches $ ldd libQt5Multimedia.so.5.15.0 linux-vdso.so.1 (0x00007fff70993000) libQt5Network.so.5 => /opt/repositories/KEEP_qt5_dbg_install/lib/./libQt5Network.so.5 (0x00007f79a2797000) libQt5Gui.so.5 => /opt/repositories/KEEP_qt5_dbg_install/lib/./libQt5Gui.so.5 (0x00007f79a1f1a000) libQt5Core.so.5 => /opt/repositories/KEEP_qt5_dbg_install/lib/./libQt5Core.so.5 (0x00007f79a16ec000) libpulse.so.0 => /usr/lib/x86_64-linux-gnu/libpulse.so.0 (0x00007f79a149c000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f79a1113000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f79a0d75000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f79a0984000)
You could also try
lsof
on both a healthy process and the unhealthy GUI process and see if they use the same libpulse:lsof -p ${TARGET_PID} | grep libpulse
That might in fact be quite instructive. I just did that on a
VLC
process, and I get a number ofso
files showing up. So in your case, there might be more than one pulse-relatedso
that could be different or missing between the two processes:/usr/lib/x86_64-linux-gnu/vlc/plugins/services_discovery/libpulselist_plugin.so /usr/lib/x86_64-linux-gnu/pulseaudio/libpulsecommon-11.1.so /usr/lib/x86_64-linux-gnu/libpulse.so.0.20.2 /usr/lib/x86_64-linux-gnu/vlc/plugins/audio_output/libpulse_plugin.so
Setting PulseAudio environment variables:
Refer to: https://freedesktop.org/software/pulseaudio/doxygen/index.html#logging_sec
If I'm reading that correctly, then:
export PULSE_LOG=4
would be worth setting prior to launching your process. (Again, I would try this with both the healthy and unhealthy process to compare.)
I can see that the
PULSE_LOG
var is honored withpaplay
at least:paplay test_tone.wav # total absence of console output export PULSE_LOG=4 paplay test_tone.wav Parsing configuration file '/etc/pulse/client.conf' /etc/pulse/client.conf.d does not exist, ignoring. Using shared memfd memory pool with 1024 slots of size 64.0 KiB each, total size is 64.0 MiB, maximum usable slot size is 65472 Trying to connect to {59ebf8d984d64ab58ebdac557f925927}unix:/run/user/1000/pulse/native... SHM possible: yes Protocol version: remote 32, local 32 Negotiated SHM: yes Memfd possible: yes Negotiated SHM type: shared memfd
- could a different copy of
-
I also searched the Qt bug-tracker for pulse-related issues and came across one interesting item:
https://bugreports.qt.io/browse/QTBUG-61725
This hints at Qt-specific environment variables that might be of use to you. But I haven't read the bug closely enough yet to even determine whether the proposed changes were agreed to and implemented or not.
-
export PULSE_LOG=4
I tried launching my application with this environment variable and found it printing this to the console:
Parsing configuration file '/etc/pulse/client.conf' /etc/pulse/client.conf.d does not exist, ignoring. Using shared memfd memory pool with 1024 slots of size 64.0 KiB each, total size is 64.0 MiB, maximum usable slot size is 65496 Not doing autospawn since we are root. Trying to connect to /tmp/.xdg/pulse/native... connect(): No such file or directory (2) Trying to connect to /var/run/pulse/native... connect(): No such file or directory (2) [W] MyGUI - PulseAudioService: pa_context_connect() failed
whereas the working non-gui application prints this:
/etc/pulse/client.conf.d does not exist, ignoring. Using shared memfd memory pool with 1024 slots of size 64.0 KiB each, total size is 64.0 MiB, maximum usable slot size is 65496 Trying to connect to /tmp/pulse-bVjgNsgqbmYR/native... SHM possible: yes Protocol version: remote 32, local 32 Negotiated SHM: yes Memfd possible: yes Negotiated SHM type: shared memfd
okay, I recognize the /tmp/.xdg/ portion in the GUI application as being the environment variable we need to set to get the wayland plugin to work:
export XDG_RUNTIME_DIR=/tmp/.xdg
so I tried replacing the /tmp/.xdg with /tmp/pulse-bVjgNsgqbmYR/native and it wouldnt launch because it crashes when trying to connect to the wayland server.
in your link to the pulse documentation I saw it mention an environment variable PULSE_SERVER so i left the XDG_RUNTIME_DIR to what it was and set PULSE_SERVER to /tmp/pulse-bVjgNsgqbmYR/native and it worked!
why would QT attempt to use the XDG_RUNTIME_DIR environment variable to try and look up the pulse server? obviously that pulse directory is different with every boot as it is a cookie of some sort so i dont necessarily want to create some sort of startup script to find the server socket in the temp directory and set the PULSE_SERVER but if its what will solve this i guess thats what ill have to do. why cant the GUI just use the default implementation like the console applications and not use the XDG_RUNTIME_DIR?
edit: i should clarify, the XDG_RUNTIME_DIR is set for both my console application and the GUI application.
-
@rabidmonkeyman said in Issue getting any QT application with a GUI to play nice with pulseaudio:
why would QT attempt to use the XDG_RUNTIME_DIR environment variable to try and look up the pulse server?
I don't know enough about XDG or pulse to answer that. However, it does seem like the appropriate question to be pondering.
It also continues to be fascinating that the non-gui process does it one way, and the gui process does it another way.
At this point, it seems like you might try:
- asking on the mailing list? (i'm not sure if "interest" list or "developer" list would be correct. I lean towards the developer list)
- filing an issue on https://bugreports.qt.io
It's unclear if there is an actual bug, but the fact that a Qt GUI app cannot seem to find the correct PULSE_SERVER (without help from an environment var) seems like a bug given that the non-gui app is succeeding. I would take the non-gui behavior as the "intended/expected behavior", and then by comparison the GUI app behavior seems like a bug.
If you do head to the bug-tracker, you might try to see if just adding a line or two to one of the provided sample apps will reproduce the issue. Or... if there exists both a non-gui and a gui example, then maybe all you need to do is launch both examples and the bug reproduces trivially.