Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDL 3 Loopwave : Audio no output and code freeze on USB Headphone via PulseAudio driver #8788

Closed
Dragon-Baroque opened this issue Jan 6, 2024 · 15 comments · Fixed by #9473
Closed
Assignees
Milestone

Comments

@Dragon-Baroque
Copy link
Contributor

Reference in exult/exult#379

Problem description

Test with loopwave, Linux Fedora 39, USB Headphones over PipeWire, the SDL default driver is PulseAudio => no audio output, and code freeze :

build/test/loopwave --log all
INFO: Available audio drivers:
INFO: 0: pulseaudio
INFO: 1: pipewire
INFO: 2: alsa
INFO: 3: jack
INFO: 4: dsp
INFO: 5: disk
INFO: 6: dummy
INFO: Using audio driver: pulseaudio
<- No audio output, code freeze, does not stop with Ctl-C, must use kill -9 from another session

The SDL drivers PipeWire or ALSA work OK :

SDL_AUDIO_DRIVER=pipewire build/test/loopwave --log all

The SDL driver Jack fails with :

DEBUG: Parameter 'dst_spec->channels' is invalid
ERROR: Couldn't create audio stream: Parameter 'dst_spec->channels' is invalid

The SDL driver DSP fails with :

DEBUG: dsp: No such audio device
ERROR: Couldn't initialize SDL: dsp: No such audio device

The PulseAudio ( pacat -p ) and Pipewire ( pw-cat -p ) play command lines work OK.

When the audio is routed to the PCIe Audio card to Loudspeakers - by switching off the Headphone -, then loopwave with PulseAudio works.

With loopwave via PulseAudio to the USB Headphone under GDB, at code freeze, break with Ctl-C, the GDB query threads returns :

(gdb) info threads
  Id   Target Id                                         Frame 
* 1    Thread 0x7ffff7f32480 (LWP 21213) "loopwave"      0x00007ffff7af71a3 in clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6
  2    Thread 0x7ffff72ec6c0 (LWP 21216) "PulseMainloop" 0x00007ffff7b25bcd in poll () from /lib64/libc.so.6
  3    Thread 0x7ffff7fb96c0 (LWP 21217) "PulseHotplug"  0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
  4    Thread 0x7ffff2a756c0 (LWP 21218) "SDLAudioP27"   0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
(gdb) t 1
[Switching to thread 1 (Thread 0x7ffff7f32480 (LWP 21213))]
#0  0x00007ffff7af71a3 in clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7af71a3 in clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6
#1  0x00007ffff7b09a37 in nanosleep () from /lib64/libc.so.6
#2  0x00007ffff7e61fa2 in SDL_DelayNS_REAL () from <edited>/SDL.git/build/libSDL3.so.0
#3  0x00007ffff7e628d4 in SDL_EnterAppMainCallbacks_REAL () from <edited>/SDL.git/build/libSDL3.so.0
#4  0x00007ffff7c59228 in SDL_EnterAppMainCallbacks () from <edited>/SDL.git/build/libSDL3.so.0
#5  0x0000000000404b59 in SDL_main ()
#6  0x00007ffff7c46e14 in SDL_RunApp_REAL () from <edited>/SDL.git/build/libSDL3.so.0
#7  0x00007ffff7c4c756 in SDL_RunApp_DEFAULT () from <edited>/SDL.git/build/libSDL3.so.0
#8  0x00007ffff7c55cb9 in SDL_RunApp () from <edited>/SDL.git/build/libSDL3.so.0
#9  0x0000000000404b82 in main ()
(gdb) t 2
[Switching to thread 2 (Thread 0x7ffff72ec6c0 (LWP 21216))]
#0  0x00007ffff7b25bcd in poll () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7b25bcd in poll () from /lib64/libc.so.6
#1  0x00007ffff76c7536 in poll_func () from /lib64/libpulse.so.0
#2  0x00007ffff76b0694 in pa_mainloop_poll () from /lib64/libpulse.so.0
#3  0x00007ffff76bb163 in pa_mainloop_iterate () from /lib64/libpulse.so.0
#4  0x00007ffff76bb228 in pa_mainloop_run () from /lib64/libpulse.so.0
#5  0x00007ffff76cb741 in thread () from /lib64/libpulse.so.0
#6  0x00007ffff766dccb in internal_thread_func () from /usr/lib64/pulseaudio/libpulsecommon-16.1.so
#7  0x00007ffff7aac897 in start_thread () from /lib64/libc.so.6
#8  0x00007ffff7b336fc in clone3 () from /lib64/libc.so.6
(gdb) t 3
[Switching to thread 3 (Thread 0x7ffff7fb96c0 (LWP 21217))]
#0  0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
#1  0x00007ffff7aabb09 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libc.so.6
#2  0x00007ffff76c85a4 in pa_threaded_mainloop_wait () from /lib64/libpulse.so.0
#3  0x00007ffff7df06b6 in HotplugThread () from <edited>/SDL.git/build/libSDL3.so.0
#4  0x00007ffff7ce2124 in SDL_RunThread () from <edited>/SDL.git/build/libSDL3.so.0
#5  0x00007ffff7e5e7a4 in RunThread () from <edited>/SDL.git/build/libSDL3.so.0
#6  0x00007ffff7aac897 in start_thread () from /lib64/libc.so.6
#7  0x00007ffff7b336fc in clone3 () from /lib64/libc.so.6
(gdb) t 4
[Switching to thread 4 (Thread 0x7ffff2a756c0 (LWP 21218))]
#0  0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7aa9169 in __futex_abstimed_wait_common () from /lib64/libc.so.6
#1  0x00007ffff7aabb09 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libc.so.6
#2  0x00007ffff76c85a4 in pa_threaded_mainloop_wait () from /lib64/libpulse.so.0
#3  0x00007ffff7def3cd in PULSEAUDIO_WaitDevice () from <edited>/SDL.git/build/libSDL3.so.0
#4  0x00007ffff7c33a19 in OutputAudioThread () from <edited>/SDL.git/build/libSDL3.so.0
#5  0x00007ffff7ce2124 in SDL_RunThread () from <edited>/SDL.git/build/libSDL3.so.0
#6  0x00007ffff7e5e7a4 in RunThread () from <edited>/SDL.git/build/libSDL3.so.0
#7  0x00007ffff7aac897 in start_thread () from /lib64/libc.so.6
#8  0x00007ffff7b336fc in clone3 () from /lib64/libc.so.6
(gdb) q

Complement information

  • PipeWire is installed as pipewire-(various-)1.0.0-2.fc39,

  • PipeWire provides a PulseAudio API in installed pipewire-pulseaudio-1.0.0-2.fc39,

  • The PulseAudio core pulseaudio-16.1-5.fc39 is not installed and cannot be installed because the installed pipewire-pulseaudio is in conflict,

  • The PulseAudio library pulseaudio-libs-16.1-5.fc39 is installed.

  • SDL 3 is at Git main of 2024-01-06, commit e100992, no mod.

@slouken slouken added this to the 3.2.0 milestone Jan 6, 2024
@icculus
Copy link
Collaborator

icculus commented Jan 6, 2024

We had some threading issues with pipewire's Jack compatibility layer, and I wonder if we have some with the Pulse compat too.

@Dragon-Baroque
Copy link
Contributor Author

Could it be possible to place PipeWire before PulseAudio in the bootstrap table of src/audio/SDL_audio.c ?

@icculus
Copy link
Collaborator

icculus commented Jan 25, 2024

Honestly, this isn't a bad idea. I know we talked about this at some point, but if we're just going to end up routing through PIpeWire's compatibility libraries if PipeWire is installed, maybe that should be the default, so if we fallback to PulseAudio, it's really PulseAudio (probably), etc.

@flibitijibibo, @slouken: I think y'all were there when we talked about this before. What stops us from just flipping PIpeWire's bootstrap priority, again?

EDIT: For SDL3 for sure, but also for SDL2?

@slouken
Copy link
Collaborator

slouken commented Jan 25, 2024

I thought there were issues with pipewire that went away if we used pulseaudio emulation. I don't remember the specifics though. I'll look around and see if I can find the details. Maybe our pipewire implementation just needs some good exercising to flush out the bugs?

@flibitijibibo
Copy link
Collaborator

The last TODO is the ability to know from PipeWire that it's being used for audio, which I'm tracking over at #5268.

@icculus
Copy link
Collaborator

icculus commented Jan 26, 2024

Mentioned in 5268:

Adding this to 2.28 - I have someone working on implementing PipeWire session detection right now, and expect this to be upstreamed into PW/WP by the end of the year, which should make it feasible to make the switch for the release after 2.26.

This was October 2022; did this fizzle?

@flibitijibibo
Copy link
Collaborator

Indeed it did - the PipeWire maintainers have an implementation in mind, they just need someone to type it out.

@Kontrabant
Copy link
Contributor

Have any newer distros not switched to Pipewire for audio mixing yet? Maybe SDL3 could default to Pipewire as long as the underlying version is 1.0 or higher (or it was explicitly requested), as anything running an up-to-date Pipewire version is basically guaranteed to be using it for audio mixing. Even a niche distro like AVLinux that was historically opposed to it has switched over in the newest version.

If some distro does insist on shipping Pipewire while using legacy Pulseaudio, they can always patch their shipping version of SDL to restore the higher Pulseaudio priority, and people who intentionally start ripping out the sound system that ships with their distro in favor of some legacy setup (e.g. ALSA+dmix or OSS) already have to set the necessary envvars manually.

@icculus
Copy link
Collaborator

icculus commented Jan 26, 2024

Maybe, but the concern isn't what distros will do with their copy of SDL, it's what Steam's copy of SDL will do on every distro. :)

@Dragon-Baroque
Copy link
Contributor Author

I have a tiny patch detecting in the SDL PulseAudio driver whether the PulseAudio server is actually PipeWire.
It consists essentially in asking for the PulseAudio server name and looking whether "PipeWire" occurs in it.
In that case, the SDL PulseAudio driver fails and leaves SDL to retry with the next driver : PipeWire.

The PulseAudio server name is pulseaudio if it is native PulseAudio, it is PulseAudio (on PipeWire x.y.z) if it is PipeWire.
To check with the command line : pactl info.

Would this be acceptable ?

@flibitijibibo
Copy link
Collaborator

That seems reasonable to me... not like there's another server out there that would get mixed up in there somehow, and we can always flip it around when it's possible to detect PE the "correct" way.

@Kontrabant
Copy link
Contributor

The code that does this would need an #ifdef check to make sure that the Pulse driver isn't skipped if SDL wasn't built with the Pipewire driver though.

@Dragon-Baroque
Copy link
Contributor Author

make sure that the Pulse driver isn't skipped if SDL wasn't built with the Pipewire driver though.

Please, why so ? SDL would fallback to ALSA, then, and work.

Whereas just exchanging the PipeWire driver and the PulseAudio driver, in the priority order, as I preferred first, would still hang if SDL was not built with the support of PipeWire.

And if a fallback driver was lacking, I would still prefer to have SDL report that it could not initialize the audio subsystem than having to open another terminal to kill a hung application - Control-C does not work.

@Dragon-Baroque
Copy link
Contributor Author

Dragon-Baroque commented Mar 18, 2024

I have debugged the SDL 3 + PulseAudio driver + PulseAudio daemon provided by PipeWire.

The hang in loopwave only occurs with my USB Headphones, loopwave works correctly with the other audio devices in my Linux PC.

I can avoid the hang with the headphones by increasing the size of the sample buffer, set in GetDefaultSampleFramesFromFreq in src/audio/SDL_audio.c from 1024 at 48000 Hz to 2048. I can do that with the SDL Hint SDL_AUDIO_DEVICE_SAMPLE_FRAMES.

Or I can avoid the hang if I remove the flags |= PA_STREAM_ADJUST_LATENCY; in src/audio/pulseaudio/SDL_pulseaudio.c. This flag has the effect of reducing the size of the sample buffer.

As for myself, I have settled on export SDL_AUDIO_DRIVER=pipewire in my login shell, to avoid the PulseAudio driver altogether.

I may add that the PulseAudio daemon provided by Pipewire, when it is set with a «too small» sample buffer, fails to send the required command PA_COMMAND_STARTED to start the audio playback and the commands PA_COMMAND_REQUEST to obtain a sample buffer from the client. Thus the write callback of PulseAudio is SDL in never invoked, the first sample buffer is not used, and no sound are produced.

I think that the issue might be closed.

@Dragon-Baroque
Copy link
Contributor Author

I am glad to report that this PR #9473 fixes the hang in Exult choosing PulseAudio over PipeWire :

I have removed my SDL_AUDIO_DRIVER=pipewire, then tested my migration of Exult to SDL 3 in Linux, both in KDE over Wayland and in Gnome over Wayland. Both attempts chose PipeWire and worked.

Not only that, I also find quite nice the idea of having the PipeWire driver occurring twice in the Audio driver list, with the first occurrence having more stringent but possibly failing tests than the second and original occurrence, thus preserving compatibility.

In the name of the Exult team, I am proffering my thanks for this solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants