@oddin-gg/havik-player 2.25.0 → 2.26.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/dist/havik-player.global.js +2 -2
- package/dist/havik-player.global.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +525 -444
- package/dist/index.js.map +1 -1
- package/docs/API.md +54 -49
- package/embed/index.html +1 -1
- package/examples/vanilla.html +1 -1
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -220,46 +220,46 @@ player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));
|
|
|
220
220
|
|
|
221
221
|
#### `CreatePlayerOptions`
|
|
222
222
|
|
|
223
|
-
| Option | Type | Default | Description
|
|
224
|
-
| ------------------------- | --------------------------------------- | ----------------------------------------------- |
|
|
225
|
-
| `video` | `HTMLVideoElement` | — | **Required.** The element the SDK will control.
|
|
226
|
-
| `env` | `'integration' \| 'production'` | — | **Required unless `baseUrl` is given.** Which Oddin environment to play against. Preferred: the host is correct by construction, and so is everything derived from it (the SSE events host, the QoE beacons host).
|
|
227
|
-
| `baseUrl` | `string` | — | **Required unless `env` is given.** API base URL, e.g. `https://feed-dev.oddin-video.gg`. Use for a deployment the named environments do not cover: your own CDN or gateway fronting the streams API, the CN plane, or a pinned build that must outlive a domain change. Passing both `env` and `baseUrl` — or neither — is a compile error in TypeScript and throws at runtime in JavaScript.
|
|
228
|
-
| `matchUrn` | `string` | — | **Required.** The match to play, e.g. `od:match:1234`.
|
|
229
|
-
| `credential` | [`CredentialSource`](#credentialsource) | — | **Required.** Your api‑key (or a function returning one).
|
|
230
|
-
| `autoplay` | `boolean` | `true` | Start playback once ready. With `false`, the player holds in a pre-play `'paused'` state (poster + play card, no spinner) and fetches no media and no DRM license until the first `play()`. On the hls.js engine the manifest is still parsed pre-play, so `'ready'` and the track lists keep working; the native (Safari) engine defers all fetching and emits `'ready'` at attach instead. Note: if a programmatic `play()` is policy-blocked (`autoplayblocked`), the loader keeps warming the buffer — media and DRM traffic flows from that kick, not from a viewer gesture.
|
|
231
|
-
| `muted` | `boolean` | `false` | Mute the element. Set `true` for reliable autoplay under browser sound policies.
|
|
232
|
-
| `controls` | `'custom' \| 'native' \| 'none'` | `'custom'` | Which controls to render. See [Controls & theming](#controls--theming).
|
|
233
|
-
| `theme` | [`Partial<HavikTheme>`](#haviktheme) | Oddin brand | Re-skin the custom control bar (ignored unless `controls: 'custom'`). See [Controls & theming](#controls--theming).
|
|
234
|
-
| `statusOverlays` | `boolean` | `true` | Branded full-bleed cards for the stopped/pre-play states (ended, fatal error, pre-play, armed-waiting) instead of a frozen frame + a dead play button. Themed via the control-bar tokens + `theme.logoUrl`. Set `false` to render your own UI from the `ended`/`error` events. Ignored unless `controls: 'custom'`.
|
|
235
|
-
| `cspNonce` | `string` | — | Your page's CSP nonce, stamped on the `<style>` tag the custom control bar injects. Needed only when `style-src` has no `'unsafe-inline'`: without it the browser blocks the tag and the controls render unstyled. See [Content-Security-Policy](#content-security-policy). Ignored unless `controls: 'custom'`.
|
|
236
|
-
| `endedMessage` | `string` | "This stream has ended" | Message on the end-of-stream card.
|
|
237
|
-
| `interruptedMessage` | `string` | "Reconnecting — please stand by" | Message shown **instead of** `endedMessage` when the server reports the stream stopped but the match is still running (`endedReason: 'interrupted'`, an ingest outage that outlived the bridge's hold). The player rejoins automatically when the stream returns **while `rejoinOnLive` is on** (the default); override this wording if you disable auto-rejoin, since nothing will re-attach on its own.
|
|
238
|
-
| `errorMessage` | `string` | the error's message | Message on the fatal-error card (otherwise the error's own message).
|
|
239
|
-
| `offlineMessage` | `string` | "No internet connection — waiting to reconnect" | Message on the card shown while the browser reports no network (`navigator.onLine === false`). It replaces the error and reconnecting cards for as long as the browser says offline and carries no Retry button: nothing can succeed without a route, and the player resumes by itself on the browser's `online` event. See [Connectivity](#connectivity).
|
|
240
|
-
| `waitForLive` | [`WaitForLive`](#waitforlive) | — | Arm on an upcoming match and auto‑attach at go‑live.
|
|
241
|
-
| `lowLatency` | `'auto' \| boolean` | `'auto'` | `'auto'` lets the player decide from the manifest.
|
|
242
|
-
| `engine` | `'auto' \| 'hls' \| 'native'` | `'auto'` | `'auto'` uses the browser's native player on iPhone and iPad for every stream, and for FairPlay on any Apple browser; elsewhere it prefers hls.js (desktop Safari included) and falls back to native HLS. hls.js on iOS runs on ManagedMediaSource, which closed mid‑session and played slower than real time in testing.
|
|
243
|
-
| `userId` | `string` | — | Optional viewer id (sent as `X-User-Id` on license requests).
|
|
244
|
-
| `debug` | `boolean` | `false` | Turn on hls.js's internal console logging: fragment/part scheduling, ABR switches, EME key sessions and license traffic. For diagnosing a playback problem with Oddin support; leave it off in production (it logs on every fragment, and the EME lines include license request/response metadata). hls.js engine only. `hlsConfig.debug` still wins if you set both.
|
|
245
|
-
| `hlsConfig` | `Partial<HlsConfig>` | — | Escape hatch for advanced hls.js tuning.
|
|
246
|
-
| `statsIntervalMs` | `number` | `2000` | How often `'stats'` is emitted.
|
|
247
|
-
| `licenseRefreshMs` | `number` | `480000` (8 min) | How often to silently re-resolve playback so the signed DRM license URL the player would use next stays inside its signature lifetime (~10 min). Refreshes the URL, not the license: it cannot extend a license the CDM already holds, so a session that outlasts its license still ends with a fatal [`DRM_CLIENT`](#errors) error. `0` disables the timer only: a lapsed URL is still refreshed on refocus and before a license request (Widevine on hls.js; the native FairPlay engine has neither check).
|
|
248
|
-
| `poster` | `string` | — | Poster image shown before playback starts.
|
|
249
|
-
| `startLevel` | `number` | `-1` | Initial rendition index (`-1` = auto). With auto, ABR starts on a conservative, sustainable rendition (~360p) and ramps up as bandwidth allows; see [Low‑latency & adaptive bitrate](#low-latency--adaptive-bitrate).
|
|
250
|
-
| `maxBitrate` | `number` | — | Cap ABR to renditions at/below this bitrate (bps).
|
|
251
|
-
| `liveLatencyTarget` | `number` | `2` | Target live‑edge latency in seconds (hls.js `liveSyncDuration`). The player derives a matching latency **ceiling** internally and catches up at 1.1×, so this is the only live‑latency knob you need. Lower = closer to live but more rebuffer‑prone. Live/LL only.
|
|
252
|
-
| `adaptiveHoldBack` | `boolean` | `true` | hls.js engine: raise the live‑sync target for a session whose part fetches are slow, instead of letting it stall. A viewer far from the origin plays from a local edge whose every cache miss on a 0.5 s part is a cross‑continent fetch (250–310 ms per part from South America, Canada/Mexico and APAC on the global slot, against 44 ms in Europe), so the fetch cycle outruns the part cadence and the 2 s buffer drains. The session's own part fetches are timed (first byte, as a share of the part's duration, over a 10 s window): a window whose p95 is at or above 60% **and** whose median is at or above 40%, two 3 s ticks running, steps the target up 0.5 s, to `adaptiveHoldBackCeiling` at most; a p95 under 40% for 60 s steps it back down. A session whose fetches are fast never moves. Each move fires [`latencytargetchange`](#player-events), counts in `holdBackSteps`, and shows in `targetLatencySeconds`. Live/LL only.
|
|
253
|
-
| `adaptiveHoldBackCeiling` | `number` | `3.5` | Highest live‑sync target the adaptive hold‑back may step to, in seconds. Kept a second under the drift ceiling; at or below `liveLatencyTarget` it disables the stepping.
|
|
254
|
-
| `snapToLiveOnRefocus` | `boolean` | `true` | Snap to the live edge when a backgrounded tab is refocused: on hls.js while far behind; on the native player (iPhone, iPad, FairPlay) on every return after 2 s or more away and on every back/forward‑cache restore, 3 s behind the edge once the first playlist refresh after the return moves it (up to 6 s wait; the edge on return is still the one from before leaving), which also redraws a picture iOS left black. Live/LL only.
|
|
255
|
-
| `rememberBandwidth` | `boolean` | `true` | hls.js engine: start from the bandwidth this device measured in a recent session (`localStorage` key `havik.player.bandwidth`, per connection type, up to 3 days old, reused at 80%) instead of the conservative ~1 Mbps start; a slow record starts from that lower figure (0.3 Mbps floor). A first visit, a stale record or blocked storage keeps the conservative start; an explicit `hlsConfig.abrEwmaDefaultEstimate` or `startLevel` wins. Reset with [`clearRememberedBandwidth()`](#clearrememberedbandwidth).
|
|
256
|
-
| `sizeCapHeadroom` | `number` | unset | hls.js engine: rendition rungs ABR may pick above the one that merely covers the player's pixel box. Unset: `1` on displays below 2× `devicePixelRatio`, `0` at 2× and above. A 960 px box at 1x covers with 960×540; one rung of headroom plays 720p downscaled instead, which looks sharper for the same box; phones and 2× screens keep the cover-only cap (their covering rung already matches their device pixels; a rung above it mostly costs data). An explicit value applies on every display: `0` = strict cover-only cap, `Infinity` = no size cap. Never past the top rung; `maxBitrate` and the frame-drop cap still apply. `hlsConfig.capLevelToPlayerSize: true` caps to the covering rung with no headroom.
|
|
257
|
-
| `mediaDuringKey` | `boolean` | `true` | DRM cold start: download the first init segment and parts while the first key is still being acquired, instead of after it. Bytes reach hls.js only when it asks for them; only the network time moves off the critical path (~0.5 s on Widevine LL-HLS). Ignored on clear streams and when `hlsConfig.fLoader` is set. Pass `false` to opt out.
|
|
258
|
-
| `preloadHints` | `boolean` | `
|
|
259
|
-
| `liveStateEvents` | `boolean` | `true` | Use the push-based **live-state SSE** channel for go-live and end-of-stream (see [Live state](#live-state-sse)). Graceful: falls back to polling if the endpoint is unreachable; a 410 (match gone) stops the subscription and a 404 (not in the catalog) slows it to one retry every 5 min, each with one `warning`. On a return from the background (5 s or more away, or a back/forward-cache restore) the subscription is reopened so the server replays the current state: a page suspended through the end of a match does not sit buffering on a socket that will never deliver it. Set `false` to disable.
|
|
260
|
-
| `rejoinOnLive` | `boolean` | `true` | Rejoin automatically when a stream that ended comes back live. An ingest outage longer than the bridge's reconnect grace recreates the session under a **new manifest URL** (the old playlist freezes at HTTP 200), so the player keeps the live-state subscription open after `ended` (until `gone`) and re-arms on a pushed `live`. Set `false` to keep `ended` terminal. Needs `liveStateEvents`.
|
|
261
|
-
| `analytics` | `boolean \| { endpoint?: string }` | `true` | QoE beacons. Active only when the playback response carries a server-minted `sid`. **havik-streams v0.10.0+ names the beacons host in that response, and it is used whatever `baseUrl` is**; it also wins over `{ endpoint }` (a differing value is ignored with one `console.warn`). Against an older server the host is `{ endpoint }` if given, else derived from `baseUrl` by hostname convention (`feed[-dev].<domain>` → `beacons[-dev].<domain>`); there, **if `baseUrl` is not a `feed` host nothing derives and beacons are disabled** — the SDK then logs one `console.warn` naming the host and emits a non-fatal [`warning`](#player-events) event carrying the same sentence. Recovery: pass `{ endpoint: 'https://beacons[-dev].<domain>' }`. Pass `false` to opt out deliberately (no warning).
|
|
262
|
-
| `eventsBaseUrl` | `string` | derived | Override the SSE endpoint base. Default: `baseUrl` with the host prefixed by `events.` (e.g. `feed.<d>` → `events.<d>`).
|
|
223
|
+
| Option | Type | Default | Description |
|
|
224
|
+
| ------------------------- | --------------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
225
|
+
| `video` | `HTMLVideoElement` | — | **Required.** The element the SDK will control. |
|
|
226
|
+
| `env` | `'integration' \| 'production'` | — | **Required unless `baseUrl` is given.** Which Oddin environment to play against. Preferred: the host is correct by construction, and so is everything derived from it (the SSE events host, the QoE beacons host). |
|
|
227
|
+
| `baseUrl` | `string` | — | **Required unless `env` is given.** API base URL, e.g. `https://feed-dev.oddin-video.gg`. Use for a deployment the named environments do not cover: your own CDN or gateway fronting the streams API, the CN plane, or a pinned build that must outlive a domain change. Passing both `env` and `baseUrl` — or neither — is a compile error in TypeScript and throws at runtime in JavaScript. |
|
|
228
|
+
| `matchUrn` | `string` | — | **Required.** The match to play, e.g. `od:match:1234`. |
|
|
229
|
+
| `credential` | [`CredentialSource`](#credentialsource) | — | **Required.** Your api‑key (or a function returning one). |
|
|
230
|
+
| `autoplay` | `boolean` | `true` | Start playback once ready. With `false`, the player holds in a pre-play `'paused'` state (poster + play card, no spinner) and fetches no media and no DRM license until the first `play()`. On the hls.js engine the manifest is still parsed pre-play, so `'ready'` and the track lists keep working; the native (Safari) engine defers all fetching and emits `'ready'` at attach instead. Note: if a programmatic `play()` is policy-blocked (`autoplayblocked`), the loader keeps warming the buffer — media and DRM traffic flows from that kick, not from a viewer gesture. |
|
|
231
|
+
| `muted` | `boolean` | `false` | Mute the element. Set `true` for reliable autoplay under browser sound policies. |
|
|
232
|
+
| `controls` | `'custom' \| 'native' \| 'none'` | `'custom'` | Which controls to render. See [Controls & theming](#controls--theming). |
|
|
233
|
+
| `theme` | [`Partial<HavikTheme>`](#haviktheme) | Oddin brand | Re-skin the custom control bar (ignored unless `controls: 'custom'`). See [Controls & theming](#controls--theming). |
|
|
234
|
+
| `statusOverlays` | `boolean` | `true` | Branded full-bleed cards for the stopped/pre-play states (ended, fatal error, pre-play, armed-waiting) instead of a frozen frame + a dead play button. Themed via the control-bar tokens + `theme.logoUrl`. Set `false` to render your own UI from the `ended`/`error` events. Ignored unless `controls: 'custom'`. |
|
|
235
|
+
| `cspNonce` | `string` | — | Your page's CSP nonce, stamped on the `<style>` tag the custom control bar injects. Needed only when `style-src` has no `'unsafe-inline'`: without it the browser blocks the tag and the controls render unstyled. See [Content-Security-Policy](#content-security-policy). Ignored unless `controls: 'custom'`. |
|
|
236
|
+
| `endedMessage` | `string` | "This stream has ended" | Message on the end-of-stream card. |
|
|
237
|
+
| `interruptedMessage` | `string` | "Reconnecting — please stand by" | Message shown **instead of** `endedMessage` when the server reports the stream stopped but the match is still running (`endedReason: 'interrupted'`, an ingest outage that outlived the bridge's hold). The player rejoins automatically when the stream returns **while `rejoinOnLive` is on** (the default); override this wording if you disable auto-rejoin, since nothing will re-attach on its own. |
|
|
238
|
+
| `errorMessage` | `string` | the error's message | Message on the fatal-error card (otherwise the error's own message). |
|
|
239
|
+
| `offlineMessage` | `string` | "No internet connection — waiting to reconnect" | Message on the card shown while the browser reports no network (`navigator.onLine === false`). It replaces the error and reconnecting cards for as long as the browser says offline and carries no Retry button: nothing can succeed without a route, and the player resumes by itself on the browser's `online` event. See [Connectivity](#connectivity). |
|
|
240
|
+
| `waitForLive` | [`WaitForLive`](#waitforlive) | — | Arm on an upcoming match and auto‑attach at go‑live. |
|
|
241
|
+
| `lowLatency` | `'auto' \| boolean` | `'auto'` | `'auto'` lets the player decide from the manifest. |
|
|
242
|
+
| `engine` | `'auto' \| 'hls' \| 'native'` | `'auto'` | `'auto'` uses the browser's native player on iPhone and iPad for every stream, and for FairPlay on any Apple browser; elsewhere it prefers hls.js (desktop Safari included) and falls back to native HLS. hls.js on iOS runs on ManagedMediaSource, which closed mid‑session and played slower than real time in testing. |
|
|
243
|
+
| `userId` | `string` | — | Optional viewer id (sent as `X-User-Id` on license requests). |
|
|
244
|
+
| `debug` | `boolean` | `false` | Turn on hls.js's internal console logging: fragment/part scheduling, ABR switches, EME key sessions and license traffic. For diagnosing a playback problem with Oddin support; leave it off in production (it logs on every fragment, and the EME lines include license request/response metadata). hls.js engine only. `hlsConfig.debug` still wins if you set both. |
|
|
245
|
+
| `hlsConfig` | `Partial<HlsConfig>` | — | Escape hatch for advanced hls.js tuning. |
|
|
246
|
+
| `statsIntervalMs` | `number` | `2000` | How often `'stats'` is emitted. |
|
|
247
|
+
| `licenseRefreshMs` | `number` | `480000` (8 min) | How often to silently re-resolve playback so the signed DRM license URL the player would use next stays inside its signature lifetime (~10 min). Refreshes the URL, not the license: it cannot extend a license the CDM already holds, so a session that outlasts its license still ends with a fatal [`DRM_CLIENT`](#errors) error. `0` disables the timer only: a lapsed URL is still refreshed on refocus and before a license request (Widevine on hls.js; the native FairPlay engine has neither check). |
|
|
248
|
+
| `poster` | `string` | — | Poster image shown before playback starts. |
|
|
249
|
+
| `startLevel` | `number` | `-1` | Initial rendition index (`-1` = auto). With auto, ABR starts on a conservative, sustainable rendition (~360p) and ramps up as bandwidth allows; see [Low‑latency & adaptive bitrate](#low-latency--adaptive-bitrate). |
|
|
250
|
+
| `maxBitrate` | `number` | — | Cap ABR to renditions at/below this bitrate (bps). |
|
|
251
|
+
| `liveLatencyTarget` | `number` | `2` | Target live‑edge latency in seconds (hls.js `liveSyncDuration`). The player derives a matching latency **ceiling** internally and catches up at 1.1×, so this is the only live‑latency knob you need. Lower = closer to live but more rebuffer‑prone. Live/LL only. |
|
|
252
|
+
| `adaptiveHoldBack` | `boolean` | `true` | hls.js engine: raise the live‑sync target for a session whose part fetches are slow, instead of letting it stall. A viewer far from the origin plays from a local edge whose every cache miss on a 0.5 s part is a cross‑continent fetch (250–310 ms per part from South America, Canada/Mexico and APAC on the global slot, against 44 ms in Europe), so the fetch cycle outruns the part cadence and the 2 s buffer drains. The session's own part fetches are timed (first byte, as a share of the part's duration, over a 10 s window): a window whose p95 is at or above 60% **and** whose median is at or above 40%, two 3 s ticks running, steps the target up 0.5 s, to `adaptiveHoldBackCeiling` at most; a p95 under 40% for 60 s steps it back down. A session whose fetches are fast never moves. Each move fires [`latencytargetchange`](#player-events), counts in `holdBackSteps`, and shows in `targetLatencySeconds`. Live/LL only. |
|
|
253
|
+
| `adaptiveHoldBackCeiling` | `number` | `3.5` | Highest live‑sync target the adaptive hold‑back may step to, in seconds. Kept a second under the drift ceiling; at or below `liveLatencyTarget` it disables the stepping. |
|
|
254
|
+
| `snapToLiveOnRefocus` | `boolean` | `true` | Snap to the live edge when a backgrounded tab is refocused: on hls.js while far behind; on the native player (iPhone, iPad, FairPlay) on every return after 2 s or more away and on every back/forward‑cache restore, 3 s behind the edge once the first playlist refresh after the return moves it (up to 6 s wait; the edge on return is still the one from before leaving), which also redraws a picture iOS left black. Live/LL only. |
|
|
255
|
+
| `rememberBandwidth` | `boolean` | `true` | hls.js engine: start from the bandwidth this device measured in a recent session (`localStorage` key `havik.player.bandwidth`, per connection type, up to 3 days old, reused at 80%) instead of the conservative ~1 Mbps start; a slow record starts from that lower figure (0.3 Mbps floor). A first visit, a stale record or blocked storage keeps the conservative start; an explicit `hlsConfig.abrEwmaDefaultEstimate` or `startLevel` wins. Reset with [`clearRememberedBandwidth()`](#clearrememberedbandwidth). |
|
|
256
|
+
| `sizeCapHeadroom` | `number` | unset | hls.js engine: rendition rungs ABR may pick above the one that merely covers the player's pixel box. Unset: `1` on displays below 2× `devicePixelRatio`, **earned per session on LL-HLS** (its parts download within half a part duration even at that rung's bitrate, the bandwidth estimate is twice the rung and stays above 1.5×, and no stall in the last 2 minutes; a stall after the first 10 s of playback drops the rung at once; streams without parts get the rung as before), `0` at 2× and above. A 960 px box at 1x covers with 960×540; one rung of headroom plays 720p downscaled instead, which looks sharper for the same box; phones and 2× screens keep the cover-only cap (their covering rung already matches their device pixels; a rung above it mostly costs data). An explicit value applies on every display: `0` = strict cover-only cap, `Infinity` = no size cap. Never past the top rung; `maxBitrate` and the frame-drop cap still apply. `hlsConfig.capLevelToPlayerSize: true` caps to the covering rung with no headroom. |
|
|
257
|
+
| `mediaDuringKey` | `boolean` | `true` | DRM cold start: download the first init segment and parts while the first key is still being acquired, instead of after it. Bytes reach hls.js only when it asks for them; only the network time moves off the critical path (~0.5 s on Widevine LL-HLS). Ignored on clear streams and when `hlsConfig.fLoader` is set. Pass `false` to opt out. |
|
|
258
|
+
| `preloadHints` | `boolean` | `true` | Live LL-HLS, hls.js engine: fetch the part each playlist's `EXT-X-PRELOAD-HINT` names as soon as the playlist arrives, instead of after the next playlist lists it. The origin and CDNs hold that request until the part exists, so a viewer far from the origin saves about one round trip per part (measured from São Paulo: late parts at a 3 s live distance went from 100 % to 0 %). Bytes reach hls.js only when it asks for the part; an unrequested hint is dropped after 4 s or on a rendition switch. Off while paused and while loading behind the live edge. Ignored when `hlsConfig.fLoader` is set. Pass `false` to opt out. |
|
|
259
|
+
| `liveStateEvents` | `boolean` | `true` | Use the push-based **live-state SSE** channel for go-live and end-of-stream (see [Live state](#live-state-sse)). Graceful: falls back to polling if the endpoint is unreachable; a 410 (match gone) stops the subscription and a 404 (not in the catalog) slows it to one retry every 5 min, each with one `warning`. On a return from the background (5 s or more away, or a back/forward-cache restore) the subscription is reopened so the server replays the current state: a page suspended through the end of a match does not sit buffering on a socket that will never deliver it. Set `false` to disable. |
|
|
260
|
+
| `rejoinOnLive` | `boolean` | `true` | Rejoin automatically when a stream that ended comes back live. An ingest outage longer than the bridge's reconnect grace recreates the session under a **new manifest URL** (the old playlist freezes at HTTP 200), so the player keeps the live-state subscription open after `ended` (until `gone`) and re-arms on a pushed `live`. Set `false` to keep `ended` terminal. Needs `liveStateEvents`. |
|
|
261
|
+
| `analytics` | `boolean \| { endpoint?: string }` | `true` | QoE beacons. Active only when the playback response carries a server-minted `sid`. **havik-streams v0.10.0+ names the beacons host in that response, and it is used whatever `baseUrl` is**; it also wins over `{ endpoint }` (a differing value is ignored with one `console.warn`). Against an older server the host is `{ endpoint }` if given, else derived from `baseUrl` by hostname convention (`feed[-dev].<domain>` → `beacons[-dev].<domain>`); there, **if `baseUrl` is not a `feed` host nothing derives and beacons are disabled** — the SDK then logs one `console.warn` naming the host and emits a non-fatal [`warning`](#player-events) event carrying the same sentence. Recovery: pass `{ endpoint: 'https://beacons[-dev].<domain>' }`. Pass `false` to opt out deliberately (no warning). |
|
|
262
|
+
| `eventsBaseUrl` | `string` | derived | Override the SSE endpoint base. Default: `baseUrl` with the host prefixed by `events.` (e.g. `feed.<d>` → `events.<d>`). |
|
|
263
263
|
|
|
264
264
|
### Low-latency & adaptive bitrate
|
|
265
265
|
|
|
@@ -320,7 +320,7 @@ don't need to set any of the live or ABR knobs:
|
|
|
320
320
|
80%, never below 0.3 Mbps). A first visit or a device with no usable record
|
|
321
321
|
keeps the ~1 Mbps start. In addition,
|
|
322
322
|
the session is bounded to the player's pixel box **plus one rung of
|
|
323
|
-
headroom
|
|
323
|
+
headroom, once the session shows it can carry it,** on displays below 2× `devicePixelRatio` (`sizeCapHeadroom`; set
|
|
324
324
|
it explicitly to apply a value on every display), so a small/embedded player won't fetch 1080p, while a 960 px
|
|
325
325
|
desktop embed gets 720p downscaled rather than 540p at 1:1 (a lower encode
|
|
326
326
|
shown pixel‑for‑pixel looks softer than a higher one scaled down; measured on
|
|
@@ -494,14 +494,19 @@ failure while "online" keeps its ordinary retry path.
|
|
|
494
494
|
|
|
495
495
|
What happens on a network loss, by layer:
|
|
496
496
|
|
|
497
|
-
- **hls.js engine**:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
The
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
497
|
+
- **hls.js engine**: the browser's `offline` event engages a hold at once: the
|
|
498
|
+
end-of-stream and starvation watchdogs stand down (a playlist that cannot be
|
|
499
|
+
fetched is not a stream that ended), and a fatal network error with no HTTP
|
|
500
|
+
status parks the loader instead of spending the five-attempt retry budget.
|
|
501
|
+
The hold keys off the browser's signal because hls.js itself may never raise
|
|
502
|
+
a fatal error during an outage: measured in Chromium, it sits in its own
|
|
503
|
+
playlist-retry backoff for the whole of a 20 s loss. The `online` event
|
|
504
|
+
releases the hold, refunds the budget and restarts the loader right away,
|
|
505
|
+
with hls.js, the buffer and the DRM key sessions all still in place: measured
|
|
506
|
+
in Chromium against a local live stream, the offline card is up 5 ms after
|
|
507
|
+
the loss and playback is back 120 ms after the link returns, through one
|
|
508
|
+
resolve for the whole session. A failure that carries a status is the
|
|
509
|
+
origin's answer and is never parked.
|
|
505
510
|
- **Native engine (Safari)**: the element reports one opaque error for
|
|
506
511
|
everything; while the browser says offline it is typed `NETWORK`, and the
|
|
507
512
|
managed player re-attaches on `online`.
|
package/embed/index.html
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
beacons endpoint can be derived and QoE analytics is silently off.
|
|
29
29
|
`analytics` is honoured from the injected config only, never the query.
|
|
30
30
|
-->
|
|
31
|
-
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.
|
|
31
|
+
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.26.0/dist/havik-player.global.js" integrity="sha384-1HYWmsnn7efLQVu3AhmVnoQswabKOszRPUc7YJv3iCs8lEIlRTUNHg3arv3eZq20" crossorigin="anonymous"></script>
|
|
32
32
|
<script>
|
|
33
33
|
HavikPlayer.bootEmbed();
|
|
34
34
|
</script>
|
package/examples/vanilla.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
shipped in each npm release is pinned to that release with Subresource
|
|
13
13
|
Integrity, injected at publish time (scripts/inject-sri.mjs). In
|
|
14
14
|
production, always use a pinned + SRI'd tag like the released copy. -->
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.
|
|
15
|
+
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.26.0/dist/havik-player.global.js" integrity="sha384-1HYWmsnn7efLQVu3AhmVnoQswabKOszRPUc7YJv3iCs8lEIlRTUNHg3arv3eZq20" crossorigin="anonymous"></script>
|
|
16
16
|
<script>
|
|
17
17
|
(async () => {
|
|
18
18
|
const player = await HavikPlayer.createPlayer({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oddin-gg/havik-player",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Havik HTML5 player SDK — LL-HLS + DRM playback for Oddin live streams (managed, bring-your-own-player, and iframe-embed modes).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|