bluez5: fix shared A2DP endpoint caps including disabled companion codecs

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
This commit is contained in:
George Kiagiadakis 2026-06-30 17:39:53 +03:00
parent 0a413c866c
commit 4e46489964
6 changed files with 281 additions and 78 deletions

View file

@ -136,11 +136,13 @@ static int load_media_codecs_from(struct impl *impl, const char *factory_name, c
continue;
}
/* Don't load duplicate endpoints */
/* Don't load duplicate endpoints. Two owners on the same
* endpoint is a conflict; companions sharing an owner are not.
*/
for (j = 0; j < impl->n_codecs; ++j) {
const struct media_codec *c2 = impl->codecs[j];
const char *ep2 = c2->endpoint_name ? c2->endpoint_name : c2->name;
if (spa_streq(ep, ep2) && c->fill_caps && c2->fill_caps) {
if (spa_streq(ep, ep2) && !c->endpoint_companion && !c2->endpoint_companion) {
spa_log_debug(impl->log, "media codec %s from %s duplicate endpoint %s",
c->name, factory_name, ep);
goto next_codec;