@bimccfyra/fyraplayer
v1.0.1
Published
Modern, extensible low-latency web player (WebRTC, LL-HLS, WS+WASM) with plugin/middleware architecture.
Readme
FyraPlayer
通用低延迟 Web 播放器,支持 WebRTC、LL-HLS/DASH、WebSocket+WebCodecs 等多种播放技术,采用插件/中间件架构设计。
1.0 商业基线
FyraPlayer 1.0.0 定位为可控场景下的商业基线 SDK:核心播放、插件生命周期、公共导出、示例、发布检查和主要浏览器证据已经收口。支持范围以 支持场景与已知限制、播放验证矩阵 和 1.0 发布就绪复盘 为准。
当前 1.0 不承诺所有浏览器、所有协议、所有后端组合的无条件支持。WebRTC TURN/受控中断恢复、项目专用 direct fMP4、Safari/Firefox 完整矩阵、DRM、字幕、广告/埋点、前端录制、GB28181 服务端栈和 PTZ 设备执行均按文档继续跟进或保持插件/后端边界。
特性
- 多播放技术: WebRTC (OME/WHIP/WHEP)、WS-raw (WebCodecs, FLV/TS)、HLS/DASH、fMP4、GB28181 网关适配、本地文件
- 本地文件播放: 支持 MP4(原生)、TS/MTS(mpegts.js)、FLV 等格式的本地文件播放
- 高可靠性: 自动重连、ICE 重启、playoutDelayHint、基于丢包的 ABR 回退、DataChannel 心跳
- 可扩展: 中间件管线、插件管理器、信令适配器、流媒体服务器 URL 工厂、元数据桥接 (KLV/SEI)
- 可选 UI 插件: 通过
createUiComponentsPlugin()显式启用,嵌入场景可只使用核心播放器 - 渲染器分离: PSV 全景、Cesium 3D 地图适配器放在各自独立项目
架构
┌─────────────────────────────────────────────────────────────┐
│ 用户应用 │
├─────────────────────────────────────────────────────────────┤
│ @beeviz/fyrapano @beeviz/cesium 其他渲染器集成 │ ← 外部项目
├─────────────────────────────────────────────────────────────┤
│ plugins/metadata plugins/engines │ ← 播放相关插件
├─────────────────────────────────────────────────────────────┤
│ ui/ │ ← 可选 UI 插件
├─────────────────────────────────────────────────────────────┤
│ FyraPlayer │ ← 主入口
├─────────────────────────────────────────────────────────────┤
│ core/ techs/ render/ │ ← 核心层
└─────────────────────────────────────────────────────────────┘播放技术 (Tech)
| Tech | 文件 | 用途 | 底层库 |
|------|------|------|--------|
| webrtc | tech-webrtc.ts | WebRTC 低延迟播放 (WHIP/WHEP/Oven-WS) | 原生 WebRTC |
| hls | tech-hls.ts | HLS/LL-HLS 自适应码率播放 (.m3u8) | hls.js |
| dash | tech-dash.ts | DASH 自适应码率播放 (.mpd) | dash.js(通过 @bimccfyra/fyraplayer/plugins/dash 显式启用) |
| fmp4 | tech-fmp4.ts | fMP4 直播流 (无清单,HTTP/WS + MSE) | 原生 MSE |
| ws-raw | tech-ws-raw.ts | WebSocket + WebCodecs (FLV/TS) | 自研 + mpegts.js |
| gb28181 | tech-gb28181.ts | 服务端 GB28181 网关 invite/control + FLV/TS 播放适配 | mpegts.js |
| file | tech-file.ts | 本地/远程文件播放 (MP4/TS/FLV) | 原生 + mpegts.js |
格式与 Tech 对应关系
| 格式 | 后缀/协议 | 推荐 Tech | 说明 |
|------|-----------|-----------|------|
| HLS | .m3u8 | hls | 支持 LL-HLS |
| DASH | .mpd | dash | 支持 ABR |
| fMP4 直播 | .m4s, HTTP/WS | fmp4 | 无清单的 fMP4 流 |
| FLV 直播 | ws://...flv | ws-raw | WebSocket FLV |
| HTTP-FLV | http://...flv | ws-raw | mpegts.js 播放 |
| TS 直播 | ws://...ts | ws-raw | WebSocket TS |
| MP4 文件 | .mp4 | file | 浏览器原生 |
| TS 文件 | .ts, .mts | file | mpegts.js 播放 |
| WebRTC | wss://, http:// | webrtc | WHEP/WHIP/Oven |
外部依赖
| 库 | 版本 | 用途 |
|----|------|------|
| hls.js | ^1.6.16 | HLS/LL-HLS 流播放 |
| dashjs | ^5.1.1 | DASH 流播放(optional peer,使用 DASH 插件时安装) |
| mpegts.js | ^1.8.0 | TS/FLV 容器解析 + MSE 播放(optional peer,仅 TS/FLV/GB fallback 路径需要) |
| mp4box | ^0.5.4 | MP4 容器解析(optional peer,仅 webCodecs.mp4boxLoader / 全局 MP4Box 路径需要) |
安装
pnpm install
pnpm buildDASH is optional to keep default application bundles quiet under Vite/Rolldown.
createDashTechPlugin() does not make the default @bimccfyra/fyraplayer entry import
dash.js. By default it loads the packaged dist/vendor/dash.all.min.js UMD
script only when a DASH source is played. Host apps can also provide their own
loader when they want tighter CDN/cache control.
For Vite/Rolldown applications, pass the packaged vendor file as an emitted asset URL so production builds publish the runtime script with the app:
import { createDashTechPlugin } from '@bimccfyra/fyraplayer/plugins/dash';
import dashJsScriptUrl from '@bimccfyra/fyraplayer/vendor/dash.all.min.js?url';
const player = new FyraPlayer({
video: '#video',
sources: [{ type: 'dash', url: 'https://example.com/manifest.mpd', preferTech: 'dash' }],
plugins: [createDashTechPlugin({ scriptUrl: dashJsScriptUrl })]
});
const playerWithCustomDash = new FyraPlayer({
video: '#video',
sources: [{ type: 'dash', url: 'https://example.com/manifest.mpd', preferTech: 'dash' }],
plugins: [
createDashTechPlugin({
dashjsLoader: () => import('dashjs')
})
]
});TS/FLV playback is also optional for supply-chain stability. The default package
does not install mpegts.js because [email protected] depends on a
GitHub-hosted webworkify-webpack tarball. Applications that use ws-raw,
GB28181 FLV/TS gateway output, or TS file fallback should install and pass an
explicit loader:
const player = new FyraPlayer({
video: '#video',
sources: [{ type: 'ws-raw', url: 'https://example.com/live.flv', codec: 'h264', transport: 'flv' }],
mpegtsLoader: () => import('mpegts.js')
});快速开始
import { FyraPlayer } from '@bimccfyra/fyraplayer';
const player = new FyraPlayer({
video: '#video',
sources: [{ type: 'hls', url: 'https://example.com/stream.m3u8' }],
techOrder: ['webrtc', 'ws-raw', 'hls', 'dash', 'fmp4', 'file'],
autoplay: true,
muted: true
});
player.on('ready', () => console.log('Player ready'));
player.on('error', (err) => console.error('Error:', err));
await player.init();启用 UI 插件
核心播放器不会默认挂载控件;需要 UI 时显式加入插件:
import { FyraPlayer } from '@bimccfyra/fyraplayer';
import { createUiComponentsPlugin } from '@bimccfyra/fyraplayer/plugins/ui-components';
const player = new FyraPlayer({
video: '#video',
sources: [{ type: 'hls', url: 'https://example.com/stream.m3u8' }],
plugins: [
createUiComponentsPlugin({
target: '.player-shell',
poster: '/poster.jpg'
})
]
});
await player.init();插件能力速查
FyraPlayer 的核心只负责播放生命周期、Tech 选择、事件和中间件执行。产品能力通过插件显式启用,推荐从独立子路径导入,避免把不需要的能力打进业务包。
| 插件入口 | 工厂/能力 | 适用场景 |
|---|---|---|
| @bimccfyra/fyraplayer/plugins/ui-components | createUiComponentsPlugin() | 播放控制条、质量/源选择、重试、截图入口、录制按钮钩子 |
| @bimccfyra/fyraplayer/plugins/diagnostics | createDiagnosticsPlugin(), createDebugPanelPlugin() | 当前状态、最近错误/网络/QoS/ICE 线索、诊断导出和调试面板 |
| @bimccfyra/fyraplayer/plugins/storage | createStoragePlugin() | 音量、静音、倍速、清晰度、低延迟偏好和上次播放源持久化 |
| @bimccfyra/fyraplayer/plugins/auth | createAuthSigningMiddleware(), createAuthRecoveryPlugin() | 请求头、凭证、Token、URL 签名和显式 401/403 恢复 |
| @bimccfyra/fyraplayer/plugins/recording-api | createRecordingApiPlugin() | 对接后端开始/停止/查询录制;不做浏览器本地录制 |
| @bimccfyra/fyraplayer/plugins/performance | createPerformanceMonitorPlugin() | FPS、延迟、pending buffer 等性能预算告警 |
| @bimccfyra/fyraplayer/plugins/metrics | createMetricsPlugin() | 业务自定义指标回调或上报适配 |
| @bimccfyra/fyraplayer/plugins/reconnect | createReconnectPlugin() | 重连事件日志和产品侧回调;不替代核心重连策略 |
| @bimccfyra/fyraplayer/plugins/metadata | createMetadataPlugin(), KlvBridge | KLV/SEI/private-data 等元数据业务解析 |
| @bimccfyra/fyraplayer/plugins/engines | createSourceResolverMiddleware() | MediaMTX/OME 等服务端播放 URL 到 source/fallback 链的转换 |
| @bimccfyra/fyraplayer/plugins/panoramalite | createPanoramaLitePlugin() | 轻量 WebGL2 全景图片、全景视频和全景直播渲染 |
插件应在创建 FyraPlayer 时通过 plugins 数组安装。产品 UI 可以显示已安装插件并开放安全的运行时模式开关,例如 PanoramaLite 的 handle.setEnabled();examples/basic.html 已采用统一资源列表和普通/全景模式切换示例。视频源平台可通过 source.presentation.mode = 'panorama'、source.meta.presentation 或 tags: ['panorama'] 标记全景源,应用层可用 isPanoramaSource() / getSourcePresentation() 触发全景模式。当前 SDK 不提供任意热安装插件的公共 API。具体边界见 插件化边界地图 和 SDK 发布与集成。
使用示例
HLS 播放
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'hls',
url: 'https://example.com/stream.m3u8',
lowLatency: true, // 启用 LL-HLS
preferTech: 'hls'
}]
});DASH 播放
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'dash',
url: 'https://example.com/stream.mpd',
preferTech: 'dash'
}]
});fMP4 直播流(无清单)
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'fmp4',
url: 'https://example.com/live/stream',
transport: 'http', // 或 'ws'
codec: 'h264',
isLive: true,
preferTech: 'fmp4'
}]
});WebSocket FLV/TS 流
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'ws-raw',
url: 'wss://server/stream.flv',
codec: 'h264',
transport: 'flv', // 或 'ts'
preferTech: 'ws-raw'
}]
});ws-raw defaults to the stable MSE path (pipeline: 'mse'). The in-house
WebCodecs/WASM path is opt-in and should be treated as experimental:
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'ws-raw',
url: 'wss://server/stream.ts',
codec: 'h264',
transport: 'ts',
pipeline: 'experimental',
preferTech: 'ws-raw'
}]
});本地文件播放
// 通过 file input 选择本地文件
const fileInput = document.getElementById('file-input');
fileInput.onchange = async () => {
const file = fileInput.files[0];
const blobUrl = URL.createObjectURL(file);
const ext = file.name.split('.').pop().toLowerCase();
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'file',
url: blobUrl,
// 对于 blob URL,需要指定 container 类型
container: ext === 'ts' ? 'ts' : (ext === 'mp4' ? 'mp4' : undefined)
}]
});
await player.init();
};元数据提取 (KLV/SEI)
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'ws-raw',
url: 'wss://server/stream',
codec: 'h264',
transport: 'ts',
metadata: {
privateData: { enable: true },
sei: { enable: true }
}
}]
});
player.on('metadata', (evt) => {
// 传递给 @aspect/openklv 解析
console.log('Metadata:', evt.type, evt.raw);
});API 文档
详细 API 文档请参阅 docs/api.md
长期跟进文档
- 商业化成熟度路线图:当前商业级差距、优先级、验收门槛和延期插件占位。
- 插件化边界地图:已插件化能力、候选插件、核心边界和插件 API 后续方向。
- 渲染桥边界:PSV/Cesium/map/panorama 适配器的外部化方案和通用视频/画布输出契约。
- PanoramaLite 计划:轻量 WebGL2 全景图片、全景视频和全景直播插件专项设计与跟踪。
- 支持场景与已知限制:当前可对外承诺的场景、实验项和明确边界。
- 播放验证矩阵:真实浏览器/协议验证范围、样例流、场景和证据记录。
- 性能基线:可选性能预算插件、默认阈值和后续 profiling 证据状态。
- SDK 发布与集成:ESM、插件子路径、IIFE/CDN 包、发布检查清单和迁移规则。
- 1.0 发布就绪复盘:正式 1.0 前的架构、功能、证据、风险和后续任务复盘。
- 代码审查对齐文档:历史审查结论、已完成整改和复审记录。
- P0 执行清单:第一批正确性修复的执行与验收记录。
脚本
pnpm build— 构建到dist/pnpm build:release— 构建 ESM 包并生成浏览器 IIFE 包pnpm bundle:iife— 生成dist/fyraplayer.iife.jspnpm check:release— 运行发布前自检流水线pnpm check:sources— 校验examples/sources.js的示例源结构pnpm check:public-api— 校验 README/API 关键公共用法可通过 TypeScript 编译pnpm check:exports— 清理并重建dist/,校验package.jsonexports 指向的文件存在pnpm smoke:panoramalite— 启动 PanoramaLite 示例并做浏览器 canvas 像素/交互 smokepnpm test— 运行 Jest 测试pnpm dev:vite— Vite 开发服务器pnpm bundle:examples— 打包示例
示例资产
examples/basic.html:主要 ESM 播放器示例、协议测试入口,以及普通播放/ PanoramaLite 全景播放统一 demo。examples/sources.js:示例流配置清单,受pnpm check:sources校验。examples/minimal-iife.html:无构建环境的 IIFE 集成示例。examples/panoramalite.html:PanoramaLite WebGL2 全景渲染专项示例和 smoke 目标。
PSV、Cesium、地图、全景和 KLV 业务解析示例不再放在 examples/ 里作为可运行占位页面;对应集成边界请看 docs/render-bridges.md、docs/integration-psv.md、docs/integration-cesium.md 和 docs/klv-integration.md。
与 @aspect/openklv 集成
FyraPlayer 可以与 @aspect/openklv 配合使用,实现无人机视频的 KLV 元数据提取和解析。
架构关系
┌─────────────────────────────────────────────────────────────────┐
│ 业务应用 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ FyraPlayer │ │
│ │ ┌─────────────┐ │ │
│ │ │ ts-demuxer │ ← TS 解复用(视频/音频/私有数据) │ │
│ │ └──────┬──────┘ │ │
│ │ │ onPrivateData(pid, bytes, pts) │ │
│ └─────────┼───────────────────────────────────────────────┘ │
│ │ │
│ │ 私有数据(可能是 KLV) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ @aspect/openklv │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ KLVParser │→ │ SyncEngine │→ │ DroneState │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘集成示例
import { FyraPlayer } from '@bimccfyra/fyraplayer';
import { KLVParser, SyncEngine } from '@aspect/openklv';
const parser = new KLVParser();
const syncEngine = new SyncEngine();
const player = new FyraPlayer({
video: '#video',
sources: [{
type: 'ws-raw',
url: 'wss://server/stream',
codec: 'h264',
transport: 'ts',
metadata: { privateData: { enable: true } }
}]
});
// FyraPlayer 提取私有数据,openklv 解析 KLV 语义
player.on('metadata', (evt) => {
if (evt.type === 'private-data') {
// 检测 KLV Universal Label (06 0E 2B 34)
if (evt.raw[0] === 0x06 && evt.raw[1] === 0x0E) {
const frame = parser.parse(evt.raw, BigInt(evt.pts * 90));
if (frame) syncEngine.push(frame);
}
}
});
// 获取同步状态用于投影
function render() {
const videoPts = BigInt(player.currentTime * 90000);
const state = syncEngine.getInterpolatedStateAtPts(videoPts);
if (state) {
updateProjection(state); // 更新 Cesium 投影
}
requestAnimationFrame(render);
}相关项目
- @aspect/openklv - KLV/MISB 元数据解析
- @beeviz/fyrapano - 全景直播组件
- beeviz - 视频投影业务层
License
MIT
