rivmux 0.2.0 → 0.4.0
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 +31 -10
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -16
- package/dist/index.js.map +1 -1
- package/package.json +15 -6
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Public browser player facade for Rivmux low-latency live playback.
|
|
|
6
6
|
audio/video into fragmented MP4, and attaches the resulting media stream to a
|
|
7
7
|
browser `<video>` element.
|
|
8
8
|
|
|
9
|
+
M1 supports only the dedicated-worker MSE runtime path. Main-thread MSE is not available.
|
|
10
|
+
|
|
9
11
|
## Install
|
|
10
12
|
|
|
11
13
|
```sh
|
|
@@ -98,10 +100,6 @@ const player = new RivmuxPlayer('https://example.com/live.flv', {
|
|
|
98
100
|
backoffMs: 500,
|
|
99
101
|
},
|
|
100
102
|
},
|
|
101
|
-
runtime: {
|
|
102
|
-
workerUrl: '/assets/rivmux-runtime-worker.js',
|
|
103
|
-
wasmUrl: '/assets/rivmux-transmux-core.wasm',
|
|
104
|
-
},
|
|
105
103
|
diagnostics: {
|
|
106
104
|
statsIntervalMs: 1000,
|
|
107
105
|
debug: false,
|
|
@@ -139,12 +137,35 @@ Values are in seconds.
|
|
|
139
137
|
|
|
140
138
|
### `runtime`
|
|
141
139
|
|
|
142
|
-
| Option | Default | Description
|
|
143
|
-
| ----------------- | ------------------- |
|
|
144
|
-
| `preferWorkerMse` | `true` |
|
|
145
|
-
| `workerUrl` | bundled worker URL |
|
|
146
|
-
| `wasmUrl` | bundled WASM module |
|
|
147
|
-
|
|
140
|
+
| Option | Default | Description |
|
|
141
|
+
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| `preferWorkerMse` | `true` | Must remain `true` in M1. Main-thread MSE fallback is not implemented. |
|
|
143
|
+
| `workerUrl` | bundled worker URL | Advanced override for the packaged Dedicated Worker script URL. |
|
|
144
|
+
| `wasmUrl` | bundled WASM module | Advanced override for the packaged WASM binary URL. It must match the Worker release that contains the wasm-bindgen glue. |
|
|
145
|
+
|
|
146
|
+
### Advanced Asset Deployment
|
|
147
|
+
|
|
148
|
+
Most applications should omit `runtime.workerUrl` and `runtime.wasmUrl`. The
|
|
149
|
+
default package assets are tracked by Vite and emitted with the application
|
|
150
|
+
build.
|
|
151
|
+
|
|
152
|
+
Set these options only when your deployment serves Rivmux assets from a fixed
|
|
153
|
+
public path or CDN:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const player = new RivmuxPlayer('https://example.com/live.flv', {
|
|
157
|
+
runtime: {
|
|
158
|
+
workerUrl: 'https://cdn.example.com/rivmux/0.4.0/rivmux-runtime-worker.js',
|
|
159
|
+
wasmUrl: 'https://cdn.example.com/rivmux/0.4.0/rivmux-transmux-core.wasm',
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`workerUrl` does not derive `wasmUrl`; when you override both assets, publish
|
|
165
|
+
them as a matching release pair. Treat both URLs as trusted executable asset
|
|
166
|
+
locations and never construct them from untrusted input. The host application
|
|
167
|
+
is responsible for compatible CSP and CORS policies, serving the WASM asset as
|
|
168
|
+
`application/wasm`, and cache-busting the Worker/WASM pair together.
|
|
148
169
|
|
|
149
170
|
### `diagnostics`
|
|
150
171
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { RuntimeWorkerFactory } from "@rivmux/runtime-worker";
|
|
2
2
|
import { DiagnosticsOptions, LatencyOptions, MediaInfo, NetworkOptions, NormalizedRivmuxPlayerOptions, NormalizedRivmuxPlayerOptions as NormalizedRivmuxPlayerOptions$1, PlaybackOptions, PlayerError, PlayerError as PlayerError$1, PlayerErrorKind, PlayerErrorKind as PlayerErrorKind$1, PlayerEventListener, PlayerEventListener as PlayerEventListener$1, PlayerEventMap, PlayerEventType, PlayerEventType as PlayerEventType$1, PlayerStats, PlayerWarning, RivmuxPlayerOptions, RivmuxPlayerOptions as RivmuxPlayerOptions$1, RuntimeOptions } from "@rivmux/protocol";
|
|
3
|
-
|
|
4
3
|
//#region src/player.d.ts
|
|
5
4
|
type RivmuxPlayerInternals = {
|
|
6
5
|
workerFactory?: RuntimeWorkerFactory;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/player.ts","../src/options.ts","../src/errors.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/player.ts","../src/options.ts","../src/errors.ts"],"mappings":";;;KAmBY;EACV,gBAAgB;EAChB,sBAAsB;EACtB;;;;;;;;cASW;;WAEF;;WAGA,SAAS;mBACD;mBACA;mBACA;mBACA;UACT;UACA;UACA;UACA;;;;;;cAOI,aAAa,UAAU,uBAAqB,YAAW;;;;;;EAa7D,OAAO,OAAO,mBAAmB;;;;;;EAyBjC,SAAS;;;;;;;EA2BT,QAAQ;;;;;;;EAuBR,WAAW;;EA6BjB,GAAG,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;;EAKrE,IAAI,UAAU,mBAAiB,MAAM,GAAG,UAAU,sBAAoB;UAI9D;UAaA;UAoCA;UAeA;UAKA;UAUA;UASA;UAoBM;UAkCN;UAYA;;;;cCvUG,+BAA+B;iBA6B5B,uBAAuB,UAAS,wBAA2B;;;iBC/B3D,kBAAkB,MAAM,mBAAiB,cAAc,iBAAiB,mBAAmB,kBAAkB"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { WorkerClient, createRuntimeWorker } from "@rivmux/runtime-worker";
|
|
1
|
+
import { WorkerClient, createRuntimeWorker, getBundledWasmUrl } from "@rivmux/runtime-worker";
|
|
2
2
|
//#region src/events.ts
|
|
3
3
|
var PlayerEventEmitter = class {
|
|
4
|
-
|
|
5
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
6
|
-
}
|
|
4
|
+
listeners = /* @__PURE__ */ new Map();
|
|
7
5
|
on(type, listener) {
|
|
8
6
|
const typedListener = listener;
|
|
9
7
|
const listeners = this.listeners.get(type);
|
|
@@ -49,8 +47,15 @@ function playerErrorToException(error) {
|
|
|
49
47
|
}
|
|
50
48
|
//#endregion
|
|
51
49
|
//#region src/feature-detect.ts
|
|
52
|
-
const
|
|
53
|
-
|
|
50
|
+
const REQUIRED_MSE_MIME_TYPES = [{
|
|
51
|
+
mediaType: "video",
|
|
52
|
+
mimeType: createMp4Mime("video", "avc1.42C01E"),
|
|
53
|
+
unsupportedCode: "RIVMUX_UNSUPPORTED_MSE_VIDEO_MIME"
|
|
54
|
+
}, {
|
|
55
|
+
mediaType: "audio",
|
|
56
|
+
mimeType: createMp4Mime("audio", "mp4a.40.2"),
|
|
57
|
+
unsupportedCode: "RIVMUX_UNSUPPORTED_MSE_AUDIO_MIME"
|
|
58
|
+
}];
|
|
54
59
|
function detectMainThreadRuntime() {
|
|
55
60
|
if (typeof Worker === "undefined") return createPlayerError("unsupported", "RIVMUX_UNSUPPORTED_WORKER", "Dedicated Worker is not available in this runtime.", true);
|
|
56
61
|
if (typeof fetch !== "function") return createPlayerError("unsupported", "RIVMUX_UNSUPPORTED_FETCH", "Fetch is not available in this runtime.", true);
|
|
@@ -59,8 +64,10 @@ function detectMainThreadRuntime() {
|
|
|
59
64
|
if (typeof MediaSource === "undefined") return createPlayerError("unsupported", "RIVMUX_UNSUPPORTED_MSE", "MediaSource is not available in this runtime.", true);
|
|
60
65
|
if (MediaSource.canConstructInDedicatedWorker !== true) return createPlayerError("unsupported", "RIVMUX_UNSUPPORTED_WORKER_MSE", "MediaSource cannot be constructed in a dedicated worker in this runtime.", true);
|
|
61
66
|
if (typeof MediaSource.isTypeSupported !== "function") return createPlayerError("unsupported", "RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK", "MediaSource.isTypeSupported is not available in this runtime.", true);
|
|
62
|
-
if (!MediaSource.isTypeSupported(
|
|
63
|
-
|
|
67
|
+
for (const requirement of REQUIRED_MSE_MIME_TYPES) if (!MediaSource.isTypeSupported(requirement.mimeType)) return createPlayerError("unsupported", requirement.unsupportedCode, `MSE does not support ${requirement.mimeType}.`, true);
|
|
68
|
+
}
|
|
69
|
+
function createMp4Mime(mediaType, codec) {
|
|
70
|
+
return `${mediaType}/mp4; codecs="${codec}"`;
|
|
64
71
|
}
|
|
65
72
|
//#endregion
|
|
66
73
|
//#region src/options.ts
|
|
@@ -112,10 +119,7 @@ function normalizePlayerOptions(options = {}) {
|
|
|
112
119
|
...options.network?.retry
|
|
113
120
|
}
|
|
114
121
|
},
|
|
115
|
-
runtime:
|
|
116
|
-
...DEFAULT_RIVMUX_PLAYER_OPTIONS.runtime,
|
|
117
|
-
...options.runtime
|
|
118
|
-
},
|
|
122
|
+
runtime: normalizeRuntimeOptions(options),
|
|
119
123
|
diagnostics: {
|
|
120
124
|
...DEFAULT_RIVMUX_PLAYER_OPTIONS.diagnostics,
|
|
121
125
|
...options.diagnostics
|
|
@@ -140,7 +144,13 @@ function validateLatencyOptions(options) {
|
|
|
140
144
|
}
|
|
141
145
|
function validateRuntimeOptions(options) {
|
|
142
146
|
if (options.runtime.preferWorkerMse !== true) throwOptionError("unsupported", "RIVMUX_UNSUPPORTED_MAIN_THREAD_MSE_FALLBACK", "Main-thread MSE fallback is not implemented; runtime.preferWorkerMse must remain true.");
|
|
143
|
-
|
|
147
|
+
}
|
|
148
|
+
function normalizeRuntimeOptions(options) {
|
|
149
|
+
return {
|
|
150
|
+
preferWorkerMse: options.runtime?.preferWorkerMse ?? DEFAULT_RIVMUX_PLAYER_OPTIONS.runtime.preferWorkerMse,
|
|
151
|
+
...options.runtime?.workerUrl === void 0 ? {} : { workerUrl: options.runtime.workerUrl },
|
|
152
|
+
...options.runtime?.wasmUrl === void 0 ? {} : { wasmUrl: options.runtime.wasmUrl }
|
|
153
|
+
};
|
|
144
154
|
}
|
|
145
155
|
function assertFiniteNonNegativeLatency(value, field) {
|
|
146
156
|
if (!Number.isFinite(value) || value < 0) throwOptionError("runtime", "RIVMUX_INVALID_LATENCY_OPTION", `${field} must be a finite number greater than or equal to 0.`);
|
|
@@ -160,14 +170,24 @@ function throwOptionError(kind, code, message) {
|
|
|
160
170
|
* `start()`. Call `destroy()` when the instance is no longer needed.
|
|
161
171
|
*/
|
|
162
172
|
var RivmuxPlayer = class {
|
|
173
|
+
/** Original stream URL passed to the constructor. */
|
|
174
|
+
url;
|
|
175
|
+
/** Fully normalized options with defaults applied. */
|
|
176
|
+
options;
|
|
177
|
+
id;
|
|
178
|
+
events = new PlayerEventEmitter();
|
|
179
|
+
workerFactory;
|
|
180
|
+
detectRuntime;
|
|
181
|
+
workerClient;
|
|
182
|
+
video;
|
|
183
|
+
videoStateTimer;
|
|
184
|
+
state = "idle";
|
|
163
185
|
/**
|
|
164
186
|
* Creates a player instance for one stream URL.
|
|
165
187
|
*
|
|
166
188
|
* The instance does not start network loading until `start()` is called.
|
|
167
189
|
*/
|
|
168
190
|
constructor(url, options, internals = {}) {
|
|
169
|
-
this.events = new PlayerEventEmitter();
|
|
170
|
-
this.state = "idle";
|
|
171
191
|
this.url = url;
|
|
172
192
|
this.options = normalizePlayerOptions(options);
|
|
173
193
|
this.id = internals.idFactory?.() ?? createPlayerId();
|
|
@@ -271,7 +291,7 @@ var RivmuxPlayer = class {
|
|
|
271
291
|
type: "init",
|
|
272
292
|
id: this.id,
|
|
273
293
|
url: this.url,
|
|
274
|
-
options: this.options
|
|
294
|
+
options: withBundledWasmUrl(this.options)
|
|
275
295
|
});
|
|
276
296
|
}
|
|
277
297
|
handleWorkerMessage(message) {
|
|
@@ -404,6 +424,16 @@ function getDroppedFrames(video) {
|
|
|
404
424
|
const webkitDroppedFrameCount = video.webkitDroppedFrameCount;
|
|
405
425
|
return Number.isFinite(webkitDroppedFrameCount) ? webkitDroppedFrameCount : void 0;
|
|
406
426
|
}
|
|
427
|
+
function withBundledWasmUrl(options) {
|
|
428
|
+
if (options.runtime.wasmUrl !== void 0) return options;
|
|
429
|
+
return {
|
|
430
|
+
...options,
|
|
431
|
+
runtime: {
|
|
432
|
+
...options.runtime,
|
|
433
|
+
wasmUrl: getBundledWasmUrl()
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
407
437
|
//#endregion
|
|
408
438
|
export { DEFAULT_RIVMUX_PLAYER_OPTIONS, RivmuxPlayer, createPlayerError, normalizePlayerOptions };
|
|
409
439
|
|
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/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 listener(payload)\n }\n }\n\n clear(): void {\n this.listeners.clear()\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 }\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 = toError(error.cause ?? error.message)\n exception.name = error.code\n return exception\n}\n","import { createPlayerError } from './errors'\n\nimport type { PlayerError } from '@rivmux/protocol'\n\nexport const M1_VIDEO_MIME = 'video/mp4; codecs=\"avc1.42C01E\"'\nexport const M1_AUDIO_MIME = 'audio/mp4; codecs=\"mp4a.40.2\"'\n\nexport function detectMainThreadRuntime(): PlayerError | undefined {\n if (typeof Worker === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER', 'Dedicated Worker is not available in this runtime.', true)\n }\n\n if (typeof fetch !== 'function') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_FETCH', 'Fetch is not available in this runtime.', true)\n }\n\n if (typeof ReadableStream === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_READABLE_STREAM', 'ReadableStream is not available in this runtime.', true)\n }\n\n if (typeof WebAssembly === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WASM', 'WebAssembly is not available in this runtime.', true)\n }\n\n if (typeof MediaSource === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE', 'MediaSource is not available in this runtime.', true)\n }\n\n if (MediaSource.canConstructInDedicatedWorker !== true) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER_MSE', 'MediaSource cannot be constructed in a dedicated worker in this runtime.', true)\n }\n\n if (typeof MediaSource.isTypeSupported !== 'function') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK', 'MediaSource.isTypeSupported is not available in this runtime.', true)\n }\n\n if (!MediaSource.isTypeSupported(M1_VIDEO_MIME)) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_M1_VIDEO_MIME', `MSE does not support ${M1_VIDEO_MIME}.`, true)\n }\n\n if (!MediaSource.isTypeSupported(M1_AUDIO_MIME)) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_M1_AUDIO_MIME', `MSE does not support ${M1_AUDIO_MIME}.`, true)\n }\n\n return undefined\n}\n","import { createPlayerError, playerErrorToException } from './errors'\n\nimport type { NormalizedRivmuxPlayerOptions, PlayerErrorKind, RivmuxPlayerOptions } from '@rivmux/protocol'\n\nexport const 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 retry: {\n maxAttempts: 3,\n backoffMs: 500,\n },\n },\n runtime: {\n preferWorkerMse: true,\n },\n diagnostics: {\n statsIntervalMs: 1000,\n debug: false,\n },\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: {\n ...DEFAULT_RIVMUX_PLAYER_OPTIONS.runtime,\n ...options.runtime,\n },\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 validateRuntimeOptions(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 validateRuntimeOptions(options: NormalizedRivmuxPlayerOptions): void {\n if (options.runtime.preferWorkerMse !== true) {\n throwOptionError(\n 'unsupported',\n 'RIVMUX_UNSUPPORTED_MAIN_THREAD_MSE_FALLBACK',\n 'Main-thread MSE fallback is not implemented; runtime.preferWorkerMse must remain true.'\n )\n }\n\n if (options.runtime.wasmModule !== undefined) {\n throwOptionError('unsupported', 'RIVMUX_UNSUPPORTED_WASM_MODULE_OPTION', 'runtime.wasmModule is reserved and is not implemented by the default runtime.')\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 throwOptionError(kind: PlayerErrorKind, code: string, message: string): never {\n throw playerErrorToException(createPlayerError(kind, code, message, true))\n}\n","import { PlayerEventEmitter } from './events'\nimport { createPlayerError, playerErrorToException } from './errors'\nimport { detectMainThreadRuntime } from './feature-detect'\nimport { normalizePlayerOptions } from './options'\nimport { createRuntimeWorker, 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' | 'started' | 'stopped' | 'destroyed'\n\nexport type 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 /** 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 workerClient?: WorkerClient\n private video?: HTMLVideoElement\n private videoStateTimer?: ReturnType<typeof setInterval>\n private state: PlayerState = 'idle'\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, internals: RivmuxPlayerInternals = {}) {\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 }\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.assertNotDestroyed('attach')\n\n if (this.video !== undefined && this.video !== 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.video = video\n this.applyPlaybackOptions(video)\n this.ensureWorkerClient()\n await this.workerClient?.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.state = 'attached'\n }\n\n /**\n * Starts loading, transmuxing, buffering, and playback control.\n *\n * Requires a successful `attach(video)` call first.\n */\n async start(): Promise<void> {\n this.assertNotDestroyed('start')\n\n if (this.video === undefined || this.workerClient === undefined) {\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_START_REQUIRES_ATTACH', 'start() requires a previously attached video element.', false))\n }\n\n if (this.state === 'started') {\n return\n }\n\n if (this.state === 'stopped') {\n await this.workerClient.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.state = 'attached'\n }\n\n this.workerClient.post({ type: 'start' })\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 async stop(): Promise<void> {\n if (this.state === 'destroyed') {\n return\n }\n\n if (this.workerClient === undefined) {\n this.state = 'stopped'\n this.events.emit('stopped', undefined)\n return\n }\n\n this.stopVideoStateReporting()\n await this.workerClient.waitForStopped({ type: 'stop' })\n this.detachVideoSource()\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 const workerClient = this.workerClient\n this.workerClient = undefined\n this.stopVideoStateReporting()\n\n if (workerClient !== undefined) {\n try {\n await workerClient.waitForDestroyed({ type: 'destroy' })\n } finally {\n workerClient.dispose()\n }\n } else {\n this.events.emit('destroyed', undefined)\n }\n\n if (this.video !== undefined) {\n this.detachVideoSource()\n this.video = undefined\n }\n\n this.state = 'destroyed'\n this.events.clear()\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) => this.events.emit('error', error),\n })\n this.workerClient.post({ type: 'init', id: this.id, url: this.url, options: this.options })\n }\n\n private handleWorkerMessage(message: WorkerMessage): void {\n if (this.state === 'destroyed') {\n return\n }\n\n switch (message.type) {\n case 'ready':\n this.events.emit('ready', undefined)\n return\n case 'media-source-handle':\n this.attachMediaSourceHandle(message.handle)\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 'error':\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.video === undefined) {\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 Reflect.set(this.video, 'srcObject', handle)\n }\n\n private applyPlaybackOptions(video: HTMLVideoElement): void {\n video.muted = this.options.playback.muted\n video.autoplay = this.options.playback.autoPlay\n }\n\n private startVideoStateReporting(): void {\n this.stopVideoStateReporting()\n this.postVideoState()\n\n const intervalMs = Math.max(100, Math.min(this.options.diagnostics.statsIntervalMs, 250))\n this.videoStateTimer = setInterval(() => {\n this.postVideoState()\n }, intervalMs)\n }\n\n private stopVideoStateReporting(): void {\n if (this.videoStateTimer === undefined) {\n return\n }\n\n clearInterval(this.videoStateTimer)\n this.videoStateTimer = undefined\n }\n\n private postVideoState(): void {\n const video = this.video\n const workerClient = this.workerClient\n if (video === undefined || workerClient === undefined || this.state !== 'started') {\n return\n }\n\n const droppedFrames = getDroppedFrames(video)\n workerClient.post({\n type: 'video-state',\n state: {\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\n private async applyPlaybackControl(action: PlaybackControlAction): Promise<void> {\n const video = this.video\n const workerClient = this.workerClient\n if (video === undefined || workerClient === undefined || this.state !== 'started') {\n return\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\n workerClient.post({ type: 'playback-control-result', result: { type: action.type, accepted: true } })\n this.postVideoState()\n } catch (cause) {\n workerClient.post({\n type: 'playback-control-result',\n result: {\n type: action.type,\n accepted: false,\n message: cause instanceof Error ? cause.message : String(cause),\n },\n })\n }\n }\n\n private detachVideoSource(): 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 private assertNotDestroyed(method: string): void {\n if (this.state !== 'destroyed') {\n return\n }\n\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_PLAYER_DESTROYED', `RivmuxPlayer.${method}() cannot be called after destroy().`, true))\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 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"],"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/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 listener(payload)\n }\n }\n\n clear(): void {\n this.listeners.clear()\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 }\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 = toError(error.cause ?? error.message)\n exception.name = error.code\n return exception\n}\n","import { createPlayerError } from './errors'\n\nimport type { PlayerError } from '@rivmux/protocol'\n\nexport type RequiredMseMimeType = {\n readonly mediaType: 'video' | 'audio'\n readonly mimeType: string\n readonly unsupportedCode: string\n}\n\nexport const REQUIRED_MSE_MIME_TYPES = [\n {\n mediaType: 'video',\n mimeType: createMp4Mime('video', 'avc1.42C01E'),\n unsupportedCode: 'RIVMUX_UNSUPPORTED_MSE_VIDEO_MIME',\n },\n {\n mediaType: 'audio',\n mimeType: createMp4Mime('audio', 'mp4a.40.2'),\n unsupportedCode: 'RIVMUX_UNSUPPORTED_MSE_AUDIO_MIME',\n },\n] as const satisfies readonly RequiredMseMimeType[]\n\nexport function detectMainThreadRuntime(): PlayerError | undefined {\n if (typeof Worker === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER', 'Dedicated Worker is not available in this runtime.', true)\n }\n\n if (typeof fetch !== 'function') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_FETCH', 'Fetch is not available in this runtime.', true)\n }\n\n if (typeof ReadableStream === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_READABLE_STREAM', 'ReadableStream is not available in this runtime.', true)\n }\n\n if (typeof WebAssembly === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WASM', 'WebAssembly is not available in this runtime.', true)\n }\n\n if (typeof MediaSource === 'undefined') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE', 'MediaSource is not available in this runtime.', true)\n }\n\n if (MediaSource.canConstructInDedicatedWorker !== true) {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_WORKER_MSE', 'MediaSource cannot be constructed in a dedicated worker in this runtime.', true)\n }\n\n if (typeof MediaSource.isTypeSupported !== 'function') {\n return createPlayerError('unsupported', 'RIVMUX_UNSUPPORTED_MSE_TYPE_CHECK', 'MediaSource.isTypeSupported is not available in this runtime.', true)\n }\n\n for (const requirement of REQUIRED_MSE_MIME_TYPES) {\n if (!MediaSource.isTypeSupported(requirement.mimeType)) {\n return createPlayerError('unsupported', requirement.unsupportedCode, `MSE does not support ${requirement.mimeType}.`, true)\n }\n }\n\n return undefined\n}\n\nfunction createMp4Mime(mediaType: RequiredMseMimeType['mediaType'], codec: string): string {\n return `${mediaType}/mp4; codecs=\"${codec}\"`\n}\n","import { createPlayerError, playerErrorToException } from './errors'\n\nimport type { NormalizedRivmuxPlayerOptions, PlayerErrorKind, RivmuxPlayerOptions } from '@rivmux/protocol'\n\nexport const 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 retry: {\n maxAttempts: 3,\n backoffMs: 500,\n },\n },\n runtime: {\n preferWorkerMse: true,\n },\n diagnostics: {\n statsIntervalMs: 1000,\n debug: false,\n },\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 validateRuntimeOptions(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 validateRuntimeOptions(options: NormalizedRivmuxPlayerOptions): void {\n if (options.runtime.preferWorkerMse !== true) {\n throwOptionError(\n 'unsupported',\n 'RIVMUX_UNSUPPORTED_MAIN_THREAD_MSE_FALLBACK',\n 'Main-thread MSE fallback is not implemented; runtime.preferWorkerMse must remain true.'\n )\n }\n}\n\nfunction normalizeRuntimeOptions(options: RivmuxPlayerOptions): NormalizedRivmuxPlayerOptions['runtime'] {\n return {\n preferWorkerMse: options.runtime?.preferWorkerMse ?? DEFAULT_RIVMUX_PLAYER_OPTIONS.runtime.preferWorkerMse,\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 throwOptionError(kind: PlayerErrorKind, code: string, message: string): never {\n throw playerErrorToException(createPlayerError(kind, code, message, true))\n}\n","import { PlayerEventEmitter } from './events'\nimport { createPlayerError, playerErrorToException } from './errors'\nimport { detectMainThreadRuntime } from './feature-detect'\nimport { normalizePlayerOptions } from './options'\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' | 'started' | 'stopped' | 'destroyed'\n\nexport type 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 /** 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 workerClient?: WorkerClient\n private video?: HTMLVideoElement\n private videoStateTimer?: ReturnType<typeof setInterval>\n private state: PlayerState = 'idle'\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, internals: RivmuxPlayerInternals = {}) {\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 }\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.assertNotDestroyed('attach')\n\n if (this.video !== undefined && this.video !== 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.video = video\n this.applyPlaybackOptions(video)\n this.ensureWorkerClient()\n await this.workerClient?.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.state = 'attached'\n }\n\n /**\n * Starts loading, transmuxing, buffering, and playback control.\n *\n * Requires a successful `attach(video)` call first.\n */\n async start(): Promise<void> {\n this.assertNotDestroyed('start')\n\n if (this.video === undefined || this.workerClient === undefined) {\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_START_REQUIRES_ATTACH', 'start() requires a previously attached video element.', false))\n }\n\n if (this.state === 'started') {\n return\n }\n\n if (this.state === 'stopped') {\n await this.workerClient.waitForMediaSourceHandle({ type: 'attach-media-source' })\n this.state = 'attached'\n }\n\n this.workerClient.post({ type: 'start' })\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 async stop(): Promise<void> {\n if (this.state === 'destroyed') {\n return\n }\n\n if (this.workerClient === undefined) {\n this.state = 'stopped'\n this.events.emit('stopped', undefined)\n return\n }\n\n this.stopVideoStateReporting()\n await this.workerClient.waitForStopped({ type: 'stop' })\n this.detachVideoSource()\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 const workerClient = this.workerClient\n this.workerClient = undefined\n this.stopVideoStateReporting()\n\n if (workerClient !== undefined) {\n try {\n await workerClient.waitForDestroyed({ type: 'destroy' })\n } finally {\n workerClient.dispose()\n }\n } else {\n this.events.emit('destroyed', undefined)\n }\n\n if (this.video !== undefined) {\n this.detachVideoSource()\n this.video = undefined\n }\n\n this.state = 'destroyed'\n this.events.clear()\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) => this.events.emit('error', error),\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 switch (message.type) {\n case 'ready':\n this.events.emit('ready', undefined)\n return\n case 'media-source-handle':\n this.attachMediaSourceHandle(message.handle)\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 'error':\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.video === undefined) {\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 Reflect.set(this.video, 'srcObject', handle)\n }\n\n private applyPlaybackOptions(video: HTMLVideoElement): void {\n video.muted = this.options.playback.muted\n video.autoplay = this.options.playback.autoPlay\n }\n\n private startVideoStateReporting(): void {\n this.stopVideoStateReporting()\n this.postVideoState()\n\n const intervalMs = Math.max(100, Math.min(this.options.diagnostics.statsIntervalMs, 250))\n this.videoStateTimer = setInterval(() => {\n this.postVideoState()\n }, intervalMs)\n }\n\n private stopVideoStateReporting(): void {\n if (this.videoStateTimer === undefined) {\n return\n }\n\n clearInterval(this.videoStateTimer)\n this.videoStateTimer = undefined\n }\n\n private postVideoState(): void {\n const video = this.video\n const workerClient = this.workerClient\n if (video === undefined || workerClient === undefined || this.state !== 'started') {\n return\n }\n\n const droppedFrames = getDroppedFrames(video)\n workerClient.post({\n type: 'video-state',\n state: {\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\n private async applyPlaybackControl(action: PlaybackControlAction): Promise<void> {\n const video = this.video\n const workerClient = this.workerClient\n if (video === undefined || workerClient === undefined || this.state !== 'started') {\n return\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\n workerClient.post({ type: 'playback-control-result', result: { type: action.type, accepted: true } })\n this.postVideoState()\n } catch (cause) {\n workerClient.post({\n type: 'playback-control-result',\n result: {\n type: action.type,\n accepted: false,\n message: cause instanceof Error ? cause.message : String(cause),\n },\n })\n }\n }\n\n private detachVideoSource(): 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 private assertNotDestroyed(method: string): void {\n if (this.state !== 'destroyed') {\n return\n }\n\n throw playerErrorToException(createPlayerError('runtime', 'RIVMUX_PLAYER_DESTROYED', `RivmuxPlayer.${method}() cannot be called after destroy().`, true))\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 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\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,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rivmux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Public player package for Rivmux.",
|
|
5
|
+
"homepage": "https://www.npmjs.com/package/rivmux",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/yangxu52/Rivmux/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/yangxu52/Rivmux.git",
|
|
12
|
+
"directory": "packages/player"
|
|
13
|
+
},
|
|
5
14
|
"license": "Apache-2.0",
|
|
6
15
|
"type": "module",
|
|
7
16
|
"sideEffects": false,
|
|
@@ -21,13 +30,13 @@
|
|
|
21
30
|
"module": "./dist/index.js",
|
|
22
31
|
"types": "./dist/index.d.ts",
|
|
23
32
|
"dependencies": {
|
|
24
|
-
"@rivmux/runtime-worker": "0.
|
|
25
|
-
"@rivmux/protocol": "0.
|
|
33
|
+
"@rivmux/runtime-worker": "0.4.0",
|
|
34
|
+
"@rivmux/protocol": "0.4.0"
|
|
26
35
|
},
|
|
27
36
|
"devDependencies": {
|
|
28
|
-
"tsdown": "^0.22.
|
|
29
|
-
"typescript": "~
|
|
30
|
-
"vitest": "^4.1.
|
|
37
|
+
"tsdown": "^0.22.7",
|
|
38
|
+
"typescript": "~6.0.3",
|
|
39
|
+
"vitest": "^4.1.10"
|
|
31
40
|
},
|
|
32
41
|
"engines": {
|
|
33
42
|
"node": "^22.18.0 || >=24.11.0",
|