RustDesk + HDMI 诱骗器:远程听不到本机声音的修复记录
AI

RustDesk + HDMI 诱骗器:远程听不到本机声音的修复记录

TomHanck4
2026-05-14 / 0 评论 / 1 阅读 / 正在检测是否收录...

RustDesk + HDMI 诱骗器:远程听不到本机声音的修复记录

环境:Win11 控制端 → Ubuntu 22.04 被控端,被控为 X11,本机插 HDMI 诱骗器(虚拟显示器),音频走 PipeWire(界面显示为 PulseAudio on PipeWire)。

现象:远程连接后,本机明明在放视频/音乐,对端却听不到;或曾经修好过,重启后又没声;或安装 Wine / Win32 播放器后,远端再次听不到本机声音。


根因(本次已核实)

  1. RustDesk 采的是「某个声卡输出的 Monitor」,不是「物理麦克风」。若采到 板载 analog 输出的 monitor,而实际播放却在 HDMI 诱骗器,对端就会静音(或只有环境噪声)。
  2. WirePlumber / stream-restore 会按应用名记住 RustDesk 上次用的采集端,容易一直钉在 analog-stereo 上;仅改「默认信源」有时不会自动改掉已建立的 RustDesk 录音流。
  3. 播放器输出也必须走同一条 HDMI sink。安装 Wine 后,新出现的 Wine / PotPlayer / Win32 应用播放流可能被旧路由记忆影响,跑到 analog 或其它输出;此时 RustDesk 即使已经采 HDMI monitor,也会因为 HDMI monitor 没有实际音频样本而静音。
  4. 当时若没有应用在出声,monitor 上也没有波形,听起来也像「没声音」——需本机有播放器在播,且采集端对准正在出声的那条 HDMI。

现场诊断命令(建议保留)

# 默认 sink / source、设备是否在跑
pactl info | head -20
pactl list short sinks
pactl list short sources

# 谁在「播放」(应看到浏览器 / PotPlayer / Wine 应用,Sink 应对应 HDMI)
pactl list sink-inputs

# 谁在「录音」(应出现 application.name = RustDesk)
pactl list source-outputs

# PipeWire 视角看路由:播放器 output 应到 HDMI playback,RustDesk input 应来自 HDMI monitor
wpctl status

# 看 RustDesk 绑定的信源索引:信源一行应对应 hdmi 的 .monitor
LANG=C pactl list source-outputs | grep -E '^(Source Output|Source:|application.name)'

判读要点:

  • pactl list short sources 里 HDMI 诱骗器一般是带 hdmi 且以 .monitor 结尾的条目(例如 alsa_output....hdmi-stereo.monitor)。
  • pactl list sink-inputs 里播放器 / Wine 应用的 Sink: 应指向 HDMI sink;若播放流去了 analog,HDMI monitor 仍然可能是全 0。
  • 在 source-outputs 里若看到 RustDesk 的 Source: 指向 analog 的 monitor,而本机声音实际从 HDMI 出来,就需要把该录音流 挪到 HDMI 的 monitor。
  • 若 RustDesk 已指向 HDMI monitor 但远端仍无声,应先用测试音确认 HDMI monitor 是否真的有波形;若测试音有波形,重点检查控制端 RustDesk 的音频开关/播放设备。

一次性手工修复(当场救急)

把 <HDMI_MONITOR> 换成你机器上 pactl list short sources 里那条 hdmi 的 .monitor 名称,把 <HDMI_SINK> 换成 pactl list short sinks 里那条 hdmi 输出名称。

# 先设默认播放为 HDMI,默认采集为 HDMI monitor
pactl set-default-sink '<HDMI_SINK>'
pactl set-default-source '<HDMI_MONITOR>'

# 若播放器 / Wine 应用已经在播,查 sink-inputs 里的「Sink Input #数字」并挪到 HDMI
pactl move-sink-input <SINK_INPUT_ID> '<HDMI_SINK>'

# 若 RustDesk 已在采,查 source-outputs 里的「Source Output #数字」并挪到 HDMI monitor
pactl move-source-output <SOURCE_OUTPUT_ID> '<HDMI_MONITOR>'

改完后:控制端断开再连一次,并在本机播放一段有声音的内容验证。


持久化(本机已部署)

架构总览

应用播放 ─▶ rustdesk_capture (虚拟 sink, 受系统音量控制)
              │
              ├─▶ monitor ─▶ RustDesk 采集 ─▶ [软件音量镜像] ─▶ 远端
              │
              └─▶ loopback (固定 100%, 低延迟) ─▶ HDMI 诱骗器

之所以走虚拟 sink 而不是直接采 HDMI monitor:HDMI sink 的 monitor 抓取点位于硬件音量节点之前,系统音量条对远端音频完全无效(远端听到的永远是满音量)。

但 PipeWire 的 monitor port 始终位于 sink 音量节点之前,单纯改虚拟 sink 音量也无法衰减 monitor 输出。所以本方案在 keep 脚本里把虚拟 sink 当前音量实时镜像到 RustDesk 自身 source-output 的软件音量——RustDesk 采集流末端的软件衰减直接作用于发送给远端的 PCM 数据,调节系统音量条因此可以真正控制远端音量。

层 1:PipeWire 虚拟 sink(loopback 模块)

文件 ~/.config/pipewire/pipewire.conf.d/99-rustdesk-capture-sink.conf:

context.modules = [
  { name = libpipewire-module-loopback
    args = {
      node.description = "RustDesk Capture Sink"
      capture.props = {
        node.name      = "rustdesk_capture"
        node.nick      = "RustDesk Capture"
        media.class    = "Audio/Sink"
        audio.position = [ FL FR ]
        priority.session = 3000
        priority.driver  = 3000
        node.latency   = "256/48000"
      }
      playback.props = {
        node.name   = "rustdesk_capture_loopback"
        media.name  = "RustDesk Capture → HDMI"
        node.passive = true
        target.object = "alsa_output.pci-0000_08_00.1.hdmi-stereo"
        stream.dont-remix = true
        node.latency = "256/48000"
      }
    }
  }
]

要点:

  • priority.session = 3000 保证它优先于 HDMI(2000)和 analog,被自动选为默认 sink/source。
  • node.latency = "256/48000" ≈ 5.3 ms,loopback 两端总延迟约 10 ms,听感上无明显延迟。如仍觉得延迟,可降到 128/48000(约 2.7 ms,CPU 占用略升)。
  • target.object 写死了 HDMI 节点名;换显卡时需更新此处。

修改后 systemctl --user restart pipewire pipewire-pulse 立即生效。

层 2:WirePlumber 优先级规则(备份兜底)

~/.config/wireplumber/main.lua.d/51-hdmi-priority.lua:把 HDMI 节点 priority.session 抬高到 2000。即使虚拟 sink 没起来(pipewire conf 出错时),HDMI 仍是次优默认设备,远端不会因为默认设备掉到 analog 而完全失声。

alsa_monitor.rules = alsa_monitor.rules or {}
table.insert(alsa_monitor.rules, {
  matches = {
    { { "node.name", "matches", "alsa_output.*hdmi*" } },
    { { "node.name", "matches", "alsa_input.*hdmi*" } },
  },
  apply_properties = {
    ["priority.session"] = 2000,
    ["priority.driver"]  = 2000,
  },
})

修改后 systemctl --user restart wireplumber 生效。

层 3:systemd keep 服务(路由 + 音量镜像)

~/.local/bin/rustdesk-hdmi-audio-keep.sh 由 ~/.config/systemd/user/rustdesk-hdmi-audio.service 拉起,每 2 秒:

  1. 将默认 sink/source 设为虚拟 rustdesk_capture / rustdesk_capture.monitor;
  2. 取消 monitor 静音;强制 HDMI sink 音量 100%、不静音(避免双重衰减);
  3. 把所有普通 sink-input 挪到 rustdesk_capture(loopback 自身被识别后跳过,避免环路);
  4. 把 application.name = "RustDesk" 的 source-output 挪到 rustdesk_capture.monitor,并将虚拟 sink 当前音量值/静音状态镜像到该 source-output 的软件音量上——这是远端音量真正受控的关键。
项目路径
虚拟 sink 配置~/.config/pipewire/pipewire.conf.d/99-rustdesk-capture-sink.conf
WirePlumber 规则~/.config/wireplumber/main.lua.d/51-hdmi-priority.lua
keep 脚本~/.local/bin/rustdesk-hdmi-audio-keep.sh
systemd unit~/.config/systemd/user/rustdesk-hdmi-audio.service

常用命令:

systemctl --user daemon-reload
systemctl --user enable --now rustdesk-hdmi-audio.service
systemctl --user status rustdesk-hdmi-audio.service --no-pager
journalctl --user -u rustdesk-hdmi-audio.service -f

停用:systemctl --user disable --now rustdesk-hdmi-audio.service

换硬件注意:更换主板/显卡后,99-rustdesk-capture-sink.conf 里的 target.object 需更新为新的 HDMI 节点名(pactl list short sinks 第 2 列);其他配置使用 hdmi 通配符匹配,无需修改。


使用侧注意

  • 远程要听到本机系统声,本机需有应用正在向 HDMI(或当前默认输出)播放;仅「连着没播」时 monitor 上也是空的。
  • 安装 Wine / Win32 播放器后,优先检查播放流是否真的去了 HDMI:pactl list sink-inputs 或 wpctl status。
  • 控制端可在 RustDesk 工具栏检查 音频相关选项(版本不同菜单位置可能不同);根本对齐仍是被控端 PipeWire 上「播放流进 HDMI、RustDesk 采 HDMI monitor」。

记录日期以本机整理为准;修改服务后记得 daemon-reload 再 enable --now。

0

评论 (0)

取消