注册 登录
自由的生活_软路由 返回首页

心想事成的个人空间 https://www.routerclub.com/?681 [收藏] [复制] [分享] [RSS]

日志

在蓝牙耳机连接上设置 PulseAudio 配置文件

已有 101 次阅读2024-4-11 10:20

 我最喜欢在 Linux 上进行开发并使用 Linux 作为我的日常驱动程序操作系统,但有时,当我不喜欢标准的做事方式时,我也会遇到一些感觉太复杂而无法“修复”的小事情。

有一件事困扰了我一段时间,我终于想出了一个可行的解决方案,那就是让我的 Jabra 耳机在重新连接到我的计算机时始终使用 HFP 配置文件。就像现在很多在电脑上工作的人一样,我每周都会花相当多的时间进行视频通话,当我将耳机连接到我的设备时,必须手动将耳机上的配置文件切换为 HFP,这是一个小小的但持续的烦恼。计算机,这样我就可以使用内置麦克风,而不是 Linux 只是将其视为常规音频输出。

问题

当我的蓝牙耳塞重新与计算机连接时,它们始终默认为 A2DP 配置文件,这意味着麦克风被禁用,我需要手动打开pavucontrol配置文件并将其切换为 HFP,然后才能加入 Zoom 通话。使问题更加复杂的是,如果我在加入会议之前忘记执行此操作,我需要离开会议并在切换配置文件后重新加入,因为由于某种原因,一旦通话开始,Zoom 就不会接受更改。

其工作方式应该是,当 Zoom 启动时,它会向 PulseAudio 发出信号,表示它想要使用耳机进行“电话呼叫”,并且 PulseAudio 应自动将配置文件切换为 HFP。实际上,这在我的手机和 macOS 上都可以正常工作,但由于某种原因,它在 Linux 上不起作用。

失败或不完整的解决方案

每次连接时设置配置文件

我可以每次在“配置”选项卡下从 PulseAudio 音量控制小程序手动执行此操作,但我不想需要这个额外的步骤。同样,我可以pactl set-card-profile从命令行执行此操作,但我也不想这样做。

在 PulseAudio 配置文件中设置默认配置文件

我在寻找此问题的解决方案时在互联网上找到的大多数建议都涉及编辑/etc/pulse/default.pa。该解决方案对于始终连接的耳机来说效果很好,但对于蓝牙耳塞来说显然不是这样。default.pa仅在 PulseAudio 启动时运行一次,因此如果此时耳塞未连接(它们从未连接),则永远不会设置配置文件。

“锁定”配置文件或设置auto_switch=0

我发现的其他一些答案建议在pavucontrol连接的耳机上设置 HFP 配置文件后使用锁定图标,或者在/etc/pulse/default.pa添加/更改线路时使用锁定图标load-module module-bluetooth-policy auto_switch=0。这确实阻止了行为良好的应用程序自动更改 A2DP 和 HFP 之间的配置文件,但它不会改变耳机重新连接时的任何行为,并且仍然默认为 A2DP。

解决方案

由于 PulseAudio 看起来不会让我通过其配置来完成此操作,因此我决定后退一步,尝试在建立蓝牙连接时自动执行我在上面一层进行的手动配置文件切换。为此,我需要两部分:第一部分用于检测此特定耳机何时连接并运行任意脚本,第二部分用于实际切换配置文件。

将配置文件更改为 HFP 的脚本

我首先需要知道 PulseAudio 是如何指代这款耳机的。我将耳机配对并连接到我的电脑,然后运行:

# Note: in some cases you may need to substitute `pacmd` instead of `pactl`
# in the places it's used in the rest of this article. Thanks Anya for
# pointing this out to me!
$ pactl list
...
Card #20
	Name: bluez_card.70_BF_92_C9_F5_D0
	Driver: module-bluez5-device.c
	Owner Module: 42
	Properties:
		device.description = "Jabra Elite 75t"
		device.string = "70:BF:92:C9:F5:D0"
		device.api = "bluez"
		device.class = "sound"
		device.bus = "bluetooth"
		device.form_factor = "headset"
		bluez.path = "/org/bluez/hci0/dev_70_BF_92_C9_F5_D0"
		bluez.class = "0x240404"
		bluez.alias = "Jabra Elite 75t"
		bluetooth.battery = "100%"
		device.icon_name = "audio-headset-bluetooth"
		device.intended_roles = "phone"
		bluetooth.codec = "mSBC"
	Profiles:
		a2dp_sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 40, available: yes)
		handsfree_head_unit: Handsfree Head Unit (HFP) (sinks: 1, sources: 1, priority: 30, available: yes)
		off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
	Active Profile: a2dp_sink
	Ports:
		headset-output: Headset (type: Headset, priority: 0, latency offset: 0 usec, availability unknown)
			Part of profile(s): a2dp_sink, handsfree_head_unit
		headset-input: Headset (type: Headset, priority: 0, latency offset: 0 usec, availability unknown)
			Part of profile(s): handsfree_head_unit

这个输出告诉我一些我需要知道的事情:

  • 卡名是bluez_card.70_BF_92_C9_F5_D0.由于索引(20在本例中)不一致,因此我需要使用名称。
  • 我想要切换到的配置文件名为handsfree_head_unit

有了这些信息,我可以编写一个脚本来切换配置文件:

注意:如果您有托管用户而不是本地用户,您可能需要id -u myuser获取正确的用户 ID,并在1000下面的脚本中使用它。再次感谢安雅向我指出这一点。

#!/bin/bash
sleep 2 # wait for the headset to fully connect
# if `pactl` below doesn't work for you, `pacmd` instead might get you going.
sudo -u '#1000' XDG_RUNTIME_DIR=/run/user/1000 \
    pactl set-card-profile bluez_card.70_BF_92_C9_F5_D0 handsfree_head_unit
logger "Switched Jabra headset to HFP profile"

sleep 2是必要的,因为耳机需要几秒钟才能完全连接,如果我尝试过早切换配置文件,它就不起作用。

sudo -u '#1000' XDG_RUNTIME_DIR=/run/user/1000是必要的,因为我需要的第二部分是udev将执行此脚本的规则,并udev以 root 身份运行,它无权访问我的用户的 PulseAudio 套接字。让XDG_RUNTIME_DIRudev以 root 身份运行)找到要连接的 PulseAudio 套接字(由用户拥有)。

logger如果您希望能够监控syslog配置文件何时切换,您可以像我一样添加呼叫。这在调试脚本时很有用,但一旦运行可能就不需要了。

将此脚本保存在可访问的位置并chmod +x /path/to/script在其上运行将使其可执行,并且从命令行运行它会将配置文件切换为 HFP。如果手动操作不起作用,请确保您拥有正确的卡名称和个人资料名称。该脚本也应该在 shell 中正常工作,sudo -i即使此时的用户是root.

udev耳机连接时运行脚本的规则

现在我有了一个切换配置文件的脚本,我需要在耳机连接时运行它,这意味着我需要看看udev.为此,我断开了耳塞,运行udevadm monitor,然后连接耳塞。这是输出的相关部分:

$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
...
UDEV  [54588.946048] add      /devices/virtual/input/input112 (input)
UDEV  [54588.977938] add      /devices/virtual/input/input112/event256 (input)
...

其中有两个重要部分。首先,该事件是一个add我需要的事件,其次,该设备 /devices/virtual/input/input112现在是这样的。请注意,每次连接耳机时,这都会发生变化,因此在断开耳机连接之前我需要获取更多信息。为此,我udevadm info -ap使用上面的设备路径运行:

$ udevadm info -ap /devices/virtual/input/input112

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/virtual/input/input112':
    KERNEL=="input112"
    SUBSYSTEM=="input"
    DRIVER==""
    ATTR{capabilities/abs}=="0"
    ATTR{capabilities/ev}=="100007"
    ATTR{capabilities/ff}=="0"
    ATTR{capabilities/key}=="2fc800 145200000000 0 10300 49e800000c00 e16800000000f f810000010000ffc"
    ATTR{capabilities/led}=="0"
    ATTR{capabilities/msc}=="0"
    ATTR{capabilities/rel}=="0"
    ATTR{capabilities/snd}=="0"
    ATTR{capabilities/sw}=="0"
    ATTR{id/bustype}=="0005"
    ATTR{id/product}=="24a7"
    ATTR{id/vendor}=="0067"
    ATTR{id/version}=="0200"
    ATTR{inhibited}=="0"
    ATTR{name}=="Jabra Elite 75t (AVRCP)"
    ATTR{phys}=="e0:d4:64:38:d1:db"
    ATTR{power/async}=="disabled"
    ATTR{power/control}=="auto"
    ATTR{power/runtime_active_kids}=="0"
    ATTR{power/runtime_active_time}=="0"
    ATTR{power/runtime_enabled}=="disabled"
    ATTR{power/runtime_status}=="unsupported"
    ATTR{power/runtime_suspended_time}=="0"
    ATTR{power/runtime_usage}=="0"
    ATTR{properties}=="0"
    ATTR{uniq}==""

我需要从这里提取一些内容来编写我的udev规则:

  • 设备子系统是input
  • 设备供应商 ID 是0067
  • 设备产品 ID 为24a7

此时,您可以再次断开耳机连接,因为您已获得所需的所有信息。

如果我希望我的规则仅特定于这一款 Jabra 耳机,我也可以使用它ATTR{phys}来匹配 MAC 地址,但我只有一台耳机,所以我不需要这样做。

/etc/udev/rules.d/52-jabra-headset.rules为了编写这个新规则,我创建了包含以下内容的文件:

ACTION=="add", SUBSYSTEM=="input", ATTR{id/vendor}=="0067", ATTR{id/product}=="24a7", RUN+="/home/<myUsername>/.config/myPactlScript.sh"

显然,您需要替换该规则的一些部分,以匹配您的特定设备配置以及pactl之前放置脚本的位置,但这应该足以为您指明正确的方向。

测试规则

为了测试规则,运行tail -f /var/log/syslog并重新连接耳塞。由于我logger的脚本中有这个调用,我能够看到(在多次尝试正确处理之后......)该规则正常工作:

$ tail -f /var/log/syslog
...
Jun  8 21:38:04 <myHostname> kernel: [  545.888000] input: Jabra Elite 75t (AVRCP) as /devices/virtual/input/input112
Jun  8 21:38:04 <myHostname> root: Switched Jabra headset to HFP profile

包起来

在解决了一个恼人的、晦涩的问题后,剩下要做的唯一的事情就是我平常做的事情:

  • 将新配置添加到我的 dotfiles 存储库中,以便下次更容易,并且
  • 写一篇关于它的简短文章,这样我下次需要它时就能记住我的推理,而点文件并没有给我足够的记忆力

如果上述某些内容不清楚,或者您是否找到了针对此特定问题的更好解决方案,请随时告诉我。您可以在主页上找到我的联系信息


路过

雷人

握手

鲜花

鸡蛋

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|Archiver|手机版|小黑屋|软路由 ( 渝ICP备15001194号-1|渝公网安备 50011602500124号 )

GMT+8, 2024-5-21 00:36 , Processed in 0.041119 second(s), 5 queries , Gzip On, Redis On.

Powered by Discuz! X3.5 Licensed

© 2001-2023 Discuz! Team.

返回顶部