├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── stale.yml ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── config.h.in └── src └── modules └── bluetooth ├── a2dp ├── a2dp-api.h ├── a2dp-codecs.h ├── a2dp_aac.c ├── a2dp_aptx.c ├── a2dp_ldac.c ├── a2dp_sbc.c ├── a2dp_util.c ├── ffmpeg_libs.c ├── ffmpeg_libs.h ├── ldac_libs.c ├── ldac_libs.h └── rtp.h ├── backend-native.c ├── backend-ofono.c ├── bluez5-util.c ├── bluez5-util.h ├── module-bluetooth-discover.c ├── module-bluetooth-policy.c ├── module-bluez5-device.c └── module-bluez5-discover.c /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: EHfive 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Environment (please complete the following information):** 20 | - Distribution [e.g. Ubuntu, Fedora, Arch ...] 21 | - Pulseaudio Version [e.g. v12.2] 22 | - Version [e.g. v1.2] 23 | - Installing via [e.g. manually compiled, precompiled package from xxx, ...] 24 | 25 | **Pulseaudio logs:** 26 | (set `log-level` to `4` in `/etc/pulse/daemon.conf`) 27 | 28 | **`avinfo` of device:** 29 | (guide: https://github.com/EHfive/pulseaudio-modules-bt/issues/31#issuecomment-462717049) 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - wip 9 | - tips 10 | - enhancement 11 | - help wanted 12 | # Label to use when marking an issue as stale 13 | staleLabel: stale 14 | # Comment to post when marking an issue as stale. Set to `false` to disable 15 | markComment: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: false 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pa"] 2 | path = pa 3 | url = https://github.com/pulseaudio/pulseaudio.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # pulseaudio-modules-bt 3 | # 4 | # Copyright (C) 2018-2019 Huang-Huang Bao 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | # 19 | 20 | cmake_minimum_required(VERSION 3.10) 21 | project(pulseaudio_modules_bt C) 22 | 23 | 24 | set(CMAKE_C_STANDARD 11) 25 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 26 | set(CMAKE_C_FLAGS "$ENV{CFLAGS} -O2 -Wall -Wno-builtin-macro-redefined -Wno-unused -fno-common -DFASTPATH") 27 | if(CMAKE_GENERATOR STREQUAL "Unix Makefiles") 28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__FILE__='\"$(subst ${CMAKE_SOURCE_DIR}/src/,,$(abspath $<))\"'") 29 | endif() 30 | 31 | option(CODEC_AAC_FDK "LC-AAC support using fdk-aac(-free)" ON) 32 | option(CODEC_APTX_FF "aptX Classic support using FFmpeg" ON) 33 | option(CODEC_APTX_HD_FF "aptX HD support using FFmpeg" ON) 34 | option(CODEC_LDAC "LDAC encoding, abr support using libldac" ON) 35 | option(FORCE_LARGEST_PA_VERSION "Build for largest available version( <= PulseAudio git master) of PulseAudio" OFF) 36 | option(OFONO_HEADSET "ofono HFP headset support" ON) 37 | option(NATIVE_HEADSET "native HSP headset support" ON) 38 | 39 | add_definitions(-DHAVE_CONFIG_H) 40 | add_definitions(-D_REENTRANT) 41 | 42 | find_package(PkgConfig REQUIRED) 43 | pkg_check_modules(PulseAudio REQUIRED "libpulse") 44 | pkg_check_modules(DBUS REQUIRED "dbus-1") 45 | pkg_check_modules(SBC REQUIRED "sbc") 46 | pkg_check_modules(AVCODEC libavcodec>=58.18.100) 47 | pkg_check_modules(AVUTIL libavutil>=56.14.100) 48 | pkg_check_modules(FDKAAC "fdk-aac>=0.1.5") 49 | pkg_check_modules(LDACENC "ldacBT-enc") 50 | pkg_check_modules(LDACABR "ldacBT-abr") 51 | 52 | set(LDAC_INCLUDE_DIRS ${LDACENC_INCLUDE_DIRS} ${LDACABR_INCLUDE_DIRS}) 53 | set(PulseAudio_CORE_LIBDIR ${PulseAudio_LIBDIR}/pulseaudio) 54 | set(MOD_BT_DIR ${PROJECT_SOURCE_DIR}/src/modules/bluetooth) 55 | 56 | set(bluez5_util_SOURCES 57 | ${MOD_BT_DIR}/bluez5-util.c 58 | ${MOD_BT_DIR}/a2dp/a2dp_sbc.c 59 | ${MOD_BT_DIR}/a2dp/a2dp_util.c 60 | ) 61 | set(bluez5_util_RPATH "") 62 | set(bluez5_util_LIBS "") 63 | 64 | if(${OFONO_HEADSET} STREQUAL "ON") 65 | set(HAVE_BLUEZ_5_OFONO_HEADSET 1) 66 | set(bluez5_util_SOURCES ${bluez5_util_SOURCES} ${MOD_BT_DIR}/backend-ofono.c) 67 | message("[HFP] ofono HFP headset support enabled") 68 | endif() 69 | if(${NATIVE_HEADSET} STREQUAL "ON") 70 | set(HAVE_BLUEZ_5_NATIVE_HEADSET 1) 71 | set(bluez5_util_SOURCES ${bluez5_util_SOURCES} ${MOD_BT_DIR}/backend-native.c) 72 | message("[HSP] native HSP headset support enabled") 73 | endif() 74 | 75 | if(${AVCODEC_FOUND} AND ${AVUTIL_FOUND}) 76 | if(${CODEC_APTX_FF} STREQUAL "ON") 77 | set(PA_A2DP_CODEC_APTX_FF 1) 78 | message("[A2DP] FFmpeg aptX support enabled") 79 | endif() 80 | if(${CODEC_APTX_HD_FF} STREQUAL "ON") 81 | set(PA_A2DP_CODEC_APTX_HD_FF 1) 82 | message("[A2DP] FFmpeg aptX HD support enabled") 83 | endif() 84 | 85 | if ("${PA_A2DP_CODEC_APTX_FF}" OR "${PA_A2DP_CODEC_APTX_HD_FF}") 86 | set(bluez5_util_SOURCES ${bluez5_util_SOURCES} ${MOD_BT_DIR}/a2dp/a2dp_aptx.c ${MOD_BT_DIR}/a2dp/ffmpeg_libs.c) 87 | if(NOT "${AVCODEC_LIBRARY_DIRS}" STREQUAL "") 88 | set(bluez5_util_RPATH ${bluez5_util_RPATH}:${AVCODEC_LIBRARY_DIRS}) 89 | endif() 90 | endif () 91 | endif() 92 | if (${FDKAAC_FOUND}) 93 | if(${CODEC_AAC_FDK} STREQUAL "ON") 94 | set(PA_A2DP_CODEC_AAC_FDK 1) 95 | set(bluez5_util_SOURCES ${bluez5_util_SOURCES} ${MOD_BT_DIR}/a2dp/a2dp_aac.c) 96 | set(bluez5_util_LIBS ${bluez5_util_LIBS} ${FDKAAC_LIBRARIES}) 97 | message("[A2DP] fdk-aac LC-AAC support enabled") 98 | endif() 99 | endif() 100 | if (${LDACENC_FOUND} AND ${LDACABR_FOUND}) 101 | if(${CODEC_LDAC} STREQUAL "ON") 102 | set(PA_A2DP_CODEC_LDAC 1) 103 | set(bluez5_util_SOURCES ${bluez5_util_SOURCES} ${MOD_BT_DIR}/a2dp/a2dp_ldac.c ${MOD_BT_DIR}/a2dp/ldac_libs.c) 104 | if(NOT "${LDACENC_LIBRARY_DIRS}" STREQUAL "") 105 | set(bluez5_util_RPATH ${bluez5_util_RPATH}:${LDACENC_LIBRARY_DIRS}) 106 | endif() 107 | message("[A2DP] LDAC support enabled") 108 | endif() 109 | endif () 110 | 111 | include_directories(.) 112 | include_directories(pa/src) 113 | include_directories(${MOD_BT_DIR}) 114 | include_directories(${DBUS_INCLUDE_DIRS}) 115 | include_directories(${SBC_INCLUDE_DIRS}) 116 | include_directories(${LDAC_INCLUDE_DIRS}) 117 | include_directories(${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS}) 118 | include_directories(${FDKAAC_INCLUDE_DIRS}) 119 | 120 | link_directories(${PulseAudio_CORE_LIBDIR}) 121 | link_directories(${PulseAudio_LIBRARY_DIRS}) 122 | link_directories(${DBUS_LIBRARY_DIRS}) 123 | link_directories(${SBC_LIBRARY_DIRS}) 124 | link_directories(${AVCODEC_LIBRARY_DIRS} ${AVUTIL_LIBRARY_DIRS}) 125 | link_directories(${FDKAAC_LIBRARY_DIRS}) 126 | link_directories(${LDACENC_LIBRARY_DIRS} ${LDACABR_LIBRARY_DIRS}) 127 | 128 | 129 | string(REGEX MATCH "[0-9]+(\\.[0-9]+)" PulseAudio_lib_version ${PulseAudio_VERSION}) 130 | if("" STREQUAL "${PulseAudio_lib_version}") 131 | message(SEND_ERROR "Can't match libpulse version") 132 | set(PulseAudio_lib_version ${PulseAudio_VERSION}) 133 | message(WARNING "SET PulseAudio_lib_version = ${PulseAudio_VERSION}") 134 | endif() 135 | 136 | # Definitions 137 | # PulseAudio_VERSION_12_N 138 | # PulseAudio version greater than 12.2 139 | # 140 | # PulseAudio_VERSION_11_N 141 | # PulseAudio version greater than 11.1 142 | # 143 | if((${PulseAudio_lib_version} VERSION_GREATER "12.2") 144 | OR (${FORCE_LARGEST_PA_VERSION} STREQUAL "ON")) 145 | add_definitions(-DPulseAudio_VERSION_12_N) 146 | elseif(${PulseAudio_lib_version} VERSION_GREATER "11.1") 147 | add_definitions(-DPulseAudio_VERSION_11_N) 148 | endif() 149 | 150 | 151 | execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=modlibexecdir libpulse 152 | OUTPUT_VARIABLE PulseAudio_modlibexecdir 153 | OUTPUT_STRIP_TRAILING_WHITESPACE) 154 | if("" STREQUAL "${PulseAudio_modlibexecdir}") 155 | message(SEND_ERROR "Can't found variable modlibexecdir in libpulse.pc") 156 | set(PulseAudio_modlibexecdir ${PulseAudio_LIBDIR}/pulse-${PulseAudio_lib_version}/modules) 157 | message(WARNING "SET PulseAudio_modlibexecdir = ${PulseAudio_modlibexecdir}") 158 | endif() 159 | 160 | 161 | configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_SOURCE_DIR}/config.h") 162 | 163 | set(MOD_LIBS ${PulseAudio_LIBRARIES} pthread pulsecommon-${PulseAudio_lib_version} pulsecore-${PulseAudio_lib_version}) 164 | set(CMAKE_INSTALL_RPATH ${PulseAudio_CORE_LIBDIR}:${PulseAudio_modlibexecdir}) 165 | 166 | 167 | # libbluez5-util 168 | add_library(bluez5-util SHARED 169 | ${bluez5_util_SOURCES} 170 | ) 171 | set(bluez5_util_LIBS ${bluez5_util_LIBS} ${SBC_LIBRARIES} ${DBUS_LIBRARIES} ${MOD_LIBS} dl) 172 | target_link_libraries(bluez5-util ${bluez5_util_LIBS}) 173 | 174 | if(NOT "${bluez5_util_RPATH}" STREQUAL "") 175 | set(bluez5_util_RPATH "${CMAKE_INSTALL_RPATH}${bluez5_util_RPATH}") 176 | else() 177 | set(bluez5_util_RPATH "${CMAKE_INSTALL_RPATH}") 178 | endif() 179 | set_target_properties(bluez5-util PROPERTIES 180 | INSTALL_RPATH "${bluez5_util_RPATH}" 181 | ) 182 | 183 | # module-bluez5-discover 184 | add_library(module-bluez5-discover MODULE 185 | ${MOD_BT_DIR}/module-bluez5-discover.c 186 | ) 187 | target_compile_definitions(module-bluez5-discover PUBLIC PA_MODULE_NAME=module_bluez5_discover) 188 | target_link_libraries(module-bluez5-discover bluez5-util ${DBUS_LIBRARIES} ${MOD_LIBS}) 189 | 190 | SET_TARGET_PROPERTIES(module-bluez5-discover PROPERTIES PREFIX "") 191 | 192 | 193 | # module-bluez5-device 194 | add_library(module-bluez5-device MODULE 195 | ${MOD_BT_DIR}/module-bluez5-device.c 196 | ) 197 | 198 | target_compile_definitions(module-bluez5-device PUBLIC PA_MODULE_NAME=module_bluez5_device) 199 | target_link_libraries(module-bluez5-device bluez5-util ${MOD_LIBS}) 200 | SET_TARGET_PROPERTIES(module-bluez5-device PROPERTIES PREFIX "") 201 | 202 | # module-bluetooth-discover 203 | add_library(module-bluetooth-discover MODULE 204 | ${MOD_BT_DIR}/module-bluetooth-discover.c 205 | ) 206 | 207 | target_compile_definitions(module-bluetooth-discover PUBLIC PA_MODULE_NAME=module_bluetooth_discover) 208 | target_link_libraries(module-bluetooth-discover ${MOD_LIBS}) 209 | SET_TARGET_PROPERTIES(module-bluetooth-discover PROPERTIES PREFIX "") 210 | 211 | # module-bluetooth-policy 212 | add_library(module-bluetooth-policy MODULE 213 | ${MOD_BT_DIR}/module-bluetooth-policy.c 214 | ) 215 | 216 | target_compile_definitions(module-bluetooth-policy PUBLIC PA_MODULE_NAME=module_bluetooth_policy) 217 | target_link_libraries(module-bluetooth-policy ${MOD_LIBS}) 218 | SET_TARGET_PROPERTIES(module-bluetooth-policy PROPERTIES PREFIX "") 219 | 220 | message(STATUS "CMAKE_C_FLAGS = " ${CMAKE_C_FLAGS}) 221 | 222 | INSTALL(TARGETS 223 | bluez5-util 224 | module-bluez5-discover 225 | module-bluez5-device 226 | module-bluetooth-discover 227 | module-bluetooth-policy 228 | LIBRARY DESTINATION ${PulseAudio_modlibexecdir}) 229 | 230 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pulseaudio-modules-bt 2 | 3 | this repo is a fork of pulseaudio bluetooth modules 4 | 5 | and adds LDAC, APTX, APTX-HD, AAC support, extended configuration for SBC 6 | 7 | #### Added Codecs 8 | |Codec|Encoding(source role)|Decoding(sink role)|Sample format(s)|Sample frequencies| 9 | |:---:|:---:|:---:|:---:|:---:| 10 | |AAC |✔ |✔ |s16|8, 11.025, 12,16, 22.05, 24, 32, 44.1, 48, 64, 88.2, 96 khz| 11 | |APTX | ✔| ✔ |s16|16, 32, 44.1, 48 khz| 12 | |APTX HD| ✔| ✔ |s24|| 13 | |LDAC |✔ |✘|s16,s24,s32,f32|44.1, 48, 88.2, 96 khz| 14 | 15 | APTX/APTX_HD sample format fixed to s32 in PA. 16 | (ffmpeg do the sample format transformation) 17 | 18 | #### Extended SBC configuration 19 | 20 | Added support for manual (expert) configuration for SBC codec parameters: 21 | 22 | * Min and Max bitpool limits (2-250) 23 | * Sampling frequency 24 | * Audio channel mode 25 | * Quantization bit allocation mode 26 | * Frequency bands number 27 | * Audio blocks number 28 | 29 | You can use this parameters to override and fine-tune default SBC codec config and manually setup configurations like SBC XQ, or Dual Channel HD mode. 30 | More info about SBC configuration options can be found at [LineageOS documentation](https://lineageos.org/engineering/Bluetooth-SBC-XQ). 31 | Also there is [interactive calculator](https://btcodecs.valdikss.org.ru/sbc-bitrate-calculator) and unofficial [device compatibility list](https://btcodecs.valdikss.org.ru/codec-compatibility) that may help you to select proper values. 32 | 33 | Parameter-names for module-bluez5-discover and valid values provided at the table below. 34 | 35 | NOTE: Use these parameters with caution at your own risk! Invalid or extreme "out-of-spec" configurations may sometimes even cause malfunction for some cheap BT-audio devices. Usually these malfunctions can be fixed by resetting audio-device or sometimes simply by reconnecting with valid configuration. 36 | 37 | ## Usage 38 | 39 | ### Pre-built binary packages 40 | 41 | See the [wiki/Packages](https://github.com/EHfive/pulseaudio-modules-bt/wiki/Packages) to find pre-build binary packages for you distribution. 42 | 43 | Please also check [issue#3](https://github.com/EHfive/pulseaudio-modules-bt/issues/3). 44 | 45 | After installing your pre-built binary packages, see [below](#configure) to configure the module. 46 | 47 | ### Build from source 48 | 49 | #### Make Dependencies 50 | 51 | * pulseaudio>=11.59.1 52 | * bluez~=5.0 53 | * dbus 54 | * sbc 55 | * \[Optional] ffmpeg(libavcodec>=58, libavutil>=56) >= 4.0 56 | * \[Optional] fdk-aac(-free)>=0.1.5: pulseaudio-modules-bt use LC-AAC only 57 | * \[Optional] [ldacBT](https://github.com/EHfive/ldacBT)/libldac 58 | * cmake 59 | * pkg-config, libtool, ... 60 | 61 | On Debian based distributions (like Ubuntu), you can install the make dependencies with: 62 | 63 | `sudo apt-get install -y libfdk-aac-dev libavcodec-dev libpulse-dev libdbus-1-dev libsbc-dev libldacbt-abr-dev libldacbt-enc-dev libltdl-dev libbluetooth-dev` 64 | 65 | #### Runtime Dependencies 66 | 67 | * pulseaudio ( force preopen disabled / built with `--disable-force-preopen`) 68 | * bluez 69 | * dbus 70 | * sbc 71 | * \[ fdk-aac(-free) ] 72 | * \[ libavcodec.so ]: APTX, APTX-HD support \[Optional] 73 | * \[ libldac ]: LDAC encoding support, LDAC ABR support \[Optional] 74 | 75 | #### Build 76 | 77 | **backup original pulseaudio bt modules** 78 | 79 | ```bash 80 | MODDIR=`pkg-config --variable=modlibexecdir libpulse` 81 | 82 | sudo find $MODDIR -regex ".*\(bluez5\|bluetooth\).*\.so" -exec cp {} {}.bak \; 83 | ``` 84 | 85 | **pull sources** 86 | 87 | Make sure to pull into a filesystem that supports colons (`:`) as valid characters in filename (see [#144](https://github.com/EHfive/pulseaudio-modules-bt/issues/144)). Ext4 does, while NTFS does not. 88 | 89 | ```bash 90 | git clone https://github.com/EHfive/pulseaudio-modules-bt.git 91 | cd pulseaudio-modules-bt 92 | git submodule update --init 93 | ``` 94 | 95 | **install** 96 | 97 | A. build for PulseAudio releases (e.g., v12.0, v12.2, etc.) 98 | ```bash 99 | git -C pa/ checkout v`pkg-config libpulse --modversion|sed 's/[^0-9.]*\([0-9.]*\).*/\1/'` 100 | 101 | mkdir build && cd build 102 | cmake .. 103 | make 104 | sudo make install 105 | ``` 106 | 107 | B. or build for PulseAudio git master 108 | ```bash 109 | git -C pa/ checkout master 110 | mkdir build && cd build 111 | cmake -DFORCE_LARGEST_PA_VERSION=ON .. 112 | make 113 | sudo make install 114 | ``` 115 | 116 | *Cmake A2DP codecs options*: `CODEC_APTX_FF`, `CODEC_APTX_HD_FF`, `CODEC_AAC_FDK`, `CODEC_LDAC` 117 | 118 | #### Load Modules 119 | 120 | ```bash 121 | pulseaudio -k 122 | 123 | # if pulseaudio not restart automatically, run 124 | pulseaudio --start 125 | ``` 126 | 127 | if you got a warning like below, you need to rebuild `pulseaudio` with `--disable-force-preopen` flag 128 | ``` 129 | pulseaudio: symbol lookup error: pulseaudio: undefined symbol: pa_a2dp_codec_sbc 130 | ``` 131 | 132 | ### Connect device 133 | 134 | Connect your bluetooth device and switch audio profile to 'A2DP Sink'; 135 | 136 | If there is only profile 'HSP/HFP' and 'off', disconnect and reconnect your device. 137 | 138 | The issue has been fixed in bluez 5.51. 139 | 140 | #### Module Arguments 141 | 142 | **module-bluez5-discover arg:a2dp_config** 143 | 144 | Encoders configurations 145 | 146 | |Key| Value|Desc |Default| 147 | |---|---|---|---| 148 | |sbc_min_bp|2-250|minimum allowed bitpool|auto| 149 | |sbc_max_bp|2-250|maximum allowed bitpool, may not be < sbc_min_bp|auto| 150 | |sbc_freq|16k, 32k, 44k, 48k|16000/32000/44100/48000 Hz sample frequency|auto| 151 | ||auto|do not enforce sample frequency (default)| 152 | |sbc_cmode|mono|mono channel-mode|auto| 153 | ||dual|dual channel-mode| 154 | ||stereo|stereo channel-mode| 155 | ||joint_stereo|joint stereo channel-mode| 156 | ||auto|do not enforce channel-mode (default)| 157 | |sbc_alloc|snr|use SNR bit-allocation algorithm|auto| 158 | ||loudness|use loudness bit-allocation algorithm| 159 | ||auto|do not enforce bit-allocation algorithm (default)| 160 | |sbc_sbands|4, 8|4 or 8 subbands|auto| 161 | ||auto|do not enforce subbands count (default)| 162 | |sbc_blen|4, 8, 12, 16|4/8/12/16 audio blocks in one audio frame|auto| 163 | ||auto|do not enforce audio blocks count (default)| 164 | |ldac_eqmid|hq|LDAC High Quality|auto| 165 | ||sq|LDAC Standard Quality| 166 | ||mq|LDAC Mobile use Quality| 167 | ||auto /abr|LDAC Adaptive Bit Rate| 168 | |ldac_fmt|s16|16-bit signed (little endian)|auto| 169 | ||s24|24-bit signed| 170 | ||s32|32-bit signed| 171 | ||f32|32-bit float| 172 | ||auto|Ref default-sample-format| 173 | |ldac_abr_t1|\|safety threshold for LDACBT_EQMID_HQ and LDACBT_EQMID_SQ|2| 174 | |ldac_abr_t2|\|threshold for dangerous trend of TxQueueDepth|4| 175 | |ldac_abr_t3|\|threshold for critical TxQueueDepth status|6| 176 | |aac_bitrate_mode|\[1, 5\]|Variable Bitrate (VBR)|0| 177 | ||0|Constant Bitrate (CBR)| 178 | |aac_afterburner||Enable/Disable AAC encoder afterburner feature|off| 179 | |aac_fmt|s16|16-bit signed (little endian)|auto| 180 | ||s32|32-bit signed| 181 | ||auto|Ref default-sample-format| 182 | 183 | #### Configure 184 | 185 | edit `/etc/pulse/default.pa` 186 | 187 | append arguments to 'load-module module-bluetooth-discover' 188 | 189 | (module-bluetooth-discover pass all arguments to module-bluez5-discover) 190 | 191 | # LDAC Standard Quality 192 | load-module module-bluetooth-discover a2dp_config="ldac_eqmid=sq" 193 | 194 | # LDAC High Quality; Force LDAC/PA PCM sample format as Float32LE 195 | #load-module module-bluetooth-discover a2dp_config="ldac_eqmid=hq ldac_fmt=f32" 196 | 197 | 198 | equivalent to commands below if you do not use 'module-bluetooth-discover' 199 | 200 | load-module module-bluez5-discover a2dp_config="ldac_eqmid=sq" 201 | 202 | #load-module module-bluez5-discover a2dp_config="ldac_eqmid=hq ldac_fmt=f32" 203 | 204 | #### Others 205 | 206 | see [Wiki](https://github.com/EHfive/pulseaudio-modules-bt/wiki) 207 | 208 | ## TODO 209 | 210 | ~~add ldac abr (Adaptive Bit Rate) supprot~~ 211 | 212 | ~~add APTX , APTX HD Codec support using ffmpeg~~ 213 | 214 | ~~add AAC support using Fraunhofer FDK AAC codec library~~ 215 | 216 | ~~add codec switching support using latest blueZ's experimental feature~~ 217 | 218 | ## Copyright 219 | ``` 220 | pulseaudio-modules-bt 221 | 222 | Copyright (C) 2018-2019 Huang-Huang Bao 223 | 224 | This program is free software: you can redistribute it and/or modify 225 | it under the terms of the GNU General Public License as published by 226 | the Free Software Foundation, either version 3 of the License, or 227 | (at your option) any later version. 228 | 229 | This program is distributed in the hope that it will be useful, 230 | but WITHOUT ANY WARRANTY; without even the implied warranty of 231 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 232 | GNU General Public License for more details. 233 | 234 | You should have received a copy of the GNU General Public License 235 | along with this program. If not, see . 236 | ``` 237 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | #define PACKAGE 21 | #define HAVE_ATOMIC_BUILTINS 22 | 23 | #cmakedefine HAVE_BLUEZ_5_OFONO_HEADSET 24 | #cmakedefine HAVE_BLUEZ_5_NATIVE_HEADSET 25 | #define PACKAGE_VERSION "@PulseAudio_VERSION@" 26 | 27 | #cmakedefine PA_A2DP_CODEC_APTX_FF 28 | #cmakedefine PA_A2DP_CODEC_APTX_HD_FF 29 | #cmakedefine PA_A2DP_CODEC_AAC_FDK 30 | #cmakedefine PA_A2DP_CODEC_LDAC 31 | 32 | #include -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/a2dp-api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef fooa2dpcodecapifoo 22 | #define fooa2dpcodecapifoo 23 | 24 | #ifdef HAVE_CONFIG_H 25 | 26 | #include 27 | 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "a2dp-codecs.h" 35 | #include "rtp.h" 36 | 37 | typedef struct pa_a2dp_codec pa_a2dp_codec_t; 38 | typedef struct pa_a2dp_config pa_a2dp_config_t; 39 | 40 | extern const pa_a2dp_codec_t pa_a2dp_sbc; 41 | extern const pa_a2dp_codec_t pa_a2dp_aac; 42 | extern const pa_a2dp_codec_t pa_a2dp_aptx; 43 | extern const pa_a2dp_codec_t pa_a2dp_aptx_hd; 44 | extern const pa_a2dp_codec_t pa_a2dp_ldac; 45 | 46 | #define PTR_PA_A2DP_SBC (&pa_a2dp_sbc) 47 | #ifdef PA_A2DP_CODEC_AAC_FDK 48 | #define PTR_PA_A2DP_AAC (&pa_a2dp_aac) 49 | #else 50 | #define PTR_PA_A2DP_AAC (NULL) 51 | #endif 52 | 53 | #ifdef PA_A2DP_CODEC_APTX_FF 54 | #define PTR_PA_A2DP_APTX (&pa_a2dp_aptx) 55 | #else 56 | #define PTR_PA_A2DP_APTX (NULL) 57 | #endif 58 | 59 | #ifdef PA_A2DP_CODEC_APTX_HD_FF 60 | #define PTR_PA_A2DP_APTX_HD (&pa_a2dp_aptx_hd) 61 | #else 62 | #define PTR_PA_A2DP_APTX_HD (NULL) 63 | #endif 64 | 65 | #ifdef PA_A2DP_CODEC_LDAC 66 | #define PTR_PA_A2DP_LDAC (&pa_a2dp_ldac) 67 | #else 68 | #define PTR_PA_A2DP_LDAC (NULL) 69 | #endif 70 | 71 | /* Implement in module-bluez5-device.c, run from .encode */ 72 | 73 | typedef void (*pa_a2dp_source_read_cb_t)(const void **read_buf, size_t read_buf_size, void *data); 74 | 75 | typedef void (*pa_a2dp_source_read_buf_free_cb_t)(const void **read_buf, void *data); 76 | 77 | 78 | typedef enum pa_a2dp_codec_index { 79 | PA_A2DP_SINK_MIN, 80 | PA_A2DP_SINK_SBC, 81 | #ifdef PA_A2DP_CODEC_AAC_FDK 82 | PA_A2DP_SINK_AAC, 83 | #endif 84 | #ifdef PA_A2DP_CODEC_APTX_FF 85 | PA_A2DP_SINK_APTX, 86 | #endif 87 | #ifdef PA_A2DP_CODEC_APTX_HD_FF 88 | PA_A2DP_SINK_APTX_HD, 89 | #endif 90 | PA_A2DP_SINK_MAX, 91 | PA_A2DP_SOURCE_MIN = PA_A2DP_SINK_MAX, 92 | PA_A2DP_SOURCE_SBC, 93 | #ifdef PA_A2DP_CODEC_AAC_FDK 94 | PA_A2DP_SOURCE_AAC, 95 | #endif 96 | #ifdef PA_A2DP_CODEC_APTX_FF 97 | PA_A2DP_SOURCE_APTX, 98 | #endif 99 | #ifdef PA_A2DP_CODEC_APTX_HD_FF 100 | PA_A2DP_SOURCE_APTX_HD, 101 | #endif 102 | #ifdef PA_A2DP_CODEC_LDAC 103 | PA_A2DP_SOURCE_LDAC, 104 | #endif 105 | PA_A2DP_SOURCE_MAX, 106 | PA_A2DP_CODEC_INDEX_UNAVAILABLE, 107 | #ifndef PA_A2DP_CODEC_AAC_FDK 108 | PA_A2DP_SINK_AAC = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 109 | #endif 110 | #ifndef PA_A2DP_CODEC_APTX_FF 111 | PA_A2DP_SINK_APTX = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 112 | #endif 113 | #ifndef PA_A2DP_CODEC_APTX_HD_FF 114 | PA_A2DP_SINK_APTX_HD = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 115 | #endif 116 | #ifndef PA_A2DP_CODEC_AAC_FDK 117 | PA_A2DP_SOURCE_AAC = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 118 | #endif 119 | #ifndef PA_A2DP_CODEC_APTX_FF 120 | PA_A2DP_SOURCE_APTX = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 121 | #endif 122 | #ifndef PA_A2DP_CODEC_APTX_HD_FF 123 | PA_A2DP_SOURCE_APTX_HD = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 124 | #endif 125 | #ifndef PA_A2DP_CODEC_LDAC 126 | PA_A2DP_SOURCE_LDAC = PA_A2DP_CODEC_INDEX_UNAVAILABLE, 127 | #endif 128 | } pa_a2dp_codec_index_t; 129 | 130 | typedef struct pa_a2dp_sink { 131 | int priority; 132 | 133 | /* Load decoder syms if it's not loaded; Return true if it's loaded */ 134 | bool (*decoder_load)(); 135 | 136 | /* Memory management is pa_a2dp_sink's work */ 137 | bool (*init)(void **codec_data); 138 | 139 | /* Optional. Update user configurations 140 | * Note: not transport 'configuration' or 'capabilities' */ 141 | int (*update_user_config)(pa_proplist *user_config, void **codec_data); 142 | 143 | void (*config_transport)(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, 144 | pa_sample_spec *sample_spec, void **codec_data); 145 | 146 | void (*get_block_size)(size_t read_link_mtu, size_t *read_block_size, void **codec_data); 147 | 148 | void (*setup_stream)(void **codec_data); 149 | 150 | size_t 151 | (*decode)(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *decoded, 152 | uint32_t *timestamp, void **codec_data); 153 | 154 | void (*free)(void **codec_data); 155 | } pa_a2dp_sink_t; 156 | 157 | 158 | typedef struct pa_a2dp_source { 159 | int priority; 160 | 161 | /* Load encoder syms if it's not loaded; Return true if it's loaded */ 162 | bool (*encoder_load)(); 163 | 164 | /* Memory management is pa_a2dp_source's work */ 165 | bool (*init)(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data); 166 | 167 | /* Optional. Update user configurations 168 | * Note: not transport 'configuration' or 'capabilities' */ 169 | int (*update_user_config)(pa_proplist *user_config, void **codec_data); 170 | 171 | void (*config_transport)(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, 172 | pa_sample_spec *sample_spec, void **codec_data); 173 | 174 | void (*get_block_size)(size_t write_link_mtu, size_t *write_block_size, void **codec_data); 175 | 176 | size_t (*handle_update_buffer_size)(void **codec_data); 177 | 178 | void (*setup_stream)(void **codec_data); 179 | 180 | /* Pass read_cb_data to pa_a2dp_source_read_cb, pa_a2dp_source_read_buf_free_cb */ 181 | size_t (*encode)(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *encoded, 182 | void *read_cb_data, void **codec_data); 183 | 184 | /* Optional, return size of bytes to skip */ 185 | size_t (*handle_skipping)(size_t bytes_to_send, void **codec_data); 186 | 187 | /* Optional */ 188 | void (*set_tx_length)(size_t len, void **codec_data); 189 | 190 | /* Optional */ 191 | void (*decrease_quality)(void **codec_data); 192 | 193 | void (*free)(void **codec_data); 194 | } pa_a2dp_source_t; 195 | 196 | 197 | struct pa_a2dp_codec { 198 | const char *name; 199 | uint8_t codec; 200 | const a2dp_vendor_codec_t *vendor_codec; 201 | pa_a2dp_sink_t *a2dp_sink; 202 | pa_a2dp_source_t *a2dp_source; 203 | 204 | /* Memory management is pa_a2dp_codec's work */ 205 | size_t (*get_capabilities)(void **capabilities); 206 | 207 | void (*free_capabilities)(void **capabilities); 208 | 209 | size_t (*select_configuration)(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, 210 | const size_t capabilities_size, void **configuration); 211 | 212 | void (*free_configuration)(void **configuration); 213 | 214 | /* Return if configuration valid */ 215 | bool (*validate_configuration)(const uint8_t *selected_configuration, const size_t configuration_size); 216 | 217 | }; 218 | 219 | 220 | typedef struct pa_a2dp_freq_cap { 221 | uint32_t rate; 222 | uint32_t cap; 223 | } pa_a2dp_freq_cap_t; 224 | 225 | 226 | 227 | /* Utils */ 228 | 229 | bool pa_a2dp_select_cap_frequency(uint32_t freq_cap, pa_sample_spec default_sample_spec, 230 | const pa_a2dp_freq_cap_t *freq_cap_table, 231 | size_t n, pa_a2dp_freq_cap_t *result); 232 | 233 | void pa_a2dp_init(pa_a2dp_config_t **a2dp_config); 234 | 235 | void pa_a2dp_set_max_priority(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config); 236 | 237 | void pa_a2dp_set_disable(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config); 238 | 239 | void pa_a2dp_free(pa_a2dp_config_t **a2dp_config); 240 | 241 | 242 | void pa_a2dp_get_sink_indices(pa_hashmap **sink_indices, pa_a2dp_config_t **a2dp_config); 243 | 244 | void pa_a2dp_get_source_indices(pa_hashmap **source_indices, pa_a2dp_config_t **a2dp_config); 245 | 246 | void pa_a2dp_get_ordered_indices(pa_hashmap **ordered_indices, pa_a2dp_config_t **a2dp_config); 247 | 248 | 249 | void pa_a2dp_codec_index_to_endpoint(pa_a2dp_codec_index_t codec_index, const char **endpoint); 250 | 251 | void pa_a2dp_endpoint_to_codec_index(const char *endpoint, pa_a2dp_codec_index_t *codec_index); 252 | 253 | void pa_a2dp_codec_index_to_a2dp_codec(pa_a2dp_codec_index_t codec_index, const pa_a2dp_codec_t **a2dp_codec); 254 | 255 | void 256 | pa_a2dp_a2dp_codec_to_codec_index(const pa_a2dp_codec_t *a2dp_codec, bool is_sink, pa_a2dp_codec_index_t *codec_index); 257 | 258 | void pa_a2dp_get_a2dp_codec(uint8_t codec, const a2dp_vendor_codec_t *vendor_codec, const pa_a2dp_codec_t **a2dp_codec); 259 | 260 | bool pa_a2dp_codec_index_is_sink(pa_a2dp_codec_index_t codec_index); 261 | 262 | bool pa_a2dp_codec_index_is_source(pa_a2dp_codec_index_t codec_index); 263 | 264 | 265 | #endif 266 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/a2dp-codecs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2006-2010 Nokia Corporation 6 | * Copyright (C) 2004-2010 Marcel Holtmann 7 | * Copyright (C) 2018 Pali Rohár 8 | * 9 | * 10 | * This library is free software; you can redistribute it and/or 11 | * modify it under the terms of the GNU Lesser General Public 12 | * License as published by the Free Software Foundation; either 13 | * version 2.1 of the License, or (at your option) any later version. 14 | * 15 | * This library is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * Lesser General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Lesser General Public 21 | * License along with this library; if not, write to the Free Software 22 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #define A2DP_CODEC_SBC 0x00 30 | #define A2DP_CODEC_MPEG12 0x01 31 | #define A2DP_CODEC_MPEG24 0x02 32 | #define A2DP_CODEC_ATRAC 0x04 33 | #define A2DP_CODEC_VENDOR 0xFF 34 | 35 | #define SBC_SAMPLING_FREQ_16000 (1 << 3) 36 | #define SBC_SAMPLING_FREQ_32000 (1 << 2) 37 | #define SBC_SAMPLING_FREQ_44100 (1 << 1) 38 | #define SBC_SAMPLING_FREQ_48000 1 39 | 40 | #define SBC_CHANNEL_MODE_MONO (1 << 3) 41 | #define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) 42 | #define SBC_CHANNEL_MODE_STEREO (1 << 1) 43 | #define SBC_CHANNEL_MODE_JOINT_STEREO 1 44 | 45 | #define SBC_BLOCK_LENGTH_4 (1 << 3) 46 | #define SBC_BLOCK_LENGTH_8 (1 << 2) 47 | #define SBC_BLOCK_LENGTH_12 (1 << 1) 48 | #define SBC_BLOCK_LENGTH_16 1 49 | 50 | #define SBC_SUBBANDS_4 (1 << 1) 51 | #define SBC_SUBBANDS_8 1 52 | 53 | #define SBC_ALLOCATION_SNR (1 << 1) 54 | #define SBC_ALLOCATION_LOUDNESS 1 55 | 56 | #define SBC_MAX_BITPOOL_FORCED 250 57 | #define SBC_MAX_BITPOOL 64 58 | #define SBC_MIN_BITPOOL 2 59 | 60 | #define MPEG_CHANNEL_MODE_MONO (1 << 3) 61 | #define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) 62 | #define MPEG_CHANNEL_MODE_STEREO (1 << 1) 63 | #define MPEG_CHANNEL_MODE_JOINT_STEREO 1 64 | 65 | #define MPEG_LAYER_MP1 (1 << 2) 66 | #define MPEG_LAYER_MP2 (1 << 1) 67 | #define MPEG_LAYER_MP3 1 68 | 69 | #define MPEG_SAMPLING_FREQ_16000 (1 << 5) 70 | #define MPEG_SAMPLING_FREQ_22050 (1 << 4) 71 | #define MPEG_SAMPLING_FREQ_24000 (1 << 3) 72 | #define MPEG_SAMPLING_FREQ_32000 (1 << 2) 73 | #define MPEG_SAMPLING_FREQ_44100 (1 << 1) 74 | #define MPEG_SAMPLING_FREQ_48000 1 75 | 76 | #define MPEG_BIT_RATE_INDEX_0 (1 << 0) 77 | #define MPEG_BIT_RATE_INDEX_1 (1 << 1) 78 | #define MPEG_BIT_RATE_INDEX_2 (1 << 2) 79 | #define MPEG_BIT_RATE_INDEX_3 (1 << 3) 80 | #define MPEG_BIT_RATE_INDEX_4 (1 << 4) 81 | #define MPEG_BIT_RATE_INDEX_5 (1 << 5) 82 | #define MPEG_BIT_RATE_INDEX_6 (1 << 6) 83 | #define MPEG_BIT_RATE_INDEX_7 (1 << 7) 84 | #define MPEG_BIT_RATE_INDEX_8 (1 << 8) 85 | #define MPEG_BIT_RATE_INDEX_9 (1 << 9) 86 | #define MPEG_BIT_RATE_INDEX_10 (1 << 10) 87 | #define MPEG_BIT_RATE_INDEX_11 (1 << 11) 88 | #define MPEG_BIT_RATE_INDEX_12 (1 << 12) 89 | #define MPEG_BIT_RATE_INDEX_13 (1 << 13) 90 | #define MPEG_BIT_RATE_INDEX_14 (1 << 14) 91 | 92 | #define MPEG_MP1_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 93 | #define MPEG_MP1_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_2 94 | #define MPEG_MP1_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_3 95 | #define MPEG_MP1_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_4 96 | #define MPEG_MP1_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_5 97 | #define MPEG_MP1_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_6 98 | #define MPEG_MP1_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_7 99 | #define MPEG_MP1_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_8 100 | #define MPEG_MP1_BIT_RATE_288000 MPEG_BIT_RATE_INDEX_9 101 | #define MPEG_MP1_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_10 102 | #define MPEG_MP1_BIT_RATE_352000 MPEG_BIT_RATE_INDEX_11 103 | #define MPEG_MP1_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_12 104 | #define MPEG_MP1_BIT_RATE_416000 MPEG_BIT_RATE_INDEX_13 105 | #define MPEG_MP1_BIT_RATE_448000 MPEG_BIT_RATE_INDEX_14 106 | 107 | #define MPEG_MP2_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 108 | #define MPEG_MP2_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_2 109 | #define MPEG_MP2_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_3 110 | #define MPEG_MP2_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_4 111 | #define MPEG_MP2_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_5 112 | #define MPEG_MP2_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_6 113 | #define MPEG_MP2_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_7 114 | #define MPEG_MP2_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_8 115 | #define MPEG_MP2_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_9 116 | #define MPEG_MP2_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_10 117 | #define MPEG_MP2_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_11 118 | #define MPEG_MP2_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_12 119 | #define MPEG_MP2_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_13 120 | #define MPEG_MP2_BIT_RATE_384000 MPEG_BIT_RATE_INDEX_14 121 | 122 | #define MPEG_MP3_BIT_RATE_32000 MPEG_BIT_RATE_INDEX_1 123 | #define MPEG_MP3_BIT_RATE_40000 MPEG_BIT_RATE_INDEX_2 124 | #define MPEG_MP3_BIT_RATE_48000 MPEG_BIT_RATE_INDEX_3 125 | #define MPEG_MP3_BIT_RATE_56000 MPEG_BIT_RATE_INDEX_4 126 | #define MPEG_MP3_BIT_RATE_64000 MPEG_BIT_RATE_INDEX_5 127 | #define MPEG_MP3_BIT_RATE_80000 MPEG_BIT_RATE_INDEX_6 128 | #define MPEG_MP3_BIT_RATE_96000 MPEG_BIT_RATE_INDEX_7 129 | #define MPEG_MP3_BIT_RATE_112000 MPEG_BIT_RATE_INDEX_8 130 | #define MPEG_MP3_BIT_RATE_128000 MPEG_BIT_RATE_INDEX_9 131 | #define MPEG_MP3_BIT_RATE_160000 MPEG_BIT_RATE_INDEX_10 132 | #define MPEG_MP3_BIT_RATE_192000 MPEG_BIT_RATE_INDEX_11 133 | #define MPEG_MP3_BIT_RATE_224000 MPEG_BIT_RATE_INDEX_12 134 | #define MPEG_MP3_BIT_RATE_256000 MPEG_BIT_RATE_INDEX_13 135 | #define MPEG_MP3_BIT_RATE_320000 MPEG_BIT_RATE_INDEX_14 136 | 137 | #define MPEG_BIT_RATE_FREE MPEG_BIT_RATE_INDEX_0 138 | 139 | #define MPEG_GET_BITRATE(a) ((uint16_t)(a).bitrate1 << 8 | (a).bitrate2) 140 | #define MPEG_SET_BITRATE(a, b) \ 141 | do { \ 142 | (a).bitrate1 = ((b) >> 8) & 0x7f; \ 143 | (a).bitrate2 = (b) & 0xff; \ 144 | } while (0) 145 | 146 | #define AAC_OBJECT_TYPE_MPEG2_AAC_LC 0x80 147 | #define AAC_OBJECT_TYPE_MPEG4_AAC_LC 0x40 148 | #define AAC_OBJECT_TYPE_MPEG4_AAC_LTP 0x20 149 | #define AAC_OBJECT_TYPE_MPEG4_AAC_SCA 0x10 150 | 151 | #define AAC_SAMPLING_FREQ_8000 0x0800 152 | #define AAC_SAMPLING_FREQ_11025 0x0400 153 | #define AAC_SAMPLING_FREQ_12000 0x0200 154 | #define AAC_SAMPLING_FREQ_16000 0x0100 155 | #define AAC_SAMPLING_FREQ_22050 0x0080 156 | #define AAC_SAMPLING_FREQ_24000 0x0040 157 | #define AAC_SAMPLING_FREQ_32000 0x0020 158 | #define AAC_SAMPLING_FREQ_44100 0x0010 159 | #define AAC_SAMPLING_FREQ_48000 0x0008 160 | #define AAC_SAMPLING_FREQ_64000 0x0004 161 | #define AAC_SAMPLING_FREQ_88200 0x0002 162 | #define AAC_SAMPLING_FREQ_96000 0x0001 163 | 164 | #define AAC_CHANNELS_1 0x02 165 | #define AAC_CHANNELS_2 0x01 166 | 167 | #define AAC_GET_BITRATE(a) ((a).bitrate1 << 16 | \ 168 | (a).bitrate2 << 8 | (a).bitrate3) 169 | #define AAC_GET_FREQUENCY(a) ((a).frequency1 << 4 | (a).frequency2) 170 | 171 | #define AAC_SET_BITRATE(a, b) \ 172 | do { \ 173 | (a).bitrate1 = (b >> 16) & 0x7f; \ 174 | (a).bitrate2 = (b >> 8) & 0xff; \ 175 | (a).bitrate3 = b & 0xff; \ 176 | } while (0) 177 | #define AAC_SET_FREQUENCY(a, f) \ 178 | do { \ 179 | (a).frequency1 = (f >> 4) & 0xff; \ 180 | (a).frequency2 = f & 0x0f; \ 181 | } while (0) 182 | 183 | #define AAC_INIT_BITRATE(b) \ 184 | .bitrate1 = (b >> 16) & 0x7f, \ 185 | .bitrate2 = (b >> 8) & 0xff, \ 186 | .bitrate3 = b & 0xff, 187 | #define AAC_INIT_FREQUENCY(f) \ 188 | .frequency1 = (f >> 4) & 0xff, \ 189 | .frequency2 = f & 0x0f, 190 | 191 | #define APTX_VENDOR_ID 0x0000004f 192 | #define APTX_CODEC_ID 0x0001 193 | 194 | #define APTX_CHANNEL_MODE_MONO 0x01 195 | #define APTX_CHANNEL_MODE_STEREO 0x02 196 | 197 | #define APTX_SAMPLING_FREQ_16000 0x08 198 | #define APTX_SAMPLING_FREQ_32000 0x04 199 | #define APTX_SAMPLING_FREQ_44100 0x02 200 | #define APTX_SAMPLING_FREQ_48000 0x01 201 | 202 | #define FASTSTREAM_VENDOR_ID 0x0000000a 203 | #define FASTSTREAM_CODEC_ID 0x0001 204 | 205 | #define FASTSTREAM_DIRECTION_SINK 0x1 206 | #define FASTSTREAM_DIRECTION_SOURCE 0x2 207 | 208 | #define FASTSTREAM_SINK_SAMPLING_FREQ_44100 0x2 209 | #define FASTSTREAM_SINK_SAMPLING_FREQ_48000 0x1 210 | 211 | #define FASTSTREAM_SOURCE_SAMPLING_FREQ_16000 0x2 212 | 213 | #define APTX_LL_VENDOR_ID 0x0000000a 214 | #define APTX_LL_CODEC_ID 0x0002 215 | 216 | #define APTX_LL_CHANNEL_MODE_MONO 0x01 217 | #define APTX_LL_CHANNEL_MODE_STEREO 0x02 218 | 219 | #define APTX_LL_SAMPLING_FREQ_16000 0x08 220 | #define APTX_LL_SAMPLING_FREQ_32000 0x04 221 | #define APTX_LL_SAMPLING_FREQ_44100 0x02 222 | #define APTX_LL_SAMPLING_FREQ_48000 0x01 223 | 224 | /* Default parameters for aptX Low Latency encoder */ 225 | 226 | /* Target codec buffer level = 180 */ 227 | #define APTX_LL_TARGET_LEVEL2 0xb4 228 | #define APTX_LL_TARGET_LEVEL1 0x00 229 | 230 | /* Initial codec buffer level = 360 */ 231 | #define APTX_LL_INITIAL_LEVEL2 0x68 232 | #define APTX_LL_INITIAL_LEVEL1 0x01 233 | 234 | /* SRA max rate 0.005 * 10000 = 50 */ 235 | #define APTX_LL_SRA_MAX_RATE 0x32 236 | 237 | /* SRA averaging time = 1s */ 238 | #define APTX_LL_SRA_AVG_TIME 0x01 239 | 240 | /* Good working codec buffer level = 180 */ 241 | #define APTX_LL_GOOD_WORKING_LEVEL2 0xB4 242 | #define APTX_LL_GOOD_WORKING_LEVEL1 0x00 243 | 244 | #define APTX_HD_VENDOR_ID 0x000000D7 245 | #define APTX_HD_CODEC_ID 0x0024 246 | 247 | #define APTX_HD_CHANNEL_MODE_MONO 0x1 248 | #define APTX_HD_CHANNEL_MODE_STEREO 0x2 249 | 250 | #define APTX_HD_SAMPLING_FREQ_16000 0x8 251 | #define APTX_HD_SAMPLING_FREQ_32000 0x4 252 | #define APTX_HD_SAMPLING_FREQ_44100 0x2 253 | #define APTX_HD_SAMPLING_FREQ_48000 0x1 254 | 255 | #define LDAC_VENDOR_ID 0x0000012d 256 | #define LDAC_CODEC_ID 0x00aa 257 | 258 | #define LDAC_SAMPLING_FREQ_44100 0x20 259 | #define LDAC_SAMPLING_FREQ_48000 0x10 260 | #define LDAC_SAMPLING_FREQ_88200 0x08 261 | #define LDAC_SAMPLING_FREQ_96000 0x04 262 | #define LDAC_SAMPLING_FREQ_176400 0x02 263 | #define LDAC_SAMPLING_FREQ_192000 0x01 264 | 265 | #define LDAC_CHANNEL_MODE_MONO 0x04 266 | #define LDAC_CHANNEL_MODE_DUAL 0x02 267 | #define LDAC_CHANNEL_MODE_STEREO 0x01 268 | 269 | typedef struct { 270 | uint8_t vendor_id4; 271 | uint8_t vendor_id3; 272 | uint8_t vendor_id2; 273 | uint8_t vendor_id1; 274 | uint8_t codec_id2; 275 | uint8_t codec_id1; 276 | } __attribute__ ((packed)) a2dp_vendor_codec_t; 277 | 278 | #define A2DP_GET_VENDOR_ID(a) ( \ 279 | (((uint32_t)(a).vendor_id4) << 0) | \ 280 | (((uint32_t)(a).vendor_id3) << 8) | \ 281 | (((uint32_t)(a).vendor_id2) << 16) | \ 282 | (((uint32_t)(a).vendor_id1) << 24) \ 283 | ) 284 | #define A2DP_GET_CODEC_ID(a) ((a).codec_id2 | (((uint16_t)(a).codec_id1) << 8)) 285 | #define A2DP_SET_VENDOR_ID_CODEC_ID(v, c) ((a2dp_vendor_codec_t){ \ 286 | .vendor_id4 = (((v) >> 0) & 0xff), \ 287 | .vendor_id3 = (((v) >> 8) & 0xff), \ 288 | .vendor_id2 = (((v) >> 16) & 0xff), \ 289 | .vendor_id1 = (((v) >> 24) & 0xff), \ 290 | .codec_id2 = (((c) >> 0) & 0xff), \ 291 | .codec_id1 = (((c) >> 8) & 0xff), \ 292 | }) 293 | 294 | typedef struct { 295 | uint8_t reserved; 296 | uint8_t target_level2; 297 | uint8_t target_level1; 298 | uint8_t initial_level2; 299 | uint8_t initial_level1; 300 | uint8_t sra_max_rate; 301 | uint8_t sra_avg_time; 302 | uint8_t good_working_level2; 303 | uint8_t good_working_level1; 304 | } __attribute__ ((packed)) a2dp_aptx_ll_new_caps_t; 305 | 306 | typedef struct { 307 | a2dp_vendor_codec_t info; 308 | uint8_t frequency; 309 | uint8_t channel_mode; 310 | } __attribute__ ((packed)) a2dp_ldac_t; 311 | 312 | #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ 313 | __BYTE_ORDER == __LITTLE_ENDIAN 314 | 315 | typedef struct { 316 | uint8_t channel_mode:4; 317 | uint8_t frequency:4; 318 | uint8_t allocation_method:2; 319 | uint8_t subbands:2; 320 | uint8_t block_length:4; 321 | uint8_t min_bitpool; 322 | uint8_t max_bitpool; 323 | } __attribute__ ((packed)) a2dp_sbc_t; 324 | 325 | typedef struct { 326 | uint8_t channel_mode:4; 327 | uint8_t crc:1; 328 | uint8_t layer:3; 329 | uint8_t frequency:6; 330 | uint8_t mpf:1; 331 | uint8_t rfa:1; 332 | uint8_t bitrate1:7; 333 | uint8_t vbr:1; 334 | uint8_t bitrate2; 335 | } __attribute__ ((packed)) a2dp_mpeg_t; 336 | 337 | typedef struct { 338 | uint8_t object_type; 339 | uint8_t frequency1; 340 | uint8_t rfa:2; 341 | uint8_t channels:2; 342 | uint8_t frequency2:4; 343 | uint8_t bitrate1:7; 344 | uint8_t vbr:1; 345 | uint8_t bitrate2; 346 | uint8_t bitrate3; 347 | } __attribute__ ((packed)) a2dp_aac_t; 348 | 349 | typedef struct { 350 | a2dp_vendor_codec_t info; 351 | uint8_t channel_mode:4; 352 | uint8_t frequency:4; 353 | } __attribute__ ((packed)) a2dp_aptx_t; 354 | 355 | typedef struct { 356 | a2dp_vendor_codec_t info; 357 | uint8_t direction; 358 | uint8_t sink_frequency:4; 359 | uint8_t source_frequency:4; 360 | } __attribute__ ((packed)) a2dp_faststream_t; 361 | 362 | typedef struct { 363 | a2dp_vendor_codec_t info; 364 | uint8_t channel_mode:4; 365 | uint8_t frequency:4; 366 | uint8_t bidirect_link:1; 367 | uint8_t has_new_caps:1; 368 | uint8_t reserved:6; 369 | a2dp_aptx_ll_new_caps_t new_caps[0]; 370 | } __attribute__ ((packed)) a2dp_aptx_ll_t; 371 | 372 | typedef struct { 373 | a2dp_vendor_codec_t info; 374 | uint8_t channel_mode:4; 375 | uint8_t frequency:4; 376 | uint8_t reserved0; 377 | uint8_t reserved1; 378 | uint8_t reserved2; 379 | uint8_t reserved3; 380 | } __attribute__ ((packed)) a2dp_aptx_hd_t; 381 | 382 | #elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ 383 | __BYTE_ORDER == __BIG_ENDIAN 384 | 385 | typedef struct { 386 | uint8_t frequency:4; 387 | uint8_t channel_mode:4; 388 | uint8_t block_length:4; 389 | uint8_t subbands:2; 390 | uint8_t allocation_method:2; 391 | uint8_t min_bitpool; 392 | uint8_t max_bitpool; 393 | } __attribute__ ((packed)) a2dp_sbc_t; 394 | 395 | typedef struct { 396 | uint8_t layer:3; 397 | uint8_t crc:1; 398 | uint8_t channel_mode:4; 399 | uint8_t rfa:1; 400 | uint8_t mpf:1; 401 | uint8_t frequency:6; 402 | uint8_t vbr:1; 403 | uint8_t bitrate1:7; 404 | uint8_t bitrate2; 405 | } __attribute__ ((packed)) a2dp_mpeg_t; 406 | 407 | typedef struct { 408 | uint8_t object_type; 409 | uint8_t frequency1; 410 | uint8_t frequency2:4; 411 | uint8_t channels:2; 412 | uint8_t rfa:2; 413 | uint8_t vbr:1; 414 | uint8_t bitrate1:7; 415 | uint8_t bitrate2; 416 | uint8_t bitrate3; 417 | } __attribute__ ((packed)) a2dp_aac_t; 418 | 419 | typedef struct { 420 | a2dp_vendor_codec_t info; 421 | uint8_t frequency:4; 422 | uint8_t channel_mode:4; 423 | } __attribute__ ((packed)) a2dp_aptx_t; 424 | 425 | typedef struct { 426 | a2dp_vendor_codec_t info; 427 | uint8_t direction; 428 | uint8_t source_frequency:4; 429 | uint8_t sink_frequency:4; 430 | } __attribute__ ((packed)) a2dp_faststream_t; 431 | 432 | typedef struct { 433 | a2dp_vendor_codec_t info; 434 | uint8_t frequency:4; 435 | uint8_t channel_mode:4; 436 | uint8_t reserved:6; 437 | uint8_t has_new_caps:1; 438 | uint8_t bidirect_link:1; 439 | a2dp_aptx_ll_new_caps_t new_caps[0]; 440 | } __attribute__ ((packed)) a2dp_aptx_ll_t; 441 | 442 | typedef struct { 443 | a2dp_vendor_codec_t info; 444 | uint8_t frequency:4; 445 | uint8_t channel_mode:4; 446 | uint8_t reserved0; 447 | uint8_t reserved1; 448 | uint8_t reserved2; 449 | uint8_t reserved3; 450 | } __attribute__ ((packed)) a2dp_aptx_hd_t; 451 | 452 | #else 453 | #error "Unknown byte order" 454 | #endif 455 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/a2dp_aptx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "a2dp-api.h" 30 | 31 | #include "ffmpeg_libs.h" 32 | 33 | #define streq(a, b) (!strcmp((a),(b))) 34 | 35 | 36 | typedef struct aptx_info { 37 | pa_a2dp_source_read_cb_t read_pcm; 38 | pa_a2dp_source_read_buf_free_cb_t read_buf_free; 39 | 40 | bool is_a2dp_sink; 41 | bool is_hd; 42 | 43 | size_t aptx_frame_size; 44 | int nb_samples; 45 | const AVCodec *av_codec; 46 | AVCodecContext *av_codec_ctx; 47 | int channel_mode; 48 | uint16_t seq_num; 49 | 50 | 51 | size_t block_size; 52 | 53 | 54 | } aptx_info_t; 55 | 56 | static const AVCodec *av_codec_aptx_decoder = NULL; 57 | static const AVCodec *av_codec_aptx_encoder = NULL; 58 | 59 | static const AVCodec *av_codec_aptx_hd_decoder = NULL; 60 | static const AVCodec *av_codec_aptx_hd_encoder = NULL; 61 | 62 | 63 | static bool pa_aptx_decoder_load() { 64 | if(!ffmpeg_libs_load()) 65 | return false; 66 | if (av_codec_aptx_decoder) 67 | return true; 68 | av_codec_aptx_decoder = avcodec_find_decoder_func(AV_CODEC_ID_APTX); 69 | if (!av_codec_aptx_decoder) { 70 | pa_log_debug("Cannot find APTX decoder in FFmpeg avcodec library"); 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | 77 | static bool pa_aptx_encoder_load() { 78 | if(!ffmpeg_libs_load()) 79 | return false; 80 | if (av_codec_aptx_encoder) 81 | return true; 82 | av_codec_aptx_encoder = avcodec_find_encoder_func(AV_CODEC_ID_APTX); 83 | if (!av_codec_aptx_encoder) { 84 | pa_log_debug("Cannot find APTX encoder in FFmpeg avcodec library"); 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | 91 | static bool pa_aptx_hd_decoder_load() { 92 | if(!ffmpeg_libs_load()) 93 | return false; 94 | if (av_codec_aptx_hd_decoder) 95 | return true; 96 | av_codec_aptx_hd_decoder = avcodec_find_decoder_func(AV_CODEC_ID_APTX_HD); 97 | if (!av_codec_aptx_hd_decoder) { 98 | pa_log_debug("Cannot find APTX HD decoder in FFmpeg avcodec library"); 99 | return false; 100 | } 101 | 102 | return true; 103 | } 104 | 105 | static bool pa_aptx_hd_encoder_load() { 106 | if(!ffmpeg_libs_load()) 107 | return false; 108 | if (av_codec_aptx_hd_encoder) 109 | return true; 110 | av_codec_aptx_hd_encoder = avcodec_find_encoder_func(AV_CODEC_ID_APTX_HD); 111 | if (!av_codec_aptx_hd_encoder) { 112 | pa_log_debug("Cannot find APTX HD encoder in FFmpeg avcodec library"); 113 | return false; 114 | } 115 | 116 | return true; 117 | } 118 | 119 | static bool _internal_pa_dual_decoder_init(bool is_hd, void **codec_data) { 120 | aptx_info_t *info = pa_xmalloc0(sizeof(aptx_info_t)); 121 | *codec_data = info; 122 | info->is_hd = is_hd; 123 | info->is_a2dp_sink = true; 124 | info->aptx_frame_size = is_hd ? 6 : 4; 125 | info->av_codec = is_hd ? av_codec_aptx_hd_decoder : av_codec_aptx_decoder; 126 | return true; 127 | } 128 | 129 | static bool 130 | _internal_pa_dual_encoder_init(bool is_hd, pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, 131 | void **codec_data) { 132 | aptx_info_t *info = pa_xmalloc0(sizeof(aptx_info_t)); 133 | *codec_data = info; 134 | info->is_hd = is_hd; 135 | info->is_a2dp_sink = false; 136 | info->read_pcm = read_cb; 137 | info->read_buf_free = free_cb; 138 | info->aptx_frame_size = is_hd ? 6 : 4; 139 | info->av_codec = is_hd ? av_codec_aptx_hd_encoder : av_codec_aptx_encoder; 140 | return true; 141 | } 142 | 143 | static int pa_dual_update_user_config(pa_proplist *user_config, void **codec_data) { 144 | return 0; 145 | } 146 | 147 | static size_t 148 | pa_dual_decode(const void *read_buf, size_t read_buf_size, void *write_buf, size_t write_buf_size, size_t *_decoded, 149 | uint32_t *timestamp, void **codec_data) { 150 | const struct rtp_header *header; 151 | void *p; 152 | int ret = 1; 153 | size_t i; 154 | AVPacket *pkt; 155 | size_t to_decode; 156 | size_t total_written = 0; 157 | AVFrame *av_frame = NULL; 158 | aptx_info_t *aptx_info = *codec_data; 159 | 160 | 161 | pa_assert(aptx_info); 162 | pa_assert(aptx_info->av_codec); 163 | pa_assert(aptx_info->av_codec_ctx); 164 | 165 | if (aptx_info->is_hd) { 166 | header = read_buf; 167 | 168 | *timestamp = ntohl(header->timestamp); 169 | 170 | p = (uint8_t *) read_buf + sizeof(*header); 171 | to_decode = read_buf_size - sizeof(*header); 172 | } else { 173 | *timestamp = (uint32_t) -1; 174 | 175 | p = (uint8_t *) read_buf; 176 | to_decode = read_buf_size; 177 | } 178 | 179 | av_frame = av_frame_alloc_func(); 180 | pkt = av_packet_alloc_func(); 181 | pkt->data = p; 182 | pkt->size = (int) to_decode; 183 | 184 | 185 | *_decoded = 0; 186 | 187 | while(ret){ 188 | ret = avcodec_send_packet_func(aptx_info->av_codec_ctx, pkt); 189 | if (PA_UNLIKELY(ret == AVERROR(EINVAL))) { 190 | avcodec_flush_buffers_func(aptx_info->av_codec_ctx); 191 | continue; 192 | } else if (PA_UNLIKELY(ret < 0 && ret != AVERROR(EAGAIN))) { 193 | pa_log_debug("Error submitting the packet to the decoder"); 194 | goto done; 195 | } 196 | ret = avcodec_receive_frame_func(aptx_info->av_codec_ctx, av_frame); 197 | if (PA_UNLIKELY(ret < 0)) { 198 | pa_log_debug("Error during decoding"); 199 | goto done; 200 | } 201 | } 202 | 203 | *_decoded = aptx_info->aptx_frame_size * av_frame->nb_samples / 4; 204 | 205 | total_written = (size_t) av_frame->nb_samples * (4 * 2); 206 | 207 | pa_assert_fp(_decoded <= read_buf_size); 208 | pa_assert_fp(total_written <= write_buf_size); 209 | 210 | for (i = 0; i < av_frame->nb_samples * sizeof(uint32_t); i += sizeof(uint32_t)) { 211 | memcpy((uint8_t *) write_buf + i * 2, av_frame->data[0] + i, sizeof(uint32_t)); 212 | memcpy((uint8_t *) write_buf + i * 2 + sizeof(uint32_t), av_frame->data[1] + i, sizeof(uint32_t)); 213 | } 214 | 215 | done: 216 | av_frame_free_func(&av_frame); 217 | av_packet_free_func(&pkt); 218 | return total_written; 219 | } 220 | 221 | static size_t 222 | pa_dual_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, 223 | void **codec_data) { 224 | struct rtp_header *header; 225 | size_t nbytes; 226 | void *d; 227 | const void *p; 228 | AVFrame *av_frame; 229 | AVPacket *pkt; 230 | aptx_info_t *aptx_info = *codec_data; 231 | int ret; 232 | size_t i; 233 | 234 | pa_assert(aptx_info); 235 | pa_assert(aptx_info->av_codec); 236 | pa_assert(aptx_info->av_codec_ctx); 237 | 238 | pa_assert_fp(aptx_info->av_codec_ctx->frame_size <= write_buf_size); 239 | 240 | aptx_info->read_pcm(&p, (size_t) aptx_info->block_size, read_cb_data); 241 | 242 | if (aptx_info->is_hd) { 243 | header = write_buf; 244 | memset(write_buf, 0, sizeof(*header)); 245 | header->v = 2; 246 | header->pt = 96; 247 | header->sequence_number = htons(aptx_info->seq_num++); 248 | header->timestamp = htonl(timestamp); 249 | header->ssrc = htonl(1); 250 | d = (uint8_t *) write_buf + sizeof(*header); 251 | } else { 252 | d = (uint8_t *) write_buf; 253 | } 254 | 255 | av_frame = av_frame_alloc_func(); 256 | av_frame->nb_samples = aptx_info->nb_samples; 257 | av_frame->format = aptx_info->av_codec_ctx->sample_fmt; 258 | av_frame->channel_layout = aptx_info->av_codec_ctx->channel_layout; 259 | 260 | pkt = av_packet_alloc_func(); 261 | 262 | pa_assert_se(av_frame_get_buffer_func(av_frame, 0) >= 0); 263 | pa_assert_se(av_frame_make_writable_func(av_frame) >= 0); 264 | 265 | 266 | for (i = 0; i < av_frame->nb_samples * sizeof(uint32_t); i += sizeof(uint32_t)) { 267 | memcpy(av_frame->data[0] + i, (uint8_t *) p + i * 2, sizeof(uint32_t)); 268 | memcpy(av_frame->data[1] + i, (uint8_t *) p + i * 2 + sizeof(uint32_t), sizeof(uint32_t)); 269 | } 270 | *_encoded = 0; 271 | 272 | ret = avcodec_send_frame_func(aptx_info->av_codec_ctx, av_frame); 273 | 274 | if (PA_UNLIKELY(ret < 0)) { 275 | fprintf(stderr, "Error sending the frame to the encoder\n"); 276 | nbytes = 0; 277 | goto done; 278 | } 279 | 280 | ret = avcodec_receive_packet_func(aptx_info->av_codec_ctx, pkt); 281 | 282 | if (PA_UNLIKELY(ret != 0)) { 283 | fprintf(stderr, "Error receiving the packet from the encoder\n"); 284 | nbytes = 0; 285 | goto done; 286 | } 287 | 288 | memcpy(d, pkt->data, (size_t) pkt->size); 289 | d = (uint8_t *) d + pkt->size; 290 | 291 | nbytes = (uint8_t *) d - (uint8_t *) write_buf; 292 | *_encoded += aptx_info->block_size; 293 | 294 | done: 295 | av_frame_free_func(&av_frame); 296 | av_packet_free_func(&pkt); 297 | aptx_info->read_buf_free(&p, read_cb_data); 298 | return nbytes; 299 | } 300 | 301 | static void 302 | pa_dual_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, 303 | pa_sample_spec *sample_spec, void **codec_data) { 304 | aptx_info_t *aptx_info = *codec_data; 305 | a2dp_aptx_t *config = (a2dp_aptx_t *) configuration; 306 | AVCodecContext *aptx_ctx; 307 | pa_assert(aptx_info); 308 | pa_assert(aptx_info->av_codec); 309 | pa_assert_se(configuration_size == (aptx_info->is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t))); 310 | 311 | if (aptx_info->av_codec_ctx) 312 | avcodec_free_context_func(&aptx_info->av_codec_ctx); 313 | 314 | aptx_info->av_codec_ctx = avcodec_alloc_context3_func(aptx_info->av_codec); 315 | aptx_ctx = aptx_info->av_codec_ctx; 316 | 317 | aptx_ctx->sample_fmt = AV_SAMPLE_FMT_S32P; 318 | sample_spec->format = PA_SAMPLE_S32LE; 319 | 320 | switch (config->frequency) { 321 | case APTX_SAMPLING_FREQ_16000: 322 | aptx_ctx->sample_rate = 16000; 323 | aptx_ctx->bit_rate = 16000; 324 | sample_spec->rate = 16000U; 325 | break; 326 | case APTX_SAMPLING_FREQ_32000: 327 | aptx_ctx->sample_rate = 32000; 328 | aptx_ctx->bit_rate = 32000; 329 | sample_spec->rate = 32000U; 330 | break; 331 | case APTX_SAMPLING_FREQ_44100: 332 | aptx_ctx->sample_rate = 44100; 333 | aptx_ctx->bit_rate = 44100; 334 | sample_spec->rate = 44100U; 335 | break; 336 | case APTX_SAMPLING_FREQ_48000: 337 | aptx_ctx->sample_rate = 48000; 338 | aptx_ctx->bit_rate = 48000; 339 | sample_spec->rate = 48000U; 340 | break; 341 | default: 342 | pa_assert_not_reached(); 343 | } 344 | 345 | switch (config->channel_mode) { 346 | case APTX_CHANNEL_MODE_STEREO: 347 | aptx_ctx->channel_layout = AV_CH_LAYOUT_STEREO; 348 | aptx_ctx->channels = 2; 349 | sample_spec->channels = 2; 350 | break; 351 | default: 352 | pa_assert_not_reached(); 353 | } 354 | 355 | pa_assert_se(avcodec_open2_func(aptx_info->av_codec_ctx, aptx_info->av_codec, NULL) == 0); 356 | 357 | }; 358 | 359 | static void pa_dual_get_read_block_size(size_t read_link_mtu, size_t *read_block_size, void **codec_data) { 360 | aptx_info_t *aptx_info = *codec_data; 361 | size_t aptx_frame_size = aptx_info->aptx_frame_size; 362 | size_t rtp_use_size = aptx_info->is_hd ? sizeof(struct rtp_header) : 0; 363 | pa_assert(aptx_info); 364 | 365 | /* 366 |  * PCM 32-bit, 2 channel (4 bytes * 2) 367 |  * PCM frames/APTX frames == 4 368 |  * */ 369 | *read_block_size = (read_link_mtu - rtp_use_size) / aptx_frame_size * (4 * 2) * 4; 370 | aptx_info->block_size = *read_block_size; 371 | }; 372 | 373 | static void pa_dual_get_write_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { 374 | aptx_info_t *aptx_info = *codec_data; 375 | size_t aptx_frame_size = aptx_info->aptx_frame_size; 376 | size_t rtp_use_size = aptx_info->is_hd ? sizeof(struct rtp_header) : 0; 377 | pa_assert(aptx_info); 378 | 379 | /* 380 | * PCM 32-bit, 2 channel (4 bytes * 2) 381 | * PCM frames/APTX frames == 4 382 | * */ 383 | *write_block_size = (write_link_mtu - rtp_use_size) / aptx_frame_size * (4 * 2) * 4; 384 | aptx_info->block_size = *write_block_size; 385 | }; 386 | 387 | 388 | static void pa_dual_setup_stream(void **codec_data) { 389 | aptx_info_t *aptx_info = *codec_data; 390 | pa_assert(aptx_info); 391 | aptx_info->nb_samples = (int) (aptx_info->block_size / (4 * 2)); 392 | aptx_info->av_codec_ctx->frame_size = (int) (aptx_info->aptx_frame_size * aptx_info->nb_samples / 4); 393 | }; 394 | 395 | static void pa_dual_free(void **codec_data) { 396 | aptx_info_t *aptx_info = *codec_data; 397 | if (!aptx_info) 398 | return; 399 | if (aptx_info->av_codec_ctx) 400 | avcodec_free_context_func(&aptx_info->av_codec_ctx); 401 | pa_xfree(aptx_info); 402 | *codec_data = NULL; 403 | 404 | }; 405 | 406 | static size_t _internal_pa_dual_get_capabilities(bool is_hd, void **_capabilities) { 407 | 408 | const size_t cap_size = is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t); 409 | a2dp_aptx_t *capabilities = (a2dp_aptx_t *) pa_xmalloc0(cap_size); 410 | 411 | if (is_hd) { 412 | capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID); 413 | } else { 414 | capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID); 415 | } 416 | 417 | capabilities->channel_mode = APTX_CHANNEL_MODE_STEREO; 418 | capabilities->frequency = APTX_SAMPLING_FREQ_16000 | APTX_SAMPLING_FREQ_32000 | APTX_SAMPLING_FREQ_44100 | 419 | APTX_SAMPLING_FREQ_48000; 420 | *_capabilities = capabilities; 421 | 422 | return cap_size; 423 | }; 424 | 425 | 426 | static size_t 427 | _internal_pa_dual_select_configuration(bool is_hd, const pa_sample_spec default_sample_spec, 428 | const uint8_t *supported_capabilities, 429 | const size_t capabilities_size, void **configuration) { 430 | a2dp_aptx_t *cap; 431 | a2dp_aptx_t *config; 432 | const size_t cap_size = is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t); 433 | pa_a2dp_freq_cap_t aptx_freq_cap, aptx_freq_table[] = { 434 | {16000U, APTX_SAMPLING_FREQ_16000}, 435 | {32000U, APTX_SAMPLING_FREQ_32000}, 436 | {44100U, APTX_SAMPLING_FREQ_44100}, 437 | {48000U, APTX_SAMPLING_FREQ_48000} 438 | }; 439 | 440 | cap = (a2dp_aptx_t *) supported_capabilities; 441 | 442 | if (capabilities_size != cap_size) 443 | return 0; 444 | 445 | config = (a2dp_aptx_t *) pa_xmalloc0(cap_size); 446 | 447 | if (is_hd) { 448 | config->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID); 449 | } else { 450 | config->info = A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID); 451 | } 452 | 453 | if (!pa_a2dp_select_cap_frequency(cap->frequency, default_sample_spec, aptx_freq_table, 454 | PA_ELEMENTSOF(aptx_freq_table), &aptx_freq_cap)) 455 | return 0; 456 | 457 | config->frequency = (uint8_t) aptx_freq_cap.cap; 458 | 459 | if (cap->channel_mode & APTX_CHANNEL_MODE_STEREO) 460 | config->channel_mode = APTX_CHANNEL_MODE_STEREO; 461 | else { 462 | pa_log_error("No supported channel modes"); 463 | return 0; 464 | } 465 | 466 | *configuration = config; 467 | return cap_size; 468 | }; 469 | 470 | static void pa_dual_free_capabilities(void **capabilities) { 471 | if (!capabilities || !*capabilities) 472 | return; 473 | pa_xfree(*capabilities); 474 | *capabilities = NULL; 475 | } 476 | 477 | static bool _internal_pa_dual_validate_configuration(bool is_hd, const uint8_t *selected_configuration, 478 | const size_t configuration_size) { 479 | a2dp_aptx_t *c = (a2dp_aptx_t *) selected_configuration; 480 | 481 | if (configuration_size != (is_hd ? sizeof(a2dp_aptx_hd_t) : sizeof(a2dp_aptx_t))) { 482 | pa_log_error("APTX configuration array of invalid size"); 483 | return false; 484 | } 485 | 486 | switch (c->frequency) { 487 | case APTX_SAMPLING_FREQ_16000: 488 | case APTX_SAMPLING_FREQ_32000: 489 | case APTX_SAMPLING_FREQ_44100: 490 | case APTX_SAMPLING_FREQ_48000: 491 | break; 492 | default: 493 | pa_log_error("Invalid sampling frequency in APTX configuration"); 494 | return false; 495 | } 496 | 497 | switch (c->channel_mode) { 498 | case APTX_CHANNEL_MODE_STEREO: 499 | break; 500 | default: 501 | pa_log_error("Invalid channel mode in APTX Configuration"); 502 | return false; 503 | } 504 | return true; 505 | }; 506 | 507 | 508 | static bool pa_aptx_decoder_init(void **codec_data) { 509 | return _internal_pa_dual_decoder_init(false, codec_data); 510 | } 511 | 512 | static bool pa_aptx_hd_decoder_init(void **codec_data) { 513 | return _internal_pa_dual_decoder_init(true, codec_data); 514 | } 515 | 516 | static bool 517 | pa_aptx_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { 518 | return _internal_pa_dual_encoder_init(false, read_cb, free_cb, codec_data); 519 | } 520 | 521 | static bool 522 | pa_aptx_hd_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, 523 | void **codec_data) { 524 | return _internal_pa_dual_encoder_init(true, read_cb, free_cb, codec_data); 525 | } 526 | 527 | 528 | static size_t pa_aptx_get_capabilities(void **_capabilities) { 529 | return _internal_pa_dual_get_capabilities(false, _capabilities); 530 | } 531 | 532 | static size_t pa_aptx_select_configuration(const pa_sample_spec default_sample_spec, 533 | const uint8_t *supported_capabilities, 534 | const size_t capabilities_size, void **configuration) { 535 | return _internal_pa_dual_select_configuration(false, default_sample_spec, supported_capabilities, capabilities_size, 536 | configuration); 537 | } 538 | 539 | static bool pa_aptx_validate_configuration(const uint8_t *selected_configuration, 540 | const size_t configuration_size) { 541 | return _internal_pa_dual_validate_configuration(false, selected_configuration, configuration_size); 542 | } 543 | 544 | static size_t pa_aptx_hd_get_capabilities(void **_capabilities) { 545 | return _internal_pa_dual_get_capabilities(true, _capabilities); 546 | } 547 | 548 | static size_t pa_aptx_hd_select_configuration(const pa_sample_spec default_sample_spec, 549 | const uint8_t *supported_capabilities, 550 | const size_t capabilities_size, void **configuration) { 551 | return _internal_pa_dual_select_configuration(true, default_sample_spec, supported_capabilities, capabilities_size, 552 | configuration); 553 | } 554 | 555 | static bool pa_aptx_hd_validate_configuration(const uint8_t *selected_configuration, 556 | const size_t configuration_size) { 557 | return _internal_pa_dual_validate_configuration(true, selected_configuration, configuration_size); 558 | } 559 | 560 | static pa_a2dp_source_t pa_aptx_source = { 561 | .encoder_load = pa_aptx_encoder_load, 562 | .init = pa_aptx_encoder_init, 563 | .update_user_config = pa_dual_update_user_config, 564 | .encode = pa_dual_encode, 565 | .config_transport = pa_dual_config_transport, 566 | .get_block_size = pa_dual_get_write_block_size, 567 | .setup_stream = pa_dual_setup_stream, 568 | .set_tx_length = NULL, 569 | .decrease_quality = NULL, 570 | .free = pa_dual_free 571 | }; 572 | 573 | static pa_a2dp_sink_t pa_aptx_sink = { 574 | .decoder_load = pa_aptx_decoder_load, 575 | .init = pa_aptx_decoder_init, 576 | .update_user_config = pa_dual_update_user_config, 577 | .config_transport = pa_dual_config_transport, 578 | .get_block_size = pa_dual_get_read_block_size, 579 | .setup_stream = pa_dual_setup_stream, 580 | .decode = pa_dual_decode, 581 | .free = pa_dual_free 582 | }; 583 | 584 | const pa_a2dp_codec_t pa_a2dp_aptx = { 585 | .name = "aptX", 586 | .codec = A2DP_CODEC_VENDOR, 587 | .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(APTX_VENDOR_ID, APTX_CODEC_ID), 588 | .a2dp_sink = &pa_aptx_sink, 589 | .a2dp_source = &pa_aptx_source, 590 | .get_capabilities = pa_aptx_get_capabilities, 591 | .select_configuration = pa_aptx_select_configuration, 592 | .free_capabilities = pa_dual_free_capabilities, 593 | .free_configuration = pa_dual_free_capabilities, 594 | .validate_configuration = pa_aptx_validate_configuration 595 | }; 596 | 597 | static pa_a2dp_source_t pa_aptx_hd_source = { 598 | .encoder_load = pa_aptx_hd_encoder_load, 599 | .init = pa_aptx_hd_encoder_init, 600 | .update_user_config = pa_dual_update_user_config, 601 | .encode = pa_dual_encode, 602 | .config_transport = pa_dual_config_transport, 603 | .get_block_size = pa_dual_get_write_block_size, 604 | .setup_stream = pa_dual_setup_stream, 605 | .set_tx_length = NULL, 606 | .decrease_quality = NULL, 607 | .free = pa_dual_free 608 | }; 609 | 610 | static pa_a2dp_sink_t pa_aptx_hd_sink = { 611 | .decoder_load = pa_aptx_hd_decoder_load, 612 | .init = pa_aptx_hd_decoder_init, 613 | .update_user_config = pa_dual_update_user_config, 614 | .config_transport = pa_dual_config_transport, 615 | .get_block_size = pa_dual_get_read_block_size, 616 | .setup_stream = pa_dual_setup_stream, 617 | .decode = pa_dual_decode, 618 | .free = pa_dual_free 619 | }; 620 | 621 | const pa_a2dp_codec_t pa_a2dp_aptx_hd = { 622 | .name = "aptX_HD", 623 | .codec = A2DP_CODEC_VENDOR, 624 | .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(APTX_HD_VENDOR_ID, APTX_HD_CODEC_ID), 625 | .a2dp_sink = &pa_aptx_hd_sink, 626 | .a2dp_source = &pa_aptx_hd_source, 627 | .get_capabilities = pa_aptx_hd_get_capabilities, 628 | .select_configuration = pa_aptx_hd_select_configuration, 629 | .free_capabilities = pa_dual_free_capabilities, 630 | .free_configuration = pa_dual_free_capabilities, 631 | .validate_configuration = pa_aptx_hd_validate_configuration 632 | }; 633 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/a2dp_ldac.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef HAVE_CONFIG_H 26 | 27 | #include 28 | 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | #include "a2dp-api.h" 39 | 40 | #include "ldac_libs.h" 41 | 42 | #define streq(a, b) (!strcmp((a),(b))) 43 | 44 | #define LDAC_ABR_THRESHOLD_CRITICAL 5 45 | #define LDAC_ABR_THRESHOLD_DANGEROUSTREND 3 46 | #define LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ 1 47 | 48 | 49 | #define LDAC_ABR_INTERVAL_MS 5 50 | 51 | 52 | typedef struct ldac_info { 53 | HANDLE_LDAC_BT hLdacBt; 54 | HANDLE_LDAC_ABR hLdacAbr; 55 | 56 | pa_a2dp_source_read_cb_t read_pcm; 57 | pa_a2dp_source_read_buf_free_cb_t read_buf_free; 58 | 59 | int eqmid; 60 | bool enable_abr; 61 | int channel_mode; 62 | pa_sample_format_t force_pa_fmt; 63 | LDACBT_SMPL_FMT_T pcm_fmt; 64 | unsigned int abr_t1; 65 | unsigned int abr_t2; 66 | unsigned int abr_t3; 67 | uint32_t pcm_frequency; 68 | 69 | uint16_t pcm_lsu; 70 | size_t ldac_frame_size; 71 | size_t pcm_read_size; 72 | size_t q_write_block_size; 73 | pa_sample_spec sample_spec; 74 | 75 | uint16_t seq_num; 76 | uint32_t layer_specific; 77 | uint32_t written; 78 | size_t tx_length; 79 | 80 | size_t interval_bytes; 81 | size_t read_bytes; 82 | 83 | uint8_t buf_index; 84 | void *buf; 85 | 86 | size_t mtu; 87 | 88 | } ldac_info_t; 89 | 90 | static bool pa_ldac_encoder_load() { 91 | return ldac_encoder_load(); 92 | } 93 | 94 | static bool 95 | pa_ldac_encoder_init(pa_a2dp_source_read_cb_t read_cb, pa_a2dp_source_read_buf_free_cb_t free_cb, void **codec_data) { 96 | ldac_info_t *info = pa_xmalloc0(sizeof(ldac_info_t)); 97 | *codec_data = info; 98 | info->read_pcm = read_cb; 99 | info->read_buf_free = free_cb; 100 | info->eqmid = LDACBT_EQMID_HQ; 101 | if(is_ldac_abr_loaded()) 102 | info->enable_abr = true; 103 | info->force_pa_fmt = PA_SAMPLE_INVALID; 104 | 105 | info->abr_t1 = LDAC_ABR_THRESHOLD_SAFETY_FOR_HQSQ; 106 | info->abr_t2 = LDAC_ABR_THRESHOLD_DANGEROUSTREND; 107 | info->abr_t3 = LDAC_ABR_THRESHOLD_CRITICAL; 108 | 109 | return true; 110 | } 111 | 112 | static int pa_ldac_update_user_config(pa_proplist *user_config, void **codec_data) { 113 | ldac_info_t *i = *codec_data; 114 | const char *ldac_eqmid_str, *ldac_fmt_str, *abr_t1_str, *abr_t2_str, *abr_t3_str; 115 | unsigned int abr_t1, abr_t2, abr_t3; 116 | int ret = 0; 117 | ldac_eqmid_str = pa_proplist_gets(user_config, "ldac_eqmid"); 118 | ldac_fmt_str = pa_proplist_gets(user_config, "ldac_fmt"); 119 | abr_t1_str = pa_proplist_gets(user_config, "ldac_abr_t1"); 120 | abr_t2_str = pa_proplist_gets(user_config, "ldac_abr_t2"); 121 | abr_t3_str = pa_proplist_gets(user_config, "ldac_abr_t3"); 122 | 123 | pa_log_debug("LDAC ABR library loaded: %s",is_ldac_abr_loaded()?"true":"false"); 124 | 125 | if (ldac_eqmid_str) { 126 | if (streq(ldac_eqmid_str, "hq")) { 127 | i->eqmid = LDACBT_EQMID_HQ; 128 | i->enable_abr = false; 129 | ret++; 130 | } else if (streq(ldac_eqmid_str, "sq")) { 131 | i->eqmid = LDACBT_EQMID_SQ; 132 | i->enable_abr = false; 133 | ret++; 134 | } else if (streq(ldac_eqmid_str, "mq")) { 135 | i->eqmid = LDACBT_EQMID_MQ; 136 | i->enable_abr = false; 137 | ret++; 138 | } else if (streq(ldac_eqmid_str, "auto") || 139 | streq(ldac_eqmid_str, "abr")) { 140 | i->eqmid = LDACBT_EQMID_HQ; 141 | if(is_ldac_abr_loaded()) 142 | i->enable_abr = true; 143 | ret++; 144 | } else { 145 | pa_log("ldac_eqmid parameter must be either hq, sq, mq, or auto/abr (found %s)", ldac_eqmid_str); 146 | } 147 | } 148 | 149 | if (ldac_fmt_str) { 150 | if (streq(ldac_fmt_str, "s16")) { 151 | i->force_pa_fmt = PA_SAMPLE_S16LE; 152 | ret++; 153 | } else if (streq(ldac_fmt_str, "s24")) { 154 | i->force_pa_fmt = PA_SAMPLE_S24LE; 155 | ret++; 156 | } else if (streq(ldac_fmt_str, "s32")) { 157 | i->force_pa_fmt = PA_SAMPLE_S32LE; 158 | ret++; 159 | } else if (streq(ldac_fmt_str, "f32")) { 160 | i->force_pa_fmt = PA_SAMPLE_FLOAT32LE; 161 | ret++; 162 | } else if (streq(ldac_fmt_str, "auto")) { 163 | i->force_pa_fmt = PA_SAMPLE_INVALID; 164 | ret++; 165 | } else { 166 | pa_log("ldac_fmt parameter must be either s16, s24, s32, f32 or auto (found %s)", ldac_fmt_str); 167 | } 168 | } 169 | 170 | abr_t1 = abr_t1_str ? (unsigned int) atoi(abr_t1_str) : i->abr_t1; 171 | abr_t2 = abr_t2_str ? (unsigned int) atoi(abr_t2_str) : i->abr_t2; 172 | abr_t3 = abr_t3_str ? (unsigned int) atoi(abr_t3_str) : i->abr_t3; 173 | 174 | if (0 < abr_t1 && abr_t1 <= abr_t2 && abr_t2 <= abr_t3) { 175 | i->abr_t1 = abr_t1; 176 | i->abr_t2 = abr_t2; 177 | i->abr_t3 = abr_t3; 178 | ret += i->abr_t1 != abr_t1; 179 | ret += i->abr_t2 != abr_t2; 180 | ret += i->abr_t3 != abr_t3; 181 | } else 182 | pa_log("ldac_abr_t1,2,3 parameter(s) invalid, ensure 0 < ldac_abr_t1 <= ldac_abr_t2 <= ldac_abr_t3"); 183 | 184 | return ret; 185 | } 186 | 187 | static size_t 188 | pa_ldac_encode(uint32_t timestamp, void *write_buf, size_t write_buf_size, size_t *_encoded, void *read_cb_data, 189 | void **codec_data) { 190 | struct rtp_header *header; 191 | struct rtp_payload *payload; 192 | size_t nbytes; 193 | void *d; 194 | const void *p; 195 | size_t to_write, to_encode, ldac_enc_read; 196 | unsigned frame_count; 197 | ldac_info_t *ldac_info = *codec_data; 198 | pa_assert(ldac_info); 199 | pa_assert(ldac_info->hLdacBt); 200 | 201 | if(PA_UNLIKELY(ldac_info->buf != write_buf && ldac_info->buf)){ 202 | int ret; 203 | ldac_info->buf_index = 0; 204 | ldacBT_close_handle_func(ldac_info->hLdacBt); 205 | ret = ldacBT_init_handle_encode_func(ldac_info->hLdacBt, 206 | (int) ldac_info->mtu, 207 | ldac_info->eqmid, 208 | ldac_info->channel_mode, 209 | ldac_info->pcm_fmt, 210 | ldac_info->pcm_frequency); 211 | if (ret != 0) { 212 | pa_log_warn("Failed to init ldacBT handle"); 213 | return 0; 214 | } 215 | } 216 | 217 | if (!ldac_info->buf_index && ldac_info->hLdacAbr && ldac_info->enable_abr && 218 | ldac_info->read_bytes >= ldac_info->interval_bytes) { 219 | ldac_ABR_Proc_func(ldac_info->hLdacBt, ldac_info->hLdacAbr, 220 | (unsigned int) (ldac_info->tx_length / ldac_info->q_write_block_size), 221 | (unsigned int) ldac_info->enable_abr); 222 | ldac_info->tx_length = 0; 223 | ldac_info->read_bytes = 0; 224 | } 225 | 226 | ldac_info->buf = write_buf; 227 | 228 | 229 | ldac_enc_read = (pa_frame_size(&ldac_info->sample_spec) * LDACBT_ENC_LSU); 230 | 231 | header = write_buf; 232 | payload = (struct rtp_payload *) ((uint8_t *) write_buf + sizeof(*header)); 233 | 234 | frame_count = 0; 235 | 236 | to_encode = ldac_info->q_write_block_size; 237 | 238 | d = (uint8_t *) write_buf + sizeof(*header) + sizeof(*payload) + ldac_info->buf_index; 239 | to_write = write_buf_size - sizeof(*header) - sizeof(*payload) - ldac_info->buf_index; 240 | 241 | *_encoded = 0; 242 | while (PA_LIKELY(to_encode > 0 && to_write > 0 && frame_count == 0)) { 243 | int written; 244 | int encoded; 245 | int ldac_frame_num; 246 | int ret_code; 247 | ldac_info->read_pcm(&p, ldac_enc_read, read_cb_data); 248 | 249 | ret_code = ldacBT_encode_func(ldac_info->hLdacBt, (void *) p, &encoded, (uint8_t *) d, &written, &ldac_frame_num); 250 | 251 | ldac_info->read_buf_free(&p, read_cb_data); 252 | 253 | if (PA_UNLIKELY(ret_code < 0)) { 254 | int err; 255 | pa_log_error("LDAC encoding error, written:%d encoded:%d ldac_frame_num:%d", written, encoded, 256 | ldac_frame_num); 257 | err = ldacBT_get_error_code_func(ldac_info->hLdacBt); 258 | pa_log_error("LDACBT_API_ERR:%d LDACBT_HANDLE_ERR:%d LDACBT_BLOCK_ERR:%d", LDACBT_API_ERR(err), 259 | LDACBT_HANDLE_ERR(err), LDACBT_BLOCK_ERR(err)); 260 | *_encoded = 0; 261 | return 0; 262 | } 263 | 264 | pa_assert_fp(encoded == (int) ldac_enc_read); 265 | pa_assert_fp(written <= (int) to_write); 266 | 267 | *_encoded += encoded; 268 | to_encode -= encoded; 269 | 270 | d = (uint8_t *) d + written; 271 | ldac_info->buf_index += written; 272 | to_write -= written; 273 | 274 | frame_count += ldac_frame_num; 275 | 276 | } 277 | 278 | ldac_info->read_bytes += *_encoded; 279 | 280 | 281 | PA_ONCE_BEGIN 282 | { 283 | const int v = ldacBT_get_version_func(); 284 | pa_log_notice("Using LDAC library: version: %x.%02x.%02x", 285 | v >> 16, 286 | (v >> 8) & 0x0ff, 287 | v & 0x0ff 288 | ); 289 | } 290 | PA_ONCE_END; 291 | 292 | if(frame_count == 0) 293 | return -EINPROGRESS; 294 | ldac_info->buf_index = 0; 295 | 296 | /* write it to the fifo */ 297 | memset(write_buf, 0, sizeof(*header) + sizeof(*payload)); 298 | header->v = 2; 299 | header->pt = 96; 300 | header->sequence_number = htons(ldac_info->seq_num++); 301 | header->timestamp = htonl(timestamp); 302 | header->ssrc = htonl(1); 303 | payload->frame_count = frame_count; 304 | ldac_info->layer_specific += frame_count; 305 | 306 | nbytes = (uint8_t *) d - (uint8_t *) write_buf; 307 | 308 | ldac_info->written += nbytes - sizeof(*header) - sizeof(*payload); 309 | 310 | return nbytes; 311 | } 312 | 313 | static void 314 | pa_ldac_config_transport(pa_sample_spec default_sample_spec, const void *configuration, size_t configuration_size, 315 | pa_sample_spec *sample_spec, void **codec_data) { 316 | ldac_info_t *ldac_info = *codec_data; 317 | a2dp_ldac_t *config = (a2dp_ldac_t *) configuration; 318 | pa_sample_format_t fmt; 319 | pa_assert(ldac_info); 320 | pa_assert_se(configuration_size == sizeof(*config)); 321 | 322 | ldac_info->hLdacBt = NULL; 323 | ldac_info->hLdacAbr = NULL; 324 | 325 | if (ldac_info->force_pa_fmt == PA_SAMPLE_INVALID) 326 | fmt = default_sample_spec.format; 327 | else 328 | fmt = ldac_info->force_pa_fmt; 329 | 330 | switch (fmt) { 331 | case PA_SAMPLE_FLOAT32LE: 332 | case PA_SAMPLE_FLOAT32BE: 333 | ldac_info->pcm_fmt = LDACBT_SMPL_FMT_F32; 334 | sample_spec->format = PA_SAMPLE_FLOAT32LE; 335 | break; 336 | case PA_SAMPLE_S32LE: 337 | case PA_SAMPLE_S32BE: 338 | ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S32; 339 | sample_spec->format = PA_SAMPLE_S32LE; 340 | break; 341 | case PA_SAMPLE_S24LE: 342 | case PA_SAMPLE_S24BE: 343 | case PA_SAMPLE_S24_32LE: 344 | case PA_SAMPLE_S24_32BE: 345 | ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S24; 346 | sample_spec->format = PA_SAMPLE_S24LE; 347 | break; 348 | default: 349 | ldac_info->pcm_fmt = LDACBT_SMPL_FMT_S16; 350 | sample_spec->format = PA_SAMPLE_S16LE; 351 | } 352 | 353 | 354 | switch (config->frequency) { 355 | case LDACBT_SAMPLING_FREQ_044100: 356 | ldac_info->pcm_frequency = 44100U; 357 | sample_spec->rate = 44100U; 358 | break; 359 | case LDACBT_SAMPLING_FREQ_048000: 360 | ldac_info->pcm_frequency = 48000U; 361 | sample_spec->rate = 48000U; 362 | break; 363 | case LDACBT_SAMPLING_FREQ_088200: 364 | ldac_info->pcm_frequency = 88200U; 365 | sample_spec->rate = 88200U; 366 | break; 367 | case LDACBT_SAMPLING_FREQ_096000: 368 | ldac_info->pcm_frequency = 96000U; 369 | sample_spec->rate = 96000U; 370 | break; 371 | case LDACBT_SAMPLING_FREQ_176400: 372 | ldac_info->pcm_frequency = 176400U; 373 | sample_spec->rate = 176400U; 374 | break; 375 | case LDACBT_SAMPLING_FREQ_192000: 376 | ldac_info->pcm_frequency = 192000U; 377 | sample_spec->rate = 192000U; 378 | break; 379 | default: 380 | pa_assert_not_reached(); 381 | } 382 | 383 | switch (config->channel_mode) { 384 | case LDACBT_CHANNEL_MODE_MONO: 385 | ldac_info->channel_mode = LDACBT_CHANNEL_MODE_MONO; 386 | sample_spec->channels = 1; 387 | break; 388 | case LDACBT_CHANNEL_MODE_DUAL_CHANNEL: 389 | ldac_info->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; 390 | sample_spec->channels = 2; 391 | break; 392 | case LDACBT_CHANNEL_MODE_STEREO: 393 | ldac_info->channel_mode = LDACBT_CHANNEL_MODE_STEREO; 394 | sample_spec->channels = 2; 395 | break; 396 | default: 397 | pa_assert_not_reached(); 398 | } 399 | 400 | switch (ldac_info->pcm_frequency) { 401 | case 44100: 402 | case 48000: 403 | ldac_info->pcm_lsu = 128; 404 | break; 405 | case 88200: 406 | case 96000: 407 | ldac_info->pcm_lsu = 256; 408 | break; 409 | case 176400: 410 | case 192000: 411 | ldac_info->pcm_lsu = 512; 412 | break; 413 | default: 414 | pa_assert_not_reached(); 415 | } 416 | 417 | switch (ldac_info->eqmid) { 418 | case LDACBT_EQMID_HQ: 419 | ldac_info->ldac_frame_size = 330; 420 | break; 421 | case LDACBT_EQMID_SQ: 422 | ldac_info->ldac_frame_size = 220; 423 | break; 424 | case LDACBT_EQMID_MQ: 425 | ldac_info->ldac_frame_size = 110; 426 | break; 427 | default: 428 | pa_assert_not_reached(); 429 | } 430 | 431 | ldac_info->sample_spec = *sample_spec; 432 | ldac_info->pcm_read_size = (ldac_info->pcm_lsu * pa_frame_size(&ldac_info->sample_spec)); 433 | ldac_info->interval_bytes = pa_usec_to_bytes(LDAC_ABR_INTERVAL_MS * 1000, &ldac_info->sample_spec); 434 | 435 | }; 436 | 437 | static size_t pa_ldac_handle_update_buffer_size(void **codec_data) { 438 | ldac_info_t *ldac_info = *codec_data; 439 | pa_assert(ldac_info); 440 | return ldac_info->q_write_block_size * ldac_info->abr_t3; 441 | } 442 | 443 | static void pa_ldac_get_block_size(size_t write_link_mtu, size_t *write_block_size, void **codec_data) { 444 | ldac_info_t *ldac_info = *codec_data; 445 | pa_assert(ldac_info); 446 | 447 | ldac_info->mtu = write_link_mtu; 448 | 449 | ldac_info->q_write_block_size = ((write_link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) 450 | / ldac_info->ldac_frame_size * ldac_info->pcm_read_size); 451 | *write_block_size = ldac_info->q_write_block_size; 452 | }; 453 | 454 | 455 | static void pa_ldac_setup_stream(void **codec_data) { 456 | int ret; 457 | ldac_info_t *ldac_info = *codec_data; 458 | pa_assert(ldac_info); 459 | 460 | ldac_info->layer_specific = 0; 461 | ldac_info->written = 0; 462 | ldac_info->buf = NULL; 463 | ldac_info->buf_index = 0; 464 | if (ldac_info->hLdacBt) 465 | ldacBT_free_handle_func(ldac_info->hLdacBt); 466 | ldac_info->hLdacBt = ldacBT_get_handle_func(); 467 | 468 | 469 | ret = ldacBT_init_handle_encode_func(ldac_info->hLdacBt, 470 | (int) ldac_info->mtu, 471 | ldac_info->eqmid, 472 | ldac_info->channel_mode, 473 | ldac_info->pcm_fmt, 474 | ldac_info->pcm_frequency); 475 | if (ret != 0) { 476 | pa_log_warn("Failed to init ldacBT handle"); 477 | goto fail; 478 | } 479 | 480 | if (!is_ldac_abr_loaded()) 481 | return; 482 | 483 | if (ldac_info->hLdacAbr) 484 | ldac_ABR_free_handle_func(ldac_info->hLdacAbr); 485 | ldac_info->hLdacAbr = ldac_ABR_get_handle_func(); 486 | 487 | ret = ldac_ABR_Init_func(ldac_info->hLdacAbr, LDAC_ABR_INTERVAL_MS); 488 | if (ret != 0) { 489 | pa_log_warn("Failed to init ldacBT_ABR handle"); 490 | goto fail1; 491 | } 492 | 493 | ldac_ABR_set_thresholds_func(ldac_info->hLdacAbr, ldac_info->abr_t3, ldac_info->abr_t2, ldac_info->abr_t1); 494 | return; 495 | 496 | fail: 497 | ldacBT_free_handle_func(ldac_info->hLdacBt); 498 | ldac_info->hLdacBt = NULL; 499 | if (!is_ldac_abr_loaded()) 500 | return; 501 | fail1: 502 | ldac_ABR_free_handle_func(ldac_info->hLdacAbr); 503 | ldac_info->hLdacAbr = NULL; 504 | ldac_info->enable_abr = false; 505 | }; 506 | 507 | static size_t pa_ldac_handle_skipping(size_t bytes_to_send, void **codec_data) { 508 | ldac_info_t *info = *codec_data; 509 | size_t skip_bytes; 510 | pa_assert(info); 511 | skip_bytes = pa_frame_align(bytes_to_send - ((bytes_to_send / 2) % info->q_write_block_size), 512 | &info->sample_spec); 513 | if(!info->enable_abr){ 514 | if(bytes_to_send > 2 * info->q_write_block_size) 515 | return skip_bytes; 516 | } else if (bytes_to_send / info->q_write_block_size > info->abr_t3) 517 | return skip_bytes; 518 | return 0; 519 | } 520 | 521 | static void pa_ldac_set_tx_length(size_t len, void **codec_data) { 522 | ldac_info_t *ldac_info = *codec_data; 523 | pa_assert(ldac_info); 524 | ldac_info->tx_length += PA_MAX(ldac_info->tx_length, len); 525 | }; 526 | 527 | static void pa_ldac_free(void **codec_data) { 528 | ldac_info_t *ldac_info = *codec_data; 529 | if (!ldac_info) 530 | return; 531 | 532 | if (ldac_info->hLdacBt) 533 | ldacBT_free_handle_func(ldac_info->hLdacBt); 534 | 535 | if (ldac_info->hLdacAbr && is_ldac_abr_loaded()) 536 | ldac_ABR_free_handle_func(ldac_info->hLdacAbr); 537 | 538 | pa_xfree(ldac_info); 539 | *codec_data = NULL; 540 | 541 | }; 542 | 543 | static size_t pa_ldac_get_capabilities(void **_capabilities) { 544 | a2dp_ldac_t *capabilities = pa_xmalloc0(sizeof(a2dp_ldac_t)); 545 | 546 | capabilities->info = A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID); 547 | capabilities->frequency = LDACBT_SAMPLING_FREQ_044100 | LDACBT_SAMPLING_FREQ_048000 | 548 | LDACBT_SAMPLING_FREQ_088200 | LDACBT_SAMPLING_FREQ_096000; 549 | capabilities->channel_mode = LDACBT_CHANNEL_MODE_MONO | LDACBT_CHANNEL_MODE_DUAL_CHANNEL | 550 | LDACBT_CHANNEL_MODE_STEREO; 551 | *_capabilities = capabilities; 552 | 553 | return sizeof(*capabilities); 554 | }; 555 | 556 | static size_t 557 | pa_ldac_select_configuration(const pa_sample_spec default_sample_spec, const uint8_t *supported_capabilities, 558 | const size_t capabilities_size, void **configuration) { 559 | a2dp_ldac_t *cap = (a2dp_ldac_t *) supported_capabilities; 560 | a2dp_ldac_t *config = pa_xmalloc0(sizeof(a2dp_ldac_t)); 561 | pa_a2dp_freq_cap_t ldac_freq_cap, ldac_freq_table[] = { 562 | {44100U, LDACBT_SAMPLING_FREQ_044100}, 563 | {48000U, LDACBT_SAMPLING_FREQ_048000}, 564 | {88200U, LDACBT_SAMPLING_FREQ_088200}, 565 | {96000U, LDACBT_SAMPLING_FREQ_096000} 566 | }; 567 | 568 | if (capabilities_size != sizeof(a2dp_ldac_t)) 569 | return 0; 570 | 571 | config->info = A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID); 572 | 573 | if (!pa_a2dp_select_cap_frequency(cap->frequency, default_sample_spec, ldac_freq_table, 574 | PA_ELEMENTSOF(ldac_freq_table), &ldac_freq_cap)) 575 | return 0; 576 | 577 | config->frequency = (uint8_t) ldac_freq_cap.cap; 578 | 579 | if (default_sample_spec.channels <= 1) { 580 | if (cap->channel_mode & LDACBT_CHANNEL_MODE_MONO) 581 | config->channel_mode = LDACBT_CHANNEL_MODE_MONO; 582 | else if (cap->channel_mode & LDACBT_CHANNEL_MODE_STEREO) 583 | config->channel_mode = LDACBT_CHANNEL_MODE_STEREO; 584 | else if (cap->channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL) 585 | config->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; 586 | else { 587 | pa_log_error("No supported channel modes"); 588 | return 0; 589 | } 590 | } 591 | 592 | if (default_sample_spec.channels >= 2) { 593 | if (cap->channel_mode & LDACBT_CHANNEL_MODE_STEREO) 594 | config->channel_mode = LDACBT_CHANNEL_MODE_STEREO; 595 | else if (cap->channel_mode & LDACBT_CHANNEL_MODE_DUAL_CHANNEL) 596 | config->channel_mode = LDACBT_CHANNEL_MODE_DUAL_CHANNEL; 597 | else if (cap->channel_mode & LDACBT_CHANNEL_MODE_MONO) 598 | config->channel_mode = LDACBT_CHANNEL_MODE_MONO; 599 | else { 600 | pa_log_error("No supported channel modes"); 601 | return 0; 602 | } 603 | } 604 | *configuration = config; 605 | return sizeof(*config); 606 | }; 607 | 608 | static void pa_ldac_free_capabilities(void **capabilities) { 609 | if (!capabilities || !*capabilities) 610 | return; 611 | pa_xfree(*capabilities); 612 | *capabilities = NULL; 613 | } 614 | 615 | static bool pa_ldac_validate_configuration(const uint8_t *selected_configuration, const size_t configuration_size) { 616 | a2dp_ldac_t *c = (a2dp_ldac_t *) selected_configuration; 617 | 618 | if (configuration_size != sizeof(a2dp_ldac_t)) { 619 | pa_log_error("LDAC configuration array of invalid size"); 620 | return false; 621 | } 622 | 623 | switch (c->frequency) { 624 | case LDACBT_SAMPLING_FREQ_044100: 625 | case LDACBT_SAMPLING_FREQ_048000: 626 | case LDACBT_SAMPLING_FREQ_088200: 627 | case LDACBT_SAMPLING_FREQ_096000: 628 | case LDACBT_SAMPLING_FREQ_176400: 629 | case LDACBT_SAMPLING_FREQ_192000: 630 | break; 631 | default: 632 | pa_log_error("Invalid sampling frequency in LDAC configuration"); 633 | return false; 634 | } 635 | 636 | switch (c->channel_mode) { 637 | case LDACBT_CHANNEL_MODE_STEREO: 638 | case LDACBT_CHANNEL_MODE_DUAL_CHANNEL: 639 | case LDACBT_CHANNEL_MODE_MONO: 640 | break; 641 | default: 642 | pa_log_error("Invalid channel mode in LDAC Configuration"); 643 | return false; 644 | } 645 | 646 | return true; 647 | }; 648 | 649 | 650 | static pa_a2dp_source_t pa_ldac_source = { 651 | .encoder_load = pa_ldac_encoder_load, 652 | .init = pa_ldac_encoder_init, 653 | .update_user_config = pa_ldac_update_user_config, 654 | .encode = pa_ldac_encode, 655 | .config_transport = pa_ldac_config_transport, 656 | .handle_update_buffer_size = pa_ldac_handle_update_buffer_size, 657 | .get_block_size = pa_ldac_get_block_size, 658 | .setup_stream = pa_ldac_setup_stream, 659 | .handle_skipping = pa_ldac_handle_skipping, 660 | .set_tx_length = pa_ldac_set_tx_length, 661 | .decrease_quality = NULL, 662 | .free = pa_ldac_free 663 | }; 664 | 665 | const pa_a2dp_codec_t pa_a2dp_ldac = { 666 | .name = "LDAC", 667 | .codec = A2DP_CODEC_VENDOR, 668 | .vendor_codec = &A2DP_SET_VENDOR_ID_CODEC_ID(LDAC_VENDOR_ID, LDAC_CODEC_ID), 669 | .a2dp_sink = NULL, 670 | .a2dp_source = &pa_ldac_source, 671 | .get_capabilities = pa_ldac_get_capabilities, 672 | .select_configuration = pa_ldac_select_configuration, 673 | .free_capabilities = pa_ldac_free_capabilities, 674 | .free_configuration = pa_ldac_free_capabilities, 675 | .validate_configuration = pa_ldac_validate_configuration 676 | }; 677 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/a2dp_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "a2dp-api.h" 25 | 26 | #define streq(a, b) (!strcmp((a),(b))) 27 | 28 | #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource" 29 | #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink" 30 | 31 | #define A2DP_SBC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/SBC" 32 | #define A2DP_SBC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/SBC" 33 | 34 | #define A2DP_AAC_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/AAC" 35 | #define A2DP_AAC_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/AAC" 36 | 37 | #define A2DP_VENDOR_SRC_ENDPOINT A2DP_SOURCE_ENDPOINT "/VENDOR" 38 | #define A2DP_VENDOR_SNK_ENDPOINT A2DP_SINK_ENDPOINT "/VENDOR" 39 | 40 | #define A2DP_APTX_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/APTX" 41 | #define A2DP_APTX_SNK_ENDPOINT A2DP_VENDOR_SNK_ENDPOINT "/APTX" 42 | 43 | #define A2DP_APTX_HD_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/APTXHD" 44 | #define A2DP_APTX_HD_SNK_ENDPOINT A2DP_VENDOR_SNK_ENDPOINT "/APTXHD" 45 | 46 | #define A2DP_LDAC_SRC_ENDPOINT A2DP_VENDOR_SRC_ENDPOINT "/LDAC" 47 | 48 | #define PA_A2DP_PRIORITY_DISABLE 0 49 | #define PA_A2DP_PRIORITY_MIN 1 50 | 51 | 52 | struct pa_a2dp_config { 53 | int max_priority; 54 | pa_hashmap *a2dp_sinks; 55 | pa_hashmap *a2dp_sources; 56 | pa_hashmap *active_index_priorities; 57 | pa_hashmap *ordered_indices; 58 | }; 59 | 60 | static unsigned int_hash_func(const void *p) { 61 | return (unsigned) *((const int *) p); 62 | } 63 | 64 | static int int_compare_func(const void *a, const void *b) { 65 | const int x = *((const int *) a); 66 | const int y = *((const int *) b); 67 | return x < y ? -1 : (x > y ? 1 : 0); 68 | }; 69 | 70 | 71 | void pa_a2dp_init(pa_a2dp_config_t **a2dp_config) { 72 | pa_a2dp_config_t *config; 73 | pa_a2dp_codec_index_t codec_index = PA_A2DP_SINK_MIN; 74 | const pa_a2dp_codec_t *a2dp_codec; 75 | 76 | config = pa_xmalloc(sizeof(pa_a2dp_config_t)); 77 | *a2dp_config = config; 78 | 79 | config->a2dp_sinks = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); 80 | config->a2dp_sources = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); 81 | config->active_index_priorities = pa_hashmap_new_full(int_hash_func, int_compare_func, 82 | pa_xfree, pa_xfree); 83 | config->ordered_indices = NULL; 84 | 85 | config->max_priority = PA_A2DP_PRIORITY_MIN - 1; 86 | while (++codec_index < PA_A2DP_SINK_MAX) { 87 | pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); 88 | if (!a2dp_codec || !a2dp_codec->a2dp_sink || !a2dp_codec->a2dp_sink->decoder_load()) 89 | continue; 90 | ++config->max_priority; 91 | pa_hashmap_put(config->a2dp_sinks, pa_xmemdup(&config->max_priority, sizeof(int)), 92 | pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t))); 93 | pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), 94 | pa_xmemdup(&config->max_priority, sizeof(int))); 95 | a2dp_codec->a2dp_sink->priority = config->max_priority; 96 | } 97 | while (++codec_index < PA_A2DP_SOURCE_MAX) { 98 | pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); 99 | if (!a2dp_codec || !a2dp_codec->a2dp_source || !a2dp_codec->a2dp_source->encoder_load()) 100 | continue; 101 | ++config->max_priority; 102 | pa_hashmap_put(config->a2dp_sources, pa_xmemdup(&config->max_priority, sizeof(int)), 103 | pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t))); 104 | pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), 105 | pa_xmemdup(&config->max_priority, sizeof(int))); 106 | a2dp_codec->a2dp_source->priority = config->max_priority; 107 | } 108 | }; 109 | 110 | void pa_a2dp_set_max_priority(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config) { 111 | const pa_a2dp_codec_t *a2dp_codec; 112 | pa_a2dp_config_t *config = *a2dp_config; 113 | 114 | pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); 115 | 116 | if (!a2dp_codec || !pa_hashmap_remove(config->active_index_priorities, &codec_index)) { 117 | printf("no entry;"); 118 | pa_log_debug("No such codec: %d", codec_index); 119 | return; 120 | } 121 | 122 | ++config->max_priority; 123 | pa_hashmap_put(config->active_index_priorities, pa_xmemdup(&codec_index, sizeof(pa_a2dp_codec_index_t)), 124 | pa_xmemdup(&config->max_priority, sizeof(int))); 125 | 126 | if (pa_a2dp_codec_index_is_sink(codec_index)) 127 | a2dp_codec->a2dp_sink->priority = config->max_priority; 128 | else 129 | a2dp_codec->a2dp_source->priority = config->max_priority; 130 | }; 131 | 132 | void pa_a2dp_set_disable(pa_a2dp_codec_index_t codec_index, pa_a2dp_config_t **a2dp_config) { 133 | const pa_a2dp_codec_t *a2dp_codec; 134 | pa_a2dp_config_t *config = *a2dp_config; 135 | pa_a2dp_codec_index_to_a2dp_codec(codec_index, &a2dp_codec); 136 | 137 | if (!a2dp_codec || !pa_hashmap_remove(config->active_index_priorities, &codec_index)) { 138 | pa_log_debug("No such codec: %d", codec_index); 139 | return; 140 | } 141 | 142 | if (pa_a2dp_codec_index_is_sink(codec_index)) 143 | a2dp_codec->a2dp_sink->priority = PA_A2DP_PRIORITY_DISABLE; 144 | else 145 | a2dp_codec->a2dp_source->priority = PA_A2DP_PRIORITY_DISABLE; 146 | }; 147 | 148 | void pa_a2dp_free(pa_a2dp_config_t **a2dp_config) { 149 | pa_a2dp_config_t *config = *a2dp_config; 150 | 151 | if (!config) 152 | return; 153 | if (config->ordered_indices) 154 | pa_hashmap_free(config->ordered_indices); 155 | 156 | if (config->active_index_priorities) 157 | pa_hashmap_free(config->active_index_priorities); 158 | 159 | if (config->a2dp_sinks) 160 | pa_hashmap_free(config->a2dp_sinks); 161 | 162 | if (config->a2dp_sources) 163 | pa_hashmap_free(config->a2dp_sources); 164 | 165 | pa_xfree(config); 166 | *a2dp_config = NULL; 167 | } 168 | 169 | 170 | void pa_a2dp_get_sink_indices(pa_hashmap **sink_indices, pa_a2dp_config_t **a2dp_config) { 171 | pa_a2dp_config_t *config = *a2dp_config; 172 | *sink_indices = config->a2dp_sinks; 173 | }; 174 | 175 | void pa_a2dp_get_source_indices(pa_hashmap **source_indices, pa_a2dp_config_t **a2dp_config) { 176 | pa_a2dp_config_t *config = *a2dp_config; 177 | *source_indices = config->a2dp_sources; 178 | }; 179 | 180 | void pa_a2dp_get_ordered_indices(pa_hashmap **ordered_indices, pa_a2dp_config_t **a2dp_config) { 181 | void *state; 182 | pa_a2dp_codec_index_t *index, *indices; 183 | int *priority, i; 184 | pa_a2dp_config_t *config = *a2dp_config; 185 | 186 | indices = pa_xmalloc(sizeof(pa_a2dp_codec_index_t) * (config->max_priority + 1)); 187 | 188 | for (i = 0; i <= config->max_priority; i++) 189 | indices[i] = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 190 | 191 | PA_HASHMAP_FOREACH_KV(index, priority, config->active_index_priorities, state) { 192 | if (*priority <= 0) 193 | continue; 194 | indices[*priority] = *index; 195 | } 196 | 197 | if (config->ordered_indices) 198 | pa_hashmap_free(config->ordered_indices); 199 | config->ordered_indices = pa_hashmap_new_full(int_hash_func, int_compare_func, pa_xfree, pa_xfree); 200 | 201 | for (i = config->max_priority; i >= PA_A2DP_PRIORITY_MIN; i--) { 202 | if (indices[i] == PA_A2DP_CODEC_INDEX_UNAVAILABLE) 203 | continue; 204 | priority = pa_xmemdup(&i, sizeof(int)); 205 | index = pa_xmemdup(indices + i, sizeof(pa_a2dp_codec_index_t)); 206 | pa_hashmap_put(config->ordered_indices, priority, index); 207 | } 208 | 209 | *ordered_indices = config->ordered_indices; 210 | }; 211 | 212 | 213 | void pa_a2dp_codec_index_to_endpoint(pa_a2dp_codec_index_t codec_index, const char **endpoint) { 214 | if(codec_index == PA_A2DP_CODEC_INDEX_UNAVAILABLE) 215 | *endpoint = NULL; 216 | else if(codec_index == PA_A2DP_SINK_SBC) 217 | *endpoint = A2DP_SBC_SNK_ENDPOINT; 218 | else if(codec_index == PA_A2DP_SOURCE_SBC) 219 | *endpoint = A2DP_SBC_SRC_ENDPOINT; 220 | else if(codec_index == PA_A2DP_SINK_AAC) 221 | *endpoint = A2DP_AAC_SNK_ENDPOINT; 222 | else if(codec_index == PA_A2DP_SOURCE_AAC) 223 | *endpoint = A2DP_AAC_SRC_ENDPOINT; 224 | else if(codec_index == PA_A2DP_SINK_APTX) 225 | *endpoint = A2DP_APTX_SNK_ENDPOINT; 226 | else if(codec_index == PA_A2DP_SOURCE_APTX) 227 | *endpoint = A2DP_APTX_SRC_ENDPOINT; 228 | else if(codec_index == PA_A2DP_SINK_APTX_HD) 229 | *endpoint = A2DP_APTX_HD_SNK_ENDPOINT; 230 | else if(codec_index == PA_A2DP_SOURCE_APTX_HD) 231 | *endpoint = A2DP_APTX_HD_SRC_ENDPOINT; 232 | else if(codec_index == PA_A2DP_SOURCE_LDAC) 233 | *endpoint = A2DP_LDAC_SRC_ENDPOINT; 234 | else 235 | pa_assert_not_reached(); 236 | }; 237 | 238 | void pa_a2dp_endpoint_to_codec_index(const char *endpoint, pa_a2dp_codec_index_t *codec_index) { 239 | if (streq(endpoint, A2DP_SBC_SNK_ENDPOINT)) 240 | *codec_index = PA_A2DP_SINK_SBC; 241 | else if (streq(endpoint, A2DP_SBC_SRC_ENDPOINT)) 242 | *codec_index = PA_A2DP_SOURCE_SBC; 243 | else if (streq(endpoint, A2DP_AAC_SNK_ENDPOINT)) 244 | *codec_index = PA_A2DP_SINK_AAC; 245 | else if (streq(endpoint, A2DP_AAC_SRC_ENDPOINT)) 246 | *codec_index = PA_A2DP_SOURCE_AAC; 247 | else if (streq(endpoint, A2DP_APTX_SNK_ENDPOINT)) 248 | *codec_index = PA_A2DP_SINK_APTX; 249 | else if (streq(endpoint, A2DP_APTX_SRC_ENDPOINT)) 250 | *codec_index = PA_A2DP_SOURCE_APTX; 251 | else if (streq(endpoint, A2DP_APTX_HD_SNK_ENDPOINT)) 252 | *codec_index = PA_A2DP_SINK_APTX_HD; 253 | else if (streq(endpoint, A2DP_APTX_HD_SRC_ENDPOINT)) 254 | *codec_index = PA_A2DP_SOURCE_APTX_HD; 255 | else if (streq(endpoint, A2DP_LDAC_SRC_ENDPOINT)) 256 | *codec_index = PA_A2DP_SOURCE_LDAC; 257 | else 258 | *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 259 | }; 260 | 261 | void pa_a2dp_codec_index_to_a2dp_codec(pa_a2dp_codec_index_t codec_index, const pa_a2dp_codec_t **a2dp_codec) { 262 | if(codec_index == PA_A2DP_CODEC_INDEX_UNAVAILABLE) 263 | *a2dp_codec = NULL; 264 | else if(codec_index == PA_A2DP_SINK_SBC || codec_index == PA_A2DP_SOURCE_SBC) 265 | *a2dp_codec = PTR_PA_A2DP_SBC; 266 | else if(codec_index == PA_A2DP_SINK_AAC || codec_index == PA_A2DP_SOURCE_AAC) 267 | *a2dp_codec = PTR_PA_A2DP_AAC; 268 | else if(codec_index == PA_A2DP_SINK_APTX || codec_index == PA_A2DP_SOURCE_APTX) 269 | *a2dp_codec = PTR_PA_A2DP_APTX; 270 | else if(codec_index == PA_A2DP_SINK_APTX_HD || codec_index == PA_A2DP_SOURCE_APTX_HD) 271 | *a2dp_codec = PTR_PA_A2DP_APTX_HD; 272 | else if(codec_index == PA_A2DP_SOURCE_LDAC) 273 | *a2dp_codec = PTR_PA_A2DP_LDAC; 274 | else 275 | *a2dp_codec = NULL; 276 | }; 277 | 278 | void pa_a2dp_a2dp_codec_to_codec_index(const pa_a2dp_codec_t *a2dp_codec, bool is_a2dp_sink, 279 | pa_a2dp_codec_index_t *codec_index) { 280 | if (!a2dp_codec) { 281 | *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 282 | return; 283 | } 284 | switch (a2dp_codec->codec) { 285 | case A2DP_CODEC_SBC: 286 | *codec_index = is_a2dp_sink ? PA_A2DP_SINK_SBC : PA_A2DP_SOURCE_SBC; 287 | return; 288 | case A2DP_CODEC_MPEG24: 289 | *codec_index = is_a2dp_sink ? PA_A2DP_SINK_AAC : PA_A2DP_SOURCE_AAC; 290 | return; 291 | case A2DP_CODEC_VENDOR: 292 | if (!a2dp_codec->vendor_codec) { 293 | *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 294 | return; 295 | } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == APTX_VENDOR_ID && 296 | A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == APTX_CODEC_ID) { 297 | *codec_index = is_a2dp_sink ? PA_A2DP_SINK_APTX : PA_A2DP_SOURCE_APTX; 298 | return; 299 | } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == APTX_HD_VENDOR_ID && 300 | A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == APTX_HD_CODEC_ID) { 301 | *codec_index = is_a2dp_sink ? PA_A2DP_SINK_APTX_HD : PA_A2DP_SOURCE_APTX_HD; 302 | return; 303 | } else if (A2DP_GET_VENDOR_ID(*a2dp_codec->vendor_codec) == LDAC_VENDOR_ID && 304 | A2DP_GET_CODEC_ID(*a2dp_codec->vendor_codec) == LDAC_CODEC_ID) { 305 | *codec_index = is_a2dp_sink ? PA_A2DP_CODEC_INDEX_UNAVAILABLE : PA_A2DP_SOURCE_LDAC; 306 | return; 307 | } 308 | *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 309 | break; 310 | default: 311 | *codec_index = PA_A2DP_CODEC_INDEX_UNAVAILABLE; 312 | } 313 | }; 314 | 315 | void 316 | pa_a2dp_get_a2dp_codec(uint8_t codec, const a2dp_vendor_codec_t *vendor_codec, const pa_a2dp_codec_t **a2dp_codec) { 317 | switch (codec) { 318 | case A2DP_CODEC_SBC: 319 | *a2dp_codec = PTR_PA_A2DP_SBC; 320 | return; 321 | case A2DP_CODEC_MPEG24: 322 | *a2dp_codec = PTR_PA_A2DP_AAC; 323 | return; 324 | case A2DP_CODEC_VENDOR: 325 | if (!vendor_codec) { 326 | *a2dp_codec = NULL; 327 | pa_assert_not_reached(); 328 | } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == APTX_VENDOR_ID && 329 | A2DP_GET_CODEC_ID(*vendor_codec) == APTX_CODEC_ID) { 330 | *a2dp_codec = PTR_PA_A2DP_APTX; 331 | return; 332 | } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == APTX_HD_VENDOR_ID && 333 | A2DP_GET_CODEC_ID(*vendor_codec) == APTX_HD_CODEC_ID) { 334 | *a2dp_codec = PTR_PA_A2DP_APTX_HD; 335 | return; 336 | } else if (A2DP_GET_VENDOR_ID(*vendor_codec) == LDAC_VENDOR_ID && 337 | A2DP_GET_CODEC_ID(*vendor_codec) == LDAC_CODEC_ID) { 338 | *a2dp_codec = PTR_PA_A2DP_LDAC; 339 | return; 340 | } 341 | *a2dp_codec = NULL; 342 | break; 343 | default: 344 | *a2dp_codec = NULL; 345 | } 346 | }; 347 | 348 | bool pa_a2dp_codec_index_is_sink(pa_a2dp_codec_index_t codec_index) { 349 | if (codec_index > PA_A2DP_SINK_MIN && codec_index < PA_A2DP_SINK_MAX) 350 | return true; 351 | return false; 352 | }; 353 | 354 | bool pa_a2dp_codec_index_is_source(pa_a2dp_codec_index_t codec_index) { 355 | if (codec_index > PA_A2DP_SOURCE_MIN && codec_index < PA_A2DP_SOURCE_MAX) 356 | return true; 357 | return false; 358 | }; 359 | 360 | bool 361 | pa_a2dp_select_cap_frequency(uint32_t freq_cap, pa_sample_spec default_sample_spec, 362 | const pa_a2dp_freq_cap_t *freq_cap_table, 363 | size_t n, pa_a2dp_freq_cap_t *result) { 364 | int i; 365 | /* Find the lowest freq that is at least as high as the requested sampling rate */ 366 | for (i = 0; (unsigned) i < n; i++) 367 | if (freq_cap_table[i].rate >= default_sample_spec.rate && (freq_cap & freq_cap_table[i].cap)) { 368 | *result = freq_cap_table[i]; 369 | break; 370 | } 371 | 372 | if ((unsigned) i == n) { 373 | for (--i; i >= 0; i--) { 374 | if (freq_cap & freq_cap_table[i].cap) { 375 | *result = freq_cap_table[i]; 376 | break; 377 | } 378 | } 379 | 380 | if (i < 0) { 381 | pa_log_error("Not suitable sample rate"); 382 | return false; 383 | } 384 | } 385 | pa_assert((unsigned) i < n); 386 | return true; 387 | }; 388 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/ffmpeg_libs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #ifdef HAVE_CONFIG_H 28 | 29 | #include 30 | 31 | #endif 32 | 33 | #include 34 | 35 | #include "ffmpeg_libs.h" 36 | 37 | static const char *AVCODEC_LIB_NAMES[] = { 38 | "libavcodec.so.58", 39 | "libavcodec.so" 40 | }; 41 | 42 | static const char *avcodec_find_decoder_func_name = "avcodec_find_decoder"; 43 | static const char *avcodec_find_encoder_func_name = "avcodec_find_encoder"; 44 | static const char *av_packet_alloc_func_name = "av_packet_alloc"; 45 | static const char *av_packet_free_func_name = "av_packet_free"; 46 | static const char *avcodec_send_packet_func_name = "avcodec_send_packet"; 47 | static const char *avcodec_receive_frame_func_name = "avcodec_receive_frame"; 48 | static const char *avcodec_send_frame_func_name = "avcodec_send_frame"; 49 | static const char *avcodec_receive_packet_func_name = "avcodec_receive_packet"; 50 | static const char *avcodec_flush_buffers_func_name = "avcodec_flush_buffers"; 51 | static const char *avcodec_alloc_context3_func_name = "avcodec_alloc_context3"; 52 | static const char *avcodec_free_context_func_name = "avcodec_free_context"; 53 | static const char *avcodec_open2_func_name = "avcodec_open2"; 54 | 55 | avcodec_find_decoder_func_t avcodec_find_decoder_func; 56 | avcodec_find_encoder_func_t avcodec_find_encoder_func; 57 | av_packet_alloc_func_t av_packet_alloc_func; 58 | av_packet_free_func_t av_packet_free_func; 59 | avcodec_send_packet_func_t avcodec_send_packet_func; 60 | avcodec_receive_frame_func_t avcodec_receive_frame_func; 61 | avcodec_send_frame_func_t avcodec_send_frame_func; 62 | avcodec_receive_packet_func_t avcodec_receive_packet_func; 63 | avcodec_flush_buffers_func_t avcodec_flush_buffers_func; 64 | avcodec_alloc_context3_func_t avcodec_alloc_context3_func; 65 | avcodec_free_context_func_t avcodec_free_context_func; 66 | avcodec_open2_func_t avcodec_open2_func; 67 | 68 | static const char *AVUTIL_LIB_NAMES[] = { 69 | "libavutil.so.56", 70 | "libavutil.so" 71 | }; 72 | 73 | static const char *av_frame_alloc_func_name = "av_frame_alloc"; 74 | static const char *av_frame_get_buffer_func_name = "av_frame_get_buffer"; 75 | static const char *av_frame_make_writable_func_name = "av_frame_make_writable"; 76 | static const char *av_frame_free_func_name = "av_frame_free"; 77 | 78 | av_frame_alloc_func_t av_frame_alloc_func; 79 | av_frame_get_buffer_func_t av_frame_get_buffer_func; 80 | av_frame_make_writable_func_t av_frame_make_writable_func; 81 | av_frame_free_func_t av_frame_free_func; 82 | 83 | static void *libavcodec_h = NULL; 84 | 85 | static void *libavutil_h = NULL; 86 | 87 | 88 | static void *load_func(void *lib_handle, const char *func_name) { 89 | void *func = dlsym(lib_handle, func_name); 90 | if (func == NULL) { 91 | pa_log_error("No function %s in provide library. %s", func_name, dlerror()); 92 | return NULL; 93 | } 94 | return func; 95 | } 96 | 97 | static void libavcodec_unload() { 98 | avcodec_find_decoder_func = NULL; 99 | avcodec_find_encoder_func = NULL; 100 | av_packet_alloc_func = NULL; 101 | av_packet_free_func = NULL; 102 | avcodec_send_packet_func = NULL; 103 | avcodec_receive_frame_func = NULL; 104 | avcodec_send_frame_func = NULL; 105 | avcodec_receive_packet_func = NULL; 106 | avcodec_flush_buffers_func = NULL; 107 | avcodec_alloc_context3_func = NULL; 108 | avcodec_free_context_func = NULL; 109 | avcodec_open2_func = NULL; 110 | if (libavcodec_h) { 111 | dlclose(libavcodec_h); 112 | libavcodec_h = NULL; 113 | } 114 | } 115 | 116 | static void libavutil_unload() { 117 | av_frame_alloc_func = NULL; 118 | av_frame_get_buffer_func = NULL; 119 | av_frame_make_writable_func = NULL; 120 | av_frame_free_func = NULL; 121 | if (libavutil_h) { 122 | dlclose(libavutil_h); 123 | libavutil_h = NULL; 124 | } 125 | } 126 | 127 | static bool libavcodec_load() { 128 | if (libavcodec_h) 129 | return true; 130 | for (int i = 0; i < PA_ELEMENTSOF(AVCODEC_LIB_NAMES); ++i) { 131 | libavutil_unload(); 132 | libavcodec_h = dlopen(AVCODEC_LIB_NAMES[i], RTLD_NOW); 133 | if (libavcodec_h == NULL) { 134 | pa_log_warn("Cannot open libavcodec library: %s. %s", AVCODEC_LIB_NAMES[i], dlerror()); 135 | continue; 136 | } 137 | avcodec_find_decoder_func = load_func(libavcodec_h, avcodec_find_decoder_func_name); 138 | if (avcodec_find_decoder_func == NULL) 139 | continue; 140 | avcodec_find_encoder_func = load_func(libavcodec_h, avcodec_find_encoder_func_name); 141 | if (avcodec_find_encoder_func == NULL) 142 | continue; 143 | av_packet_alloc_func = load_func(libavcodec_h, av_packet_alloc_func_name); 144 | if (av_packet_alloc_func == NULL) 145 | continue; 146 | av_packet_free_func = load_func(libavcodec_h, av_packet_free_func_name); 147 | if (av_packet_free_func == NULL) 148 | continue; 149 | avcodec_send_packet_func = load_func(libavcodec_h, avcodec_send_packet_func_name); 150 | if (avcodec_send_packet_func == NULL) 151 | continue; 152 | avcodec_receive_frame_func = load_func(libavcodec_h, avcodec_receive_frame_func_name); 153 | if (avcodec_receive_frame_func == NULL) 154 | continue; 155 | avcodec_send_frame_func = load_func(libavcodec_h, avcodec_send_frame_func_name); 156 | if (avcodec_send_frame_func == NULL) 157 | continue; 158 | avcodec_receive_packet_func = load_func(libavcodec_h, avcodec_receive_packet_func_name); 159 | if (avcodec_receive_packet_func == NULL) 160 | continue; 161 | avcodec_flush_buffers_func = load_func(libavcodec_h, avcodec_flush_buffers_func_name); 162 | if (avcodec_flush_buffers_func == NULL) 163 | continue; 164 | avcodec_alloc_context3_func = load_func(libavcodec_h, avcodec_alloc_context3_func_name); 165 | if (avcodec_alloc_context3_func == NULL) 166 | continue; 167 | avcodec_free_context_func = load_func(libavcodec_h, avcodec_free_context_func_name); 168 | if (avcodec_free_context_func == NULL) 169 | continue; 170 | avcodec_open2_func = load_func(libavcodec_h, avcodec_open2_func_name); 171 | if (avcodec_open2_func == NULL) 172 | continue; 173 | return true; 174 | } 175 | return false; 176 | } 177 | 178 | static bool libavutil_load() { 179 | if (libavutil_h) 180 | return true; 181 | for (int i = 0; i < PA_ELEMENTSOF(AVUTIL_LIB_NAMES); ++i) { 182 | libavutil_h = dlopen(AVUTIL_LIB_NAMES[i], RTLD_NOW); 183 | if (libavutil_h == NULL) { 184 | pa_log_warn("Cannot open libavutil library: %s. %s", AVUTIL_LIB_NAMES[i], dlerror()); 185 | continue; 186 | } 187 | av_frame_alloc_func = load_func(libavutil_h, av_frame_alloc_func_name); 188 | if (av_frame_alloc_func == NULL) 189 | continue; 190 | av_frame_get_buffer_func = load_func(libavutil_h, av_frame_get_buffer_func_name); 191 | if (av_frame_get_buffer_func == NULL) 192 | continue; 193 | av_frame_make_writable_func = load_func(libavutil_h, av_frame_make_writable_func_name); 194 | if (av_frame_make_writable_func == NULL) 195 | continue; 196 | av_frame_free_func = load_func(libavutil_h, av_frame_free_func_name); 197 | if (av_frame_free_func == NULL) 198 | continue; 199 | return true; 200 | } 201 | 202 | return false; 203 | } 204 | 205 | bool ffmpeg_libs_load() { 206 | if (libavcodec_load() && libavutil_load()) 207 | return true; 208 | libavcodec_unload(); 209 | libavutil_unload(); 210 | return false; 211 | } -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/ffmpeg_libs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef PULSEAUDIO_MODULES_BT_FFMPEG_H 22 | #define PULSEAUDIO_MODULES_BT_FFMPEG_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | typedef AVCodec *(*avcodec_find_decoder_func_t)(enum AVCodecID id); 29 | 30 | typedef AVCodec *(*avcodec_find_encoder_func_t)(enum AVCodecID id); 31 | 32 | typedef AVPacket *(*av_packet_alloc_func_t)(void); 33 | 34 | typedef void (*av_packet_free_func_t)(AVPacket **pkt); 35 | 36 | typedef int (*avcodec_send_packet_func_t)(AVCodecContext *avctx, const AVPacket *avpkt); 37 | 38 | typedef int (*avcodec_receive_frame_func_t)(AVCodecContext *avctx, AVFrame *frame); 39 | 40 | typedef int (*avcodec_send_frame_func_t)(AVCodecContext *avctx, const AVFrame *frame); 41 | 42 | typedef int (*avcodec_receive_packet_func_t)(AVCodecContext *avctx, AVPacket *avpkt); 43 | 44 | typedef void (*avcodec_flush_buffers_func_t)(AVCodecContext *avctx); 45 | 46 | typedef AVCodecContext *(*avcodec_alloc_context3_func_t)(const AVCodec *codec); 47 | 48 | typedef void (*avcodec_free_context_func_t)(AVCodecContext **avctx); 49 | 50 | typedef int (*avcodec_open2_func_t)(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); 51 | 52 | 53 | extern avcodec_find_decoder_func_t avcodec_find_decoder_func; 54 | extern avcodec_find_encoder_func_t avcodec_find_encoder_func; 55 | extern av_packet_alloc_func_t av_packet_alloc_func; 56 | extern av_packet_free_func_t av_packet_free_func; 57 | extern avcodec_send_packet_func_t avcodec_send_packet_func; 58 | extern avcodec_receive_frame_func_t avcodec_receive_frame_func; 59 | extern avcodec_send_frame_func_t avcodec_send_frame_func; 60 | extern avcodec_receive_packet_func_t avcodec_receive_packet_func; 61 | extern avcodec_flush_buffers_func_t avcodec_flush_buffers_func; 62 | extern avcodec_alloc_context3_func_t avcodec_alloc_context3_func; 63 | extern avcodec_free_context_func_t avcodec_free_context_func; 64 | extern avcodec_open2_func_t avcodec_open2_func; 65 | 66 | 67 | typedef AVFrame *(*av_frame_alloc_func_t)(void); 68 | 69 | 70 | typedef int (*av_frame_get_buffer_func_t)(AVFrame *frame, int align); 71 | 72 | typedef int (*av_frame_make_writable_func_t)(AVFrame *frame); 73 | 74 | typedef void (*av_frame_free_func_t)(AVFrame **frame); 75 | 76 | 77 | extern av_frame_alloc_func_t av_frame_alloc_func; 78 | extern av_frame_get_buffer_func_t av_frame_get_buffer_func; 79 | extern av_frame_make_writable_func_t av_frame_make_writable_func; 80 | extern av_frame_free_func_t av_frame_free_func; 81 | 82 | 83 | bool ffmpeg_libs_load(); 84 | 85 | #endif //PULSEAUDIO_MODULES_BT_FFMPEG_H 86 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/ldac_libs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2018-2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | #include 21 | #include 22 | #include 23 | 24 | #ifdef HAVE_CONFIG_H 25 | 26 | #include 27 | 28 | #endif 29 | 30 | #include 31 | 32 | #include "ldac_libs.h" 33 | 34 | static const char *LDAC_ENCODER_LIB_NAMES[] = { 35 | "libldacBT_enc.so.2", 36 | "libldacBT_enc.so" 37 | }; 38 | 39 | static const char *LDAC_GET_HANDLE_FUNC_NAME = "ldacBT_get_handle"; 40 | static const char *LDAC_FREE_HANDLE_FUNC_NAME = "ldacBT_free_handle"; 41 | static const char *LDAC_CLOSE_HANDLE_FUNC_NAME = "ldacBT_close_handle"; 42 | static const char *LDAC_GET_VERSION_FUNC_NAME = "ldacBT_get_version"; 43 | static const char *LDAC_GET_SAMPLING_FREQ_FUNC_NAME = "ldacBT_get_sampling_freq"; 44 | static const char *LDAC_GET_BITRATE_FUNC_NAME = "ldacBT_get_bitrate"; 45 | static const char *LDAC_INIT_HANDLE_ENCODE_FUNC_NAME = "ldacBT_init_handle_encode"; 46 | static const char *LDAC_SET_EQMID_FUNC_NAME = "ldacBT_set_eqmid"; 47 | static const char *LDAC_GET_EQMID_FUNC_NAME = "ldacBT_get_eqmid"; 48 | static const char *LDAC_ALTER_EQMID_PRIORITY_FUNC_NAME = "ldacBT_alter_eqmid_priority"; 49 | static const char *LDAC_ENCODE_FUNC_NAME = "ldacBT_encode"; 50 | static const char *LDAC_GET_ERROR_CODE_FUNC_NAME = "ldacBT_get_error_code"; 51 | 52 | 53 | static const char *LDAC_ABR_LIB_NAMES[] = { 54 | "libldacBT_abr.so.2", 55 | "libldacBT_abr.so" 56 | }; 57 | 58 | static const char *LDAC_ABR_GET_HANDLE_FUNC_NAME = "ldac_ABR_get_handle"; 59 | static const char *LDAC_ABR_FREE_HANDLE_FUNC_NAME = "ldac_ABR_free_handle"; 60 | static const char *LDAC_ABR_INIT_FUNC_NAME = "ldac_ABR_Init"; 61 | static const char *LDAC_ABR_SET_THRESHOLDS_FUNC_NAME = "ldac_ABR_set_thresholds"; 62 | static const char *LDAC_ABR_PROC_FUNC_NAME = "ldac_ABR_Proc"; 63 | 64 | 65 | ldacBT_get_handle_func_t ldacBT_get_handle_func; 66 | ldacBT_free_handle_func_t ldacBT_free_handle_func; 67 | ldacBT_close_handle_func_t ldacBT_close_handle_func; 68 | ldacBT_get_version_func_t ldacBT_get_version_func; 69 | ldacBT_get_sampling_freq_func_t ldacBT_get_sampling_freq_func; 70 | ldacBT_get_bitrate_func_t ldacBT_get_bitrate_func; 71 | ldacBT_init_handle_encode_func_t ldacBT_init_handle_encode_func; 72 | ldacBT_set_eqmid_func_t ldacBT_set_eqmid_func; 73 | ldacBT_get_eqmid_func_t ldacBT_get_eqmid_func; 74 | ldacBT_alter_eqmid_priority_func_t ldacBT_alter_eqmid_priority_func; 75 | ldacBT_encode_func_t ldacBT_encode_func; 76 | ldacBT_get_error_code_func_t ldacBT_get_error_code_func; 77 | 78 | 79 | ldac_ABR_get_handle_func_t ldac_ABR_get_handle_func; 80 | ldac_ABR_free_handle_func_t ldac_ABR_free_handle_func; 81 | ldac_ABR_Init_func_t ldac_ABR_Init_func; 82 | ldac_ABR_set_thresholds_func_t ldac_ABR_set_thresholds_func; 83 | ldac_ABR_Proc_func_t ldac_ABR_Proc_func; 84 | 85 | static void *ldac_encoder_lib_h = NULL; 86 | static void *ldac_abr_lib_h = NULL; 87 | 88 | static bool ldac_abr_loaded = false; 89 | 90 | 91 | static void *load_func(void *lib_handle, const char *func_name) { 92 | void *func = dlsym(lib_handle, func_name); 93 | if (func == NULL) { 94 | pa_log_error("No function %s in provide library. %s", func_name, dlerror()); 95 | return NULL; 96 | } 97 | return func; 98 | } 99 | 100 | static void ldac_abr_unload() { 101 | if (ldac_abr_lib_h != NULL) { 102 | dlclose(ldac_abr_lib_h); 103 | ldac_abr_lib_h = NULL; 104 | } 105 | ldac_ABR_get_handle_func = NULL; 106 | ldac_ABR_free_handle_func = NULL; 107 | ldac_ABR_Init_func = NULL; 108 | ldac_ABR_set_thresholds_func = NULL; 109 | ldac_ABR_Proc_func = NULL; 110 | } 111 | 112 | static bool ldac_abr_load() { 113 | if (ldac_abr_lib_h) 114 | return true; 115 | for (int i = 0; i < PA_ELEMENTSOF(LDAC_ABR_LIB_NAMES); ++i) { 116 | ldac_abr_unload(); 117 | ldac_abr_lib_h = dlopen(LDAC_ABR_LIB_NAMES[i], RTLD_NOW); 118 | if (ldac_abr_lib_h == NULL) { 119 | pa_log_warn("Cannot open LDAC abr library: %s. %s", LDAC_ABR_LIB_NAMES[i], dlerror()); 120 | continue; 121 | } 122 | ldac_ABR_get_handle_func = (ldac_ABR_get_handle_func_t) load_func(ldac_abr_lib_h, 123 | LDAC_ABR_GET_HANDLE_FUNC_NAME); 124 | if (ldac_ABR_get_handle_func == NULL) 125 | continue; 126 | ldac_ABR_free_handle_func = (ldac_ABR_free_handle_func_t) load_func(ldac_abr_lib_h, 127 | LDAC_ABR_FREE_HANDLE_FUNC_NAME); 128 | if (ldac_ABR_free_handle_func == NULL) 129 | continue; 130 | ldac_ABR_Init_func = (ldac_ABR_Init_func_t) load_func(ldac_abr_lib_h, LDAC_ABR_INIT_FUNC_NAME); 131 | if (ldac_ABR_Init_func == NULL) 132 | continue; 133 | ldac_ABR_set_thresholds_func = (ldac_ABR_set_thresholds_func_t) load_func(ldac_abr_lib_h, 134 | LDAC_ABR_SET_THRESHOLDS_FUNC_NAME); 135 | if (ldac_ABR_set_thresholds_func == NULL) 136 | continue; 137 | ldac_ABR_Proc_func = (ldac_ABR_Proc_func_t) load_func(ldac_abr_lib_h, LDAC_ABR_PROC_FUNC_NAME); 138 | if (ldac_ABR_Proc_func == NULL) 139 | continue; 140 | return true; 141 | } 142 | return false; 143 | } 144 | 145 | static void ldac_encoder_unload() { 146 | if (ldac_encoder_lib_h != NULL) { 147 | dlclose(ldac_encoder_lib_h); 148 | ldac_encoder_lib_h = NULL; 149 | } 150 | ldacBT_get_handle_func = NULL; 151 | ldacBT_free_handle_func = NULL; 152 | ldacBT_close_handle_func = NULL; 153 | ldacBT_get_version_func = NULL; 154 | ldacBT_get_sampling_freq_func = NULL; 155 | ldacBT_get_bitrate_func = NULL; 156 | ldacBT_init_handle_encode_func = NULL; 157 | ldacBT_set_eqmid_func = NULL; 158 | ldacBT_get_eqmid_func = NULL; 159 | ldacBT_alter_eqmid_priority_func = NULL; 160 | ldacBT_encode_func = NULL; 161 | ldacBT_get_error_code_func = NULL; 162 | ldac_ABR_get_handle_func = NULL; 163 | ldac_ABR_free_handle_func = NULL; 164 | ldac_ABR_Init_func = NULL; 165 | ldac_ABR_set_thresholds_func = NULL; 166 | ldac_ABR_Proc_func = NULL; 167 | } 168 | 169 | static bool _ldac_encoder_load() { 170 | if (ldac_encoder_lib_h) 171 | return true; 172 | for (int i = 0; i < PA_ELEMENTSOF(LDAC_ENCODER_LIB_NAMES); ++i) { 173 | ldac_encoder_unload(); 174 | ldac_encoder_lib_h = dlopen(LDAC_ENCODER_LIB_NAMES[i], RTLD_NOW); 175 | if (ldac_encoder_lib_h == NULL) { 176 | pa_log_warn("Cannot open LDAC encoder library: %s. %s", LDAC_ENCODER_LIB_NAMES[i], dlerror()); 177 | continue; 178 | } 179 | ldacBT_get_handle_func = (ldacBT_get_handle_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_HANDLE_FUNC_NAME); 180 | if (ldacBT_get_handle_func == NULL) 181 | continue; 182 | ldacBT_free_handle_func = (ldacBT_free_handle_func_t) load_func(ldac_encoder_lib_h, LDAC_FREE_HANDLE_FUNC_NAME); 183 | if (ldacBT_free_handle_func == NULL) 184 | continue; 185 | ldacBT_close_handle_func = (ldacBT_close_handle_func_t) load_func(ldac_encoder_lib_h, 186 | LDAC_CLOSE_HANDLE_FUNC_NAME); 187 | if (ldacBT_close_handle_func == NULL) 188 | continue; 189 | ldacBT_get_version_func = (ldacBT_get_version_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_VERSION_FUNC_NAME); 190 | if (ldacBT_get_version_func == NULL) 191 | continue; 192 | ldacBT_get_sampling_freq_func = (ldacBT_get_sampling_freq_func_t) load_func(ldac_encoder_lib_h, 193 | LDAC_GET_SAMPLING_FREQ_FUNC_NAME); 194 | if (ldacBT_get_sampling_freq_func == NULL) 195 | continue; 196 | ldacBT_get_bitrate_func = (ldacBT_get_bitrate_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_BITRATE_FUNC_NAME); 197 | if (ldacBT_get_bitrate_func == NULL) 198 | continue; 199 | ldacBT_init_handle_encode_func = (ldacBT_init_handle_encode_func_t) load_func(ldac_encoder_lib_h, 200 | LDAC_INIT_HANDLE_ENCODE_FUNC_NAME); 201 | if (ldacBT_init_handle_encode_func == NULL) 202 | continue; 203 | ldacBT_set_eqmid_func = (ldacBT_set_eqmid_func_t) load_func(ldac_encoder_lib_h, LDAC_SET_EQMID_FUNC_NAME); 204 | if (ldacBT_set_eqmid_func == NULL) 205 | continue; 206 | ldacBT_get_eqmid_func = (ldacBT_get_eqmid_func_t) load_func(ldac_encoder_lib_h, LDAC_GET_EQMID_FUNC_NAME); 207 | if (ldacBT_get_eqmid_func == NULL) 208 | continue; 209 | ldacBT_alter_eqmid_priority_func = (ldacBT_alter_eqmid_priority_func_t) load_func(ldac_encoder_lib_h, 210 | LDAC_ALTER_EQMID_PRIORITY_FUNC_NAME); 211 | if (ldacBT_alter_eqmid_priority_func == NULL) 212 | continue; 213 | ldacBT_encode_func = (ldacBT_encode_func_t) load_func(ldac_encoder_lib_h, LDAC_ENCODE_FUNC_NAME); 214 | if (ldacBT_encode_func == NULL) 215 | continue; 216 | ldacBT_get_error_code_func = (ldacBT_get_error_code_func_t) load_func(ldac_encoder_lib_h, 217 | LDAC_GET_ERROR_CODE_FUNC_NAME); 218 | if (ldacBT_get_error_code_func == NULL) 219 | continue; 220 | if (!ldac_abr_load()) { 221 | pa_log_debug("Cannot load the LDAC ABR library"); 222 | ldac_abr_unload(); 223 | ldac_abr_loaded = false; 224 | } else 225 | ldac_abr_loaded = true; 226 | return true; 227 | } 228 | return false; 229 | } 230 | 231 | 232 | bool ldac_encoder_load() { 233 | if (!_ldac_encoder_load()) { 234 | pa_log_debug("Cannot load the LDAC encoder library"); 235 | ldac_encoder_unload(); 236 | return false; 237 | } 238 | return true; 239 | } 240 | 241 | bool is_ldac_abr_loaded() { 242 | return ldac_abr_lib_h ? true : false; 243 | } 244 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/ldac_libs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2019 Huang-Huang Bao 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | */ 20 | 21 | #ifndef PULSEAUDIO_MODULES_BT_LDAC_LIBS_H 22 | #define PULSEAUDIO_MODULES_BT_LDAC_LIBS_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | typedef HANDLE_LDAC_BT (*ldacBT_get_handle_func_t)(void); 29 | 30 | typedef void (*ldacBT_free_handle_func_t)(HANDLE_LDAC_BT hLdacBt); 31 | 32 | typedef void (*ldacBT_close_handle_func_t)(HANDLE_LDAC_BT hLdacBt); 33 | 34 | typedef int (*ldacBT_get_version_func_t)(void); 35 | 36 | typedef int (*ldacBT_get_sampling_freq_func_t)(HANDLE_LDAC_BT hLdacBt); 37 | 38 | typedef int (*ldacBT_get_bitrate_func_t)(HANDLE_LDAC_BT hLdacBt); 39 | 40 | typedef int (*ldacBT_init_handle_encode_func_t)(HANDLE_LDAC_BT hLdacBt, int mtu, int eqmid, int cm, 41 | LDACBT_SMPL_FMT_T fmt, int sf); 42 | 43 | typedef int (*ldacBT_set_eqmid_func_t)(HANDLE_LDAC_BT hLdacBt, int eqmid); 44 | 45 | typedef int (*ldacBT_get_eqmid_func_t)(HANDLE_LDAC_BT hLdacBt); 46 | 47 | typedef int (*ldacBT_alter_eqmid_priority_func_t)(HANDLE_LDAC_BT hLdacBt, int priority); 48 | 49 | typedef int (*ldacBT_encode_func_t)(HANDLE_LDAC_BT hLdacBt, void *p_pcm, int *pcm_used, 50 | unsigned char *p_stream, int *stream_sz, int *frame_num); 51 | 52 | typedef int (*ldacBT_get_error_code_func_t)(HANDLE_LDAC_BT hLdacBt); 53 | 54 | 55 | typedef HANDLE_LDAC_ABR (*ldac_ABR_get_handle_func_t)(void); 56 | 57 | typedef void (*ldac_ABR_free_handle_func_t)(HANDLE_LDAC_ABR hLdacAbr); 58 | 59 | typedef int (*ldac_ABR_Init_func_t)(HANDLE_LDAC_ABR hLdacAbr, unsigned int interval_ms); 60 | 61 | typedef int (*ldac_ABR_set_thresholds_func_t)(HANDLE_LDAC_ABR hLdacAbr, unsigned int thCritical, 62 | unsigned int thDangerousTrend, unsigned int thSafety4HQSQ); 63 | 64 | typedef int (*ldac_ABR_Proc_func_t)(HANDLE_LDAC_BT hLdacBt, HANDLE_LDAC_ABR hLdacAbr, 65 | unsigned int TxQueueDepth, unsigned int flagEnable); 66 | 67 | extern ldacBT_get_handle_func_t ldacBT_get_handle_func; 68 | extern ldacBT_free_handle_func_t ldacBT_free_handle_func; 69 | extern ldacBT_close_handle_func_t ldacBT_close_handle_func; 70 | extern ldacBT_get_version_func_t ldacBT_get_version_func; 71 | extern ldacBT_get_sampling_freq_func_t ldacBT_get_sampling_freq_func; 72 | extern ldacBT_get_bitrate_func_t ldacBT_get_bitrate_func; 73 | extern ldacBT_init_handle_encode_func_t ldacBT_init_handle_encode_func; 74 | extern ldacBT_set_eqmid_func_t ldacBT_set_eqmid_func; 75 | extern ldacBT_get_eqmid_func_t ldacBT_get_eqmid_func; 76 | extern ldacBT_alter_eqmid_priority_func_t ldacBT_alter_eqmid_priority_func; 77 | extern ldacBT_encode_func_t ldacBT_encode_func; 78 | extern ldacBT_get_error_code_func_t ldacBT_get_error_code_func; 79 | 80 | 81 | extern ldac_ABR_get_handle_func_t ldac_ABR_get_handle_func; 82 | extern ldac_ABR_free_handle_func_t ldac_ABR_free_handle_func; 83 | extern ldac_ABR_Init_func_t ldac_ABR_Init_func; 84 | extern ldac_ABR_set_thresholds_func_t ldac_ABR_set_thresholds_func; 85 | extern ldac_ABR_Proc_func_t ldac_ABR_Proc_func; 86 | 87 | bool ldac_encoder_load(); 88 | 89 | bool is_ldac_abr_loaded(); 90 | 91 | #endif //PULSEAUDIO_MODULES_BT_LDAC_LIBS_H 92 | -------------------------------------------------------------------------------- /src/modules/bluetooth/a2dp/rtp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * BlueZ - Bluetooth protocol stack for Linux 4 | * 5 | * Copyright (C) 2004-2010 Marcel Holtmann 6 | * 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, see . 20 | */ 21 | 22 | #if __BYTE_ORDER == __LITTLE_ENDIAN 23 | 24 | struct rtp_header { 25 | unsigned cc:4; 26 | unsigned x:1; 27 | unsigned p:1; 28 | unsigned v:2; 29 | 30 | unsigned pt:7; 31 | unsigned m:1; 32 | 33 | uint16_t sequence_number; 34 | uint32_t timestamp; 35 | uint32_t ssrc; 36 | uint32_t csrc[0]; 37 | } __attribute__ ((packed)); 38 | 39 | struct rtp_payload { 40 | unsigned frame_count:4; 41 | unsigned rfa0:1; 42 | unsigned is_last_fragment:1; 43 | unsigned is_first_fragment:1; 44 | unsigned is_fragmented:1; 45 | } __attribute__ ((packed)); 46 | 47 | #elif __BYTE_ORDER == __BIG_ENDIAN 48 | 49 | struct rtp_header { 50 | unsigned v:2; 51 | unsigned p:1; 52 | unsigned x:1; 53 | unsigned cc:4; 54 | 55 | unsigned m:1; 56 | unsigned pt:7; 57 | 58 | uint16_t sequence_number; 59 | uint32_t timestamp; 60 | uint32_t ssrc; 61 | uint32_t csrc[0]; 62 | } __attribute__ ((packed)); 63 | 64 | struct rtp_payload { 65 | unsigned is_fragmented:1; 66 | unsigned is_first_fragment:1; 67 | unsigned is_last_fragment:1; 68 | unsigned rfa0:1; 69 | unsigned frame_count:4; 70 | } __attribute__ ((packed)); 71 | 72 | #else 73 | #error "Unknown byte order" 74 | #endif 75 | -------------------------------------------------------------------------------- /src/modules/bluetooth/backend-native.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2008-2013 João Paulo Rechi Vita 5 | * Copyright 2018-2019 Huang-Huang Bao 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | #include "bluez5-util.h" 40 | 41 | struct pa_bluetooth_backend { 42 | pa_core *core; 43 | pa_dbus_connection *connection; 44 | pa_bluetooth_discovery *discovery; 45 | bool enable_hs_role; 46 | 47 | PA_LLIST_HEAD(pa_dbus_pending, pending); 48 | }; 49 | 50 | struct transport_data { 51 | int rfcomm_fd; 52 | pa_io_event *rfcomm_io; 53 | int sco_fd; 54 | pa_io_event *sco_io; 55 | pa_mainloop_api *mainloop; 56 | }; 57 | 58 | #define BLUEZ_SERVICE "org.bluez" 59 | #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1" 60 | 61 | #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported" 62 | 63 | #define BLUEZ_PROFILE_MANAGER_INTERFACE BLUEZ_SERVICE ".ProfileManager1" 64 | #define BLUEZ_PROFILE_INTERFACE BLUEZ_SERVICE ".Profile1" 65 | 66 | #define HSP_AG_PROFILE "/Profile/HSPAGProfile" 67 | #define HSP_HS_PROFILE "/Profile/HSPHSProfile" 68 | 69 | /* RFCOMM channel for HSP headset role 70 | * The choice seems to be a bit arbitrary -- it looks like at least channels 2, 4 and 5 also work*/ 71 | #define HSP_HS_DEFAULT_CHANNEL 3 72 | 73 | #define PROFILE_INTROSPECT_XML \ 74 | DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ 75 | "" \ 76 | " " \ 77 | " " \ 78 | " " \ 79 | " " \ 80 | " " \ 81 | " " \ 82 | " " \ 83 | " " \ 84 | " " \ 85 | " " \ 86 | " " \ 87 | " " \ 88 | " " \ 89 | " " \ 90 | " " \ 91 | " " \ 92 | " " \ 93 | "" 94 | 95 | static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, 96 | DBusPendingCallNotifyFunction func, void *call_data) { 97 | 98 | pa_dbus_pending *p; 99 | DBusPendingCall *call; 100 | 101 | pa_assert(backend); 102 | pa_assert(m); 103 | 104 | pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1)); 105 | 106 | p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data); 107 | PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p); 108 | dbus_pending_call_set_notify(call, func, p, NULL); 109 | 110 | return p; 111 | } 112 | 113 | static int sco_do_connect(pa_bluetooth_transport *t) { 114 | pa_bluetooth_device *d = t->device; 115 | struct sockaddr_sco addr; 116 | socklen_t len; 117 | int err, i; 118 | int sock; 119 | bdaddr_t src; 120 | bdaddr_t dst; 121 | const char *src_addr, *dst_addr; 122 | 123 | src_addr = d->adapter->address; 124 | dst_addr = d->address; 125 | 126 | /* don't use ba2str to avoid -lbluetooth */ 127 | for (i = 5; i >= 0; i--, src_addr += 3) 128 | src.b[i] = strtol(src_addr, NULL, 16); 129 | for (i = 5; i >= 0; i--, dst_addr += 3) 130 | dst.b[i] = strtol(dst_addr, NULL, 16); 131 | 132 | sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); 133 | if (sock < 0) { 134 | pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno)); 135 | return -1; 136 | } 137 | 138 | len = sizeof(addr); 139 | memset(&addr, 0, len); 140 | addr.sco_family = AF_BLUETOOTH; 141 | bacpy(&addr.sco_bdaddr, &src); 142 | 143 | if (bind(sock, (struct sockaddr *) &addr, len) < 0) { 144 | pa_log_error("bind(): %s", pa_cstrerror(errno)); 145 | goto fail_close; 146 | } 147 | 148 | memset(&addr, 0, len); 149 | addr.sco_family = AF_BLUETOOTH; 150 | bacpy(&addr.sco_bdaddr, &dst); 151 | 152 | pa_log_info("doing connect"); 153 | err = connect(sock, (struct sockaddr *) &addr, len); 154 | if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { 155 | pa_log_error("connect(): %s", pa_cstrerror(errno)); 156 | goto fail_close; 157 | } 158 | return sock; 159 | 160 | fail_close: 161 | close(sock); 162 | return -1; 163 | } 164 | 165 | static int sco_do_accept(pa_bluetooth_transport *t) { 166 | struct transport_data *trd = t->userdata; 167 | struct sockaddr_sco addr; 168 | socklen_t optlen; 169 | int sock; 170 | 171 | memset(&addr, 0, sizeof(addr)); 172 | optlen = sizeof(addr); 173 | 174 | pa_log_info ("doing accept"); 175 | sock = accept(trd->sco_fd, (struct sockaddr *) &addr, &optlen); 176 | if (sock < 0) { 177 | if (errno != EAGAIN) 178 | pa_log_error("accept(): %s", pa_cstrerror(errno)); 179 | goto fail; 180 | } 181 | return sock; 182 | 183 | fail: 184 | return -1; 185 | } 186 | 187 | static int sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { 188 | int sock; 189 | socklen_t len; 190 | 191 | if (optional) 192 | sock = sco_do_accept(t); 193 | else 194 | sock = sco_do_connect(t); 195 | 196 | if (sock < 0) 197 | goto fail; 198 | 199 | if (imtu) *imtu = 48; 200 | if (omtu) *omtu = 48; 201 | 202 | if (t->device->autodetect_mtu) { 203 | struct sco_options sco_opt; 204 | 205 | len = sizeof(sco_opt); 206 | memset(&sco_opt, 0, len); 207 | 208 | if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) 209 | pa_log_warn("getsockopt(SCO_OPTIONS) failed, loading defaults"); 210 | else { 211 | pa_log_debug("autodetected imtu = omtu = %u", sco_opt.mtu); 212 | if (imtu) *imtu = sco_opt.mtu; 213 | if (omtu) *omtu = sco_opt.mtu; 214 | } 215 | } 216 | 217 | return sock; 218 | 219 | fail: 220 | return -1; 221 | } 222 | 223 | static void sco_release_cb(pa_bluetooth_transport *t) { 224 | pa_log_info("Transport %s released", t->path); 225 | /* device will close the SCO socket for us */ 226 | } 227 | 228 | static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { 229 | pa_bluetooth_transport *t = userdata; 230 | 231 | pa_assert(io); 232 | pa_assert(t); 233 | 234 | if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { 235 | pa_log_error("error listening SCO connection: %s", pa_cstrerror(errno)); 236 | goto fail; 237 | } 238 | 239 | if (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) { 240 | pa_log_info("SCO incoming connection: changing state to PLAYING"); 241 | pa_bluetooth_transport_set_state (t, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 242 | } 243 | 244 | fail: 245 | return; 246 | } 247 | 248 | static int sco_listen(pa_bluetooth_transport *t) { 249 | struct transport_data *trd = t->userdata; 250 | struct sockaddr_sco addr; 251 | int sock, i; 252 | bdaddr_t src; 253 | const char *src_addr; 254 | 255 | sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO); 256 | if (sock < 0) { 257 | pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno)); 258 | return -1; 259 | } 260 | 261 | src_addr = t->device->adapter->address; 262 | 263 | /* don't use ba2str to avoid -lbluetooth */ 264 | for (i = 5; i >= 0; i--, src_addr += 3) 265 | src.b[i] = strtol(src_addr, NULL, 16); 266 | 267 | /* Bind to local address */ 268 | memset(&addr, 0, sizeof(addr)); 269 | addr.sco_family = AF_BLUETOOTH; 270 | bacpy(&addr.sco_bdaddr, &src); 271 | 272 | if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 273 | pa_log_error("bind(): %s", pa_cstrerror(errno)); 274 | goto fail_close; 275 | } 276 | 277 | pa_log_info ("doing listen"); 278 | if (listen(sock, 1) < 0) { 279 | pa_log_error("listen(): %s", pa_cstrerror(errno)); 280 | goto fail_close; 281 | } 282 | 283 | trd->sco_fd = sock; 284 | trd->sco_io = trd->mainloop->io_new(trd->mainloop, sock, PA_IO_EVENT_INPUT, 285 | sco_io_callback, t); 286 | 287 | return sock; 288 | 289 | fail_close: 290 | close(sock); 291 | return -1; 292 | } 293 | 294 | static void register_profile_reply(DBusPendingCall *pending, void *userdata) { 295 | DBusMessage *r; 296 | pa_dbus_pending *p; 297 | pa_bluetooth_backend *b; 298 | char *profile; 299 | 300 | pa_assert(pending); 301 | pa_assert_se(p = userdata); 302 | pa_assert_se(b = p->context_data); 303 | pa_assert_se(profile = p->call_data); 304 | pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 305 | 306 | if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) { 307 | pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", profile); 308 | goto finish; 309 | } 310 | 311 | if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 312 | pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r), 313 | pa_dbus_get_error_message(r)); 314 | goto finish; 315 | } 316 | 317 | finish: 318 | dbus_message_unref(r); 319 | 320 | PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p); 321 | pa_dbus_pending_free(p); 322 | 323 | pa_xfree(profile); 324 | } 325 | 326 | static void register_profile(pa_bluetooth_backend *b, const char *profile, const char *uuid) { 327 | DBusMessage *m; 328 | DBusMessageIter i, d; 329 | dbus_bool_t autoconnect; 330 | dbus_uint16_t version, chan; 331 | 332 | pa_log_debug("Registering Profile %s %s", profile, uuid); 333 | 334 | pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile")); 335 | 336 | dbus_message_iter_init_append(m, &i); 337 | pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &profile)); 338 | pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid)); 339 | dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING 340 | DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d); 341 | if (pa_bluetooth_uuid_is_hsp_hs(uuid)) { 342 | /* In the headset role, the connection will only be initiated from the remote side */ 343 | autoconnect = 0; 344 | pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect); 345 | chan = HSP_HS_DEFAULT_CHANNEL; 346 | pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan); 347 | /* HSP version 1.2 */ 348 | version = 0x0102; 349 | pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version); 350 | } 351 | dbus_message_iter_close_container(&i, &d); 352 | 353 | send_and_add_to_pending(b, m, register_profile_reply, pa_xstrdup(profile)); 354 | } 355 | 356 | static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { 357 | pa_bluetooth_transport *t = userdata; 358 | 359 | pa_assert(io); 360 | pa_assert(t); 361 | 362 | if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) { 363 | pa_log_info("Lost RFCOMM connection."); 364 | goto fail; 365 | } 366 | 367 | if (events & PA_IO_EVENT_INPUT) { 368 | char buf[512]; 369 | ssize_t len; 370 | int gain, dummy; 371 | bool do_reply = false; 372 | 373 | len = pa_read(fd, buf, 511, NULL); 374 | if (len < 0) { 375 | pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno)); 376 | goto fail; 377 | } 378 | buf[len] = 0; 379 | pa_log_debug("RFCOMM << %s", buf); 380 | 381 | /* There are only four HSP AT commands: 382 | * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain. 383 | * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain 384 | * is changed on the AG side. 385 | * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain. 386 | * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain 387 | * is changed on the AG side. 388 | * AT+CKPD=200: Sent by HS when headset button is pressed. 389 | * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because 390 | * it does not expect a reply. */ 391 | if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) { 392 | t->speaker_gain = gain; 393 | pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), t); 394 | do_reply = true; 395 | 396 | } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) { 397 | t->microphone_gain = gain; 398 | pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), t); 399 | do_reply = true; 400 | } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) { 401 | do_reply = true; 402 | } else { 403 | do_reply = false; 404 | } 405 | 406 | if (do_reply) { 407 | pa_log_debug("RFCOMM >> OK"); 408 | 409 | len = write(fd, "\r\nOK\r\n", 6); 410 | 411 | /* we ignore any errors, it's not critical and real errors should 412 | * be caught with the HANGUP and ERROR events handled above */ 413 | if (len < 0) 414 | pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); 415 | } 416 | } 417 | 418 | return; 419 | 420 | fail: 421 | pa_bluetooth_transport_unlink(t); 422 | pa_bluetooth_transport_free(t); 423 | } 424 | 425 | static void transport_destroy(pa_bluetooth_transport *t) { 426 | struct transport_data *trd = t->userdata; 427 | 428 | if (trd->sco_io) { 429 | trd->mainloop->io_free(trd->sco_io); 430 | shutdown(trd->sco_fd, SHUT_RDWR); 431 | close (trd->sco_fd); 432 | } 433 | 434 | trd->mainloop->io_free(trd->rfcomm_io); 435 | shutdown(trd->rfcomm_fd, SHUT_RDWR); 436 | close (trd->rfcomm_fd); 437 | 438 | pa_xfree(trd); 439 | } 440 | 441 | static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) { 442 | struct transport_data *trd = t->userdata; 443 | char buf[512]; 444 | ssize_t len, written; 445 | 446 | if (t->speaker_gain == gain) 447 | return; 448 | 449 | t->speaker_gain = gain; 450 | 451 | /* If we are in the AG role, we send a command to the head set to change 452 | * the speaker gain. In the HS role, source and sink are swapped, so 453 | * in this case we notify the AG that the microphone gain has changed */ 454 | if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) { 455 | len = sprintf(buf, "\r\n+VGS=%d\r\n", gain); 456 | pa_log_debug("RFCOMM >> +VGS=%d", gain); 457 | } else { 458 | len = sprintf(buf, "\r\nAT+VGM=%d\r\n", gain); 459 | pa_log_debug("RFCOMM >> AT+VGM=%d", gain); 460 | } 461 | 462 | written = write(trd->rfcomm_fd, buf, len); 463 | 464 | if (written != len) 465 | pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); 466 | } 467 | 468 | static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) { 469 | struct transport_data *trd = t->userdata; 470 | char buf[512]; 471 | ssize_t len, written; 472 | 473 | if (t->microphone_gain == gain) 474 | return; 475 | 476 | t->microphone_gain = gain; 477 | 478 | /* If we are in the AG role, we send a command to the head set to change 479 | * the microphone gain. In the HS role, source and sink are swapped, so 480 | * in this case we notify the AG that the speaker gain has changed */ 481 | if (t->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) { 482 | len = sprintf(buf, "\r\n+VGM=%d\r\n", gain); 483 | pa_log_debug("RFCOMM >> +VGM=%d", gain); 484 | } else { 485 | len = sprintf(buf, "\r\nAT+VGS=%d\r\n", gain); 486 | pa_log_debug("RFCOMM >> AT+VGS=%d", gain); 487 | } 488 | 489 | written = write (trd->rfcomm_fd, buf, len); 490 | 491 | if (written != len) 492 | pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno)); 493 | } 494 | 495 | static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) { 496 | pa_bluetooth_backend *b = userdata; 497 | pa_bluetooth_device *d; 498 | pa_bluetooth_transport *t; 499 | pa_bluetooth_profile_t p; 500 | DBusMessage *r; 501 | int fd; 502 | const char *sender, *path, PA_UNUSED *handler; 503 | DBusMessageIter arg_i; 504 | char *pathfd; 505 | struct transport_data *trd; 506 | 507 | if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) { 508 | pa_log_error("Invalid signature found in NewConnection"); 509 | goto fail; 510 | } 511 | 512 | handler = dbus_message_get_path(m); 513 | if (pa_streq(handler, HSP_AG_PROFILE)) { 514 | p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; 515 | } else if (pa_streq(handler, HSP_HS_PROFILE)) { 516 | p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; 517 | } else { 518 | pa_log_error("Invalid handler"); 519 | goto fail; 520 | } 521 | 522 | pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH); 523 | dbus_message_iter_get_basic(&arg_i, &path); 524 | 525 | d = pa_bluetooth_discovery_get_device_by_path(b->discovery, path); 526 | if (d == NULL) { 527 | pa_log_error("Device doesnt exist for %s", path); 528 | goto fail; 529 | } 530 | 531 | pa_assert_se(dbus_message_iter_next(&arg_i)); 532 | 533 | pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD); 534 | dbus_message_iter_get_basic(&arg_i, &fd); 535 | 536 | pa_log_debug("dbus: NewConnection path=%s, fd=%d, profile %s", path, fd, 537 | pa_bluetooth_profile_to_string(p)); 538 | 539 | sender = dbus_message_get_sender(m); 540 | 541 | pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd); 542 | t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL, 0); 543 | pa_xfree(pathfd); 544 | 545 | t->acquire = sco_acquire_cb; 546 | t->release = sco_release_cb; 547 | t->destroy = transport_destroy; 548 | t->set_speaker_gain = set_speaker_gain; 549 | t->set_microphone_gain = set_microphone_gain; 550 | 551 | trd = pa_xnew0(struct transport_data, 1); 552 | trd->rfcomm_fd = fd; 553 | trd->mainloop = b->core->mainloop; 554 | trd->rfcomm_io = trd->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT, 555 | rfcomm_io_callback, t); 556 | t->userdata = trd; 557 | 558 | sco_listen(t); 559 | 560 | pa_bluetooth_transport_put(t); 561 | 562 | pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); 563 | 564 | pa_assert_se(r = dbus_message_new_method_return(m)); 565 | 566 | return r; 567 | 568 | fail: 569 | pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to handle new connection")); 570 | return r; 571 | } 572 | 573 | static DBusMessage *profile_request_disconnection(DBusConnection *conn, DBusMessage *m, void *userdata) { 574 | DBusMessage *r; 575 | 576 | pa_assert_se(r = dbus_message_new_method_return(m)); 577 | 578 | return r; 579 | } 580 | 581 | static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void *userdata) { 582 | pa_bluetooth_backend *b = userdata; 583 | DBusMessage *r = NULL; 584 | const char *path, *interface, *member; 585 | 586 | pa_assert(b); 587 | 588 | path = dbus_message_get_path(m); 589 | interface = dbus_message_get_interface(m); 590 | member = dbus_message_get_member(m); 591 | 592 | pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); 593 | 594 | if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE)) 595 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 596 | 597 | if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { 598 | const char *xml = PROFILE_INTROSPECT_XML; 599 | 600 | pa_assert_se(r = dbus_message_new_method_return(m)); 601 | pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); 602 | 603 | } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) { 604 | pa_log_debug("Release not handled"); 605 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 606 | } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) { 607 | r = profile_request_disconnection(c, m, userdata); 608 | } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection")) 609 | r = profile_new_connection(c, m, userdata); 610 | else 611 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 612 | 613 | if (r) { 614 | pa_assert_se(dbus_connection_send(pa_dbus_connection_get(b->connection), r, NULL)); 615 | dbus_message_unref(r); 616 | } 617 | 618 | return DBUS_HANDLER_RESULT_HANDLED; 619 | } 620 | 621 | static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) { 622 | static const DBusObjectPathVTable vtable_profile = { 623 | .message_function = profile_handler, 624 | }; 625 | const char *object_name; 626 | const char *uuid; 627 | 628 | pa_assert(b); 629 | 630 | switch (profile) { 631 | case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: 632 | object_name = HSP_AG_PROFILE; 633 | uuid = PA_BLUETOOTH_UUID_HSP_AG; 634 | break; 635 | case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: 636 | object_name = HSP_HS_PROFILE; 637 | uuid = PA_BLUETOOTH_UUID_HSP_HS; 638 | break; 639 | default: 640 | pa_assert_not_reached(); 641 | break; 642 | } 643 | 644 | pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(b->connection), object_name, &vtable_profile, b)); 645 | register_profile(b, object_name, uuid); 646 | } 647 | 648 | static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) { 649 | pa_assert(b); 650 | 651 | switch (profile) { 652 | case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT: 653 | dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE); 654 | break; 655 | case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY: 656 | dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE); 657 | break; 658 | default: 659 | pa_assert_not_reached(); 660 | break; 661 | } 662 | } 663 | 664 | void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *native_backend, bool enable_hs_role) { 665 | 666 | if (enable_hs_role == native_backend->enable_hs_role) 667 | return; 668 | 669 | if (enable_hs_role) 670 | profile_init(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); 671 | else 672 | profile_done(native_backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); 673 | 674 | native_backend->enable_hs_role = enable_hs_role; 675 | } 676 | 677 | pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) { 678 | pa_bluetooth_backend *backend; 679 | DBusError err; 680 | 681 | pa_log_debug("Bluetooth Headset Backend API support using the native backend"); 682 | 683 | backend = pa_xnew0(pa_bluetooth_backend, 1); 684 | backend->core = c; 685 | 686 | dbus_error_init(&err); 687 | if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { 688 | pa_log("Failed to get D-Bus connection: %s", err.message); 689 | dbus_error_free(&err); 690 | pa_xfree(backend); 691 | return NULL; 692 | } 693 | 694 | backend->discovery = y; 695 | backend->enable_hs_role = enable_hs_role; 696 | 697 | if (enable_hs_role) 698 | profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); 699 | profile_init(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT); 700 | 701 | return backend; 702 | } 703 | 704 | void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) { 705 | pa_assert(backend); 706 | 707 | pa_dbus_free_pending_list(&backend->pending); 708 | 709 | if (backend->enable_hs_role) 710 | profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY); 711 | profile_done(backend, PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT); 712 | 713 | pa_dbus_connection_unref(backend->connection); 714 | 715 | pa_xfree(backend); 716 | } 717 | -------------------------------------------------------------------------------- /src/modules/bluetooth/backend-ofono.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2008-2013 João Paulo Rechi Vita 5 | * Copyright 2018-2019 Huang-Huang Bao 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "bluez5-util.h" 35 | 36 | #define HFP_AUDIO_CODEC_CVSD 0x01 37 | #define HFP_AUDIO_CODEC_MSBC 0x02 38 | 39 | #define OFONO_SERVICE "org.ofono" 40 | #define HF_AUDIO_AGENT_INTERFACE OFONO_SERVICE ".HandsfreeAudioAgent" 41 | #define HF_AUDIO_MANAGER_INTERFACE OFONO_SERVICE ".HandsfreeAudioManager" 42 | 43 | #define HF_AUDIO_AGENT_PATH "/HandsfreeAudioAgent" 44 | 45 | #define HF_AUDIO_AGENT_XML \ 46 | DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ 47 | "" \ 48 | " " \ 49 | " " \ 50 | " " \ 51 | " " \ 52 | " " \ 53 | " " \ 54 | " " \ 55 | " " \ 56 | " " \ 57 | " " \ 58 | " " \ 59 | " " \ 60 | " " \ 61 | " " \ 62 | "" 63 | 64 | struct hf_audio_card { 65 | pa_bluetooth_backend *backend; 66 | char *path; 67 | char *remote_address; 68 | char *local_address; 69 | 70 | bool connecting; 71 | int fd; 72 | int (*acquire)(struct hf_audio_card *card); 73 | 74 | pa_bluetooth_transport *transport; 75 | }; 76 | 77 | struct pa_bluetooth_backend { 78 | pa_core *core; 79 | pa_bluetooth_discovery *discovery; 80 | pa_dbus_connection *connection; 81 | pa_hashmap *cards; 82 | char *ofono_bus_id; 83 | 84 | PA_LLIST_HEAD(pa_dbus_pending, pending); 85 | }; 86 | 87 | static pa_dbus_pending* hf_dbus_send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, 88 | DBusPendingCallNotifyFunction func, void *call_data) { 89 | pa_dbus_pending *p; 90 | DBusPendingCall *call; 91 | 92 | pa_assert(backend); 93 | pa_assert(m); 94 | 95 | pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1)); 96 | 97 | p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data); 98 | PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p); 99 | dbus_pending_call_set_notify(call, func, p, NULL); 100 | 101 | return p; 102 | } 103 | 104 | static DBusMessage *card_send(struct hf_audio_card *card, const char *method, DBusError *err) 105 | { 106 | pa_bluetooth_transport *t = card->transport; 107 | DBusMessage *m, *r; 108 | 109 | pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.ofono.HandsfreeAudioCard", method)); 110 | r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(card->backend->connection), m, -1, err); 111 | dbus_message_unref(m); 112 | 113 | return r; 114 | } 115 | 116 | static int card_connect(struct hf_audio_card *card) { 117 | DBusMessage *r; 118 | DBusError err; 119 | 120 | if (card->connecting) 121 | return -EAGAIN; 122 | 123 | card->connecting = true; 124 | 125 | dbus_error_init(&err); 126 | r = card_send(card, "Connect", &err); 127 | 128 | if (!r) { 129 | pa_log_error("Failed to connect %s: %s", err.name, err.message); 130 | card->connecting = false; 131 | dbus_error_free(&err); 132 | return -1; 133 | } 134 | 135 | dbus_message_unref(r); 136 | 137 | if (card->connecting) 138 | return -EAGAIN; 139 | 140 | return 0; 141 | } 142 | 143 | static int card_acquire(struct hf_audio_card *card) { 144 | int fd; 145 | uint8_t codec; 146 | DBusMessage *r; 147 | DBusError err; 148 | 149 | /* Try acquiring the stream first which was introduced in 1.21 */ 150 | dbus_error_init(&err); 151 | r = card_send(card, "Acquire", &err); 152 | 153 | if (!r) { 154 | if (!pa_streq(err.name, DBUS_ERROR_UNKNOWN_METHOD)) { 155 | pa_log_error("Failed to acquire %s: %s", err.name, err.message); 156 | dbus_error_free(&err); 157 | return -1; 158 | } 159 | dbus_error_free(&err); 160 | /* Fallback to Connect as this might be an old version of ofono */ 161 | card->acquire = card_connect; 162 | return card_connect(card); 163 | } 164 | 165 | if ((dbus_message_get_args(r, NULL, DBUS_TYPE_UNIX_FD, &fd, 166 | DBUS_TYPE_BYTE, &codec, 167 | DBUS_TYPE_INVALID) == true)) { 168 | dbus_message_unref(r); 169 | if (codec != HFP_AUDIO_CODEC_CVSD) { 170 | pa_log_error("Invalid codec: %u", codec); 171 | /* shutdown to make sure connection is dropped immediately */ 172 | shutdown(fd, SHUT_RDWR); 173 | close(fd); 174 | return -1; 175 | } 176 | card->transport->codec = codec; 177 | card->fd = fd; 178 | return 0; 179 | } 180 | 181 | pa_log_error("Unable to acquire"); 182 | dbus_message_unref(r); 183 | return -1; 184 | } 185 | 186 | static struct hf_audio_card *hf_audio_card_new(pa_bluetooth_backend *backend, const char *path) { 187 | struct hf_audio_card *card = pa_xnew0(struct hf_audio_card, 1); 188 | 189 | card->path = pa_xstrdup(path); 190 | card->backend = backend; 191 | card->fd = -1; 192 | card->acquire = card_acquire; 193 | 194 | return card; 195 | } 196 | 197 | static void hf_audio_card_free(struct hf_audio_card *card) { 198 | pa_assert(card); 199 | 200 | if (card->transport) 201 | pa_bluetooth_transport_free(card->transport); 202 | 203 | pa_xfree(card->path); 204 | pa_xfree(card->remote_address); 205 | pa_xfree(card->local_address); 206 | pa_xfree(card); 207 | } 208 | 209 | static int socket_accept(int sock) 210 | { 211 | char c; 212 | struct pollfd pfd; 213 | 214 | if (sock < 0) 215 | return -ENOTCONN; 216 | 217 | memset(&pfd, 0, sizeof(pfd)); 218 | pfd.fd = sock; 219 | pfd.events = POLLOUT; 220 | 221 | if (poll(&pfd, 1, 0) < 0) 222 | return -errno; 223 | 224 | /* 225 | * If socket already writable then it is not in defer setup state, 226 | * otherwise it needs to be read to authorize the connection. 227 | */ 228 | if ((pfd.revents & POLLOUT)) 229 | return 0; 230 | 231 | /* Enable socket by reading 1 byte */ 232 | if (read(sock, &c, 1) < 0) 233 | return -errno; 234 | 235 | return 0; 236 | } 237 | 238 | static int hf_audio_agent_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) { 239 | struct hf_audio_card *card = t->userdata; 240 | int err; 241 | 242 | pa_assert(card); 243 | 244 | if (!optional && card->fd < 0) { 245 | err = card->acquire(card); 246 | if (err < 0) 247 | return err; 248 | } 249 | 250 | /* The correct block size should take into account the SCO MTU from 251 | * the Bluetooth adapter and (for adapters in the USB bus) the MxPS 252 | * value from the Isoc USB endpoint in use by btusb and should be 253 | * made available to userspace by the Bluetooth kernel subsystem. 254 | * Meanwhile the empiric value 48 will be used. */ 255 | if (imtu) 256 | *imtu = 48; 257 | if (omtu) 258 | *omtu = 48; 259 | 260 | err = socket_accept(card->fd); 261 | if (err < 0) { 262 | pa_log_error("Deferred setup failed on fd %d: %s", card->fd, pa_cstrerror(-err)); 263 | return -1; 264 | } 265 | 266 | return card->fd; 267 | } 268 | 269 | static void hf_audio_agent_transport_release(pa_bluetooth_transport *t) { 270 | struct hf_audio_card *card = t->userdata; 271 | 272 | pa_assert(card); 273 | 274 | if (card->fd < 0) { 275 | pa_log_info("Transport %s already released", t->path); 276 | return; 277 | } 278 | 279 | /* shutdown to make sure connection is dropped immediately */ 280 | shutdown(card->fd, SHUT_RDWR); 281 | close(card->fd); 282 | card->fd = -1; 283 | } 284 | 285 | static void hf_audio_agent_card_found(pa_bluetooth_backend *backend, const char *path, DBusMessageIter *props_i) { 286 | DBusMessageIter i, value_i; 287 | const char *key, *value; 288 | struct hf_audio_card *card; 289 | pa_bluetooth_device *d; 290 | pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; 291 | 292 | pa_assert(backend); 293 | pa_assert(path); 294 | pa_assert(props_i); 295 | 296 | pa_log_debug("New HF card found: %s", path); 297 | 298 | card = hf_audio_card_new(backend, path); 299 | 300 | while (dbus_message_iter_get_arg_type(props_i) != DBUS_TYPE_INVALID) { 301 | char c; 302 | 303 | dbus_message_iter_recurse(props_i, &i); 304 | 305 | dbus_message_iter_get_basic(&i, &key); 306 | dbus_message_iter_next(&i); 307 | dbus_message_iter_recurse(&i, &value_i); 308 | 309 | if ((c = dbus_message_iter_get_arg_type(&value_i)) != DBUS_TYPE_STRING) { 310 | pa_log_error("Invalid properties for %s: expected 's', received '%c'", path, c); 311 | goto fail; 312 | } 313 | 314 | dbus_message_iter_get_basic(&value_i, &value); 315 | 316 | if (pa_streq(key, "RemoteAddress")) { 317 | pa_xfree(card->remote_address); 318 | card->remote_address = pa_xstrdup(value); 319 | } else if (pa_streq(key, "LocalAddress")) { 320 | pa_xfree(card->local_address); 321 | card->local_address = pa_xstrdup(value); 322 | } else if (pa_streq(key, "Type")) { 323 | if (pa_streq(value, "gateway")) 324 | p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; 325 | } 326 | 327 | pa_log_debug("%s: %s", key, value); 328 | 329 | dbus_message_iter_next(props_i); 330 | } 331 | 332 | d = pa_bluetooth_discovery_get_device_by_address(backend->discovery, card->remote_address, card->local_address); 333 | if (!d) { 334 | pa_log_error("Device doesnt exist for %s", path); 335 | goto fail; 336 | } 337 | 338 | card->transport = pa_bluetooth_transport_new(d, backend->ofono_bus_id, path, p, NULL, 0); 339 | card->transport->acquire = hf_audio_agent_transport_acquire; 340 | card->transport->release = hf_audio_agent_transport_release; 341 | card->transport->userdata = card; 342 | 343 | pa_bluetooth_transport_put(card->transport); 344 | pa_hashmap_put(backend->cards, card->path, card); 345 | 346 | return; 347 | 348 | fail: 349 | hf_audio_card_free(card); 350 | } 351 | 352 | static void hf_audio_agent_card_removed(pa_bluetooth_backend *backend, const char *path) { 353 | struct hf_audio_card *card; 354 | 355 | pa_assert(backend); 356 | pa_assert(path); 357 | 358 | pa_log_debug("HF card removed: %s", path); 359 | 360 | card = pa_hashmap_remove(backend->cards, path); 361 | if (!card) 362 | return; 363 | 364 | hf_audio_card_free(card); 365 | } 366 | 367 | static void hf_audio_agent_get_cards_reply(DBusPendingCall *pending, void *userdata) { 368 | DBusMessage *r; 369 | pa_dbus_pending *p; 370 | pa_bluetooth_backend *backend; 371 | DBusMessageIter i, array_i, struct_i, props_i; 372 | 373 | pa_assert_se(p = userdata); 374 | pa_assert_se(backend = p->context_data); 375 | pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 376 | 377 | if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 378 | pa_log_error("Failed to get a list of handsfree audio cards from ofono: %s: %s", 379 | dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); 380 | goto finish; 381 | } 382 | 383 | if (!dbus_message_iter_init(r, &i) || !pa_streq(dbus_message_get_signature(r), "a(oa{sv})")) { 384 | pa_log_error("Invalid arguments in GetCards() reply"); 385 | goto finish; 386 | } 387 | 388 | dbus_message_iter_recurse(&i, &array_i); 389 | while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) { 390 | const char *path; 391 | 392 | dbus_message_iter_recurse(&array_i, &struct_i); 393 | dbus_message_iter_get_basic(&struct_i, &path); 394 | dbus_message_iter_next(&struct_i); 395 | 396 | dbus_message_iter_recurse(&struct_i, &props_i); 397 | 398 | hf_audio_agent_card_found(backend, path, &props_i); 399 | 400 | dbus_message_iter_next(&array_i); 401 | } 402 | 403 | finish: 404 | dbus_message_unref(r); 405 | 406 | PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); 407 | pa_dbus_pending_free(p); 408 | } 409 | 410 | static void hf_audio_agent_get_cards(pa_bluetooth_backend *hf) { 411 | DBusMessage *m; 412 | 413 | pa_assert(hf); 414 | 415 | pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "GetCards")); 416 | hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_get_cards_reply, NULL); 417 | } 418 | 419 | static void ofono_bus_id_destroy(pa_bluetooth_backend *backend) { 420 | pa_hashmap_remove_all(backend->cards); 421 | 422 | if (backend->ofono_bus_id) { 423 | pa_xfree(backend->ofono_bus_id); 424 | backend->ofono_bus_id = NULL; 425 | pa_bluetooth_discovery_set_ofono_running(backend->discovery, false); 426 | } 427 | } 428 | 429 | static void hf_audio_agent_register_reply(DBusPendingCall *pending, void *userdata) { 430 | DBusMessage *r; 431 | pa_dbus_pending *p; 432 | pa_bluetooth_backend *backend; 433 | 434 | pa_assert_se(p = userdata); 435 | pa_assert_se(backend = p->context_data); 436 | pa_assert_se(r = dbus_pending_call_steal_reply(pending)); 437 | 438 | if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { 439 | pa_log_error("Failed to register as a handsfree audio agent with ofono: %s: %s", 440 | dbus_message_get_error_name(r), pa_dbus_get_error_message(r)); 441 | goto finish; 442 | } 443 | 444 | backend->ofono_bus_id = pa_xstrdup(dbus_message_get_sender(r)); 445 | 446 | hf_audio_agent_get_cards(backend); 447 | 448 | finish: 449 | dbus_message_unref(r); 450 | 451 | PA_LLIST_REMOVE(pa_dbus_pending, backend->pending, p); 452 | pa_dbus_pending_free(p); 453 | 454 | pa_bluetooth_discovery_set_ofono_running(backend->discovery, backend->ofono_bus_id != NULL); 455 | } 456 | 457 | static void hf_audio_agent_register(pa_bluetooth_backend *hf) { 458 | DBusMessage *m; 459 | uint8_t codecs[2]; 460 | const uint8_t *pcodecs = codecs; 461 | int ncodecs = 0; 462 | const char *path = HF_AUDIO_AGENT_PATH; 463 | 464 | pa_assert(hf); 465 | 466 | pa_assert_se(m = dbus_message_new_method_call(OFONO_SERVICE, "/", HF_AUDIO_MANAGER_INTERFACE, "Register")); 467 | 468 | codecs[ncodecs++] = HFP_AUDIO_CODEC_CVSD; 469 | 470 | pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pcodecs, ncodecs, 471 | DBUS_TYPE_INVALID)); 472 | 473 | hf_dbus_send_and_add_to_pending(hf, m, hf_audio_agent_register_reply, NULL); 474 | } 475 | 476 | static void hf_audio_agent_unregister(pa_bluetooth_backend *backend) { 477 | DBusMessage *m; 478 | const char *path = HF_AUDIO_AGENT_PATH; 479 | 480 | pa_assert(backend); 481 | pa_assert(backend->connection); 482 | 483 | if (backend->ofono_bus_id) { 484 | pa_assert_se(m = dbus_message_new_method_call(backend->ofono_bus_id, "/", HF_AUDIO_MANAGER_INTERFACE, "Unregister")); 485 | pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)); 486 | pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), m, NULL)); 487 | 488 | ofono_bus_id_destroy(backend); 489 | } 490 | } 491 | 492 | static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *data) { 493 | const char *sender; 494 | DBusError err; 495 | pa_bluetooth_backend *backend = data; 496 | 497 | pa_assert(bus); 498 | pa_assert(m); 499 | pa_assert(backend); 500 | 501 | sender = dbus_message_get_sender(m); 502 | if (!pa_safe_streq(backend->ofono_bus_id, sender) && !pa_streq("org.freedesktop.DBus", sender)) 503 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 504 | 505 | dbus_error_init(&err); 506 | 507 | if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) { 508 | const char *name, *old_owner, *new_owner; 509 | 510 | if (!dbus_message_get_args(m, &err, 511 | DBUS_TYPE_STRING, &name, 512 | DBUS_TYPE_STRING, &old_owner, 513 | DBUS_TYPE_STRING, &new_owner, 514 | DBUS_TYPE_INVALID)) { 515 | pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message); 516 | goto fail; 517 | } 518 | 519 | if (pa_streq(name, OFONO_SERVICE)) { 520 | 521 | if (old_owner && *old_owner) { 522 | pa_log_debug("oFono disappeared"); 523 | ofono_bus_id_destroy(backend); 524 | } 525 | 526 | if (new_owner && *new_owner) { 527 | pa_log_debug("oFono appeared"); 528 | hf_audio_agent_register(backend); 529 | } 530 | } 531 | 532 | } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardAdded")) { 533 | const char *p; 534 | DBusMessageIter arg_i, props_i; 535 | 536 | if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) { 537 | pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardAdded"); 538 | goto fail; 539 | } 540 | 541 | dbus_message_iter_get_basic(&arg_i, &p); 542 | 543 | pa_assert_se(dbus_message_iter_next(&arg_i)); 544 | pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY); 545 | 546 | dbus_message_iter_recurse(&arg_i, &props_i); 547 | 548 | hf_audio_agent_card_found(backend, p, &props_i); 549 | } else if (dbus_message_is_signal(m, "org.ofono.HandsfreeAudioManager", "CardRemoved")) { 550 | const char *p; 551 | 552 | if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID)) { 553 | pa_log_error("Failed to parse org.ofono.HandsfreeAudioManager.CardRemoved: %s", err.message); 554 | goto fail; 555 | } 556 | 557 | hf_audio_agent_card_removed(backend, p); 558 | } 559 | 560 | fail: 561 | dbus_error_free(&err); 562 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 563 | } 564 | 565 | static DBusMessage *hf_audio_agent_release(DBusConnection *c, DBusMessage *m, void *data) { 566 | DBusMessage *r; 567 | const char *sender; 568 | pa_bluetooth_backend *backend = data; 569 | 570 | pa_assert(backend); 571 | 572 | sender = dbus_message_get_sender(m); 573 | if (!pa_safe_streq(backend->ofono_bus_id, sender)) { 574 | pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); 575 | return r; 576 | } 577 | 578 | pa_log_debug("HF audio agent has been unregistered by oFono (%s)", backend->ofono_bus_id); 579 | 580 | ofono_bus_id_destroy(backend); 581 | 582 | pa_assert_se(r = dbus_message_new_method_return(m)); 583 | 584 | return r; 585 | } 586 | 587 | static DBusMessage *hf_audio_agent_new_connection(DBusConnection *c, DBusMessage *m, void *data) { 588 | DBusMessage *r; 589 | const char *sender, *path; 590 | int fd; 591 | uint8_t codec; 592 | struct hf_audio_card *card; 593 | pa_bluetooth_backend *backend = data; 594 | 595 | pa_assert(backend); 596 | 597 | sender = dbus_message_get_sender(m); 598 | if (!pa_safe_streq(backend->ofono_bus_id, sender)) { 599 | pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.NotAllowed", "Operation is not allowed by this sender")); 600 | return r; 601 | } 602 | 603 | if (dbus_message_get_args(m, NULL, 604 | DBUS_TYPE_OBJECT_PATH, &path, 605 | DBUS_TYPE_UNIX_FD, &fd, 606 | DBUS_TYPE_BYTE, &codec, 607 | DBUS_TYPE_INVALID) == FALSE) { 608 | pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); 609 | return r; 610 | } 611 | 612 | card = pa_hashmap_get(backend->cards, path); 613 | 614 | card->connecting = false; 615 | 616 | if (!card || codec != HFP_AUDIO_CODEC_CVSD || card->fd >= 0) { 617 | pa_log_warn("New audio connection invalid arguments (path=%s fd=%d, codec=%d)", path, fd, codec); 618 | pa_assert_se(r = dbus_message_new_error(m, "org.ofono.Error.InvalidArguments", "Invalid arguments in method call")); 619 | shutdown(fd, SHUT_RDWR); 620 | close(fd); 621 | return r; 622 | } 623 | 624 | pa_log_debug("New audio connection on card %s (fd=%d, codec=%d)", path, fd, codec); 625 | 626 | card->fd = fd; 627 | card->transport->codec = codec; 628 | 629 | pa_bluetooth_transport_set_state(card->transport, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING); 630 | 631 | pa_assert_se(r = dbus_message_new_method_return(m)); 632 | 633 | return r; 634 | } 635 | 636 | static DBusHandlerResult hf_audio_agent_handler(DBusConnection *c, DBusMessage *m, void *data) { 637 | pa_bluetooth_backend *backend = data; 638 | DBusMessage *r = NULL; 639 | const char *path, *interface, *member; 640 | 641 | pa_assert(backend); 642 | 643 | path = dbus_message_get_path(m); 644 | interface = dbus_message_get_interface(m); 645 | member = dbus_message_get_member(m); 646 | 647 | if (!pa_streq(path, HF_AUDIO_AGENT_PATH)) 648 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 649 | 650 | pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member); 651 | 652 | if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { 653 | const char *xml = HF_AUDIO_AGENT_XML; 654 | 655 | pa_assert_se(r = dbus_message_new_method_return(m)); 656 | pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID)); 657 | 658 | } else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "NewConnection")) 659 | r = hf_audio_agent_new_connection(c, m, data); 660 | else if (dbus_message_is_method_call(m, HF_AUDIO_AGENT_INTERFACE, "Release")) 661 | r = hf_audio_agent_release(c, m, data); 662 | else 663 | return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 664 | 665 | if (r) { 666 | pa_assert_se(dbus_connection_send(pa_dbus_connection_get(backend->connection), r, NULL)); 667 | dbus_message_unref(r); 668 | } 669 | 670 | return DBUS_HANDLER_RESULT_HANDLED; 671 | } 672 | 673 | pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y) { 674 | pa_bluetooth_backend *backend; 675 | DBusError err; 676 | static const DBusObjectPathVTable vtable_hf_audio_agent = { 677 | .message_function = hf_audio_agent_handler, 678 | }; 679 | 680 | pa_assert(c); 681 | 682 | backend = pa_xnew0(pa_bluetooth_backend, 1); 683 | backend->core = c; 684 | backend->discovery = y; 685 | backend->cards = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, 686 | (pa_free_cb_t) hf_audio_card_free); 687 | 688 | dbus_error_init(&err); 689 | 690 | if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) { 691 | pa_log("Failed to get D-Bus connection: %s", err.message); 692 | dbus_error_free(&err); 693 | pa_xfree(backend); 694 | return NULL; 695 | } 696 | 697 | /* dynamic detection of handsfree audio cards */ 698 | if (!dbus_connection_add_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend, NULL)) { 699 | pa_log_error("Failed to add filter function"); 700 | pa_dbus_connection_unref(backend->connection); 701 | pa_xfree(backend); 702 | return NULL; 703 | } 704 | 705 | if (pa_dbus_add_matches(pa_dbus_connection_get(backend->connection), &err, 706 | "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," 707 | "arg0='" OFONO_SERVICE "'", 708 | "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", 709 | "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", 710 | NULL) < 0) { 711 | pa_log("Failed to add oFono D-Bus matches: %s", err.message); 712 | dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); 713 | pa_dbus_connection_unref(backend->connection); 714 | pa_xfree(backend); 715 | return NULL; 716 | } 717 | 718 | pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH, 719 | &vtable_hf_audio_agent, backend)); 720 | 721 | hf_audio_agent_register(backend); 722 | 723 | return backend; 724 | } 725 | 726 | void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *backend) { 727 | pa_assert(backend); 728 | 729 | pa_dbus_free_pending_list(&backend->pending); 730 | 731 | hf_audio_agent_unregister(backend); 732 | 733 | dbus_connection_unregister_object_path(pa_dbus_connection_get(backend->connection), HF_AUDIO_AGENT_PATH); 734 | 735 | pa_dbus_remove_matches(pa_dbus_connection_get(backend->connection), 736 | "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'," 737 | "arg0='" OFONO_SERVICE "'", 738 | "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardAdded'", 739 | "type='signal',sender='" OFONO_SERVICE "',interface='" HF_AUDIO_MANAGER_INTERFACE "',member='CardRemoved'", 740 | NULL); 741 | 742 | dbus_connection_remove_filter(pa_dbus_connection_get(backend->connection), filter_cb, backend); 743 | 744 | pa_dbus_connection_unref(backend->connection); 745 | 746 | pa_hashmap_free(backend->cards); 747 | 748 | pa_xfree(backend); 749 | } 750 | -------------------------------------------------------------------------------- /src/modules/bluetooth/bluez5-util.h: -------------------------------------------------------------------------------- 1 | #ifndef foobluez5utilhfoo 2 | #define foobluez5utilhfoo 3 | 4 | /* 5 | * pulseaudio-modules-bt 6 | * 7 | * Copyright 2008-2013 João Paulo Rechi Vita 8 | * Copyright 2018-2019 Huang-Huang Bao 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | * 23 | */ 24 | 25 | #include 26 | 27 | #include "a2dp/a2dp-api.h" 28 | 29 | #define PA_BLUETOOTH_UUID_A2DP_SOURCE "0000110a-0000-1000-8000-00805f9b34fb" 30 | #define PA_BLUETOOTH_UUID_A2DP_SINK "0000110b-0000-1000-8000-00805f9b34fb" 31 | 32 | /* There are two HSP HS UUIDs. The first one (older?) is used both as the HSP 33 | * profile identifier and as the HS role identifier, while the second one is 34 | * only used to identify the role. As far as PulseAudio is concerned, the two 35 | * UUIDs mean exactly the same thing. */ 36 | #define PA_BLUETOOTH_UUID_HSP_HS "00001108-0000-1000-8000-00805f9b34fb" 37 | #define PA_BLUETOOTH_UUID_HSP_HS_ALT "00001131-0000-1000-8000-00805f9b34fb" 38 | 39 | #define PA_BLUETOOTH_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" 40 | #define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" 41 | #define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" 42 | 43 | typedef struct pa_bluetooth_transport pa_bluetooth_transport; 44 | typedef struct pa_bluetooth_stream_endpoint pa_bluetooth_stream_endpoint; 45 | typedef struct pa_bluetooth_device pa_bluetooth_device; 46 | typedef struct pa_bluetooth_adapter pa_bluetooth_adapter; 47 | typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; 48 | typedef struct pa_bluetooth_backend pa_bluetooth_backend; 49 | 50 | typedef enum pa_bluetooth_hook { 51 | PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ 52 | PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ 53 | PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ 54 | PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ 55 | PA_BLUETOOTH_HOOK_MAX 56 | } pa_bluetooth_hook_t; 57 | 58 | typedef enum profile { 59 | PA_BLUETOOTH_PROFILE_A2DP_SINK, 60 | PA_BLUETOOTH_PROFILE_A2DP_SOURCE, 61 | PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT, 62 | PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY, 63 | PA_BLUETOOTH_PROFILE_OFF 64 | } pa_bluetooth_profile_t; 65 | #define PA_BLUETOOTH_PROFILE_COUNT PA_BLUETOOTH_PROFILE_OFF 66 | 67 | typedef enum pa_bluetooth_transport_state { 68 | PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED, 69 | PA_BLUETOOTH_TRANSPORT_STATE_IDLE, 70 | PA_BLUETOOTH_TRANSPORT_STATE_PLAYING 71 | } pa_bluetooth_transport_state_t; 72 | 73 | typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu); 74 | typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t); 75 | typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t); 76 | typedef void (*pa_bluetooth_transport_set_speaker_gain_cb)(pa_bluetooth_transport *t, uint16_t gain); 77 | typedef void (*pa_bluetooth_transport_set_microphone_gain_cb)(pa_bluetooth_transport *t, uint16_t gain); 78 | 79 | struct pa_bluetooth_transport { 80 | pa_bluetooth_device *device; 81 | 82 | char *owner; 83 | char *path; 84 | pa_bluetooth_profile_t profile; 85 | 86 | const pa_a2dp_codec_t *a2dp_codec; 87 | pa_a2dp_sink_t *a2dp_sink; 88 | pa_a2dp_source_t *a2dp_source; 89 | 90 | uint8_t codec; 91 | uint8_t *config; 92 | size_t config_size; 93 | 94 | uint16_t microphone_gain; 95 | uint16_t speaker_gain; 96 | 97 | pa_bluetooth_transport_state_t state; 98 | 99 | pa_bluetooth_transport_acquire_cb acquire; 100 | pa_bluetooth_transport_release_cb release; 101 | pa_bluetooth_transport_destroy_cb destroy; 102 | pa_bluetooth_transport_set_speaker_gain_cb set_speaker_gain; 103 | pa_bluetooth_transport_set_microphone_gain_cb set_microphone_gain; 104 | void *userdata; 105 | }; 106 | 107 | struct pa_bluetooth_stream_endpoint { 108 | pa_bluetooth_discovery *discovery; 109 | pa_bluetooth_device *device; 110 | 111 | char *path; 112 | char *device_path; 113 | char *uuid; 114 | uint8_t codec; 115 | uint8_t *config; 116 | size_t config_size; 117 | 118 | const pa_a2dp_codec_t *a2dp_codec; 119 | pa_a2dp_codec_index_t codec_index; 120 | bool valid; 121 | }; 122 | 123 | struct pa_bluetooth_device { 124 | pa_bluetooth_discovery *discovery; 125 | pa_bluetooth_adapter *adapter; 126 | 127 | bool properties_received; 128 | bool tried_to_link_with_adapter; 129 | bool valid; 130 | bool autodetect_mtu; 131 | 132 | bool sep_setting_configuration; 133 | 134 | /* Device information */ 135 | char *path; 136 | char *adapter_path; 137 | char *alias; 138 | char *address; 139 | uint32_t class_of_device; 140 | pa_hashmap *uuids; /* char* -> char* (hashmap-as-a-set) */ 141 | 142 | pa_bluetooth_transport *transports[PA_BLUETOOTH_PROFILE_COUNT]; 143 | 144 | pa_time_event *wait_for_profiles_timer; 145 | }; 146 | 147 | struct pa_bluetooth_adapter { 148 | pa_bluetooth_discovery *discovery; 149 | char *path; 150 | char *address; 151 | 152 | bool valid; 153 | }; 154 | 155 | #ifdef HAVE_BLUEZ_5_OFONO_HEADSET 156 | pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y); 157 | void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b); 158 | #else 159 | static inline pa_bluetooth_backend *pa_bluetooth_ofono_backend_new(pa_core *c, pa_bluetooth_discovery *y) { 160 | return NULL; 161 | } 162 | static inline void pa_bluetooth_ofono_backend_free(pa_bluetooth_backend *b) {} 163 | #endif 164 | 165 | #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET 166 | pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role); 167 | void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b); 168 | void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role); 169 | #else 170 | static inline pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_hs_role) { 171 | return NULL; 172 | } 173 | static inline void pa_bluetooth_native_backend_free(pa_bluetooth_backend *b) {} 174 | static inline void pa_bluetooth_native_backend_enable_hs_role(pa_bluetooth_backend *b, bool enable_hs_role) {} 175 | #endif 176 | 177 | pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path, 178 | pa_bluetooth_profile_t p, const uint8_t *config, size_t size); 179 | 180 | void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state); 181 | void pa_bluetooth_transport_put(pa_bluetooth_transport *t); 182 | void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t); 183 | void pa_bluetooth_transport_free(pa_bluetooth_transport *t); 184 | 185 | bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d); 186 | 187 | pa_bluetooth_stream_endpoint *pa_bluetooth_device_get_sep_by_codec_index(pa_bluetooth_device *d, 188 | pa_a2dp_codec_index_t codec_index); 189 | 190 | void pa_bluetooth_sep_set_configuration(pa_bluetooth_stream_endpoint *sep, pa_sample_spec default_sample_spec, void (*cb)(bool success, void *data), void *cb_data); 191 | 192 | pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path); 193 | pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local); 194 | 195 | pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook); 196 | 197 | const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile); 198 | const char *pa_bluetooth_a2dp_profile_to_string(pa_a2dp_codec_index_t codec_index); 199 | const char *pa_bluetooth_profile_codec_to_string(pa_bluetooth_profile_t profile, const pa_a2dp_codec_t *a2dp_codec); 200 | 201 | static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) { 202 | return pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS_ALT); 203 | } 204 | 205 | #define HEADSET_BACKEND_OFONO 0 206 | #define HEADSET_BACKEND_NATIVE 1 207 | #define HEADSET_BACKEND_AUTO 2 208 | 209 | pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *core, int headset_backend); 210 | pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y); 211 | void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y); 212 | void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running); 213 | #endif 214 | -------------------------------------------------------------------------------- /src/modules/bluetooth/module-bluetooth-discover.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2013 João Paulo Rechi Vita 5 | * Copyright 2018-2019 Huang-Huang Bao 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | PA_MODULE_AUTHOR("João Paulo Rechi Vita"); 30 | PA_MODULE_DESCRIPTION("Detect available Bluetooth daemon and load the corresponding discovery module"); 31 | PA_MODULE_VERSION(PACKAGE_VERSION); 32 | PA_MODULE_LOAD_ONCE(true); 33 | PA_MODULE_USAGE( 34 | "headset=ofono|native|auto (bluez 5 only)" 35 | "autodetect_mtu= (bluez 5 only)" 36 | ); 37 | 38 | struct userdata { 39 | uint32_t bluez5_module_idx; 40 | }; 41 | 42 | int pa__init(pa_module* m) { 43 | struct userdata *u; 44 | pa_module *mm; 45 | 46 | pa_assert(m); 47 | 48 | m->userdata = u = pa_xnew0(struct userdata, 1); 49 | u->bluez5_module_idx = PA_INVALID_INDEX; 50 | 51 | if (pa_module_exists("module-bluez5-discover")) { 52 | pa_module_load(&mm, m->core, "module-bluez5-discover", m->argument); 53 | if (mm) 54 | u->bluez5_module_idx = mm->index; 55 | } 56 | 57 | if (u->bluez5_module_idx == PA_INVALID_INDEX) { 58 | pa_xfree(u); 59 | return -1; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | void pa__done(pa_module* m) { 66 | struct userdata *u; 67 | 68 | pa_assert(m); 69 | 70 | if (!(u = m->userdata)) 71 | return; 72 | 73 | if (u->bluez5_module_idx != PA_INVALID_INDEX) 74 | pa_module_unload_by_index(m->core, u->bluez5_module_idx, true); 75 | 76 | pa_xfree(u); 77 | } 78 | -------------------------------------------------------------------------------- /src/modules/bluetooth/module-bluetooth-policy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2006 Lennart Poettering 5 | * Copyright 2009 Canonical Ltd 6 | * Copyright (C) 2012 Intel Corporation 7 | * Copyright 2018-2019 Huang-Huang Bao 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include 26 | #endif 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define pa_bt_prefix_eq(a,b) (pa_strneq((a),(b),(PA_MIN((strlen((a))),(strlen((b))))))) 37 | 38 | PA_MODULE_AUTHOR("Frédéric Dalleau, Pali Rohár"); 39 | PA_MODULE_DESCRIPTION("Policy module to make using bluetooth devices out-of-the-box easier"); 40 | PA_MODULE_VERSION(PACKAGE_VERSION); 41 | PA_MODULE_LOAD_ONCE(true); 42 | PA_MODULE_USAGE( 43 | "auto_switch= " 44 | "a2dp_source= " 45 | "ag= "); 46 | 47 | static const char* const valid_modargs[] = { 48 | "auto_switch", 49 | "a2dp_source", 50 | "ag", 51 | "hfgw", 52 | NULL 53 | }; 54 | 55 | struct userdata { 56 | uint32_t auto_switch; 57 | bool enable_a2dp_source; 58 | bool enable_ag; 59 | pa_hook_slot *source_put_slot; 60 | pa_hook_slot *sink_put_slot; 61 | pa_hook_slot *source_output_put_slot; 62 | pa_hook_slot *source_output_unlink_slot; 63 | pa_hook_slot *card_init_profile_slot; 64 | pa_hook_slot *card_unlink_slot; 65 | pa_hook_slot *profile_available_changed_slot; 66 | /** Map between cards and their previous profile. */ 67 | pa_hashmap *old_profile_card_map; 68 | }; 69 | 70 | /* When a source is created, loopback it to default sink */ 71 | static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, void *userdata) { 72 | struct userdata *u = userdata; 73 | const char *s; 74 | const char *role; 75 | char *args; 76 | pa_module *m = NULL; 77 | 78 | pa_assert(c); 79 | pa_assert(source); 80 | 81 | /* Only consider bluetooth sinks and sources */ 82 | s = pa_proplist_gets(source->proplist, PA_PROP_DEVICE_BUS); 83 | if (!s) 84 | return PA_HOOK_OK; 85 | 86 | if (!pa_streq(s, "bluetooth")) 87 | return PA_HOOK_OK; 88 | 89 | s = pa_proplist_gets(source->proplist, "bluetooth.protocol"); 90 | if (!s) 91 | return PA_HOOK_OK; 92 | 93 | if (u->enable_a2dp_source && pa_bt_prefix_eq(s, "a2dp_source")) 94 | role = "music"; 95 | else if (u->enable_ag && pa_streq(s, "headset_audio_gateway")) 96 | role = "phone"; 97 | else { 98 | pa_log_debug("Profile %s cannot be selected for loopback", s); 99 | return PA_HOOK_OK; 100 | } 101 | 102 | /* Load module-loopback */ 103 | args = pa_sprintf_malloc("source=\"%s\" source_dont_move=\"true\" sink_input_properties=\"media.role=%s\"", source->name, 104 | role); 105 | (void) pa_module_load(&m, c, "module-loopback", args); 106 | pa_xfree(args); 107 | 108 | return PA_HOOK_OK; 109 | } 110 | 111 | /* When a sink is created, loopback it to default source */ 112 | static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void *userdata) { 113 | struct userdata *u = userdata; 114 | const char *s; 115 | const char *role; 116 | char *args; 117 | pa_module *m = NULL; 118 | 119 | pa_assert(c); 120 | pa_assert(sink); 121 | 122 | /* Only consider bluetooth sinks and sources */ 123 | s = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_BUS); 124 | if (!s) 125 | return PA_HOOK_OK; 126 | 127 | if (!pa_streq(s, "bluetooth")) 128 | return PA_HOOK_OK; 129 | 130 | s = pa_proplist_gets(sink->proplist, "bluetooth.protocol"); 131 | if (!s) 132 | return PA_HOOK_OK; 133 | 134 | if (u->enable_ag && pa_streq(s, "headset_audio_gateway")) 135 | role = "phone"; 136 | else { 137 | pa_log_debug("Profile %s cannot be selected for loopback", s); 138 | return PA_HOOK_OK; 139 | } 140 | 141 | /* Load module-loopback */ 142 | args = pa_sprintf_malloc("sink=\"%s\" sink_dont_move=\"true\" source_output_properties=\"media.role=%s\"", sink->name, 143 | role); 144 | (void) pa_module_load(&m, c, "module-loopback", args); 145 | pa_xfree(args); 146 | 147 | return PA_HOOK_OK; 148 | } 149 | 150 | static void card_set_profile(struct userdata *u, pa_card *card, bool revert_to_a2dp, const char* new_profile) 151 | { 152 | pa_card_profile *profile; 153 | void *state; 154 | 155 | /* The revert_to_a2dp and profile parameter are mutually exclusive. */ 156 | pa_assert(revert_to_a2dp != (!new_profile)); 157 | char* current_profile = pa_xstrdup(card->active_profile->name); 158 | bool switched = false; 159 | 160 | /* Find available profile and activate it */ 161 | PA_HASHMAP_FOREACH(profile, card->profiles, state) { 162 | if (profile->available == PA_AVAILABLE_NO) 163 | continue; 164 | 165 | /* Check for correct profile based on revert_to_a2dp */ 166 | if (revert_to_a2dp) { 167 | if (!pa_streq(profile->name, new_profile)) 168 | continue; 169 | } else { 170 | if (!pa_streq(profile->name, "hsp") && !pa_streq(profile->name, "headset_head_unit")) 171 | continue; 172 | } 173 | 174 | pa_log_debug("Setting card '%s' to profile '%s'", card->name, profile->name); 175 | 176 | if (pa_card_set_profile(card, profile, false) != 0) { 177 | pa_log_warn("Could not set profile '%s'", profile->name); 178 | continue; 179 | } 180 | switched = true; 181 | break; 182 | } 183 | /* 184 | * When we are not in revert_to_a2dp phase flag that this card will need a revert. 185 | * Save the old profile. 186 | */ 187 | if (switched && !revert_to_a2dp) { 188 | pa_hashmap_put(u->old_profile_card_map, card, current_profile); 189 | } else { 190 | free(current_profile); 191 | } 192 | } 193 | 194 | /* Switch profile for one card */ 195 | static void switch_profile(pa_card *card, bool revert_to_a2dp, void *userdata) { 196 | struct userdata *u = userdata; 197 | const char *s; 198 | const char *old_profile = NULL; 199 | 200 | /* Only consider bluetooth cards */ 201 | s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS); 202 | if (!s || !pa_streq(s, "bluetooth")) 203 | return; 204 | 205 | if (revert_to_a2dp) { 206 | /* In revert_to_a2dp phase only consider cards with an old profile stored and remove it. */ 207 | if (!(old_profile = pa_hashmap_get(u->old_profile_card_map, card))) 208 | goto fail; 209 | 210 | /* Skip card if does not have active hsp profile */ 211 | if (!pa_streq(card->active_profile->name, "hsp") && !pa_streq(card->active_profile->name, "headset_head_unit")) 212 | goto fail; 213 | 214 | /* Skip card if already has active a2dp profile */ 215 | if (pa_streq(card->active_profile->name, "a2dp") || pa_strneq(card->active_profile->name, "a2dp_sink", strlen("a2dp_sink"))) 216 | goto fail; 217 | } else { 218 | /* Skip card if does not have active a2dp profile */ 219 | if (!pa_streq(card->active_profile->name, "a2dp") && !pa_bt_prefix_eq(card->active_profile->name, "a2dp_sink")) 220 | goto fail; 221 | 222 | /* Skip card if already has active hsp profile */ 223 | if (pa_streq(card->active_profile->name, "hsp") || pa_streq(card->active_profile->name, "headset_head_unit")) 224 | goto fail; 225 | } 226 | 227 | card_set_profile(u, card, revert_to_a2dp, old_profile); 228 | fail: 229 | if (revert_to_a2dp) { 230 | pa_hashmap_remove_and_free(u->old_profile_card_map, card); 231 | } 232 | } 233 | 234 | /* Return true if we should ignore this source output */ 235 | static bool ignore_output(pa_source_output *source_output, void *userdata) { 236 | struct userdata *u = userdata; 237 | const char *s; 238 | 239 | /* New applications could set media.role for identifying streams */ 240 | /* We are interested only in media.role=phone */ 241 | s = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE); 242 | if (s) 243 | return !pa_streq(s, "phone"); 244 | 245 | /* If media.role is not set use some heuristic (if enabled) */ 246 | if (u->auto_switch != 2) 247 | return true; 248 | 249 | /* Ignore if resample method is peaks (used by desktop volume programs) */ 250 | if (pa_source_output_get_resample_method(source_output) == PA_RESAMPLER_PEAKS) 251 | return true; 252 | 253 | /* Ignore if there is no client/application assigned (used by virtual stream) */ 254 | if (!source_output->client) 255 | return true; 256 | 257 | /* Ignore if recording from monitor of sink */ 258 | if (source_output->direct_on_input) 259 | return true; 260 | 261 | return false; 262 | } 263 | 264 | static unsigned source_output_count(pa_core *c, void *userdata) { 265 | pa_source_output *source_output; 266 | uint32_t idx; 267 | unsigned count = 0; 268 | 269 | PA_IDXSET_FOREACH(source_output, c->source_outputs, idx) 270 | if (!ignore_output(source_output, userdata)) 271 | ++count; 272 | 273 | return count; 274 | } 275 | 276 | /* Switch profile for all cards */ 277 | static void switch_profile_all(pa_idxset *cards, bool revert_to_a2dp, void *userdata) { 278 | pa_card *card; 279 | uint32_t idx; 280 | 281 | PA_IDXSET_FOREACH(card, cards, idx) 282 | switch_profile(card, revert_to_a2dp, userdata); 283 | } 284 | 285 | /* When a source output is created, switch profile a2dp to profile hsp */ 286 | static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) { 287 | pa_assert(c); 288 | pa_assert(source_output); 289 | 290 | if (ignore_output(source_output, userdata)) 291 | return PA_HOOK_OK; 292 | 293 | switch_profile_all(c->cards, false, userdata); 294 | return PA_HOOK_OK; 295 | } 296 | 297 | /* When all source outputs are unlinked, switch profile hsp back back to profile a2dp */ 298 | static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, void *userdata) { 299 | pa_assert(c); 300 | pa_assert(source_output); 301 | 302 | if (ignore_output(source_output, userdata)) 303 | return PA_HOOK_OK; 304 | 305 | /* If there are still some source outputs do nothing. */ 306 | if (source_output_count(c, userdata) > 0) 307 | return PA_HOOK_OK; 308 | 309 | switch_profile_all(c->cards, true, userdata); 310 | return PA_HOOK_OK; 311 | } 312 | 313 | static pa_hook_result_t card_init_profile_hook_callback(pa_core *c, pa_card *card, void *userdata) { 314 | struct userdata *u = userdata; 315 | const char *s; 316 | 317 | pa_assert(c); 318 | pa_assert(card); 319 | 320 | if (source_output_count(c, userdata) == 0) 321 | return PA_HOOK_OK; 322 | 323 | /* Only consider bluetooth cards */ 324 | s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS); 325 | if (!s || !pa_streq(s, "bluetooth")) 326 | return PA_HOOK_OK; 327 | 328 | /* Ignore card if has already set other initial profile than a2dp */ 329 | if (card->active_profile && 330 | !pa_streq(card->active_profile->name, "a2dp") && 331 | !pa_bt_prefix_eq(card->active_profile->name, "a2dp_sink")) 332 | return PA_HOOK_OK; 333 | 334 | /* Set initial profile to hsp */ 335 | card_set_profile(u, card, false, NULL); 336 | return PA_HOOK_OK; 337 | } 338 | 339 | static pa_hook_result_t card_unlink_hook_callback(pa_core *c, pa_card *card, void *userdata) { 340 | pa_assert(c); 341 | pa_assert(card); 342 | switch_profile(card, true, userdata); 343 | return PA_HOOK_OK; 344 | } 345 | 346 | static pa_card_profile *find_best_profile(pa_card *card) { 347 | void *state; 348 | pa_card_profile *profile; 349 | pa_card_profile *result = card->active_profile; 350 | 351 | PA_HASHMAP_FOREACH(profile, card->profiles, state) { 352 | if (profile->available == PA_AVAILABLE_NO) 353 | continue; 354 | 355 | if (result == NULL || 356 | (profile->available == PA_AVAILABLE_YES && result->available == PA_AVAILABLE_UNKNOWN) || 357 | (profile->available == result->available && profile->priority > result->priority)) 358 | result = profile; 359 | } 360 | 361 | return result; 362 | } 363 | 364 | static pa_hook_result_t profile_available_hook_callback(pa_core *c, pa_card_profile *profile, void *userdata) { 365 | pa_card *card; 366 | const char *s; 367 | bool is_active_profile; 368 | pa_card_profile *selected_profile; 369 | 370 | pa_assert(c); 371 | pa_assert(profile); 372 | pa_assert_se((card = profile->card)); 373 | 374 | /* Only consider bluetooth cards */ 375 | s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS); 376 | if (!s || !pa_streq(s, "bluetooth")) 377 | return PA_HOOK_OK; 378 | 379 | /* Do not automatically switch profiles for headsets, just in case */ 380 | if (pa_bt_prefix_eq(profile->name, "a2dp_sink") || pa_streq(profile->name, "headset_head_unit")) 381 | return PA_HOOK_OK; 382 | 383 | is_active_profile = card->active_profile == profile; 384 | 385 | if (profile->available == PA_AVAILABLE_YES) { 386 | if (is_active_profile) 387 | return PA_HOOK_OK; 388 | 389 | if (card->active_profile->available == PA_AVAILABLE_YES && card->active_profile->priority >= profile->priority) 390 | return PA_HOOK_OK; 391 | 392 | selected_profile = profile; 393 | } else { 394 | if (!is_active_profile) 395 | return PA_HOOK_OK; 396 | 397 | pa_assert_se((selected_profile = find_best_profile(card))); 398 | 399 | if (selected_profile == card->active_profile) 400 | return PA_HOOK_OK; 401 | } 402 | 403 | pa_log_debug("Setting card '%s' to profile '%s'", card->name, selected_profile->name); 404 | 405 | if (pa_card_set_profile(card, selected_profile, false) != 0) 406 | pa_log_warn("Could not set profile '%s'", selected_profile->name); 407 | 408 | return PA_HOOK_OK; 409 | } 410 | 411 | static void handle_all_profiles(pa_core *core) { 412 | pa_card *card; 413 | uint32_t state; 414 | 415 | PA_IDXSET_FOREACH(card, core->cards, state) { 416 | pa_card_profile *profile; 417 | void *state2; 418 | 419 | PA_HASHMAP_FOREACH(profile, card->profiles, state2) 420 | profile_available_hook_callback(core, profile, NULL); 421 | } 422 | } 423 | 424 | int pa__init(pa_module *m) { 425 | pa_modargs *ma; 426 | struct userdata *u; 427 | 428 | pa_assert(m); 429 | 430 | if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 431 | pa_log_error("Failed to parse module arguments"); 432 | goto fail; 433 | } 434 | 435 | m->userdata = u = pa_xnew0(struct userdata, 1); 436 | 437 | u->auto_switch = 1; 438 | 439 | if (pa_modargs_get_value(ma, "auto_switch", NULL)) { 440 | bool auto_switch_bool; 441 | 442 | /* auto_switch originally took a boolean value, let's keep 443 | * compatibility with configuration files that still pass a boolean. */ 444 | if (pa_modargs_get_value_boolean(ma, "auto_switch", &auto_switch_bool) >= 0) { 445 | if (auto_switch_bool) 446 | u->auto_switch = 1; 447 | else 448 | u->auto_switch = 0; 449 | 450 | } else if (pa_modargs_get_value_u32(ma, "auto_switch", &u->auto_switch) < 0) { 451 | pa_log("Failed to parse auto_switch argument."); 452 | goto fail; 453 | } 454 | } 455 | 456 | u->enable_a2dp_source = true; 457 | if (pa_modargs_get_value_boolean(ma, "a2dp_source", &u->enable_a2dp_source) < 0) { 458 | pa_log("Failed to parse a2dp_source argument."); 459 | goto fail; 460 | } 461 | 462 | u->enable_ag = true; 463 | if (pa_modargs_get_value_boolean(ma, "ag", &u->enable_ag) < 0) { 464 | pa_log("Failed to parse ag argument."); 465 | goto fail; 466 | } 467 | 468 | u->old_profile_card_map = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); 469 | 470 | u->source_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_NORMAL, 471 | (pa_hook_cb_t) source_put_hook_callback, u); 472 | 473 | u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_NORMAL, 474 | (pa_hook_cb_t) sink_put_hook_callback, u); 475 | 476 | if (u->auto_switch) { 477 | u->source_output_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_NORMAL, 478 | (pa_hook_cb_t) source_output_put_hook_callback, u); 479 | 480 | u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, 481 | (pa_hook_cb_t) source_output_unlink_hook_callback, u); 482 | 483 | u->card_init_profile_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_CHOOSE_INITIAL_PROFILE], PA_HOOK_NORMAL, 484 | (pa_hook_cb_t) card_init_profile_hook_callback, u); 485 | 486 | u->card_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_UNLINK], PA_HOOK_NORMAL, 487 | (pa_hook_cb_t) card_unlink_hook_callback, u); 488 | } 489 | 490 | u->profile_available_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED], 491 | PA_HOOK_NORMAL, (pa_hook_cb_t) profile_available_hook_callback, u); 492 | 493 | handle_all_profiles(m->core); 494 | 495 | pa_modargs_free(ma); 496 | return 0; 497 | 498 | fail: 499 | if (ma) 500 | pa_modargs_free(ma); 501 | return -1; 502 | } 503 | 504 | void pa__done(pa_module *m) { 505 | struct userdata *u; 506 | 507 | pa_assert(m); 508 | 509 | if (!(u = m->userdata)) 510 | return; 511 | 512 | if (u->source_put_slot) 513 | pa_hook_slot_free(u->source_put_slot); 514 | 515 | if (u->sink_put_slot) 516 | pa_hook_slot_free(u->sink_put_slot); 517 | 518 | if (u->source_output_put_slot) 519 | pa_hook_slot_free(u->source_output_put_slot); 520 | 521 | if (u->source_output_unlink_slot) 522 | pa_hook_slot_free(u->source_output_unlink_slot); 523 | 524 | if (u->card_init_profile_slot) 525 | pa_hook_slot_free(u->card_init_profile_slot); 526 | 527 | if (u->card_unlink_slot) 528 | pa_hook_slot_free(u->card_unlink_slot); 529 | 530 | if (u->profile_available_changed_slot) 531 | pa_hook_slot_free(u->profile_available_changed_slot); 532 | 533 | pa_hashmap_free(u->old_profile_card_map); 534 | 535 | pa_xfree(u); 536 | } 537 | -------------------------------------------------------------------------------- /src/modules/bluetooth/module-bluez5-discover.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulseaudio-modules-bt 3 | * 4 | * Copyright 2008-2013 João Paulo Rechi Vita 5 | * Copyright 2018-2019 Huang-Huang Bao 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include 24 | #endif 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "bluez5-util.h" 34 | 35 | PA_MODULE_AUTHOR("João Paulo Rechi Vita"); 36 | PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers"); 37 | PA_MODULE_VERSION(PACKAGE_VERSION); 38 | PA_MODULE_LOAD_ONCE(true); 39 | PA_MODULE_USAGE( 40 | "headset=ofono|native|auto" 41 | ); 42 | 43 | static const char* const valid_modargs[] = { 44 | "headset", 45 | "autodetect_mtu", 46 | "a2dp_config", 47 | NULL 48 | }; 49 | 50 | struct userdata { 51 | pa_module *module; 52 | pa_core *core; 53 | pa_hashmap *loaded_device_paths; 54 | pa_hook_slot *device_connection_changed_slot; 55 | pa_bluetooth_discovery *discovery; 56 | bool autodetect_mtu; 57 | const char *a2dp_config; 58 | }; 59 | 60 | static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) { 61 | bool module_loaded; 62 | 63 | pa_assert(d); 64 | pa_assert(u); 65 | 66 | module_loaded = pa_hashmap_get(u->loaded_device_paths, d->path) ? true : false; 67 | 68 | if (module_loaded && !pa_bluetooth_device_any_transport_connected(d)) { 69 | /* disconnection, the module unloads itself */ 70 | pa_log_debug("Unregistering module for %s", d->path); 71 | pa_hashmap_remove(u->loaded_device_paths, d->path); 72 | return PA_HOOK_OK; 73 | } 74 | 75 | if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) { 76 | /* a new device has been connected */ 77 | pa_module *m; 78 | char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i a2dp_config=\"%s\"", 79 | d->path, (int) u->autodetect_mtu, u->a2dp_config); 80 | 81 | pa_log_debug("Loading module-bluez5-device %s", args); 82 | pa_module_load(&m, u->module->core, "module-bluez5-device", args); 83 | pa_xfree(args); 84 | 85 | if (m) 86 | /* No need to duplicate the path here since the device object will 87 | * exist for the whole hashmap entry lifespan */ 88 | pa_hashmap_put(u->loaded_device_paths, d->path, d->path); 89 | else 90 | pa_log_warn("Failed to load module for device %s", d->path); 91 | 92 | return PA_HOOK_OK; 93 | } 94 | 95 | return PA_HOOK_OK; 96 | } 97 | 98 | #ifdef HAVE_BLUEZ_5_NATIVE_HEADSET 99 | const char *default_headset_backend = "auto"; 100 | #else 101 | const char *default_headset_backend = "ofono"; 102 | #endif 103 | 104 | int pa__init(pa_module *m) { 105 | struct userdata *u; 106 | pa_modargs *ma; 107 | const char *headset_str, *a2dp_config; 108 | int headset_backend; 109 | bool autodetect_mtu; 110 | 111 | pa_assert(m); 112 | 113 | if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 114 | pa_log("failed to parse module arguments."); 115 | goto fail; 116 | } 117 | 118 | pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend)); 119 | if (pa_streq(headset_str, "ofono")) 120 | headset_backend = HEADSET_BACKEND_OFONO; 121 | else if (pa_streq(headset_str, "native")) 122 | headset_backend = HEADSET_BACKEND_NATIVE; 123 | else if (pa_streq(headset_str, "auto")) 124 | headset_backend = HEADSET_BACKEND_AUTO; 125 | else { 126 | pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str); 127 | goto fail; 128 | } 129 | 130 | autodetect_mtu = true; 131 | if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) { 132 | pa_log("Invalid boolean value for autodetect_mtu parameter"); 133 | goto fail; 134 | } 135 | 136 | pa_assert_se(a2dp_config = pa_modargs_get_value(ma, "a2dp_config", "")); 137 | 138 | 139 | m->userdata = u = pa_xnew0(struct userdata, 1); 140 | u->module = m; 141 | u->core = m->core; 142 | u->autodetect_mtu = autodetect_mtu; 143 | u->a2dp_config = pa_xmemdup(a2dp_config, strlen(a2dp_config) + 1); 144 | u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); 145 | 146 | if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend))) 147 | goto fail; 148 | 149 | u->device_connection_changed_slot = 150 | pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED), 151 | PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u); 152 | 153 | pa_modargs_free(ma); 154 | return 0; 155 | 156 | fail: 157 | if (ma) 158 | pa_modargs_free(ma); 159 | pa__done(m); 160 | return -1; 161 | } 162 | 163 | void pa__done(pa_module *m) { 164 | struct userdata *u; 165 | 166 | pa_assert(m); 167 | 168 | if (!(u = m->userdata)) 169 | return; 170 | 171 | if (u->device_connection_changed_slot) 172 | pa_hook_slot_free(u->device_connection_changed_slot); 173 | 174 | if (u->discovery) 175 | pa_bluetooth_discovery_unref(u->discovery); 176 | 177 | if (u->loaded_device_paths) 178 | pa_hashmap_free(u->loaded_device_paths); 179 | 180 | if (u->a2dp_config) 181 | pa_xfree((void *) u->a2dp_config); 182 | 183 | pa_xfree(u); 184 | } 185 | --------------------------------------------------------------------------------