@livepeer-frameworks/player-core 0.1.0 → 0.1.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.
Files changed (59) hide show
  1. package/README.md +11 -9
  2. package/dist/player.css +182 -42
  3. package/package.json +1 -1
  4. package/src/core/ABRController.ts +38 -36
  5. package/src/core/CodecUtils.ts +49 -46
  6. package/src/core/Disposable.ts +4 -4
  7. package/src/core/EventEmitter.ts +1 -1
  8. package/src/core/GatewayClient.ts +41 -39
  9. package/src/core/InteractionController.ts +89 -82
  10. package/src/core/LiveDurationProxy.ts +14 -15
  11. package/src/core/MetaTrackManager.ts +73 -65
  12. package/src/core/MistReporter.ts +72 -45
  13. package/src/core/MistSignaling.ts +59 -56
  14. package/src/core/PlayerController.ts +527 -384
  15. package/src/core/PlayerInterface.ts +83 -59
  16. package/src/core/PlayerManager.ts +79 -133
  17. package/src/core/PlayerRegistry.ts +59 -42
  18. package/src/core/QualityMonitor.ts +38 -31
  19. package/src/core/ScreenWakeLockManager.ts +8 -9
  20. package/src/core/SeekingUtils.ts +31 -22
  21. package/src/core/StreamStateClient.ts +74 -68
  22. package/src/core/SubtitleManager.ts +24 -22
  23. package/src/core/TelemetryReporter.ts +34 -31
  24. package/src/core/TimeFormat.ts +13 -17
  25. package/src/core/TimerManager.ts +24 -8
  26. package/src/core/UrlUtils.ts +20 -17
  27. package/src/core/detector.ts +44 -44
  28. package/src/core/index.ts +57 -48
  29. package/src/core/scorer.ts +136 -141
  30. package/src/core/selector.ts +2 -6
  31. package/src/global.d.ts +1 -1
  32. package/src/index.ts +46 -35
  33. package/src/players/DashJsPlayer.ts +164 -115
  34. package/src/players/HlsJsPlayer.ts +132 -78
  35. package/src/players/MewsWsPlayer/SourceBufferManager.ts +41 -36
  36. package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -9
  37. package/src/players/MewsWsPlayer/index.ts +192 -152
  38. package/src/players/MewsWsPlayer/types.ts +21 -21
  39. package/src/players/MistPlayer.ts +45 -26
  40. package/src/players/MistWebRTCPlayer/index.ts +175 -129
  41. package/src/players/NativePlayer.ts +203 -143
  42. package/src/players/VideoJsPlayer.ts +170 -118
  43. package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
  44. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
  45. package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
  46. package/src/players/WebCodecsPlayer/SyncController.ts +45 -53
  47. package/src/players/WebCodecsPlayer/WebSocketController.ts +66 -68
  48. package/src/players/WebCodecsPlayer/index.ts +263 -221
  49. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
  50. package/src/players/WebCodecsPlayer/types.ts +56 -56
  51. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +238 -182
  52. package/src/players/WebCodecsPlayer/worker/types.ts +31 -31
  53. package/src/players/index.ts +8 -8
  54. package/src/styles/animations.css +2 -1
  55. package/src/styles/player.css +182 -42
  56. package/src/styles/tailwind.css +473 -159
  57. package/src/types.ts +43 -43
  58. package/src/vanilla/FrameWorksPlayer.ts +29 -14
  59. package/src/vanilla/index.ts +4 -4
@@ -12,7 +12,7 @@
12
12
  * - Track switch msgqueue handling
13
13
  */
14
14
 
15
- import type { SourceBufferManagerOptions } from './types';
15
+ import type { SourceBufferManagerOptions } from "./types";
16
16
 
17
17
  export class SourceBufferManager {
18
18
  private mediaSource: MediaSource;
@@ -62,12 +62,12 @@ export class SourceBufferManager {
62
62
  return true;
63
63
  }
64
64
  if (!codecs || !codecs.length) {
65
- this.onError('No codecs provided');
65
+ this.onError("No codecs provided");
66
66
  return false;
67
67
  }
68
68
 
69
- const container = 'mp4'; // Could be 'webm' for WebM container
70
- const mime = `video/${container};codecs="${codecs.join(',')}"`;
69
+ const container = "mp4"; // Could be 'webm' for WebM container
70
+ const mime = `video/${container};codecs="${codecs.join(",")}"`;
71
71
 
72
72
  if (!MediaSource.isTypeSupported(mime)) {
73
73
  this.onError(`Unsupported MSE codec: ${mime}`);
@@ -78,7 +78,7 @@ export class SourceBufferManager {
78
78
  // Create SourceBuffer (mews.js:211)
79
79
  this.sourceBuffer = this.mediaSource.addSourceBuffer(mime);
80
80
  // Use segments mode - fragments will be put at correct time (mews.js:212)
81
- this.sourceBuffer.mode = 'segments';
81
+ this.sourceBuffer.mode = "segments";
82
82
  // Save current codecs (mews.js:215)
83
83
  this._codecs = codecs.slice();
84
84
 
@@ -92,7 +92,7 @@ export class SourceBufferManager {
92
92
 
93
93
  return true;
94
94
  } catch (e: any) {
95
- this.onError(e?.message || 'Failed to create SourceBuffer');
95
+ this.onError(e?.message || "Failed to create SourceBuffer");
96
96
  return false;
97
97
  }
98
98
  }
@@ -144,7 +144,7 @@ export class SourceBufferManager {
144
144
 
145
145
  if (this._busy) {
146
146
  // Still busy, put back in queue (mews.js:296-300)
147
- if (this.debugging) console.warn('MEWS: wanted to append but busy, requeuing');
147
+ if (this.debugging) console.warn("MEWS: wanted to append but busy, requeuing");
148
148
  this.queue.unshift(data);
149
149
  return;
150
150
  }
@@ -165,13 +165,13 @@ export class SourceBufferManager {
165
165
  } catch (e: any) {
166
166
  // Error handling (mews.js:306-338)
167
167
  switch (e?.name) {
168
- case 'QuotaExceededError': {
168
+ case "QuotaExceededError": {
169
169
  // Buffer is full - clean up and retry (mews.js:308-324)
170
170
  const buffered = this.videoElement.buffered;
171
171
  if (buffered.length && this.videoElement.currentTime - buffered.start(0) > 1) {
172
172
  // Clear as much from buffer as we can
173
173
  if (this.debugging) {
174
- console.log('MEWS: QuotaExceededError, cleaning buffer');
174
+ console.log("MEWS: QuotaExceededError, cleaning buffer");
175
175
  }
176
176
  this._clean(1); // Keep 1 second
177
177
  this._busy = false;
@@ -181,7 +181,7 @@ export class SourceBufferManager {
181
181
  // Can't clean more, skip ahead (mews.js:316-319)
182
182
  const bufferEnd = buffered.end(buffered.length - 1);
183
183
  if (this.debugging) {
184
- console.log('MEWS: QuotaExceededError, skipping ahead');
184
+ console.log("MEWS: QuotaExceededError, skipping ahead");
185
185
  }
186
186
  this.videoElement.currentTime = bufferEnd;
187
187
  this._busy = false;
@@ -190,7 +190,7 @@ export class SourceBufferManager {
190
190
  }
191
191
  break;
192
192
  }
193
- case 'InvalidStateError': {
193
+ case "InvalidStateError": {
194
194
  // Playback is borked (mews.js:326-334)
195
195
  if (this.videoElement.error) {
196
196
  // Video element error will handle this
@@ -200,7 +200,7 @@ export class SourceBufferManager {
200
200
  break;
201
201
  }
202
202
  }
203
- this.onError(e?.message || 'Append buffer failed');
203
+ this.onError(e?.message || "Append buffer failed");
204
204
  this._busy = false;
205
205
  }
206
206
  }
@@ -245,12 +245,12 @@ export class SourceBufferManager {
245
245
  changeCodecs(codecs: string[], switchPointMs?: number): void {
246
246
  // Skip reinit if codecs are identical (mews.js:676)
247
247
  if (this.codecsEqual(this._codecs, codecs)) {
248
- if (this.debugging) console.log('MEWS: keeping source buffer, codecs same');
248
+ if (this.debugging) console.log("MEWS: keeping source buffer, codecs same");
249
249
  return;
250
250
  }
251
251
 
252
- const container = 'mp4';
253
- const mime = `video/${container};codecs="${codecs.join(',')}"`;
252
+ const container = "mp4";
253
+ const mime = `video/${container};codecs="${codecs.join(",")}"`;
254
254
  if (!MediaSource.isTypeSupported(mime)) {
255
255
  this.onError(`Unsupported codec for switch: ${mime}`);
256
256
  return;
@@ -265,7 +265,7 @@ export class SourceBufferManager {
265
265
 
266
266
  const pendingCodecs = codecs.slice();
267
267
 
268
- if (typeof switchPointMs === 'number' && switchPointMs > 0) {
268
+ if (typeof switchPointMs === "number" && switchPointMs > 0) {
269
269
  // Wait for playback to reach switching point (mews.js:751-785)
270
270
  this.awaitSwitchingPoint(mime, switchPointMs, pendingCodecs);
271
271
  } else {
@@ -368,7 +368,7 @@ export class SourceBufferManager {
368
368
  this.queue = [];
369
369
 
370
370
  // Remove old sourceBuffer
371
- if (this.sourceBuffer && this.mediaSource.readyState === 'open') {
371
+ if (this.sourceBuffer && this.mediaSource.readyState === "open") {
372
372
  try {
373
373
  this.mediaSource.removeSourceBuffer(this.sourceBuffer);
374
374
  } catch {
@@ -381,7 +381,7 @@ export class SourceBufferManager {
381
381
  try {
382
382
  // Create new sourceBuffer (mews.js:713-715)
383
383
  this.sourceBuffer = this.mediaSource.addSourceBuffer(mime);
384
- this.sourceBuffer.mode = 'segments';
384
+ this.sourceBuffer.mode = "segments";
385
385
  this._codecs = newCodecs;
386
386
 
387
387
  this.installEventHandlers();
@@ -399,7 +399,7 @@ export class SourceBufferManager {
399
399
  this.append(frag);
400
400
  }
401
401
  } catch (e: any) {
402
- this.onError(e?.message || 'Failed to reinit SourceBuffer');
402
+ this.onError(e?.message || "Failed to reinit SourceBuffer");
403
403
  }
404
404
  }
405
405
 
@@ -417,21 +417,21 @@ export class SourceBufferManager {
417
417
  // Wait for video.currentTime to reach switch point
418
418
  const onTimeUpdate = () => {
419
419
  if (this.videoElement.currentTime >= tSec) {
420
- this.videoElement.removeEventListener('timeupdate', onTimeUpdate);
421
- this.videoElement.removeEventListener('waiting', onWaiting);
420
+ this.videoElement.removeEventListener("timeupdate", onTimeUpdate);
421
+ this.videoElement.removeEventListener("waiting", onWaiting);
422
422
  clearAndReinit();
423
423
  }
424
424
  };
425
425
 
426
426
  // Or if video is waiting (buffer empty before switch point)
427
427
  const onWaiting = () => {
428
- this.videoElement.removeEventListener('timeupdate', onTimeUpdate);
429
- this.videoElement.removeEventListener('waiting', onWaiting);
428
+ this.videoElement.removeEventListener("timeupdate", onTimeUpdate);
429
+ this.videoElement.removeEventListener("waiting", onWaiting);
430
430
  clearAndReinit();
431
431
  };
432
432
 
433
- this.videoElement.addEventListener('timeupdate', onTimeUpdate);
434
- this.videoElement.addEventListener('waiting', onWaiting);
433
+ this.videoElement.addEventListener("timeupdate", onTimeUpdate);
434
+ this.videoElement.addEventListener("waiting", onWaiting);
435
435
  }
436
436
 
437
437
  /**
@@ -468,14 +468,14 @@ export class SourceBufferManager {
468
468
 
469
469
  if (this.debugging) {
470
470
  console.log(
471
- 'MEWS: drained msgqueue',
472
- this.msgqueue ? `${this.msgqueue.length} more queue(s) remain` : ''
471
+ "MEWS: drained msgqueue",
472
+ this.msgqueue ? `${this.msgqueue.length} more queue(s) remain` : ""
473
473
  );
474
474
  }
475
475
 
476
476
  // Manually trigger updateend if queue was empty (mews.js:363-366)
477
477
  if (do_do && this.sourceBuffer) {
478
- this.sourceBuffer.dispatchEvent(new Event('updateend'));
478
+ this.sourceBuffer.dispatchEvent(new Event("updateend"));
479
479
  }
480
480
  }
481
481
 
@@ -486,9 +486,9 @@ export class SourceBufferManager {
486
486
  private installEventHandlers(): void {
487
487
  if (!this.sourceBuffer) return;
488
488
 
489
- this.sourceBuffer.addEventListener('updateend', () => {
489
+ this.sourceBuffer.addEventListener("updateend", () => {
490
490
  if (!this.sourceBuffer) {
491
- if (this.debugging) console.log('MEWS: updateend but sourceBuffer is null');
491
+ if (this.debugging) console.log("MEWS: updateend but sourceBuffer is null");
492
492
  return;
493
493
  }
494
494
 
@@ -506,33 +506,38 @@ export class SourceBufferManager {
506
506
 
507
507
  for (let i = 0; i < do_funcs.length; i++) {
508
508
  if (!this.sourceBuffer) {
509
- if (this.debugging) console.warn('MEWS: doing updateend but sb was reset');
509
+ if (this.debugging) console.warn("MEWS: doing updateend but sb was reset");
510
510
  break;
511
511
  }
512
512
  if (this.sourceBuffer.updating) {
513
513
  // Still updating, requeue remaining functions (mews.js:255-259)
514
514
  this.do_on_updateend = this.do_on_updateend.concat(do_funcs.slice(i));
515
- if (this.debugging) console.warn('MEWS: doing updateend but was interrupted');
515
+ if (this.debugging) console.warn("MEWS: doing updateend but was interrupted");
516
516
  break;
517
517
  }
518
518
  try {
519
519
  // Pass remaining functions as argument (mews.js:261)
520
520
  do_funcs[i](i < do_funcs.length - 1 ? do_funcs.slice(i + 1) : []);
521
521
  } catch (e) {
522
- console.error('MEWS: error in do_on_updateend:', e);
522
+ console.error("MEWS: error in do_on_updateend:", e);
523
523
  }
524
524
  }
525
525
 
526
526
  this._busy = false;
527
527
 
528
528
  // Process queued data (mews.js:269-272)
529
- if (this.sourceBuffer && this.queue.length > 0 && !this.sourceBuffer.updating && !this.videoElement.error) {
529
+ if (
530
+ this.sourceBuffer &&
531
+ this.queue.length > 0 &&
532
+ !this.sourceBuffer.updating &&
533
+ !this.videoElement.error
534
+ ) {
530
535
  this._append(this.queue.shift()!);
531
536
  }
532
537
  });
533
538
 
534
- this.sourceBuffer.addEventListener('error', () => {
535
- this.onError('SourceBuffer error');
539
+ this.sourceBuffer.addEventListener("error", () => {
540
+ this.onError("SourceBuffer error");
536
541
  });
537
542
  }
538
543
 
@@ -7,7 +7,7 @@
7
7
  * Ported from reference: mews.js:387-883
8
8
  */
9
9
 
10
- import type { WebSocketManagerOptions, MewsMessage, MewsMessageListener } from './types';
10
+ import type { WebSocketManagerOptions, MewsMessage, MewsMessageListener } from "./types";
11
11
 
12
12
  export class WebSocketManager {
13
13
  private ws: WebSocket | null = null;
@@ -44,7 +44,7 @@ export class WebSocketManager {
44
44
 
45
45
  // Protocol mismatch check (non-fatal warning)
46
46
  try {
47
- const pageProto = window.location.protocol.replace(/^http/, 'ws');
47
+ const pageProto = window.location.protocol.replace(/^http/, "ws");
48
48
  const srcProto = new URL(this.url, window.location.href).protocol;
49
49
  if (pageProto !== srcProto) {
50
50
  this.onError(`Protocol mismatch ${pageProto} vs ${srcProto}`);
@@ -52,7 +52,7 @@ export class WebSocketManager {
52
52
  } catch {}
53
53
 
54
54
  const ws = new WebSocket(this.url);
55
- ws.binaryType = 'arraybuffer';
55
+ ws.binaryType = "arraybuffer";
56
56
  this.ws = ws;
57
57
 
58
58
  ws.onopen = () => {
@@ -67,7 +67,7 @@ export class WebSocketManager {
67
67
  };
68
68
 
69
69
  ws.onerror = () => {
70
- this.onError('WebSocket error');
70
+ this.onError("WebSocket error");
71
71
  };
72
72
 
73
73
  ws.onclose = () => {
@@ -83,7 +83,7 @@ export class WebSocketManager {
83
83
  }, backoff);
84
84
  } else {
85
85
  this.onClose();
86
- this.onError('WebSocket closed');
86
+ this.onError("WebSocket closed");
87
87
  }
88
88
  };
89
89
  }
@@ -101,7 +101,7 @@ export class WebSocketManager {
101
101
  if (this.isDestroyed) return false;
102
102
 
103
103
  if (retry > MAX_RETRIES) {
104
- this.onError('Too many send retries');
104
+ this.onError("Too many send retries");
105
105
  return false;
106
106
  }
107
107
 
@@ -156,7 +156,7 @@ export class WebSocketManager {
156
156
  }
157
157
 
158
158
  destroy(): void {
159
- console.debug('[WebSocketManager] destroy() called');
159
+ console.debug("[WebSocketManager] destroy() called");
160
160
  this.isDestroyed = true;
161
161
  this.clearReconnectTimer();
162
162
 
@@ -175,7 +175,7 @@ export class WebSocketManager {
175
175
  } catch {}
176
176
  this.ws = null;
177
177
  }
178
- console.debug('[WebSocketManager] destroy() completed');
178
+ console.debug("[WebSocketManager] destroy() completed");
179
179
  }
180
180
 
181
181
  isConnected(): boolean {
@@ -232,7 +232,7 @@ export class WebSocketManager {
232
232
  callbacks[i](msg);
233
233
  } catch (e) {
234
234
  // Don't let one listener crash others
235
- console.error('MEWS listener error:', e);
235
+ console.error("MEWS listener error:", e);
236
236
  }
237
237
  }
238
238
  }