bluez5.50+pulseaudio实现蓝牙音响音频播放
环境介绍
执行流程
蓝牙服务配置和启动流程
蓝牙连接
音频播放
问题记录
环境介绍
硬件环境:
- 开发板使用全志 T507 作为主控芯片
- WiFi蓝牙模块采用移远FC21、AP6236,操作方式一样,实现结果相同。(区别是不同的蓝牙模块在初始化和上电的操作略有不同)
软件环境:
- bluez-5.50
- pulseaudio-12.2
- dbus-1.12.10
执行流程
参考以下文章完成对 bluez 蓝牙协议栈的编译和部署:
手把手教你移植bluez 5.47蓝牙协议栈
参考文章里使用的是 bluez5.47的版本,和我这边使用的 5.50 操作是一样的。目前对于 bluez版本主要区别在于小于5的版本(3.xx-4.xx)和bluez5(大于5.0)版本。
bluez 旧版本(3.xx-4.xx)下结合了应用层的部分协议,所以在增加库依赖后可以仅通过 bluez 完成蓝牙设备的连接;
而 bluez-5 新版本之后,将蓝牙应用层协议剥离开,仅有 bluez 是无法正常完成蓝牙多媒体设备的连接的(会报无应用协议支持而断开),所以在 bluez-5 之后,对于蓝牙音响的使用,就需要借助 pulseaudio 或者 bluealse 等音频服务完成对蓝牙音频设备的连接。
蓝牙服务配置和启动流程
修改 pulseaudio 的部分默认配置,
【/etc/pulse/default.pa】
【/etc/pulse/system.pa】
注释掉其中的以下模块:
load-module module-suspend-on-idle load-module module-bluetooth-policy
查看蓝牙设备是否已经存在
# hciconfig -a hci0: Type: Primary Bus: UART BD Address: BE:5A:D6:6A:9D:5F ACL MTU: 1021:7 SCO MTU: 64:1 UP RUNNING PSCAN RX bytes:244301 acl:651 sco:0 events:27591 errors:0 TX bytes:34440936 acl:54726 sco:0 commands:132 errors:0 Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3 Link policy: RSWITCH SNIFF Link mode: SLAVE ACCEPT Name: 'BlueZ 5.50' Class: 0x0c0000 Service Classes: Rendering, Capturing Device Class: Miscellaneous, HCI Version: 4.2 (0x8) Revision: 0x70 LMP Version: 4.2 (0x8) Subversion: 0x410c Manufacturer: Broadcom Corporation (15)
如果不存在,则检查蓝牙模块初始化部分是否正常。
蓝牙设备已经存在后,执行以下启动脚本:
#!/bin/sh btmgmt -i hci0 name "wwtest" btmgmt -i hci0 connectable on btmgmt -i hci0 le on btmgmt -i hci0 advertising on btmgmt -i hci0 power on sdptool add --channel=1 GATT SP A2SNK A2DP btgatt-server & /usr/libexec/bluetooth/bluetoothd -d -C -n & pulseaudio -D --exit-idle-time=-1 --start --log-target=file:/tmp/pulse.log
这里 /usr/libexec/bluetooth/bluetoothd 使用 -d 参数打开了 debug 选项,如果后续一切正常,可以去掉这个参数,就不会显示很多信息了。
蓝牙连接
蓝牙设备连接这里可以使用 bluetoothctl 和 hcitool 这两种工具,bluetoothctl 是 bluez 自带的蓝牙调试工具,具体连接方式和过程如下:
# bluetoothctl bluetoothd[8116]: src/agent.c:agent_ref() 0x298260a0: ref=1 bluetoothd[8116]: src/agent.c:register_agent() agent :1.9 Agent registered [bluetooth]# show Controller BE:5A:D6:6A:9D:5F (public) Name: BlueZ 5.50 Alias: BlueZ 5.50 Class: 0x000c0000 Powered: yes Discoverable: no Pairable: yes UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb) UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb) UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb) UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) Modalias: usb:v1D6Bp0246d0532 Discovering: no [bluetooth]# devices Device 60:AA:EF:B4:89:8A HUAWEI P40 Device 28:37:13:B0:F1:D7 HUAWEI CM510 [bluetooth]# connect 28:37:13:B0:F1:D7 Attempting to connect to 28:37:13:B0:F1:D7 bluetoothd[8116]: src/device.c:connect_profiles() /org/bluez/hci0/dev_28_37_13_B0_F1_D7 (all), client :1.10 bluetoothd[8116]: profiles/audio/a2dp.c:a2dp_sink_connect() path /org/bluez/hci0/dev_28_37_13_B0_F1_D7 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_ref() 0x298480f0: ref=1 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_ref() 0x298480f0: ref=2 bluetoothd[8116]: profiles/audio/a2dp.c:setup_ref() 0x29825ed0: ref=1 bluetoothd[8116]: profiles/audio/source.c:source_set_state() State changed /org/bluez/hci0/dev_28_37_13_B0_F1_D7: SOURCE_STATE_DISCONNECTED -> SOURCE_STATE_CONNECTING bluetoothd[8116]: profiles/audio/sink.c:sink_set_state() State changed /org/bluez/hci0/dev_28_37_13_B0_F1_D7: SINK_STATE_DISCONNECTED -> SINK_STATE_CONNECTING bluetoothd[8116]: profiles/audio/sink.c:sink_connect() stream creation in progress bluetoothd[8116]: src/service.c:change_state() 0x29829e80: device 28:37:13:B0:F1:D7 profile a2dp-sink state changed: disconnected -> connecting (0) [bluetooth]# bluetoothd[8116]: src/adapter.c:connected_callback() hci0 device 28:37:13:B0:F1:D7 connected eir_len 14 [CHG] Device 28:37:13:B0:F1:D7 Connected: yes [HUAWEI CM510]# bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_connect_cb() AVDTP: connected signaling channel to 28:37:13:B0:F1:D7 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_connect_cb() AVDTP imtu=672, omtu=672 bluetoothd[8116]: profiles/audio/avdtp.c:session_cb() bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_parse_resp() DISCOVER request succeeded bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_discover_resp() seid 1 type 1 media 0 in use 0 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_discover_resp() seid 2 type 0 media 0 in use 0 bluetoothd[8116]: profiles/audio/avdtp.c:session_cb() bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_parse_resp() GET_ALL_CAPABILITIES request succeeded bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_get_capabilities_resp() seid 1 type 1 media 0 bluetoothd[8116]: profiles/audio/avdtp.c:session_cb() bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_parse_resp() GET_ALL_CAPABILITIES request succeeded bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_get_capabilities_resp() seid 2 type 0 media 0 bluetoothd[8116]: profiles/audio/a2dp.c:discover_cb() err (nil) bluetoothd[8116]: profiles/audio/sink.c:discovery_complete() Discovery complete bluetoothd[8116]: profiles/audio/a2dp.c:setup_ref() 0x29825ed0: ref=2 bluetoothd[8116]: profiles/audio/a2dp.c:setup_ref() 0x29825ed0: ref=3 bluetoothd[8116]: profiles/audio/media.c:media_endpoint_async_call() Calling SelectConfiguration: name = :1.3 path = /MediaEndpoint/A2DPSource bluetoothd[8116]: profiles/audio/a2dp.c:setup_unref() 0x29825ed0: ref=2 bluetoothd[8116]: profiles/audio/a2dp.c:a2dp_config() a2dp_config: selected SEP 0x2982ca00 bluetoothd[8116]: profiles/audio/a2dp.c:setup_ref() 0x29825ed0: ref=3 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_set_configuration() 0x298480f0: int_seid=1, acp_seid=1 bluetoothd[8116]: profiles/audio/a2dp.c:setup_unref() 0x29825ed0: ref=2 bluetoothd[8116]: profiles/audio/a2dp.c:setup_unref() 0x29825ed0: ref=1 bluetoothd[8116]: profiles/audio/avdtp.c:session_cb() bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_parse_resp() SET_CONFIGURATION request succeeded bluetoothd[8116]: profiles/audio/a2dp.c:setconf_cfm() Source 0x2982ca00: Set_Configuration_Cfm bluetoothd[8116]: profiles/audio/a2dp.c:setup_ref() 0x29825ed0: ref=2 bluetoothd[8116]: profiles/audio/media.c:media_endpoint_async_call() Calling SetConfiguration: name = :1.3 path = /MediaEndpoint/A2DPSource bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_sep_set_state() stream state changed: IDLE -> CONFIGURED bluetoothd[8116]: profiles/audio/a2dp.c:setup_unref() 0x29825ed0: ref=1 bluetoothd[8116]: profiles/audio/avdtp.c:session_cb() bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_parse_resp() OPEN request succeeded bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_connect_cb() AVDTP: connected transport channel to 28:37:13:B0:F1:D7 bluetoothd[8116]: profiles/audio/avdtp.c:handle_transport_connect() Flushable packets enabled bluetoothd[8116]: profiles/audio/avdtp.c:handle_transport_connect() sk 20, omtu 672, send buffer size 114688 bluetoothd[8116]: profiles/audio/a2dp.c:open_cfm() Source 0x2982ca00: Open_Cfm bluetoothd[8116]: profiles/audio/a2dp.c:setup_unref() 0x29825ed0: ref=0 bluetoothd[8116]: profiles/audio/a2dp.c:setup_free() 0x29825ed0 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_unref() 0x298480f0: ref=1 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_sep_set_state() stream state changed: CONFIGURED -> OPEN bluetoothd[8116]: src/service.c:change_state() 0x29829e80: device 28:37:13:B0[ 4785.589898] input: 28:37:13:B0:F1:D7 as /devices/virtual/input/input8 :F1:D7 profile a2dp-sink state changed: connecting -> connected (0) bluetoothd[8116]: src/device.c:device_profile_connected() a2dp-sink Success (0) bluetoothd[8116]: profiles/audio/a2dp.c:a2dp_source_connect() path /org/bluez/hci0/dev_28_37_13_B0_F1_D7 bluetoothd[8116]: profiles/audio/avdtp.c:avdtp_ref() 0x298480f0: ref=2 bluetoothd[8116]: a2dp-source profile connect failed for 28:37:13:B0:F1:D7: Device or resource busy bluetoothd[8116]: src/device.c:device_profile_connected() returning response to :1.10 bluetoothd[8116]: plugins/policy.c:policy_connect() /org/bluez/hci0/dev_28_37_13_B0_F1_D7 profile avrcp-controller bluetoothd[8116]: profiles/audio/avrcp.c:avrcp_connect() path /org/bluez/hci0/dev_28_37_13_B0_F1_D7 bluetoothd[8116]: profiles/audio/avctp.c:avctp_set_state() AVCTP Connecting bluetoothd[8116]: src/service.c:change_state() 0x298286c0: device 28:37:13:B0:F1:D7 profile avrcp-controller state changed: disconnected -> connecting (0) Connection successful [HUAWEI CM510]# bluetoothd[8116]: plugins/policy.c:service_cb() Added a2dp-sink reconnect 0 bluetoothd[8116]: profiles/audio/sink.c:sink_set_state() State changed /org/bluez/hci0/dev_28_37_13_B0_F1_D7: SINK_STATE_CONNECTING -> SINK_STATE_CONNECTED bluetoothd[8116]: profiles/audio/transport.c:transport_update_playing() /org/bluez/hci0/dev_28_37_13_B0_F1_D7/fd4 State=TRANSPORT_STATE_IDLE Playing=0 bluetoothd[8116]: profiles/audio/avctp.c:avctp_connect_cb() AVCTP: connected to 28:37:13:B0:F1:D7 bluetoothd[8116]: profiles/audio/avctp.c:init_uinput() AVRCP: uinput initialized for 28:37:13:B0:F1:D7 bluetoothd[8116]: profiles/audio/avrcp.c:controller_init() 0x2982f220 version 0x0000 bluetoothd[8116]: src/service.c:change_state() 0x298457c0: device 28:37:13:B0:F1:D7 profile audio-avrcp-target state changed: disconnected -> connected (0) bluetoothd[8116]: profiles/audio/avrcp.c:target_init() 0x2985d0e0 version 0x0106 bluetoothd[8116]: src/service.c:change_state() 0x298286c0: device 28:37:13:B0:F1:D7 profile avrcp-controller state changed: connecting -> connected (0) bluetoothd[8116]: src/device.c:device_profile_connected() avrcp-controller Success (0) bluetoothd[8116]: profiles/audio/avctp.c:avctp_set_state() AVCTP Browsing Connecting bluetoothd[8116]: profiles/audio/avctp.c:avctp_set_state() AVCTP Connected bluetoothd[8116]: profiles/audio/avrcp.c:handle_vendordep_pdu() AVRCP PDU 0x10, company 0x001958 len 0x0001 bluetoothd[8116]: profiles/audio/avrcp.c:avrcp_handle_get_capabilities() id=3 bluetoothd[8116]: profiles/audio/avctp.c:avctp_connect_browsing_cb() AVCTP Browsing: connected to 28:37:13:B0:F1:D7 bluetoothd[8116]: profiles/audio/avctp.c:avctp_set_state() AVCTP Browsing Connected bluetoothd[8116]: profiles/audio/avrcp.c:handle_vendordep_pdu() AVRCP PDU 0x31, company 0x001958 len 0x0005 bluetoothd[8116]: src/device.c:search_cb() 28:37:13:B0:F1:D7: No service update bluetoothd[8116]: src/device.c:device_svc_resolved() /org/bluez/hci0/dev_28_37_13_B0_F1_D7 err 0 [CHG] Device 28:37:13:B0:F1:D7 ServicesResolved: yes
这里因为在启动 bluetoothd 时添加了 -d 选项,所以导致这里 debug 信息比较多,但是也能帮助我们在出现问题的时候快速定位。看自己需要是否进行输出显示咯。
从 终端最后的消息可以看到蓝牙音响设备成功连接了,这里也可以使用 hcitool -i hci0 con 来查看:
# hcitool -i hci0 con Connections: < ACL 28:37:13:B0:F1:D7 handle 11 state 1 lm MASTER AUTH ENCRYPT
音频播放
由于我的 aplay 编译的时候可能存在问题,所以即使蓝牙音响已经成功连接,但是 aplay 依然查找不到有新的声卡设备,如下:
但是通过 pulseaudio 的工具 pactl 可以查找到有蓝牙声卡设备,这时候除了 aplay 播放工具之外,paplayer 和 mpg123 等音频播放工具都可以正常播放音频文件
# paplay dx.wav
我是使用的 paplay 工具进行播放的。
此外,还可以通过 pacmd 工具进行音量的设置和缓冲音频文件、播放音频等操作,具体可以通过 pacmd help 查看帮助信息完成
#pacmd set-sink-volume 0 10000 #左声道 #pacmd set-sink-volume 1 10000 #右声道 # pacmd dump-volumes Sink 0: reference = front-left: 15000 / 23% / -38.42 dB, front-right: 15000 / 23% / -38.42 dB, real = front-left: 15000 / 23% / -38.42 dB, front-right: 15000 / 23% / -38.42 dB, soft = front-left: 15000 / 23% / -38.42 dB, front-right: 15000 / 23% / -38.42 dB, current_hw = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, save = yes Sink 3: reference = front-left: 12000 / 18% / -44.24 dB, front-right: 12000 / 18% / -44.24 dB, real = front-left: 12000 / 18% / -44.24 dB, front-right: 12000 / 18% / -44.24 dB, soft = front-left: 12000 / 18% / -44.24 dB, front-right: 12000 / 18% / -44.24 dB, current_hw = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, save = yes Source 0: reference = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, real = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, soft = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, current_hw = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, save = no Source 1: reference = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, real = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, soft = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, current_hw = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, save = no Source 6: reference = front-left: 10000 / 15% / -48.99 dB, front-right: 10000 / 15% / -48.99 dB, real = front-left: 10000 / 15% / -48.99 dB, front-right: 10000 / 15% / -48.99 dB, soft = front-left: 10000 / 15% / -48.99 dB, front-right: 10000 / 15% / -48.99 dB, current_hw = front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB, save = yes # pacmd set-sink-volume 3 15000
问题记录
- 如 果 出 现 类 似 的 错 误 :“ main.c: Failed to acquire org.pulseaudio.Server:
org.freedesktop.DBus.Error.AccessDenied: Connection “:1.46” is not allowed to own the service
“org.pulseaudio.Server” due to security policies in the configuration file ”, 需 要 在
/etc/dbus-1/system.d/pulseaudio-system.conf ( 此 文 件 来 之 于 <pulseaudio-x.y>/src/daemon/
pulseaudio-system.conf)中添加如下内容。如果以普通用户运行 pulseaudio,在这个文件中为
普通用户添加一个 policy,内容和 root 用户类似。
<?xml version="1.0"?><!--*-nxml-*--> <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <!-- This file is part of PulseAudio. PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. --> <busconfig> <!-- System-wide PulseAudio runs as 'pulse' user. This fragment is not necessary for user PulseAudio instances. --> <policy user="pulse"> <allow own="org.pulseaudio.Server"/> <allow send_destination="org.bluez"/> <allow send_interface="org.bluez.Manager"/> </policy> <policy user="pulse"> <allow own="org.pulseaudio.Server"/> <allow send_destination="org.bluez"/> <allow send_interface="org.bluez.Manager"/> </policy> <policy context="default"> <deny own="org.pulseaudio.Server"/> <deny send_destination="org.bluez"/> <deny send_interface="org.bluez.Manager"/> </policy> </busconfig>