thewierdnut / asha_pipewire_sink

Asha audio protocol implementation for linux.
The Unlicense
26 stars 2 forks source link

Instability with multiple BT devices connected. #16

Open barolo opened 2 weeks ago

barolo commented 2 weeks ago

Additional BT peripherals, like mouse, keyboard etc. [besides hearing devices] will led to a very unstable stream.

thewierdnut commented 2 weeks ago

If you would like to try an experiment, it may be worth updating the connection interval:

  1. Use your bluetooth UI to "forget" your devices
  2. Edit /etc/bluetooth/main.conf and reduce the MinConnectionInterval and MaxConnectionInterval from 16 to 8
  3. Restart your bluetooth service
  4. Verify that the configuration has been updated by running btmgmt read-sysconfig (root not needed). The values you are looking for are Type: 0x0017 and Type: 0x0018, which should now both be 0800 instead of 1600

Note that on my system, I had to restart the bluetooth service several times, and eventually remove and reinsert the btusb kernel module before the config would take. It may be easier to just restart your system.

Having the connection interval cut in half gives the adapter twice as many opportunities to transmit/receive information, which I'm hoping will make your connection more stable. It may also have an adverse effect on your battery life on both your hearing devices and your peripherals, so watch out for that and let me know how bad it gets.

The android code attempts to set this value if a config value tells it to, but it will fall back to a 16 (20ms) interval if it doesn't work, which I think means that not all hearing devices will support this. I have no way to check if the connection settings negotiation succeeded or not, so if this fails, your audio probably just won't work, and I won't have any feedback in the program indicating why.

A strict reading of the ASHA protocol would seem to indicate that by cutting the connection interval in half, I should be cutting the frames in half and sending them twice as frequently. It seems to work fine on my devices without this change though.

barolo commented 2 weeks ago
  • Use your bluetooth UI to "forget" your devices

  • Edit /etc/bluetooth/main.conf and reduce the MinConnectionInterval and MaxConnectionInterval from 16 to 8

  • Restart your bluetooth service

  • Verify that the configuration has been updated by running btmgmt sysconfig (root not needed). The values you are looking for are Type: 0x0017 and Type: 0x0018, which should now both be 0800 instead of 1600

It was btmgmt read-sysconfig for me, and indeed it was changed to 0800. That being said the connection became significantly worse. I could tell from the start, I had a hard time pairing them, and after I could barely keep both connected, one would always disconnected. After them both connected, starting asha_pipewire_sink would disconnect them immediately.

thewierdnut commented 2 weeks ago

So I reduced my connection interval to 8 and my frame size to 80, and all I get out of my hearing aids is a loud buzzing. I tried several different variants on the connection parameter update and the ACP status, and it still doesn't work.

I think the fact that my hearing aids can continue to use 160 byte frames even with a connection interval of 8 is a bug in the hearing aids.

barolo commented 2 weeks ago

I'm starting to wonder if going through pipewire is the right approach, and is there any other possibility? Not in the context of multiple devices, just in general.

thewierdnut commented 2 weeks ago

I'm starting to wonder if going through pipewire is the right approach, and is there any other possibility?

Not really? On a modern linux system, even pulseaudio clients use pipewire.

I'm not sure where you are going with this. I don't think your instability is due to pipewire, its due to an incomplete linux bluetooth implementation.

barolo commented 2 weeks ago

Not really? On a modern linux system, even pulseaudio clients use pipewire.

Yeah, I'm aware, also alsa clients via alsa plugin. But I have some Raspberry PIs which don't have any sound-server and just use bluetooth-alsa directly. That Pi Zero embedded project made me think of this. Is there an exclusive audio mode in Pipewire? And could it make any things better?. Because some of the instability is coming from pipewire too. Possibly more related to #13

MadSpindel commented 2 weeks ago

If I connect my Stadia Bluetooth controller, asha_pipewire_sink will start dropping frames like crazy and disconnect the hearing aids. It's not possible to have both my Stadia controller and hearing aids connected at the same time. Is this a limitation with ASHA protocol or something with Bluez? Because I think you can have more than 3 devices connected at the same time? Most adapters says up to eight devices but that's maybe not true on Linux?

thewierdnut commented 2 weeks ago

but that's maybe not true on Linux?

Linux can handle 8 devices fine. I believe that the problem is that ASHA requires the host to explicitly set the connection interval, where normally the peripheral negotiates the interval that it needs. Linux does not provide an api for the host to set the connection interval, so we work around that by setting the interval globally for all devices, which is not ideal.

You may have some luck decreasing the interval from 16 to 8. It works for me, but it breaks others.

MadSpindel commented 2 weeks ago

Linux does not provide an api for the host to set the connection interval, so we work around that by setting the interval globally for all devices, which is not ideal.

Linux in this case is the Bluez Bluetooth stack? I know Google is replacing the Bluez stack with Fluoride in ChromeOS: https://chromeos.dev/en/posts/androids-bluetooth-stack-fluoride-comes-to-chromeos

Just thinking if Fluoride might fix this issue or if the same problem exists in the Fluoride Bluetooth stack?

barolo commented 2 weeks ago

Linux does not provide an api for the host to set the connection interval, so we work around that by setting the interval globally for all devices, which is not ideal.

Linux in this case is the Bluez Bluetooth stack? I know Google is replacing the Bluez stack with Fluoride in ChromeOS: https://chromeos.dev/en/posts/androids-bluetooth-stack-fluoride-comes-to-chromeos

Just thinking if Fluoride might fix this issue or if the same problem exists in the Fluoride Bluetooth stack?

They seem to be skipping most of the kernel, so it's fair to assume that they don't share the same issues.

MadSpindel commented 2 weeks ago

Linux does not provide an api for the host to set the connection interval, so we work around that by setting the interval globally for all devices, which is not ideal.

Linux in this case is the Bluez Bluetooth stack? I know Google is replacing the Bluez stack with Fluoride in ChromeOS: https://chromeos.dev/en/posts/androids-bluetooth-stack-fluoride-comes-to-chromeos Just thinking if Fluoride might fix this issue or if the same problem exists in the Fluoride Bluetooth stack?

They seem to be skipping most of the kernel, so it's fair to assume that they don't share the same issues.

According to this website it should be possible to run Flouride on Debian based distros: https://android.googlesource.com/platform/packages/modules/Bluetooth/

thewierdnut commented 2 weeks ago

@barolo Can you reproduce this using asha_stream_test using the fixed algorithm?

barolo commented 2 weeks ago

@barolo Can you reproduce this using asha_stream_test using the fixed algorithm?

Yes I can, algorithm doesn't matter.

kmart commented 2 weeks ago

4. Verify that the configuration has been updated by running btmgmt read-sysconfig (root not needed). The values you are looking for are Type: 0x0017 and Type: 0x0018, which should now both be 0800 instead of 1600

Hm, I'm getting the value 1000 with MinConnectionInterval and MaxConnectionInterval set to 16...

$ btmgmt read-sysconfig
Type: 0x0017    Length: 02  Value: 1000
Type: 0x0018    Length: 02  Value: 1000

Have not tried to set the values down to 8 yet, but the values are set to 16 and was a bit suprised when the btmgmt command shows 1000 and not the expected 1600 value...

Are on bluez ver. 5.71 while the last one is ver. 5.76. Maybe this could be the reason?

thewierdnut commented 2 weeks ago

Have not tried to set the values down to 8 yet, but the values are set to 16 and was a bit suprised when the btmgmt command shows 1000 and not the expected 1600 value...

This is expected. the "Value" printed there is little-endian hexidecimal. The values in the config file are decimal, so decimal 16 = hexidecimal 10

thewierdnut commented 2 weeks ago

@barolo Can you reproduce this using asha_stream_test using the fixed algorithm?

Yes I can, algorithm doesn't matter.

I pushed a new version of asha_stream_test which has some options for explicitly setting the phy and the ce length. Both of these options require CAP_NET_RAW to work, so you will either need to use setcap to set it, or you need to run it as root.

Of particular interest is the --celength option, which is used to "suggest" to the peripheral how long it should listen for traffic when it wakes up for network activity. The usual, ASHA spec requires the central (the computer) to set it, but bluetooth normally expects the peripheral to set it. ASHA says that this value should be set to 8, and the android implementation sets it to 12.

I would like you to try --celength 12 and maybe higher values like --celength 16. If your hearing device's bluetooth stack is religiously following this value instead of adjusting it to fit the connection parameters, then this may help.

Note that asha_stream_test is pretty bad at getting your hearing devices both up and running (I need to fix this), so please get them up and the audio streaming reliably first, and then test what happens when you activate other peripherals.

thewierdnut commented 2 weeks ago

Also... I would like you to explicitly disable 2M PHY and see if that works better. My original streaming test apps still don't work with 1M PHY, but for some reason, the latest asha_pipewire_sink works great with it. Go figure.

barolo commented 2 weeks ago

I did some test with asha_stream_test and the only workable setup when it comes to multiple devices [tested with Logitech's BT mouse] is having only one side connected and to use -algo poll with --phy1m [this one probably doesn't matter], it's still unstable and will disconnect eventually. . Fixed will keep breaking with any mouse movement and will disconnect quite fast. If both sides are connected they will just drop frames then disconnect. I can use celength from 4 to 16, It doesn't seem to make any difference though.

barolo commented 2 weeks ago

I would like you to explicitly disable 2M PHY and see if that works better

I'm not sure how to do this

thewierdnut commented 2 weeks ago

I would like you to explicitly disable 2M PHY and see if that works better

I'm not sure how to do this

Use btmgmt phy to remove the LE2MTX and LE2MRX entries. On my system that looks like this:

btmgmt phy BR1M1SLOT BR1M3SLOT BR1M5SLOT EDR2M1SLOT EDR2M3SLOT EDR2M5SLOT EDR3M1SLOT EDR3M3SLOT EDR3M5SLOT LE1MTX LE1MRX

I'm not entirely sure its possible for you to disable it though... with your kernel version it may just enable it if both sides support it, regardless of what the global defaults are set to.

barolo commented 2 weeks ago

I would like you to explicitly disable 2M PHY and see if that works better

I'm not sure how to do this

Use btmgmt phy to remove the LE2MTX and LE2MRX entries. On my system that looks like this:

btmgmt phy BR1M1SLOT BR1M3SLOT BR1M5SLOT EDR2M1SLOT EDR2M3SLOT EDR2M5SLOT EDR3M1SLOT EDR3M3SLOT EDR3M5SLOT LE1MTX LE1MRX

I'm not entirely sure its possible for you to disable it though... with your kernel version it may just enable it if both sides support it, regardless of what the global defaults are set to.

I cannot make it work [asha_pipewire_sink], with both sides and 2M PHY disabled; will start dropping packets, then one side will disconnect.

Also, the weird thing about using sudo ./asha_stream_test --left ../sounds/left.g722 --algorithm poll --volume -64 --celength 16 --phy1m with mouse connected is, that it will be perfectly stable initially until it's not, and how long it will last seems totally random.

thewierdnut commented 2 weeks ago

Can you get a btmon capture of the bluetooth traffic when you get a disconnect? use asha_pipewire_sink this time, and use whatever bluetooth devices you can to get it to drop.

barolo commented 2 weeks ago

Can you get a btmon capture of the bluetooth traffic when you get a disconnect? use asha_pipewire_sink this time, and use whatever bluetooth devices you can to get it to drop.

Here you go btmon.zip My uneducated guess is the issue might be in the stack, at least part of it, since even before launching asha_pipewire_sink it might be hard to connect them all at once, asha_pipewire_sink just exacerbates it, causing everything to disconnect. Would the asha stuff that landed in blueZ master be of any help?

thewierdnut commented 2 weeks ago

Here you go btmon.zip

Your devices are stingy with the flow control credits. I think they are trying to keep the latency down, but mostly just makes the kernel hesitate a lot before sending.

We are dropping audio before your other bluetooth devices even start sending a lot. I can see your hearing devices just not acknowledging the traffic I'm sending.

Would the asha stuff that landed in blueZ master be of any help?

Probably not. I don't think they will even work together.

thewierdnut commented 2 weeks ago

Your devices are stingy with the flow control credits. I think they are trying to keep the latency down, but mostly just makes the kernel hesitate a lot before sending.

Actually, now that I think about it, that is probably why the poll algorithm works for you when it doesn't work for anybody else. That polling algorithm waits until your hearing aids dish out another flow control credit before sending the the next packet.

barolo commented 2 weeks ago

Poll has one downside though, I've noticed that left and right sides are not properly synced [it's slight] and don't create coherent stereo field.

thewierdnut commented 2 weeks ago

That makes sense. I can see one side going out of sync first, then the other.

barolo commented 2 weeks ago

That makes sense. I can see one side going out of sync first, then the other.

I don't think that's unique to poll though, it happens in a similar way with all of them.

kmart commented 1 week ago

Use btmgmt phy to remove the LE2MTX and LE2MRX entries. On my system that looks like this:

btmgmt phy BR1M1SLOT BR1M3SLOT BR1M5SLOT EDR2M1SLOT EDR2M3SLOT EDR2M5SLOT EDR3M1SLOT EDR3M3SLOT EDR3M5SLOT LE1MTX LE1MRX

Hi! Tried this but with this setting nothing worked. Just a short "beep" and then disconnect. With LE2MTX LE2MRX it works.

Played around a bit with asha_stream_test , but then only with the --left option as I only have one aid. Tried the three different parameters to the --algorithm option. All three played sound, but it is, with the amount of testing that I've done so far, hard to say which one that worked the best. All three started out with stable sound (only latency messages), but then with "Dropped frame" message after some time.

I'm not sure whether it is of interest but I collected a btmon dump file where I was running asha_pipewire_sink and where the was one disconnect. Attached here: btmon-oticon.zip

kmart commented 1 week ago

Hm, forgot to remove and reestablish the pairing when doing the configuration changes. Could have something to say? Will to some more testing.

(Btw., the testing was done with BT LE keyboard and mouse connected and in use.)

thewierdnut commented 3 hours ago

Sorry for the long delay, as I've been distracted adding other features... I've pushed an update with 5 different configurable queuing algorithms in an attempt to work around the lack of flow control information. The only algorithm that doesn't work for me is "none", which doesn't do any queuing at all. I've also added dbus property logging for asha devices. One of those properties is RSSI, which unfortunately only gets logged if the userspace bluez daemon is processing advertisement packets (such as when adding a new bluetooth device).

I would like you to retry your test with either asha_stream_test or asha_pipewire_sink (they both take the same arguments now), while you leave your bluetooth gui's add new device window open. Please note in the program output when the audio starts to get unstable, and note any logged RSSI levels.