@banou/media-player 0.10.5 → 0.10.7

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
@@ -3,7 +3,7 @@
3
3
  # @banou/media-player
4
4
 
5
5
  A React video player for files the browser cannot open on its own. It takes a `read(offset, size)` and
6
- a byte length, remuxes into fragmented MP4 as it plays through [libav-wasm](https://github.com/Banou26/libav-wasm),
6
+ a byte length, remuxes into fragmented MP4 as it plays through [libav-wasm](https://github.com/banou26/libav-wasm),
7
7
  and renders ASS/SSA subtitles with jassub. Nothing is downloaded up front, so it plays a 4 GB MKV over
8
8
  HTTP range requests, out of a torrent, off a local disk, or out of anything else that can answer for a
9
9
  byte range.
@@ -208,6 +208,30 @@ play/pause button.
208
208
  If anything in that path is unavailable the player falls back to handing the browser the bare video,
209
209
  which plays without subtitles rather than not at all.
210
210
 
211
+ ### Picture in picture for a media you do not own
212
+
213
+ A browser opens picture in picture only for a click in the document that owns the `<video>`, so a
214
+ remote media gets the control only when the host opts in to taking that click:
215
+
216
+ ```tsx
217
+ // `.frame` sets `pointer-events: none`, so clicks reach the chrome everywhere else
218
+ <MediaPlayer
219
+ media={media}
220
+ pictureInPicture={{ onArmedChange: (armed) => { frame.current!.style.pointerEvents = armed ? 'auto' : '' } }}
221
+ >
222
+ <iframe ref={frame} className='frame' />
223
+ </MediaPlayer>
224
+ ```
225
+
226
+ While the pointer is over the control, the chrome lets a click through at the control's box and the
227
+ host is told it is armed, so its frame takes that click and enters picture in picture from inside.
228
+ The control follows the media's `enterpictureinpicture` and `leavepictureinpicture` events, and in
229
+ picture in picture a click on it calls the media's `exitPictureInPicture`.
230
+
231
+ A finger never rests on the control before it presses, so a tap cannot arm it. The control is offered
232
+ only where the primary pointer hovers (`(hover: hover) and (pointer: fine)`), and a touch hides it
233
+ until a pointer that hovers moves again.
234
+
211
235
  ## Layout
212
236
 
213
237
  One package. `src/` is the demo app, `src/lib` is the library it publishes, and the app imports it by
@@ -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-CB5J5kah.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-C6PdWBah.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 };
@@ -75,7 +75,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
75
75
  x || (x = !0, queueMicrotask(() => {
76
76
  x = !1;
77
77
  let e = l;
78
- !e || !t.videoWidth || e.ready.then(() => {
78
+ e && t.videoWidth && e.ready.then(() => {
79
79
  if (l === e) return e.manualRender({
80
80
  expectedDisplayTime: performance.now(),
81
81
  width: t.videoWidth,
@@ -241,7 +241,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
241
241
  await F(e.data), e.subtitles?.length && s.pushFragments(e.subtitles), te?.();
242
242
  let I = !1, L = !1, R = 0, z, B = () => z ?? n.currentTime, V = !1, H = 0, U = 0, W = null, G = 0, ue = 0, K = !1, de = 0, q = (e, t, n = !1) => {
243
243
  let r = e?.message === "Cancelled";
244
- t && r || (console.error(e), !(K && !n) && (K = !0, ne?.(r ? Error("Reading the video file failed", { cause: e }) : e)));
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
247
  let t = n.currentTime, r = e ? x : v, a = e ? S : y;
@@ -521,7 +521,7 @@ var i = (e) => Array(e.buffered.length).fill(void 0).map((t, n) => ({
521
521
  }, g = "requestVideoFrameCallback" in t, _ = () => {
522
522
  h(), v();
523
523
  }, v = () => {
524
- d || !a || (o = g ? t.requestVideoFrameCallback(_) : requestAnimationFrame(_));
524
+ !d && a && (o = g ? t.requestVideoFrameCallback(_) : requestAnimationFrame(_));
525
525
  }, y = () => {
526
526
  o !== void 0 && (g ? t.cancelVideoFrameCallback(o) : cancelAnimationFrame(o)), s !== void 0 && clearInterval(s), o = void 0, s = void 0;
527
527
  }, b = () => {
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  export { MediaPlayer, default } from './react/video-player';
2
2
  export type { MediaPlayerOptions, MediaPlayerLocalOptions, MediaPlayerRemoteOptions, MediaPlayerSource, } from './react/video-player';
3
3
  export { isDelegatedTracks, isExternalThumbnails } from './react/media';
4
- export type { DelegatedSelection, DelegatedTracks, ExternalThumbnails, PlayerMedia, TimeRangesLike, } from './react/media';
4
+ export type { DelegatedSelection, DelegatedTracks, ExternalThumbnails, PassThroughPictureInPicture, PlayerMedia, TimeRangesLike, } from './react/media';
5
5
  export { Player, usePlayer } from './react/player';
6
6
  export type { PlayerStore } from './react/player';
7
7
  export { sourceFeature } from './react/source-feature';
8
- export type { DownloadedRange, PlaybackErrorEntry, SourceState } from './react/source-feature';
8
+ export type { DownloadedRange, PassThroughControl, PlaybackErrorEntry, SourceState, ViewportBox, } from './react/source-feature';
9
9
  export { useSeekThumbnails } from './react/hooks/use-thumbnails';
10
10
  export { usePictureInPicture } from './react/hooks/use-picture-in-picture';
11
11
  export { inputToRemuxerInput } from './utils/source';