@meshstream/p2p-web 0.0.0-stage → 0.2.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,3 +1,498 @@
1
- # Temporary Holding Version
1
+ # MeshStream P2P Web SDK
2
+
3
+ Browser HLS and DASH P2P delivery SDK for MeshStream P2P-CDN. The SDK wraps
4
+ `hls.js`, Shaka Player, and `p2p-media-loader`, fetches runtime configuration
5
+ from Config API, joins a WebRTC tracker swarm, reports playback/P2P statistics,
6
+ and keeps CDN playback as the fallback path.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ # SDK-managed player: MeshStream bundles its HLS, DASH, and P2P runtimes.
12
+ npm install @meshstream/p2p-web
13
+ ```
2
14
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
15
+ For an application-managed `hls.js` instance, install the player in the
16
+ application as well. MeshStream will bind P2P to that instance without taking
17
+ over its configuration or lifecycle:
18
+
19
+ ```bash
20
+ npm install @meshstream/p2p-web hls.js
21
+ ```
22
+
23
+ In this monorepo the SDK is consumed as a workspace package:
24
+
25
+ ```bash
26
+ npm run build:web-sdk
27
+ npm run build -w @p2pcdn/web-demo
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ import { attach } from "@meshstream/p2p-web";
34
+
35
+ const player = attach({
36
+ key: "your_sdk_key",
37
+ url: "https://cdn.example.com/live/index.m3u8", // or .mpd / .mp4 / .mp3
38
+ video: document.querySelector("video"),
39
+ });
40
+
41
+ await player.start();
42
+
43
+ window.addEventListener("pagehide", () => player.dispose(), { once: true });
44
+ ```
45
+
46
+ The historic `video` option accepts any `HTMLMediaElement`; pass an `<audio>`
47
+ element for progressive MP3 playback.
48
+
49
+ The SDK fetches SaaS bootstrap configuration, selects trackers/reporting
50
+ endpoints, detects browser capability, starts WebRTC P2P when possible, and
51
+ keeps CDN playback as the automatic fallback path.
52
+
53
+ When MeshStream creates hls.js, use `hlsConfig` to retain normal player tuning
54
+ such as `maxBufferLength`, ABR limits, or low-latency settings:
55
+
56
+ ```ts
57
+ const player = attach({
58
+ key,
59
+ url,
60
+ video,
61
+ hlsConfig: { maxBufferLength: 45, lowLatencyMode: true },
62
+ });
63
+ ```
64
+
65
+ MeshStream always reapplies its P2P loader and safe origin request settings
66
+ after the instance is created. For `attachToHls`, configure the caller-owned
67
+ hls.js instance directly instead.
68
+
69
+ To display compatibility information before playback, call `getCapability()`.
70
+ It returns only `mseSupported`, `webrtcSupported`, and `serviceWorkerSupported`;
71
+ it does not override the SDK's playback or fallback decision.
72
+
73
+ The web transport selects one content-affine Tracker for a playback session.
74
+ Tracker nodes keep independent in-memory peer maps; switching or retrying a
75
+ Tracker does not copy peers between nodes. If signaling becomes unavailable,
76
+ the player continues through CDN rather than registering the same session with
77
+ every shard. Configure tested hot-content buckets and same-region fallback
78
+ nodes when a stream needs faster recovery.
79
+
80
+ `player.statistics()` always returns the same complete counter shape. During
81
+ startup and CDN fallback, P2P counters are `0` instead of being omitted, so
82
+ application dashboards can use one schema for HLS, DASH, MP4/MP3, and RxPlayer.
83
+
84
+ ## Optional Service Worker Transport
85
+
86
+ Service Worker support is an optional transport layer. MSE P2P remains the
87
+ default and is preferred whenever it is available. To deploy the SW runtime,
88
+ copy the published `dist/meshstream-sw.js` file to a same-origin HTTPS path
89
+ such as `/meshstream-sw.js`, then opt in:
90
+
91
+ ```ts
92
+ import { attach } from "@meshstream/p2p-web";
93
+
94
+ const player = attach({
95
+ key,
96
+ url,
97
+ video,
98
+ transportPreference: "auto",
99
+ serviceWorker: {
100
+ url: "/meshstream-sw.js",
101
+ scope: "/",
102
+ autoRegister: true,
103
+ p2pTimeoutMs: 5000,
104
+ onSegmentStats: ({ source, bytes, url }) => {
105
+ console.debug("segment", source, bytes, url);
106
+ },
107
+ },
108
+ });
109
+ await player.start();
110
+ ```
111
+
112
+ `auto` uses MSE P2P first. If MSE is unavailable and the page is controlled by
113
+ the registered SW, playback uses a virtual manifest URL under
114
+ `/__meshstream__/v1/`; failed or unavailable page-side P2P requests fall back
115
+ to the original CDN URL. `transportPreference: "service-worker"` is available
116
+ for explicit testing. When bootstrap returns a tracker configuration, the SDK
117
+ also starts a page-side `p2p-media-loader-core` engine that works without MSE,
118
+ indexes HLS master/media playlists, and answers SW segment requests from the
119
+ WebRTC swarm with CDN fallback. A custom `segmentProvider` can still be passed
120
+ to replace that engine for application-specific routing.
121
+
122
+ For manual registration, import `registerMeshStreamServiceWorker()` and
123
+ `createMeshStreamServiceWorkerSession()` from `@meshstream/p2p-web/service-worker`.
124
+ Registration requires HTTPS (localhost is allowed by browsers), and the SW script
125
+ must be same-origin with the page. Session metadata and rewritten resource mappings
126
+ are kept in IndexedDB so a browser-restarted SW can recover a registered session;
127
+ unregistering the player removes that metadata. `onSegmentStats` reports whether
128
+ each media request was served by the page provider (`p2p`) or CDN (`cdn`).
129
+
130
+ For a local browser smoke check after building, run
131
+ `node sdks/web/test/sw-browser-fixture.mjs` and open the printed URL. The page
132
+ reports the SW controller state, rewritten segment count, response bodies, and
133
+ per-segment source statistics.
134
+
135
+ For the real two-page WebRTC check, run the automated smoke from the repository
136
+ root:
137
+
138
+ ```powershell
139
+ npm run test:web-sw-real
140
+ ```
141
+
142
+ It builds the SDK and Tracker, opens seed/follower Chromium pages, and fails
143
+ unless the follower receives a non-empty segment through P2P. For manual
144
+ inspection, build the SDK and start
145
+ `node sdks/web/test/sw-real-p2p-browser-fixture.mjs`, then open the seed URL
146
+ first (`?role=seed`) and the follower URL (`?role=follower`).
147
+
148
+ ### Staging rollout checklist
149
+
150
+ 1. Build the browser package with `npm run build:web-sdk:release` and publish the
151
+ generated npm tarball. Copy `dist/meshstream-sw.js` from the package to a stable
152
+ same-origin HTTPS URL such as `/meshstream-sw.js`; do not serve it from a CDN
153
+ origin or a path outside the intended registration scope.
154
+ 2. Deploy the page with `transportPreference: "auto"` and the SW `url` configured.
155
+ Confirm the normal browser path reports `p2p-hls`; test Safari/iOS or a forced
156
+ `transportPreference: "service-worker"` path and confirm `sw-hls-cdn`.
157
+ 3. Check the browser Network panel for the virtual manifest under
158
+ `/__meshstream__/v1/` and verify segment requests return either `p2p` or `cdn`
159
+ in the SDK `onSegmentStats` callback.
160
+ 4. Verify the configured report endpoint receives `reportType: "stats"` with
161
+ `p2pDownloaded`, `httpDownloaded`, `connectedPeers`, and tracker diagnostics.
162
+ The SDK sends an initial report, periodic reports, and a final `pagehide`
163
+ report; report failures must not interrupt playback.
164
+ 5. Roll back by removing the `serviceWorker` option or setting
165
+ `transportPreference: "mse"`; the SDK then keeps the existing MSE/CDN path.
166
+
167
+ ## Advanced Player Adapters
168
+
169
+ Use `@meshstream/p2p-web/adapters` for an existing player instance. Controlled
170
+ test environments that need to construct `MeshStreamHlsPlayer` directly can
171
+ import it from `@meshstream/p2p-web/advanced`; standard integrations should
172
+ continue to use root-level `attach`.
173
+
174
+ For a plain video element:
175
+
176
+ ```ts
177
+ import { attach } from "@meshstream/p2p-web";
178
+
179
+ const player = attach({ key, url, video });
180
+ await player.start();
181
+ ```
182
+
183
+ ### Choosing an HLS integration
184
+
185
+ Both HLS modes use the same P2P scheduling, authorization, and telemetry.
186
+ Use `attach` when the SDK should create and manage hls.js. Use `attachToHls`
187
+ when the application already owns an hls.js instance and must retain its
188
+ buffering, ABR, event, or plugin configuration. In the latter mode, the
189
+ application remains responsible for `loadSource`, `attachMedia`, and
190
+ `destroy`; call `session.start()` before `loadSource`. Do not use both modes
191
+ for the same media element or playback URL.
192
+
193
+ For an existing hls.js instance, bind MeshStream before the application starts
194
+ media loading. This is a native HLS P2P integration: the SDK keeps the caller's
195
+ hls.js configuration and P2P-enables that same instance. It never calls
196
+ `loadSource`, `attachMedia`, or `destroy` on the caller-owned player.
197
+
198
+ ```ts
199
+ import Hls from "hls.js";
200
+ import { attachToHls } from "@meshstream/p2p-web";
201
+
202
+ const hls = new Hls({ maxBufferLength: 30 });
203
+ const session = attachToHls({ key, url, video, player: hls });
204
+
205
+ await session.start(); // Bind P2P before hls.js makes media requests.
206
+ hls.loadSource(url);
207
+ hls.attachMedia(video);
208
+
209
+ window.addEventListener("pagehide", () => {
210
+ session.dispose(); // Does not destroy hls.
211
+ hls.destroy(); // The application owns this lifecycle.
212
+ }, { once: true });
213
+ ```
214
+
215
+ For rotating signed URLs, pass a stable business identity so viewers join the
216
+ same swarm. `mimeType` is useful when the media URL has no `.m3u8`, `.mpd`, `.mp4`, or `.mp3`
217
+ suffix. `originHeaders` are sent only to same-origin manifest, segment, and key requests on the
218
+ configured media origin. SDK-controlled headers such as `Range` and hop-by-hop headers are ignored;
219
+ the browser still applies its own forbidden-header and CORS rules. For Shaka and dash.js
220
+ adapter objects, the player must expose its request interception API; otherwise
221
+ the SDK fails explicitly instead of starting without the requested headers.
222
+
223
+ ```ts
224
+ const player = attach({
225
+ key,
226
+ url: signedManifestUrl,
227
+ video,
228
+ contentId: "live-channel-123",
229
+ mimeType: "m3u8",
230
+ originHeaders: { Authorization: "Bearer ...", "X-Playback-Session": "..." },
231
+ });
232
+ await player.start();
233
+ ```
234
+
235
+ For tokens that rotate during a long playback session, provide the current
236
+ value with a synchronous callback. The callback runs immediately before an
237
+ eligible CDN-origin request; it must not refresh a token or return a Promise.
238
+ Refresh credentials in application state first. Dynamic values override static
239
+ headers with the same name and, like static values, are never sent to peers.
240
+
241
+ ```ts
242
+ let playbackToken = initialToken;
243
+
244
+ const player = attach({
245
+ key,
246
+ url: signedManifestUrl,
247
+ video,
248
+ originHeaders: { "X-Playback-Session": playbackSession },
249
+ originHeadersProvider: ({ kind }) =>
250
+ kind === "segment" ? { Authorization: `Bearer ${playbackToken}` } : undefined,
251
+ });
252
+ ```
253
+
254
+ For Video.js:
255
+
256
+ ```ts
257
+ import { attachToVideoJs } from "@meshstream/p2p-web/adapters";
258
+
259
+ const player = attachToVideoJs({
260
+ key,
261
+ url,
262
+ player: videojs("player"),
263
+ });
264
+ await player.start();
265
+ ```
266
+
267
+ This adapter follows the Video.js instance lifecycle and disposes the
268
+ MeshStream session automatically when Video.js is destroyed. It is a
269
+ media-element bridge: MeshStream owns protocol-compatible playback for that
270
+ element. For HLS, do not also load the same URL through Video.js VHS.
271
+
272
+ For Video.js HLS playback that must retain VHS and Video.js ABR ownership, use
273
+ the native VHS adapter instead. It gives VHS a same-origin virtual manifest and
274
+ routes the resulting segment traffic through the Service Worker P2P path. The
275
+ SDK releases its session automatically when Video.js is disposed; if the
276
+ Service Worker cannot start, VHS continues with the original CDN manifest.
277
+
278
+ ```ts
279
+ import { attachToVhs } from "@meshstream/p2p-web/adapters";
280
+
281
+ const session = attachToVhs({
282
+ key,
283
+ url: "https://cdn.example.com/live/index.m3u8",
284
+ player: videojs("player"),
285
+ serviceWorker: { url: "/meshstream-sw.js" },
286
+ });
287
+ await session.start();
288
+ ```
289
+
290
+ For players built on top of a standard `<video>` element:
291
+
292
+ ```ts
293
+ import { attachToArtPlayer, attachToDPlayer, attachToPlyr } from "@meshstream/p2p-web/adapters";
294
+
295
+ await attachToPlyr({ key, url, player: plyr }).start();
296
+ await attachToDPlayer({ key, url, player: dplayer }).start();
297
+ await attachToArtPlayer({ key, url, player: art }).start();
298
+ ```
299
+
300
+ The named adapters resolve common `video` or `media` fields, or a `<video>` nested
301
+ inside the player's root element, and then use the
302
+ same automatic HLS, DASH, and progressive-media path as the plain video adapter. They are media-element bridges,
303
+ not native Video.js, Plyr, DPlayer, or ArtPlayer engine plugins: MeshStream
304
+ owns the playback instance used for the supplied media element.
305
+
306
+ For DASH with the standard SDK path:
307
+
308
+ ```ts
309
+ import { attach } from "@meshstream/p2p-web";
310
+
311
+ const player = attach({
312
+ key,
313
+ url: "https://cdn.example.com/live/index.mpd",
314
+ video,
315
+ });
316
+ await player.start();
317
+ ```
318
+
319
+ `attach` selects DASH for `.mpd` URLs and HLS for `.m3u8` URLs. Customers
320
+ do not pass a protocol flag. DASH P2P uses Shaka Player internally for the
321
+ standard SDK path.
322
+
323
+ For an existing Shaka Player:
324
+
325
+ ```ts
326
+ import { attachToShaka } from "@meshstream/p2p-web/adapters";
327
+
328
+ const player = attachToShaka({
329
+ key,
330
+ url: "https://cdn.example.com/live/index.mpd",
331
+ player: shakaPlayer,
332
+ });
333
+ await player.start();
334
+ ```
335
+
336
+ `attachToShaka()` binds and releases only the MeshStream P2P session. The
337
+ application retains ownership of the supplied Shaka instance and must destroy
338
+ it when appropriate.
339
+
340
+ For an existing dash.js player:
341
+
342
+ ```ts
343
+ import { attachToDashJs } from "@meshstream/p2p-web/adapters";
344
+
345
+ const player = attachToDashJs({
346
+ key,
347
+ url: "https://cdn.example.com/live/index.mpd",
348
+ video,
349
+ player: dashJsPlayer,
350
+ });
351
+ await player.start();
352
+ ```
353
+
354
+ `attachToDashJs` provides native DASH P2P for dash.js `>= 3.1.0 < 6`. Create
355
+ the dash.js instance first, but do not initialize it or load the MPD before
356
+ `session.start()`: MeshStream installs its `XHRLoader` extension first, then
357
+ initializes the supplied instance. Regular audio/video initialization, index,
358
+ and media segments use P2P with CDN fallback. MPD, DRM licenses, subtitles,
359
+ low-latency partial segments, and credentialed cookie requests remain on the
360
+ customer's dash.js/CDN path. When passing
361
+ `originHeaders` to an existing dash.js or Shaka instance, its request interception
362
+ API is required; native Safari HLS cannot accept arbitrary custom headers, so use
363
+ hls.js or signed URLs for that case.
364
+
365
+ ### Adapter capability matrix
366
+
367
+ | Player path | P2P integration | Notes |
368
+ | --- | --- | --- |
369
+ | `attach` with HLS | Native hls.js P2P | MeshStream creates and owns hls.js. |
370
+ | `attachToHls` | Native existing hls.js P2P | Application keeps the hls.js lifecycle. |
371
+ | `attach` with DASH; `attachToShaka` with HLS/DASH | Native Shaka P2P | Uses Shaka's networking integration. |
372
+ | `attachToRxPlayer` with DASH | Native RxPlayer segment P2P | Regular DASH segments; same-origin origin headers are supported. |
373
+ | `attachToDashJs` with dash.js `>= 3.1.0 < 6` | Native dash.js P2P | Installs a supported `XHRLoader` extension before dash.js initialization. |
374
+ | `attachToVhs` with Video.js VHS | Native VHS via Service Worker | VHS retains player and ABR ownership; requires a same-origin Service Worker. |
375
+ | `attachToVideoJs`, Plyr, DPlayer, ArtPlayer | Media-element bridge | Not a native plugin for the player's own media engine. |
376
+
377
+ For an existing RxPlayer DASH instance:
378
+
379
+ ```ts
380
+ import RxPlayer from "rx-player";
381
+ import { attachToRxPlayer } from "@meshstream/p2p-web";
382
+
383
+ const rxPlayer = new RxPlayer({ videoElement: video });
384
+ const player = attachToRxPlayer({ key, url: dashMpdUrl, contentId: "asset-123", player: rxPlayer });
385
+ await player.start();
386
+ ```
387
+
388
+ `attachToRxPlayer` intercepts RxPlayer's actual DASH audio/video segment loader and sends those
389
+ requests through MeshStream P2P. When `originHeaders` or
390
+ `originHeadersProvider` are configured, same-origin MPD and regular
391
+ audio/video fallback requests retain those headers; dynamic values override
392
+ static values. Cross-origin media, text, DRM, non-contiguous ranges, and
393
+ RxPlayer low-latency chunked loading remain on the customer's RxPlayer/CDN
394
+ path without SDK-supplied headers.
395
+
396
+ Telemetry keeps the playback host and path for diagnostics but strips query and fragment data,
397
+ so signed playback credentials are not included in reports.
398
+
399
+ ## Runtime Contract
400
+
401
+ - `key` is the customer-facing SDK Key and can be embedded in Web pages.
402
+ - `homeRegion` is resolved from the SDK Key and identifies the tenant's registered data-home
403
+ region. It is returned by bootstrap for diagnostics and future regional routing; customer code
404
+ cannot override it.
405
+ - Request-network GeoIP is used only for access-side Tracker/ICE locality and is not a data-residency
406
+ selection.
407
+ - Customer integrations should only need `key`, the media `url`, and a `video`
408
+ element for the standard Web path.
409
+ - The SaaS bootstrap endpoint, tracker pool, ICE servers, reporting route, and
410
+ scheduling policy are internal service configuration.
411
+ - P2P, tracker, authorization, and reporting failures must not block `hls.js`
412
+ playback.
413
+ - `detectWebP2pCapability` should be called before constructing the P2P client.
414
+ iOS Safari HLS without MSE is routed to native HLS CDN playback.
415
+ - Fallback reporting uses the service-owned endpoint returned by bootstrap.
416
+ - Signed CDN URL query fields such as `token`, `signature`, `expires`,
417
+ `x-amz-*`, `x-oss-*`, and `x-goog-*` are excluded from URL-derived swarm IDs.
418
+ - `contentId` is optional. When supplied, the SDK derives the internal swarm ID
419
+ from it so rotating signed URL paths remain in the same swarm; otherwise it
420
+ derives identity from the canonical playback URL.
421
+ - `swarmId` and rendition/quality identifiers are internal runtime values. They
422
+ are intentionally not part of the customer-facing configuration; the SDK
423
+ derives and manages them from the content and manifest.
424
+ - Direct progressive `.mp4` and `.mp3` files use the same P2P Service Worker transport as a
425
+ byte-range stream. The SDK registers the Worker by default for this path and divides the file
426
+ into deterministic 1 MiB pieces. Large and open-ended ranges are returned as a backpressure-aware
427
+ stream, so they do not require buffering the whole response in the page. Full-file requests
428
+ without `Range`, suffix ranges, multi-range requests, and metadata remain on CDN.
429
+ Use a stable `contentId` when signed URLs have per-viewer path differences.
430
+ - The direct-file origin must support byte ranges, expose `Content-Length`, `Content-Type`, and
431
+ `Accept-Ranges` through CORS, and allow `GET`, `HEAD`, `OPTIONS`, and the `Range` request header.
432
+ `HEAD` is recommended; if the origin explicitly returns `405` or `501`, extensionless URL detection
433
+ uses a one-byte range `GET` instead. Missing metadata or any P2P error falls back to the original CDN URL.
434
+
435
+ ## Public API
436
+
437
+ ```ts
438
+ // Standard customer-facing entry point
439
+ attach(options)
440
+
441
+ // Advanced player adapters
442
+ import { attachToShaka, attachToDashJs, attachToVhs, attachToVideoJs } from "@meshstream/p2p-web/adapters";
443
+
444
+ // P2P runtime controls
445
+ player.start()
446
+ player.dispose()
447
+ player.setP2pEnabled(enabled)
448
+ player.addEventListener(listener) // returns unsubscribe
449
+ player.statistics()
450
+ player.diagnostics()
451
+ player.qoe()
452
+ ```
453
+
454
+ `setP2pEnabled(false)` may be called before `start()` to force a CDN-only
455
+ session, or during playback to stop P2P without interrupting CDN delivery.
456
+ Repeated `start()` calls share the active session; call `dispose()` before
457
+ starting a fresh session for a different source or player lifecycle.
458
+
459
+ `start()` returns `{ mode, capability, fallbackReason? }`. On a CDN fallback,
460
+ `fallbackReason` is one of `CAPABILITY_UNSUPPORTED`, `P2P_DISABLED`,
461
+ `SDK_CONFIG_UNAVAILABLE`, `TRACKER_UNAVAILABLE`, or
462
+ `P2P_INITIALIZATION_FAILED`. It is intended for customer telemetry and does
463
+ not expose the server bootstrap configuration.
464
+
465
+ `addEventListener()` accepts `onPlaybackStateChanged`, `onP2pReady`,
466
+ `onP2pUnavailable`, `onPeerCountChanged`, `onTrackerError`,
467
+ `onPeerConnectError`, `onCdnFallback`, and `onStatistics`. Register before `start()` when the
468
+ initial connection state matters. Startup CDN fallbacks emit `UNSUPPORTED`,
469
+ `DISABLED`, `CONFIG_UNAVAILABLE`, `TRACKER_ERROR`, or
470
+ `INITIALIZATION_FAILED`; call the returned function during cleanup.
471
+
472
+ `onPlaybackStateChanged` reports `idle`, `initializing`, `connecting`,
473
+ `p2p-ready`, `cdn-fallback`, and `disposed`. `qoe()` returns a safe
474
+ application-telemetry snapshot: total/P2P/CDN download bytes, P2P download
475
+ ratio, connected peers, first-peer latency when available, cold-start CDN
476
+ bytes, and fallback count. It intentionally excludes Tracker, ICE, and peer
477
+ identifiers.
478
+
479
+ Low-latency HLS/DASH partial CMAF segment requests remain on the player CDN
480
+ path. P2P only distributes complete segments: sharing a response before its
481
+ partial body is complete breaks player cancellation semantics and causes
482
+ latency jitter. Playback continues normally through CDN for those parts.
483
+
484
+ ## Release Checks
485
+
486
+ ```bash
487
+ npm test -w @meshstream/p2p-web
488
+ npm run build:web-sdk:release
489
+ npm run pack:dry-run -w @meshstream/p2p-web
490
+ ```
491
+
492
+ `prepack` runs the SDK test suite, bundles the runtime dependencies into the
493
+ public entry points, and then minifies the release output before package
494
+ creation. The published package contains only bundled JavaScript, `.d.ts`
495
+ types, the Service Worker, README, and licenses; customers do not need to
496
+ install the internal player or P2P packages separately. Public exports and
497
+ object property names remain stable, while readable formatting and source maps
498
+ are omitted from the published JavaScript.
@@ -0,0 +1,140 @@
1
+ import type { ShakaPlayerLike } from "./dash-p2p.js";
2
+ import { type HlsLike, type MeshStreamP2pFallbackReason, type MeshStreamServiceWorkerTransportOptions, type MeshStreamTransportPreference } from "./player.js";
3
+ import type { P2pEventListener } from "./hls.js";
4
+ import { type P2pStats } from "./report.js";
5
+ import type { WebP2pCapabilityResult } from "./capabilities.js";
6
+ import { type MeshStreamQoeSnapshot } from "./qoe.js";
7
+ import { type OriginHeadersProvider } from "./origin-headers.js";
8
+ import { type MeshStreamRxPlayerOptions } from "./rx-player.js";
9
+ export interface MeshStreamAttachToVideoOptions {
10
+ key: string;
11
+ url: string;
12
+ video: HTMLMediaElement;
13
+ contentId?: string;
14
+ mimeType?: "m3u8" | "mpd" | "mp4" | "mp3";
15
+ /** Optional Config API bootstrap endpoint, mainly for private deployments. */
16
+ bootstrapUrl?: string;
17
+ originHeaders?: Record<string, string>;
18
+ /** Resolves request headers immediately before an origin CDN request. */
19
+ originHeadersProvider?: OriginHeadersProvider;
20
+ autoplay?: boolean;
21
+ /** Set `false` to start this session on CDN without creating P2P transport. */
22
+ p2pEnabled?: boolean;
23
+ httpDownloadInitialTimeoutMs?: number;
24
+ /** hls.js options used only when MeshStream creates the HLS instance. */
25
+ hlsConfig?: Record<string, unknown>;
26
+ transportPreference?: MeshStreamTransportPreference;
27
+ serviceWorker?: MeshStreamServiceWorkerTransportOptions;
28
+ }
29
+ export type MeshStreamAttachToMediaOptions = MeshStreamAttachToVideoOptions;
30
+ /** Standard customer-facing configuration for MeshStream Web playback. */
31
+ export type MeshStreamAttachOptions = MeshStreamAttachToMediaOptions;
32
+ /** Playback state and transfer callbacks shared by every MeshStream adapter. */
33
+ export type MeshStreamP2pEventListener = P2pEventListener;
34
+ /** A common, configuration-safe result returned by every public media adapter. */
35
+ export interface MeshStreamPlaybackStartResult {
36
+ mode: string;
37
+ capability?: WebP2pCapabilityResult;
38
+ fallbackReason?: MeshStreamP2pFallbackReason;
39
+ }
40
+ export type { MeshStreamP2pFallbackReason } from "./player.js";
41
+ export interface MeshStreamMediaPlayer {
42
+ start(): Promise<MeshStreamPlaybackStartResult>;
43
+ setP2pEnabled(enabled: boolean): void;
44
+ /**
45
+ * Subscribes to P2P playback events. The returned function removes this
46
+ * listener and is safe to call more than once.
47
+ */
48
+ addEventListener(listener: MeshStreamP2pEventListener): () => void;
49
+ statistics(): P2pStats;
50
+ diagnostics(): {
51
+ lastPeerError?: string;
52
+ lastTrackerError?: string;
53
+ };
54
+ qoe(): MeshStreamQoeSnapshot;
55
+ dispose(): void;
56
+ }
57
+ type MeshStreamMediaMimeType = NonNullable<MeshStreamAttachToVideoOptions["mimeType"]>;
58
+ export interface MeshStreamAttachToVideoJsOptions extends Omit<MeshStreamAttachToVideoOptions, "video"> {
59
+ player: VideoJsLikePlayer;
60
+ }
61
+ /** Native Video.js VHS path. VHS retains player and ABR ownership. */
62
+ export interface MeshStreamAttachToVhsOptions extends Omit<MeshStreamAttachToVideoOptions, "video" | "mimeType"> {
63
+ mimeType?: "m3u8";
64
+ player: VideoJsVhsLikePlayer;
65
+ }
66
+ /**
67
+ * Binds P2P to an existing hls.js instance without taking over its lifecycle.
68
+ * Call start() before the application calls hls.loadSource().
69
+ */
70
+ export interface MeshStreamAttachToHlsOptions extends Omit<MeshStreamAttachToVideoOptions, "mimeType" | "hlsConfig"> {
71
+ player: HlsLike;
72
+ }
73
+ export interface MeshStreamAttachToDashJsOptions extends Omit<MeshStreamAttachToVideoOptions, "video" | "mimeType"> {
74
+ /** Required before dash.js initialization; otherwise resolved from the player. */
75
+ video?: HTMLMediaElement;
76
+ mimeType?: "mpd";
77
+ player: DashJsLikePlayer;
78
+ }
79
+ export interface MeshStreamAttachToShakaOptions extends Omit<MeshStreamAttachToVideoOptions, "video" | "mimeType"> {
80
+ mimeType?: "m3u8" | "mpd";
81
+ player: ShakaLikePlayer;
82
+ }
83
+ export interface MeshStreamAttachToWrappedPlayerOptions extends Omit<MeshStreamAttachToVideoOptions, "video"> {
84
+ player: WrappedVideoPlayer;
85
+ }
86
+ export type MeshStreamAttachToRxPlayerOptions = MeshStreamRxPlayerOptions;
87
+ export interface VideoJsLikePlayer {
88
+ el?: () => unknown;
89
+ tech?: (safe?: boolean) => unknown;
90
+ on?: (event: string, listener: () => void) => void;
91
+ off?: (event: string, listener: () => void) => void;
92
+ }
93
+ export interface VideoJsVhsLikePlayer extends VideoJsLikePlayer {
94
+ src?: (source: {
95
+ src: string;
96
+ type?: string;
97
+ } | string) => unknown;
98
+ }
99
+ export interface WrappedVideoPlayer {
100
+ video?: unknown;
101
+ media?: unknown;
102
+ el?: unknown;
103
+ element?: unknown;
104
+ }
105
+ export interface DashJsLikePlayer {
106
+ getVideoElement?: () => unknown;
107
+ initialize?: (video: HTMLMediaElement, url: string, autoplay?: boolean) => void;
108
+ addRequestInterceptor?(interceptor: Function): void;
109
+ removeRequestInterceptor?(interceptor: Function): void;
110
+ reset?: () => void;
111
+ }
112
+ export interface ShakaLikePlayer extends ShakaPlayerLike {
113
+ getMediaElement?: () => HTMLMediaElement | null;
114
+ }
115
+ export declare function attachToVideo(options: MeshStreamAttachToVideoOptions): MeshStreamMediaPlayer;
116
+ export declare function attachToHls(options: MeshStreamAttachToHlsOptions): MeshStreamMediaPlayer;
117
+ /** Standard integration entry point. Selects HLS or DASH from the manifest URL. */
118
+ export declare function attach(options: MeshStreamAttachOptions): MeshStreamMediaPlayer;
119
+ export declare function attachToMedia(options: MeshStreamAttachToMediaOptions): MeshStreamMediaPlayer;
120
+ /**
121
+ * Probes extensionless media URLs without replaying customer headers. A small
122
+ * range GET is used only when the origin explicitly does not support HEAD.
123
+ */
124
+ export declare function probeMediaType(url: string, fetcher?: typeof fetch, signal?: AbortSignal): Promise<MeshStreamMediaMimeType | undefined>;
125
+ export declare function attachToDashJs(options: MeshStreamAttachToDashJsOptions): MeshStreamMediaPlayer;
126
+ export declare function attachToShaka(options: MeshStreamAttachToShakaOptions): MeshStreamMediaPlayer;
127
+ export declare function attachToVideoJs(options: MeshStreamAttachToVideoJsOptions): MeshStreamMediaPlayer;
128
+ /**
129
+ * Uses the Video.js VHS engine with MeshStream's Service Worker transport.
130
+ * Do not combine this with attachToVideoJs(), which creates a separate hls.js
131
+ * playback path for the same media element.
132
+ */
133
+ export declare function attachToVhs(options: MeshStreamAttachToVhsOptions): MeshStreamMediaPlayer;
134
+ export declare function attachToPlyr(options: MeshStreamAttachToWrappedPlayerOptions): MeshStreamMediaPlayer;
135
+ export declare function attachToDPlayer(options: MeshStreamAttachToWrappedPlayerOptions): MeshStreamMediaPlayer;
136
+ export declare function attachToArtPlayer(options: MeshStreamAttachToWrappedPlayerOptions): MeshStreamMediaPlayer;
137
+ /** Attach a DASH-capable RxPlayer through its real segment-loader API. */
138
+ export declare function attachToRxPlayer(options: MeshStreamAttachToRxPlayerOptions): MeshStreamMediaPlayer;
139
+ export declare function resolveVideoJsElement(player: VideoJsLikePlayer): HTMLMediaElement | undefined;
140
+ export declare function resolveWrappedVideoElement(player: WrappedVideoPlayer): HTMLMediaElement | undefined;