@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.
- package/dist/cjs/index.js +19493 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.js +19398 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/player.css +2140 -0
- package/dist/types/core/ABRController.d.ts +164 -0
- package/dist/types/core/CodecUtils.d.ts +54 -0
- package/dist/types/core/Disposable.d.ts +61 -0
- package/dist/types/core/EventEmitter.d.ts +73 -0
- package/dist/types/core/GatewayClient.d.ts +144 -0
- package/dist/types/core/InteractionController.d.ts +121 -0
- package/dist/types/core/LiveDurationProxy.d.ts +102 -0
- package/dist/types/core/MetaTrackManager.d.ts +220 -0
- package/dist/types/core/MistReporter.d.ts +163 -0
- package/dist/types/core/MistSignaling.d.ts +148 -0
- package/dist/types/core/PlayerController.d.ts +665 -0
- package/dist/types/core/PlayerInterface.d.ts +230 -0
- package/dist/types/core/PlayerManager.d.ts +182 -0
- package/dist/types/core/PlayerRegistry.d.ts +27 -0
- package/dist/types/core/QualityMonitor.d.ts +184 -0
- package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
- package/dist/types/core/SeekingUtils.d.ts +142 -0
- package/dist/types/core/StreamStateClient.d.ts +108 -0
- package/dist/types/core/SubtitleManager.d.ts +111 -0
- package/dist/types/core/TelemetryReporter.d.ts +79 -0
- package/dist/types/core/TimeFormat.d.ts +97 -0
- package/dist/types/core/TimerManager.d.ts +83 -0
- package/dist/types/core/UrlUtils.d.ts +81 -0
- package/dist/types/core/detector.d.ts +149 -0
- package/dist/types/core/index.d.ts +49 -0
- package/dist/types/core/scorer.d.ts +167 -0
- package/dist/types/core/selector.d.ts +9 -0
- package/dist/types/index.d.ts +45 -0
- package/dist/types/lib/utils.d.ts +2 -0
- package/dist/types/players/DashJsPlayer.d.ts +102 -0
- package/dist/types/players/HlsJsPlayer.d.ts +70 -0
- package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
- package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
- package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
- package/dist/types/players/MistPlayer.d.ts +25 -0
- package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
- package/dist/types/players/NativePlayer.d.ts +143 -0
- package/dist/types/players/VideoJsPlayer.d.ts +59 -0
- package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
- package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
- package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
- package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
- package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
- package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
- package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
- package/dist/types/players/index.d.ts +14 -0
- package/dist/types/styles/index.d.ts +11 -0
- package/dist/types/types.d.ts +363 -0
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
- package/dist/types/vanilla/index.d.ts +19 -0
- package/dist/workers/decoder.worker.js +989 -0
- package/dist/workers/decoder.worker.js.map +1 -0
- package/package.json +80 -0
- package/src/core/ABRController.ts +550 -0
- package/src/core/CodecUtils.ts +257 -0
- package/src/core/Disposable.ts +120 -0
- package/src/core/EventEmitter.ts +113 -0
- package/src/core/GatewayClient.ts +439 -0
- package/src/core/InteractionController.ts +712 -0
- package/src/core/LiveDurationProxy.ts +270 -0
- package/src/core/MetaTrackManager.ts +753 -0
- package/src/core/MistReporter.ts +543 -0
- package/src/core/MistSignaling.ts +346 -0
- package/src/core/PlayerController.ts +2829 -0
- package/src/core/PlayerInterface.ts +432 -0
- package/src/core/PlayerManager.ts +900 -0
- package/src/core/PlayerRegistry.ts +149 -0
- package/src/core/QualityMonitor.ts +597 -0
- package/src/core/ScreenWakeLockManager.ts +163 -0
- package/src/core/SeekingUtils.ts +364 -0
- package/src/core/StreamStateClient.ts +457 -0
- package/src/core/SubtitleManager.ts +297 -0
- package/src/core/TelemetryReporter.ts +308 -0
- package/src/core/TimeFormat.ts +205 -0
- package/src/core/TimerManager.ts +209 -0
- package/src/core/UrlUtils.ts +179 -0
- package/src/core/detector.ts +382 -0
- package/src/core/index.ts +140 -0
- package/src/core/scorer.ts +553 -0
- package/src/core/selector.ts +16 -0
- package/src/global.d.ts +11 -0
- package/src/index.ts +75 -0
- package/src/lib/utils.ts +6 -0
- package/src/players/DashJsPlayer.ts +642 -0
- package/src/players/HlsJsPlayer.ts +483 -0
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
- package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
- package/src/players/MewsWsPlayer/index.ts +1065 -0
- package/src/players/MewsWsPlayer/types.ts +106 -0
- package/src/players/MistPlayer.ts +188 -0
- package/src/players/MistWebRTCPlayer/index.ts +703 -0
- package/src/players/NativePlayer.ts +820 -0
- package/src/players/VideoJsPlayer.ts +643 -0
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
- package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
- package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
- package/src/players/WebCodecsPlayer/index.ts +1650 -0
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
- package/src/players/WebCodecsPlayer/types.ts +542 -0
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
- package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
- package/src/players/index.ts +22 -0
- package/src/styles/animations.css +21 -0
- package/src/styles/index.ts +52 -0
- package/src/styles/player.css +2126 -0
- package/src/styles/tailwind.css +1015 -0
- package/src/types.ts +421 -0
- package/src/vanilla/FrameWorksPlayer.ts +367 -0
- package/src/vanilla/index.ts +22 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlayerController.ts
|
|
3
|
+
*
|
|
4
|
+
* Main headless orchestrator for the player. This class encapsulates all business logic
|
|
5
|
+
* (gateway resolution, stream state polling, player selection/initialization) in a
|
|
6
|
+
* framework-agnostic manner.
|
|
7
|
+
*
|
|
8
|
+
* Both React and Vanilla wrappers use this class internally.
|
|
9
|
+
*/
|
|
10
|
+
import { TypedEventEmitter } from './EventEmitter';
|
|
11
|
+
import { PlayerManager } from './PlayerManager';
|
|
12
|
+
import { type LatencyTier, type LiveThresholds } from './SeekingUtils';
|
|
13
|
+
import type { ABRMode, PlaybackQuality, ContentType } from '../types';
|
|
14
|
+
import type { ContentEndpoints, ContentMetadata, PlayerState, PlayerStateContext, StreamState } from '../types';
|
|
15
|
+
import type { StreamInfo, StreamSource, IPlayer } from './PlayerInterface';
|
|
16
|
+
export interface PlayerControllerConfig {
|
|
17
|
+
/** Content identifier (stream name) */
|
|
18
|
+
contentId: string;
|
|
19
|
+
/** Content type */
|
|
20
|
+
contentType: ContentType;
|
|
21
|
+
/** Pre-resolved endpoints (skip gateway) */
|
|
22
|
+
endpoints?: ContentEndpoints;
|
|
23
|
+
/** Gateway URL (for FrameWorks Gateway resolution) */
|
|
24
|
+
gatewayUrl?: string;
|
|
25
|
+
/** Direct MistServer base URL (bypasses Gateway, fetches json_{contentId}.js directly) */
|
|
26
|
+
mistUrl?: string;
|
|
27
|
+
/** Auth token for private streams */
|
|
28
|
+
authToken?: string;
|
|
29
|
+
/** Playback options */
|
|
30
|
+
autoplay?: boolean;
|
|
31
|
+
muted?: boolean;
|
|
32
|
+
controls?: boolean;
|
|
33
|
+
poster?: string;
|
|
34
|
+
/** Debug logging */
|
|
35
|
+
debug?: boolean;
|
|
36
|
+
/** Custom PlayerManager instance (optional, uses global by default) */
|
|
37
|
+
playerManager?: PlayerManager;
|
|
38
|
+
/** Force a specific player (e.g., 'hlsjs', 'dashjs', 'native') */
|
|
39
|
+
forcePlayer?: string;
|
|
40
|
+
/** Force a specific MIME type (e.g., 'html5/application/vnd.apple.mpegurl') */
|
|
41
|
+
forceType?: string;
|
|
42
|
+
/** Force a specific source index */
|
|
43
|
+
forceSource?: number;
|
|
44
|
+
/** Playback mode preference */
|
|
45
|
+
playbackMode?: 'auto' | 'low-latency' | 'quality' | 'vod';
|
|
46
|
+
}
|
|
47
|
+
export interface PlayerControllerEvents {
|
|
48
|
+
/** Player state changed */
|
|
49
|
+
stateChange: {
|
|
50
|
+
state: PlayerState;
|
|
51
|
+
context?: PlayerStateContext;
|
|
52
|
+
};
|
|
53
|
+
/** Stream state changed (for live streams) */
|
|
54
|
+
streamStateChange: {
|
|
55
|
+
state: StreamState;
|
|
56
|
+
};
|
|
57
|
+
/** Time update during playback */
|
|
58
|
+
timeUpdate: {
|
|
59
|
+
currentTime: number;
|
|
60
|
+
duration: number;
|
|
61
|
+
};
|
|
62
|
+
/** Error occurred */
|
|
63
|
+
error: {
|
|
64
|
+
error: string;
|
|
65
|
+
code?: string;
|
|
66
|
+
};
|
|
67
|
+
/** Error was cleared (auto-cleared or manually) */
|
|
68
|
+
errorCleared: void;
|
|
69
|
+
/** Player ready with video element */
|
|
70
|
+
ready: {
|
|
71
|
+
videoElement: HTMLVideoElement;
|
|
72
|
+
};
|
|
73
|
+
/** Controller destroyed */
|
|
74
|
+
destroyed: void;
|
|
75
|
+
/** Player/source was selected */
|
|
76
|
+
playerSelected: {
|
|
77
|
+
player: string;
|
|
78
|
+
source: StreamSource;
|
|
79
|
+
score: number;
|
|
80
|
+
};
|
|
81
|
+
/** Quality level changed (ABR switch) */
|
|
82
|
+
qualityChanged: {
|
|
83
|
+
fromLevel?: string;
|
|
84
|
+
toLevel: string;
|
|
85
|
+
};
|
|
86
|
+
/** Volume or mute state changed */
|
|
87
|
+
volumeChange: {
|
|
88
|
+
volume: number;
|
|
89
|
+
muted: boolean;
|
|
90
|
+
};
|
|
91
|
+
/** Fullscreen state changed */
|
|
92
|
+
fullscreenChange: {
|
|
93
|
+
isFullscreen: boolean;
|
|
94
|
+
};
|
|
95
|
+
/** Picture-in-Picture state changed */
|
|
96
|
+
pipChange: {
|
|
97
|
+
isPiP: boolean;
|
|
98
|
+
};
|
|
99
|
+
/** Loop mode changed */
|
|
100
|
+
loopChange: {
|
|
101
|
+
isLoopEnabled: boolean;
|
|
102
|
+
};
|
|
103
|
+
/** Playback rate changed */
|
|
104
|
+
speedChange: {
|
|
105
|
+
rate: number;
|
|
106
|
+
};
|
|
107
|
+
/** User skipped forward */
|
|
108
|
+
skipForward: {
|
|
109
|
+
seconds: number;
|
|
110
|
+
};
|
|
111
|
+
/** User skipped backward */
|
|
112
|
+
skipBackward: {
|
|
113
|
+
seconds: number;
|
|
114
|
+
};
|
|
115
|
+
/** Speed hold started (hold-for-2x gesture) */
|
|
116
|
+
holdSpeedStart: {
|
|
117
|
+
speed: number;
|
|
118
|
+
};
|
|
119
|
+
/** Speed hold ended */
|
|
120
|
+
holdSpeedEnd: void;
|
|
121
|
+
/** Captions/subtitles toggled */
|
|
122
|
+
captionsChange: {
|
|
123
|
+
enabled: boolean;
|
|
124
|
+
};
|
|
125
|
+
/** Seeking/live state changed - emitted on timeupdate when values change */
|
|
126
|
+
seekingStateChange: {
|
|
127
|
+
seekableStart: number;
|
|
128
|
+
liveEdge: number;
|
|
129
|
+
canSeek: boolean;
|
|
130
|
+
isNearLive: boolean;
|
|
131
|
+
isLive: boolean;
|
|
132
|
+
isWebRTC: boolean;
|
|
133
|
+
latencyTier: LatencyTier;
|
|
134
|
+
buffered: TimeRanges | null;
|
|
135
|
+
hasAudio: boolean;
|
|
136
|
+
supportsPlaybackRate: boolean;
|
|
137
|
+
};
|
|
138
|
+
/** User started hovering over player */
|
|
139
|
+
hoverStart: void;
|
|
140
|
+
/** User stopped hovering (after timeout) */
|
|
141
|
+
hoverEnd: void;
|
|
142
|
+
/** User became idle (no interaction for N seconds) */
|
|
143
|
+
interactionIdle: void;
|
|
144
|
+
/** User resumed interaction after being idle */
|
|
145
|
+
interactionActive: void;
|
|
146
|
+
/** Playback metadata updated */
|
|
147
|
+
metadataUpdate: {
|
|
148
|
+
currentTime: number;
|
|
149
|
+
duration: number;
|
|
150
|
+
bufferedAhead: number;
|
|
151
|
+
qualityScore?: number;
|
|
152
|
+
playerInfo?: {
|
|
153
|
+
name: string;
|
|
154
|
+
shortname: string;
|
|
155
|
+
};
|
|
156
|
+
sourceInfo?: {
|
|
157
|
+
url: string;
|
|
158
|
+
type: string;
|
|
159
|
+
};
|
|
160
|
+
isLive: boolean;
|
|
161
|
+
isBuffering: boolean;
|
|
162
|
+
isPaused: boolean;
|
|
163
|
+
volume: number;
|
|
164
|
+
muted: boolean;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Complete MistServer source type mapping
|
|
169
|
+
* Maps MistServer's `source[].type` field to player selection info
|
|
170
|
+
*
|
|
171
|
+
* type field = MIME type used for player selection
|
|
172
|
+
* hrn = human readable name for UI
|
|
173
|
+
* player = recommended player implementation
|
|
174
|
+
* supported = whether we have a working player for it
|
|
175
|
+
*/
|
|
176
|
+
export declare const MIST_SOURCE_TYPES: Record<string, {
|
|
177
|
+
hrn: string;
|
|
178
|
+
player: string;
|
|
179
|
+
supported: boolean;
|
|
180
|
+
}>;
|
|
181
|
+
/**
|
|
182
|
+
* Map Gateway protocol names to MistServer MIME types
|
|
183
|
+
* Gateway outputs use simplified protocol names like "HLS", "WHEP"
|
|
184
|
+
* while MistServer uses full MIME types
|
|
185
|
+
*/
|
|
186
|
+
export declare const PROTOCOL_TO_MIME: Record<string, string>;
|
|
187
|
+
/**
|
|
188
|
+
* Get the MIME type for a Gateway protocol name
|
|
189
|
+
*/
|
|
190
|
+
export declare function getMimeTypeForProtocol(protocol: string): string;
|
|
191
|
+
/**
|
|
192
|
+
* Get source type info for a MIME type
|
|
193
|
+
*/
|
|
194
|
+
export declare function getSourceTypeInfo(mimeType: string): {
|
|
195
|
+
hrn: string;
|
|
196
|
+
player: string;
|
|
197
|
+
supported: boolean;
|
|
198
|
+
} | undefined;
|
|
199
|
+
/**
|
|
200
|
+
* Build StreamInfo from Gateway ContentEndpoints.
|
|
201
|
+
*
|
|
202
|
+
* This function extracts playback sources and track information from
|
|
203
|
+
* the Gateway's resolved endpoint data. It handles:
|
|
204
|
+
* - Parsing `outputs` JSON string (GraphQL returns JSON scalar as string)
|
|
205
|
+
* - Converting output protocols to StreamSource format
|
|
206
|
+
* - Deriving track info from capabilities
|
|
207
|
+
*
|
|
208
|
+
* Use this for VOD/clip content where Gateway data is sufficient,
|
|
209
|
+
* without waiting for MistServer to load the stream.
|
|
210
|
+
*
|
|
211
|
+
* @param endpoints - ContentEndpoints from Gateway resolution
|
|
212
|
+
* @param contentId - Stream/content identifier
|
|
213
|
+
* @returns StreamInfo with sources and tracks, or null if no valid data
|
|
214
|
+
*/
|
|
215
|
+
export declare function buildStreamInfoFromEndpoints(endpoints: ContentEndpoints, contentId: string): StreamInfo | null;
|
|
216
|
+
/**
|
|
217
|
+
* Headless player controller that manages the entire player lifecycle.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const controller = new PlayerController({
|
|
222
|
+
* contentId: 'my-stream',
|
|
223
|
+
* contentType: 'live',
|
|
224
|
+
* gatewayUrl: 'https://gateway.example.com/graphql',
|
|
225
|
+
* });
|
|
226
|
+
*
|
|
227
|
+
* controller.on('stateChange', ({ state }) => console.log('State:', state));
|
|
228
|
+
* controller.on('ready', ({ videoElement }) => console.log('Ready!'));
|
|
229
|
+
*
|
|
230
|
+
* const container = document.getElementById('player');
|
|
231
|
+
* await controller.attach(container);
|
|
232
|
+
*
|
|
233
|
+
* // Later...
|
|
234
|
+
* controller.destroy();
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export declare class PlayerController extends TypedEventEmitter<PlayerControllerEvents> {
|
|
238
|
+
private config;
|
|
239
|
+
private state;
|
|
240
|
+
private lastEmittedState;
|
|
241
|
+
private gatewayClient;
|
|
242
|
+
private streamStateClient;
|
|
243
|
+
private playerManager;
|
|
244
|
+
private currentPlayer;
|
|
245
|
+
private videoElement;
|
|
246
|
+
private container;
|
|
247
|
+
private endpoints;
|
|
248
|
+
private streamInfo;
|
|
249
|
+
private streamState;
|
|
250
|
+
/** Tracks parsed from MistServer JSON response (used for direct MistServer mode) */
|
|
251
|
+
private mistTracks;
|
|
252
|
+
private cleanupFns;
|
|
253
|
+
private isDestroyed;
|
|
254
|
+
private isAttached;
|
|
255
|
+
private _isBuffering;
|
|
256
|
+
private _hasPlaybackStarted;
|
|
257
|
+
private _errorText;
|
|
258
|
+
private _isPassiveError;
|
|
259
|
+
private _isHoldingSpeed;
|
|
260
|
+
private _holdSpeed;
|
|
261
|
+
private _isLoopEnabled;
|
|
262
|
+
private _currentPlayerInfo;
|
|
263
|
+
private _currentSourceInfo;
|
|
264
|
+
private _pendingForceOptions;
|
|
265
|
+
private _errorShownAt;
|
|
266
|
+
private _errorCleared;
|
|
267
|
+
private _isTransitioning;
|
|
268
|
+
private _errorCount;
|
|
269
|
+
private _lastErrorTime;
|
|
270
|
+
private _prevStreamIsOnline;
|
|
271
|
+
private _isHovering;
|
|
272
|
+
private _hoverTimeout;
|
|
273
|
+
private static readonly HOVER_HIDE_DELAY_MS;
|
|
274
|
+
private static readonly HOVER_LEAVE_DELAY_MS;
|
|
275
|
+
private _subtitlesEnabled;
|
|
276
|
+
private _stallStartTime;
|
|
277
|
+
private static readonly HARD_FAILURE_STALL_THRESHOLD_MS;
|
|
278
|
+
private _seekableStart;
|
|
279
|
+
private _liveEdge;
|
|
280
|
+
private _canSeek;
|
|
281
|
+
private _isNearLive;
|
|
282
|
+
private _latencyTier;
|
|
283
|
+
private _liveThresholds;
|
|
284
|
+
private _buffered;
|
|
285
|
+
private _hasAudio;
|
|
286
|
+
private _supportsPlaybackRate;
|
|
287
|
+
private _isWebRTC;
|
|
288
|
+
private static readonly AUTO_CLEAR_ERROR_DELAY_MS;
|
|
289
|
+
private static readonly HARD_FAILURE_ERROR_THRESHOLD;
|
|
290
|
+
private static readonly HARD_FAILURE_ERROR_WINDOW_MS;
|
|
291
|
+
private static readonly FATAL_ERROR_KEYWORDS;
|
|
292
|
+
private abrController;
|
|
293
|
+
private interactionController;
|
|
294
|
+
private mistReporter;
|
|
295
|
+
private qualityMonitor;
|
|
296
|
+
private metaTrackManager;
|
|
297
|
+
private _playbackQuality;
|
|
298
|
+
private bootMs;
|
|
299
|
+
constructor(config: PlayerControllerConfig);
|
|
300
|
+
/**
|
|
301
|
+
* Attach to a container element and start the player lifecycle.
|
|
302
|
+
* This is the main entry point after construction.
|
|
303
|
+
*/
|
|
304
|
+
attach(container: HTMLElement): Promise<void>;
|
|
305
|
+
/**
|
|
306
|
+
* Detach from the current container and clean up resources.
|
|
307
|
+
* The controller can be re-attached to a new container.
|
|
308
|
+
*/
|
|
309
|
+
detach(): void;
|
|
310
|
+
/**
|
|
311
|
+
* Fully destroy the controller. Cannot be reused after this.
|
|
312
|
+
*/
|
|
313
|
+
destroy(): void;
|
|
314
|
+
/** Get current player state */
|
|
315
|
+
getState(): PlayerState;
|
|
316
|
+
/** Get current stream state (for live streams) */
|
|
317
|
+
getStreamState(): StreamState | null;
|
|
318
|
+
/** Get resolved endpoints */
|
|
319
|
+
getEndpoints(): ContentEndpoints | null;
|
|
320
|
+
/** Get content metadata (title, description, duration, etc.) */
|
|
321
|
+
getMetadata(): ContentMetadata | null;
|
|
322
|
+
/** Get stream info (sources + tracks for player selection) */
|
|
323
|
+
getStreamInfo(): StreamInfo | null;
|
|
324
|
+
/** Get video element (null if not ready) */
|
|
325
|
+
getVideoElement(): HTMLVideoElement | null;
|
|
326
|
+
/** Get current player instance */
|
|
327
|
+
getPlayer(): IPlayer | null;
|
|
328
|
+
/** Check if player is ready */
|
|
329
|
+
isReady(): boolean;
|
|
330
|
+
/** Check if video is currently playing (not paused) */
|
|
331
|
+
isPlaying(): boolean;
|
|
332
|
+
/** Check if currently buffering */
|
|
333
|
+
isBuffering(): boolean;
|
|
334
|
+
/** Get current error message (null if no error) */
|
|
335
|
+
getError(): string | null;
|
|
336
|
+
/** Check if error is passive (video still playing despite error) */
|
|
337
|
+
isPassiveError(): boolean;
|
|
338
|
+
/** Check if playback has ever started (for idle screen logic) */
|
|
339
|
+
hasPlaybackStarted(): boolean;
|
|
340
|
+
/** Check if currently holding for speed boost */
|
|
341
|
+
isHoldingSpeed(): boolean;
|
|
342
|
+
/** Get current hold speed value */
|
|
343
|
+
getHoldSpeed(): number;
|
|
344
|
+
/** Get current player implementation info */
|
|
345
|
+
getCurrentPlayerInfo(): {
|
|
346
|
+
name: string;
|
|
347
|
+
shortname: string;
|
|
348
|
+
} | null;
|
|
349
|
+
/** Get current source info (URL and type) */
|
|
350
|
+
getCurrentSourceInfo(): {
|
|
351
|
+
url: string;
|
|
352
|
+
type: string;
|
|
353
|
+
} | null;
|
|
354
|
+
/** Get current volume (0-1) */
|
|
355
|
+
getVolume(): number;
|
|
356
|
+
/** Check if loop mode is enabled */
|
|
357
|
+
isLoopEnabled(): boolean;
|
|
358
|
+
/** Check if subtitles/captions are enabled */
|
|
359
|
+
isSubtitlesEnabled(): boolean;
|
|
360
|
+
/** Set subtitles/captions enabled state */
|
|
361
|
+
setSubtitlesEnabled(enabled: boolean): void;
|
|
362
|
+
/** Toggle subtitles/captions */
|
|
363
|
+
toggleSubtitles(): void;
|
|
364
|
+
/** Get start of seekable range (seconds) */
|
|
365
|
+
getSeekableStart(): number;
|
|
366
|
+
/** Get live edge / end of seekable range (seconds) */
|
|
367
|
+
getLiveEdge(): number;
|
|
368
|
+
/** Check if seeking is currently available */
|
|
369
|
+
canSeekStream(): boolean;
|
|
370
|
+
/** Check if playback is near the live edge (for live badge display) */
|
|
371
|
+
isNearLive(): boolean;
|
|
372
|
+
/** Get buffered ranges, preferring player override when available */
|
|
373
|
+
getBufferedRanges(): TimeRanges | null;
|
|
374
|
+
/** Get current latency tier based on protocol */
|
|
375
|
+
getLatencyTier(): LatencyTier;
|
|
376
|
+
/** Get live thresholds for entering/exiting "LIVE" state */
|
|
377
|
+
getLiveThresholds(): LiveThresholds;
|
|
378
|
+
/** Get buffered time ranges */
|
|
379
|
+
getBuffered(): TimeRanges | null;
|
|
380
|
+
/** Check if stream has audio track */
|
|
381
|
+
hasAudioTrack(): boolean;
|
|
382
|
+
/** Check if playback rate adjustment is supported */
|
|
383
|
+
canAdjustPlaybackRate(): boolean;
|
|
384
|
+
/** Check if source is WebRTC/MediaStream */
|
|
385
|
+
isWebRTCSource(): boolean;
|
|
386
|
+
/** Check if currently in fullscreen mode */
|
|
387
|
+
isFullscreen(): boolean;
|
|
388
|
+
/** Check if content is effectively live (live or DVR still recording) */
|
|
389
|
+
isEffectivelyLive(): boolean;
|
|
390
|
+
/** Check if content is strictly live (not DVR/clip/vod) */
|
|
391
|
+
isLive(): boolean;
|
|
392
|
+
/**
|
|
393
|
+
* Check if content needs cold start (VOD-like loading).
|
|
394
|
+
* True for: clips, DVR (recording OR completed) - any stored/VOD content
|
|
395
|
+
* False for: live streams only (real-time MistServer stream)
|
|
396
|
+
* DVR-while-recording needs cold start because MistServer may not be serving the VOD yet
|
|
397
|
+
*/
|
|
398
|
+
needsColdStart(): boolean;
|
|
399
|
+
/**
|
|
400
|
+
* Check if we should show idle/loading screen.
|
|
401
|
+
* Logic:
|
|
402
|
+
* - For cold start content (VOD/DVR): Show loading only while waiting for Gateway sources
|
|
403
|
+
* - For live streams: Show loading while waiting for MistServer to come online
|
|
404
|
+
* - Never show idle after playback has started (unless explicit error)
|
|
405
|
+
*/
|
|
406
|
+
shouldShowIdleScreen(): boolean;
|
|
407
|
+
/**
|
|
408
|
+
* Get the effective content type for playback mode selection.
|
|
409
|
+
* This ensures WHEP/WebRTC gets penalized for VOD content (no seek support)
|
|
410
|
+
* while HLS/MP4 are preferred for clips and completed DVR recordings.
|
|
411
|
+
*/
|
|
412
|
+
getEffectiveContentType(): 'live' | 'vod';
|
|
413
|
+
/** Check if user is currently hovering over the player */
|
|
414
|
+
isHovering(): boolean;
|
|
415
|
+
/**
|
|
416
|
+
* Check if controls should be visible.
|
|
417
|
+
* Controls are visible when:
|
|
418
|
+
* - User is hovering over the player
|
|
419
|
+
* - Video is paused
|
|
420
|
+
* - There's an error
|
|
421
|
+
*/
|
|
422
|
+
shouldShowControls(): boolean;
|
|
423
|
+
/**
|
|
424
|
+
* Handle mouse enter event - show controls immediately.
|
|
425
|
+
* Call this from your UI wrapper's onMouseEnter handler.
|
|
426
|
+
*/
|
|
427
|
+
handleMouseEnter(): void;
|
|
428
|
+
/**
|
|
429
|
+
* Handle mouse leave event - hide controls after delay.
|
|
430
|
+
* Call this from your UI wrapper's onMouseLeave handler.
|
|
431
|
+
*/
|
|
432
|
+
handleMouseLeave(): void;
|
|
433
|
+
/**
|
|
434
|
+
* Handle mouse move event - show controls and reset hide timer.
|
|
435
|
+
* Call this from your UI wrapper's onMouseMove handler.
|
|
436
|
+
*/
|
|
437
|
+
handleMouseMove(): void;
|
|
438
|
+
/**
|
|
439
|
+
* Handle touch start event - show controls.
|
|
440
|
+
* Call this from your UI wrapper's onTouchStart handler.
|
|
441
|
+
*/
|
|
442
|
+
handleTouchStart(): void;
|
|
443
|
+
/** Clear hover timeout */
|
|
444
|
+
private clearHoverTimeout;
|
|
445
|
+
/** Get current playback rate */
|
|
446
|
+
getPlaybackRate(): number;
|
|
447
|
+
/** Get playback quality metrics from QualityMonitor */
|
|
448
|
+
getPlaybackQuality(): PlaybackQuality | null;
|
|
449
|
+
/** Get current ABR mode */
|
|
450
|
+
getABRMode(): ABRMode;
|
|
451
|
+
/** Set ABR mode at runtime */
|
|
452
|
+
setABRMode(mode: ABRMode): void;
|
|
453
|
+
/** Start playback */
|
|
454
|
+
play(): Promise<void>;
|
|
455
|
+
/** Pause playback */
|
|
456
|
+
pause(): void;
|
|
457
|
+
/** Seek to time */
|
|
458
|
+
seek(time: number): void;
|
|
459
|
+
/** Set volume (0-1) */
|
|
460
|
+
setVolume(volume: number): void;
|
|
461
|
+
/** Set muted state */
|
|
462
|
+
setMuted(muted: boolean): void;
|
|
463
|
+
/** Set playback rate */
|
|
464
|
+
setPlaybackRate(rate: number): void;
|
|
465
|
+
/** Jump to live edge (for live streams) */
|
|
466
|
+
jumpToLive(): void;
|
|
467
|
+
/** Emit current seeking state */
|
|
468
|
+
private emitSeekingState;
|
|
469
|
+
/** Request fullscreen */
|
|
470
|
+
requestFullscreen(): Promise<void>;
|
|
471
|
+
/** Request Picture-in-Picture */
|
|
472
|
+
requestPiP(): Promise<void>;
|
|
473
|
+
/** Get available quality levels */
|
|
474
|
+
getQualities(): Array<{
|
|
475
|
+
id: string;
|
|
476
|
+
label: string;
|
|
477
|
+
bitrate?: number;
|
|
478
|
+
width?: number;
|
|
479
|
+
height?: number;
|
|
480
|
+
isAuto?: boolean;
|
|
481
|
+
active?: boolean;
|
|
482
|
+
}>;
|
|
483
|
+
/** Select a quality level */
|
|
484
|
+
selectQuality(id: string): void;
|
|
485
|
+
/** Get available text tracks */
|
|
486
|
+
getTextTracks(): Array<{
|
|
487
|
+
id: string;
|
|
488
|
+
label: string;
|
|
489
|
+
lang?: string;
|
|
490
|
+
active: boolean;
|
|
491
|
+
}>;
|
|
492
|
+
/** Select a text track */
|
|
493
|
+
selectTextTrack(id: string | null): void;
|
|
494
|
+
private getEffectiveCurrentTime;
|
|
495
|
+
private getEffectiveDuration;
|
|
496
|
+
private getPlayerSeekableRange;
|
|
497
|
+
private deriveBufferWindowMsFromTracks;
|
|
498
|
+
/** Get current time */
|
|
499
|
+
getCurrentTime(): number;
|
|
500
|
+
/** Get duration */
|
|
501
|
+
getDuration(): number;
|
|
502
|
+
/** Check if paused */
|
|
503
|
+
isPaused(): boolean;
|
|
504
|
+
/** Check if muted */
|
|
505
|
+
isMuted(): boolean;
|
|
506
|
+
/** Skip backward by specified seconds (default 10) */
|
|
507
|
+
skipBack(seconds?: number): void;
|
|
508
|
+
/** Skip forward by specified seconds (default 10) */
|
|
509
|
+
skipForward(seconds?: number): void;
|
|
510
|
+
/** Toggle play/pause */
|
|
511
|
+
togglePlay(): void;
|
|
512
|
+
/** Toggle mute */
|
|
513
|
+
toggleMute(): void;
|
|
514
|
+
/** Seek relative to current position */
|
|
515
|
+
seekBy(delta: number): void;
|
|
516
|
+
/** Seek to percentage (0-1) of duration */
|
|
517
|
+
seekPercent(percent: number): void;
|
|
518
|
+
/** Toggle loop mode */
|
|
519
|
+
toggleLoop(): void;
|
|
520
|
+
/** Set loop mode */
|
|
521
|
+
setLoopEnabled(enabled: boolean): void;
|
|
522
|
+
/** Clear current error */
|
|
523
|
+
clearError(): void;
|
|
524
|
+
/**
|
|
525
|
+
* Update seeking and live detection state.
|
|
526
|
+
* Called on timeupdate and progress events.
|
|
527
|
+
* Emits seekingStateChange event when values change.
|
|
528
|
+
*/
|
|
529
|
+
private updateSeekingState;
|
|
530
|
+
/**
|
|
531
|
+
* Detect audio tracks on the video element.
|
|
532
|
+
* Called after video metadata is loaded.
|
|
533
|
+
*/
|
|
534
|
+
private detectAudioTracks;
|
|
535
|
+
/**
|
|
536
|
+
* Attempt to clear error automatically if playback is progressing.
|
|
537
|
+
* Called on timeupdate, playing, and canplay events.
|
|
538
|
+
*/
|
|
539
|
+
private attemptClearError;
|
|
540
|
+
/**
|
|
541
|
+
* Check if we should attempt playback fallback due to hard failure.
|
|
542
|
+
* Returns true if:
|
|
543
|
+
* - Error count exceeds threshold (5+) within time window (60s)
|
|
544
|
+
* - Error contains fatal keywords
|
|
545
|
+
* - Sustained stall for 30+ seconds
|
|
546
|
+
*/
|
|
547
|
+
private shouldAttemptFallback;
|
|
548
|
+
/**
|
|
549
|
+
* Set error with passive mode support.
|
|
550
|
+
* - Ignores errors during player transitions
|
|
551
|
+
* - Marks error as passive if video is still playing
|
|
552
|
+
* - Attempts automatic fallback on hard failures
|
|
553
|
+
*/
|
|
554
|
+
setPassiveError(error: string): Promise<void>;
|
|
555
|
+
/**
|
|
556
|
+
* Retry playback with fallback to next player/source.
|
|
557
|
+
* Returns true if a fallback option was available and attempted.
|
|
558
|
+
*/
|
|
559
|
+
retryWithFallback(): Promise<boolean>;
|
|
560
|
+
/** Toggle fullscreen */
|
|
561
|
+
toggleFullscreen(): Promise<void>;
|
|
562
|
+
/** Toggle Picture-in-Picture */
|
|
563
|
+
togglePictureInPicture(): Promise<void>;
|
|
564
|
+
/** Check if Picture-in-Picture is supported */
|
|
565
|
+
isPiPSupported(): boolean;
|
|
566
|
+
/** Check if currently in Picture-in-Picture mode */
|
|
567
|
+
isPiPActive(): boolean;
|
|
568
|
+
/** Force a retry of the current playback */
|
|
569
|
+
retry(): Promise<void>;
|
|
570
|
+
/** Get playback statistics */
|
|
571
|
+
getStats(): Promise<unknown>;
|
|
572
|
+
/** Get current latency (for live streams) */
|
|
573
|
+
getLatency(): Promise<unknown>;
|
|
574
|
+
/**
|
|
575
|
+
* Update configuration at runtime without full re-initialization.
|
|
576
|
+
* Only certain options can be updated without re-init.
|
|
577
|
+
*/
|
|
578
|
+
updateConfig(partialConfig: Partial<Pick<PlayerControllerConfig, 'debug' | 'autoplay' | 'muted'>>): void;
|
|
579
|
+
/**
|
|
580
|
+
* Force a complete re-initialization with current config.
|
|
581
|
+
* Stops and re-initializes the entire player.
|
|
582
|
+
*/
|
|
583
|
+
reload(): Promise<void>;
|
|
584
|
+
/**
|
|
585
|
+
* Select a specific player/source combination (one-shot).
|
|
586
|
+
* Used by DevModePanel to manually pick a combo.
|
|
587
|
+
*
|
|
588
|
+
* Note: This is a ONE-SHOT selection. The force settings are used for
|
|
589
|
+
* the next initialization only. If that player fails, normal fallback
|
|
590
|
+
* logic proceeds without the force settings.
|
|
591
|
+
*/
|
|
592
|
+
selectCombo(options: {
|
|
593
|
+
forcePlayer?: string;
|
|
594
|
+
forceType?: string;
|
|
595
|
+
forceSource?: number;
|
|
596
|
+
}): Promise<void>;
|
|
597
|
+
/**
|
|
598
|
+
* Set playback mode preference.
|
|
599
|
+
* Unlike selectCombo, this is a persistent preference that affects scoring.
|
|
600
|
+
*/
|
|
601
|
+
setPlaybackMode(mode: 'auto' | 'low-latency' | 'quality' | 'vod'): void;
|
|
602
|
+
/**
|
|
603
|
+
* @deprecated Use selectCombo() for one-shot selection or setPlaybackMode() for mode changes.
|
|
604
|
+
* This method exists for backwards compatibility but may override fallback behavior.
|
|
605
|
+
*/
|
|
606
|
+
setDevModeOptions(options: {
|
|
607
|
+
forcePlayer?: string;
|
|
608
|
+
forceType?: string;
|
|
609
|
+
forceSource?: number;
|
|
610
|
+
playbackMode?: 'auto' | 'low-latency' | 'quality' | 'vod';
|
|
611
|
+
}): Promise<void>;
|
|
612
|
+
/**
|
|
613
|
+
* Get metadata update payload for external consumers.
|
|
614
|
+
* Combines current state into a single metadata object.
|
|
615
|
+
*/
|
|
616
|
+
getMetadataPayload(): PlayerControllerEvents['metadataUpdate'];
|
|
617
|
+
/**
|
|
618
|
+
* Emit a metadata update event with current state.
|
|
619
|
+
* Useful for periodic telemetry/reporting.
|
|
620
|
+
*/
|
|
621
|
+
emitMetadataUpdate(): void;
|
|
622
|
+
private resolveEndpoints;
|
|
623
|
+
/**
|
|
624
|
+
* Resolve endpoints directly from MistServer (bypasses Gateway)
|
|
625
|
+
* Fetches json_{contentId}.js and builds ContentEndpoints from source array
|
|
626
|
+
*/
|
|
627
|
+
private resolveFromMistServer;
|
|
628
|
+
/**
|
|
629
|
+
* Map MistServer type to protocol identifier
|
|
630
|
+
*/
|
|
631
|
+
private mapMistTypeToProtocol;
|
|
632
|
+
/**
|
|
633
|
+
* Select best source based on protocol priority
|
|
634
|
+
*/
|
|
635
|
+
private selectBestSource;
|
|
636
|
+
/**
|
|
637
|
+
* Resolve endpoints from Gateway GraphQL API
|
|
638
|
+
*/
|
|
639
|
+
private resolveFromGateway;
|
|
640
|
+
private startStreamStatePolling;
|
|
641
|
+
/**
|
|
642
|
+
* Initialize player late when stream comes online after initial attach failed.
|
|
643
|
+
* Uses MistStreamInfo from stream state polling instead of re-fetching.
|
|
644
|
+
*/
|
|
645
|
+
private initializeLateFromStreamState;
|
|
646
|
+
private buildStreamInfo;
|
|
647
|
+
/**
|
|
648
|
+
* Parse MistServer track metadata from the tracks object.
|
|
649
|
+
* MistServer returns tracks as a Record keyed by track name (e.g., "video_H264_800x600_25fps_1").
|
|
650
|
+
* This converts to our StreamTrack[] format with codecstring and init data.
|
|
651
|
+
*/
|
|
652
|
+
private parseMistTracks;
|
|
653
|
+
private initializePlayer;
|
|
654
|
+
private setupVideoEventListeners;
|
|
655
|
+
private initializeSubControllers;
|
|
656
|
+
private initializeABRController;
|
|
657
|
+
private initializeQualityMonitor;
|
|
658
|
+
private initializeInteractionController;
|
|
659
|
+
private initializeMistReporter;
|
|
660
|
+
private initializeMetaTrackManager;
|
|
661
|
+
private cleanup;
|
|
662
|
+
private setState;
|
|
663
|
+
private log;
|
|
664
|
+
}
|
|
665
|
+
export default PlayerController;
|