pipewire-alsa Format change issue
Steps:
- start deadbeef
- click play
Background:
DeaDBeeF Music Player has an architecture where it starts off with a basic format of 44100Hz, 2ch, 16bps. Then during decoding stage it changes the format. So output plugins and audio backends needs to handle this. I can cofirm that the format change happens from deadbeef and its output plugins side. It does call snd_pcm_hwparams()
. The logs also make this clear.
Yet the format isn't updated by pipewire.
Output from PIPEWIRE_DEBUG=3 deadbeef
:
[I][000596772.907262][pipewire.c:458 pw_init()] version 0.3.20
[I][000596772.909525][local-socket.c:96 try_connect()] connecting to 'pipewire-0' runtime_dir:/run/user/1001
[I][000596772.909774][pcm_pipewire.c:670 snd_pcm_pipewire_hw_params()] alsa-plugin 0x7f196400ac50: format:Spa:Enum:AudioFormat:S16LE channels:2 rate:44100 stride:4 blocks:1
[I][000596772.910383][impl-node.c:860 check_properties()] (PipeWire ALSA [deadbeef]-0) latency:1024/44100 ->quantum 1024/48000
[I][000596772.911091][impl-node.c:860 check_properties()] (PipeWire ALSA [deadbeef]-0) latency:1024/44100 ->quantum 1024/48000
[I][000596772.911168][impl-node.c:315 node_update_state()] (PipeWire ALSA [deadbeef]-0) creating -> suspended
[I][000596772.911232][context.c:893 pw_context_recalc_graph()] context 0x7f1964013340: busy:0 reason:register active node
[I][000596772.913035][context.c:893 pw_context_recalc_graph()] context 0x7f1964013340: busy:0 reason:node flags changed
[I][000596772.914289][pcm_pipewire.c:321 on_stream_param_changed()] alsa-plugin 0x7f196400ac50: buffer_size:4096 period_size:1024 buffers:4 size:4096 min_avail:1024
[I][000596772.914433][fmtconvert.c:165 setup_convert()] fmtconvert 0x7f196c448198: Spa:Enum:AudioFormat:S16LE/2@44100->Spa:Enum:AudioFormat:F32P/2@44100
[I][000596772.914458][splitter.c:591 setup_convert()] splitter 0x7f196c471468: Spa:Enum:AudioFormat:F32P/2@48000->Spa:Enum:AudioFormat:F32P/1@48000x2
[I][000596772.914471][channelmix.c:268 setup_convert()] channelmix 0x7f196c451588: Spa:Enum:AudioFormat:F32P/2@44100->Spa:Enum:AudioFormat:F32P/2@44100 00000018:00000018
[I][000596772.914511][resample.c:146 setup_convert()] resample 0x7f196c467048: Spa:Enum:AudioFormat:F32P/2@44100->Spa:Enum:AudioFormat:F32P/2@48000
[I][000596772.915047][impl-node.c:315 node_update_state()] (PipeWire ALSA [deadbeef]-17) suspended -> running
[I][000596772.963463][context.c:893 pw_context_recalc_graph()] context 0x7f1964013340: busy:0 reason:node deactivate
[I][000596772.963554][pcm_pipewire.c:670 snd_pcm_pipewire_hw_params()] alsa-plugin 0x7f196400ac50: format:Spa:Enum:AudioFormat:S24LE channels:6 rate:44100 stride:18 blocks:1
[I][000596772.964333][impl-node.c:315 node_update_state()] (PipeWire ALSA [deadbeef]-17) running -> idle
[I][000596772.964413][context.c:893 pw_context_recalc_graph()] context 0x7f1964013340: busy:0 reason:node activate
[I][000596772.965280][impl-node.c:315 node_update_state()] (PipeWire ALSA [deadbeef]-17) idle -> running
[I][000596811.084846][context.c:893 pw_context_recalc_graph()] context 0x7f1964013340: busy:0 reason:node deactivate
[I][000596811.086154][impl-node.c:315 node_update_state()] (PipeWire ALSA [deadbeef]-17) running -> idle
The highlight from the log above is:
[I][000596772.963554][pcm_pipewire.c:670 snd_pcm_pipewire_hw_params()] alsa-plugin 0x7f196400ac50: format:Spa:Enum:AudioFormat:S24LE channels:6 rate:44100 stride:18 blocks:1
pw-dump
on the node shows wrong format:
"params": {
"EnumFormat": [
{
"mediaType": "audio",
"mediaSubtype": "raw",
"format": "S16LE",
"rate": 44100,
"channels": 2,
"position": [ "FL", "FR" ]
}
],
...
"Format": [
{
"mediaType": "audio",
"mediaSubtype": "raw",
"format": "S16LE",
"rate": 44100,
"channels": 2,
"position": [ "FL", "FR" ]
}
A more complete debug log (LEVEL 4): https://gist.github.com/saivert/3dcc6a320d90e93b9d3dba7bd7f75324
This code in pcm_pipewire.c is suspect:
pw->min_avail = SPA_MAX(pw->min_avail, min_period);
pw_log_debug(NAME" %p: prepare %d %p %lu %ld", pw,
pw->error, pw->stream, io->period_size, pw->min_avail);
if (pw->error >= 0 && pw->stream != NULL)
goto done;
if (pw->stream != NULL) {
pw_stream_destroy(pw->stream);
pw->stream = NULL;
}