Skip to content

feat(EMI): 播放速率的基础实现#851

Merged
apoint123 merged 6 commits intoimsyy:devfrom
MoYingJi:pr/r
Feb 11, 2026
Merged

feat(EMI): 播放速率的基础实现#851
apoint123 merged 6 commits intoimsyy:devfrom
MoYingJi:pr/r

Conversation

@MoYingJi
Copy link
Copy Markdown
Collaborator

@MoYingJi MoYingJi commented Feb 8, 2026

现在可以在 EMI 中读写 SPlayer 的播放速率

变更

通用

  • SystemMediaControls 下新增 update_playback_rate 函数
  • 会在 PlayerControllersetRate 中通过 Ipc 调用 updatePlaybackRate 更新播放速率
  • SystemMediaEventType 中新增 SetRate 事件类型,并在 SystemMediaEvent 中添加了 rate 字段
  • MediaSessionManager 中监听了 SetRate 事件,并调用 PlayerController 中的 setRate

Linux 实现

  • 通过 mpris-serverset_rateconnect_set_rate 来设置和监听 MPRIS 的 Rate 属性
  • 设置了 MinimumRateMaximumRate

macOS 实现

AI 写的,我也不知道(

参考:

备注

此提交为 AI 生成后人工更正,Linux 实现在 KDE 下测试正常工作,macOS 实现也会找人测的

已在 此处 执行 GitHub Actions,可以下载测试

未产生实际变更的自动生成代码均未包含在本提交中,native/external-media-integration/index.d.ts 产生了实际变更,在提交前已格式化以匹配仓库中的文件(现在自动格式化排除了这些自动生成文件)

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @MoYingJi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在为应用程序的外部媒体集成模块引入播放速率控制功能。通过建立新的 IPC 通信机制、扩展系统媒体事件模型,并为 Linux 和 macOS 提供平台特定的实现,它使得应用程序能够与操作系统的媒体控件无缝交互,从而允许用户通过系统级界面调整播放速率。这显著提升了用户体验,并增强了应用程序与外部媒体环境的兼容性。

Highlights

  • 播放速率功能实现: 为外部媒体集成 (EMI) 模块实现了播放速率的基础功能,允许外部媒体控件读取和写入播放速率。
  • 跨平台支持: 该功能已在 Linux 和 macOS 平台上实现,其中 Linux 使用 MPRIS,macOS 使用 MPNowPlayingInfoCenter。Windows 平台目前尚未实现此功能。
  • IPC 通信: 新增了 media-update-playback-rate IPC 通道,用于在渲染进程和主进程之间传递播放速率更新事件。
  • 系统媒体事件扩展: 在 SystemMediaEvent 接口中添加了 SetRate 事件类型和 rate 字段,以支持处理来自外部媒体控件的播放速率变更请求。
  • 播放器控制器集成: PlayerControllersetRate 方法现在会通过 MediaSessionManager 调用 updatePlaybackRate,将播放速率同步到系统媒体控件。
  • MPRIS (Linux) 集成: 通过 mpris-server 实现了对 MPRIS Rate 属性的设置和监听,并设置了 MinimumRate (0.5) 和 MaximumRate (2.0)。
Changelog
  • electron/main/ipc/ipc-media.ts
    • 新增了 media-update-playback-rate IPC 监听器,用于接收并处理播放速率更新请求。
  • native/external-media-integration/index.d.ts
    • SystemMediaEvent 接口中添加了可选的 rate?: number 字段。
    • SystemMediaEventType 类型中新增了 SetRate 事件类型。
    • 声明了 updatePlaybackRate(rate: number): void 函数,用于更新播放速率。
  • native/external-media-integration/src/lib.rs
    • 新增了 update_playback_rate NAPI 函数,用于更新系统媒体控件的播放速率。
  • native/external-media-integration/src/model.rs
    • SystemMediaEventType 枚举中添加了 SetRate 成员。
    • SystemMediaEvent 结构体中添加了可选的 rate: Option<f64> 字段。
    • SystemMediaEvent 结构体新增了 set_rate 构造函数。
  • native/external-media-integration/src/sys_media/linux.rs
    • MprisCommand 枚举中添加了 UpdatePlaybackRate(f64) 成员。
    • 实现了 player.connect_set_rate,用于监听 MPRIS 的播放速率变化并分发 SystemMediaEvent::set_rate
    • handle_command 中处理 MprisCommand::UpdatePlaybackRate,调用 player.set_rate 更新 MPRIS 播放速率。
    • 为 MPRIS 播放器设置了 minimum_rate (0.5) 和 maximum_rate (2.0)。
    • LinuxImpl 实现了 update_playback_rate 方法,发送 MprisCommand::UpdatePlaybackRate
  • native/external-media-integration/src/sys_media/macos.rs
    • 导入了 MPNowPlayingInfoPropertyPlaybackRate
    • MacosImpl 实现了 update_playback_rate 方法,用于更新 MPNowPlayingInfoCenter 中的播放速率信息。
  • native/external-media-integration/src/sys_media/mod.rs
    • SystemMediaControls trait 中添加了 update_playback_rate 方法。
    • NoOpControls 实现了 update_playback_rate 方法(空操作)。
  • native/external-media-integration/src/sys_media/windows.rs
    • WindowsImpl 实现了 update_playback_rate 方法(目前为空操作,表示未实现)。
  • src/core/player/MediaSessionManager.ts
    • 导入了 sendMediaPlaybackRate 函数。
    • handleSystemMediaEvent 中添加了 SetRate 事件的处理逻辑,调用 player.setRate
    • 在初始化时,通过 sendMediaPlaybackRate 同步初始播放速率。
    • 新增了 updatePlaybackRate 公有方法,用于通过 IPC 发送播放速率更新。
  • src/core/player/PlayerController.ts
    • setRate 方法中,除了调用 audioManager.setRate 外,还新增了调用 mediaSessionManager.updatePlaybackRate(rate) 来更新系统播放速率。
  • src/core/player/PlayerIpc.ts
    • 新增了 sendMediaPlaybackRate 函数,用于向主进程发送 media-update-playback-rate IPC 消息。
Activity
  • 目前没有检测到此拉取请求的评论或审查活动。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@MoYingJi MoYingJi marked this pull request as draft February 8, 2026 21:06
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR implements playback rate synchronization with native media controls, covering Linux and macOS platforms, and the code structure is clear. I mainly proposed two improvement suggestions:

  1. In the Linux implementation, it is recommended to extract the hardcoded playback rate range into constants to improve code maintainability.
  2. In the macOS implementation, currently only one-way rate updates from the application to the system are implemented. It is recommended to add handling for rate change events from the system UI to achieve complete bidirectional synchronization.

Apart from that, the overall implementation has successfully achieved its intended goals. Good job!

Comment thread native/external-media-integration/src/sys_media/linux.rs Outdated
Comment thread native/external-media-integration/src/sys_media/macos.rs
@MoYingJi MoYingJi force-pushed the pr/r branch 5 times, most recently from 2d92a4b to a6a80cb Compare February 8, 2026 21:45
@MoYingJi
Copy link
Copy Markdown
Collaborator Author

MoYingJi commented Feb 8, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 成功地为 Linux 和 macOS 平台实现了播放速率控制功能,整体代码结构清晰,横跨了前端、IPC 通信和原生模块。代码变更符合 PR 描述,逻辑清晰。

我发现了一个在 macOS 实现中的严重内存安全问题,并已在具体评论中提出修复建议。此外,我还对 Linux 实现中的硬编码值提出了一些改进建议,以提高代码的可维护性。

总体来说,这是一次很棒的功能添加,在修正了指出的问题后即可合并。

Comment thread native/external-media-integration/src/sys_media/macos.rs Outdated
Comment on lines +326 to +327
.minimum_rate(0.2)
.maximum_rate(2.0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

建议将硬编码的最小和最大播放速率值 0.22.0 提取为具名常量,以提高代码的可读性和可维护性。例如,可以在文件顶部定义:

const MINIMUM_RATE: f64 = 0.2;
const MAXIMUM_RATE: f64 = 2.0;

然后在构建 Player 时使用这些常量。

@MoYingJi MoYingJi force-pushed the pr/r branch 2 times, most recently from 184c975 to 7b4cbfb Compare February 8, 2026 22:31
@MoYingJi
Copy link
Copy Markdown
Collaborator Author

MoYingJi commented Feb 8, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 实现了在 Linux 和 macOS 上的播放速率控制功能,代码结构清晰,跨越了 TypeScript 前端、Electron 主进程和 Rust 原生模块。整体实现遵循了项目现有的模式。我主要提出了一些关于将硬编码的数值(如播放速率范围)提取为常量的建议,以提高代码的可维护性和跨平台一致性。macOS 部分的实现虽然是 AI 生成,但看起来是正确的,与平台 API 的使用方式一致,建议按计划进行充分测试。

Comment on lines +326 to +327
.minimum_rate(0.2)
.maximum_rate(2.0)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

建议将硬编码的最小和最大播放速率(0.2 和 2.0)定义为常量。这可以提高代码的可读性和可维护性,并有助于确保不同平台实现之间的一致性。

例如,可以在文件顶部定义:

const MINIMUM_RATE: f64 = 0.2;
const MAXIMUM_RATE: f64 = 2.0;

然后在构建 Player 时使用这些常量。

Comment on lines +167 to +176
let rates = NSArray::from_retained_slice(&[
NSNumber::new_f64(0.25),
NSNumber::new_f64(0.5),
NSNumber::new_f64(0.75),
NSNumber::new_f64(1.0),
NSNumber::new_f64(1.25),
NSNumber::new_f64(1.5),
NSNumber::new_f64(1.75),
NSNumber::new_f64(2.0),
]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

建议将硬编码的支持播放速率列表提取为常量,以提高代码的可读性和可维护性。可以定义一个 f64 切片常量,然后在函数中动态构建 NSArray,这样更清晰。

// 可以在文件或模块级别定义
const SUPPORTED_RATES: &[f64] = &[0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0];

// 在函数中使用
let rates_vec: Vec<_> = SUPPORTED_RATES
    .iter()
    .map(|&r| NSNumber::new_f64(r))
    .collect();
let rates = NSArray::from_vec(rates_vec);
command.setSupportedPlaybackRates(&rates);

NSArray::from_retained_slice 在这里不是最理想的,因为它需要一个 Retained<T> 的切片,导致了多行 NSNumber::new_f64 的调用。使用 from_vec 会更简洁。

@MoYingJi
Copy link
Copy Markdown
Collaborator Author

@ITManCHINA 记得来测测 macOS 端喵

星谷米游姬-我吃一口

@ITManCHINA
Copy link
Copy Markdown
Contributor

在测了在测了
丝柯克-弹奏

@ITManCHINA
Copy link
Copy Markdown
Contributor

@ITManCHINA 记得来测测 macOS 端喵

星谷米游姬-我吃一口

macOS的倍速播放一切正常
橘福福-嗷呜

@MoYingJi MoYingJi marked this pull request as ready for review February 11, 2026 07:32
Copilot AI review requested due to automatic review settings February 11, 2026 07:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 为 EMI(External Media Integration)增加“播放速率(playback rate)”的读写能力,使应用可将当前倍速同步到系统媒体控件,并能接收系统媒体控件发起的倍速变更事件(Linux / macOS)。

Changes:

  • 在渲染进程与主进程之间新增“播放速率更新”IPC 通道,并调用 EMI 的 updatePlaybackRate
  • Rust 侧扩展 SystemMediaControls trait 与 SystemMediaEvent,新增 update_playback_rateSetRate 事件。
  • Linux(MPRIS)与 macOS(MPRemoteCommand / NowPlayingInfo)实现倍速同步与倍速事件监听。

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/core/player/PlayerIpc.ts 新增 sendMediaPlaybackRate,用于向主进程发送倍速更新 IPC。
src/core/player/PlayerController.ts setRate 中同步更新系统媒体控件倍速。
src/core/player/MediaSessionManager.ts 初始化时同步初始倍速;监听 SetRate 事件并调用 player.setRate;新增 updatePlaybackRate 方法。
electron/main/ipc/ipc-media.ts 主进程新增 "media-update-playback-rate" 监听并调用 emi.updatePlaybackRate
native/external-media-integration/src/lib.rs 暴露 N-API 方法 update_playback_rate 给 JS 调用。
native/external-media-integration/src/model.rs SystemMediaEventType 增加 SetRate,并在事件对象中增加 rate 字段。
native/external-media-integration/src/sys_media/mod.rs SystemMediaControls trait 增加 update_playback_rate
native/external-media-integration/src/sys_media/linux.rs 基于 mpris-server 的 set_rate / connect_set_rate 实现倍速同步与事件派发,并设置 min/max rate。
native/external-media-integration/src/sys_media/macos.rs 增加 MPChangePlaybackRateCommand 处理与 NowPlayingInfo 的 PlaybackRate 更新。
native/external-media-integration/src/sys_media/windows.rs trait 补齐 update_playback_rate(当前未实现)。
native/external-media-integration/index.d.ts 更新 TS 声明:事件增加 rate,事件类型增加 SetRate,新增 updatePlaybackRate 导出。

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/player/PlayerIpc.ts Outdated
Comment thread src/core/player/PlayerController.ts Outdated
Comment on lines +944 to +947
audioManager.setRate(rate);

// 更新系统播放速率
mediaSessionManager.updatePlaybackRate(rate);
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里会把 rate 直接同步到系统媒体控件;如果 rate 来自原生事件(SetRate)或外部调用,可能出现 NaN/Infinity/越界值,进而把非法值写入 statusStore、音频引擎和 EMI。建议在调用 audioManager.setRate / updatePlaybackRate 前对 rateNumber.isFinite 校验并按应用支持范围(例如 0.2~2.0)进行 clamp/round。

Copilot uses AI. Check for mistakes.
unsafe {
command.setEnabled(true);
// 这里可以设置 supportedPlaybackRates,但如果不设置,系统可能会提供默认选项或允许任意值
let rates = NSArray::from_retained_slice(&[
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

应用内倍速下限看起来支持到 0.2x(例如 UI slider 的 min),但这里设置的 supportedPlaybackRates 从 0.25 开始,导致系统侧可选速率与应用内可设置范围不一致。建议把 0.2 加入列表,或在上层/本地实现中统一最小倍速为 0.25。

Suggested change
let rates = NSArray::from_retained_slice(&[
let rates = NSArray::from_retained_slice(&[
NSNumber::new_f64(0.2),

Copilot uses AI. Check for mistakes.
Comment thread native/external-media-integration/src/sys_media/windows.rs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +205 to +212
* @description 通过外部媒体集成模块更新媒体控件的播放速率
* @note 仅在 Electron 上有效
* @param rate - 播放速率 (默认 1.0)
* @see {@link EmiModule.updatePlaybackRate 外部媒体集成模块的 `updatePlaybackRate` 方法}
*/
export const sendMediaPlaybackRate = (rate: number) => {
if (isElectron) window.electron.ipcRenderer.send("media-update-playback-rate", { rate });
};
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSDoc 写了 rate 有“默认 1.0”,但函数签名是必传参数且没有提供默认值。建议移除“默认 1.0”的描述,或把参数改为可选并在函数内部默认到 1.0,避免文档与行为不一致。

Copilot uses AI. Check for mistakes.
let rate = args.RequestedPlaybackRate()?;
debug!(rate, "SMTC 请求更改播放速率");
dispatch_event(SystemMediaEvent::set_rate(rate));
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypedEventHandler 的闭包返回类型是 windows::core::Result<()>,但当前实现里在 if let Some(args) ... 之后没有返回 Ok(()),导致闭包在某些路径上返回 ()(无法编译)。请在闭包末尾补上 Ok(())(与其它 handler 保持一致)。

Suggested change
}
}
Ok(())

Copilot uses AI. Check for mistakes.
Comment on lines +76 to +77
if (event.rate != null) {
player.setRate(event.rate);
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetRate 事件来自系统媒体控件,event.rate 可能是 NaN/Infinity 或超出播放器支持范围(注释里写的是 0.25-2.0)。建议在这里对 event.rateNumber.isFinite 校验并 clamp 到允许区间,再调用 player.setRate(...),避免把异常值写入 store/传给底层音频引擎。

Suggested change
if (event.rate != null) {
player.setRate(event.rate);
if (event.rate != null && Number.isFinite(event.rate)) {
const clampedRate = Math.min(2.0, Math.max(0.25, event.rate));
player.setRate(clampedRate);

Copilot uses AI. Check for mistakes.
MoYingJi and others added 5 commits February 11, 2026 21:28
现在可以在 EMI 中读写 SPlayer 的播放速率,支持 Linux 和 macOS

## 变更

### 通用

- 在 `SystemMediaControls` 下新增 `update_playback_rate` 函数
- 会在 `PlayerController` 的 `setRate` 中通过 Ipc 调用 `updatePlaybackRate` 更新播放速率
- 在 `SystemMediaEventType` 中新增 `SetRate` 事件类型,并在 `SystemMediaEvent` 中添加了 `rate` 字段
- 在 `MediaSessionManager` 中监听了 `SetRate` 事件,并调用 `PlayerController` 中的 `setRate`

### Linux 实现

- 通过 `mpris-server` 的 `set_rate` 和 `connect_set_rate` 来设置和监听 MPRIS 的 `Rate` 属性
- 设置了 `MinimumRate` 和 `MaximumRate`

### macOS 实现

AI 写的,我也不知道(

## 备注

此提交为 AI 生成后人工更正,Linux 实现在 KDE 下测试正常工作,macOS 实现也会找人测的
@apoint123 apoint123 merged commit 2126981 into imsyy:dev Feb 11, 2026
@MoYingJi MoYingJi deleted the pr/r branch February 12, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants