npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mm-player/player-core

v0.3.0

Published

播放器核心(Headless,无 DOM,Node 可运行)

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/methodflv.jsshaka-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 内建重试) | ✓ |

适配器协议要点

  • ShakainitShakaPlayer 在创建 shaka.PlayerisBrowserSupported() 之前调用 shaka.polyfill.installAll()(安装 MSE/EME/VTTCue 等 polyfill,DASH/HLS 依赖)。网络请求重试用 SHAKA_RETRY_CONFIGmaxAttempts 含首次请求,0 为非法值,项目取 Shaka 默认值 2。误用 WHEP 的 RECONNECT_CONFIG(原默认 0)会让初始媒体分片请求直接抛 ATTEMPTS_EXHAUSTED。MP4 走 src= 原生加载,不经 Shaka networking;DASH/HLS 播放时会把 Shaka 统计映射为统一 NetworkMetrics
  • WHEP:非 trickle(end-of-candidates)流程——优先通过 recvonly transceiver 生成 Offer,不支持 addTransceiver 的旧运行时退回 offerToReceiveAudio/Video,等 ICE 收集完成后发送含完整候选的 localDescription,设置远端描述后继续等待首个异步 track,再将媒体流交给 MediaTechwhepMedia 默认为 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 报致命错误并 emit reconnectfailed
  • 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 后不得再调用实例方法。

相关文档