usehlsvideoplayer 2.0.0 → 2.0.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/CHANGELOG.md +6 -0
- package/README.md +38 -59
- package/SECURITY.md +1 -9
- package/docs/api.md +103 -95
- package/docs/compatibility.md +10 -20
- package/docs/migration.md +30 -15
- package/docs/troubleshooting.md +19 -0
- package/docs/usage.md +161 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
- Added a shorter React quick start and a usage guide for core playback, live controls, recovery, and authenticated streams.
|
|
6
|
+
- Separated the API reference, browser compatibility, troubleshooting, and migration guides.
|
|
7
|
+
- Restored the build toolchain used for 2.0.0 to fix clean development installs.
|
|
8
|
+
|
|
3
9
|
## 2.0.0
|
|
4
10
|
|
|
5
11
|
- Added an independent playback controller and native and hls.js adapters.
|
package/README.md
CHANGED
|
@@ -1,74 +1,53 @@
|
|
|
1
1
|
# usehlsvideoplayer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Play HLS streams in an HTML video element with React hooks or a TypeScript controller. You supply the video element and choose between the browser's controls and your own.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Play a stream in React
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
1. Install the package in a React app. The React entry supports React 18.2 and React 19.
|
|
8
8
|
|
|
9
|
-
```sh
|
|
10
|
-
npm install usehlsvideoplayer
|
|
11
|
-
```
|
|
9
|
+
```sh
|
|
10
|
+
npm install usehlsvideoplayer
|
|
11
|
+
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
"use client";
|
|
17
|
-
|
|
18
|
-
import { useHlsPlayer } from "usehlsvideoplayer";
|
|
19
|
-
|
|
20
|
-
export function StreamPlayer({ src }: { src: string }) {
|
|
21
|
-
const {
|
|
22
|
-
videoRef,
|
|
23
|
-
state,
|
|
24
|
-
controls,
|
|
25
|
-
quality,
|
|
26
|
-
audio,
|
|
27
|
-
subtitles,
|
|
28
|
-
live,
|
|
29
|
-
metrics,
|
|
30
|
-
error,
|
|
31
|
-
} = useHlsPlayer({ src, autoPlay: false, lowLatency: true });
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<>
|
|
35
|
-
<video ref={videoRef} controls playsInline />
|
|
36
|
-
<button onClick={() => void controls.play()}>Play</button>
|
|
37
|
-
<button disabled={!live.canSeekToLive} onClick={controls.seekToLive}>
|
|
38
|
-
Seek to live
|
|
39
|
-
</button>
|
|
40
|
-
<p role="status">{state.status}</p>
|
|
41
|
-
{error?.fatal && state.status === "error" && (
|
|
42
|
-
<p role="alert">{error.message}</p>
|
|
43
|
-
)}
|
|
44
|
-
</>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
```
|
|
13
|
+
2. Create `StreamPlayer.tsx` with this component.
|
|
48
14
|
|
|
49
|
-
|
|
15
|
+
```tsx
|
|
16
|
+
"use client";
|
|
50
17
|
|
|
51
|
-
|
|
18
|
+
import { useHlsPlayer } from "usehlsvideoplayer";
|
|
52
19
|
|
|
53
|
-
|
|
54
|
-
|
|
20
|
+
export function StreamPlayer({ src }: { src: string }) {
|
|
21
|
+
const { videoRef, state, error } = useHlsPlayer({ src });
|
|
55
22
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<video ref={videoRef} controls playsInline />
|
|
26
|
+
<p role="status">{state.status}</p>
|
|
27
|
+
{state.status === "error" && error && (
|
|
28
|
+
<p role="alert">{error.message}</p>
|
|
29
|
+
)}
|
|
30
|
+
</>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
59
34
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
35
|
+
3. Render `<StreamPlayer src="/streams/master.m3u8" />` where you want the player. Replace the path with a reachable HLS playlist. If the stream uses a different origin, allow your app's origin on the HLS server for hls.js requests.
|
|
36
|
+
4. Once the status reaches `ready`, select **Play** in the video controls. The status changes to `playing` when playback starts.
|
|
37
|
+
|
|
38
|
+
Pass the playlist URL to the hook. Leave the video's `src` attribute and `<source>` children unset. In Next.js App Router, keep `"use client"` at the top of the player module and pass the URL as a string prop. The server renders the `idle` state. Playback starts in the browser.
|
|
39
|
+
|
|
40
|
+
## Framework-independent usage
|
|
41
|
+
|
|
42
|
+
The `usehlsvideoplayer/core` entry works without React. See how to [attach a stream to a video element and release it on teardown](docs/usage.md#play-a-stream-without-react).
|
|
64
43
|
|
|
65
44
|
## Documentation
|
|
66
45
|
|
|
67
|
-
- [
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
-
- [
|
|
71
|
-
- [
|
|
46
|
+
- [Usage guide](docs/usage.md) for source changes, live playback, track selection, recovery, and authenticated streams.
|
|
47
|
+
- [Playback API reference](docs/api.md) for options, commands, state, events, and metrics.
|
|
48
|
+
- [Browser compatibility](docs/compatibility.md) and [playback troubleshooting](docs/troubleshooting.md).
|
|
49
|
+
- [Migrate from 1.x](docs/migration.md) if you use `Usehlsplayer` or `useHlsVideoPlayer`.
|
|
50
|
+
- [Security policy](SECURITY.md) for request policy limits and private vulnerability reporting.
|
|
72
51
|
|
|
73
52
|
## Development
|
|
74
53
|
|
|
@@ -83,8 +62,8 @@ npm run fixtures
|
|
|
83
62
|
npm run dev
|
|
84
63
|
```
|
|
85
64
|
|
|
86
|
-
|
|
65
|
+
Open the local URL printed by Vite to play generated VOD, live, alternate audio, and captions. See the [engineering record](docs/modernization.md) for browser checks and release notes.
|
|
87
66
|
|
|
88
67
|
## License
|
|
89
68
|
|
|
90
|
-
ISC
|
|
69
|
+
[ISC](LICENSE)
|
package/SECURITY.md
CHANGED
|
@@ -14,15 +14,7 @@ Report suspected vulnerabilities privately through GitHub Security Advisories wh
|
|
|
14
14
|
|
|
15
15
|
Without `request`, hls.js or the native media element uses its normal network behaviour. Native playback can use signed URLs and browser cookies, but cannot receive injected headers or enforce the per-request allowlist. Set native `crossOrigin` on the element before attaching the source when required. Custom hls.js loaders, DRM and interstitials are trusted application integrations. Restricted request mode overrides custom loaders and disables EME and interstitial playback. Allowlisted origins use HTTPS, with HTTP permitted for local loopback development.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
const request = {
|
|
19
|
-
allowedOrigins: ["https://media.example.com"],
|
|
20
|
-
credentials: "omit" as const,
|
|
21
|
-
headers: () => ({ Authorization: `Bearer ${getCurrentToken()}` }),
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const { videoRef, error } = useHlsPlayer({ src: signedManifest, request });
|
|
25
|
-
```
|
|
17
|
+
See [Send credentials with HLS requests](docs/usage.md#send-credentials-with-hls-requests) for a React example.
|
|
26
18
|
|
|
27
19
|
Use a stable header provider in a component. A server receiving allowed credentials can read them. The browser and server still control CORS, cookie policy, caching and redirects outside the restricted path. A redirect blocked by Fetch can surface as a generic network or manifest failure because Fetch withholds response details.
|
|
28
20
|
|
package/docs/api.md
CHANGED
|
@@ -1,98 +1,106 @@
|
|
|
1
|
-
# Playback API
|
|
1
|
+
# Playback API reference
|
|
2
2
|
|
|
3
3
|
## React hooks
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Controls
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
5
|
+
The package root exports the hooks and playback types. The React hooks expose an idle server snapshot and attach media after a client render.
|
|
6
|
+
|
|
7
|
+
| Hook | Result |
|
|
8
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `useHlsPlayer(options)` | Returns `videoRef`, `store`, `state`, `controls`, `quality`, `audio`, `subtitles`, `live`, `metrics`, and `error`. The component subscribes to state and metrics. |
|
|
10
|
+
| `useHlsController(options)` | Returns a stable callback `videoRef`, a stable `store`, and stable `controls`. The hook leaves playback and metrics subscriptions to the caller. |
|
|
11
|
+
| `usePlaybackState(store, selector?, isEqual?)` | Returns the full snapshot or the result of a pure selector. Equality defaults to `Object.is`. A selector that returns a new object needs an equality function to suppress updates when the selected values are unchanged. |
|
|
12
|
+
| `useHlsMetrics(store)` | Subscribes to metrics separately from playback state. Returns `null` until the controller has a source with metrics enabled. |
|
|
13
|
+
|
|
14
|
+
## Options
|
|
15
|
+
|
|
16
|
+
| Option | Default and effect |
|
|
17
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `src` | React only. Omitted, `null`, or an empty string leaves playback idle. The controller resolves relative HTTP or HTTPS URLs against the video's document base URL. A URL with a username or password is invalid. |
|
|
19
|
+
| `onEvent` | React only. Receives transition, error, and recovery events. Changing the callback preserves the active session. The core controller provides an `onEvent(listener)` method. |
|
|
20
|
+
| `autoPlay` | `false`. When true, the controller requests playback on first readiness, subject to the browser's autoplay policy. |
|
|
21
|
+
| `lowLatency` | Defaults to the hls.js setting. An explicit value overrides `hlsConfig.lowLatencyMode`. The browser controls latency on the native path. |
|
|
22
|
+
| `engine` | `"auto"` prefers hls.js and falls back to native HLS when hls.js is unavailable. `"hls.js"` and `"native"` require the selected engine. The `request` option requires hls.js. |
|
|
23
|
+
| `metrics` | `false`. When true, the controller publishes a separate metrics snapshot on attachment and samples it every second. |
|
|
24
|
+
| `recovery` | Two automatic attempts by default. `false` disables controller recovery. An object overrides the settings below. |
|
|
25
|
+
| `hlsConfig` | Partial hls.js configuration from trusted application code. See the [security policy](../SECURITY.md) for overrides and limits. |
|
|
26
|
+
| `request` | Off by default. Enables the origin allowlist and optional request headers. `credentials` defaults to `"same-origin"` in this mode. See the [security policy](../SECURITY.md) for the request rules. |
|
|
27
|
+
|
|
28
|
+
### Recovery options
|
|
29
|
+
|
|
30
|
+
| Field | Default | Accepted range |
|
|
31
|
+
| ------------------ | ------- | -------------- |
|
|
32
|
+
| `maxAttempts` | 2 | 0 to 5 |
|
|
33
|
+
| `baseDelayMs` | 500 | 0 to 30,000 |
|
|
34
|
+
| `maxDelayMs` | 5,000 | 0 to 60,000 |
|
|
35
|
+
| `attemptTimeoutMs` | 30,000 | 1 to 120,000 |
|
|
36
|
+
|
|
37
|
+
The controller rounds finite numbers down and clamps them to the accepted range. Omitted and non-finite values use the defaults. The delay grows exponentially with jitter up to `maxDelayMs`. The timeout applies to each recovery attempt.
|
|
38
|
+
|
|
39
|
+
## Core controller
|
|
40
|
+
|
|
41
|
+
`usehlsvideoplayer/core` exports the controller, engine selection function, and playback and adapter types. The entry has no React runtime dependency.
|
|
42
|
+
|
|
43
|
+
| Method | Contract |
|
|
44
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
45
|
+
| `createPlaybackController(factory?)` | Creates an idle controller. An optional `EngineFactory` returns an unattached adapter or `null` when playback is unsupported. |
|
|
46
|
+
| `selectEngine(mode, mse, native, requiresMse?)` | Selects `"hls.js"`, `"native"`, or `null` from the supplied mode and capabilities. `requiresMse` defaults to `false`. |
|
|
47
|
+
| `setSource(video, src, options?)` | Replaces the previous session, even for the same URL. `setSource(null)` clears the session and leaves the controller reusable. |
|
|
48
|
+
| `getState()`, `getMetrics()` | Return read-only snapshots. Repeated reads return the same snapshot until the store publishes an update. |
|
|
49
|
+
| `subscribe(listener)`, `subscribeMetrics(listener)` | Subscribe to changes in the respective store. The listener takes no arguments. Each method returns an unsubscribe function. |
|
|
50
|
+
| `onEvent(listener)` | Subscribes to `PlaybackEvent` values and returns an unsubscribe function. |
|
|
51
|
+
| `destroy()` | Releases media resources, clears subscribers, and closes the controller permanently. |
|
|
52
|
+
|
|
53
|
+
Each video element needs one controller. The controller owns adapter attachment and teardown. An adapter's `destroy()` method releases the media attachment synchronously and cancels any work the adapter started.
|
|
54
|
+
|
|
55
|
+
## Controls
|
|
56
|
+
|
|
57
|
+
Commands return `true` for an accepted request and `false` for an unavailable, invalid, or rejected request. `play()` returns a promise for that result.
|
|
58
|
+
|
|
59
|
+
| Command | Result |
|
|
60
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
61
|
+
| `play()`, `pause()` | Request playback or pause. A browser autoplay rejection from `play()` yields a non-fatal `playback-blocked` error. |
|
|
62
|
+
| `seek(seconds)` | Clamps finite seconds to the nearest seekable range. If no seekable range is available, the command uses the finite duration. |
|
|
63
|
+
| `setMuted(muted)`, `setVolume(volume)`, `setPlaybackRate(rate)` | Set mute, volume, or playback rate. Volume is clamped to 0 through 1. Playback rate must be finite, positive, and supported by the browser. |
|
|
64
|
+
| `setQuality(level)` | Selects a level ID from the snapshot or restores adaptive bitrate selection with `"auto"`. Available with hls.js when levels exist. |
|
|
65
|
+
| `setAudioTrack(id)` | Selects an audio track ID from the snapshot. Available with hls.js when tracks exist. |
|
|
66
|
+
| `setSubtitleTrack(id)` | Selects a subtitle track ID from the snapshot or turns subtitles off with `"off"`. |
|
|
67
|
+
| `seekToLive()` | Seeks to the live sync position when a seekable range is available. |
|
|
68
|
+
| `startLoading()` | Starts hls.js loading, including when `hlsConfig.autoStartLoad` is false. |
|
|
69
|
+
| `retry()` | Starts a new session with the retained source and a fresh recovery budget. Requires a retained source and `state.status === "error"`. |
|
|
70
|
+
|
|
71
|
+
Track and level IDs refer to the choices in the current snapshot. A successful selection accepts the request. Media from the selected rendition may appear after one or more segments.
|
|
72
|
+
|
|
73
|
+
## State and events
|
|
74
|
+
|
|
75
|
+
`PlaybackState` contains `status`, the active or last `engine`, playback time, duration, buffered and seekable ranges, volume, mute, and playback rate. Time and range values are in seconds. Unknown or infinite duration appears as `null`.
|
|
76
|
+
|
|
77
|
+
- `quality` reports the available `levels`, selection `mode`, requested `selected` level, and `current` level.
|
|
78
|
+
- `audio` and `subtitles` each report `tracks` and the selected track ID or `null`.
|
|
79
|
+
- `live` reports `isLive`, `edge`, `syncPosition`, and `latency`, each nullable. `canSeekToLive` requires a sync position and a seekable range. `atEdge` is true when playback reaches two seconds before the sync position or later.
|
|
80
|
+
- `error` contains a `PlaybackError` or `null`. A playback error includes a code, fixed message, fatal and retryable flags, the engine or `null`, and an HTTP status when available.
|
|
81
|
+
- `recovery` contains the attempt number, limit, phase, and delay, or `null`.
|
|
82
|
+
|
|
83
|
+
A typical session moves through `idle`, `initialising`, `loading`, `ready`, and `playing`. Media events can skip `ready`. After playback starts, the browser can report `paused`, `buffering`, `seeking`, or `ended`. Source replacement returns to `idle`. Destruction leaves the controller in `destroyed`.
|
|
84
|
+
|
|
85
|
+
The controller delivers `PlaybackEvent` values synchronously as it publishes changes or reports failures.
|
|
86
|
+
|
|
87
|
+
| `type` | Fields |
|
|
88
|
+
| -------------- | -------------------------------------------------------------------------------- |
|
|
89
|
+
| `"transition"` | `from` and `to` contain the previous and next status. |
|
|
90
|
+
| `"error"` | `error` contains the structured failure, including non-fatal hls.js diagnostics. |
|
|
91
|
+
| `"recovery"` | `recovery` contains the current attempt, limit, delay, and phase. |
|
|
92
|
+
|
|
93
|
+
Non-fatal hls.js errors appear as events while hls.js retries or switches renditions. A fatal, retryable error can move the controller to `recovering`. The controller destroys the old engine before it schedules a replacement. The attempt budget applies to the entire source session, including after a successful recovery. Automatic recovery preserves playback intent and restores the last known position when possible. Track choices may revert to stream defaults. Non-retryable errors and exhausted budgets end in `error`.
|
|
94
|
+
|
|
95
|
+
## Metrics
|
|
96
|
+
|
|
97
|
+
| Field | Measurement |
|
|
98
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| `startupMs` | Milliseconds from attachment to first playback, including time spent waiting for viewer input. `null` before first playback. |
|
|
100
|
+
| `playingMs` | Milliseconds in `playing`. |
|
|
101
|
+
| `rebufferMs`, `rebufferCount` | Time in `buffering` and entries into `buffering` after first playback. Startup waits, seeks, and explicit recovery are separate states. |
|
|
102
|
+
| `rebufferRatio` | `rebufferMs / (playingMs + rebufferMs)`, or zero before any measured playback or rebuffering. |
|
|
103
|
+
| `droppedFrames`, `totalFrames` | Counts from `getVideoPlaybackQuality()` when available, otherwise `null`. |
|
|
104
|
+
| `liveLatency` | Estimated seconds between the playback position and the live edge visible to the player, or `null`. |
|
|
105
|
+
|
|
106
|
+
State samples from `timeupdate` publish at most four times per second. Media events and commands can publish state changes immediately.
|
package/docs/compatibility.md
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
# Browser compatibility
|
|
1
|
+
# Browser compatibility
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
| ---------------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
5
|
-
| Chromium or Firefox with supported MSE and codecs | hls.js | ABR, manual quality, audio groups, subtitles, authenticated fetch, VOD and live. |
|
|
6
|
-
| Safari or iOS browsers with a supported hls.js Media Source path | hls.js in auto mode | Availability depends on OS and media capabilities. |
|
|
7
|
-
| Safari or another browser advertising native HLS | Native when selected or as fallback | The browser manages ABR, loading and audio. The library exposes browser text tracks and seekable ranges when available. |
|
|
8
|
-
| Browser with neither HLS path | Unsupported | The controller reports a typed error. |
|
|
9
|
-
| Node and Next.js server rendering | Inactive | Imports and initial snapshots work without browser globals. |
|
|
3
|
+
`engine: "auto"` selects hls.js when the browser exposes a supported Media Source Extensions (MSE) or Managed Media Source path. Otherwise, it selects native HLS when the browser advertises native support and the options permit it. This choice happens when the controller attaches a source. A failure after attachment follows the recovery rules in the [API reference](api.md#state-and-events).
|
|
10
4
|
|
|
11
|
-
|
|
5
|
+
| Playback path | Available features |
|
|
6
|
+
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| hls.js on browsers with a supported media source path | Adaptive bitrate selection, manual quality, alternate audio, subtitles, authenticated requests, and live playback. Each stream needs compatible codecs and the relevant playlist entries. |
|
|
8
|
+
| Native HLS on browsers that advertise support | The browser manages quality, loading, and audio. The library exposes available browser subtitle and caption tracks, and seekable ranges. Request header injection and per-request origin rules require hls.js. |
|
|
9
|
+
| No compatible playback path | The controller reports `unsupported`. |
|
|
10
|
+
| Node or Next.js server rendering | Imports and the idle snapshot work without browser globals. Media attachment requires a video element in the browser. |
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
-
| Stream remains idle | Pass a source and attach the ref. Keep the video's own source attribute and children clear. |
|
|
16
|
-
| Stream starts loading but never plays | Use `controls.play()` from a click. If `autoStartLoad` is false, call `controls.startLoading()`. |
|
|
17
|
-
| Unsupported engine | Leave `engine` on auto, verify MSE or native HLS, and check the encoded codecs. |
|
|
18
|
-
| Request fails across origins | Configure CORS for the manifest, variants, segments, subtitles and keys. With credentialled CORS, allow the exact application origin. |
|
|
19
|
-
| Authentication failure | Obtain a fresh signed URL or token through the application and replace the source. Native playback may not reveal HTTP status. |
|
|
20
|
-
| No manual quality or audio options | Native playback does not expose library-managed rendition selection. Check that the master playlist actually lists alternatives. |
|
|
21
|
-
| Seek-to-live disabled | Wait for a live playlist and a seekable range. Native detection depends on the browser exposing an infinite duration. |
|
|
22
|
-
| Metrics remain empty | Enable `metrics` on the attachment. Frame quality and latency can remain unavailable on some browsers. |
|
|
12
|
+
Safari and iOS browsers can select different paths depending on the OS and media capabilities. The engine check establishes browser capability. Decoding still depends on the stream's codecs and container.
|
|
23
13
|
|
|
24
|
-
The
|
|
14
|
+
The local playground uses a rolling MPEG-TS live stream. See the [engineering record](modernization.md) for browser test coverage or [playback troubleshooting](troubleshooting.md) for help with a stream.
|
package/docs/migration.md
CHANGED
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
# Migrate from 1.x
|
|
2
2
|
|
|
3
|
-
`Usehlsplayer(url, options)` and `useHlsVideoPlayer(url, options)` remain available as deprecated adapters.
|
|
3
|
+
`Usehlsplayer(url, options)` and `useHlsVideoPlayer(url, options)` remain available as deprecated adapters. They return the original `{ videoRef }` object ref and pass flat hls.js options to the engine.
|
|
4
|
+
|
|
5
|
+
## Switch to `useHlsPlayer`
|
|
6
|
+
|
|
7
|
+
1. Replace the legacy import with `useHlsPlayer` from the package root.
|
|
8
|
+
2. Pass the URL as `src` and move hls.js options under `hlsConfig`. The old call `Usehlsplayer(url, { startLevel: -1 })` becomes `useHlsPlayer({ src: url, hlsConfig: { startLevel: -1 } })`.
|
|
9
|
+
3. Attach `videoRef` directly to the video element. Remove any `src` attribute or `<source>` children. The new ref is a callback. For imperative playback, use `controls`.
|
|
10
|
+
4. Replace the legacy `onError` callback with `onEvent` if you need diagnostics. Use `state.status === "error"` and `error` to show a terminal failure in the UI.
|
|
4
11
|
|
|
5
12
|
```tsx
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
13
|
+
"use client";
|
|
14
|
+
|
|
15
|
+
import { useHlsPlayer } from "usehlsvideoplayer";
|
|
16
|
+
|
|
17
|
+
export function Player({ url }: { url: string }) {
|
|
18
|
+
const { videoRef, state, error } = useHlsPlayer({
|
|
19
|
+
src: url,
|
|
20
|
+
hlsConfig: { startLevel: -1 },
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<video ref={videoRef} controls playsInline />
|
|
26
|
+
{state.status === "error" && error && <p role="alert">{error.message}</p>}
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
14
30
|
```
|
|
15
31
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- The new hook uses a bounded recovery budget. The legacy adapters leave upstream retries in charge, preserving their original recovery behaviour.
|
|
32
|
+
If you keep a legacy adapter, update any code that reads raw hls.js response data or browser exceptions. The `kind` discriminator stays the same, and the `error` or `cause` field now contains a structured `PlaybackError` when present. The library disables hls.js debugging and exposes structured events for diagnostics.
|
|
33
|
+
|
|
34
|
+
The new hook makes up to two automatic recovery attempts by default. To keep the legacy recovery behaviour, set `recovery: false` and leave retries to the engine.
|
|
35
|
+
|
|
36
|
+
Replace deep imports of generated 1.x files with the package root for React or `usehlsvideoplayer/core` for the controller. Both entries support ESM and CommonJS. See the [API reference](api.md) for options and commands.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Troubleshoot playback
|
|
2
|
+
|
|
3
|
+
Start with `state.status`, `state.engine`, and `state.error?.code`. For hls.js, subscribe to `onEvent` to see non-fatal errors while the engine handles retries. Keep signed URLs and credentials out of logs.
|
|
4
|
+
|
|
5
|
+
| Symptom | What to check |
|
|
6
|
+
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
7
|
+
| The player stays idle | Attach `videoRef` and pass a playlist URL to the React hook, or call `setSource(video, url)` on the core controller. Leave the video's own `src` and `<source>` children unset. |
|
|
8
|
+
| Playback reports `setup` | Check that the video element has one controller. If you supplied a custom adapter, check its factory and attachment. |
|
|
9
|
+
| The player reaches `ready` but stays paused | Use the video controls or call `controls.play()` from a click. If the browser denied autoplay, the player can report `playback-blocked`. |
|
|
10
|
+
| The player stays in `loading` with `autoStartLoad: false` | Call `controls.startLoading()` for hls.js. |
|
|
11
|
+
| The player reports `unsupported` | Check the available playback paths and the stream's codecs. If you forced an engine, try `"auto"`. If you supplied `request`, check that the browser supports hls.js. |
|
|
12
|
+
| A cross-origin request fails | Check CORS on every origin that serves playlists, segments, subtitles, or keys. If the request includes credentials, allow the exact app origin and enable credentialed CORS. |
|
|
13
|
+
| The player reports `request-policy` | Check the exact origins in `request.allowedOrigins`, including the scheme and port. Use final URLs. A blocked redirect can also appear as a network or manifest error. |
|
|
14
|
+
| The player reports `authentication` | Get a fresh signed URL or refresh the token through your application. The native engine may report a generic media error when the server rejects the request. |
|
|
15
|
+
| Manual quality or alternate audio is missing | Check `state.engine`. With hls.js, check whether the master playlist advertises alternate levels or audio tracks. |
|
|
16
|
+
| **Go live** stays disabled | Check whether `live.isLive` is true and `state.seekable` contains a range. Native live detection depends on the browser reporting an infinite duration. |
|
|
17
|
+
| Metrics stay empty | Pass `metrics: true` when you attach the stream. Frame counts and live latency can remain unavailable when the browser or stream cannot provide them. |
|
|
18
|
+
|
|
19
|
+
See [browser compatibility](compatibility.md) for engine capabilities and the [usage guide](usage.md#send-credentials-with-hls-requests) for the protected request setup.
|
package/docs/usage.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Use the player
|
|
2
|
+
|
|
3
|
+
## Play a stream without React
|
|
4
|
+
|
|
5
|
+
Import from `usehlsvideoplayer/core`. Pass a video element with no `src` or `<source>` children, and an element for the status. Call the returned cleanup function before removing the player.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createPlaybackController } from "usehlsvideoplayer/core";
|
|
9
|
+
|
|
10
|
+
export function mountPlayer(
|
|
11
|
+
video: HTMLVideoElement,
|
|
12
|
+
status: HTMLElement,
|
|
13
|
+
src: string,
|
|
14
|
+
) {
|
|
15
|
+
const player = createPlaybackController();
|
|
16
|
+
const render = () => {
|
|
17
|
+
const state = player.getState();
|
|
18
|
+
status.textContent =
|
|
19
|
+
state.status === "error"
|
|
20
|
+
? (state.error?.message ?? "Playback failed.")
|
|
21
|
+
: state.status;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const unsubscribe = player.subscribe(render);
|
|
25
|
+
video.controls = true;
|
|
26
|
+
video.playsInline = true;
|
|
27
|
+
render();
|
|
28
|
+
player.setSource(video, src);
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
unsubscribe();
|
|
32
|
+
player.destroy();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Replace or clear a stream
|
|
38
|
+
|
|
39
|
+
Give each player its own video element. In React, change `src` to replace the playlist or pass `src: null` to clear it. The hook clears the source on unmount. With the core controller, call `setSource(video, nextUrl, options)` to replace the playlist or `setSource(null)` to clear it.
|
|
40
|
+
|
|
41
|
+
Keep nested `hlsConfig` values and `request.headers` stable across React renders. For example, put static configuration at module scope. Use `useCallback` for a header provider that depends on props. A changed option can replace the playback session.
|
|
42
|
+
|
|
43
|
+
## Add live controls
|
|
44
|
+
|
|
45
|
+
Keep the browser's video controls for play, pause, volume, and seeking. Enable **Go live** when `live.canSeekToLive` is true. A live playlist needs a seekable range before the command becomes available.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
"use client";
|
|
49
|
+
|
|
50
|
+
import { useHlsPlayer } from "usehlsvideoplayer";
|
|
51
|
+
|
|
52
|
+
export function LivePlayer({ src }: { src: string }) {
|
|
53
|
+
const { videoRef, controls, live } = useHlsPlayer({ src, lowLatency: true });
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<>
|
|
57
|
+
<video ref={videoRef} controls playsInline />
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
disabled={!live.canSeekToLive}
|
|
61
|
+
onClick={controls.seekToLive}
|
|
62
|
+
>
|
|
63
|
+
Go live
|
|
64
|
+
</button>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For LL-HLS, serve an LL-HLS playlist with partial segments and blocking reloads. The origin and CDN need to support those requests. To offer rewind on a live stream, keep enough segments in the live playlist and available on the server.
|
|
71
|
+
|
|
72
|
+
## Add quality, audio, and subtitle choices
|
|
73
|
+
|
|
74
|
+
Build the menus from the current snapshot. Rebuild them as the available choices change.
|
|
75
|
+
|
|
76
|
+
- Offer **Auto** with `controls.setQuality("auto")`. For manual choices, pass an ID from `quality.levels`. Hide the selector when `quality.mode` is `"unavailable"`.
|
|
77
|
+
- Pass an ID from `audio.tracks` to `controls.setAudioTrack(id)`. Hide the selector when there are no audio choices.
|
|
78
|
+
- Pass an ID from `subtitles.tracks` to `controls.setSubtitleTrack(id)`. Add an **Off** choice that passes `"off"`.
|
|
79
|
+
|
|
80
|
+
Use `quality.current` to display the level in use. The requested level can take one or more segments to become current.
|
|
81
|
+
|
|
82
|
+
## Recover from a playback failure
|
|
83
|
+
|
|
84
|
+
- If the browser blocks autoplay, call `controls.play()` from a click or let the viewer use the video controls.
|
|
85
|
+
- While `state.status` is `"recovering"`, show progress and let the controller finish its automatic attempts.
|
|
86
|
+
- If `state.status` is `"error"` and the URL and configuration still apply, offer `controls.retry()`. A return value of `true` means the controller started a fresh attempt.
|
|
87
|
+
- If the URL expired, get a fresh URL and replace `src`. If credentials expired, refresh them through your application before starting another attempt.
|
|
88
|
+
|
|
89
|
+
To collect diagnostics, use `onEvent` for transitions, errors, and recovery. For playback measurements, set `metrics: true`.
|
|
90
|
+
|
|
91
|
+
## Subscribe to part of the playback state
|
|
92
|
+
|
|
93
|
+
If only one part of a React player needs playback updates, use `useHlsController` in the parent. Select the field you need in the child. In this example, the store updates `PlaybackStatus` only when `state.status` changes.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
"use client";
|
|
97
|
+
|
|
98
|
+
import {
|
|
99
|
+
type PlaybackStore,
|
|
100
|
+
useHlsController,
|
|
101
|
+
usePlaybackState,
|
|
102
|
+
} from "usehlsvideoplayer";
|
|
103
|
+
|
|
104
|
+
function PlaybackStatus({ store }: { store: PlaybackStore }) {
|
|
105
|
+
const status = usePlaybackState(store, (state) => state.status);
|
|
106
|
+
return <p role="status">{status}</p>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function Player({ src }: { src: string }) {
|
|
110
|
+
const { videoRef, store } = useHlsController({ src });
|
|
111
|
+
return (
|
|
112
|
+
<>
|
|
113
|
+
<video ref={videoRef} controls playsInline />
|
|
114
|
+
<PlaybackStatus store={store} />
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Send credentials with HLS requests
|
|
121
|
+
|
|
122
|
+
If the stream requires an authorization header, pass `request`. This mode requires hls.js. Include the exact origins of the master playlist, child playlists, segments, subtitles, and keys in `allowedOrigins`. Use final URLs because this mode rejects redirects.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
"use client";
|
|
126
|
+
|
|
127
|
+
import { useCallback } from "react";
|
|
128
|
+
import { useHlsPlayer } from "usehlsvideoplayer";
|
|
129
|
+
|
|
130
|
+
export function ProtectedPlayer({
|
|
131
|
+
src,
|
|
132
|
+
getAccessToken,
|
|
133
|
+
}: {
|
|
134
|
+
src: string;
|
|
135
|
+
getAccessToken: () => string;
|
|
136
|
+
}) {
|
|
137
|
+
const headers = useCallback(
|
|
138
|
+
() => ({ Authorization: `Bearer ${getAccessToken()}` }),
|
|
139
|
+
[getAccessToken],
|
|
140
|
+
);
|
|
141
|
+
const { videoRef, state, error } = useHlsPlayer({
|
|
142
|
+
src,
|
|
143
|
+
request: {
|
|
144
|
+
allowedOrigins: ["https://media.example.com"],
|
|
145
|
+
credentials: "omit",
|
|
146
|
+
headers,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<>
|
|
152
|
+
<video ref={videoRef} controls playsInline />
|
|
153
|
+
{state.status === "error" && error && <p role="alert">{error.message}</p>}
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Replace the example origin with the media origin and pass a stable `getAccessToken` function that reads the current token. The header callback in this example sends the token to every allowed origin. If different origins need different credentials, use the callback's URL argument to decide which headers to return.
|
|
160
|
+
|
|
161
|
+
For cross-origin requests, allow your app's origin and the `Authorization` header in the server's CORS response. For cookie authentication across origins, use `credentials: "include"` and configure the server to allow credentials for the exact app origin. For native playback, use a signed URL or browser-managed cookies. See the [security policy](../SECURITY.md) for request and credential limits.
|