@mm-player/player-core
v0.3.0
Published
播放器核心(Headless,无 DOM,Node 可运行)
Maintainers
Readme
@mm-player/player-core
mm-player Headless 核心:无 DOM、无
HTMLElement/document/window访问,可在 Node / Electron / React / Vue / Native Wrapper 中复用。
模块用途
提供与 UI 解耦的播放器核心:统一命令入口(CommandBus)、唯一状态源(StateManager)、能力声明(Capability)与多协议适配器加载。媒体能力由注入的 MediaTech 提供;不传 tech 时为 headless 模式,不创建任何 DOM。
适用场景
- 需要自定义渲染层(不用内置 UI)的场景。
- Node / SSR / 测试环境下的状态机与适配器逻辑验证。
- Electron / Native Wrapper 中复用同一套播放控制。
如需开箱即用的浏览器 UI,直接使用 @mm-player/player-ui 或兼容入口 @mm-player/player。
引入
pnpm add @mm-player/player-core运行时依赖:@mm-custom/method、flv.js、shaka-player(已在 dependencies 中声明,安装时自动获取;构建产物中为 external,按需动态加载)。
最小示例
import { PlayerCore } from '@mm-player/player-core';
import type { PlayerConfig } from '@mm-player/player-core';
// headless:不创建 DOM,仅驱动状态与适配器
const core = new PlayerCore({ bandwidthMode: 'low' });
// 订阅状态(唯一真相源 PlayerSnapshot)
const unsubscribe = core.subscribeState((snap) => {
console.log(snap.status, snap.currentTime, snap.buffering);
});
// 监听事件(在适配器就绪后绑定,跨适配器重建自动重绑)
core.on('error', (err) => console.error('error', err));
// load 默认使用 whep 适配器;可指定 'flv' | 'shaka'
await core.load({ url: 'https://example.com/stream.mpd', adapter: 'shaka' });
// WHEP 默认 whepMedia: 'auto';已知纯视频源也可显式使用 'video-only'
await core.load({
url: 'https://example.com/whep/stream',
adapter: 'whep',
whepMedia: 'auto'
});
// 媒体能力(play/screenshot 等)需要注入 MediaTech;headless 下调用会抛错。
// 浏览器侧由 @mm-player/player-ui 注入 VideoMediaTech。
// 释放
unsubscribe();
await core.destroy();关键 API
| 方法 | 签名 | 说明 |
| --- | --- | --- |
| constructor | (config?: PlayerConfig, tech?: MediaTech) | 不传 tech 为 headless |
| load | (config: PlayerConfig) => Promise<void> | 加载媒体源,默认 adapter: 'whep';切换前先销毁旧适配器 |
| play | () => Promise<void> | 需已注入 MediaTech |
| pause | () => void | |
| stop | () => Promise<void> | 异步等待资源释放 |
| seek | (time: number) => Promise<void> | WHEP 不支持,返回 resolve |
| setVolume | (volume: number) => void | 0–1 |
| setMuted | (muted: boolean) => void | |
| setPlaybackRate | (rate: number) => void | |
| switchQuality | (level: number) => void | 仅 Shaka 有效 |
| screenshot | (options?: CaptureOptions) => CaptureResult | 需 MediaTech 与 video readyState≥2 |
| getSnapshot | () => PlayerSnapshot | 不可变状态快照 |
| subscribeState | (listener) => () => void | 返回取消订阅 |
| dispatch | (cmd: Command) => Promise<void> | CommandBus 统一入口 |
| supports / getCapabilities | (cap) => boolean | 能力查询 |
| on / off / once / clear | | 事件监听 |
| destroy | () => Promise<void> | 异步等待适配器释放完成 |
完整签名见 docs/api.md。适配器从子入口导入:
import { WhenAdapter, FlvAdapter, ShakaAdapter } from '@mm-player/player-core/adapters';| 适配器 | 协议 | seek | 清晰度切换 | 重连 | 播放列表 |
| --- | --- | --- | --- | --- | --- |
| WhenAdapter | WHEP/WebRTC | ✗ | ✗ | ✓ | ✗ |
| FlvAdapter | FLV(flv.js) | ✓ | ✗ | ✗ | ✗ |
| ShakaAdapter | DASH/HLS(Shaka) | ✓ | ✓ | ✗(Shaka 内建重试) | ✓ |
适配器协议要点
- Shaka:
initShakaPlayer在创建shaka.Player与isBrowserSupported()之前调用shaka.polyfill.installAll()(安装 MSE/EME/VTTCue 等 polyfill,DASH/HLS 依赖)。网络请求重试用SHAKA_RETRY_CONFIG;maxAttempts含首次请求,0为非法值,项目取 Shaka 默认值2。误用 WHEP 的RECONNECT_CONFIG(原默认0)会让初始媒体分片请求直接抛ATTEMPTS_EXHAUSTED。MP4 走src=原生加载,不经 Shaka networking;DASH/HLS 播放时会把 Shaka 统计映射为统一NetworkMetrics。 - WHEP:非 trickle(end-of-candidates)流程——优先通过
recvonlytransceiver 生成 Offer,不支持addTransceiver的旧运行时退回offerToReceiveAudio/Video,等 ICE 收集完成后发送含完整候选的localDescription,设置远端描述后继续等待首个异步track,再将媒体流交给MediaTech。whepMedia默认为auto:先协商音视频;若 Answer 仅保留 Offer 的video:1、删除audio:0,则 DELETE 第一次会话、关闭旧 PeerConnection,并让第二次纯视频 Offer 沿用服务端选择的video:1;已知纯视频源可直接设为video-only,该模式收到不同的单视频 MID 时也会沿用服务端 MID 重试一次;要求音视频且禁止回退可设为audio-video。适配器只调整兼容回退时的本地 Offer MID/BUNDLE,不修改 Answer SDP。信令 POST 与资源 DELETE 均有超时并可在清理时取消,响应Location支持绝对和相对 URL。默认最多自动重连 3 次(可用setReconnectConfig调整);仅 HTTP 408、429、5xx 与网络错误重试,其他 4xx、配置和适配器错误立即失败。load/stop/destroy会取消 ICE、请求、首轨等待与重连。跨 NAT 需经rtcConfig.iceServers配置 STUN/TURN,默认iceServers: []仅收集 host 候选。
生命周期与资源释放
- 状态枚举
PlayState:IDLE / READY / LOADING / PLAYING / PAUSED / ENDED / ERROR。 stop/destroy均异步,必须await完成资源释放后再返回;禁止 fire-and-forget。destroy幂等;在adapter.destroy()完成后才置空引用,避免异步释放未完成时丢失引用。- 重复
play/pause在同状态下安全忽略。 load/stop/destroy经生命周期队列串行执行;play等待已排队加载,切换适配器前先await adapter.destroy()。
线程与回调约束
- 浏览器单线程事件循环;WebRTC 内部线程、媒体回调、定时器均回主线程。
- WHEP 重连用
isReconnecting去重;指数退避,超maxAttempts报致命错误并 emitreconnectfailed。 once监听器跨适配器重建不重触发(fired标志守卫)。- 事件源为适配器 bus,Core 聚合为唯一
PlayerSnapshot;UI 应订阅subscribeState,不直读 adapter。
常见错误与注意事项
- headless 模式调用
play/screenshot等媒体能力会抛MediaTech not injected;需注入 MediaTech 或使用 PlayerUI。 volume范围为 0–1(默认 1),不是 0–100。seek在 WHEP 实时流上无效(仅 resolve)。destroy后不得再调用实例方法。
