{"_id":"@bimccfyra/fyraplayer","_rev":"3-dad736121a5b6fdcb3bf4e67260b239c","name":"@bimccfyra/fyraplayer","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@bimccfyra/fyraplayer","version":"1.0.0","_id":"@bimccfyra/fyraplayer@1.0.0","maintainers":[{"name":"fyrabimcc","email":"yf@bimcc.com"}],"dist":{"shasum":"be4332911ed800ae989476fd319b11177e6203a7","tarball":"https://registry.npmjs.org/@bimccfyra/fyraplayer/-/fyraplayer-1.0.0.tgz","fileCount":385,"integrity":"sha512-PKEg3J8Ypfnf+Ngcfthrr9KbEjqBSZYbiS2AJIYLozI6vrWtnJIro9WyJ3duAS+2NQPuK8vu9O1u59YiQ88UEA==","signatures":[{"sig":"MEUCIQDZFN3AoAL9BtwDEeUbKJe3iQoKKdPRS24lo9wWvSknJwIgMGY9XzBpIPrcxeM9dfK3DAzs5FAefZC57+bF/qtV8ik=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3858368},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core.d.ts","import":"./dist/core.js"},"./plugins":{"types":"./dist/plugins/index.d.ts","import":"./dist/plugins/index.js"},"./plugins/auth":{"types":"./dist/plugins/auth.d.ts","import":"./dist/plugins/auth.js"},"./plugins/dash":{"types":"./dist/plugins/dash.d.ts","import":"./dist/plugins/dash.js"},"./plugins/engines":{"types":"./dist/plugins/engines/index.d.ts","import":"./dist/plugins/engines/index.js"},"./plugins/metrics":{"types":"./dist/plugins/metrics.d.ts","import":"./dist/plugins/metrics.js"},"./plugins/storage":{"types":"./dist/plugins/storage.d.ts","import":"./dist/plugins/storage.js"},"./plugins/metadata":{"types":"./dist/plugins/metadata/index.d.ts","import":"./dist/plugins/metadata/index.js"},"./plugins/reconnect":{"types":"./dist/plugins/reconnect.d.ts","import":"./dist/plugins/reconnect.js"},"./plugins/diagnostics":{"types":"./dist/plugins/diagnostics.d.ts","import":"./dist/plugins/diagnostics.js"},"./plugins/performance":{"types":"./dist/plugins/performance.d.ts","import":"./dist/plugins/performance.js"},"./techs/wsRaw/demuxer":{"types":"./dist/techs/wsRaw/demuxer.d.ts","import":"./dist/techs/wsRaw/demuxer.js"},"./plugins/panoramalite":{"types":"./dist/plugins/panoramalite.d.ts","import":"./dist/plugins/panoramalite.js"},"./plugins/recording-api":{"types":"./dist/plugins/recording.d.ts","import":"./dist/plugins/recording.js"},"./plugins/ui-components":{"types":"./dist/plugins/ui-components.d.ts","import":"./dist/plugins/ui-components.js"},"./vendor/dash.all.min.js":"./dist/vendor/dash.all.min.js"},"gitHead":"651f4ef70805409b52172b73dd9cc93284686137","scripts":{"dev":"concurrently \"pnpm bundle:examples:watch\" \"pnpm serve:examples\"","test":"jest","build":"pnpm clean && tsc -p tsconfig.json && node checks/copy-vendor.mjs","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","dev:vite":"vite --config vite.config.ts","test:watch":"jest --watch","bundle:iife":"esbuild src/iife.ts --bundle --outfile=dist/fyraplayer.iife.js --platform=browser --format=iife --global-name=FyraPlayerSDK","build:release":"pnpm build && pnpm bundle:iife","check:exports":"pnpm build && node checks/export-contract.mjs","check:release":"pnpm exec jest --runInBand && pnpm check:public-api && pnpm check:exports && pnpm check:sources && pnpm build:release","check:sources":"node checks/sources-contract.mjs","dev:vite:host":"vite --config vite.config.ts --host --port 3000","test:coverage":"jest --coverage","serve:examples":"npx http-server examples -p 5173 -c-1","bundle:examples":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm","long-run:assert":"node checks/browser-long-run-assert.mjs","check:public-api":"tsc -p tsconfig.public-api.json","long-run:browser":"node checks/browser-long-run.mjs","serve:fmp4-fixture":"node checks/ffmpeg-fmp4-fixture.mjs","smoke:panoramalite":"node checks/panoramalite-smoke.mjs","bundle:examples:watch":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm --watch"},"_npmUser":{"name":"fyrabimcc","email":"yf@bimcc.com"},"_npmVersion":"11.14.1","description":"Modern, extensible low-latency web player (WebRTC, LL-HLS, WS+WASM) with plugin/middleware architecture.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"hls.js":"^1.6.16"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.1.2","devDependencies":{"jest":"^30.4.2","vite":"^8.0.13","dashjs":"^5.1.1","mp4box":"^0.5.4","esbuild":"^0.28.0","ts-jest":"^29.4.10","fast-check":"^4.8.0","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^20.19.41"},"peerDependencies":{"dashjs":"^5.1.1","mp4box":"^0.5.4","mpegts.js":"^1.8.0"},"peerDependenciesMeta":{"dashjs":{"optional":true},"mp4box":{"optional":true},"mpegts.js":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/fyraplayer_1.0.0_1779274525504_0.7714542824501844","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@bimccfyra/fyraplayer","version":"1.0.1","_id":"@bimccfyra/fyraplayer@1.0.1","maintainers":[{"name":"fyrabimcc","email":"yf@bimcc.com"}],"dist":{"shasum":"edc92ca2ef96c4272d85cf0dc7046aed5f9c8f98","tarball":"https://registry.npmjs.org/@bimccfyra/fyraplayer/-/fyraplayer-1.0.1.tgz","fileCount":389,"integrity":"sha512-tegcD3r/ahtWKeiGKxPrGL8pN3kcN7kuzMiTnSEK62GM5gxo1lHZu/BVQLDUcDNVAmtmji444eyTsINFfeZxeg==","signatures":[{"sig":"MEUCIG5iIIGydc08dxbxEv+0+m1pILp9EJrF6TaORmbYIJ96AiEAiJ0EVmSnOsi7QQaOxSi1Wxc/6rp+Puksr5oxsc0m85A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":3878740},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core.d.ts","import":"./dist/core.js"},"./plugins":{"types":"./dist/plugins/index.d.ts","import":"./dist/plugins/index.js"},"./plugins/auth":{"types":"./dist/plugins/auth.d.ts","import":"./dist/plugins/auth.js"},"./plugins/dash":{"types":"./dist/plugins/dash.d.ts","import":"./dist/plugins/dash.js"},"./plugins/engines":{"types":"./dist/plugins/engines/index.d.ts","import":"./dist/plugins/engines/index.js"},"./plugins/metrics":{"types":"./dist/plugins/metrics.d.ts","import":"./dist/plugins/metrics.js"},"./plugins/storage":{"types":"./dist/plugins/storage.d.ts","import":"./dist/plugins/storage.js"},"./plugins/metadata":{"types":"./dist/plugins/metadata/index.d.ts","import":"./dist/plugins/metadata/index.js"},"./plugins/reconnect":{"types":"./dist/plugins/reconnect.d.ts","import":"./dist/plugins/reconnect.js"},"./plugins/diagnostics":{"types":"./dist/plugins/diagnostics.d.ts","import":"./dist/plugins/diagnostics.js"},"./plugins/performance":{"types":"./dist/plugins/performance.d.ts","import":"./dist/plugins/performance.js"},"./techs/wsRaw/demuxer":{"types":"./dist/techs/wsRaw/demuxer.d.ts","import":"./dist/techs/wsRaw/demuxer.js"},"./plugins/panoramalite":{"types":"./dist/plugins/panoramalite.d.ts","import":"./dist/plugins/panoramalite.js"},"./plugins/recording-api":{"types":"./dist/plugins/recording.d.ts","import":"./dist/plugins/recording.js"},"./plugins/ui-components":{"types":"./dist/plugins/ui-components.d.ts","import":"./dist/plugins/ui-components.js"},"./vendor/dash.all.min.js":"./dist/vendor/dash.all.min.js"},"gitHead":"4fc19c002837d5c68903101b068e96f3cda9c101","scripts":{"dev":"concurrently \"pnpm bundle:examples:watch\" \"pnpm serve:examples\"","test":"jest","build":"pnpm clean && tsc -p tsconfig.json && node checks/copy-vendor.mjs","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","dev:vite":"vite --config vite.config.ts","test:watch":"jest --watch","bundle:iife":"esbuild src/iife.ts --bundle --outfile=dist/fyraplayer.iife.js --platform=browser --format=iife --global-name=FyraPlayerSDK","build:release":"pnpm build && pnpm bundle:iife","check:exports":"pnpm build && node checks/export-contract.mjs","check:release":"pnpm exec jest --runInBand && pnpm check:public-api && pnpm check:exports && pnpm check:sources && pnpm build:release","check:sources":"node checks/sources-contract.mjs","dev:vite:host":"vite --config vite.config.ts --host --port 3000","test:coverage":"jest --coverage","serve:examples":"npx http-server examples -p 5173 -c-1","bundle:examples":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm","long-run:assert":"node checks/browser-long-run-assert.mjs","check:public-api":"tsc -p tsconfig.public-api.json","long-run:browser":"node checks/browser-long-run.mjs","serve:fmp4-fixture":"node checks/ffmpeg-fmp4-fixture.mjs","smoke:panoramalite":"node checks/panoramalite-smoke.mjs","bundle:examples:watch":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm --watch"},"_npmUser":{"name":"fyrabimcc","email":"yf@bimcc.com"},"_npmVersion":"11.14.1","description":"Modern, extensible low-latency web player (WebRTC, LL-HLS, WS+WASM) with plugin/middleware architecture.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"hls.js":"^1.6.16"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.1.2","devDependencies":{"jest":"^30.4.2","vite":"^8.0.13","dashjs":"^5.1.1","mp4box":"^0.5.4","esbuild":"^0.28.0","ts-jest":"^29.4.10","fast-check":"^4.8.0","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^20.19.41"},"peerDependencies":{"dashjs":"^5.1.1","mp4box":"^0.5.4","mpegts.js":"^1.8.0"},"peerDependenciesMeta":{"dashjs":{"optional":true},"mp4box":{"optional":true},"mpegts.js":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/fyraplayer_1.0.1_1780312868270_0.36050304032740965","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@bimccfyra/fyraplayer","version":"1.0.2","description":"Modern, extensible low-latency web player (WebRTC, LL-HLS, WS+WASM) with plugin/middleware architecture.","type":"module","packageManager":"pnpm@11.1.2","publishConfig":{"access":"public"},"main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./core":{"import":"./dist/core.js","types":"./dist/core.d.ts"},"./plugins":{"import":"./dist/plugins/index.js","types":"./dist/plugins/index.d.ts"},"./plugins/engines":{"import":"./dist/plugins/engines/index.js","types":"./dist/plugins/engines/index.d.ts"},"./plugins/metadata":{"import":"./dist/plugins/metadata/index.js","types":"./dist/plugins/metadata/index.d.ts"},"./plugins/ui-components":{"import":"./dist/plugins/ui-components.js","types":"./dist/plugins/ui-components.d.ts"},"./plugins/metrics":{"import":"./dist/plugins/metrics.js","types":"./dist/plugins/metrics.d.ts"},"./plugins/performance":{"import":"./dist/plugins/performance.js","types":"./dist/plugins/performance.d.ts"},"./plugins/panoramalite":{"import":"./dist/plugins/panoramalite.js","types":"./dist/plugins/panoramalite.d.ts"},"./plugins/reconnect":{"import":"./dist/plugins/reconnect.js","types":"./dist/plugins/reconnect.d.ts"},"./plugins/recording-api":{"import":"./dist/plugins/recording.js","types":"./dist/plugins/recording.d.ts"},"./plugins/diagnostics":{"import":"./dist/plugins/diagnostics.js","types":"./dist/plugins/diagnostics.d.ts"},"./plugins/auth":{"import":"./dist/plugins/auth.js","types":"./dist/plugins/auth.d.ts"},"./plugins/dash":{"import":"./dist/plugins/dash.js","types":"./dist/plugins/dash.d.ts"},"./vendor/dash.all.min.js":"./dist/vendor/dash.all.min.js","./plugins/storage":{"import":"./dist/plugins/storage.js","types":"./dist/plugins/storage.d.ts"},"./techs/wsRaw/demuxer":{"import":"./dist/techs/wsRaw/demuxer.js","types":"./dist/techs/wsRaw/demuxer.d.ts"}},"scripts":{"clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","build":"pnpm clean && tsc -p tsconfig.json && node checks/copy-vendor.mjs","build:release":"pnpm build && pnpm bundle:iife","check:public-api":"tsc -p tsconfig.public-api.json","check:exports":"pnpm build && node checks/export-contract.mjs","check:release":"pnpm exec jest --runInBand && pnpm check:public-api && pnpm check:exports && pnpm check:sources && pnpm build:release","check:sources":"node checks/sources-contract.mjs","serve:fmp4-fixture":"node checks/ffmpeg-fmp4-fixture.mjs","long-run:browser":"node checks/browser-long-run.mjs","long-run:assert":"node checks/browser-long-run-assert.mjs","smoke:panoramalite":"node checks/panoramalite-smoke.mjs","bundle:examples":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm","bundle:iife":"esbuild src/iife.ts --bundle --outfile=dist/fyraplayer.iife.js --platform=browser --format=iife --global-name=FyraPlayerSDK","bundle:examples:watch":"esbuild examples/app.ts --bundle --outfile=examples/bundle.js --platform=browser --format=esm --watch","dev":"concurrently \"pnpm bundle:examples:watch\" \"pnpm serve:examples\"","serve:examples":"npx http-server examples -p 5173 -c-1","dev:vite":"vite --config vite.config.ts","dev:vite:host":"vite --config vite.config.ts --host --port 3000","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage"},"dependencies":{"hls.js":"^1.6.16"},"peerDependencies":{"dashjs":"^5.1.1","mpegts.js":"^1.8.0","mp4box":"^0.5.4"},"peerDependenciesMeta":{"dashjs":{"optional":true},"mpegts.js":{"optional":true},"mp4box":{"optional":true}},"devDependencies":{"@types/jest":"^30.0.0","@types/node":"^20.19.41","dashjs":"^5.1.1","esbuild":"^0.28.0","fast-check":"^4.8.0","jest":"^30.4.2","mp4box":"^0.5.4","ts-jest":"^29.4.10","typescript":"^5.9.3","vite":"^8.0.13"},"gitHead":"2558caf83d38a9b499fa291813f58c615fcec425","_id":"@bimccfyra/fyraplayer@1.0.2","_nodeVersion":"24.15.0","_npmVersion":"11.14.1","dist":{"integrity":"sha512-KSpOlilVT9NrzqO1SeGZIhyUlQFAve8c8LtJ6nzPUaAth1qFnf++w8XF/VJuZgb8hCL2kKxkG55MXc4KZCwntw==","shasum":"b2d5f7f17a1e28f5cb2fdc4c9dfe07d008be8fb0","tarball":"https://registry.npmjs.org/@bimccfyra/fyraplayer/-/fyraplayer-1.0.2.tgz","fileCount":389,"unpackedSize":3878686,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCYZdCU8/zCAHfQpKCuyxptA1fImP96aEa9qLHymLOeogIgcLVAShZsafCshTm6lSL19GuZI9XDv/Gkodq84o9QLnI="}]},"_npmUser":{"name":"fyrabimcc","email":"yf@bimcc.com"},"directories":{},"maintainers":[{"name":"fyrabimcc","email":"yf@bimcc.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fyraplayer_1.0.2_1782832705085_0.48808804782788595"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-20T10:55:25.343Z","modified":"2026-06-30T15:18:25.440Z","1.0.0":"2026-05-20T10:55:25.815Z","1.0.1":"2026-06-01T11:21:08.503Z","1.0.2":"2026-06-30T15:18:25.284Z"},"description":"Modern, extensible low-latency web player (WebRTC, LL-HLS, WS+WASM) with plugin/middleware architecture.","maintainers":[{"name":"fyrabimcc","email":"yf@bimcc.com"}],"readme":"# FyraPlayer\r\n\r\n通用低延迟 Web 播放器，支持 WebRTC、LL-HLS/DASH、WebSocket+WebCodecs 等多种播放技术，采用插件/中间件架构设计。\r\n\r\n## 1.0 商业基线\r\n\r\nFyraPlayer `1.0.0` 定位为可控场景下的商业基线 SDK：核心播放、插件生命周期、公共导出、示例、发布检查和主要浏览器证据已经收口。支持范围以 [支持场景与已知限制](./docs/supported-scenarios.md)、[播放验证矩阵](./docs/playback-verification-matrix.md) 和 [1.0 发布就绪复盘](./docs/release-1.0-readiness.md) 为准。\r\n\r\n当前 1.0 不承诺所有浏览器、所有协议、所有后端组合的无条件支持。WebRTC TURN/受控中断恢复、项目专用 direct fMP4、Safari/Firefox 完整矩阵、DRM、字幕、广告/埋点、前端录制、GB28181 服务端栈和 PTZ 设备执行均按文档继续跟进或保持插件/后端边界。\r\n\r\n## 特性\r\n\r\n- **多播放技术**: WebRTC (OME/WHIP/WHEP)、WS-raw (WebCodecs, FLV/TS)、HLS/DASH、fMP4、GB28181 网关适配、本地文件\r\n- **本地文件播放**: 支持 MP4（原生）、TS/MTS（mpegts.js）、FLV 等格式的本地文件播放\r\n- **高可靠性**: 自动重连、ICE 重启、playoutDelayHint、基于丢包的 ABR 回退、DataChannel 心跳\r\n- **可扩展**: 中间件管线、插件管理器、信令适配器、流媒体服务器 URL 工厂、元数据桥接 (KLV/SEI)\r\n- **可选 UI 插件**: 通过 `createUiComponentsPlugin()` 显式启用，嵌入场景可只使用核心播放器\r\n- **渲染器分离**: PSV 全景、Cesium 3D 地图适配器放在各自独立项目\r\n\r\n## 架构\r\n\r\n```\r\n┌─────────────────────────────────────────────────────────────┐\r\n│                        用户应用                              │\r\n├─────────────────────────────────────────────────────────────┤\r\n│  @beeviz/fyrapano    @beeviz/cesium    其他渲染器集成        │  ← 外部项目\r\n├─────────────────────────────────────────────────────────────┤\r\n│  plugins/metadata    plugins/engines                         │  ← 播放相关插件\r\n├─────────────────────────────────────────────────────────────┤\r\n│                          ui/                                 │  ← 可选 UI 插件\r\n├─────────────────────────────────────────────────────────────┤\r\n│                       FyraPlayer                             │  ← 主入口\r\n├─────────────────────────────────────────────────────────────┤\r\n│     core/          techs/           render/                  │  ← 核心层\r\n└─────────────────────────────────────────────────────────────┘\r\n```\r\n\r\n## 播放技术 (Tech)\r\n\r\n| Tech | 文件 | 用途 | 底层库 |\r\n|------|------|------|--------|\r\n| `webrtc` | tech-webrtc.ts | WebRTC 低延迟播放 (WHIP/WHEP/Oven-WS) | 原生 WebRTC |\r\n| `hls` | tech-hls.ts | HLS/LL-HLS 自适应码率播放 (.m3u8) | hls.js |\r\n| `dash` | tech-dash.ts | DASH 自适应码率播放 (.mpd) | dash.js（通过 `@bimccfyra/fyraplayer/plugins/dash` 显式启用） |\r\n| `fmp4` | tech-fmp4.ts | fMP4 直播流 (无清单，HTTP/WS + MSE) | 原生 MSE |\r\n| `ws-raw` | tech-ws-raw.ts | WebSocket + WebCodecs (FLV/TS) | 自研 + mpegts.js |\r\n| `gb28181` | tech-gb28181.ts | 服务端 GB28181 网关 invite/control + FLV/TS 播放适配 | mpegts.js |\r\n| `file` | tech-file.ts | 本地/远程文件播放 (MP4/TS/FLV) | 原生 + mpegts.js |\r\n\r\n## 格式与 Tech 对应关系\r\n\r\n| 格式 | 后缀/协议 | 推荐 Tech | 说明 |\r\n|------|-----------|-----------|------|\r\n| HLS | .m3u8 | `hls` | 支持 LL-HLS |\r\n| DASH | .mpd | `dash` | 支持 ABR |\r\n| fMP4 直播 | .m4s, HTTP/WS | `fmp4` | 无清单的 fMP4 流 |\r\n| FLV 直播 | ws://...flv | `ws-raw` | WebSocket FLV |\r\n| HTTP-FLV | http://...flv | `ws-raw` | mpegts.js 播放 |\r\n| TS 直播 | ws://...ts | `ws-raw` | WebSocket TS |\r\n| MP4 文件 | .mp4 | `file` | 浏览器原生 |\r\n| TS 文件 | .ts, .mts | `file` | mpegts.js 播放 |\r\n| WebRTC | wss://, http:// | `webrtc` | WHEP/WHIP/Oven |\r\n\r\n## 外部依赖\r\n\r\n| 库 | 版本 | 用途 |\r\n|----|------|------|\r\n| `hls.js` | ^1.6.16 | HLS/LL-HLS 流播放 |\r\n| `dashjs` | ^5.1.1 | DASH 流播放（optional peer，使用 DASH 插件时安装） |\r\n| `mpegts.js` | ^1.8.0 | TS/FLV 容器解析 + MSE 播放（optional peer，仅 TS/FLV/GB fallback 路径需要） |\r\n| `mp4box` | ^0.5.4 | MP4 容器解析（optional peer，仅 `webCodecs.mp4boxLoader` / 全局 MP4Box 路径需要） |\r\n\r\n## 安装\r\n\r\n```bash\r\npnpm install\r\npnpm build\r\n```\r\n\r\nDASH is optional to keep default application bundles quiet under Vite/Rolldown.\r\n`createDashTechPlugin()` does not make the default `@bimccfyra/fyraplayer` entry import\r\ndash.js. By default it loads the packaged `dist/vendor/dash.all.min.js` UMD\r\nscript only when a DASH source is played. Host apps can also provide their own\r\nloader when they want tighter CDN/cache control.\r\n\r\nFor Vite/Rolldown applications, pass the packaged vendor file as an emitted\r\nasset URL so production builds publish the runtime script with the app:\r\n\r\n```typescript\r\nimport { createDashTechPlugin } from '@bimccfyra/fyraplayer/plugins/dash';\r\nimport dashJsScriptUrl from '@bimccfyra/fyraplayer/vendor/dash.all.min.js?url';\r\n\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{ type: 'dash', url: 'https://example.com/manifest.mpd', preferTech: 'dash' }],\r\n  plugins: [createDashTechPlugin({ scriptUrl: dashJsScriptUrl })]\r\n});\r\n\r\nconst playerWithCustomDash = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{ type: 'dash', url: 'https://example.com/manifest.mpd', preferTech: 'dash' }],\r\n  plugins: [\r\n    createDashTechPlugin({\r\n      dashjsLoader: () => import('dashjs')\r\n    })\r\n  ]\r\n});\r\n```\r\n\r\nTS/FLV playback is also optional for supply-chain stability. The default package\r\ndoes not install `mpegts.js` because `mpegts.js@1.8.0` depends on a\r\nGitHub-hosted `webworkify-webpack` tarball. Applications that use `ws-raw`,\r\nGB28181 FLV/TS gateway output, or TS file fallback should install and pass an\r\nexplicit loader:\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{ type: 'ws-raw', url: 'https://example.com/live.flv', codec: 'h264', transport: 'flv' }],\r\n  mpegtsLoader: () => import('mpegts.js')\r\n});\r\n```\r\n\r\n## 快速开始\r\n\r\n```typescript\r\nimport { FyraPlayer } from '@bimccfyra/fyraplayer';\r\n\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{ type: 'hls', url: 'https://example.com/stream.m3u8' }],\r\n  techOrder: ['webrtc', 'ws-raw', 'hls', 'dash', 'fmp4', 'file'],\r\n  autoplay: true,\r\n  muted: true\r\n});\r\n\r\nplayer.on('ready', () => console.log('Player ready'));\r\nplayer.on('error', (err) => console.error('Error:', err));\r\n\r\nawait player.init();\r\n```\r\n\r\n### 启用 UI 插件\r\n\r\n核心播放器不会默认挂载控件；需要 UI 时显式加入插件：\r\n\r\n```typescript\r\nimport { FyraPlayer } from '@bimccfyra/fyraplayer';\r\nimport { createUiComponentsPlugin } from '@bimccfyra/fyraplayer/plugins/ui-components';\r\n\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{ type: 'hls', url: 'https://example.com/stream.m3u8' }],\r\n  plugins: [\r\n    createUiComponentsPlugin({\r\n      target: '.player-shell',\r\n      poster: '/poster.jpg'\r\n    })\r\n  ]\r\n});\r\n\r\nawait player.init();\r\n```\r\n\r\n### 插件能力速查\r\n\r\nFyraPlayer 的核心只负责播放生命周期、Tech 选择、事件和中间件执行。产品能力通过插件显式启用，推荐从独立子路径导入，避免把不需要的能力打进业务包。\r\n\r\n| 插件入口 | 工厂/能力 | 适用场景 |\r\n|---|---|---|\r\n| `@bimccfyra/fyraplayer/plugins/ui-components` | `createUiComponentsPlugin()` | 播放控制条、质量/源选择、重试、截图入口、录制按钮钩子 |\r\n| `@bimccfyra/fyraplayer/plugins/diagnostics` | `createDiagnosticsPlugin()`, `createDebugPanelPlugin()` | 当前状态、最近错误/网络/QoS/ICE 线索、诊断导出和调试面板 |\r\n| `@bimccfyra/fyraplayer/plugins/storage` | `createStoragePlugin()` | 音量、静音、倍速、清晰度、低延迟偏好和上次播放源持久化 |\r\n| `@bimccfyra/fyraplayer/plugins/auth` | `createAuthSigningMiddleware()`, `createAuthRecoveryPlugin()` | 请求头、凭证、Token、URL 签名和显式 401/403 恢复 |\r\n| `@bimccfyra/fyraplayer/plugins/recording-api` | `createRecordingApiPlugin()` | 对接后端开始/停止/查询录制；不做浏览器本地录制 |\r\n| `@bimccfyra/fyraplayer/plugins/performance` | `createPerformanceMonitorPlugin()` | FPS、延迟、pending buffer 等性能预算告警 |\r\n| `@bimccfyra/fyraplayer/plugins/metrics` | `createMetricsPlugin()` | 业务自定义指标回调或上报适配 |\r\n| `@bimccfyra/fyraplayer/plugins/reconnect` | `createReconnectPlugin()` | 重连事件日志和产品侧回调；不替代核心重连策略 |\r\n| `@bimccfyra/fyraplayer/plugins/metadata` | `createMetadataPlugin()`, `KlvBridge` | KLV/SEI/private-data 等元数据业务解析 |\r\n| `@bimccfyra/fyraplayer/plugins/engines` | `createSourceResolverMiddleware()` | MediaMTX/OME 等服务端播放 URL 到 source/fallback 链的转换 |\r\n| `@bimccfyra/fyraplayer/plugins/panoramalite` | `createPanoramaLitePlugin()` | 轻量 WebGL2 全景图片、全景视频和全景直播渲染 |\r\n\r\n插件应在创建 `FyraPlayer` 时通过 `plugins` 数组安装。产品 UI 可以显示已安装插件并开放安全的运行时模式开关，例如 PanoramaLite 的 `handle.setEnabled()`；`examples/basic.html` 已采用统一资源列表和普通/全景模式切换示例。视频源平台可通过 `source.presentation.mode = 'panorama'`、`source.meta.presentation` 或 `tags: ['panorama']` 标记全景源，应用层可用 `isPanoramaSource()` / `getSourcePresentation()` 触发全景模式。当前 SDK 不提供任意热安装插件的公共 API。具体边界见 [插件化边界地图](./docs/pluginization-map.md) 和 [SDK 发布与集成](./docs/sdk-release-integration.md)。\r\n\r\n## 使用示例\r\n\r\n### HLS 播放\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'hls',\r\n    url: 'https://example.com/stream.m3u8',\r\n    lowLatency: true,  // 启用 LL-HLS\r\n    preferTech: 'hls'\r\n  }]\r\n});\r\n```\r\n\r\n### DASH 播放\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'dash',\r\n    url: 'https://example.com/stream.mpd',\r\n    preferTech: 'dash'\r\n  }]\r\n});\r\n```\r\n\r\n### fMP4 直播流（无清单）\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'fmp4',\r\n    url: 'https://example.com/live/stream',\r\n    transport: 'http',  // 或 'ws'\r\n    codec: 'h264',\r\n    isLive: true,\r\n    preferTech: 'fmp4'\r\n  }]\r\n});\r\n```\r\n\r\n### WebSocket FLV/TS 流\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'ws-raw',\r\n    url: 'wss://server/stream.flv',\r\n    codec: 'h264',\r\n    transport: 'flv',  // 或 'ts'\r\n    preferTech: 'ws-raw'\r\n  }]\r\n});\r\n```\r\n\r\n`ws-raw` defaults to the stable MSE path (`pipeline: 'mse'`). The in-house\r\nWebCodecs/WASM path is opt-in and should be treated as experimental:\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'ws-raw',\r\n    url: 'wss://server/stream.ts',\r\n    codec: 'h264',\r\n    transport: 'ts',\r\n    pipeline: 'experimental',\r\n    preferTech: 'ws-raw'\r\n  }]\r\n});\r\n```\r\n\r\n### 本地文件播放\r\n\r\n```typescript\r\n// 通过 file input 选择本地文件\r\nconst fileInput = document.getElementById('file-input');\r\nfileInput.onchange = async () => {\r\n  const file = fileInput.files[0];\r\n  const blobUrl = URL.createObjectURL(file);\r\n  const ext = file.name.split('.').pop().toLowerCase();\r\n  \r\n  const player = new FyraPlayer({\r\n    video: '#video',\r\n    sources: [{\r\n      type: 'file',\r\n      url: blobUrl,\r\n      // 对于 blob URL，需要指定 container 类型\r\n      container: ext === 'ts' ? 'ts' : (ext === 'mp4' ? 'mp4' : undefined)\r\n    }]\r\n  });\r\n  \r\n  await player.init();\r\n};\r\n```\r\n\r\n### 元数据提取 (KLV/SEI)\r\n\r\n```typescript\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'ws-raw',\r\n    url: 'wss://server/stream',\r\n    codec: 'h264',\r\n    transport: 'ts',\r\n    metadata: {\r\n      privateData: { enable: true },\r\n      sei: { enable: true }\r\n    }\r\n  }]\r\n});\r\n\r\nplayer.on('metadata', (evt) => {\r\n  // 传递给 fyraklv 解析\n  console.log('Metadata:', evt.type, evt.raw);\r\n});\r\n```\r\n\r\n## API 文档\r\n\r\n详细 API 文档请参阅 [docs/api.md](./docs/api.md)\r\n\r\n## 长期跟进文档\r\n\r\n- [商业化成熟度路线图](./docs/commercial-readiness-roadmap.md)：当前商业级差距、优先级、验收门槛和延期插件占位。\r\n- [插件化边界地图](./docs/pluginization-map.md)：已插件化能力、候选插件、核心边界和插件 API 后续方向。\r\n- [渲染桥边界](./docs/render-bridges.md)：PSV/Cesium/map/panorama 适配器的外部化方案和通用视频/画布输出契约。\r\n- [PanoramaLite 计划](./docs/panoramalite.md)：轻量 WebGL2 全景图片、全景视频和全景直播插件专项设计与跟踪。\r\n- [支持场景与已知限制](./docs/supported-scenarios.md)：当前可对外承诺的场景、实验项和明确边界。\r\n- [播放验证矩阵](./docs/playback-verification-matrix.md)：真实浏览器/协议验证范围、样例流、场景和证据记录。\r\n- [性能基线](./docs/performance-baseline.md)：可选性能预算插件、默认阈值和后续 profiling 证据状态。\r\n- [SDK 发布与集成](./docs/sdk-release-integration.md)：ESM、插件子路径、IIFE/CDN 包、发布检查清单和迁移规则。\r\n- [1.0 发布就绪复盘](./docs/release-1.0-readiness.md)：正式 1.0 前的架构、功能、证据、风险和后续任务复盘。\r\n- [代码审查对齐文档](./docs/review-alignment.md)：历史审查结论、已完成整改和复审记录。\r\n- [P0 执行清单](./docs/p0-execution-checklist.md)：第一批正确性修复的执行与验收记录。\r\n\r\n## 脚本\r\n\r\n- `pnpm build` — 构建到 `dist/`\r\n- `pnpm build:release` — 构建 ESM 包并生成浏览器 IIFE 包\r\n- `pnpm bundle:iife` — 生成 `dist/fyraplayer.iife.js`\r\n- `pnpm check:release` — 运行发布前自检流水线\r\n- `pnpm check:sources` — 校验 `examples/sources.js` 的示例源结构\r\n- `pnpm check:public-api` — 校验 README/API 关键公共用法可通过 TypeScript 编译\r\n- `pnpm check:exports` — 清理并重建 `dist/`，校验 `package.json` exports 指向的文件存在\r\n- `pnpm smoke:panoramalite` — 启动 PanoramaLite 示例并做浏览器 canvas 像素/交互 smoke\r\n- `pnpm test` — 运行 Jest 测试\r\n- `pnpm dev:vite` — Vite 开发服务器\r\n- `pnpm bundle:examples` — 打包示例\r\n\r\n## 示例资产\r\n\r\n- `examples/basic.html`：主要 ESM 播放器示例、协议测试入口，以及普通播放/ PanoramaLite 全景播放统一 demo。\r\n- `examples/sources.js`：示例流配置清单，受 `pnpm check:sources` 校验。\r\n- `examples/minimal-iife.html`：无构建环境的 IIFE 集成示例。\r\n- `examples/panoramalite.html`：PanoramaLite WebGL2 全景渲染专项示例和 smoke 目标。\r\n\r\nPSV、Cesium、地图、全景和 KLV 业务解析示例不再放在 `examples/` 里作为可运行占位页面；对应集成边界请看 `docs/render-bridges.md`、`docs/integration-psv.md`、`docs/integration-cesium.md` 和 `docs/klv-integration.md`。\r\n\r\n## 与 fyraklv 集成\n\nFyraPlayer 可以与 fyraklv 配合使用，实现无人机视频的 KLV 元数据提取和解析。\n\r\n### 架构关系\r\n\r\n```\r\n┌─────────────────────────────────────────────────────────────────┐\r\n│                         业务应用                                 │\r\n├─────────────────────────────────────────────────────────────────┤\r\n│                                                                 │\r\n│  ┌─────────────────────────────────────────────────────────┐   │\r\n│  │                    FyraPlayer                            │   │\r\n│  │  ┌─────────────┐                                        │   │\r\n│  │  │ ts-demuxer  │ ← TS 解复用（视频/音频/私有数据）        │   │\r\n│  │  └──────┬──────┘                                        │   │\r\n│  │         │ onPrivateData(pid, bytes, pts)                │   │\r\n│  └─────────┼───────────────────────────────────────────────┘   │\r\n│            │                                                    │\r\n│            │ 私有数据（可能是 KLV）                              │\r\n│            ▼                                                    │\r\n│  ┌─────────────────────────────────────────────────────────┐   │\r\n│  │                      fyraklv                            │   │\n│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │   │\r\n│  │  │ KLVParser   │→ │ SyncEngine  │→ │ DroneState  │     │   │\r\n│  │  └─────────────┘  └─────────────┘  └─────────────┘     │   │\r\n│  └─────────────────────────────────────────────────────────┘   │\r\n│                                                                 │\r\n└─────────────────────────────────────────────────────────────────┘\r\n```\r\n\r\n### 集成示例\r\n\r\n```typescript\r\nimport { FyraPlayer } from '@bimccfyra/fyraplayer';\r\nimport { KLVParser, SyncEngine } from 'fyraklv';\n\r\nconst parser = new KLVParser();\r\nconst syncEngine = new SyncEngine();\r\n\r\nconst player = new FyraPlayer({\r\n  video: '#video',\r\n  sources: [{\r\n    type: 'ws-raw',\r\n    url: 'wss://server/stream',\r\n    codec: 'h264',\r\n    transport: 'ts',\r\n    metadata: { privateData: { enable: true } }\r\n  }]\r\n});\r\n\r\n// FyraPlayer 提取私有数据，fyraklv 解析 KLV 语义\nplayer.on('metadata', (evt) => {\r\n  if (evt.type === 'private-data') {\r\n    // 检测 KLV Universal Label (06 0E 2B 34)\r\n    if (evt.raw[0] === 0x06 && evt.raw[1] === 0x0E) {\r\n      const frame = parser.parse(evt.raw, BigInt(evt.pts * 90));\r\n      if (frame) syncEngine.push(frame);\r\n    }\r\n  }\r\n});\r\n\r\n// 获取同步状态用于投影\r\nfunction render() {\r\n  const videoPts = BigInt(player.currentTime * 90000);\r\n  const state = syncEngine.getInterpolatedStateAtPts(videoPts);\r\n  if (state) {\r\n    updateProjection(state); // 更新 Cesium 投影\r\n  }\r\n  requestAnimationFrame(render);\r\n}\r\n```\r\n\r\n## 相关项目\r\n\r\n- [fyraklv](https://github.com/bimcc/fyraklv) - KLV/MISB 元数据解析\n- [@beeviz/fyrapano](https://github.com/beeviz/fyrapano) - 全景直播组件\r\n- [beeviz](https://github.com/beeviz/beeviz) - 视频投影业务层\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}