@banou/media-player 0.10.10 → 0.10.12

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.
@@ -1,2 +1,2 @@
1
- import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-C6PdWBah.js";
1
+ import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "../engine-DKjUYAmJ.js";
2
2
  export { i as DEFAULT_BUFFER_SIZE, e as MediaElementError, a as SUBTITLES_OFF, u as createPictureInPicture, d as createSubtitleRenderer, c as createThumbnailGenerator, n as getTimeRanges, s as isMediaElementError, o as pictureInPictureMode, l as startPlayback, t as terminateRemuxer, r as updateSourceBuffer };
@@ -244,8 +244,9 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
244
244
  t && r || (console.error(e), (!K || n) && (K = !0, ne?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
245
245
  }, fe = async (e = !1) => {
246
246
  if (R > 0 && !e) return;
247
- let t = n.currentTime, r = e ? x : v, a = e ? S : y;
248
- for (let { start: e, end: n } of i(A)) e < t + r && await se(e, t + r), n > t + a && await se(t + a, n);
247
+ let t = n.currentTime, r = e ? x : v, a = e ? S : y, o = i(A);
248
+ o.length && o[0].start < t + r && await se(0, t + r);
249
+ for (let { end: e } of o) e > t + a && await se(t + a, e);
249
250
  }, pe = () => {
250
251
  let e = i(A);
251
252
  if (!e.length) return !0;
@@ -305,11 +306,11 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
305
306
  } finally {
306
307
  e === H && (I = !1);
307
308
  }
308
- }, ye = (e) => e >= de && !!me(), be = (e) => i(A).some((t) => t.start <= e + T && e < t.end), Y = (e) => {
309
+ }, ye = (e) => e >= de && be(e), be = (e) => i(A).some((t) => t.start <= e && e < t.end), Y = (e) => {
309
310
  let t = i(A).find((t) => t.start <= e + T && e < t.end);
310
311
  return t ? t.end - e : 0;
311
312
  }, xe = async (e) => {
312
- if (!N && !(Y(e) >= E)) {
313
+ if (!N && !(be(e) && Y(e) >= E)) {
313
314
  V = !1, R++, z = e;
314
315
  try {
315
316
  be(e) || await J(e);
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { l as e, n as t, o as n, r, s as i, t as a, u as o } from "./engine-C6PdWBah.js";
1
+ import { l as e, n as t, o as n, r, s as i, t as a, u as o } from "./engine-DKjUYAmJ.js";
2
2
  import { Children as s, Fragment as c, isValidElement as l, useCallback as u, useEffect as d, useMemo as f, useRef as p, useState as m, useSyncExternalStore as h } from "react";
3
3
  import { css as g, keyframes as _ } from "@emotion/react";
4
4
  import { createPlayer as v, useContainerAttach as y, useMediaAttach as b } from "@videojs/react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@banou/media-player",
3
- "version": "0.10.10",
3
+ "version": "0.10.12",
4
4
  "description": "A video player for containers and codecs the browser cannot play natively, remuxed on the fly",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -102,7 +102,15 @@ const PRE_EVICT_TIGHT = -5
102
102
  const POST_EVICT_TIGHT = 20
103
103
  const MAX_APPEND_ATTEMPTS = 5
104
104
  const SOURCE_OPEN_TIMEOUT = 15_000
105
- // how far past the playhead a range may start and still count as the one holding it
105
+ /*
106
+ * How far past the playhead a range may start and still count as the one holding it, which decides how
107
+ * much to read and never whether to fetch.
108
+ *
109
+ * Firefox 151 reports a range from its first presented frame, the reorder delay past its keyframe: 82 ms
110
+ * on most fixtures and 166 ms on anime-chapters, at load and after every remuxer seek, where Chrome 153
111
+ * reports the keyframe itself. Without the slack a playhead paused at 0 reads the whole file, and a
112
+ * prepared seek to a keyframe builds no runway (measured 2026-10-11).
113
+ */
106
114
  const BOUNDARY_SLACK = 1
107
115
  /*
108
116
  * Seconds of data a seek target needs behind it before the playhead is allowed to move there.
@@ -304,8 +312,12 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
304
312
  const ct = videoElement.currentTime
305
313
  const pre = tight ? PRE_EVICT_TIGHT : PRE_EVICT
306
314
  const post = tight ? POST_EVICT_TIGHT : POST_EVICT
307
- for (const { start, end } of getTimeRanges(sourceBuffer)) {
308
- if (start < ct + pre) await unbufferRange(start, ct + pre)
315
+ const ranges = getTimeRanges(sourceBuffer)
316
+ // From 0, never from the reported start: once a group is removed, Firefox 151 reports the start
317
+ // 1 ms after the keyframe it still holds, so a remove from there keeps that frame for good
318
+ // (measured 2026-10-11).
319
+ if (ranges.length && ranges[0].start < ct + pre) await unbufferRange(0, ct + pre)
320
+ for (const { end } of ranges) {
309
321
  if (end > ct + post) await unbufferRange(ct + post, end)
310
322
  }
311
323
  }
@@ -447,11 +459,17 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
447
459
  * seek, and playback would stall at the end of this range instead. At or after it, the bytes from
448
460
  * here to the read cursor are contiguous and reading simply carries on.
449
461
  */
450
- const alreadyPlayable = (time: number) => time >= lastSeekPosition && !!playheadRange()
462
+ const alreadyPlayable = (time: number) => time >= lastSeekPosition && playableAt(time)
451
463
 
452
- /** Whether a GIVEN time has data behind it, as opposed to whether the playhead does. */
464
+ /**
465
+ * Whether the element can play from a GIVEN time without a fetch.
466
+ *
467
+ * Exact. A seek that lands before a range waits there for good once the gap passes 60 to 90 ms in
468
+ * Chrome 153, or 250 to 300 ms in Firefox 151, so Firefox's reorder delay cannot be allowed for here
469
+ * and a target inside it costs a remuxer seek (24 fps fixtures, measured 2026-10-11).
470
+ */
453
471
  const playableAt = (time: number) =>
454
- getTimeRanges(sourceBuffer).some((r) => r.start <= time + BOUNDARY_SLACK && time < r.end)
472
+ getTimeRanges(sourceBuffer).some((r) => r.start <= time && time < r.end)
455
473
 
456
474
  /**
457
475
  * Put the data in place for a position the playhead has NOT moved to yet.
@@ -478,7 +496,7 @@ export const startPlayback = async (options: PlaybackOptions): Promise<PlaybackC
478
496
 
479
497
  const prepareSeek = async (time: number) => {
480
498
  if (destroyed) return
481
- if (runwayFrom(time) >= SEEK_RUNWAY) return
499
+ if (playableAt(time) && runwayFrom(time) >= SEEK_RUNWAY) return
482
500
  finished = false
483
501
  preparing++
484
502
  prepareTarget = time