@stinkycomputing/web-live-player 0.1.1 → 0.1.2

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
@@ -9,6 +9,7 @@ A framework-agnostic video streaming library for playing back **Sesame** video s
9
9
  - **MoQ support** - Native Media over QUIC protocol support via `stinky-moq-js`
10
10
  - **Pluggable stream sources** - Use dependency injection to provide video data from any transport
11
11
  - **Frame scheduling** - Automatic buffering and drift correction for smooth playback
12
+ - **Optimized file loading** - Range-based chunked loading for fast playback of large MP4 files
12
13
  - **No framework dependencies** - Works with vanilla JS, React, Three.js, or any other framework
13
14
 
14
15
  ## Installation
@@ -17,12 +18,6 @@ A framework-agnostic video streaming library for playing back **Sesame** video s
17
18
  npm install @stinkycomputing/web-live-player
18
19
  ```
19
20
 
20
- For MoQ support, also install:
21
-
22
- ```bash
23
- npm install stinky-moq-js
24
- ```
25
-
26
21
  ## Quick Start
27
22
 
28
23
  ### Using with MoQ (Standalone)
@@ -33,7 +28,7 @@ import { createPlayer, createStandaloneMoQSource } from '@stinkycomputing/web-li
33
28
  // Create player
34
29
  const player = createPlayer({
35
30
  preferredDecoder: 'webcodecs-hw',
36
- bufferSizeFrames: 3,
31
+ bufferDelayMs: 100,
37
32
  });
38
33
 
39
34
  // Create MoQ source
@@ -42,6 +37,7 @@ const moqSource = createStandaloneMoQSource({
42
37
  namespace: 'live/stream',
43
38
  subscriptions: [
44
39
  { trackName: 'video', streamType: 'video' },
40
+ { trackName: 'audio', streamType: 'audio' },
45
41
  ],
46
42
  });
47
43
 
@@ -62,23 +58,6 @@ function render(timestamp) {
62
58
  requestAnimationFrame(render);
63
59
  ```
64
60
 
65
- ### Using with Elmo's MoQSession
66
-
67
- ```typescript
68
- import { createPlayer } from '@stinkycomputing/web-live-player';
69
- import { MoQDiscoveryUtils } from '@elmo/core';
70
-
71
- // Find session from Elmo's node tree
72
- // MoQSessionNode implements IStreamSource directly
73
- const session = MoQDiscoveryUtils.findMoQSession(currentNode, 'my-session');
74
-
75
- // Create and configure player - session can be used directly as stream source
76
- const player = createPlayer();
77
- player.setStreamSource(session);
78
- player.setTrackFilter('video-track');
79
- player.play();
80
- ```
81
-
82
61
  ### Custom Stream Source
83
62
 
84
63
  ```typescript
@@ -109,6 +88,60 @@ player.setStreamSource(source);
109
88
  player.play();
110
89
  ```
111
90
 
91
+ ### File Playback
92
+
93
+ For playing MP4 files from URLs or local files:
94
+
95
+ ```typescript
96
+ import { createFilePlayer } from '@stinkycomputing/web-live-player';
97
+
98
+ const filePlayer = createFilePlayer({
99
+ preferredDecoder: 'webcodecs-hw',
100
+ enableAudio: true,
101
+ debugLogging: false,
102
+ playMode: 'once', // or 'loop' for continuous playback
103
+ });
104
+
105
+ // Load from URL (with optimized chunked loading)
106
+ await filePlayer.loadFromUrl('https://example.com/video.mp4');
107
+
108
+ // Or load from File object (e.g., from file input)
109
+ const file = fileInput.files[0];
110
+ await filePlayer.loadFromFile(file);
111
+
112
+ // Play the file
113
+ filePlayer.play();
114
+
115
+ // Render loop
116
+ function render() {
117
+ const frame = filePlayer.getVideoFrame();
118
+ if (frame) {
119
+ ctx.drawImage(frame, 0, 0);
120
+ frame.close();
121
+ }
122
+ requestAnimationFrame(render);
123
+ }
124
+ requestAnimationFrame(render);
125
+
126
+ // Seek to position (in seconds)
127
+ await filePlayer.seek(30);
128
+
129
+ // Listen to events
130
+ filePlayer.on('ready', (info) => {
131
+ console.log(`Video loaded: ${info.width}x${info.height}, ${info.duration}s`);
132
+ });
133
+
134
+ filePlayer.on('progress', (loaded, total) => {
135
+ console.log(`Loading: ${(loaded / total * 100).toFixed(1)}%`);
136
+ });
137
+ ```
138
+
139
+ **Optimized Loading**: The file player uses HTTP Range requests to load large files in chunks (1MB each). This means:
140
+ - Playback starts as soon as metadata is available (~1-2MB typically)
141
+ - Remaining file loads in the background during playback
142
+ - 10-30x faster time-to-first-frame for large files
143
+ - Automatic fallback to full download if server doesn't support ranges
144
+
112
145
  ## API Reference
113
146
 
114
147
  ### `createPlayer(config?)`
@@ -116,10 +149,51 @@ player.play();
116
149
  Creates a new player instance.
117
150
 
118
151
  **Config options:**
119
- - `preferredDecoder`: `'webcodecs-hw'` | `'webcodecs-sw'` | `'wasm'` - Decoder preference
120
- - `bufferSizeFrames`: `number` - Target buffer size (default: 3)
152
+ - `preferredDecoder`: `'webcodecs-hw'` | `'webcodecs-sw'` | `'wasm'` - Decoder preference (default: `'webcodecs-sw'`). Note: WASM decoder only supports H.264 Baseline profile.
153
+ - `bufferDelayMs`: `number` - Buffer delay in milliseconds (default: 100)
154
+ - `enableAudio`: `boolean` - Enable audio playback (default: true)
155
+ - `videoTrackName`: `string | null` - Video track name for MoQ streams (default: `'video'`)
156
+ - `audioTrackName`: `string | null` - Audio track name for MoQ streams (default: `'audio'`)
157
+ - `debugLogging`: `boolean` - Enable debug logging
158
+
159
+ ### `createFilePlayer(config?)`
160
+
161
+ Creates a file player instance for MP4 playback.
162
+
163
+ **Config options:**
164
+ - `preferredDecoder`: `'webcodecs-hw'` | `'webcodecs-sw'` | `'wasm'` - Decoder preference (default: `'webcodecs-sw'`)
165
+ - `enableAudio`: `boolean` - Enable audio playback (default: true)
166
+ - `audioContext`: `AudioContext` - Optional audio context (creates one if not provided)
167
+ - `playMode`: `'once'` | `'loop'` - Play mode (default: `'once'`)
121
168
  - `debugLogging`: `boolean` - Enable debug logging
122
169
 
170
+ ### `FileVideoPlayer`
171
+
172
+ File player class.
173
+
174
+ **Methods:**
175
+ - `loadFromUrl(url: string)` - Load MP4 from URL (uses range-based chunked loading)
176
+ - `loadFromFile(file: File)` - Load MP4 from File object
177
+ - `play()` - Start playback
178
+ - `pause()` - Pause playback
179
+ - `seek(timeSeconds: number)` - Seek to position
180
+ - `getVideoFrame()` - Get current video frame for rendering
181
+ - `getPosition()` - Get current position in seconds
182
+ - `getDuration()` - Get duration in seconds
183
+ - `getStats()` - Get playback statistics
184
+ - `setVolume(volume: number)` - Set audio volume (0-1)
185
+ - `setPlayMode(mode: 'once' | 'loop')` - Set play mode
186
+ - `dispose()` - Clean up resources
187
+
188
+ **Events:**
189
+ - `ready` - Emitted when file is loaded and ready to play
190
+ - `progress` - Emitted during file loading with (loaded, total) bytes
191
+ - `statechange` - Emitted when player state changes
192
+ - `ended` - Emitted when playback ends (in 'once' mode)
193
+ - `loop` - Emitted when video loops (in 'loop' mode)
194
+ - `seeked` - Emitted after seeking completes
195
+ - `error` - Emitted on errors
196
+
123
197
  ### `LiveVideoPlayer`
124
198
 
125
199
  Main player class.
@@ -127,10 +201,13 @@ Main player class.
127
201
  **Methods:**
128
202
  - `setStreamSource(source: IStreamSource)` - Set the stream data source
129
203
  - `setTrackFilter(trackName: string)` - Filter for specific track
204
+ - `connectToMoQRelay(relayUrl, namespace, options?)` - Connect directly to a MoQ relay
130
205
  - `play()` - Start playback
131
206
  - `pause()` - Pause playback
132
207
  - `getVideoFrame(timestampMs: number)` - Get frame for current render timestamp
133
208
  - `getStats()` - Get playback statistics
209
+ - `setVolume(volume: number)` - Set audio volume (0-1)
210
+ - `setDebugLogging(enabled: boolean)` - Enable/disable debug logging at runtime
134
211
  - `dispose()` - Clean up resources
135
212
 
136
213
  **Events:**
@@ -210,6 +287,8 @@ function render(timestamp: number) {
210
287
 
211
288
  ### Handling YUV Frames (WASM Decoder)
212
289
 
290
+ > **Note:** The WASM decoder only supports **H.264 Baseline profile**. For Main or High profile streams, use `'webcodecs-hw'` or `'webcodecs-sw'` instead.
291
+
213
292
  When using the WASM decoder, the library automatically converts YUV frames to `VideoFrame` objects using the browser's native I420 support. The GPU handles YUV→RGB conversion, so you can use the same rendering code regardless of decoder:
214
293
 
215
294
  ```typescript
@@ -298,7 +377,6 @@ const player = createPlayer({
298
377
  Run the demo application:
299
378
 
300
379
  ```bash
301
- cd video-player
302
380
  npm install
303
381
  npm run dev
304
382
  ```
@@ -54,6 +54,7 @@ export declare class MP4FileSource {
54
54
  private totalVideoSamples;
55
55
  private totalAudioSamples;
56
56
  private samplesRequested;
57
+ private isProgressiveLoading;
57
58
  private fileSize;
58
59
  private loadedBytes;
59
60
  private videoDescription;
@@ -73,9 +74,14 @@ export declare class MP4FileSource {
73
74
  */
74
75
  getAudioDescription(): Uint8Array | null;
75
76
  /**
76
- * Load an MP4 file from a URL
77
+ * Load an MP4 file from a URL using range-based loading for better performance.
78
+ * Automatically falls back to full download if server doesn't support ranges.
77
79
  */
78
80
  loadFromUrl(url: string): Promise<MP4FileInfo>;
81
+ /**
82
+ * Continue loading remaining chunks in background after initial metadata is ready
83
+ */
84
+ private continueLoadingInBackground;
79
85
  /**
80
86
  * Load an MP4 file from a File object (e.g., from file input)
81
87
  */