Change media-sink to use sco-io for HFP codecs.
Move SCO fragmentation to sco-io side.
Replace sco-sink with media-sink.
sco-sink is mostly copypaste from media-sink, and only differed in the
fragmentation detail, which can as well be handled on sco-io side.
Unicast BAP codec switch requires CIG reconfiguration, which cannot be
done if there is an acquired transport.
When doing BAP codec switch, disable nodes of other devices sharing the
same CIG.
To avoid problems with node start/stop, just remove and re-add them.
Use SelectProperties() DBus API to reconfigure BAP unicast setup.
Add support to spa_bt_ensure_codec() to select whether to configure as
sink/source/duplex.
Simplify codec switching code: determine what switch to perform
immediately in spa_bt_device_ensure_media_codec().
The previous code doing "fallback" switching to various codecs is not
useful, as A2DP generally disconnects on the first failure and all
remote endpoints disappear.
Add iteration over multiple endpoints, for reconfiguring both source and
sink directions at the same time. This is in preparation of supporting
BAP reconfiguration (for A2DP there's usually only one direction
connected at a time).
config.h needs to be consistently included before any standard headers
if we ever want to set feature test macros (like _GNU_SOURCE or whatever)
inside. It can lead to hard-to-debug issues without that.
It can also be problematic just for our own HAVE_* that it may define
if it's not consistently made available before our own headers. Just
always include it first, before everything.
We already did this in many files, just not consistently.
Including C headers inside of `extern "C"` breaks use from C++. Hoist
the includes of standard C headers above the block so we don't try
to mangle the stdlib.
I initially tried to scope this with a targeted change but it's too
hard to do correctly that way. This way, we avoid whack-a-mole.
Firefox is working around this in their e21461b7b8b39cc31ba53c47d4f6f310c673ff2f
commit.
Bug: https://bugzilla.mozilla.org/1953080
Set different icons for A2DP & HFP output routes, so that they look
different (in Gnome).
Don't call the non-HFP output route as "headset" or "handsfree" in this
case, to be less ambiguous about microphone availability.
Also set device.icon-name for the device too.
ASHA devices with the same HiSyncId are a pair and this
will be used later on to set them up together with a
combine sink like device set for BAP. Side information
is required for channel information when setting up the
combine sink.
Use kernel-provided packet reception timestamps to get less jitter in
packet timings. Mostly matters for ISO/SCO which have regular schedule.
A2DP (L2CAP) doesn't currently do RX timestamps in kernel, but we can as
well use the same mechanism for it.
When in A2DP sink role and remote end switches codec, BlueZ nowadays
appears sometimes emit first SetConfiguration (creating new transport),
and then ClearConfiguration (removing old transport).
Handle this case: emit profiles_changed event always when transports
come/go.
Redefine profiles_changed() to take bitmask of profiles whose connection
status changed, so we don't need to emit two remove+add events.
Now that BlueZ supports delay reporting in A2DP sink role, implement
that.
Report value that gives the total latency between packet reception and
audio rendering.
Also make Latency parameter in media-source to be not just a dummy
value.
A new 'broadcasting' state is to be added in BlueZ. It is
functionally similar to 'pending', but for the Broadcast scenario.
Until now, on Broadcast sink side, the transports associated with a
scanned source would automatically be switched to pending. PipeWire
then acquired any transport that was pending.
This is to be changed, transports will now remain in 'idle' state
until the user calls transport.select on them from bluetoothctl.
This changes the state to 'broadcasting'. PipeWire will then acquire
these selected transports.
This way, the user can select to which sink he wishes to sync.
Kernel-provided MTU does not work for USB controllers and the correct
packet size to send can be known currently only from RX. So we are
waiting for RX to get it.
The known problem is USB-specific, we shouldn't need the workaround for
other transport types.
Don't wait for POLLIN for non-USB controllers on connect, but ready
things on POLLOUT as usual.
For non-USB controllers, pick some sensible packet sizes to use
initially, before we switch to same size as for RX.
BlueZ API as BAP Server gives us the ISO interval, instead of the SDU
interval, in the MediaTransport.QoS.Interval property. They are not
necessarily the same. What we need is the SDU interval.
The SDU interval is the interval between packets the encoder outputs, so
it is determined by the codec configuration, and for LC3 is equal to the
frame duration.
Add codec method get_interval() that returns the correct interval, and
use it in iso-io.
HFP 1.9 adds LC3 as a possible codec in addition to CVSD & mSBC.
E.g. Pixel Buds Pro latest firmware supports it.
Add the RFCOMM side and codec selection for it.
Don't have separate input route for A2DP and HFP, as it is generally not
necessary.
When in A2DP mode when there's also HFP possible, emit the input route
in SPA_PARAM_Route, even though there is no corresponding input node
emitted.
The host may then emit a loopback microphone node, and switch profiles
according to its status. Having the input route available at all times
allows to retain changes to volume settings made when there is no real
input node.
Make supported codec checks to use profiles, not "is-a-sink" flag, to
determine which codecs can be used.
Fixes bluez5-device checking only source profiles, even when the local
device is only a sink.
Once Pipewire is started it will try to register a BAP broadcast source media endpoint on UUID 00001852-0000-1000-8000-00805f9b34fb if the media codec that supports BAP and the adapter indicates LE Audio is supported.
When the endpoint is detected (over DBus) by Pipewire and it has a broadcast sink UUID, a new device will be created with the address 00:00:00:00:00:00. This device will be our simulated remote device. This is done because a broadcast source emitting device does not need any connection to start transmitting the audio. This device is set as connected.
When the SetConfiguration DBus method is called and the spa_bt_transport structure with the profile BAP broadcast source is created we switch the device from the one read from DBus to the one created by us. This is done because in BlueZ, when the transport is created, at the Device property, BlueZ sets the adapter as the device that the transport is connected to. Here the device will have the newly created SPA_BT_PROFILE_BAP_BROADCAST_SINK profile connected.
Added code that allows to create a node in the graph for a device connected to the SPA_BT_PROFILE_BAP_BROADCAST_SINK profile.
The transport set volume call may take a long time or never complete, so
make them async to not block main loop.
Also reduce log level to info for the failed volume setting, as this is
something the user can do nothing about.
Do BAP audio location selection properly in SelectProperties, now that
BlueZ provides the supported locations there. Remove a previous
workaround.
The audio location in SelectProperties determines the audio channel
allocation, which determines the channel positions.
For ISO server sockets, the QOS struct from getsockopt contains values
with different meaning from ISO client socket. Get the values via DBus
instead, which is right in both cases.
If BAP codec configuration is mono channel with unspecified location,
set the channel position from transport location.
This in principle should be set in SelectProperties, but currently BlueZ
doesn't tell us that yet there, so we hack it up later on.
Add factored out helper for ISO socket I/O.
ISO sockets need synchronization of writes and audio position for
different stream fds in the same isochronous group, and it's easier to
separate out the part that coordinates it.
If transport goes into error state too often, fail instead of trying to
acquire it again.
This avoids getting into a tight acquire->fail->reacquire loop.
Change A2DP/BAP transport acquire and release to be async.
Since BlueZ acquiring ISO sockets blocks until all sockets in same CIG
are acquired, BAP transports must be acquired asynchronously.
Allow asynchronous changes in transport state in the sinks/sources.
Also allow transport acquire to be actually synchronous, in this case it
must set transport state during acquire call.
Separate driver start/stop from transport start/stop.
For BAP server audio sink, set buffering target so that we try to match
the target presentation delay. Also adjust requested node latency to be
smaller than the delay.
Also fix BAP transport presentation delay value parsing, and parse also
the other BAP transport properties. Of these, transport latency value
needs to be taken into account in the total sink latency.
BlueZ fails registering object managers containing A2DP endpoints if
controller is in LE-only mode.
Make the A2DP and BAP object managers separate, so that failure to
register one does not prevent registering the other.
Also rename some functions to indicate which ones deal with the legacy
BlueZ API.
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.
Multiple transport from the same device may share the same stream (CIS)
and group (CIG) but for different direction, e.g. a speaker and a
microphone. In this case they are linked.
In this case:
- On acquire, if another transport has already been acquired, the new
transport should not call Acquire or TryAcquire but re-use values from
the previously acquired transport,
- on release, the closing of transport fd and call to Release should be
done only for the last transport.