When acting as SCO AG / A2DP sink, the remote end should decide when to
close the connection. This does not work currently properly, because
stopping sources/sinks releases the transport, which causes it to go
idle, and which then destroys dynamic nodes. The sources/sinks should
not cause the transport to be released.
Implement keepalive flag for spa_bt_transport, such that
spa_bt_transport_release does not actually release the transport when
the refcount reaches zero. Set the flag for dynamic nodes when the
transport becomes pending (remote end connects) and unset the flag when
idle (remote end disconnected, or dynamic node removed).
Higher priority for A2DP over HFP/HSP. Prefer mSBC over CVSD for HFP,
and put A2DP codecs in the order we tell BlueZ to use.
Ensures that picking highest-priority profile gives sensible results
(e.g. does not pick HFP unless input route is required, and prefers A2DP
duplex codecs over HFP).
In one we can duplicate the spa_asprintf call without real drawbacks.
The second one can be split up without losing details. Note that there
is another one in backend-native.c where splitting it up will make the
code harder to understand. The warning for that one remains.
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.
There doesn't seem to be a way to control the A2DP duplex microphone
HW volume gain, and devices sometimes have very low mic volumes.
Work around this by boosting the software volume scale by +20 dB. If it
causes clipping, the user can just reduce the volume to bring SW gain
below 1.0.
Some non-standard A2DP codecs (FastStream/aptX-LL) have "voice duplex
channel" that can be used to provide an A2DP duplex mode.
Add support for duplex channels, accounting for the fact that the two
directions may be encoded with different actual codecs.
The codec IDs are user-visible properties.
Some codecs can have multiple endpoints (e.g. different caps struct, or
multiple possible vendor ids), so this detail should not leak to the
user.
Mark some structures, arrays static/const at various places.
In some cases this prevents unnecessary initialization
when a function is entered.
All in all, the text segments across all shared
libraries are reduced by about 2 KiB. However,
the total size increases by about 2 KiB as well.
impl_add_listener() could be called more than one time, ensure that we always emit node info
so that session manager(bluez-monitor) can receives it.
Fixes#1308
bluez5 nodes will always be removed & created again during profile changing, hence
node volume & mute will always be reset. This is OK if profile did changed, because
session manager would carries volume & mute to bluez5 route param. But if profile
was not changed after setting profile (a2dp-sink-sbc -> a2dp-sink -> a2dp-sink-sbc),
session manager would think node volume & mute are not changed and no route
setting is performed, causing route volume out of sync with node volume.
To fix this, we emit node volume and mute right after bluez5 node is created.
Fixes#1254
When we add a new listener to an object, it will emit the full state
of the object. For this it temporarily sets the change_mask to all
changes. Restore the previous state after this or else we might not
emit the right change_mask for the next listener.
Consider the case where one there are two listeners on an object.
The object emits a change and the first listener wants to enumerate the
changed params. For this is adds a new listener and then triggers the
enumeration. If we set the change_mask to 0 after adding the listener,
the second listener would get a 0 change_mask and fail to update
its state.
Make a new softVolume property that contains only the soft volume
to apply.
In the case of HW/SW volume, we pass the real volume in the
channelVolume and the leftover volume in softVolume. We don't
use the monitorVolume for this anymore because it is a completely
separate volume handled by the merger node.
This way, channelVolume always represents the effective volume
set on routes, channelmix and merger and only the softVolume (when
available) is applied as software volume by channelmix.
This makes things map a bit better to what is actually happening with
the real volume and leftover software volumes after applying the
hardware volumes in the device.
With this change, the volume on the monitor is not affected by the
sink volume anymore and we can use the monitorVolume for this later.
This also means that the monitor volume in pavucontrol of the sinks
does not change when the sink volume changes. PulseAudio is inconsistent
here: If the volume is HW, the monitor volume is not affected, if the
volume is SW, it is. In PipeWire there is an option in merger to
let the volume affect the monitor with monitor.channel-volumes = true.
Add necessary apis to bluez transport.
Add A2DP(AVRCP) absolute volume support. Source volume can only update to adapter node but not from due to AG nodes don't have route.
Since A2DP/HSP/HFP volume is already percentage itself, it has been mapped to pulseaudio volume then converting to linear volume.
Auto-connect all paired & trusted devices on startup.
Since devices that already connected or powering off would reject the connecting requests, it should be fine with this behavior.
Also reconnect remaining profiles if only partial profiles are connected.
So that we can know if this node has routes or not.
Nodes without routes might need their volumes restored directly
with the node properties, like streams. Nodes with a route need their
volumes set on the device managing the node.
Implement switching HFP codecs in bluez5-devices, currently only for
backend-native.
Codecs are exposed via profiles similarly as for A2DP.
Some hardware appears to not properly reply to the +BCS message. Catch
these cases with a timeout, in which case we fall back to previously
existing transports.
This profile is meant to be used with audio gateways, such as mobile
phones, making pipewire act as a headset. It activates all 3 "dynamic"
nodes (all of which are "Stream/*/Audio"), allowing both A2DP source
and HSP/HFP AG to be available at the same time. Ultimately, the remote
device (the AG), is the one that decides which profile to use and pipewire
just creates/destroys the appropriate stream nodes dynamically.
To make things less confusing, the HFP/HSP profile is now only available
if the remote device is a Head Unit and the A2DP profile is only available
if the remote device has an A2DP Sink.
If the device has both A2DP Source & A2DP Sink (not sure if this is a real world
possibility, but just in case...), the A2DP profile allows using them both,
while the AG profile will only allow the source.
In addition, to keep things less complex, the routes are now only used for
device nodes (the "Audio/*" ones). A2DP source and HSP/HFP AG never have a route.
Restoring their props should be possible to be handled by the restore-stream
module.
errno should be set to the positive errno value.
This does not cause problems except for the pulse-server where the
errno value is negated and returned as an error result.
Typically a source stops the connection when it has nothing to play
and this causes the transport to become "idle" and our A2DP source
also stops. However, the node is still present and "running" (if linked),
which causes the graph to underrun as it receives no data from this node.
This patch dynamically creates and destroys the a2dp source node depending
on the transport state. So, when the transport is idle, there is no node
in the graph at all.