@livepeer-frameworks/player-core 0.0.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.
Files changed (120) hide show
  1. package/dist/cjs/index.js +19493 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/esm/index.js +19398 -0
  4. package/dist/esm/index.js.map +1 -0
  5. package/dist/player.css +2140 -0
  6. package/dist/types/core/ABRController.d.ts +164 -0
  7. package/dist/types/core/CodecUtils.d.ts +54 -0
  8. package/dist/types/core/Disposable.d.ts +61 -0
  9. package/dist/types/core/EventEmitter.d.ts +73 -0
  10. package/dist/types/core/GatewayClient.d.ts +144 -0
  11. package/dist/types/core/InteractionController.d.ts +121 -0
  12. package/dist/types/core/LiveDurationProxy.d.ts +102 -0
  13. package/dist/types/core/MetaTrackManager.d.ts +220 -0
  14. package/dist/types/core/MistReporter.d.ts +163 -0
  15. package/dist/types/core/MistSignaling.d.ts +148 -0
  16. package/dist/types/core/PlayerController.d.ts +665 -0
  17. package/dist/types/core/PlayerInterface.d.ts +230 -0
  18. package/dist/types/core/PlayerManager.d.ts +182 -0
  19. package/dist/types/core/PlayerRegistry.d.ts +27 -0
  20. package/dist/types/core/QualityMonitor.d.ts +184 -0
  21. package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
  22. package/dist/types/core/SeekingUtils.d.ts +142 -0
  23. package/dist/types/core/StreamStateClient.d.ts +108 -0
  24. package/dist/types/core/SubtitleManager.d.ts +111 -0
  25. package/dist/types/core/TelemetryReporter.d.ts +79 -0
  26. package/dist/types/core/TimeFormat.d.ts +97 -0
  27. package/dist/types/core/TimerManager.d.ts +83 -0
  28. package/dist/types/core/UrlUtils.d.ts +81 -0
  29. package/dist/types/core/detector.d.ts +149 -0
  30. package/dist/types/core/index.d.ts +49 -0
  31. package/dist/types/core/scorer.d.ts +167 -0
  32. package/dist/types/core/selector.d.ts +9 -0
  33. package/dist/types/index.d.ts +45 -0
  34. package/dist/types/lib/utils.d.ts +2 -0
  35. package/dist/types/players/DashJsPlayer.d.ts +102 -0
  36. package/dist/types/players/HlsJsPlayer.d.ts +70 -0
  37. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
  38. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
  39. package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
  40. package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
  41. package/dist/types/players/MistPlayer.d.ts +25 -0
  42. package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
  43. package/dist/types/players/NativePlayer.d.ts +143 -0
  44. package/dist/types/players/VideoJsPlayer.d.ts +59 -0
  45. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
  46. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
  47. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
  48. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
  49. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
  50. package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
  51. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
  52. package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
  53. package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
  54. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
  55. package/dist/types/players/index.d.ts +14 -0
  56. package/dist/types/styles/index.d.ts +11 -0
  57. package/dist/types/types.d.ts +363 -0
  58. package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
  59. package/dist/types/vanilla/index.d.ts +19 -0
  60. package/dist/workers/decoder.worker.js +989 -0
  61. package/dist/workers/decoder.worker.js.map +1 -0
  62. package/package.json +80 -0
  63. package/src/core/ABRController.ts +550 -0
  64. package/src/core/CodecUtils.ts +257 -0
  65. package/src/core/Disposable.ts +120 -0
  66. package/src/core/EventEmitter.ts +113 -0
  67. package/src/core/GatewayClient.ts +439 -0
  68. package/src/core/InteractionController.ts +712 -0
  69. package/src/core/LiveDurationProxy.ts +270 -0
  70. package/src/core/MetaTrackManager.ts +753 -0
  71. package/src/core/MistReporter.ts +543 -0
  72. package/src/core/MistSignaling.ts +346 -0
  73. package/src/core/PlayerController.ts +2829 -0
  74. package/src/core/PlayerInterface.ts +432 -0
  75. package/src/core/PlayerManager.ts +900 -0
  76. package/src/core/PlayerRegistry.ts +149 -0
  77. package/src/core/QualityMonitor.ts +597 -0
  78. package/src/core/ScreenWakeLockManager.ts +163 -0
  79. package/src/core/SeekingUtils.ts +364 -0
  80. package/src/core/StreamStateClient.ts +457 -0
  81. package/src/core/SubtitleManager.ts +297 -0
  82. package/src/core/TelemetryReporter.ts +308 -0
  83. package/src/core/TimeFormat.ts +205 -0
  84. package/src/core/TimerManager.ts +209 -0
  85. package/src/core/UrlUtils.ts +179 -0
  86. package/src/core/detector.ts +382 -0
  87. package/src/core/index.ts +140 -0
  88. package/src/core/scorer.ts +553 -0
  89. package/src/core/selector.ts +16 -0
  90. package/src/global.d.ts +11 -0
  91. package/src/index.ts +75 -0
  92. package/src/lib/utils.ts +6 -0
  93. package/src/players/DashJsPlayer.ts +642 -0
  94. package/src/players/HlsJsPlayer.ts +483 -0
  95. package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
  96. package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
  97. package/src/players/MewsWsPlayer/index.ts +1065 -0
  98. package/src/players/MewsWsPlayer/types.ts +106 -0
  99. package/src/players/MistPlayer.ts +188 -0
  100. package/src/players/MistWebRTCPlayer/index.ts +703 -0
  101. package/src/players/NativePlayer.ts +820 -0
  102. package/src/players/VideoJsPlayer.ts +643 -0
  103. package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
  104. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
  105. package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
  106. package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
  107. package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
  108. package/src/players/WebCodecsPlayer/index.ts +1650 -0
  109. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
  110. package/src/players/WebCodecsPlayer/types.ts +542 -0
  111. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
  112. package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
  113. package/src/players/index.ts +22 -0
  114. package/src/styles/animations.css +21 -0
  115. package/src/styles/index.ts +52 -0
  116. package/src/styles/player.css +2126 -0
  117. package/src/styles/tailwind.css +1015 -0
  118. package/src/types.ts +421 -0
  119. package/src/vanilla/FrameWorksPlayer.ts +367 -0
  120. package/src/vanilla/index.ts +22 -0
@@ -0,0 +1,379 @@
1
+ /**
2
+ * MediaStreamTrackGenerator Polyfill
3
+ *
4
+ * Provides fallback for browsers without native MediaStreamTrackGenerator (Firefox).
5
+ *
6
+ * Video: Uses Canvas2D + captureStream()
7
+ * Audio: Uses AudioWorklet + createMediaStreamDestination()
8
+ *
9
+ * Based on legacy rawws.js polyfill with improvements:
10
+ * - TypeScript types
11
+ * - Pull-based audio to prevent tin-can distortion
12
+ * - Better resource cleanup
13
+ */
14
+
15
+ /**
16
+ * Check if native MediaStreamTrackGenerator is available
17
+ */
18
+ export function hasNativeMediaStreamTrackGenerator(): boolean {
19
+ return typeof (globalThis as any).MediaStreamTrackGenerator !== 'undefined';
20
+ }
21
+
22
+ /**
23
+ * Polyfill for MediaStreamTrackGenerator (video)
24
+ *
25
+ * Uses an offscreen canvas and captureStream() to create a MediaStreamTrack
26
+ * that can be fed VideoFrames via a WritableStream.
27
+ */
28
+ export class VideoTrackGeneratorPolyfill {
29
+ private canvas: HTMLCanvasElement;
30
+ private ctx: CanvasRenderingContext2D;
31
+ private track: MediaStreamTrack;
32
+ private _writable: WritableStream<VideoFrame>;
33
+ private closed = false;
34
+
35
+ constructor() {
36
+ // Create offscreen canvas
37
+ this.canvas = document.createElement('canvas');
38
+ this.canvas.width = 1920; // Will be resized on first frame
39
+ this.canvas.height = 1080;
40
+
41
+ const ctx = this.canvas.getContext('2d', { desynchronized: true });
42
+ if (!ctx) {
43
+ throw new Error('Failed to create canvas 2D context');
44
+ }
45
+ this.ctx = ctx;
46
+
47
+ // Capture stream from canvas
48
+ const stream = this.canvas.captureStream();
49
+ const tracks = stream.getVideoTracks();
50
+ if (tracks.length === 0) {
51
+ throw new Error('Failed to capture stream from canvas');
52
+ }
53
+ this.track = tracks[0];
54
+
55
+ // Create writable stream that draws frames to canvas
56
+ this._writable = new WritableStream<VideoFrame>({
57
+ write: (frame: VideoFrame) => {
58
+ if (this.closed) {
59
+ frame.close();
60
+ return;
61
+ }
62
+
63
+ // Resize canvas to match frame if needed
64
+ if (
65
+ this.canvas.width !== frame.displayWidth ||
66
+ this.canvas.height !== frame.displayHeight
67
+ ) {
68
+ this.canvas.width = frame.displayWidth;
69
+ this.canvas.height = frame.displayHeight;
70
+ }
71
+
72
+ // Draw frame to canvas
73
+ this.ctx.drawImage(
74
+ frame as unknown as CanvasImageSource,
75
+ 0,
76
+ 0,
77
+ this.canvas.width,
78
+ this.canvas.height
79
+ );
80
+
81
+ // Close the frame to release resources
82
+ frame.close();
83
+ },
84
+ close: () => {
85
+ this.close();
86
+ },
87
+ abort: () => {
88
+ this.close();
89
+ },
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Get the writable stream for writing VideoFrames
95
+ */
96
+ get writable(): WritableStream<VideoFrame> {
97
+ return this._writable;
98
+ }
99
+
100
+ /**
101
+ * Get the MediaStreamTrack for adding to MediaStream
102
+ */
103
+ getTrack(): MediaStreamTrack {
104
+ return this.track;
105
+ }
106
+
107
+ /**
108
+ * Close and cleanup resources
109
+ */
110
+ close(): void {
111
+ if (this.closed) return;
112
+ this.closed = true;
113
+
114
+ this.track.stop();
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Polyfill for MediaStreamTrackGenerator (audio)
120
+ *
121
+ * Uses AudioWorklet to create a pull-based audio output that prevents
122
+ * the "tin-can" distortion issue from the legacy implementation.
123
+ *
124
+ * Key improvement: Audio samples are pulled by the worklet at a fixed rate,
125
+ * and we feed samples proactively rather than pushing them as they arrive.
126
+ */
127
+ export class AudioTrackGeneratorPolyfill {
128
+ private audioContext: AudioContext;
129
+ private destination: MediaStreamAudioDestinationNode;
130
+ private workletNode: AudioWorkletNode | null = null;
131
+ private track: MediaStreamTrack;
132
+ private _writable: WritableStream<AudioData>;
133
+ private closed = false;
134
+ private initialized = false;
135
+ private initPromise: Promise<void>;
136
+
137
+ // Ring buffer for samples (main thread side)
138
+ private sampleBuffer: Float32Array[] = [];
139
+ private maxBufferChunks = 20; // ~400ms at 48kHz with 1024 samples/chunk
140
+
141
+ constructor() {
142
+ // Create audio context
143
+ this.audioContext = new AudioContext({ latencyHint: 'interactive' });
144
+
145
+ // Create destination for MediaStreamTrack
146
+ this.destination = this.audioContext.createMediaStreamDestination();
147
+ const tracks = this.destination.stream.getAudioTracks();
148
+ if (tracks.length === 0) {
149
+ throw new Error('Failed to create audio destination');
150
+ }
151
+ this.track = tracks[0];
152
+
153
+ // Initialize worklet
154
+ this.initPromise = this.initializeWorklet();
155
+
156
+ // Create writable stream for AudioData
157
+ this._writable = new WritableStream<AudioData>({
158
+ write: (data: AudioData) => {
159
+ if (this.closed) {
160
+ data.close();
161
+ return;
162
+ }
163
+
164
+ // Convert AudioData to Float32Array
165
+ const samples = this.audioDataToSamples(data);
166
+ data.close();
167
+
168
+ // Add to ring buffer
169
+ this.sampleBuffer.push(samples);
170
+ if (this.sampleBuffer.length > this.maxBufferChunks) {
171
+ this.sampleBuffer.shift(); // Drop oldest
172
+ }
173
+
174
+ // Feed samples to worklet
175
+ this.feedSamples();
176
+ },
177
+ close: () => {
178
+ this.close();
179
+ },
180
+ abort: () => {
181
+ this.close();
182
+ },
183
+ });
184
+ }
185
+
186
+ /**
187
+ * Initialize the AudioWorklet
188
+ */
189
+ private async initializeWorklet(): Promise<void> {
190
+ // Create worklet code as blob
191
+ const workletCode = `
192
+ class SyncedAudioProcessor extends AudioWorkletProcessor {
193
+ constructor() {
194
+ super();
195
+ this.ringBuffer = [];
196
+ this.maxBufferSize = 10;
197
+
198
+ this.port.onmessage = (e) => {
199
+ if (e.data.type === 'samples') {
200
+ // Add samples to ring buffer
201
+ this.ringBuffer.push(e.data.samples);
202
+ if (this.ringBuffer.length > this.maxBufferSize) {
203
+ this.ringBuffer.shift(); // Drop oldest
204
+ }
205
+ }
206
+ };
207
+ }
208
+
209
+ process(inputs, outputs, parameters) {
210
+ const output = outputs[0];
211
+ if (!output || output.length === 0) return true;
212
+
213
+ const channel = output[0];
214
+
215
+ if (this.ringBuffer.length > 0) {
216
+ const samples = this.ringBuffer.shift();
217
+ const len = Math.min(samples.length, channel.length);
218
+ for (let i = 0; i < len; i++) {
219
+ channel[i] = samples[i];
220
+ }
221
+ // Fill remainder with last sample (smooth transition)
222
+ for (let i = len; i < channel.length; i++) {
223
+ channel[i] = samples[len - 1] || 0;
224
+ }
225
+ } else {
226
+ // No samples - output silence
227
+ channel.fill(0);
228
+ }
229
+
230
+ return true;
231
+ }
232
+ }
233
+
234
+ registerProcessor('synced-audio-processor', SyncedAudioProcessor);
235
+ `;
236
+
237
+ const blob = new Blob([workletCode], { type: 'application/javascript' });
238
+ const url = URL.createObjectURL(blob);
239
+
240
+ try {
241
+ await this.audioContext.audioWorklet.addModule(url);
242
+
243
+ this.workletNode = new AudioWorkletNode(
244
+ this.audioContext,
245
+ 'synced-audio-processor'
246
+ );
247
+ this.workletNode.connect(this.destination);
248
+
249
+ this.initialized = true;
250
+ } finally {
251
+ URL.revokeObjectURL(url);
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Convert AudioData to mono Float32Array
257
+ */
258
+ private audioDataToSamples(data: AudioData): Float32Array {
259
+ const channels = data.numberOfChannels;
260
+ const frames = data.numberOfFrames;
261
+
262
+ // Get all samples
263
+ const allSamples = new Float32Array(frames * channels);
264
+ data.copyTo(allSamples, { planeIndex: 0 });
265
+
266
+ // Convert to mono if needed
267
+ if (channels === 1) {
268
+ return allSamples;
269
+ }
270
+
271
+ // Mix down to mono
272
+ const mono = new Float32Array(frames);
273
+ for (let i = 0; i < frames; i++) {
274
+ let sum = 0;
275
+ for (let ch = 0; ch < channels; ch++) {
276
+ sum += allSamples[i * channels + ch];
277
+ }
278
+ mono[i] = sum / channels;
279
+ }
280
+
281
+ return mono;
282
+ }
283
+
284
+ /**
285
+ * Feed samples to the worklet
286
+ */
287
+ private feedSamples(): void {
288
+ if (!this.initialized || !this.workletNode || this.sampleBuffer.length === 0) {
289
+ return;
290
+ }
291
+
292
+ // Send oldest chunk to worklet
293
+ const samples = this.sampleBuffer.shift();
294
+ if (samples) {
295
+ this.workletNode.port.postMessage(
296
+ { type: 'samples', samples },
297
+ { transfer: [samples.buffer] }
298
+ );
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Get the writable stream for writing AudioData
304
+ */
305
+ get writable(): WritableStream<AudioData> {
306
+ return this._writable;
307
+ }
308
+
309
+ /**
310
+ * Get the MediaStreamTrack for adding to MediaStream
311
+ */
312
+ getTrack(): MediaStreamTrack {
313
+ return this.track;
314
+ }
315
+
316
+ /**
317
+ * Wait for initialization to complete
318
+ */
319
+ async waitForInit(): Promise<void> {
320
+ await this.initPromise;
321
+ }
322
+
323
+ /**
324
+ * Close and cleanup resources
325
+ */
326
+ close(): void {
327
+ if (this.closed) return;
328
+ this.closed = true;
329
+
330
+ this.track.stop();
331
+ this.workletNode?.disconnect();
332
+ this.audioContext.close();
333
+ this.sampleBuffer = [];
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Create appropriate track generator based on browser support
339
+ *
340
+ * @param kind - 'video' or 'audio'
341
+ * @returns Native generator or polyfill
342
+ */
343
+ export function createTrackGenerator(
344
+ kind: 'video' | 'audio'
345
+ ): {
346
+ writable: WritableStream<VideoFrame | AudioData>;
347
+ getTrack: () => MediaStreamTrack;
348
+ close: () => void;
349
+ waitForInit?: () => Promise<void>;
350
+ } {
351
+ // Try native first
352
+ if (hasNativeMediaStreamTrackGenerator()) {
353
+ const Generator = (globalThis as any).MediaStreamTrackGenerator;
354
+ const generator = new Generator({ kind });
355
+ return {
356
+ writable: generator.writable,
357
+ getTrack: () => generator,
358
+ close: () => generator.stop?.(),
359
+ };
360
+ }
361
+
362
+ // Fall back to polyfill
363
+ if (kind === 'video') {
364
+ const polyfill = new VideoTrackGeneratorPolyfill();
365
+ return {
366
+ writable: polyfill.writable as WritableStream<VideoFrame | AudioData>,
367
+ getTrack: () => polyfill.getTrack(),
368
+ close: () => polyfill.close(),
369
+ };
370
+ } else {
371
+ const polyfill = new AudioTrackGeneratorPolyfill();
372
+ return {
373
+ writable: polyfill.writable as WritableStream<VideoFrame | AudioData>,
374
+ getTrack: () => polyfill.getTrack(),
375
+ close: () => polyfill.close(),
376
+ waitForInit: () => polyfill.waitForInit(),
377
+ };
378
+ }
379
+ }