在 KDE Plasma 的音量控制里,多个 Electron 应用常常会被折叠成同一个 Chromium:明明是 Bilibili 和 YesPlayMusic 在同时播放,界面里却只有两个难以分辨的 Chromium 流。问题不在 KDE 没有拿到流,而是 Electron/Chromium 向 PipeWire-Pulse 提交的应用名过于通用。下面记录一次从属性排查、错误方案验证,到做成用户级服务的完整处理过程。

问题不是没有流,而是显示字段选错了

先不要猜测应用名,直接读取当前播放流:

pactl list sink-inputs
wpctl status
wpctl inspect <PipeWire-node-id>

一条 Bilibili 音频流的关键属性如下:

属性

示例值

是否适合作为区分依据

application.name

Chromium

否,KDE 默认优先显示它

node.name

Chromium

否,通常同样通用

media.name

Playback

否,只描述播放行为

application.process.binary

bilibili

是,实际程序名且稳定

application.process.id

6448

仅用于本次运行,重启后会变化

重点是 application.process.binary。在这台 Arch Linux + KDE Plasma + PipeWire 1.6.8 的环境中,Bilibili、YesPlayMusic、QQ、微信等 Chromium/Electron 程序的 application.name 可以完全相同,但 application.process.binary 分别是 bilibiliyesplaymusicqqwechat。这正是可以交给规则匹配的字段。

先验证一个看似合理、实际上走不通的方向

最开始的想法是:监听到 sink input 后,直接把它改名。这个思路很自然,但本机 pactl 并没有修改已有 sink-input 属性的命令:

pactl update-sink-input-proplist 608 application.name=bilibili
# No valid command specified.

随后也验证了 pw-cli set-param <node-id> Props ...。命令能够返回 Props 对象,但 PipeWire-Pulse 创建的流不会因此改写 application.name。这说明不能依赖“流已经出现后再从外部改属性”:KDE 要显示的名字应该在 PulseAudio 兼容层创建流时就设置好。

利用 pipewire-pulse 的 pulse.rules 在创建阶段改名

PipeWire 自带的 /usr/share/pipewire/pipewire-pulse.conf 已经定义了 pulse.rules。规则可以按客户端 / 流属性匹配,并通过 update-props 覆盖属性。用户级配置放在:

~/.config/pipewire/pipewire-pulse.conf.d/90-kde-electron-audio-renamer.conf

例如对 Bilibili 的最小规则:

pulse.rules = [
    {
        matches = [
            { application.name = "Chromium" application.process.binary = "bilibili" }
        ]
        actions = {
            update-props = {
                application.name = "bilibili"
                node.name = "bilibili"
            }
        }
    }
]

规则的两个匹配条件必须同时成立。只按 application.process.binary 匹配会影响不应处理的音频客户端;只按 application.name = Chromium 则仍然无法区分不同 Electron 程序。规则生效后,再次检查即可看到:

pactl list sink-inputs | rg 'application.name|application.process.binary|node.name'
# application.name = "bilibili"
# application.process.binary = "bilibili"
# node.name = "bilibili"

把发现和规则维护做成用户级服务

手工为每一个程序写规则不难,但新增 Electron 应用后又会回到 Chromium。最终实现选择了一个用户级 systemd 服务,它由两部分组成:

  1. pactl subscribe 监听 sink-input 事件,并在启动时先扫描一次现有流。

  2. 只取 application.name = Chromium 的流,再从 application.process.binary 中提取安全的二进制名;electronchromiumchrome、空值等通用名称会被忽略。

  3. 把第一次遇到的二进制名写入状态文件,重新生成 pulse.rules 配置。

服务安装位置与启动命令如下:

install -Dm755 kde-electron-audio-renamer.sh \
  "$HOME/.local/bin/kde-electron-audio-renamer"
install -Dm644 kde-electron-audio-renamer.service \
  "$HOME/.config/systemd/user/kde-electron-audio-renamer.service"

systemctl --user daemon-reload
systemctl --user enable --now kde-electron-audio-renamer.service

服务本身不需要 root 权限。规则保存在 ~/.config/pipewire/pipewire-pulse.conf.d/,已识别的程序名保存在 ~/.local/state/kde-electron-audio-renamer/binaries。因此登录后服务会自动启动,已有规则也会继续保留。

为什么首次识别新应用仍然需要一次短暂中断

这里有一个不能回避的边界:pulse.rules 是创建流时读取的,PipeWire-Pulse 不会可靠地热加载这条规则并改写已经存在的客户端流。服务发现一个从未见过的二进制名后,会执行一次:

systemctl --user restart pipewire-pulse.service

当前音频会短暂中断,客户端随后重新连接,并带着新的 application.name 出现在 KDE 音量控制中。这个重启只发生在某个新二进制名第一次被发现时;例如 bilibiliyesplaymusic 已经写入规则后,之后启动它们不会再触发重启。

如果完全不能接受这一次中断,唯一可靠的替代方式是在启动应用之前就注入属性,例如用包装启动器设置 PULSE_PROP_OVERRIDE,或者提前手工写好静态 pulse.rules。对任意已运行、且此前未知的 Electron 流做“无中断即时改名”,在现有 PipeWire-Pulse/PulseAudio 工具链中并没有稳定接口。

验证和排错

systemctl --user status kde-electron-audio-renamer.service
journalctl --user -u kde-electron-audio-renamer.service -f

pactl list sink-inputs
wpctl status

日志出现 discovered Chromium stream binary: yesplaymusic 说明服务已捕获到新程序。随后再次查看 pactl list sink-inputs,应确认 application.nameapplication.process.binarynode.name 都是同一个实际程序名。这样 KDE 的每个音频滑块才会真正对应到具体应用,而不是一排无法分辨的 Chromium。


完整脚本、systemd 服务和规则模板已整理在 LuorixDev/kde-electron-audio-renamer。这套方案的核心不是再去猜 PipeWire 的临时 ID,而是利用已经存在但默认未被 KDE 展示的 application.process.binary,在流的创建阶段建立稳定的显示身份。