bluez-alsa icon indicating copy to clipboard operation
bluez-alsa copied to clipboard

Add support for LHDC V3 A2DP source and sink

Open anonymix007 opened this issue 1 year ago • 17 comments

Closes #588

~~Source does not work, only some buzzing noise is produced instead of music. I've tried saving pcm.data as raw pcm and it seems to be incorrect already. Library expects interleaved 24 or 16 bit data depending on selected bit depth, so probably related to the use of BA_TRANSPORT_PCM_FORMAT_S24_4LE.~~ It seems to be somewhat working now, but not yet perfect.

anonymix007 avatar Nov 12 '23 14:11 anonymix007

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 63.97%. Comparing base (b7bb861) to head (004360a).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #672      +/-   ##
==========================================
+ Coverage   63.93%   63.97%   +0.04%     
==========================================
  Files          93       93              
  Lines       14809    14809              
  Branches     2374     2375       +1     
==========================================
+ Hits         9468     9474       +6     
+ Misses       5235     5229       -6     
  Partials      106      106              

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar Nov 13 '23 06:11 codecov[bot]

I still have no idea why this happens even with BA_TRANSPORT_PCM_FORMAT_S32_4LE. Apparently, some variables in a2dp_lhdc_enc_thread aren't what I thought. liblhdcBT_enc.so built from same sources works fine in Android. PCM dump appears to have same issues as sound in headphones. liblhdcBT_enc.so built from same sources works fine in Android, so I can't see what's wrong.

anonymix007 avatar Nov 15 '23 14:11 anonymix007

I'd like to test it locally. From where I can get the ldhcBT-dec/enc libraries? If this PR were to go to master I need to have a possibility to test the code for potential regressions.

arkq avatar Nov 19 '23 14:11 arkq

UPDATED Apr 5, 2024 libs.zip headers.zip

Source code for libraries will be available later.

LHDC_INCLUDE_DIR=/path/to/headers

export LHDC_DEC_CFLAGS="-I$LHDC_INCLUDE_DIR/liblhdcdec/include -I$LHDC_INCLUDE_DIR/liblhdcdec/inc -L$LHDC_INCLUDE_DIR"
export LHDC_DEC_LIBS="-llhdcBT_dec"
export LHDC_ENC_CFLAGS="-I$LHDC_INCLUDE_DIR/liblhdc/include -I$LHDC_INCLUDE_DIR/liblhdc/inc -L$LHDC_INCLUDE_DIR"
export LHDC_ENC_LIBS="-llhdcBT_enc"

autoreconf --install
mkdir build && cd build
../configure --enable-aac --enable-lhdc --enable-debug
make
sudo make install

anonymix007 avatar Nov 19 '23 14:11 anonymix007

In the last few days I've done long overdue refactoring around A2DP codecs. Now, the logic is kept mostly in one file, which should simplify maintenance and addition of new codecs. Due to extensive changes, I've rebased your work on top of current master. The code seems to compile, but I've got no time yet to test whether it works :D

Also, I've left some commented code (I'm not sure whether it will be required or not). In the A2DP configuration for LHDC you've added #define LHDC_CHANNEL_MODE_STEREO 0x03 which suggests that the channel mode can be selected, is that true? If yes, the channel mode selection will need to be done right in a2dp_lhdc_configuration_select() and a2dp_lhdc_configuration_check() functions, otherwise, maybe the define should be removed?

arkq avatar Jan 09 '24 15:01 arkq

Most constants were directly copied from AOSP patches. This must be a remnant of that. Seems to be unused, so might be removed probably.

The commented out code seems to be for channel mode, so could be removed as well.

Another issue would be to implement LLAC/V3/V4 selection logic, which is basically the following:

  1. If LLAC is supported and LLAC-friendly bitrate is selected (ABR or < 500 kbps) then default to 48 kHz and enable LLAC (V4 flag needs to be set to 0).
  2. If LLAC is not supported then enable V4 if it's supported, otherwise go for V3.

At least sink seems to still work.

anonymix007 avatar Jan 09 '24 16:01 anonymix007

I've tried to run it once again, but now it doesn't seem to work (even AAC doesn't work).

$ bluealsa-aplay
bluealsa-aplay: [8934] W: ../../../utils/aplay/aplay.c:1278: Couldn't get BlueALSA PCM list: Sender is not authorized to send message

or from root:

bluealsa-aplay: [9413] D: ../../../utils/aplay/aplay.c:876: Creating IO worker XX:XX:XX:XX:XX:XX
bluealsa-aplay: [9413] D: ../../../utils/aplay/aplay.c:1287: Starting main loop
bluealsa-aplay: [9414] D: ../../../utils/aplay/aplay.c:542: Opening BlueALSA source PCM: /org/bluealsa/hci0/dev_XX_XX_XX_XX_XX_XX/a2dpsnk/source
bluealsa-aplay: [9414] D: ../../../utils/aplay/aplay.c:568: Starting IO loop
bluealsa-aplay: [9414] D: ../../../utils/aplay/aplay.c:692: Opening ALSA playback PCM: name=default channels=2 rate=44100
bluealsa-aplay: [9414] W: ../../../utils/aplay/aplay.c:696: Couldn't open ALSA playback PCM: Open PCM: Host is down

Probably related to #681, though I haven't recompiled PipeWire, just renamed /usr/lib/spa-0.2/bluez5. My phone also says "Problem connecting. Turn device off & back on", but I definitely can see audio packets in WireShark.

UPD This works for AAC:

sudo bluealsa-cli open /org/bluealsa/hci0/dev_XX_XX_XX_XX_XX_XX/a2dpsnk/source | play -e signed-integer -b 16 -c 2 -r 44100 -t raw -

But it segfaults with LHDC:

#0  0x00007ffff768632c in ??? () at /usr/lib/libc.so.6
#1  0x00007ffff76356c8 in raise () at /usr/lib/libc.so.6
#2  0x00007ffff761d4b8 in abort () at /usr/lib/libc.so.6
#3  0x00007ffff761e395 in ??? () at /usr/lib/libc.so.6
#4  0x00007ffff76902a7 in ??? () at /usr/lib/libc.so.6
#5  0x00007ffff7690de4 in ??? () at /usr/lib/libc.so.6
#6  0x00007ffff7690f2b in ??? () at /usr/lib/libc.so.6
#7  0x00007ffff7691fdd in ??? () at /usr/lib/libc.so.6
#8  0x00007ffff7692669 in ??? () at /usr/lib/libc.so.6
#9  0x00007ffff7694e93 in free () at /usr/lib/libc.so.6
#10 0x000055555555ff81 in ffb_free (ffb=0x7ffff4bffc70) at ../../src/shared/ffb.c:46
#11 0x000055555557b1d7 in a2dp_lhdc_dec_thread (t_pcm=0x5555555c08f0) at ../../src/a2dp-lhdc.c:347
#12 0x00007ffff768455a in ??? () at /usr/lib/libc.so.6
#13 0x00007ffff7701a3c in ??? () at /usr/lib/libc.so.6

anonymix007 avatar Mar 01 '24 15:03 anonymix007

But it segfaults with LHDC

It seems that the problem is somewhere within encoder code. The reason of abort is heap corruption due to memmove() beyond allocated memory in ffb_t bt buffer. I've tested with new library and the old one, but the result is all the same:

==1263640== Invalid write of size 2
==1263640==    at 0x48529E3: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1263640==    by 0x4F1C7E4: ??? (in /mnt/storage1t/Downloads/balsa/build/lhdc/liblhdcBT_enc.new.so)
==1263640==    by 0x125B5E: a2dp_lhdc_enc_thread (a2dp-lhdc.c:179) 
==1263640==    by 0x5309AC2: start_thread (pthread_create.c:442)
==1263640==    by 0x539AA03: clone (clone.S:100)
==1263640==  Address 0x5af146a is 1,002 bytes inside a block of size 1,003 alloc'd
==1263640==    at 0x48487A9: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1263640==    by 0x111C9C: ffb_init (ffb.c:26) 
==1263640==    by 0x125801: a2dp_lhdc_enc_thread (a2dp-lhdc.c:125) 
==1263640==    by 0x5309AC2: start_thread (pthread_create.c:442) 
==1263640==    by 0x539AA03: clone (clone.S:100)
==1263640==
==1263640== Invalid write of size 1
==1263640==    at 0x4852A13: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1263640==    by 0x4F1C7E4: ??? (in /mnt/storage1t/Downloads/balsa/build/lhdc/liblhdcBT_enc.new.so) 
==1263640==    by 0x125B5E: a2dp_lhdc_enc_thread (a2dp-lhdc.c:179) 
==1263640==    by 0x5309AC2: start_thread (pthread_create.c:442)
==1263640==    by 0x539AA03: clone (clone.S:100) 
==1263640==  Address 0x5af1478 is 13 bytes after a block of size 1,003 alloc'd
==1263640==    at 0x48487A9: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1263640==    by 0x111C9C: ffb_init (ffb.c:26) 
==1263640==    by 0x125801: a2dp_lhdc_enc_thread (a2dp-lhdc.c:125) 
==1263640==    by 0x5309AC2: start_thread (pthread_create.c:442)
==1263640==    by 0x539AA03: clone (clone.S:100) 

arkq avatar Apr 04 '24 12:04 arkq

I was testing the decoder, but encoder having the same issue is really strange. None of them should call memmove directly (except third-party submodules). I'll look into that.

Do you have any suggestions for coexisting with pipewire?

anonymix007 avatar Apr 04 '24 12:04 anonymix007

I was testing the decoder, but encoder having the same issue is really strange.

I've added a test code which encodes PCM and then decodes it, so due to encoder failure I was not able to check decoder, not yet :D

Do you have any suggestions for coexisting with pipewire?

What do you mean "coexisting"? If you have BlueALSA and PipeWire running on the same host at the same time, then you should disable (somehow) Bluetooth support in PipeWire (I'm not sure whether renaming BlueZ plugin is sufficient, it might be though). Then, remove paired device and pair it again, so BlueZ will not reuse cached SDP information.

EDIT: In order to test encoder/decoder with IO test you can run it with valgrind like this:

make check TESTS=
CK_FORK=no valgrind ./test/test-io lhdc-v3

arkq avatar Apr 04 '24 12:04 arkq

What do you mean "coexisting"?

bluealsa-aplay doesn't play any sound: Couldn't open ALSA playback PCM: Open PCM: Host is down

Though, I guess, for debugging this issue I might just run that test.

anonymix007 avatar Apr 04 '24 12:04 anonymix007

bluealsa-aplay doesn't play any sound: Couldn't open ALSA playback PCM: Open PCM: Host is down

I guess that you have to use PCM playback which is not grabbed by PipeWire or use some playback PCM exposed by PipeWire itself (if there is any). However, I'm not PipeWire user (not yet), so I've got zero knowledge about PipeWire configuration. Anyway, the error code "Host is down" is kinda strange...

arkq avatar Apr 04 '24 12:04 arkq

Apparently, it doesn't like the following line:

memcpy(out_put, enc, handle->host_mtu_size);

And this is exactly what original library does.

So this, per say, is not a library error. Should be fixed now.

anonymix007 avatar Apr 04 '24 16:04 anonymix007

So this, per say, is not a library error. Should be fixed now.

OK, now the test passes but only with the old version of the library. With the new one (UPDATED Mar 1, 2024) encoder produces only 78 bytes per packet, where the old one produces 270 bytes. It looks like the output is truncated because these 78 bytes matches with the first 78 bytes in the packet from the old encoder.

arkq avatar Apr 05 '24 08:04 arkq

You're right, there was a stupid bug. I've updated the libraries.

anonymix007 avatar Apr 05 '24 13:04 anonymix007

UPDATED Apr 5, 2024 libs.zip headers.zip

Source code for libraries will be available later.

LHDC_INCLUDE_DIR=/path/to/headers

export LHDC_DEC_CFLAGS="-I$LHDC_INCLUDE_DIR/liblhdcdec/include -I$LHDC_INCLUDE_DIR/liblhdcdec/inc -L$LHDC_INCLUDE_DIR"
export LHDC_DEC_LIBS="-llhdcBT_dec"
export LHDC_ENC_CFLAGS="-I$LHDC_INCLUDE_DIR/liblhdc/include -I$LHDC_INCLUDE_DIR/liblhdc/inc -L$LHDC_INCLUDE_DIR"
export LHDC_ENC_LIBS="-llhdcBT_enc"

autoreconf --install
mkdir build && cd build
../configure --enable-aac --enable-lhdc --enable-debug
make
sudo make install

Where did you get the library liblhdcBT_dec.so from?

O2C14 avatar Apr 15 '24 07:04 O2C14

Where did you get the library liblhdcBT_dec.so from?

From the same place as liblhdcBT_enc.so. I compiled it from sources.

anonymix007 avatar Apr 15 '24 08:04 anonymix007