Symmetric follow-up to 47a3479490 which fixed
the same leak in media-sink. media-source transport_start() registers
update_delay_event and dups the transport fd before ensuring SCO I/O; on
HFP failure the fail path only deinited codec_data, leaking the event
source and fd. Early allocation failures also returned without cleanup.
Mirror media-sink: destroy update_delay_event, close the dup'd fd, clear
the decode buffer, and route early errors through the same fail path.
After a6f529b5, opus_05_51/71 only set endpoint_companion. The owner
fill_caps still advertised up to MAX_CHANNELS, there was no combine_caps,
and validate_config was not id-gated. Disabled surround companions remained
visible in endpoint caps, and SetConfiguration could bind a 5.1/7.1 config
to the stereo opus_05 object (wrong bitrate limits / channel profile).
Mirror a2dp-codec-aac.c:
- split fill_caps per codec id (2ch / 5.1 / 7.1; duplex 2+2; pro wide)
- add combine_caps on endpoint owners
- id-gate validate_config so the config-aware resolver picks the companion
- give companions their own fill_caps for the monitor merge path
Codecs that share an A2DP endpoint (e.g. aac + aac_eld) previously used
an asymmetric pattern: one codec (the "owner") had fill_caps and quietly
advertised the union of all siblings' capability bits; companions had
fill_caps = NULL.
This caused a bug: when aac_eld is absent from bluez5.codecs, the "aac"
endpoint still advertised the ELD object-type bit because a2dp_codec_aac's
fill_caps emitted it unconditionally (gated only by eld_supported(), never
by enabled_codecs). A remote could then select ELD; SetConfiguration
resolved the transport to a2dp_codec_aac (the only fill_caps holder for "aac")
Refactor to a symmetric, composable model:
- Add bool endpoint_companion to struct media_codec (default false = owner).
Companion codec objects set this flag instead of fill_caps = NULL.
- Add combine_caps() function to struct media_codec. Owners that share
an endpoint implement this to union two capability blobs of the same
codec_id.
- Each codec's fill_caps now describes only its own bits. AAC LC advertises
only LC object types; AAC ELD advertises only the ELD type (or returns
-ENOTSUP when FDK-AAC lacks ELD support).
- Bump SPA_VERSION_BLUEZ5_CODEC_MEDIA 16 -> 17.
On the monitor side (bluez5-dbus.c):
- New media_codec_fill_endpoint_caps() helper fills the owner's caps then
walks enabled companions on the same endpoint and merges each via
combine_caps. All five endpoint-registration fill_caps call sites are
routed through this helper.
- endpoint_should_be_registered() gates on !endpoint_companion (not on
fill_caps != NULL), so companions correctly skip endpoint registration.
- media_endpoint_to_codec() now filters by is_media_codec_enabled and
prefers owners as a tiebreaker.
- New media_endpoint_to_codec_for_config() resolves SetConfiguration by
calling each enabled candidate's validate_config against the negotiated
config bytes. This ensures that an "aac" endpoint carrying ELD bytes is
mapped to a2dp_codec_aac_eld (not a2dp_codec_aac).
- AAC validate_config and codec_init are id-gated so a stale ELD config
can never be decoded by the LC codec object.
Assisted-by: Claude Opus 4.7
If the filter-graph can apply volume, it will set the softVolume to 1.0
so that the rest of the audioconvert will not apply softVolume anymore.
If it however not applies softVolume, it should pass the given
softVolume and softMute unmodified to be applied elsewhere.
The problem is that the filter-graph completely removed the softVolume
and softMute properties and in case the filter-graph was in use, this
resulted in the channelVolumes being applied in software. This then
could result in double volumes, once in hardware and another time in
software, causing sudden volume loss.
Fixes#5344
transport_start() creates update_delay_event before ensuring SCO I/O.
When spa_bt_transport_ensure_sco_io() fails, the fail path only cleans
up codec_data and leaks the event source on the loop.
Destroy update_delay_event in fail, matching do_remove_source().
A libcamera camera has a set of static properties found in `Camera::properties()`.
Some of the properties are already available for pipewire clients
(Model, Rotation, Location), most of them are not.
So serialize all properties into a strings and make them available on the
pipewire device. The keys have the form "api.libcamera.property.<vendor>.<name>"
and the values are intended to be valid json values, parsable by
python's `json.loads()` or qt's `QJsonValue::fromJson()`.
Controls of type "rectangle", "size", or "point" are not supported for now since
their json forms are not self-evident. They can be added when the need arises.
Add the function `spa_json_encode_stringn()` that works very similarly
to `spa_json_encode_string()`, with the difference that it works with
non-null terminated strings (e.g. `std::string_view`).
`spa_json_encode_string()` is then implemented with this new function.
Additionally, since the input string is no longer null terminated,
escape bytes of value 0. This is probably better that putting
it into the result.
handle_acp_poll() copies the poll revents into ACP and clears them, but
never actually looks at them. When one of the polled fds reports
POLLERR/POLLHUP - which happens when the ALSA card goes away, e.g. the
audio function of a USB dock being unplugged - the source is never
removed, and because that condition is level-triggered the loop just
keeps redispatching the handler.
The result is wireplumber pegged at 100% CPU, spinning on the card's
control fd:
read(24, ..., 72) = -1 ENODEV (No such device)
with fd 24 pointing at a now-deleted /dev/snd/controlC<n>. It stays like
that until the card comes back or the service is restarted.
Accumulate the returned mask and, if anything reports SPA_IO_ERR or
SPA_IO_HUP, drop the poll sources so we stop hammering a dead descriptor.
The udev monitor still drives the real device removal and setup_sources()
re-arms the poll if the card reappears - the same teardown alsa-pcm.c
already does for its own control sources.
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Fix incorrect JSON iterator usage when parsing retransmissions and
max_transport_latency fields in BIS configuration.
The code incorrectly used it[2] iterator instead of it[1] when parsing
BIS object fields, causing undefined behavior and configuration values
being ignored.
Signed-off-by: liujiahe <liujiahe@uniontech.com>
These expected pointer to variables instead of variables directly, which
is inconsistent with how the other atomic macros behave. Furthermore,
SPA_STORE_ONCE was plain broken, since the underlying operation expects
an additional argument that was not included.
For Pro Audio devices, the description is the card name with "Pro" or
Pro 1", "Pro 2", etc. appended. The result is user interfaces showing
around half a dozen devices with descriptions that don't help
disambiguate devices in the slightest.
Append the underlying PCM names instead of the "Pro N" string, so that
devices can be recognised at a glance.
Fixes: https://gitlab.freedesktop.org/pipewire/pipewire/-/work_items/3982
Make this more useful by adding the capture and playback latency.
This structure can be used to say how much latency there is between the
capture and playback hardware.
Make it possible to pass context to plugins and nodes in the
filter-chain.
We can use this to make filters aware of the graph clock or
latency, for example.
Make instantiate create all the related handles in one go.
When a processing node has 1 in/out, multiple handles are created to
support multichannel. By instantiating the handles in one go, the
implementation could do some special multichannel support or do some
optimizations, like parse the config string only once.
Instead of returning a handle (and errno on NULL), return a result code
and store the handle in a return variable.
This makes it easier to handle the errors but also makes it possible to
return multiple handles later.
A forced setup_filter_graphs() deactivates and re-instantiates every graph,
which frees and recreates the underlying plugin handles (node_cleanup sets
node->hndl[i] = NULL before re-instantiating). This was done on a graph that
was still referenced by the RT data-loop snapshot (filter_graph[]), so the
RT thread could run a graph whose handles were NULL mid-rebuild, leading to a
NULL handle dereference in the filter-graph process path.
Mirror the safe ordering already used by load_filter_graph()/clean_filter_handles():
before reconfiguring, mark the graphs not-setup and sync_filter_graph() so the
data loop drops them from filter_graph[] under the loop lock. They are
republished by the sync that follows setup. The cheap snapshot swap is done
under the lock; the heavy re-instantiation stays off the RT path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Handling pending RegisterProfile callbacks was wrong as it forgot that
there can be multiple profiles to be registered.
Fix the handling by allowing several concurrent register callbacks.
No params are implemented, so remove them from the emitted `spa_device_info`.
For v4l2, `n_params` was already set to 0 in 938e2b1451
("v4l2: profiles params are not implement yet"), effectively removing them.
No implementation has materialized in the last 5 years, so remove them
altogether, and do the same in the libcamera plugin as well.
`SPA_PARAM_BUFFERS_blocks` is a specific value, the plugin host should
not use any other number of data planes, so reject other values.
For example, the `buffers[i]->n_datas > planes.size()` situation was
not handled correctly, and this removes the need for handling that.
Expose the libcamera header and library versions in the device properties
similarly to `api.v4l2.cap.version` used by the v4l2 plugin.
The keys are not yet promoted into the public `keys.h` header file.
There was one file "libcamera.c" that was a C source file, which
prevents the addition of C++ functions, includes, etc. to "libcamera.h".
So compile that file as C++ as well.
Always use the pffft aligned alloc function. The fftw alloc function
only aligns to 16 bytes and the AVX code uses stores that rely on an
alignment of 32 bytes. The pffft alloc alignes to 64 bytes.
Fixes#5320
When there is no input buffer because of an xrun or the io was removed,
ramp down the signal. Only remove the port from the mix list when the io
was removed, otherwise, ramp back up when there is a buffers on the
input again.
This avoids pops and click around xrun nodes.
Only ramp down when the IO was removed. We don't want to ramp down
because there was no buffer because of an xrun, because after the
ramp down, the port is removed from the mix ports and stays silent.
Make a new zeroramp.duration and zeroramp.gap property on audioconver.
It detects N samples of silence before triggering a fade-in or fade-out
of the given zeroramp.duration.
The zeroramp.duration is by default 5ms and zeroramp.gap is set to 0.
When the zeroramp.gap is set to 0, the audioconver will not do any gap
detection but it will only do fade-out from the last sample when the IO
Buffer area is removed from a port.
This by default makes the audio adapter perform a fade-out when the last
input of the port mixer was removed and the mixer is no longer scheduled
and the IO Area removed from the audioconverter input port.
When the port IO_Buffers is set, do a fade-in of the next buffer data
into the final mixed output.
When the port IO Buffers in cleared, do a fade-out of that last sample.
Add a zeroramp.duration property that controls the length in seconds of
the fade-in/out curve. By default, set this to 5ms.
The fade-in and fade-outs avoid popping noise from sudden DC voltage
changes when ports a linked and unlinked.
It will also trigger when the upstream peer is paused, because that will
also remove the IO Buffers from the mixer ports.
Fix incorrect assertion condition that compares fragment_size
with max_fragments instead of num_fragments.
The condition should validate num_fragments range, not mix
fragment_size with max_fragments limit.
Since we know DTS and AC3 devices don't provide the snd_pcm_info of the
real underlying device, let's look for them by name (doesn't seem to be
a better way to detect this case), and avoid overwriting any previously
detected h/w device index (from one of the PCM paths).
Always place the follower EnumFormat or Format as internalFormat on
the EnumPortConfig and PortConfig respectively.
This ways, internalFormat always refers to the follower formats and
the format is the external format after (optional) conversion by the
adapter.
The idea was to keep some sort of compatibility with older versions but
that doesn't really apply because the format was never used on
EnumPortConfig and interalFormat didn't exist.
Like the audioconvert node, the DSP mixer embedded a fixed
buffers[MAX_BUFFERS] array (MAX_BUFFERS == 64) in struct port, each
struct buffer holding datas[MAX_DATAS] (MAX_DATAS == 64), reserving
~37 KB per port regardless of the actual buffer/channel count. A DSP
mixer port is created for every link set that mixes into a port, so
this adds up.
Allocate the buffer table and the per-buffer data-pointer pool in a
single calloc in port_use_buffers(), sized to the real number of
buffers and their data blocks, and release it in clear_buffers().
remove_port() now clears the port before zeroing it so a pooled port
does not leak its allocation, and impl_clear() clears the input and
output ports on teardown. Mirrors the audioconvert dynamic-allocation
change.
Co-authored-by: Copilot <copilot@github.com>
The port buffer table was embedded in struct port as a fixed array
buffers[MAX_BUFFERS] of struct buffer, each holding datas[MAX_DATAS]
(MAX_DATAS == SPA_AUDIO_MAX_CHANNELS == 64). This reserved ~17 KB per
port regardless of the actual buffer/channel count, multiplied by every
active port.
Allocate the buffer table and the per-buffer data-pointer pool in a
single calloc in port_use_buffers(), sized to the real number of
buffers and blocks, and release it in clear_buffers(). free_dir() now
clears each port so the allocation (and any mmapped buffer data) is
released on node destruction. This mirrors the dynamic-allocation
approach already used for the channelmix matrices and preserves a lot
of memory in the common low-channel-count case.
Co-authored-by: Copilot <copilot@github.com>