@livepeer-frameworks/player-core 0.0.4 → 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 (82) hide show
  1. package/README.md +21 -6
  2. package/dist/cjs/index.js +792 -146
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/esm/index.js +792 -146
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/player.css +185 -373
  7. package/dist/types/core/GatewayClient.d.ts +3 -4
  8. package/dist/types/core/InteractionController.d.ts +12 -0
  9. package/dist/types/core/MetaTrackManager.d.ts +1 -1
  10. package/dist/types/core/PlayerController.d.ts +18 -2
  11. package/dist/types/core/PlayerInterface.d.ts +10 -0
  12. package/dist/types/core/SeekingUtils.d.ts +3 -1
  13. package/dist/types/core/StreamStateClient.d.ts +1 -1
  14. package/dist/types/players/HlsJsPlayer.d.ts +8 -0
  15. package/dist/types/players/MewsWsPlayer/index.d.ts +1 -1
  16. package/dist/types/players/VideoJsPlayer.d.ts +12 -4
  17. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +1 -1
  18. package/dist/types/players/WebCodecsPlayer/index.d.ts +11 -0
  19. package/dist/types/players/WebCodecsPlayer/types.d.ts +25 -3
  20. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +20 -2
  21. package/dist/types/types.d.ts +32 -1
  22. package/dist/types/vanilla/FrameWorksPlayer.d.ts +5 -5
  23. package/dist/types/vanilla/index.d.ts +3 -3
  24. package/dist/workers/decoder.worker.js +183 -6
  25. package/dist/workers/decoder.worker.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/core/ABRController.ts +38 -36
  28. package/src/core/CodecUtils.ts +50 -47
  29. package/src/core/Disposable.ts +4 -4
  30. package/src/core/EventEmitter.ts +1 -1
  31. package/src/core/GatewayClient.ts +48 -48
  32. package/src/core/InteractionController.ts +89 -82
  33. package/src/core/LiveDurationProxy.ts +14 -16
  34. package/src/core/MetaTrackManager.ts +74 -66
  35. package/src/core/MistReporter.ts +72 -45
  36. package/src/core/MistSignaling.ts +59 -56
  37. package/src/core/PlayerController.ts +724 -375
  38. package/src/core/PlayerInterface.ts +89 -59
  39. package/src/core/PlayerManager.ts +118 -123
  40. package/src/core/PlayerRegistry.ts +59 -42
  41. package/src/core/QualityMonitor.ts +38 -31
  42. package/src/core/ScreenWakeLockManager.ts +8 -9
  43. package/src/core/SeekingUtils.ts +31 -22
  44. package/src/core/StreamStateClient.ts +75 -69
  45. package/src/core/SubtitleManager.ts +25 -23
  46. package/src/core/TelemetryReporter.ts +34 -31
  47. package/src/core/TimeFormat.ts +13 -17
  48. package/src/core/TimerManager.ts +25 -9
  49. package/src/core/UrlUtils.ts +20 -17
  50. package/src/core/detector.ts +44 -44
  51. package/src/core/index.ts +57 -48
  52. package/src/core/scorer.ts +137 -138
  53. package/src/core/selector.ts +2 -6
  54. package/src/global.d.ts +1 -1
  55. package/src/index.ts +46 -35
  56. package/src/players/DashJsPlayer.ts +175 -114
  57. package/src/players/HlsJsPlayer.ts +154 -76
  58. package/src/players/MewsWsPlayer/SourceBufferManager.ts +44 -39
  59. package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -10
  60. package/src/players/MewsWsPlayer/index.ts +196 -154
  61. package/src/players/MewsWsPlayer/types.ts +21 -21
  62. package/src/players/MistPlayer.ts +46 -27
  63. package/src/players/MistWebRTCPlayer/index.ts +175 -129
  64. package/src/players/NativePlayer.ts +203 -143
  65. package/src/players/VideoJsPlayer.ts +200 -146
  66. package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
  67. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
  68. package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
  69. package/src/players/WebCodecsPlayer/SyncController.ts +46 -55
  70. package/src/players/WebCodecsPlayer/WebSocketController.ts +67 -69
  71. package/src/players/WebCodecsPlayer/index.ts +280 -220
  72. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
  73. package/src/players/WebCodecsPlayer/types.ts +81 -53
  74. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +255 -192
  75. package/src/players/WebCodecsPlayer/worker/types.ts +33 -29
  76. package/src/players/index.ts +8 -8
  77. package/src/styles/animations.css +2 -1
  78. package/src/styles/player.css +182 -356
  79. package/src/styles/tailwind.css +473 -159
  80. package/src/types.ts +75 -33
  81. package/src/vanilla/FrameWorksPlayer.ts +34 -19
  82. package/src/vanilla/index.ts +7 -7
@@ -15,7 +15,7 @@
15
15
  * 5. Limit lowering rate to prevent oscillation
16
16
  */
17
17
 
18
- import type { JitterState } from './types';
18
+ import type { JitterState } from "./types";
19
19
 
20
20
  /** Default sliding window size for chunk tracking */
21
21
  const DEFAULT_CHUNK_WINDOW = 8;
@@ -156,8 +156,7 @@ export class JitterTracker {
156
156
  // Calculate new weighted average
157
157
  if (this.peaks.length > 0) {
158
158
  const maxPeak = Math.max(...this.peaks);
159
- const avgPeak =
160
- this.peaks.reduce((sum, p) => sum + p, 0) / this.peaks.length;
159
+ const avgPeak = this.peaks.reduce((sum, p) => sum + p, 0) / this.peaks.length;
161
160
 
162
161
  // Weighted: emphasize max peak for safety
163
162
  let weighted = (avgPeak + maxPeak * 2) / 3 + MIN_JITTER;
@@ -193,8 +192,8 @@ export class JitterTracker {
193
192
  /**
194
193
  * Set playback speed for jitter calculation
195
194
  */
196
- setSpeed(speed: number | 'auto'): void {
197
- const newSpeed = speed === 'auto' ? 1 : speed;
195
+ setSpeed(speed: number | "auto"): void {
196
+ const newSpeed = speed === "auto" ? 1 : speed;
198
197
  if (newSpeed !== this.speed) {
199
198
  this.speed = newSpeed;
200
199
  this.reset();
@@ -267,8 +266,8 @@ export class MultiTrackJitterTracker {
267
266
  /**
268
267
  * Set playback speed for all trackers
269
268
  */
270
- setSpeed(speed: number | 'auto'): void {
271
- this.globalSpeed = speed === 'auto' ? 1 : speed;
269
+ setSpeed(speed: number | "auto"): void {
270
+ this.globalSpeed = speed === "auto" ? 1 : speed;
272
271
  for (const tracker of this.trackers.values()) {
273
272
  tracker.setSpeed(speed);
274
273
  }
@@ -10,7 +10,7 @@
10
10
  * - If buffer < desired * speedDownThreshold → slow down to minSpeedDown
11
11
  */
12
12
 
13
- import type { LatencyProfile, LatencyProfileName } from './types';
13
+ import type { LatencyProfile, LatencyProfileName } from "./types";
14
14
 
15
15
  /**
16
16
  * Ultra-low latency profile
@@ -19,15 +19,15 @@ import type { LatencyProfile, LatencyProfileName } from './types';
19
19
  * Trade-offs: May stutter on poor networks
20
20
  */
21
21
  export const ULTRA_LOW_PROFILE: LatencyProfile = {
22
- name: 'Ultra Low Latency',
23
- keepAway: 50, // 50ms base buffer
24
- jitterMultiplier: 1.0, // Full jitter protection (no extra margin)
25
- speedUpThreshold: 1.5, // Speed up when buffer > 150% of desired
26
- speedDownThreshold: 0.5, // Slow down when buffer < 50% of desired
27
- maxSpeedUp: 1.08, // 8% speed up max
28
- minSpeedDown: 0.95, // 5% slow down max
29
- audioBufferMs: 100, // 100ms audio ring buffer
30
- optimizeForLatency: true, // Tell decoders to optimize for latency
22
+ name: "Ultra Low Latency",
23
+ keepAway: 50, // 50ms base buffer
24
+ jitterMultiplier: 1.0, // Full jitter protection (no extra margin)
25
+ speedUpThreshold: 1.5, // Speed up when buffer > 150% of desired
26
+ speedDownThreshold: 0.5, // Slow down when buffer < 50% of desired
27
+ maxSpeedUp: 1.08, // 8% speed up max
28
+ minSpeedDown: 0.95, // 5% slow down max
29
+ audioBufferMs: 100, // 100ms audio ring buffer
30
+ optimizeForLatency: true, // Tell decoders to optimize for latency
31
31
  };
32
32
 
33
33
  /**
@@ -37,14 +37,14 @@ export const ULTRA_LOW_PROFILE: LatencyProfile = {
37
37
  * Trade-offs: Balanced latency/stability
38
38
  */
39
39
  export const LOW_PROFILE: LatencyProfile = {
40
- name: 'Low Latency',
41
- keepAway: 100, // 100ms base buffer
42
- jitterMultiplier: 1.2, // 20% extra jitter protection
43
- speedUpThreshold: 2.0, // Speed up when buffer > 200% of desired
44
- speedDownThreshold: 0.6, // Slow down when buffer < 60% of desired
45
- maxSpeedUp: 1.05, // 5% speed up max (legacy default)
46
- minSpeedDown: 0.98, // 2% slow down max (legacy default)
47
- audioBufferMs: 150, // 150ms audio ring buffer
40
+ name: "Low Latency",
41
+ keepAway: 100, // 100ms base buffer
42
+ jitterMultiplier: 1.2, // 20% extra jitter protection
43
+ speedUpThreshold: 2.0, // Speed up when buffer > 200% of desired
44
+ speedDownThreshold: 0.6, // Slow down when buffer < 60% of desired
45
+ maxSpeedUp: 1.05, // 5% speed up max (legacy default)
46
+ minSpeedDown: 0.98, // 2% slow down max (legacy default)
47
+ audioBufferMs: 150, // 150ms audio ring buffer
48
48
  optimizeForLatency: true,
49
49
  };
50
50
 
@@ -55,14 +55,14 @@ export const LOW_PROFILE: LatencyProfile = {
55
55
  * Trade-offs: Prioritizes stability over latency
56
56
  */
57
57
  export const BALANCED_PROFILE: LatencyProfile = {
58
- name: 'Balanced',
59
- keepAway: 200, // 200ms base buffer
60
- jitterMultiplier: 1.5, // 50% extra jitter protection
61
- speedUpThreshold: 2.5, // Speed up when buffer > 250% of desired
62
- speedDownThreshold: 0.5, // Slow down when buffer < 50% of desired
63
- maxSpeedUp: 1.03, // 3% speed up max
64
- minSpeedDown: 0.97, // 3% slow down max
65
- audioBufferMs: 200, // 200ms audio ring buffer
58
+ name: "Balanced",
59
+ keepAway: 200, // 200ms base buffer
60
+ jitterMultiplier: 1.5, // 50% extra jitter protection
61
+ speedUpThreshold: 2.5, // Speed up when buffer > 250% of desired
62
+ speedDownThreshold: 0.5, // Slow down when buffer < 50% of desired
63
+ maxSpeedUp: 1.03, // 3% speed up max
64
+ minSpeedDown: 0.97, // 3% slow down max
65
+ audioBufferMs: 200, // 200ms audio ring buffer
66
66
  optimizeForLatency: false, // Let decoders optimize for quality
67
67
  };
68
68
 
@@ -73,14 +73,14 @@ export const BALANCED_PROFILE: LatencyProfile = {
73
73
  * Trade-offs: Maximum stability, higher latency
74
74
  */
75
75
  export const QUALITY_PROFILE: LatencyProfile = {
76
- name: 'Quality Priority',
77
- keepAway: 500, // 500ms base buffer (legacy VOD default)
78
- jitterMultiplier: 2.0, // Double jitter protection
79
- speedUpThreshold: 3.0, // Speed up when buffer > 300% of desired
80
- speedDownThreshold: 0.4, // Slow down when buffer < 40% of desired
81
- maxSpeedUp: 1.02, // 2% speed up max
82
- minSpeedDown: 0.98, // 2% slow down max
83
- audioBufferMs: 300, // 300ms audio ring buffer
76
+ name: "Quality Priority",
77
+ keepAway: 500, // 500ms base buffer (legacy VOD default)
78
+ jitterMultiplier: 2.0, // Double jitter protection
79
+ speedUpThreshold: 3.0, // Speed up when buffer > 300% of desired
80
+ speedDownThreshold: 0.4, // Slow down when buffer < 40% of desired
81
+ maxSpeedUp: 1.02, // 2% speed up max
82
+ minSpeedDown: 0.98, // 2% slow down max
83
+ audioBufferMs: 300, // 300ms audio ring buffer
84
84
  optimizeForLatency: false,
85
85
  };
86
86
 
@@ -88,10 +88,10 @@ export const QUALITY_PROFILE: LatencyProfile = {
88
88
  * All available latency profiles
89
89
  */
90
90
  export const LATENCY_PROFILES: Record<LatencyProfileName, LatencyProfile> = {
91
- 'ultra-low': ULTRA_LOW_PROFILE,
92
- 'low': LOW_PROFILE,
93
- 'balanced': BALANCED_PROFILE,
94
- 'quality': QUALITY_PROFILE,
91
+ "ultra-low": ULTRA_LOW_PROFILE,
92
+ low: LOW_PROFILE,
93
+ balanced: BALANCED_PROFILE,
94
+ quality: QUALITY_PROFILE,
95
95
  };
96
96
 
97
97
  /**
@@ -103,7 +103,7 @@ export function getLatencyProfile(name?: LatencyProfileName): LatencyProfile {
103
103
  if (name && name in LATENCY_PROFILES) {
104
104
  return LATENCY_PROFILES[name];
105
105
  }
106
- return LATENCY_PROFILES['low'];
106
+ return LATENCY_PROFILES["low"];
107
107
  }
108
108
 
109
109
  /**
@@ -116,7 +116,7 @@ export function mergeLatencyProfile(
116
116
  base: LatencyProfileName | LatencyProfile,
117
117
  custom?: Partial<LatencyProfile>
118
118
  ): LatencyProfile {
119
- const baseProfile = typeof base === 'string' ? getLatencyProfile(base) : base;
119
+ const baseProfile = typeof base === "string" ? getLatencyProfile(base) : base;
120
120
 
121
121
  if (!custom) {
122
122
  return baseProfile;
@@ -140,12 +140,12 @@ export function selectDefaultProfile(
140
140
  preferLowLatency = false
141
141
  ): LatencyProfileName {
142
142
  if (!isLive) {
143
- return 'quality';
143
+ return "quality";
144
144
  }
145
145
 
146
146
  if (preferLowLatency) {
147
- return 'low';
147
+ return "low";
148
148
  }
149
149
 
150
- return 'balanced';
150
+ return "balanced";
151
151
  }
@@ -13,7 +13,7 @@
13
13
  * Combined presentation time = timestamp + offset
14
14
  */
15
15
 
16
- import type { RawChunk, ChunkType } from './types';
16
+ import type { RawChunk, ChunkType } from "./types";
17
17
 
18
18
  const HEADER_LENGTH = 12;
19
19
 
@@ -23,11 +23,11 @@ const HEADER_LENGTH = 12;
23
23
  function parseChunkType(typeByte: number): ChunkType {
24
24
  switch (typeByte) {
25
25
  case 1:
26
- return 'key';
26
+ return "key";
27
27
  case 2:
28
- return 'init';
28
+ return "init";
29
29
  default:
30
- return 'delta';
30
+ return "delta";
31
31
  }
32
32
  }
33
33
 
@@ -92,14 +92,14 @@ export function getPresentationTimestamp(chunk: RawChunk): number {
92
92
  * Check if this chunk is a keyframe
93
93
  */
94
94
  export function isKeyframe(chunk: RawChunk): boolean {
95
- return chunk.type === 'key';
95
+ return chunk.type === "key";
96
96
  }
97
97
 
98
98
  /**
99
99
  * Check if this chunk contains codec initialization data
100
100
  */
101
101
  export function isInitData(chunk: RawChunk): boolean {
102
- return chunk.type === 'init';
102
+ return chunk.type === "init";
103
103
  }
104
104
 
105
105
  /**
@@ -132,12 +132,12 @@ export class RawChunkParser {
132
132
  const chunk = parseRawChunk(data);
133
133
 
134
134
  if (this.debug) {
135
- console.log('▶️', formatChunkForLog(chunk));
135
+ console.log("▶️", formatChunkForLog(chunk));
136
136
  }
137
137
 
138
138
  return chunk;
139
139
  } catch (err) {
140
- console.error('▶️ Failed to parse chunk:', err);
140
+ console.error("▶️ Failed to parse chunk:", err);
141
141
  return null;
142
142
  }
143
143
  }
@@ -145,7 +145,7 @@ export class RawChunkParser {
145
145
  /**
146
146
  * Set debug mode
147
147
  */
148
- setDebug(enabled: boolean | 'verbose'): void {
149
- this.debug = enabled === 'verbose' || enabled === true;
148
+ setDebug(enabled: boolean | "verbose"): void {
149
+ this.debug = enabled === "verbose" || enabled === true;
150
150
  }
151
151
  }
@@ -14,18 +14,13 @@
14
14
  * - TypeScript types
15
15
  */
16
16
 
17
- import type {
18
- LatencyProfile,
19
- BufferState,
20
- SyncState,
21
- TrackInfo,
22
- } from './types';
23
- import { MultiTrackJitterTracker } from './JitterBuffer';
24
- import { getLatencyProfile } from './LatencyProfiles';
17
+ import type { LatencyProfile, SyncState, TrackInfo } from "./types";
18
+ import { MultiTrackJitterTracker } from "./JitterBuffer";
19
+ import { getLatencyProfile } from "./LatencyProfiles";
25
20
 
26
21
  /** Events emitted by SyncController */
27
22
  export interface SyncControllerEvents {
28
- speedchange: { speed: number; reason: 'catchup' | 'slowdown' | 'normal' };
23
+ speedchange: { speed: number; reason: "catchup" | "slowdown" | "normal" };
29
24
  bufferlow: { current: number; desired: number };
30
25
  bufferhigh: { current: number; desired: number };
31
26
  underrun: void;
@@ -34,9 +29,7 @@ export interface SyncControllerEvents {
34
29
  seekcomplete: { seekId: number };
35
30
  }
36
31
 
37
- type EventListener<K extends keyof SyncControllerEvents> = (
38
- data: SyncControllerEvents[K]
39
- ) => void;
32
+ type EventListener<K extends keyof SyncControllerEvents> = (data: SyncControllerEvents[K]) => void;
40
33
 
41
34
  /** Seek state tracking */
42
35
  interface SeekState {
@@ -92,13 +85,15 @@ export class SyncController {
92
85
  private onSpeedChange?: (main: number, tweak: number) => void;
93
86
  private onFastForwardRequest?: (ms: number) => void;
94
87
 
95
- constructor(options: {
96
- profile?: LatencyProfile;
97
- isLive?: boolean;
98
- onSpeedChange?: (main: number, tweak: number) => void;
99
- onFastForwardRequest?: (ms: number) => void;
100
- } = {}) {
101
- this.profile = options.profile ?? getLatencyProfile('low');
88
+ constructor(
89
+ options: {
90
+ profile?: LatencyProfile;
91
+ isLive?: boolean;
92
+ onSpeedChange?: (main: number, tweak: number) => void;
93
+ onFastForwardRequest?: (ms: number) => void;
94
+ } = {}
95
+ ) {
96
+ this.profile = options.profile ?? getLatencyProfile("low");
102
97
  this.isLive = options.isLive ?? true;
103
98
  this.onSpeedChange = options.onSpeedChange;
104
99
  this.onFastForwardRequest = options.onFastForwardRequest;
@@ -169,8 +164,10 @@ export class SyncController {
169
164
  */
170
165
  getDesiredBuffer(): number {
171
166
  // Chrome needs larger base buffer (per mews.js:482)
172
- const isChrome = typeof navigator !== 'undefined' &&
173
- /Chrome/.test(navigator.userAgent) && !/Edge|Edg/.test(navigator.userAgent);
167
+ const isChrome =
168
+ typeof navigator !== "undefined" &&
169
+ /Chrome/.test(navigator.userAgent) &&
170
+ !/Edge|Edg/.test(navigator.userAgent);
174
171
  const baseBuffer = isChrome ? 1000 : 100;
175
172
 
176
173
  const serverDelay = this.getServerDelay();
@@ -210,35 +207,37 @@ export class SyncController {
210
207
  // - Buffer is more than jitter + safety margin (not stalled)
211
208
  // - Buffer is less than 1s above desired
212
209
  // - Cooldown period has passed
213
- if (this.isLive &&
214
- currentBufferMs < this.liveCatchupThresholdMs &&
215
- currentBufferMs > Math.max(jitterMs * 1.1, jitterMs + 250) &&
216
- (currentBufferMs - desired) < 1000 &&
217
- (now - this.lastLiveCatchup) > this.liveCatchupCooldown) {
210
+ if (
211
+ this.isLive &&
212
+ currentBufferMs < this.liveCatchupThresholdMs &&
213
+ currentBufferMs > Math.max(jitterMs * 1.1, jitterMs + 250) &&
214
+ currentBufferMs - desired < 1000 &&
215
+ now - this.lastLiveCatchup > this.liveCatchupCooldown
216
+ ) {
218
217
  this.lastLiveCatchup = now;
219
218
  this.requestFastForward(this.liveCatchupRequestMs);
220
- this.emit('livecatchup', { fastForwardMs: this.liveCatchupRequestMs });
219
+ this.emit("livecatchup", { fastForwardMs: this.liveCatchupRequestMs });
221
220
  }
222
221
 
223
222
  // Determine if speed adjustment needed
224
223
  if (ratio > this.profile.speedUpThreshold && this.isLive) {
225
224
  // Buffer too high - speed up to catch up to live edge
226
- this.setTweakSpeed(this.profile.maxSpeedUp, 'catchup');
225
+ this.setTweakSpeed(this.profile.maxSpeedUp, "catchup");
227
226
  } else if (ratio < this.profile.speedDownThreshold) {
228
227
  // Buffer too low - slow down to build buffer
229
- this.setTweakSpeed(this.profile.minSpeedDown, 'slowdown');
228
+ this.setTweakSpeed(this.profile.minSpeedDown, "slowdown");
230
229
 
231
230
  // Request additional data if critically low
232
231
  if (ratio < 0.3 && this.isLive) {
233
232
  this.requestFastForward(desired - currentBufferMs);
234
- this.emit('underrun', undefined);
233
+ this.emit("underrun", undefined);
235
234
  }
236
235
 
237
- this.emit('bufferlow', { current: currentBufferMs, desired });
236
+ this.emit("bufferlow", { current: currentBufferMs, desired });
238
237
  } else {
239
238
  // Buffer in acceptable range - return to normal speed
240
239
  if (this.tweakSpeed !== 1) {
241
- this.setTweakSpeed(1, 'normal');
240
+ this.setTweakSpeed(1, "normal");
242
241
  }
243
242
  }
244
243
 
@@ -258,13 +257,14 @@ export class SyncController {
258
257
  desired,
259
258
  ratio: desired > 0 ? buffer / desired : 1,
260
259
  },
261
- jitter: this.jitterTracker.getMax() > 0
262
- ? {
263
- current: 0, // Would need per-frame tracking
264
- peak: 0,
265
- weighted: this.jitterTracker.getMax(),
266
- }
267
- : { current: 0, peak: 0, weighted: 0 },
260
+ jitter:
261
+ this.jitterTracker.getMax() > 0
262
+ ? {
263
+ current: 0, // Would need per-frame tracking
264
+ peak: 0,
265
+ weighted: this.jitterTracker.getMax(),
266
+ }
267
+ : { current: 0, peak: 0, weighted: 0 },
268
268
  playbackSpeed: this.getCombinedSpeed(),
269
269
  serverTime: this.serverTime,
270
270
  serverDelay: this.getServerDelay(),
@@ -291,14 +291,11 @@ export class SyncController {
291
291
  /**
292
292
  * Set tweak speed (automatic adjustment)
293
293
  */
294
- private setTweakSpeed(
295
- speed: number,
296
- reason: 'catchup' | 'slowdown' | 'normal'
297
- ): void {
294
+ private setTweakSpeed(speed: number, reason: "catchup" | "slowdown" | "normal"): void {
298
295
  if (this.tweakSpeed !== speed) {
299
296
  this.tweakSpeed = speed;
300
297
  this.notifySpeedChange();
301
- this.emit('speedchange', { speed: this.getCombinedSpeed(), reason });
298
+ this.emit("speedchange", { speed: this.getCombinedSpeed(), reason });
302
299
  }
303
300
  }
304
301
 
@@ -338,7 +335,7 @@ export class SyncController {
338
335
  // Reset jitter tracking on seek
339
336
  this.jitterTracker.reset();
340
337
 
341
- this.emit('seekstart', { seekId: this.seekState.id, time: targetTimeMs });
338
+ this.emit("seekstart", { seekId: this.seekState.id, time: targetTimeMs });
342
339
 
343
340
  return this.seekState.id;
344
341
  }
@@ -356,7 +353,7 @@ export class SyncController {
356
353
  completeSeek(seekId: number): void {
357
354
  if (this.seekState.id === seekId) {
358
355
  this.seekState.active = false;
359
- this.emit('seekcomplete', { seekId });
356
+ this.emit("seekcomplete", { seekId });
360
357
  }
361
358
  }
362
359
 
@@ -383,7 +380,7 @@ export class SyncController {
383
380
  /**
384
381
  * Register a new track
385
382
  */
386
- addTrack(trackIndex: number, track: TrackInfo): void {
383
+ addTrack(_trackIndex: number, _track: TrackInfo): void {
387
384
  // Jitter tracking will be initialized on first chunk
388
385
  }
389
386
 
@@ -421,20 +418,14 @@ export class SyncController {
421
418
  // Event Emitter
422
419
  // ============================================================================
423
420
 
424
- on<K extends keyof SyncControllerEvents>(
425
- event: K,
426
- listener: EventListener<K>
427
- ): void {
421
+ on<K extends keyof SyncControllerEvents>(event: K, listener: EventListener<K>): void {
428
422
  if (!this.listeners.has(event)) {
429
423
  this.listeners.set(event, new Set());
430
424
  }
431
425
  this.listeners.get(event)!.add(listener);
432
426
  }
433
427
 
434
- off<K extends keyof SyncControllerEvents>(
435
- event: K,
436
- listener: EventListener<K>
437
- ): void {
428
+ off<K extends keyof SyncControllerEvents>(event: K, listener: EventListener<K>): void {
438
429
  this.listeners.get(event)?.delete(listener);
439
430
  }
440
431