SMTC背后的技术:BetterLyrics如何监听Windows任意播放器正在播放的音乐
【免费下载链接】BetterLyrics一款优雅且高度自定义的歌词可视化与全能音乐播放应用,基于 WinUI3/Win2D 构建 | An elegant and deeply customizable lyrics visualizer & versatile music player, built with WinUI3/Win2D项目地址: https://gitcode.com/jayfunc/BetterLyrics
BetterLyrics是一款基于 WinUI3 构建的歌词可视化与音乐播放应用,它能监听 Windows 上任意播放器正在播放的音乐——网易云、QQ音乐、Apple Music、LX Music,甚至你没听过的播放器——并自动匹配歌词、专辑封面与翻译。这背后的"秘密",是 Windows 系统级的SMTC(System Media Transport Controls,系统媒体传输控件)机制。今天我们把这套监听架构拆解给你看。
一、什么是 SMTC?为什么它是监听任意播放器的"万能钥匙" 🗝️
Windows 10/11 自带一个全局机制:任何音乐播放器调用系统 API 后,都会向操作系统注册一个媒体会话(Media Session),也就是任务栏右下角"正在播放"小图标的来源。这个机制叫 SMTC,它原本是为了系统统一的播放控制而设计。
但开发者很快发现它的另一面:只要是合规注册的播放器,它的歌名、歌手、专辑、封面、播放进度、播放状态,全都对系统"敞开"。
BetterLyrics 借助开源库 WindowsMediaController 中的MediaManager来对接这一机制,平台相关代码都封装在 MediaManagerProvider.cs。核心动作只需一行_mediaManager.Start(),随后订阅六个系统事件:
| 系统事件 | 触发时机 | BetterLyrics 的用途 |
|---|---|---|
OnAnySessionOpened | 播放器启动并注册会话 | 自动登记新播放源 |
OnAnySessionClosed | 播放器退出 | 清理会话、切换目标 |
OnFocusedSessionChanged | 你点击了某个播放器窗口 | 感知"当前关注"的播放器 |
OnAnyMediaPropertyChanged | 歌曲信息变化(切歌) | 刷新歌曲信息、重新搜歌词 |
OnAnyPlaybackStateChanged | 播放/暂停切换 | 驱动歌词动画、任务栏进度 |
OnAnyTimelinePropertyChanged | 播放时间轴变化 | 逐秒同步歌词位置 |
这些订阅就发生在 GsmtcService.cs 的 InitMediaManager 方法 中——Gsmtc即Global SMTC,它是整个"外部播放器监听"的心脏,一个平台无关的跨层服务。
二、多播放器同时播放时,如何锁定"当前这首歌"?🎯
你完全可能一边开着网易云、一边开着 Apple Music。BetterLyrics 不会傻乎乎地抓第一个会话,而是用一套清晰的优先级算法选出"当前歌曲",逻辑集中在 GetCurrentDesiredSession 方法:
- 内置播放器正在播放→ 绝对优先(自己播的歌最该显示)
- 被聚焦的外部播放器正在播放→ 优先(你在哪个窗口前,就显示谁)
- 其他正在播放的外部播放器→ 兜底
- 有系统焦点但已暂停的会话→ 显示暂停状态
- 以上都不满足 → 依次降级,直到放弃监听
每当新会话出现,RecordMediaSession 会把它自动登记进设置列表,你在界面里就能对每个播放源单独配置:是否监听、时间轴同步阈值、位置偏移、歌词/封面搜索源等,对应模型是 MediaSourceProviderInfo.cs。
三、从"会话"到"歌曲":三步刷新 + 智能防抖 🔄
选中会话只是第一步。MediaSessionProvider.cs 实现了统一的 IMediaSessionProvider 接口,对外提供三种能力:
- 刷新媒体属性:取回歌名、歌手、专辑、流派、封面缩略图
- 刷新播放状态:Playing / Paused / Stopped
- 刷新时间轴:当前播放位置、总时长
值得注意的工程细节有两个:
- 防抖:切歌瞬间系统可能连发多次属性变化,OnAnyMediaPropertyChanged 用 1 秒防抖器合并处理,避免重复搜索歌词;
- 秒级计时器:ScrobbleTimerCallback 每秒检查播放进度,播过一半才把这首歌记入本地播放历史(也是 Last.fm Scrobble 的判定依据)——这样"切歌试听"不会被误统计。
四、识别具体播放器:SessionId 上的正则小魔法 🔍
SMTC 会话自带一个 SessionId(形如进程名或包名),BetterLyrics 用一组正则来识别"这是谁",见 PlayerIdHelper.cs:
| 播放器 | 识别特征 |
|---|---|
| 网易云 / LyricEase 等 | cloudmusic.exe等 |
| QQ 音乐 | QQMusic.exe |
| Apple Music | AppleMusic.exe/AppleInc.AppleMusicWin_ |
| LX Music | lx-music-desktop.exe等 |
| BetterLyrics 自身 | 37412.BetterLyrics_ |
识别结果会触发定制化处理,都在 OnAnyMediaPropertyChangedCoreAsync 中完成:
- Apple Music把"歌手 — 专辑"打包塞在 Artist 字段里,需要按
—拆开还原; - 网易云 / QQ 音乐会把歌曲 ID 藏在 Genres 字段中,取出来后可以走更精确的歌词匹配;
- 识别后还会按播放源配置位置偏移(PositionOffset)与同步阈值,补偿各家播放器上报时间的微小偏差,保证歌词与声音严丝合缝。
五、兜底方案:当播放器"不听话"时 🛟
有些播放器不注册 SMTC,或者进度上报不准。BetterLyrics 准备了两张"备胎":
- SSE 通道(LX Music 专属):检测到 LX Music 时启动 StartLXMusicSSE,通过本地服务器的 Server-Sent Events 流拿到精确的播放进度和封面 URL,断线还会自动重连;
- 内存读取器:对于完全无法对接的播放器,UniversalMemoryReaderProvider.cs 每秒打开目标进程,按 MemoryReaderConfig 中用户提供的内存地址定义(模块基址 + 指针链)直接读出"当前播放时间/总时长"。这是高级玩家的核武器,也是最后的防线。
六、身份的双向性:BetterLyrics 自己也是一个 SMTC 会话
监听是双向的。当使用内置播放器播放本地音乐库时,SmtcService.cs 通过 WinRT 的MediaPlayer注册属于自己的 SMTC 会话,并开启停止按钮、响应全局快捷键。于是形成了优雅闭环:BetterLyrics 既是监听者,也是被监听者——当你切回内置播放,优先级算法第 1 条立刻生效,无缝接管。
写在最后
回看这套架构:MediaManager 订阅系统事件 → 优先级算法锁定会话 → 三步刷新拿到歌曲数据 → 正则识别播放器做定制处理 → SSE/内存读取兜底。五步走完,"任意 Windows 播放器"就都成了 BetterLyrics 的数据源。
这也是开源社区的乐趣所在:如果你想为自己的播放器适配更精准的歌词匹配,PlayerIdHelper.cs 和 GsmtcService 目录 就是最好的起点。
【免费下载链接】BetterLyrics一款优雅且高度自定义的歌词可视化与全能音乐播放应用,基于 WinUI3/Win2D 构建 | An elegant and deeply customizable lyrics visualizer & versatile music player, built with WinUI3/Win2D项目地址: https://gitcode.com/jayfunc/BetterLyrics
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考