├── .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 |
--------------------------------------------------------------------------------