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
The AAC-ELD support was not properly tested on devices. In theory it
should be OK, but it's untested.
Bump it down in priority so it won't be selected by default.
Also log info on FDK-AAC AAC-ELD support status.
GCC 14 introduces a new -Walloc-size included in -Wextra which gives:
```
./pipewire-0.3.84/spa/plugins/bluez5/codec-loader.c:176:14: warning: allocation of insufficient size ‘1’ for type ‘struct impl’ with size ‘1032’ [-Walloc-size]
```
The calloc prototype is:
```
void *calloc(size_t nmemb, size_t size);
```
So, just swap the number of members and size arguments to match the prototype, as
we're initialising 1 struct of size `sizeof(struct impl)`. GCC then sees we're not
doing anything wrong.
Signed-off-by: Sam James <sam@gentoo.org>
AVDTP in principle allows 62 endpoints, but in practice it appears some
devices (Samsung Galaxy Buds2 Pro, Redmi Buds 3 Lite, probably others)
fail to connect A2DP when the number is somewhere above 24. A2DP
connection works when initiated from the Central, but not when the
device itself does it, so these devices are not fully broken. We should
reduce the number of registered A2DP endpoints to avoid running into
problems with such broken devices.
Some of our source codecs are the same actual codec with the same
configuration, and don't need separate source endpoints.
Allow codecs to not have a registered endpoint (fill_caps == NULL), and
tolerate codecs with the same endpoint name. In codec switch, keep
track separately which of the codecs with the same endpoint name the
local endpoint is currently associated with.
Support Opus as A2DP vendor codec.
The specification for vendor A2DP codec is our Pipewire-specific one, so
it is compatible only with devices running Pipewire.
Change codec factory names to api.codec.bluez5.*, so that they won't
conflict with old config file lib name rules for api.bluez5.*
Specify the fallback library name when loading the codecs, so that it
works without the rules in config files.
Make easier to package A2DP codecs separately, by splitting each to a
separate SPA plugin. Adjust the code to not use a global variable for
the codec list.
The A2DP SPA interface API is in the bluez5 private headers, and not
exposed in installed SPA headers, as it's too close to the
implementation.