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
Memory Safety: High
Several Bluetooth audio codec implementations calculate codesize by
multiplying samples * channels * sizeof(sample_type) without overflow
checks. The parameters come from Bluetooth codec negotiation, which is
influenced by the remote peer. If the multiplication overflows, codesize
wraps to a small value, causing subsequent buffer size checks to pass
while the actual data processing operates on the full (larger) sample
count, leading to heap buffer overflows.
Affected codecs: LC3 (BAP), LC3plus (A2DP), Opus (A2DP), Opus-G (A2DP).
Add overflow checks before each codesize multiplication to ensure the
result fits in the target integer type, returning -EINVAL on overflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
With BAP codec configuration selection goes via multiple functions,
which will need to maintain some private state.
Adjust media_codec to allow for that.
Use it for get_qos().
Pass zero-length packets to the codec. BAP/ISO may use these to indicate
missing data.
Fix A2DP codecs to not parse input with spa_return_val_if_fail, that's
meant for assertions. Just return -EINVAL directly, it's normal that
input data may contain garbage.
Currently, the PipeWire daemon registers BlueZ LE Media Endpoints
with audio capabilities covering all settings defined in the BAP spec.
However, some scenarios might require the capabilities to be restricted
to specific configurations.
This adds a method to read LC3 codec specific capabilities from the
Wireplumber config file, and provide those settings when registering
Media Endpoint objects with BlueZ. If the values are not present in
the config file, all settings will be used by default.
Below is an example of how to set the LC3 capabilities in the config
file, to support the 16_2 setting from the BAP spec:
bluez5.bap-server-capabilities.rates = [16000]
bluez5.bap-server-capabilities.durations = [10]
bluez5.bap-server-capabilities.channels = [1, 2]
bluez5.bap-server-capabilities.framelen_min = 40
bluez5.bap-server-capabilities.framelen_max = 40
bluez5.bap-server-capabilities.max_frames = 2
Encoders and some decoders have additional internal latency that needs
to be accounted for.
This mostly matters for AAC (~40ms), as the other BT codecs have much
lower delays (~5ms).
This provides access to GNU C library-style endian and byteswap functions.
Windows doesn't provide pre-processor defines for endianness, but
all current Windows architectures (X32, X64, ARM) are little-endian.
React immediately to "bad" buffer level. Use smaller bitrate
increments/decrements. Fix ABR timer increment for fragmented packets.
Handle actual bitrate being smaller than target during silence.
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.