@oddin-gg/havik-player 2.15.1 → 2.15.3

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/docs/API.md CHANGED
@@ -246,7 +246,7 @@ player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));
246
246
  | `poster` | `string` | — | Poster image shown before playback starts. |
247
247
  | `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). |
248
248
  | `maxBitrate` | `number` | — | Cap ABR to renditions at/below this bitrate (bps). |
249
- | `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.5×, so this is the only live‑latency knob you need. Lower = closer to live but more rebuffer‑prone. Live/LL only. |
249
+ | `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. |
250
250
  | `snapToLiveOnRefocus` | `boolean` | `true` | Snap to the live edge when a backgrounded tab is refocused while far behind. Live/LL only. |
251
251
  | `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. |
252
252
  | `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. Set `false` to disable. |
@@ -265,7 +265,9 @@ don't need to set any of the live or ABR knobs:
265
265
  aren't reliably published yet; chasing that edge causes constant rebuffering.
266
266
  Set `liveLatencyTarget` lower for closer‑to‑live (more rebuffer‑prone) or
267
267
  higher for steadier playback. _Lowered from 3 s; see the migration note below._
268
- - **Drift ceiling**: after a stall, the player catches up at up to **1.5×** and,
268
+ - **Drift ceiling**: after a stall, the player catches up at up to **1.1×** (gentle
269
+ enough to be invisible; it was 1.5×, which showed as the picture speeding up
270
+ between stalls on a jittery edge) and,
269
271
  as a backstop, force‑seeks back toward the target once latency exceeds an
270
272
  internal **ceiling (~12 s, derived as `max(12, liveLatencyTarget + 9)`)**. This is what
271
273
  stops latency from snowballing to tens of seconds after buffering. The ceiling
@@ -313,12 +315,23 @@ can't crash the player: if you pass the count‑based `liveSyncDurationCount` /
313
315
  rejects mixing the two); if you set `liveSyncDuration` alone, a matching ceiling
314
316
  is derived above it.
315
317
 
316
- - **Futile catch‑up is stopped.** hls.js speeds playback up (to 1.5×) whenever
318
+ - **Quality steps down when the device can't decode it.** If the browser keeps
319
+ dropping more than 20% of frames for two 3 s checks in a row, the SDK caps
320
+ ABR one rendition below the one playing (counted in `fpsDownCaps`). This is
321
+ what keeps a laptop without hardware decoding from showing a stuttering
322
+ picture that drifts away from the sound. It folds into the same cap as the
323
+ player‑size and `maxBitrate` limits, never applies to a quality the viewer
324
+ pinned, and stays down for the session. Setting
325
+ `hlsConfig.capLevelOnFPSDrop: false` turns frame‑drop capping off; `true` runs
326
+ hls.js's own controller instead, whose cap a later player‑size or `maxBitrate`
327
+ recompute can overwrite.
328
+
329
+ - **Futile catch‑up is stopped.** hls.js speeds playback up (to 1.1×) whenever
317
330
  latency sits above the target, and only lowers its ambition after a counted
318
331
  rebuffer. On an edge that is itself seconds behind, a player that keeps a
319
- deeper buffer (Safari) can chase at 1.5× for a whole session without ever
332
+ deeper buffer (Safari) can chase at full rate for a whole session without ever
320
333
  gaining a second, which viewers see as constant stutter. The SDK watches the
321
- chase on its 3 s tick: a chase at ≥1.2× that gains under 0.5 s of latency
334
+ chase on its 3 s tick: a chase at the full 1.1× that gains under 0.5 s of latency
322
335
  over the next three ticks (12 s in all) moves the target up to where playback
323
336
  actually is (never closer than 1 s to the drift ceiling), and the rate drops
324
337
  back to 1.0×. One‑way for the session; `targetLatencySeconds` reflects the
@@ -331,7 +344,7 @@ is derived above it.
331
344
  > `liveLatencyTarget: 3` explicitly.
332
345
 
333
346
  > **Migration note:** `liveCatchUpRate` was **removed** as a public option; the
334
- > catch‑up rate (1.5×) is now managed internally alongside the drift ceiling. If
347
+ > catch‑up rate (1.1×) is now managed internally alongside the drift ceiling. If
335
348
  > you set it before, drop it; reach for `hlsConfig.maxLiveSyncPlaybackRate` only
336
349
  > if you genuinely need to override the rate.
337
350
 
@@ -1244,6 +1257,8 @@ interface PlaybackStats {
1244
1257
  lowLatency?: boolean; // whether LL-HLS was detected in the manifest
1245
1258
  isLive?: boolean; // current media playlist is live
1246
1259
  atLiveEdge?: boolean; // playback is at/near the live edge
1260
+ liveRetargets?: number; // times a futile catch-up moved the live target (hls.js engine)
1261
+ fpsDownCaps?: number; // times quality was capped because the device dropped frames (hls.js engine)
1247
1262
  }
1248
1263
  ```
1249
1264
 
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.15.1/dist/havik-player.global.js" integrity="sha384-QfwGP+4bjAqO+AlPAKFvmkfq4JLAYGaAFgCPt2WDyaxn4Qf7vLZrPo4fwwoy0pDQ" crossorigin="anonymous"></script>
31
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.15.3/dist/havik-player.global.js" integrity="sha384-6QXvumRSWQy+koXgkH0D46YFSKo9W6Pw1Cc6AZfBWxqa8MpAc3T7HKiMHHBTGsZh" crossorigin="anonymous"></script>
32
32
  <script>
33
33
  HavikPlayer.bootEmbed();
34
34
  </script>
@@ -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.1/dist/havik-player.global.js" integrity="sha384-QfwGP+4bjAqO+AlPAKFvmkfq4JLAYGaAFgCPt2WDyaxn4Qf7vLZrPo4fwwoy0pDQ" crossorigin="anonymous"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@2.15.3/dist/havik-player.global.js" integrity="sha384-6QXvumRSWQy+koXgkH0D46YFSKo9W6Pw1Cc6AZfBWxqa8MpAc3T7HKiMHHBTGsZh" 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.15.1",
3
+ "version": "2.15.3",
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,