rivmux 1.0.0-rc.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,24 +1,22 @@
1
1
  # Rivmux Player
2
2
 
3
- `rivmux` 是 Rivmux 面向用户的浏览器播放器包。它在 Dedicated Worker 中读取 HTTP-FLV,将音视频转封装为 fragmented MP4,并通过 Worker MSE 连接到 `<video>` 元素。当前没有主线程 MSE 降级路径。
3
+ > A Rust/WebAssembly-powered, low-latency HTTP-FLV player for modern browsers.
4
4
 
5
- ## 安装
5
+ `rivmux`(Riv 源自拉丁语 _rivus_,意为“河流”;Mux 指媒体封装/复用;读作 `/ˈrɪvmʌks/`)是一款面向现代浏览器的低延迟 HTTP-FLV 播放器。它在 Dedicated Worker 中读取流数据,通过 Rust/WebAssembly 转封装核心生成 fragmented MP4,再借助 Worker MSE 驱动 `<video>`。网络读取与媒体处理远离主线程,同时向应用提供清晰的生命周期、可调缓冲策略和结构化诊断。
6
6
 
7
- 当前发布格式为 ESM-only,不提供 CommonJS、UMD 或全局变量构建。
7
+ ## 安装与运行要求
8
8
 
9
9
  ```sh
10
10
  pnpm add rivmux
11
11
  ```
12
12
 
13
- ```sh
14
- npm install rivmux
15
- ```
13
+ `rivmux` 以 ESM-only 格式发布,面向浏览器 ESM Bundler,不提供 CommonJS、UMD 或全局变量构建。Vite 已完成验证,其他 bundler 暂不形成兼容承诺。
16
14
 
17
- 当前发布目标是浏览器 ESM Bundler。Vite 已完成验证,其他 bundler 暂不形成兼容承诺。包的 `exports` 只提供 `import` 和类型条件,不提供 CommonJS、UMD 或全局变量入口。Node.js 仅支持 ESM 导入以及 SSR/能力探测调用,不提供 Node 播放运行时。
15
+ Node.js 仅支持 ESM 导入以及 SSR/能力探测调用,不提供播放运行时。浏览器播放依赖 Dedicated Worker、Worker MSE、流式 Fetch、ReadableStream 和 WebAssembly;当前没有主线程 MSE 降级路径。
18
16
 
19
- ## 基本用法
17
+ ## 快速开始
20
18
 
21
- 调用 `start()` 前必须等待 `attach()` 完成。`attach()` 会初始化 Worker,并把内部 `MediaSourceHandle` 连接到 video 元素。
19
+ 调用 `start()` 前必须等待 `attach()` 完成。`attach()` 会初始化 Worker,并将内部 `MediaSourceHandle` 连接到 `<video>` 元素。
22
20
 
23
21
  ```ts
24
22
  import { RivmuxPlayer } from 'rivmux'
@@ -51,7 +49,23 @@ await player.stop()
51
49
  await player.destroy()
52
50
  ```
53
51
 
54
- ## 能力探测
52
+ ## 支持范围与能力探测
53
+
54
+ ### Codec 支持边界
55
+
56
+ | 输入 | 等级 | 说明 |
57
+ | ---------------------------------------- | ------------ | ---------------------------------- |
58
+ | HTTP-FLV + AVC/H.264 + AAC-LC | Stable | 受浏览器基础 MSE 能力约束 |
59
+ | Enhanced HTTP-FLV + HEVC/`hvc1` + AAC-LC | Stable | 浏览器解码能力是条件化的 |
60
+ | Enhanced HTTP-FLV + AV1 | Experimental | 实现层实验能力,组合与兼容性未承诺 |
61
+ | Enhanced HTTP-FLV + Opus | Experimental | 实现层实验能力,组合与兼容性未承诺 |
62
+ | MPEG-TS | 不支持 | 不属于当前产品输入边界 |
63
+
64
+ HEVC Stable 的具体范围是单视频轨、固定 codec 配置、Enhanced FLV `SequenceStart`、`CodedFrames` 和 HEVC `CodedFramesX`,输出 sample entry 为 `hvc1`。最终解码能力取决于浏览器、操作系统、设备以及具体 HEVC profile、level、bit depth 和 chroma format;Rivmux 不维护固定 profile/level allowlist。结构合法但环境不支持准确 MIME 时会产生终止错误 `RIVMUX_UNSUPPORTED_MSE_CODEC`。
65
+
66
+ `hev1`、多轨 HEVC、播放期间动态 codec 配置切换和 HEVC + Opus 不属于 Stable 范围。AV1 与 Opus 仍为 Experimental;当前未承诺它们与其他音视频 codec 的组合、浏览器兼容矩阵或稳定错误语义。
67
+
68
+ ### 运行环境探测
55
69
 
56
70
  创建播放器前可同步读取基础运行环境和解码能力:
57
71
 
@@ -63,7 +77,7 @@ if (!isSupported()) {
63
77
  }
64
78
 
65
79
  const capabilities = getCapabilities()
66
- console.log(capabilities.decoding.stableProfiles.hevcAac)
80
+ console.log(capabilities)
67
81
  ```
68
82
 
69
83
  ```ts
@@ -88,15 +102,11 @@ type RivmuxCapabilities = {
88
102
  aac: SupportStatus
89
103
  opus: SupportStatus
90
104
  }
91
- stableProfiles: {
92
- avcAac: SupportStatus
93
- hevcAac: SupportStatus
94
- }
95
105
  }
96
106
  }
97
107
  ```
98
108
 
99
- `isSupported()` 始终等于 `getCapabilities().supported`。这里的 `supported` 是基础运行能力结果,只表示 Dedicated Worker、Worker MSE、流式 Fetch、ReadableStream 和 WebAssembly 等条件可用,不保证具体媒体流能够播放。下面解码矩阵中的 `supported` 是另一层 codec 前置探测结果,二者都不等于实际播放成功。
109
+ `isSupported()` 始终等于 `getCapabilities().supported`。这里的 `supported` 只表示基础运行条件可用,不保证具体媒体流能够播放。
100
110
 
101
111
  解码矩阵通过同步的 `MediaSource.isTypeSupported()` 对代表性 codec 执行前置判断:`supported` 表示环境报告支持,`unsupported` 表示环境明确拒绝,`unknown` 表示 API 不可用或无法可靠判断。该结果不会改变输入的 Stable、Experimental 或不支持边界,也不替代真实流校验。Rivmux 取得实际 codec、profile 和 level 后,仍会使用准确 MIME 做最终 MSE 校验。
102
112
 
@@ -108,23 +118,22 @@ type RivmuxCapabilities = {
108
118
  const player = new RivmuxPlayer(url, options)
109
119
  ```
110
120
 
111
- | 调用 | 含义 |
121
+ | 调用或属性 | 含义 |
112
122
  | --------------------------------- | ------------------------------------------------------------ |
113
123
  | `new RivmuxPlayer(url, options?)` | 为一个流地址创建播放器实例。 |
124
+ | `player.url` | 构造播放器时传入的只读流地址。 |
114
125
  | `await player.attach(video)` | 绑定 `<video>` 元素并准备 Worker/MSE 链路。 |
115
126
  | `await player.start()` | 启动加载、转封装、缓冲与播放控制;必须在 `attach()` 后调用。 |
116
- | `await player.stop()` | 停止加载并解绑媒体源;实例可以再次启动。 |
127
+ | `await player.stop()` | 停止加载并解绑媒体源;无需重新 `attach()` 即可再次启动。 |
117
128
  | `await player.destroy()` | 释放 Worker、定时器、监听器和媒体源;实例不可再用。 |
118
129
  | `player.on(type, listener)` | 订阅播放器事件。 |
119
130
  | `player.off(type, listener)` | 移除先前注册的监听器。 |
120
131
 
121
- `attach()` 完成只表示媒体源句柄已经连接;`start()` 完成只表示 Worker 已建立本次启动所需的内部加载、转封装和 MSE 会话。两者都不表示已经解析出媒体信息、已经追加首个媒体片段,也不表示 `<video>` 已触发 `canplay` 或开始播放。请通过 `mediaInfo`、`stats`、`error` 和视频元素事件观察后续结果。
122
-
123
- 同一启动过程中的并发 `start()` 调用会等待同一个操作,不会重复发送启动命令。播放器已经启动后再次调用 `start()` 会直接完成。若 `stop()` 或 `destroy()` 在启动确认前开始,待处理的 `start()` 会以 `RIVMUX_START_CANCELLED` 拒绝,而停止或销毁操作仍会正常完成。终止错误会使 `start()` 以相同错误码拒绝。
132
+ `attach()` 完成只表示媒体源句柄已经连接;`start()` 完成只表示 Worker 已建立本次启动所需的加载、转封装和 MSE 会话。两者都不表示已经解析出媒体信息、已经追加首个媒体片段,也不表示 `<video>` 已触发 `canplay` 或开始播放。请通过 `mediaInfo`、`stats`、`error` 和 video 元素事件观察后续结果。
124
133
 
125
- 上述规则收紧了旧版本中“发送启动命令后立即完成”的时序;依赖旧时序的调用方应改为等待 `start()`,并将媒体就绪逻辑放到对应事件中。
134
+ 同一启动过程中的并发 `start()` 调用会等待同一个操作,不会重复发送启动命令。播放器已经启动后再次调用 `start()` 会直接完成。若 `stop()` 或 `destroy()` 在启动确认前开始,待处理的 `start()` 会以 `RIVMUX_START_CANCELLED` 拒绝,而停止或销毁操作仍会正常完成。启动确认前发生的终止错误会使待处理的 `start()` 以相同错误码拒绝;启动确认后的错误通过 `error` 事件报告。
126
135
 
127
- ## 配置项
136
+ ## 配置
128
137
 
129
138
  所有配置均为可选项,缺省字段会由播放器内部默认值补齐。
130
139
 
@@ -164,10 +173,10 @@ const player = new RivmuxPlayer('https://example.com/live.flv', {
164
173
 
165
174
  ### `playback`
166
175
 
167
- | 配置项 | 默认值 | 含义 |
168
- | ---------- | ------- | ---------------------------------------------------------- |
169
- | `autoPlay` | `true` | 启动缓冲足够时,由运行时请求 `video.play()`。 |
170
- | `muted` | `false` | 设置 `video.muted`;浏览器通常只允许带音频的静音自动播放。 |
176
+ | 配置项 | 默认值 | 含义 |
177
+ | ---------- | ------- | -------------------------------------------------------------------- |
178
+ | `autoPlay` | `true` | 启动缓冲足够时,由运行时请求 `video.play()`。 |
179
+ | `muted` | `false` | 设置 `video.muted`;浏览器通常要求媒体静音后才能无用户手势自动播放。 |
171
180
 
172
181
  ### `latency`
173
182
 
@@ -193,6 +202,8 @@ const player = new RivmuxPlayer('https://example.com/live.flv', {
193
202
  | `retry.maxBackoffMs` | `8000` | 应用抖动后的最大退避延迟,单位为毫秒。 |
194
203
  | `retry.jitterRatio` | `0.2` | 对称抖动比例,取值范围为 `0` 至 `1`;`0` 表示关闭抖动。 |
195
204
 
205
+ 跨源 HTTP-FLV 地址必须满足浏览器 CORS 规则。使用自定义请求头或 `credentials: 'include'` 时,流服务还需正确处理预检请求并允许相应的请求头和凭据;配置错误可能表现为网络异常并进入重试,但不会通过重连自行恢复。
206
+
196
207
  ### `runtime`
197
208
 
198
209
  `runtime.workerUrl` 和 `runtime.wasmUrl` 是 Experimental 资产部署覆盖项;它们嵌套在稳定的 `RivmuxPlayerOptions` 形状中,但不改变当前固定的 Worker MSE runtime,也不承诺跨 bundler、CSP 或部署方式的长期兼容性。
@@ -202,30 +213,15 @@ const player = new RivmuxPlayer('https://example.com/live.flv', {
202
213
  | `workerUrl` | 包内 Worker URL | 高级配置,用于覆盖包内 Dedicated Worker 脚本 URL。 |
203
214
  | `wasmUrl` | 包内 WASM 模块 URL | 高级配置,用于覆盖 WASM URL;必须与包含 wasm-bindgen 胶水的 Worker 版本匹配。 |
204
215
 
205
- ### Worker/WASM 资产部署
206
-
207
- 多数应用无需设置 `runtime.workerUrl` 和 `runtime.wasmUrl`。默认资产 URL 会被 Vite 等打包工具跟踪,并随应用构建输出。
208
-
209
- 只有在固定公共路径或 CDN 部署 Rivmux 资产时才需要覆盖:
210
-
211
- ```ts
212
- const player = new RivmuxPlayer('https://example.com/live.flv', {
213
- runtime: {
214
- workerUrl: 'https://cdn.example.com/rivmux/0.5.0/rivmux-runtime-worker.js',
215
- wasmUrl: 'https://cdn.example.com/rivmux/0.5.0/rivmux-transmux-core.wasm',
216
- },
217
- })
218
- ```
219
-
220
- `workerUrl` 不会推导 `wasmUrl`。覆盖两项时必须发布同一版本的 Worker/WASM 资产对,并使用相同缓存版本策略。两者都是可执行资产地址,不得由不可信输入拼接。宿主应用需配置兼容的 CSP 和 CORS,并以 `application/wasm` 提供 WASM 文件。
221
-
222
216
  ### `diagnostics`
223
217
 
224
218
  | 配置项 | 默认值 | 含义 |
225
219
  | ----------------- | ------ | ------------------------------------------------------------ |
226
220
  | `statsIntervalMs` | `1000` | 请求的统计上报间隔,单位为毫秒;运行时会在内部限制实际范围。 |
227
221
 
228
- ## 事件
222
+ ## 事件与错误
223
+
224
+ ### 事件
229
225
 
230
226
  ```ts
231
227
  import type { MediaInfo, PlayerError, PlayerStats, PlayerWarning, ReconnectInfo, RecoveryInfo } from 'rivmux'
@@ -255,7 +251,63 @@ player.on('destroyed', () => {})
255
251
 
256
252
  用户事件监听器中的异常属于宿主应用异常,不属于 Rivmux 播放错误,也不会转换为 `PlayerError`。单个监听器抛出异常时,其他监听器仍会继续执行,`stop()`、`destroy()` 等生命周期 Promise 也会按内部状态正常完成。Rivmux 会优先通过平台的 `globalThis.reportError()` 报告该异常;平台不支持时使用 `console.error()` 降级报告。
257
253
 
258
- ## 自动播放拒绝
254
+ ### 错误与 warning
255
+
256
+ `error` 事件提供结构化的 `PlayerError`,`warning` 事件提供可恢复的 `PlayerWarning`:
257
+
258
+ ```ts
259
+ type PlayerErrorKind = 'network' | 'unsupported' | 'demux' | 'codec' | 'mux' | 'mse' | 'runtime'
260
+
261
+ type PlayerErrorCause = {
262
+ name: string
263
+ message: string
264
+ }
265
+
266
+ type PlayerError = {
267
+ kind: PlayerErrorKind
268
+ code: string
269
+ message: string
270
+ terminal: boolean
271
+ cause?: PlayerErrorCause
272
+ }
273
+
274
+ type PlayerWarning = {
275
+ code: string
276
+ message: string
277
+ cause?: PlayerErrorCause
278
+ }
279
+ ```
280
+
281
+ `terminal: true` 表示当前播放器实例不能继续正常工作。非终止错误仍通过 `error` 事件报告;warning 不会进入终止错误路径。
282
+
283
+ 当前 Stable error/warning code 如下:
284
+
285
+ | 类别 | code | 说明 |
286
+ | -------------- | ------------------------------------ | ---------------------------------------------------- |
287
+ | 生命周期与调用 | `RIVMUX_ALREADY_ATTACHED` | 播放器已绑定其他 video 元素,不能重复绑定。 |
288
+ | 生命周期与调用 | `RIVMUX_START_REQUIRES_ATTACH` | 尚未成功完成 `attach()` 就调用了 `start()`。 |
289
+ | 生命周期与调用 | `RIVMUX_START_CANCELLED` | 待处理的 `start()` 被 `stop()` 或 `destroy()` 取消。 |
290
+ | 生命周期与调用 | `RIVMUX_PLAYER_STOPPING` | `stop()` 尚未完成时调用了冲突的生命周期操作。 |
291
+ | 生命周期与调用 | `RIVMUX_PLAYER_DESTROYING` | `destroy()` 进行中,播放器已不再接受新操作。 |
292
+ | 生命周期与调用 | `RIVMUX_PLAYER_DESTROYED` | 播放器已销毁,实例不能继续使用。 |
293
+ | 配置 | `RIVMUX_INVALID_LATENCY_OPTION` | 延迟配置数值无效或配置项之间不满足约束。 |
294
+ | 配置 | `RIVMUX_INVALID_NETWORK_OPTION` | 读取超时或重试配置无效。 |
295
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_WORKER` | 当前环境不支持 Dedicated Worker。 |
296
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_FETCH` | 当前环境不具备所需的流式 Fetch 能力。 |
297
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_READABLE_STREAM` | 当前环境不支持 `ReadableStream`。 |
298
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_WASM` | 当前环境不支持 WebAssembly。 |
299
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_MSE` | 当前环境不支持 Media Source Extensions。 |
300
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_WORKER_MSE` | Dedicated Worker 中无法构造 `MediaSource`。 |
301
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK` | 当前环境缺少 `MediaSource.isTypeSupported()`。 |
302
+ | 能力与媒体 | `RIVMUX_UNSUPPORTED_MSE_CODEC` | MSE 不支持实际媒体流对应的准确 codec MIME。 |
303
+ | 运行行为 | `RIVMUX_RECONNECT_EXHAUSTED` | 当前故障恢复周期已用尽允许的连接次数。 |
304
+ | warning | `RIVMUX_AUTOPLAY_REJECTED` | 浏览器拒绝自动播放,可在用户手势中恢复播放。 |
305
+
306
+ 未列出的 Worker、Loader、MSE 队列和内部生命周期错误码属于内部实现,应用不应依赖其具体字符串。
307
+
308
+ ## 常见运行行为
309
+
310
+ ### 自动播放拒绝
259
311
 
260
312
  浏览器可能根据自动播放策略拒绝 Rivmux 请求的 `video.play()`。无用户手势的直播场景建议设置 `playback.muted: true`,以提高自动播放成功率。拒绝会产生非终止 warning `RIVMUX_AUTOPLAY_REJECTED`,其 `cause` 保留浏览器错误的 `name` 和 `message`;该 warning 不会中断网络加载、转封装或缓冲,也不会触发 `error` 事件。
261
313
 
@@ -269,7 +321,7 @@ button.addEventListener('click', () => {
269
321
 
270
322
  设置 `playback.autoPlay: false` 后,Rivmux 不会主动请求启动播放,也不会产生该 warning。
271
323
 
272
- ## 直播连接恢复
324
+ ### 直播连接恢复
273
325
 
274
326
  Rivmux 会为以下直播网络故障重建 Loader、转封装核心和 MSE 播放会话:
275
327
 
@@ -282,34 +334,41 @@ HTTP `401`、`403` 和其他不可恢复的 `4xx` 不会重试。媒体解析、
282
334
 
283
335
  恢复采用指数退避、最大延迟和抖动。等待重连或重建会话期间调用 `stop()` 或 `destroy()` 会立即取消恢复,不会继续创建连接。恢复会更换 `MediaSourceHandle`,因此可能出现短暂中断和时间线重置;当前不承诺无缝续播或跨连接连续时间线。
284
336
 
285
- ## Codec 支持边界
337
+ ## Worker/WASM 部署
286
338
 
287
- | 输入 | 等级 | 说明 |
288
- | ---------------------------------------- | ------------ | ---------------------------------- |
289
- | HTTP-FLV + AVC/H.264 + AAC-LC | Stable | 受浏览器基础 MSE 能力约束 |
290
- | Enhanced HTTP-FLV + HEVC/`hvc1` + AAC-LC | Stable | 浏览器解码能力是条件化的 |
291
- | Enhanced HTTP-FLV + AV1 | Experimental | 实现层实验能力,组合与兼容性未承诺 |
292
- | Enhanced HTTP-FLV + Opus | Experimental | 实现层实验能力,组合与兼容性未承诺 |
293
- | MPEG-TS | 不支持 | 不属于当前产品输入边界 |
339
+ 多数应用无需设置 `runtime.workerUrl` 和 `runtime.wasmUrl`。默认资产 URL 会被 Vite 跟踪,并随应用构建输出。
294
340
 
295
- HEVC Stable 的具体范围是单视频轨、固定 codec 配置、Enhanced FLV `SequenceStart`、`CodedFrames` 和 HEVC `CodedFramesX`,输出 sample entry 为 `hvc1`。最终解码能力取决于浏览器、操作系统、设备以及具体 HEVC profile、level、bit depth 和 chroma format;Rivmux 不维护固定 profile/level allowlist。结构合法但环境不支持准确 MIME 时会产生终止错误 `RIVMUX_UNSUPPORTED_MSE_CODEC`。
341
+ 只有在固定公共路径或 CDN 部署 Rivmux 资产时才需要覆盖:
342
+
343
+ ```ts
344
+ const player = new RivmuxPlayer('https://example.com/live.flv', {
345
+ runtime: {
346
+ workerUrl: 'https://cdn.example.com/rivmux/<version>/rivmux-runtime-worker.js',
347
+ wasmUrl: 'https://cdn.example.com/rivmux/<version>/rivmux-transmux-core.wasm',
348
+ },
349
+ })
350
+ ```
296
351
 
297
- `hev1`、多轨 HEVC、播放期间动态 codec 配置切换和 HEVC + Opus 不属于 Stable 范围。AV1 与 Opus 仍为 Experimental;当前未承诺它们与其他音视频 codec 的组合、浏览器兼容矩阵或稳定错误语义。能力矩阵中的 `supported` 不会把它们提升为 Stable。
352
+ `workerUrl` 不会推导 `wasmUrl`。覆盖两项时必须发布同一版本的 Worker/WASM 资产对,并使用相同缓存版本策略。两者都是可执行资产地址,不得由不可信输入拼接。`workerUrl` 还必须满足目标浏览器对 module Dedicated Worker 的加载限制;不允许直接加载跨源 Worker 时,应通过同源公共路径提供该脚本。宿主应用需配置兼容的 CSP 和 CORS,并以 `application/wasm` 提供 WASM 文件。
298
353
 
299
- ## 类型导入
354
+ ## 公共类型
300
355
 
301
- 主包会重新导出用户需要的主要类型:
356
+ 主包会重新导出以下 Stable 类型:
302
357
 
303
358
  ```ts
304
359
  import type {
305
- DiagnosticsOptions,
306
360
  DecodingCapabilities,
361
+ DiagnosticsOptions,
307
362
  LatencyOptions,
308
363
  MediaInfo,
309
364
  NetworkOptions,
310
365
  PlaybackOptions,
311
366
  PlayerError,
312
367
  PlayerErrorCause,
368
+ PlayerErrorKind,
369
+ PlayerEventListener,
370
+ PlayerEventMap,
371
+ PlayerEventType,
313
372
  PlayerStats,
314
373
  PlayerWarning,
315
374
  ReconnectInfo,
@@ -318,9 +377,18 @@ import type {
318
377
  RivmuxCapabilities,
319
378
  RivmuxPlayerOptions,
320
379
  RuntimeCapabilities,
321
- RuntimeOptions,
322
380
  SupportStatus,
323
381
  } from 'rivmux'
324
382
  ```
325
383
 
326
- 主包会导出上例中的 Experimental `RuntimeOptions`,但它不属于 Stable 兼容承诺。配置归一化和错误构造函数属于内部实现,未导出为应用 API。
384
+ `RivmuxPlayerOptions` 的整体配置形状属于 Stable,其中嵌套的 `runtime` 字段用于承载 Experimental 资产部署选项。主包也会导出对应的 Experimental 类型:
385
+
386
+ ```ts
387
+ import type { RuntimeOptions } from 'rivmux'
388
+ ```
389
+
390
+ 配置归一化、错误构造函数和 Worker 通信契约属于内部实现,不构成应用 API。
391
+
392
+ ## 许可证
393
+
394
+ [Apache License 2.0](./LICENSE)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/player.ts","../src/feature-detect.ts"],"mappings":";;;;;;;;qBAgCa;;WAEF;mBAIQ;mBACA;mBACA;mBACA;mBACA;UACT;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;;;;;EAOI,YAAA,aAAa,UAAU;;;;;;EAkB7B,OAAO,OAAO,mBAAmB;;;;;;;;EA8BvC,SAAS;UAiCK;;;;;;;EA0Bd,QAAQ;UAyBM;;;;;;;EAkCR,WAAW;UAeH;;EA6Bd,GAAG,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;;EAKrE,IAAI,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;UAI9D;UAgBA;UAmDA;UAiBA;UAcM;UA4BN;UAWA;UAmBA;UAIA;;;;wBCrUM,mBAAmB;wBAInB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/player.ts","../src/feature-detect.ts"],"mappings":";;;;;;;;qBAgCa;;WAEF;mBAIQ;mBACA;mBACA;mBACA;mBACA;UACT;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;;;;;EAOI,YAAA,aAAa,UAAU;;;;;;EAkB7B,OAAO,OAAO,mBAAmB;;;;;;;;EA8BvC,SAAS;UAiCK;;;;;;;EA0Bd,QAAQ;UAyBM;;;;;;;EAkCR,WAAW;UAeH;;EA6Bd,GAAG,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;;EAKrE,IAAI,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;UAI9D;UAgBA;UAmDA;UAiBA;UAcM;UA4BN;UAWA;UAmBA;UAIA;;;;wBCnVM,mBAAmB;wBAInB"}
package/dist/index.js CHANGED
@@ -119,11 +119,6 @@ function probeMime(isTypeSupported, mime) {
119
119
  return "unknown";
120
120
  }
121
121
  }
122
- function combineProfile(video, audio) {
123
- if (video === "unsupported" || audio === "unsupported") return "unsupported";
124
- if (video === "supported" && audio === "supported") return "supported";
125
- return "unknown";
126
- }
127
122
  function getCapabilitiesForEnvironment(environment) {
128
123
  const runtime = {
129
124
  dedicatedWorker: environment.dedicatedWorker,
@@ -146,11 +141,7 @@ function getCapabilitiesForEnvironment(environment) {
146
141
  runtime,
147
142
  decoding: {
148
143
  video,
149
- audio,
150
- stableProfiles: {
151
- avcAac: combineProfile(video.avc, audio.aac),
152
- hevcAac: combineProfile(video.hevc, audio.aac)
153
- }
144
+ audio
154
145
  }
155
146
  };
156
147
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/events.ts","../src/errors.ts","../src/feature-detect.ts","../src/options.ts","../src/playback-controller.ts","../src/player.ts"],"sourcesContent":["import type { PlayerEventListener, PlayerEventMap, PlayerEventType } from '@rivmux/protocol'\n\nexport class PlayerEventEmitter {\n private readonly listeners = new Map<PlayerEventType, Set<PlayerEventListener<PlayerEventType>>>()\n\n on<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n const typedListener = listener as PlayerEventListener<PlayerEventType>\n const listeners = this.listeners.get(type)\n\n if (listeners === undefined) {\n this.listeners.set(type, new Set([typedListener]))\n return\n }\n\n listeners.add(typedListener)\n }\n\n off<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.listeners.get(type)?.delete(listener as PlayerEventListener<PlayerEventType>)\n }\n\n emit<T extends PlayerEventType>(type: T, payload: PlayerEventMap[T]): void {\n for (const listener of this.listeners.get(type) ?? []) {\n try {\n listener(payload)\n } catch (error) {\n reportListenerError(error)\n }\n }\n }\n\n clear(): void {\n this.listeners.clear()\n }\n}\n\nfunction reportListenerError(error: unknown): void {\n try {\n const reportError = (globalThis as typeof globalThis & { reportError?: (error: unknown) => void }).reportError\n if (typeof reportError === 'function') {\n reportError(error)\n return\n }\n } catch {\n // Fall through to the non-throwing console fallback.\n }\n\n try {\n globalThis.console?.error(error)\n } catch {\n // Error reporting must never affect player behavior.\n }\n}\n","import type { PlayerError, PlayerErrorKind } from '@rivmux/protocol'\n\nexport function createPlayerError(kind: PlayerErrorKind, code: string, message: string, terminal: boolean, cause?: unknown): PlayerError {\n return cause === undefined ? { kind, code, message, terminal } : { kind, code, message, terminal, cause: serializeCause(cause) }\n}\n\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\nexport function playerErrorToException(error: PlayerError): Error {\n const exception = new Error(error.message)\n exception.name = error.code\n return exception\n}\n\nfunction serializeCause(cause: unknown): { name: string; message: string } {\n if (cause instanceof Error) {\n return { name: cause.name, message: cause.message }\n }\n\n if (typeof cause === 'object' && cause !== null) {\n const value = cause as { name?: unknown; message?: unknown }\n return {\n name: typeof value.name === 'string' ? value.name : 'Error',\n message: typeof value.message === 'string' ? value.message : String(cause),\n }\n }\n\n return { name: 'Error', message: String(cause) }\n}\n","import { createPlayerError } from './errors'\n\nimport type { PlayerError, RivmuxCapabilities, RuntimeCapabilities, SupportStatus } from '@rivmux/protocol'\n\ninterface CapabilityEnvironment {\n dedicatedWorker: boolean\n fetchStreaming: boolean\n readableStream: boolean\n webAssembly: boolean\n mediaSource: boolean\n workerMseConstructable: boolean\n isTypeSupported?: (mime: string) => boolean\n}\n\nconst MIME = {\n avc: 'video/mp4; codecs=\"avc1.42E01E\"',\n hevc: 'video/mp4; codecs=\"hvc1.1.6.L93.B0\"',\n av1: 'video/mp4; codecs=\"av01.0.08M.08\"',\n aac: 'audio/mp4; codecs=\"mp4a.40.2\"',\n opus: 'audio/mp4; codecs=\"opus\"',\n} as const\n\nfunction safelyRead<T>(read: () => T, fallback: T): T {\n try {\n return read()\n } catch {\n return fallback\n }\n}\n\nfunction readCapabilityEnvironment(): CapabilityEnvironment {\n const dedicatedWorker = safelyRead(() => typeof Worker === 'function', false)\n const fetchAvailable = safelyRead(() => typeof fetch === 'function', false)\n const responseBodyAvailable = safelyRead(() => typeof Response === 'function' && Response.prototype !== undefined && 'body' in Response.prototype, false)\n const readableStream = safelyRead(() => typeof ReadableStream === 'function', false)\n const webAssembly = safelyRead(() => typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function', false)\n const mediaSourceConstructor = safelyRead(() => (typeof MediaSource === 'function' ? MediaSource : undefined), undefined as typeof MediaSource | undefined)\n const mediaSource = mediaSourceConstructor !== undefined\n const workerMseConstructable = mediaSource && safelyRead(() => mediaSourceConstructor.canConstructInDedicatedWorker === true, false)\n const isTypeSupported = mediaSource\n ? safelyRead(\n () => (typeof mediaSourceConstructor.isTypeSupported === 'function' ? mediaSourceConstructor.isTypeSupported.bind(mediaSourceConstructor) : undefined),\n undefined\n )\n : undefined\n\n return {\n dedicatedWorker,\n fetchStreaming: fetchAvailable && responseBodyAvailable,\n readableStream,\n webAssembly,\n mediaSource,\n workerMseConstructable,\n isTypeSupported,\n }\n}\n\nfunction probeMime(isTypeSupported: CapabilityEnvironment['isTypeSupported'], mime: string): SupportStatus {\n if (isTypeSupported === undefined) {\n return 'unknown'\n }\n\n try {\n return isTypeSupported(mime) ? 'supported' : 'unsupported'\n } catch {\n return 'unknown'\n }\n}\n\nfunction combineProfile(video: SupportStatus, audio: SupportStatus): SupportStatus {\n if (video === 'unsupported' || audio === 'unsupported') {\n return 'unsupported'\n }\n if (video === 'supported' && audio === 'supported') {\n return 'supported'\n }\n return 'unknown'\n}\n\nfunction getCapabilitiesForEnvironment(environment: CapabilityEnvironment): RivmuxCapabilities {\n const runtime: RuntimeCapabilities = {\n dedicatedWorker: environment.dedicatedWorker,\n workerMse: environment.workerMseConstructable && environment.isTypeSupported !== undefined,\n fetchStreaming: environment.fetchStreaming,\n readableStream: environment.readableStream,\n webAssembly: environment.webAssembly,\n }\n const video = {\n avc: probeMime(environment.isTypeSupported, MIME.avc),\n hevc: probeMime(environment.isTypeSupported, MIME.hevc),\n av1: probeMime(environment.isTypeSupported, MIME.av1),\n }\n const audio = {\n aac: probeMime(environment.isTypeSupported, MIME.aac),\n opus: probeMime(environment.isTypeSupported, MIME.opus),\n }\n\n return {\n supported: Object.values(runtime).every(Boolean),\n runtime,\n decoding: {\n video,\n audio,\n stableProfiles: {\n avcAac: combineProfile(video.avc, audio.aac),\n hevcAac: combineProfile(video.hevc, audio.aac),\n },\n },\n }\n}\n\nexport function getCapabilities(): RivmuxCapabilities {\n return getCapabilitiesForEnvironment(readCapabilityEnvironment())\n}\n\nexport function isSupported(): boolean {\n return getCapabilities().supported\n}\n\nexport function detectMainThreadRuntime(environment = readCapabilityEnvironment()): PlayerError | undefined {\n if (!environment.dedicatedWorker) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER', 'Dedicated Worker is not available in this runtime.', true)\n }\n\n if (!environment.fetchStreaming) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_FETCH', 'Fetch is not available in this runtime.', true)\n }\n\n if (!environment.readableStream) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_READABLE_STREAM', 'ReadableStream is not available in this runtime.', true)\n }\n\n if (!environment.webAssembly) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WASM', 'WebAssembly is not available in this runtime.', true)\n }\n\n if (!environment.mediaSource) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE', 'MediaSource is not available in this runtime.', true)\n }\n\n if (!environment.workerMseConstructable) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER_MSE', 'MediaSource cannot be constructed in a dedicated worker in this runtime.', true)\n }\n\n if (environment.isTypeSupported === undefined) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK', 'MediaSource.isTypeSupported is not available in this runtime.', true)\n }\n\n return undefined\n}\n","import { createPlayerError, playerErrorToException } from './errors'\n\nimport type { NormalizedRivmuxPlayerOptions, PlayerErrorKind, RivmuxPlayerOptions } from '@rivmux/protocol'\n\nconst DEFAULT_RIVMUX_PLAYER_OPTIONS: NormalizedRivmuxPlayerOptions = {\n playback: {\n autoPlay: true,\n muted: false,\n },\n latency: {\n startupBuffer: 0.35,\n target: 1.2,\n max: 2.5,\n maxForwardBuffer: 4,\n backwardBuffer: 1.5,\n },\n network: {\n headers: {},\n credentials: 'same-origin',\n readIdleTimeoutMs: 10_000,\n retry: {\n maxAttempts: 3,\n backoffMs: 500,\n maxBackoffMs: 8_000,\n jitterRatio: 0.2,\n },\n },\n runtime: {},\n diagnostics: { statsIntervalMs: 1000 },\n}\n\nexport function normalizePlayerOptions(options: RivmuxPlayerOptions = {}): NormalizedRivmuxPlayerOptions {\n const normalizedOptions = {\n playback: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.playback,\n ...options.playback,\n },\n latency: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.latency,\n ...options.latency,\n },\n network: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network,\n ...options.network,\n headers: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network.headers,\n ...options.network?.headers,\n },\n retry: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network.retry,\n ...options.network?.retry,\n },\n },\n runtime: normalizeRuntimeOptions(options),\n diagnostics: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.diagnostics,\n ...options.diagnostics,\n },\n }\n\n validateNormalizedOptions(normalizedOptions)\n return normalizedOptions\n}\n\nfunction validateNormalizedOptions(options: NormalizedRivmuxPlayerOptions): void {\n validateLatencyOptions(options)\n validateNetworkOptions(options)\n}\n\nfunction validateLatencyOptions(options: NormalizedRivmuxPlayerOptions): void {\n const latency = options.latency\n\n assertFiniteNonNegativeLatency(latency.startupBuffer, 'latency.startupBuffer')\n assertFinitePositiveLatency(latency.target, 'latency.target')\n assertFiniteNonNegativeLatency(latency.max, 'latency.max')\n assertFiniteNonNegativeLatency(latency.maxForwardBuffer, 'latency.maxForwardBuffer')\n assertFiniteNonNegativeLatency(latency.backwardBuffer, 'latency.backwardBuffer')\n\n if (latency.max < latency.target) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', 'latency.max must be greater than or equal to latency.target.')\n }\n\n if (latency.maxForwardBuffer < latency.target) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', 'latency.maxForwardBuffer must be greater than or equal to latency.target.')\n }\n}\n\nfunction validateNetworkOptions(options: NormalizedRivmuxPlayerOptions): void {\n const network = options.network\n const retry = network.retry\n\n assertPositiveInteger(network.readIdleTimeoutMs, 'network.readIdleTimeoutMs')\n assertPositiveInteger(retry.maxAttempts, 'network.retry.maxAttempts')\n assertNonNegativeInteger(retry.backoffMs, 'network.retry.backoffMs')\n assertNonNegativeInteger(retry.maxBackoffMs, 'network.retry.maxBackoffMs')\n\n if (retry.maxBackoffMs < retry.backoffMs) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', 'network.retry.maxBackoffMs must be greater than or equal to network.retry.backoffMs.')\n }\n\n if (!Number.isFinite(retry.jitterRatio) || retry.jitterRatio < 0 || retry.jitterRatio > 1) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', 'network.retry.jitterRatio must be a finite number between 0 and 1.')\n }\n}\n\nfunction normalizeRuntimeOptions(options: RivmuxPlayerOptions): NormalizedRivmuxPlayerOptions['runtime'] {\n return {\n ...(options.runtime?.workerUrl === undefined ? {} : { workerUrl: options.runtime.workerUrl }),\n ...(options.runtime?.wasmUrl === undefined ? {} : { wasmUrl: options.runtime.wasmUrl }),\n }\n}\n\nfunction assertFiniteNonNegativeLatency(value: number, field: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', `${field} must be a finite number greater than or equal to 0.`)\n }\n}\n\nfunction assertFinitePositiveLatency(value: number, field: string): void {\n if (!Number.isFinite(value) || value <= 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', `${field} must be a finite number greater than 0.`)\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', `${field} must be an integer greater than or equal to 1.`)\n }\n}\n\nfunction assertNonNegativeInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', `${field} must be an integer greater than or equal to 0.`)\n }\n}\n\nfunction throwOptionError(kind: PlayerErrorKind, code: string, message: string): never {\n throw playerErrorToException(createPlayerError(kind, code, message, true))\n}\n","import type { NormalizedPlaybackOptions, PlaybackControlAction, PlaybackControlResult, VideoElementState } from '@rivmux/protocol'\n\nexport class PlaybackController {\n private readonly options: NormalizedPlaybackOptions\n private video?: HTMLVideoElement\n private stateTimer?: ReturnType<typeof setInterval>\n\n constructor(options: NormalizedPlaybackOptions) {\n this.options = options\n }\n\n get attached(): boolean {\n return this.video !== undefined\n }\n\n isAttachedTo(video: HTMLVideoElement): boolean {\n return this.video === video\n }\n\n attach(video: HTMLVideoElement): void {\n this.video = video\n video.muted = this.options.muted\n video.autoplay = this.options.autoPlay\n }\n\n attachMediaSourceHandle(handle: MediaSourceHandle): boolean {\n if (this.video === undefined) {\n return false\n }\n\n Reflect.set(this.video, 'srcObject', handle)\n return true\n }\n\n startStateReporting(intervalMs: number, report: (state: VideoElementState) => void): void {\n this.stopStateReporting()\n const reportCurrentState = (): void => {\n const state = this.snapshot()\n if (state !== undefined) {\n report(state)\n }\n }\n\n reportCurrentState()\n this.stateTimer = setInterval(reportCurrentState, intervalMs)\n }\n\n stopStateReporting(): void {\n if (this.stateTimer !== undefined) {\n clearInterval(this.stateTimer)\n this.stateTimer = undefined\n }\n }\n\n async applyControl(action: PlaybackControlAction): Promise<PlaybackControlResult> {\n const video = this.video\n if (video === undefined) {\n return {\n type: action.type,\n accepted: false,\n error: {\n name: 'NotAttachedError',\n message: 'No video element is attached.',\n },\n }\n }\n\n try {\n switch (action.type) {\n case 'play':\n await video.play()\n break\n case 'set-playback-rate':\n video.playbackRate = action.playbackRate\n break\n case 'seek':\n video.currentTime = action.targetTime\n break\n }\n return { type: action.type, accepted: true }\n } catch (cause) {\n return {\n type: action.type,\n accepted: false,\n error: normalizePlaybackControlError(cause),\n }\n }\n }\n\n detachSource(): void {\n if (this.video === undefined) {\n return\n }\n\n this.video.pause()\n this.video.playbackRate = 1\n this.video.removeAttribute('src')\n this.video.srcObject = null\n this.video.load()\n }\n\n release(): void {\n this.stopStateReporting()\n this.detachSource()\n this.video = undefined\n }\n\n private snapshot(): VideoElementState | undefined {\n const video = this.video\n if (video === undefined) {\n return undefined\n }\n\n const droppedFrames = getDroppedFrames(video)\n return {\n currentTime: Number.isFinite(video.currentTime) ? video.currentTime : 0,\n readyState: video.readyState,\n playbackRate: video.playbackRate,\n paused: video.paused,\n ...(droppedFrames === undefined ? {} : { droppedFrames }),\n }\n }\n}\n\nfunction normalizePlaybackControlError(cause: unknown): { name: string; message: string } {\n if (cause instanceof Error) {\n return {\n name: safelyStringifyErrorField(() => cause.name, 'Error'),\n message: safelyStringifyErrorField(() => cause.message, 'Unknown playback control error.'),\n }\n }\n\n try {\n return {\n name: 'Error',\n message: String(cause),\n }\n } catch {\n return {\n name: 'Error',\n message: 'Unknown playback control error.',\n }\n }\n}\n\nfunction safelyStringifyErrorField(read: () => unknown, fallback: string): string {\n try {\n return String(read())\n } catch {\n return fallback\n }\n}\n\nfunction getDroppedFrames(video: HTMLVideoElement): number | undefined {\n const quality = video.getVideoPlaybackQuality?.()\n if (quality !== undefined && Number.isFinite(quality.droppedVideoFrames)) {\n return quality.droppedVideoFrames\n }\n\n const webkitDroppedFrameCount = (video as HTMLVideoElement & { webkitDroppedFrameCount?: number }).webkitDroppedFrameCount\n return Number.isFinite(webkitDroppedFrameCount) ? webkitDroppedFrameCount : undefined\n}\n","import { PlayerEventEmitter } from './events'\nimport { createPlayerError, playerErrorToException } from './errors'\nimport { detectMainThreadRuntime } from './feature-detect'\nimport { normalizePlayerOptions } from './options'\nimport { PlaybackController } from './playback-controller'\nimport { createRuntimeWorker, getBundledWasmUrl, WorkerClient } from '@rivmux/runtime-worker'\n\nimport type {\n NormalizedRivmuxPlayerOptions,\n PlaybackControlAction,\n PlayerError,\n PlayerEventListener,\n PlayerEventType,\n RivmuxPlayerOptions,\n WorkerMessage,\n} from '@rivmux/protocol'\nimport type { RuntimeWorkerFactory } from '@rivmux/runtime-worker'\n\ntype PlayerState = 'idle' | 'attached' | 'starting' | 'started' | 'stopping' | 'stopped' | 'fatal-error' | 'destroying' | 'destroyed'\n\ntype RivmuxPlayerInternals = {\n workerFactory?: RuntimeWorkerFactory\n detectRuntime?: () => PlayerError | undefined\n idFactory?: () => string\n}\n\n/**\n * Public browser player facade for one HTTP-FLV stream.\n *\n * Create one instance per stream URL, call `attach(video)` first, then\n * `start()`. Call `destroy()` when the instance is no longer needed.\n */\nexport class RivmuxPlayer {\n /** Original stream URL passed to the constructor. */\n readonly url: string\n\n /** @internal Fully normalized options with defaults applied. */\n readonly options: NormalizedRivmuxPlayerOptions\n private readonly id: string\n private readonly events = new PlayerEventEmitter()\n private readonly workerFactory: RuntimeWorkerFactory\n private readonly detectRuntime: () => PlayerError | undefined\n private readonly playback: PlaybackController\n private workerClient?: WorkerClient\n private state: PlayerState = 'idle'\n private lifecycleGeneration = 0\n private terminalError?: PlayerError\n private autoplayWarningEmitted = false\n private startPromise?: Promise<void>\n private stopPromise?: Promise<void>\n private destroyPromise?: Promise<void>\n\n /**\n * Creates a player instance for one stream URL.\n *\n * The instance does not start network loading until `start()` is called.\n */\n constructor(url: string, options?: RivmuxPlayerOptions)\n /** @internal Test-only dependency injection; omitted from generated declarations. */\n constructor(url: string, options?: RivmuxPlayerOptions, internals?: RivmuxPlayerInternals)\n constructor(url: string, options?: RivmuxPlayerOptions, internals?: RivmuxPlayerInternals) {\n internals ??= {}\n this.url = url\n this.options = normalizePlayerOptions(options)\n this.id = internals.idFactory?.() ?? createPlayerId()\n this.workerFactory = internals.workerFactory ?? createRuntimeWorker\n this.detectRuntime = internals.detectRuntime ?? detectMainThreadRuntime\n this.playback = new PlaybackController(this.options.playback)\n }\n\n /**\n * Attaches this player to a video element and prepares the worker/MSE pipe.\n *\n * Await this method before calling `start()`.\n */\n async attach(video: HTMLVideoElement): Promise<void> {\n this.assertOperational('attach')\n const lifecycleGeneration = this.lifecycleGeneration\n\n if (this.playback.attached && !this.playback.isAttachedTo(video)) {\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_ALREADY_ATTACHED', 'This player is already attached to a video element.', false))\n }\n\n const runtimeError = this.detectRuntime()\n if (runtimeError !== undefined) {\n this.events.emit('error', runtimeError)\n throw playerErrorToException(runtimeError)\n }\n\n this.playback.attach(video)\n this.ensureWorkerClient()\n await this.workerClient?.waitForMediaSourceHandle({ type: 'attach-media-source' })\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n this.state = 'attached'\n }\n\n /**\n * Waits until the worker has created the transmux core and scheduled stream\n * consumption for this playback session.\n *\n * This does not wait for media data, `canplay`, or `video.play()`. Requires a\n * successful `attach(video)` call first.\n */\n start(): Promise<void> {\n try {\n this.assertOperational('start')\n } catch (cause) {\n return Promise.reject(cause)\n }\n\n if (!this.playback.attached || this.workerClient === undefined) {\n return Promise.reject(\n playerErrorToException(createPlayerError('runtime', 'RIVMUX_START_REQUIRES_ATTACH', 'start() requires a previously attached video element.', false))\n )\n }\n\n if (this.state === 'started') {\n return Promise.resolve()\n }\n\n if (this.startPromise !== undefined) {\n return this.startPromise\n }\n\n const lifecycleGeneration = this.lifecycleGeneration\n const previousState = this.state\n this.state = 'starting'\n const starting = this.performStart(this.workerClient, lifecycleGeneration, previousState === 'stopped').finally(() => {\n if (this.startPromise === starting) {\n this.startPromise = undefined\n }\n })\n this.startPromise = starting\n return starting\n }\n\n private async performStart(workerClient: WorkerClient, lifecycleGeneration: number, restart: boolean): Promise<void> {\n try {\n if (restart) {\n await workerClient.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.assertStartOperationCurrent(lifecycleGeneration)\n }\n\n await workerClient.waitForStarted({ type: 'start' })\n this.assertStartOperationCurrent(lifecycleGeneration)\n } catch (cause) {\n if (this.isLifecycleOperationCurrent(lifecycleGeneration) && this.state === 'starting') {\n this.state = restart ? 'stopped' : 'attached'\n }\n throw toPublicStartError(cause)\n }\n\n this.state = 'started'\n this.startVideoStateReporting()\n }\n\n /**\n * Stops loading and detaches the current media source.\n *\n * The instance remains reusable; call `start()` again to restart the same\n * stream after the player has stopped.\n */\n stop(): Promise<void> {\n if (this.state === 'destroyed' || this.state === 'destroying') {\n return Promise.resolve()\n }\n\n if (this.state === 'fatal-error') {\n return Promise.resolve()\n }\n\n if (this.stopPromise !== undefined) {\n return this.stopPromise\n }\n\n this.lifecycleGeneration += 1\n this.state = 'stopping'\n const lifecycleGeneration = this.lifecycleGeneration\n const stopping = this.performStop(lifecycleGeneration).finally(() => {\n if (this.stopPromise === stopping) {\n this.stopPromise = undefined\n }\n })\n this.stopPromise = stopping\n return stopping\n }\n\n private async performStop(lifecycleGeneration: number): Promise<void> {\n const workerClient = this.workerClient\n if (workerClient === undefined) {\n if (this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n this.autoplayWarningEmitted = false\n this.state = 'stopped'\n this.events.emit('stopped', undefined)\n }\n return\n }\n\n this.playback.stopStateReporting()\n try {\n await workerClient.waitForStopped({ type: 'stop' })\n } catch (error) {\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n throw error\n }\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n this.playback.detachSource()\n this.autoplayWarningEmitted = false\n this.state = 'stopped'\n }\n\n /**\n * Releases worker resources, timers, listeners, and the attached video source.\n *\n * The instance is terminal after destroy and cannot be attached or started\n * again.\n */\n async destroy(): Promise<void> {\n if (this.state === 'destroyed') {\n return\n }\n\n if (this.destroyPromise !== undefined) {\n return this.destroyPromise\n }\n\n this.lifecycleGeneration += 1\n this.state = 'destroying'\n this.destroyPromise = this.performDestroy()\n return this.destroyPromise\n }\n\n private async performDestroy(): Promise<void> {\n const workerClient = this.workerClient\n this.workerClient = undefined\n this.playback.stopStateReporting()\n let destroyError: unknown\n\n if (workerClient !== undefined) {\n try {\n await workerClient.waitForDestroyed({ type: 'destroy' })\n } catch (error) {\n destroyError = error\n } finally {\n workerClient.dispose()\n }\n } else {\n this.events.emit('destroyed', undefined)\n }\n\n this.playback.release()\n\n this.state = 'destroyed'\n this.events.clear()\n\n if (destroyError !== undefined) {\n throw destroyError\n }\n }\n\n /** Registers an event listener for a typed player event. */\n on<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.events.on(type, listener)\n }\n\n /** Removes a previously registered event listener. */\n off<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.events.off(type, listener)\n }\n\n private ensureWorkerClient(): void {\n if (this.workerClient !== undefined) {\n return\n }\n\n const worker = this.workerFactory(this.options)\n this.workerClient = new WorkerClient(worker, {\n onMessage: (message) => this.handleWorkerMessage(message),\n onError: (error) => {\n this.enterFatalErrorState(error)\n this.events.emit('error', error)\n },\n })\n this.workerClient.post({ type: 'init', id: this.id, url: this.url, options: withBundledWasmUrl(this.options) })\n }\n\n private handleWorkerMessage(message: WorkerMessage): void {\n if (this.state === 'destroyed') {\n return\n }\n\n if ((this.state === 'destroying' && message.type !== 'destroyed') || this.state === 'fatal-error') {\n return\n }\n\n switch (message.type) {\n case 'ready':\n this.events.emit('initialized', undefined)\n return\n case 'media-source-handle':\n this.attachMediaSourceHandle(message.handle)\n return\n case 'started':\n return\n case 'media-info':\n this.events.emit('mediaInfo', message.mediaInfo)\n return\n case 'stats':\n this.events.emit('stats', message.stats)\n return\n case 'warning':\n this.events.emit('warning', message.warning)\n return\n case 'reconnecting':\n this.events.emit('reconnecting', message.info)\n return\n case 'recovered':\n this.events.emit('recovered', message.info)\n return\n case 'error':\n if (message.error.terminal) {\n this.enterFatalErrorState(message.error)\n }\n this.events.emit('error', message.error)\n return\n case 'playback-control':\n void this.applyPlaybackControl(message.action)\n return\n case 'stopped':\n this.events.emit('stopped', undefined)\n return\n case 'destroyed':\n this.events.emit('destroyed', undefined)\n return\n }\n }\n\n private attachMediaSourceHandle(handle: MediaSourceHandle): void {\n if (this.state === 'started') {\n this.autoplayWarningEmitted = false\n this.playback.detachSource()\n }\n if (!this.playback.attachMediaSourceHandle(handle)) {\n const error = createPlayerError(\n 'runtime',\n 'RIVMUX_ATTACH_HANDLE_WITHOUT_VIDEO',\n 'Worker returned a MediaSourceHandle before a video element was attached.',\n true\n )\n this.events.emit('error', error)\n throw playerErrorToException(error)\n }\n }\n\n private startVideoStateReporting(): void {\n const intervalMs = Math.max(100, Math.min(this.options.diagnostics.statsIntervalMs, 250))\n const workerClient = this.workerClient\n if (workerClient === undefined) {\n return\n }\n\n this.playback.startStateReporting(intervalMs, (state) => {\n if (this.state === 'started' && this.workerClient === workerClient) {\n workerClient.post({ type: 'video-state', state })\n }\n })\n }\n\n private async applyPlaybackControl(action: PlaybackControlAction): Promise<void> {\n const workerClient = this.workerClient\n const lifecycleGeneration = this.lifecycleGeneration\n if (!this.playback.attached || workerClient === undefined || this.state !== 'started') {\n return\n }\n\n const result = await this.playback.applyControl(action)\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration) || this.state !== 'started' || this.workerClient !== workerClient) {\n return\n }\n workerClient.post({ type: 'playback-control-result', result })\n if (\n action.type === 'play' &&\n action.reason === 'startup-buffer-ready' &&\n this.options.playback.autoPlay &&\n !result.accepted &&\n !this.autoplayWarningEmitted\n ) {\n this.autoplayWarningEmitted = true\n this.events.emit('warning', {\n code: 'RIVMUX_AUTOPLAY_REJECTED',\n message: '浏览器拒绝了自动播放;请在用户手势处理器中调用 video.play() 恢复播放。',\n cause: result.error,\n })\n }\n }\n\n private enterFatalErrorState(error: PlayerError): void {\n if (this.state === 'destroyed' || this.state === 'destroying' || this.state === 'fatal-error') {\n return\n }\n\n this.lifecycleGeneration += 1\n this.terminalError = error\n this.state = 'fatal-error'\n this.playback.release()\n }\n\n private assertOperational(method: string): void {\n if (this.state === 'fatal-error' && this.terminalError !== undefined) {\n throw playerErrorToException(this.terminalError)\n }\n\n if (this.state === 'stopping') {\n throw playerErrorToException(\n createPlayerError('runtime', 'RIVMUX_PLAYER_STOPPING', `RivmuxPlayer.${method}() cannot be called while stop() is pending.`, false)\n )\n }\n\n if (this.state !== 'destroyed' && this.state !== 'destroying') {\n return\n }\n\n const code = this.state === 'destroying' ? 'RIVMUX_PLAYER_DESTROYING' : 'RIVMUX_PLAYER_DESTROYED'\n throw playerErrorToException(createPlayerError('runtime', code, `RivmuxPlayer.${method}() cannot be called after destroy() begins.`, true))\n }\n\n private isLifecycleOperationCurrent(generation: number): boolean {\n return generation === this.lifecycleGeneration && this.state !== 'fatal-error' && this.state !== 'destroying' && this.state !== 'destroyed'\n }\n\n private assertStartOperationCurrent(generation: number): void {\n if (this.isLifecycleOperationCurrent(generation) && this.state === 'starting') {\n return\n }\n\n const action = this.state === 'destroying' || this.state === 'destroyed' ? 'destroy' : 'stop'\n throw createPlayerError('runtime', 'RIVMUX_START_CANCELLED', `Start was cancelled by ${action}.`, false)\n }\n}\n\nfunction toPublicStartError(cause: unknown): Error {\n return isPlayerError(cause) ? playerErrorToException(cause) : cause instanceof Error ? cause : new Error(String(cause))\n}\n\nfunction isPlayerError(value: unknown): value is PlayerError {\n if (typeof value !== 'object' || value === null) {\n return false\n }\n\n const candidate = value as Partial<PlayerError>\n return (\n typeof candidate.kind === 'string' && typeof candidate.code === 'string' && typeof candidate.message === 'string' && typeof candidate.terminal === 'boolean'\n )\n}\n\nfunction createPlayerId(): string {\n const random = globalThis.crypto?.randomUUID?.()\n return random ?? `rivmux-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n}\n\nfunction withBundledWasmUrl(options: NormalizedRivmuxPlayerOptions): NormalizedRivmuxPlayerOptions {\n if (options.runtime.wasmUrl !== undefined) {\n return options\n }\n\n return {\n ...options,\n runtime: {\n ...options.runtime,\n wasmUrl: getBundledWasmUrl(),\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/events.ts","../src/errors.ts","../src/feature-detect.ts","../src/options.ts","../src/playback-controller.ts","../src/player.ts"],"sourcesContent":["import type { PlayerEventListener, PlayerEventMap, PlayerEventType } from '@rivmux/protocol'\n\nexport class PlayerEventEmitter {\n private readonly listeners = new Map<PlayerEventType, Set<PlayerEventListener<PlayerEventType>>>()\n\n on<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n const typedListener = listener as PlayerEventListener<PlayerEventType>\n const listeners = this.listeners.get(type)\n\n if (listeners === undefined) {\n this.listeners.set(type, new Set([typedListener]))\n return\n }\n\n listeners.add(typedListener)\n }\n\n off<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.listeners.get(type)?.delete(listener as PlayerEventListener<PlayerEventType>)\n }\n\n emit<T extends PlayerEventType>(type: T, payload: PlayerEventMap[T]): void {\n for (const listener of this.listeners.get(type) ?? []) {\n try {\n listener(payload)\n } catch (error) {\n reportListenerError(error)\n }\n }\n }\n\n clear(): void {\n this.listeners.clear()\n }\n}\n\nfunction reportListenerError(error: unknown): void {\n try {\n const reportError = (globalThis as typeof globalThis & { reportError?: (error: unknown) => void }).reportError\n if (typeof reportError === 'function') {\n reportError(error)\n return\n }\n } catch {\n // Fall through to the non-throwing console fallback.\n }\n\n try {\n globalThis.console?.error(error)\n } catch {\n // Error reporting must never affect player behavior.\n }\n}\n","import type { PlayerError, PlayerErrorKind } from '@rivmux/protocol'\n\nexport function createPlayerError(kind: PlayerErrorKind, code: string, message: string, terminal: boolean, cause?: unknown): PlayerError {\n return cause === undefined ? { kind, code, message, terminal } : { kind, code, message, terminal, cause: serializeCause(cause) }\n}\n\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\nexport function playerErrorToException(error: PlayerError): Error {\n const exception = new Error(error.message)\n exception.name = error.code\n return exception\n}\n\nfunction serializeCause(cause: unknown): { name: string; message: string } {\n if (cause instanceof Error) {\n return { name: cause.name, message: cause.message }\n }\n\n if (typeof cause === 'object' && cause !== null) {\n const value = cause as { name?: unknown; message?: unknown }\n return {\n name: typeof value.name === 'string' ? value.name : 'Error',\n message: typeof value.message === 'string' ? value.message : String(cause),\n }\n }\n\n return { name: 'Error', message: String(cause) }\n}\n","import { createPlayerError } from './errors'\n\nimport type { PlayerError, RivmuxCapabilities, RuntimeCapabilities, SupportStatus } from '@rivmux/protocol'\n\ninterface CapabilityEnvironment {\n dedicatedWorker: boolean\n fetchStreaming: boolean\n readableStream: boolean\n webAssembly: boolean\n mediaSource: boolean\n workerMseConstructable: boolean\n isTypeSupported?: (mime: string) => boolean\n}\n\nconst MIME = {\n avc: 'video/mp4; codecs=\"avc1.42E01E\"',\n hevc: 'video/mp4; codecs=\"hvc1.1.6.L93.B0\"',\n av1: 'video/mp4; codecs=\"av01.0.08M.08\"',\n aac: 'audio/mp4; codecs=\"mp4a.40.2\"',\n opus: 'audio/mp4; codecs=\"opus\"',\n} as const\n\nfunction safelyRead<T>(read: () => T, fallback: T): T {\n try {\n return read()\n } catch {\n return fallback\n }\n}\n\nfunction readCapabilityEnvironment(): CapabilityEnvironment {\n const dedicatedWorker = safelyRead(() => typeof Worker === 'function', false)\n const fetchAvailable = safelyRead(() => typeof fetch === 'function', false)\n const responseBodyAvailable = safelyRead(() => typeof Response === 'function' && Response.prototype !== undefined && 'body' in Response.prototype, false)\n const readableStream = safelyRead(() => typeof ReadableStream === 'function', false)\n const webAssembly = safelyRead(() => typeof WebAssembly === 'object' && typeof WebAssembly.instantiate === 'function', false)\n const mediaSourceConstructor = safelyRead(() => (typeof MediaSource === 'function' ? MediaSource : undefined), undefined as typeof MediaSource | undefined)\n const mediaSource = mediaSourceConstructor !== undefined\n const workerMseConstructable = mediaSource && safelyRead(() => mediaSourceConstructor.canConstructInDedicatedWorker === true, false)\n const isTypeSupported = mediaSource\n ? safelyRead(\n () => (typeof mediaSourceConstructor.isTypeSupported === 'function' ? mediaSourceConstructor.isTypeSupported.bind(mediaSourceConstructor) : undefined),\n undefined\n )\n : undefined\n\n return {\n dedicatedWorker,\n fetchStreaming: fetchAvailable && responseBodyAvailable,\n readableStream,\n webAssembly,\n mediaSource,\n workerMseConstructable,\n isTypeSupported,\n }\n}\n\nfunction probeMime(isTypeSupported: CapabilityEnvironment['isTypeSupported'], mime: string): SupportStatus {\n if (isTypeSupported === undefined) {\n return 'unknown'\n }\n\n try {\n return isTypeSupported(mime) ? 'supported' : 'unsupported'\n } catch {\n return 'unknown'\n }\n}\n\nfunction getCapabilitiesForEnvironment(environment: CapabilityEnvironment): RivmuxCapabilities {\n const runtime: RuntimeCapabilities = {\n dedicatedWorker: environment.dedicatedWorker,\n workerMse: environment.workerMseConstructable && environment.isTypeSupported !== undefined,\n fetchStreaming: environment.fetchStreaming,\n readableStream: environment.readableStream,\n webAssembly: environment.webAssembly,\n }\n const video = {\n avc: probeMime(environment.isTypeSupported, MIME.avc),\n hevc: probeMime(environment.isTypeSupported, MIME.hevc),\n av1: probeMime(environment.isTypeSupported, MIME.av1),\n }\n const audio = {\n aac: probeMime(environment.isTypeSupported, MIME.aac),\n opus: probeMime(environment.isTypeSupported, MIME.opus),\n }\n\n return {\n supported: Object.values(runtime).every(Boolean),\n runtime,\n decoding: {\n video,\n audio,\n },\n }\n}\n\nexport function getCapabilities(): RivmuxCapabilities {\n return getCapabilitiesForEnvironment(readCapabilityEnvironment())\n}\n\nexport function isSupported(): boolean {\n return getCapabilities().supported\n}\n\nexport function detectMainThreadRuntime(environment = readCapabilityEnvironment()): PlayerError | undefined {\n if (!environment.dedicatedWorker) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER', 'Dedicated Worker is not available in this runtime.', true)\n }\n\n if (!environment.fetchStreaming) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_FETCH', 'Fetch is not available in this runtime.', true)\n }\n\n if (!environment.readableStream) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_READABLE_STREAM', 'ReadableStream is not available in this runtime.', true)\n }\n\n if (!environment.webAssembly) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WASM', 'WebAssembly is not available in this runtime.', true)\n }\n\n if (!environment.mediaSource) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE', 'MediaSource is not available in this runtime.', true)\n }\n\n if (!environment.workerMseConstructable) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER_MSE', 'MediaSource cannot be constructed in a dedicated worker in this runtime.', true)\n }\n\n if (environment.isTypeSupported === undefined) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK', 'MediaSource.isTypeSupported is not available in this runtime.', true)\n }\n\n return undefined\n}\n","import { createPlayerError, playerErrorToException } from './errors'\n\nimport type { NormalizedRivmuxPlayerOptions, PlayerErrorKind, RivmuxPlayerOptions } from '@rivmux/protocol'\n\nconst DEFAULT_RIVMUX_PLAYER_OPTIONS: NormalizedRivmuxPlayerOptions = {\n playback: {\n autoPlay: true,\n muted: false,\n },\n latency: {\n startupBuffer: 0.35,\n target: 1.2,\n max: 2.5,\n maxForwardBuffer: 4,\n backwardBuffer: 1.5,\n },\n network: {\n headers: {},\n credentials: 'same-origin',\n readIdleTimeoutMs: 10_000,\n retry: {\n maxAttempts: 3,\n backoffMs: 500,\n maxBackoffMs: 8_000,\n jitterRatio: 0.2,\n },\n },\n runtime: {},\n diagnostics: { statsIntervalMs: 1000 },\n}\n\nexport function normalizePlayerOptions(options: RivmuxPlayerOptions = {}): NormalizedRivmuxPlayerOptions {\n const normalizedOptions = {\n playback: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.playback,\n ...options.playback,\n },\n latency: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.latency,\n ...options.latency,\n },\n network: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network,\n ...options.network,\n headers: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network.headers,\n ...options.network?.headers,\n },\n retry: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.network.retry,\n ...options.network?.retry,\n },\n },\n runtime: normalizeRuntimeOptions(options),\n diagnostics: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.diagnostics,\n ...options.diagnostics,\n },\n }\n\n validateNormalizedOptions(normalizedOptions)\n return normalizedOptions\n}\n\nfunction validateNormalizedOptions(options: NormalizedRivmuxPlayerOptions): void {\n validateLatencyOptions(options)\n validateNetworkOptions(options)\n}\n\nfunction validateLatencyOptions(options: NormalizedRivmuxPlayerOptions): void {\n const latency = options.latency\n\n assertFiniteNonNegativeLatency(latency.startupBuffer, 'latency.startupBuffer')\n assertFinitePositiveLatency(latency.target, 'latency.target')\n assertFiniteNonNegativeLatency(latency.max, 'latency.max')\n assertFiniteNonNegativeLatency(latency.maxForwardBuffer, 'latency.maxForwardBuffer')\n assertFiniteNonNegativeLatency(latency.backwardBuffer, 'latency.backwardBuffer')\n\n if (latency.max < latency.target) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', 'latency.max must be greater than or equal to latency.target.')\n }\n\n if (latency.maxForwardBuffer < latency.target) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', 'latency.maxForwardBuffer must be greater than or equal to latency.target.')\n }\n}\n\nfunction validateNetworkOptions(options: NormalizedRivmuxPlayerOptions): void {\n const network = options.network\n const retry = network.retry\n\n assertPositiveInteger(network.readIdleTimeoutMs, 'network.readIdleTimeoutMs')\n assertPositiveInteger(retry.maxAttempts, 'network.retry.maxAttempts')\n assertNonNegativeInteger(retry.backoffMs, 'network.retry.backoffMs')\n assertNonNegativeInteger(retry.maxBackoffMs, 'network.retry.maxBackoffMs')\n\n if (retry.maxBackoffMs < retry.backoffMs) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', 'network.retry.maxBackoffMs must be greater than or equal to network.retry.backoffMs.')\n }\n\n if (!Number.isFinite(retry.jitterRatio) || retry.jitterRatio < 0 || retry.jitterRatio > 1) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', 'network.retry.jitterRatio must be a finite number between 0 and 1.')\n }\n}\n\nfunction normalizeRuntimeOptions(options: RivmuxPlayerOptions): NormalizedRivmuxPlayerOptions['runtime'] {\n return {\n ...(options.runtime?.workerUrl === undefined ? {} : { workerUrl: options.runtime.workerUrl }),\n ...(options.runtime?.wasmUrl === undefined ? {} : { wasmUrl: options.runtime.wasmUrl }),\n }\n}\n\nfunction assertFiniteNonNegativeLatency(value: number, field: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', `${field} must be a finite number greater than or equal to 0.`)\n }\n}\n\nfunction assertFinitePositiveLatency(value: number, field: string): void {\n if (!Number.isFinite(value) || value <= 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_LATENCY_OPTION', `${field} must be a finite number greater than 0.`)\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', `${field} must be an integer greater than or equal to 1.`)\n }\n}\n\nfunction assertNonNegativeInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throwOptionError('runtime', 'RIVMUX_INVALID_NETWORK_OPTION', `${field} must be an integer greater than or equal to 0.`)\n }\n}\n\nfunction throwOptionError(kind: PlayerErrorKind, code: string, message: string): never {\n throw playerErrorToException(createPlayerError(kind, code, message, true))\n}\n","import type { NormalizedPlaybackOptions, PlaybackControlAction, PlaybackControlResult, VideoElementState } from '@rivmux/protocol'\n\nexport class PlaybackController {\n private readonly options: NormalizedPlaybackOptions\n private video?: HTMLVideoElement\n private stateTimer?: ReturnType<typeof setInterval>\n\n constructor(options: NormalizedPlaybackOptions) {\n this.options = options\n }\n\n get attached(): boolean {\n return this.video !== undefined\n }\n\n isAttachedTo(video: HTMLVideoElement): boolean {\n return this.video === video\n }\n\n attach(video: HTMLVideoElement): void {\n this.video = video\n video.muted = this.options.muted\n video.autoplay = this.options.autoPlay\n }\n\n attachMediaSourceHandle(handle: MediaSourceHandle): boolean {\n if (this.video === undefined) {\n return false\n }\n\n Reflect.set(this.video, 'srcObject', handle)\n return true\n }\n\n startStateReporting(intervalMs: number, report: (state: VideoElementState) => void): void {\n this.stopStateReporting()\n const reportCurrentState = (): void => {\n const state = this.snapshot()\n if (state !== undefined) {\n report(state)\n }\n }\n\n reportCurrentState()\n this.stateTimer = setInterval(reportCurrentState, intervalMs)\n }\n\n stopStateReporting(): void {\n if (this.stateTimer !== undefined) {\n clearInterval(this.stateTimer)\n this.stateTimer = undefined\n }\n }\n\n async applyControl(action: PlaybackControlAction): Promise<PlaybackControlResult> {\n const video = this.video\n if (video === undefined) {\n return {\n type: action.type,\n accepted: false,\n error: {\n name: 'NotAttachedError',\n message: 'No video element is attached.',\n },\n }\n }\n\n try {\n switch (action.type) {\n case 'play':\n await video.play()\n break\n case 'set-playback-rate':\n video.playbackRate = action.playbackRate\n break\n case 'seek':\n video.currentTime = action.targetTime\n break\n }\n return { type: action.type, accepted: true }\n } catch (cause) {\n return {\n type: action.type,\n accepted: false,\n error: normalizePlaybackControlError(cause),\n }\n }\n }\n\n detachSource(): void {\n if (this.video === undefined) {\n return\n }\n\n this.video.pause()\n this.video.playbackRate = 1\n this.video.removeAttribute('src')\n this.video.srcObject = null\n this.video.load()\n }\n\n release(): void {\n this.stopStateReporting()\n this.detachSource()\n this.video = undefined\n }\n\n private snapshot(): VideoElementState | undefined {\n const video = this.video\n if (video === undefined) {\n return undefined\n }\n\n const droppedFrames = getDroppedFrames(video)\n return {\n currentTime: Number.isFinite(video.currentTime) ? video.currentTime : 0,\n readyState: video.readyState,\n playbackRate: video.playbackRate,\n paused: video.paused,\n ...(droppedFrames === undefined ? {} : { droppedFrames }),\n }\n }\n}\n\nfunction normalizePlaybackControlError(cause: unknown): { name: string; message: string } {\n if (cause instanceof Error) {\n return {\n name: safelyStringifyErrorField(() => cause.name, 'Error'),\n message: safelyStringifyErrorField(() => cause.message, 'Unknown playback control error.'),\n }\n }\n\n try {\n return {\n name: 'Error',\n message: String(cause),\n }\n } catch {\n return {\n name: 'Error',\n message: 'Unknown playback control error.',\n }\n }\n}\n\nfunction safelyStringifyErrorField(read: () => unknown, fallback: string): string {\n try {\n return String(read())\n } catch {\n return fallback\n }\n}\n\nfunction getDroppedFrames(video: HTMLVideoElement): number | undefined {\n const quality = video.getVideoPlaybackQuality?.()\n if (quality !== undefined && Number.isFinite(quality.droppedVideoFrames)) {\n return quality.droppedVideoFrames\n }\n\n const webkitDroppedFrameCount = (video as HTMLVideoElement & { webkitDroppedFrameCount?: number }).webkitDroppedFrameCount\n return Number.isFinite(webkitDroppedFrameCount) ? webkitDroppedFrameCount : undefined\n}\n","import { PlayerEventEmitter } from './events'\nimport { createPlayerError, playerErrorToException } from './errors'\nimport { detectMainThreadRuntime } from './feature-detect'\nimport { normalizePlayerOptions } from './options'\nimport { PlaybackController } from './playback-controller'\nimport { createRuntimeWorker, getBundledWasmUrl, WorkerClient } from '@rivmux/runtime-worker'\n\nimport type {\n NormalizedRivmuxPlayerOptions,\n PlaybackControlAction,\n PlayerError,\n PlayerEventListener,\n PlayerEventType,\n RivmuxPlayerOptions,\n WorkerMessage,\n} from '@rivmux/protocol'\nimport type { RuntimeWorkerFactory } from '@rivmux/runtime-worker'\n\ntype PlayerState = 'idle' | 'attached' | 'starting' | 'started' | 'stopping' | 'stopped' | 'fatal-error' | 'destroying' | 'destroyed'\n\ntype RivmuxPlayerInternals = {\n workerFactory?: RuntimeWorkerFactory\n detectRuntime?: () => PlayerError | undefined\n idFactory?: () => string\n}\n\n/**\n * Public browser player facade for one HTTP-FLV stream.\n *\n * Create one instance per stream URL, call `attach(video)` first, then\n * `start()`. Call `destroy()` when the instance is no longer needed.\n */\nexport class RivmuxPlayer {\n /** Original stream URL passed to the constructor. */\n readonly url: string\n\n /** @internal Fully normalized options with defaults applied. */\n readonly options: NormalizedRivmuxPlayerOptions\n private readonly id: string\n private readonly events = new PlayerEventEmitter()\n private readonly workerFactory: RuntimeWorkerFactory\n private readonly detectRuntime: () => PlayerError | undefined\n private readonly playback: PlaybackController\n private workerClient?: WorkerClient\n private state: PlayerState = 'idle'\n private lifecycleGeneration = 0\n private terminalError?: PlayerError\n private autoplayWarningEmitted = false\n private startPromise?: Promise<void>\n private stopPromise?: Promise<void>\n private destroyPromise?: Promise<void>\n\n /**\n * Creates a player instance for one stream URL.\n *\n * The instance does not start network loading until `start()` is called.\n */\n constructor(url: string, options?: RivmuxPlayerOptions)\n /** @internal Test-only dependency injection; omitted from generated declarations. */\n constructor(url: string, options?: RivmuxPlayerOptions, internals?: RivmuxPlayerInternals)\n constructor(url: string, options?: RivmuxPlayerOptions, internals?: RivmuxPlayerInternals) {\n internals ??= {}\n this.url = url\n this.options = normalizePlayerOptions(options)\n this.id = internals.idFactory?.() ?? createPlayerId()\n this.workerFactory = internals.workerFactory ?? createRuntimeWorker\n this.detectRuntime = internals.detectRuntime ?? detectMainThreadRuntime\n this.playback = new PlaybackController(this.options.playback)\n }\n\n /**\n * Attaches this player to a video element and prepares the worker/MSE pipe.\n *\n * Await this method before calling `start()`.\n */\n async attach(video: HTMLVideoElement): Promise<void> {\n this.assertOperational('attach')\n const lifecycleGeneration = this.lifecycleGeneration\n\n if (this.playback.attached && !this.playback.isAttachedTo(video)) {\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_ALREADY_ATTACHED', 'This player is already attached to a video element.', false))\n }\n\n const runtimeError = this.detectRuntime()\n if (runtimeError !== undefined) {\n this.events.emit('error', runtimeError)\n throw playerErrorToException(runtimeError)\n }\n\n this.playback.attach(video)\n this.ensureWorkerClient()\n await this.workerClient?.waitForMediaSourceHandle({ type: 'attach-media-source' })\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n this.state = 'attached'\n }\n\n /**\n * Waits until the worker has created the transmux core and scheduled stream\n * consumption for this playback session.\n *\n * This does not wait for media data, `canplay`, or `video.play()`. Requires a\n * successful `attach(video)` call first.\n */\n start(): Promise<void> {\n try {\n this.assertOperational('start')\n } catch (cause) {\n return Promise.reject(cause)\n }\n\n if (!this.playback.attached || this.workerClient === undefined) {\n return Promise.reject(\n playerErrorToException(createPlayerError('runtime', 'RIVMUX_START_REQUIRES_ATTACH', 'start() requires a previously attached video element.', false))\n )\n }\n\n if (this.state === 'started') {\n return Promise.resolve()\n }\n\n if (this.startPromise !== undefined) {\n return this.startPromise\n }\n\n const lifecycleGeneration = this.lifecycleGeneration\n const previousState = this.state\n this.state = 'starting'\n const starting = this.performStart(this.workerClient, lifecycleGeneration, previousState === 'stopped').finally(() => {\n if (this.startPromise === starting) {\n this.startPromise = undefined\n }\n })\n this.startPromise = starting\n return starting\n }\n\n private async performStart(workerClient: WorkerClient, lifecycleGeneration: number, restart: boolean): Promise<void> {\n try {\n if (restart) {\n await workerClient.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.assertStartOperationCurrent(lifecycleGeneration)\n }\n\n await workerClient.waitForStarted({ type: 'start' })\n this.assertStartOperationCurrent(lifecycleGeneration)\n } catch (cause) {\n if (this.isLifecycleOperationCurrent(lifecycleGeneration) && this.state === 'starting') {\n this.state = restart ? 'stopped' : 'attached'\n }\n throw toPublicStartError(cause)\n }\n\n this.state = 'started'\n this.startVideoStateReporting()\n }\n\n /**\n * Stops loading and detaches the current media source.\n *\n * The instance remains reusable; call `start()` again to restart the same\n * stream after the player has stopped.\n */\n stop(): Promise<void> {\n if (this.state === 'destroyed' || this.state === 'destroying') {\n return Promise.resolve()\n }\n\n if (this.state === 'fatal-error') {\n return Promise.resolve()\n }\n\n if (this.stopPromise !== undefined) {\n return this.stopPromise\n }\n\n this.lifecycleGeneration += 1\n this.state = 'stopping'\n const lifecycleGeneration = this.lifecycleGeneration\n const stopping = this.performStop(lifecycleGeneration).finally(() => {\n if (this.stopPromise === stopping) {\n this.stopPromise = undefined\n }\n })\n this.stopPromise = stopping\n return stopping\n }\n\n private async performStop(lifecycleGeneration: number): Promise<void> {\n const workerClient = this.workerClient\n if (workerClient === undefined) {\n if (this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n this.autoplayWarningEmitted = false\n this.state = 'stopped'\n this.events.emit('stopped', undefined)\n }\n return\n }\n\n this.playback.stopStateReporting()\n try {\n await workerClient.waitForStopped({ type: 'stop' })\n } catch (error) {\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n throw error\n }\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration)) {\n return\n }\n this.playback.detachSource()\n this.autoplayWarningEmitted = false\n this.state = 'stopped'\n }\n\n /**\n * Releases worker resources, timers, listeners, and the attached video source.\n *\n * The instance is terminal after destroy and cannot be attached or started\n * again.\n */\n async destroy(): Promise<void> {\n if (this.state === 'destroyed') {\n return\n }\n\n if (this.destroyPromise !== undefined) {\n return this.destroyPromise\n }\n\n this.lifecycleGeneration += 1\n this.state = 'destroying'\n this.destroyPromise = this.performDestroy()\n return this.destroyPromise\n }\n\n private async performDestroy(): Promise<void> {\n const workerClient = this.workerClient\n this.workerClient = undefined\n this.playback.stopStateReporting()\n let destroyError: unknown\n\n if (workerClient !== undefined) {\n try {\n await workerClient.waitForDestroyed({ type: 'destroy' })\n } catch (error) {\n destroyError = error\n } finally {\n workerClient.dispose()\n }\n } else {\n this.events.emit('destroyed', undefined)\n }\n\n this.playback.release()\n\n this.state = 'destroyed'\n this.events.clear()\n\n if (destroyError !== undefined) {\n throw destroyError\n }\n }\n\n /** Registers an event listener for a typed player event. */\n on<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.events.on(type, listener)\n }\n\n /** Removes a previously registered event listener. */\n off<T extends PlayerEventType>(type: T, listener: PlayerEventListener<T>): void {\n this.events.off(type, listener)\n }\n\n private ensureWorkerClient(): void {\n if (this.workerClient !== undefined) {\n return\n }\n\n const worker = this.workerFactory(this.options)\n this.workerClient = new WorkerClient(worker, {\n onMessage: (message) => this.handleWorkerMessage(message),\n onError: (error) => {\n this.enterFatalErrorState(error)\n this.events.emit('error', error)\n },\n })\n this.workerClient.post({ type: 'init', id: this.id, url: this.url, options: withBundledWasmUrl(this.options) })\n }\n\n private handleWorkerMessage(message: WorkerMessage): void {\n if (this.state === 'destroyed') {\n return\n }\n\n if ((this.state === 'destroying' && message.type !== 'destroyed') || this.state === 'fatal-error') {\n return\n }\n\n switch (message.type) {\n case 'ready':\n this.events.emit('initialized', undefined)\n return\n case 'media-source-handle':\n this.attachMediaSourceHandle(message.handle)\n return\n case 'started':\n return\n case 'media-info':\n this.events.emit('mediaInfo', message.mediaInfo)\n return\n case 'stats':\n this.events.emit('stats', message.stats)\n return\n case 'warning':\n this.events.emit('warning', message.warning)\n return\n case 'reconnecting':\n this.events.emit('reconnecting', message.info)\n return\n case 'recovered':\n this.events.emit('recovered', message.info)\n return\n case 'error':\n if (message.error.terminal) {\n this.enterFatalErrorState(message.error)\n }\n this.events.emit('error', message.error)\n return\n case 'playback-control':\n void this.applyPlaybackControl(message.action)\n return\n case 'stopped':\n this.events.emit('stopped', undefined)\n return\n case 'destroyed':\n this.events.emit('destroyed', undefined)\n return\n }\n }\n\n private attachMediaSourceHandle(handle: MediaSourceHandle): void {\n if (this.state === 'started') {\n this.autoplayWarningEmitted = false\n this.playback.detachSource()\n }\n if (!this.playback.attachMediaSourceHandle(handle)) {\n const error = createPlayerError(\n 'runtime',\n 'RIVMUX_ATTACH_HANDLE_WITHOUT_VIDEO',\n 'Worker returned a MediaSourceHandle before a video element was attached.',\n true\n )\n this.events.emit('error', error)\n throw playerErrorToException(error)\n }\n }\n\n private startVideoStateReporting(): void {\n const intervalMs = Math.max(100, Math.min(this.options.diagnostics.statsIntervalMs, 250))\n const workerClient = this.workerClient\n if (workerClient === undefined) {\n return\n }\n\n this.playback.startStateReporting(intervalMs, (state) => {\n if (this.state === 'started' && this.workerClient === workerClient) {\n workerClient.post({ type: 'video-state', state })\n }\n })\n }\n\n private async applyPlaybackControl(action: PlaybackControlAction): Promise<void> {\n const workerClient = this.workerClient\n const lifecycleGeneration = this.lifecycleGeneration\n if (!this.playback.attached || workerClient === undefined || this.state !== 'started') {\n return\n }\n\n const result = await this.playback.applyControl(action)\n if (!this.isLifecycleOperationCurrent(lifecycleGeneration) || this.state !== 'started' || this.workerClient !== workerClient) {\n return\n }\n workerClient.post({ type: 'playback-control-result', result })\n if (\n action.type === 'play' &&\n action.reason === 'startup-buffer-ready' &&\n this.options.playback.autoPlay &&\n !result.accepted &&\n !this.autoplayWarningEmitted\n ) {\n this.autoplayWarningEmitted = true\n this.events.emit('warning', {\n code: 'RIVMUX_AUTOPLAY_REJECTED',\n message: '浏览器拒绝了自动播放;请在用户手势处理器中调用 video.play() 恢复播放。',\n cause: result.error,\n })\n }\n }\n\n private enterFatalErrorState(error: PlayerError): void {\n if (this.state === 'destroyed' || this.state === 'destroying' || this.state === 'fatal-error') {\n return\n }\n\n this.lifecycleGeneration += 1\n this.terminalError = error\n this.state = 'fatal-error'\n this.playback.release()\n }\n\n private assertOperational(method: string): void {\n if (this.state === 'fatal-error' && this.terminalError !== undefined) {\n throw playerErrorToException(this.terminalError)\n }\n\n if (this.state === 'stopping') {\n throw playerErrorToException(\n createPlayerError('runtime', 'RIVMUX_PLAYER_STOPPING', `RivmuxPlayer.${method}() cannot be called while stop() is pending.`, false)\n )\n }\n\n if (this.state !== 'destroyed' && this.state !== 'destroying') {\n return\n }\n\n const code = this.state === 'destroying' ? 'RIVMUX_PLAYER_DESTROYING' : 'RIVMUX_PLAYER_DESTROYED'\n throw playerErrorToException(createPlayerError('runtime', code, `RivmuxPlayer.${method}() cannot be called after destroy() begins.`, true))\n }\n\n private isLifecycleOperationCurrent(generation: number): boolean {\n return generation === this.lifecycleGeneration && this.state !== 'fatal-error' && this.state !== 'destroying' && this.state !== 'destroyed'\n }\n\n private assertStartOperationCurrent(generation: number): void {\n if (this.isLifecycleOperationCurrent(generation) && this.state === 'starting') {\n return\n }\n\n const action = this.state === 'destroying' || this.state === 'destroyed' ? 'destroy' : 'stop'\n throw createPlayerError('runtime', 'RIVMUX_START_CANCELLED', `Start was cancelled by ${action}.`, false)\n }\n}\n\nfunction toPublicStartError(cause: unknown): Error {\n return isPlayerError(cause) ? playerErrorToException(cause) : cause instanceof Error ? cause : new Error(String(cause))\n}\n\nfunction isPlayerError(value: unknown): value is PlayerError {\n if (typeof value !== 'object' || value === null) {\n return false\n }\n\n const candidate = value as Partial<PlayerError>\n return (\n typeof candidate.kind === 'string' && typeof candidate.code === 'string' && typeof candidate.message === 'string' && typeof candidate.terminal === 'boolean'\n )\n}\n\nfunction createPlayerId(): string {\n const random = globalThis.crypto?.randomUUID?.()\n return random ?? `rivmux-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n}\n\nfunction withBundledWasmUrl(options: NormalizedRivmuxPlayerOptions): NormalizedRivmuxPlayerOptions {\n if (options.runtime.wasmUrl !== undefined) {\n return options\n }\n\n return {\n ...options,\n runtime: {\n ...options.runtime,\n wasmUrl: getBundledWasmUrl(),\n },\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rivmux",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.1",
4
4
  "description": "Public player package for Rivmux.",
5
5
  "homepage": "https://www.npmjs.com/package/rivmux",
6
6
  "bugs": {
@@ -30,13 +30,13 @@
30
30
  "module": "./dist/index.js",
31
31
  "types": "./dist/index.d.ts",
32
32
  "dependencies": {
33
- "@rivmux/runtime-worker": "1.0.0-rc.1",
34
- "@rivmux/protocol": "1.0.0-rc.1"
33
+ "@rivmux/protocol": "1.0.1",
34
+ "@rivmux/runtime-worker": "1.0.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "tsdown": "^0.23.0",
38
38
  "typescript": "~6.0.3",
39
- "vitest": "^4.1.11"
39
+ "vitest": "^5.0.1"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsdown",