• ClassIn 在 Linux 下无法播放音频


    我在 Debian (KDE Plasma) 系统上使用 ClassIn 时,发现无法播放任何音频。其他应用程序(如浏览器、媒体播放器)声音正常,仅 ClassIn 存在问题。ClassIn 的音频输出设备选项中也仅有 "default" 一项,无法手动选择其他设备。

    环境

    • 系统:Debian Testing
    • 内核:7.0.7+deb14-amd64
    • 桌面环境:KDE Plasma 6.6.5
    • 音频服务:PipeWire
    • 外接显示器无扬声器
    • ClassIn 版本:6.0.6

    音频设备排查

    从 ClassIn 官方得知,ClassIn 使用 ALSA。

    aplay -l
    

    输出如下:

    **** List of PLAYBACK Hardware Devices ****
    card 0: HDMI [HDA Intel HDMI], device 3: HDMI 0 [HDMI 0]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 0: HDMI [HDA Intel HDMI], device 7: HDMI 1 [HDMI 1]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 0: HDMI [HDA Intel HDMI], device 8: HDMI 2 [HDMI 2]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 1: PCH [HDA Intel PCH], device 0: ALC3232 Analog [ALC3232 Analog]
      Subdevices: 0/1
      Subdevice #0: subdevice #0
    

    系统有两个声卡:

    • card 0:HDA Intel HDMI,对应 HDMI 输出
    • card 1:HDA Intel PCH (ALC3232 Analog),对应耳机孔或内置扬声器

    由于 ALSA 的 "default" 设备通常指向 card 0,而我的 HDMI 显示器并没有扬声器,这意味着 ClassIn 的声音可能被送到了一个没有输出能力的设备上。

    HDMI 显示器排查

    那么问题来了:我的显示器没有任何扬声器。为什么系统会认为它是一个音频输出设备?

    读取显示器的 EDID 信息:

    edid-decode /sys/class/drm/card0-HDMI-A-2/edid
    

    输出中 CTA-861 Extension Block 部分仅包含 Video Data Block 和 Vendor-Specific Data Block,完全没有 Audio Data Block 和 Speaker Allocation Data Block。这说明显示器已经如实向系统报告了自己不支持音频。

    那为什么系统还觉得我的显示器有扬声器?搜索得知,Linux 内核的 HDA HDMI 驱动(snd-hda-codec-hdmi)的设计策略是为所有 HDMI 输出端口创建音频设备,而不会根据 EDID 中的音频信息自动禁用无用的端口。

    调换声卡顺序

    既然 card 0 指向了一个没有扬声器的 HDMI 设备,一个自然的思路是让 PCH 声卡排到第一位,这样 ALSA 的 "default" 就会指向它。

    编辑 /etc/modprobe.d/alsa-reorder.conf

    options snd-hda-intel index=1,0
    

    index=1,0 的作用是将先被内核发现的 HDMI 设备降为 card 1,后被发现的 PCH 设备提为 card 0。

    我重启设备。再打开ClassIn,依然没有声音。

    aplay -l
    
    **** List of PLAYBACK Hardware Devices ****
    card 0: PCH [HDA Intel PCH], device 0: ALC3232 Analog [ALC3232 Analog]
      Subdevices: 0/1
      Subdevice #0: subdevice #0
    card 1: HDMI [HDA Intel HDMI], device 3: HDMI 0 [HDMI 0]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 1: HDMI [HDA Intel HDMI], device 7: HDMI 1 [HDMI 1]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    card 1: HDMI [HDA Intel HDMI], device 8: HDMI 2 [HDMI 2]
      Subdevices: 1/1
      Subdevice #0: subdevice #0
    

    此时 PCH 已经成功变为 card 0。配置生效了,但没有解决问题。

    ALSA default 设备排查

    speaker-test -t wav -c 2
    

    输出:

    speaker-test 1.2.15.2
    Playback device is default
    Stream parameters are 48000Hz, S16_LE, 2 channels
    WAV file(s)
    ALSA lib pcm_dmix.c:1000:(snd_pcm_dmix_open) [error.pcm] unable to open slave
    Playback open error: -16,Device or resource busy
    

    Device or resource busy。这说明声卡硬件已经被其他进程占用,ALSA 无法直接访问。

    查看谁占用了音频设备:

    fuser -v /dev/snd/*
    
                         USER        PID ACCESS COMMAND
    /dev/snd/controlC0:  user       1114 F.... pipewire
                         user       1175 F.... wireplumber
    /dev/snd/controlC1:  user       1175 F.... wireplumber
    /dev/snd/pcmC0D0p:   user       1114 F...m pipewire
    /dev/snd/seq:        user       1114 F.... pipewire
    

    PipeWire 占用了 /dev/snd/pcmC0D0p(即 card 0 的播放设备)。这正是 ClassIn 想要访问的设备。

    pactl info 2>&1 | head -5
    
    Server String: /run/user/1000/pulse/native
    Library Protocol Version: 35
    Server Protocol Version: 35
    Is Local: yes
    Client Index: 156
    

    系统使用的是 PipeWire(兼容 PulseAudio 接口)。ALSA 声卡硬件设备被占用了

    分析

    1. PipeWire 作为系统音频服务,直接占用了 ALSA 声卡硬件设备
    2. ClassIn 尝试通过 ALSA 的 "default" 设备直接访问硬件
    3. 由于硬件已被 PipeWire 独占,ClassIn 无法打开设备,播放失败
    4. 其他应用声音正常,是因为它们通过 PipeWire(PulseAudio 兼容接口)播放,而非直接操作 ALSA 硬件

    解决

    pipewire-alsa 包提供了 ALSA 到 PipeWire 的兼容层。

    sudo apt install pipewire-alsa
    

    ALSA 的 "default" 设备被重定向到 PipeWire,而非直接访问硬件。

    speaker-test -t wav -c 2
    

    这次系统不再报错,也能正常听到测试声音。打开 ClassIn,音频能播放了。

    总结

    在 Debian 12+(默认使用 PipeWire)下,某些应用(如 ClassIn)直接通过 ALSA 播放音频时可能失败。其根本原因是 PipeWire 独占了声卡硬件设备,导致直接访问 ALSA 硬件的应用无法播放。

    排查过程中,我发现 ALSA default 设备指向了无扬声器的 HDMI 输出,并尝试调换声卡顺序,但这一步并未解决问题——系统用 PipeWire 作为音频服务。

    最终的解决方案是安装 pipewire-alsa。它将 ALSA 请求桥接到 PipeWire,使其能正常播放。如果你的 ClassIn 在 Linux 上无法出声,且系统使用 PipeWire,可以检查该包是否已安装。

    二周目?

    好景不长。用着用着, ClassIn 又突然没声音了。重启系统、重装 pipewire-alsa、清空 ClassIn 自己的数据目录,统统无效。我逐渐放弃了在 Linux 上使用 ClassIn 的尝试。

    直到我的储存设备意外损坏后换到了一个新的系统(还是Debian)。26年暑假的一天,我决定重新安装 ClassIn 碰碰运气。也许重装系统修复了问题呢?

    启动时,我突然想起来没装 pipewire-alsa 。安装之后,我没有重启ClassIn,直接尝试播放音频。其竟然能发出声音了。于是我打开ClassIn,开始听课。过了一会,ClassIn突然又没声音了。

    我尝试:

    • 重启
    • 清空ClassIn数据目录(under ~/.local)后再启动
    • 卸载 pipewire-alsa ,启动ClassIn,再安装 pipewire-alsa

    都没有效果。

    检查 ALSA 配置

    还是先确认 ALSA 配置是否正常。上次的问题是 ALSA default 指向了错误的设备,这次先排除这个可能。

    aplay -L | grep -A2 -E '^default|^pipewire|^pulse'
    
    pipewire
        PipeWire Sound Server
    default
        Default ALSA Output (currently PipeWire Media Server)
    

    ALSA default 正确指向了 PipeWire,配置层没问题。

    PipeWire 文档中说,PulseAudio 可能会导致冲突。

    ls -l /etc/alsa/conf.d/
    
    lrwxrwxrwx 1 root root 44 Jul  9 19:16 50-pipewire.conf -> /usr/share/alsa/alsa.conf.d/50-pipewire.conf
    lrwxrwxrwx 1 root root 52 Jul  9 19:16 99-pipewire-default.conf -> /usr/share/alsa/alsa.conf.d/99-pipewire-default.conf
    

    只有 pipewire 的配置,没有 PulseAudio 的 99-pulse.conf 冲突。

    谁占着声卡?

    fuser -v /dev/snd/*
    

    输出:

                         USER        PID ACCESS COMMAND
    /dev/snd/controlC0:  user       1205 F.... wireplumber
                         user       6394 f.... ClassIn
    /dev/snd/controlC1:  user       1183 F.... pipewire
                         user       1205 F.... wireplumber
                         user       6394 f.... ClassIn
    /dev/snd/pcmC1D0p:   user       1183 F...m pipewire
    /dev/snd/seq:        user       1183 F.... pipewire
    

    ClassIn(PID 6394)打开了 controlC0controlC1 的控制接口,但没有打开任何 pcmC*D*p 播放设备。PCM 设备只有 pipewire 在占用。

    ClassIn 走的是 ALSA → PipeWire 桥接路径(通过 pipewire-alsa),而不是直接操作硬件。桥接层是通的。

    检查 PipeWire sink 状态

    pactl list sinks short
    
    57      alsa_output.pci-0000_00_1b.0.analog-stereo      PipeWire        s32le 2ch 48000Hz       RUNNING
    

    sink 状态是 RUNNING,设备是 alsa_output.pci-0000_00_1b.0.analog-stereo(内置音频模拟输出),看起来正常。

    持久化之处

    既然 ALSA 配置正常、设备被正确占用、sink 在 RUNNING,问题可能出在 WirePlumber 的状态层。PipeWire 体系里,跨重启持久化的东西主要在 ~/.local/state/ 下。

    ls -la ~/.local/state/wireplumber/
    
    drwx------ 2 user user 4096 Jul 14 19:47 .
    drwx------ 4 user user 4096 Jul 14 19:45 ..
    -rw-rw-r-- user user  279 Jul 14 19:16 default-routes
    -rw-rw-r-- user user 1703 Jul 14 19:47 stream-properties
    
    cat ~/.local/state/wireplumber/stream-properties
    

    部分输出:

    [stream-properties]
    Audio/Sink:node.name:auto_null={"volume":1.000000, "channelVolumes":[1.000000, 1.000000], "mute":false, "channelMap":["FL", "FR"]}
    Output/Audio:media.role:Notification={"volume":1.000000, "channelVolumes":[1.000000, 1.000000], "channelMap":[], "mute":false}
    Output/Audio:application.name:Microsoft\sEdge={"channelMap":["MONO"], "volume":1.000000, "mute":false, "channelVolumes":[1.000000]}
    Output/Audio:application.id:org.freedesktop.libcanberra={"volume":1.000000, "channelVolumes":[1.000000, 1.000000], "channelMap":[], "mute":false}
    Output/Audio:application.name:Chromium={"volume":1.000000, "channelVolumes":[1.000000, 1.000000], "channelMap":["FL", "FR"], "mute":false}
    Output/Audio:media.name:板砖\s(codec\sSBC)={"volume":1.000000, "channelMap":["FL", "FR"], "mute":false, "channelVolumes":[1.000000, 1.000000]}
    Output/Audio:application.name:PipeWire\sALSA\s\oClassIn\c={"volume":1.000000, "channelVolumes":[1.000000], "channelMap":["MONO"], "mute":true}
    Input/Audio:application.name:PipeWire\sALSA\s\oClassIn\c={"volume":1.000000, "channelVolumes":[1.000000], "channelMap":["MONO"], "mute":false}
    Output/Audio:application.name:ClassIn\s@946292853064\s(QtAV)={"channelVolumes":[1.000000], "volume":1.000000, "mute":true, "channelMap":["MONO"]}
    Output/Audio:application.name:ClassIn\s@94629287469424\s(QtAV)={"channelMap":["MONO"], "channelVolumes":[1.000000], "mute":true, "volume":1.000000}
    ...
    

    三条 ClassIn 相关的记录,全部是 "mute":true!其他应用(Edge、Chromium 等)都是 "mute":false

    检查当前 sink-input 的状态

    stream-properties 是持久化的历史状态,我还需要确认当前运行中的 ClassIn 音频流是否真的被静音了。

    pactl list sink-inputs
    
    Sink Input #153
            Driver: PipeWire
            Client: 152
            Sink: 57
            Sample Specification: s16le 1ch 44100Hz
            Channel Map: mono
            Format: pcm, format.sample_format = "\"s16le\""  format.rate = "44100"  format.channels = "1"  format.channel_map = "\"mono\""
            Corked: no
            Mute: yes
            Volume: mono: 65536 / 100% / 0.00 dB
                    balance 0.00
            ...
            Properties:
                    application.name = "PipeWire ALSA [ClassIn]"
                    node.name = "alsa_playback.ClassIn"
                    device.description = "ALSA Playback [ClassIn]"
                    media.name = "ALSA Playback"
                    ...
                    module-stream-restore.id = "sink-input-by-application-name:PipeWire ALSA [ClassIn]"
    
    • Mute: yes —— 当前确实是静音的!
    • Volume: mono: 65536 / 100% —— 音量是满的,问题不在音量
    • application.name = "PipeWire ALSA [ClassIn]" —— 和 stream-properties 里的 key 对应
    • module-stream-restore.id = "sink-input-by-application-name:PipeWire ALSA [ClassIn]" —— 这个属性告诉 WirePlumber 的 stream-restore 模块,要按应用名匹配并恢复状态

    重新分析

    stream-properties 文件是 WirePlumber 的 stream-restore 模块(源自 PulseAudio 的 module-stream-restore)用来持久化保存各应用音量/静音/路由状态的数据库。

    它的工作机制:

    1. 保存:当音频流的静音/音量/设备发生变化时,stream-restore 模块以 application.name 等属性作为 key,把新状态写入 stream-properties 文件
    2. 恢复:当新流创建时,stream-restore 模块查找该流对应的 key 是否有历史记录,有则自动恢复保存的状态

    ClassIn 的 sink-input 属性里有 module-stream-restore.id = "sink-input-by-application-name:PipeWire ALSA [ClassIn]",正好对应 stream-properties 文件里那条 "mute":true 的记录。

    这完美解释了一切。

    症状 解释
    用一会儿突然没声 某个时刻 ClassIn 被静音,stream-restore 立即持久化
    重启系统无效 WirePlumber 启动时从文件读回 mute:true
    清空 ClassIn 数据目录无效 stream-properties~/.local/state/wireplumber/,不在 ClassIn 自己的目录
    重装 pipewire-alsa 无效 问题根本不在 ALSA 桥接层,而在 WirePlumber 状态
    换新系统后能短暂播放 新系统的 state 文件是干净的,但一旦触发静音又会复发

    解决

    153 是我在 pactl list sink-inputs 里看到的 Sink Input ID。每个人的可能不同。

    pactl set-sink-input-mute 153 false
    

    总结

    Linux 音频子系统的"状态持久化"层往往比"配置"层更容易藏污纳垢。

    PipeWire/WirePlumber 把应用音量、静音、路由等信息存在 ~/.local/state/ 下,这些状态会跨重启保留。当应用出现"莫名其妙"的音量/静音问题时,第一时间应该检查这个目录。

    如果你使用 PipeWire 时应用突然哑巴了,可以试试:

    1. pactl list sink-inputs 看当前流是否被静音/音量为 0
    2. cat ~/.local/state/wireplumber/stream-properties 看持久化状态是否有异常
    3. 两者的 module-stream-restore.id / application.name 对应起来,就能确认是否是 stream-restore 在作祟

    完全总结

    阶段 症状 根因 解决
    第一阶段 ClassIn 从来没声音 PipeWire 独占声卡,ClassIn 直接访问 ALSA 失败 安装 pipewire-alsa 桥接
    第二阶段 ClassIn 用一会儿突然没声,重启无效 WirePlumber 的 stream-restore 把 mute:true 持久化了 pactl set-sink-input-mute
  • 相关阅读:
    Intellij Debugger slow: Method breakpoints may dramatically slow down debugging
    后端返回图片流前端展示图片
    母婴类目电商平台数据分析
    JMeter入门教程(9) --参数化
    OpenCV-Python实战(1) —— 给图片添加图片水印【利用 OpenCV 像素的读写原理实现】
    【300+精选大厂面试题持续分享】大数据运维尖刀面试题专栏(十六)
    Redis混合模式下的持久化原理
    Linux shell 脚本中的$$、$#、$?、$1的具体含义是什么呢?
    (十九)mmdetection源码解读:Hook子类之一OptimizerHook
    C++要笑着学:非类型模板参数 | 模板的特化
  • 原文地址:https://www.cnblogs.com/54145a/p/20136860