@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.
- package/README.md +21 -6
- package/dist/cjs/index.js +792 -146
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +792 -146
- package/dist/esm/index.js.map +1 -1
- package/dist/player.css +185 -373
- package/dist/types/core/GatewayClient.d.ts +3 -4
- package/dist/types/core/InteractionController.d.ts +12 -0
- package/dist/types/core/MetaTrackManager.d.ts +1 -1
- package/dist/types/core/PlayerController.d.ts +18 -2
- package/dist/types/core/PlayerInterface.d.ts +10 -0
- package/dist/types/core/SeekingUtils.d.ts +3 -1
- package/dist/types/core/StreamStateClient.d.ts +1 -1
- package/dist/types/players/HlsJsPlayer.d.ts +8 -0
- package/dist/types/players/MewsWsPlayer/index.d.ts +1 -1
- package/dist/types/players/VideoJsPlayer.d.ts +12 -4
- package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +1 -1
- package/dist/types/players/WebCodecsPlayer/index.d.ts +11 -0
- package/dist/types/players/WebCodecsPlayer/types.d.ts +25 -3
- package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +20 -2
- package/dist/types/types.d.ts +32 -1
- package/dist/types/vanilla/FrameWorksPlayer.d.ts +5 -5
- package/dist/types/vanilla/index.d.ts +3 -3
- package/dist/workers/decoder.worker.js +183 -6
- package/dist/workers/decoder.worker.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ABRController.ts +38 -36
- package/src/core/CodecUtils.ts +50 -47
- package/src/core/Disposable.ts +4 -4
- package/src/core/EventEmitter.ts +1 -1
- package/src/core/GatewayClient.ts +48 -48
- package/src/core/InteractionController.ts +89 -82
- package/src/core/LiveDurationProxy.ts +14 -16
- package/src/core/MetaTrackManager.ts +74 -66
- package/src/core/MistReporter.ts +72 -45
- package/src/core/MistSignaling.ts +59 -56
- package/src/core/PlayerController.ts +724 -375
- package/src/core/PlayerInterface.ts +89 -59
- package/src/core/PlayerManager.ts +118 -123
- package/src/core/PlayerRegistry.ts +59 -42
- package/src/core/QualityMonitor.ts +38 -31
- package/src/core/ScreenWakeLockManager.ts +8 -9
- package/src/core/SeekingUtils.ts +31 -22
- package/src/core/StreamStateClient.ts +75 -69
- package/src/core/SubtitleManager.ts +25 -23
- package/src/core/TelemetryReporter.ts +34 -31
- package/src/core/TimeFormat.ts +13 -17
- package/src/core/TimerManager.ts +25 -9
- package/src/core/UrlUtils.ts +20 -17
- package/src/core/detector.ts +44 -44
- package/src/core/index.ts +57 -48
- package/src/core/scorer.ts +137 -138
- package/src/core/selector.ts +2 -6
- package/src/global.d.ts +1 -1
- package/src/index.ts +46 -35
- package/src/players/DashJsPlayer.ts +175 -114
- package/src/players/HlsJsPlayer.ts +154 -76
- package/src/players/MewsWsPlayer/SourceBufferManager.ts +44 -39
- package/src/players/MewsWsPlayer/WebSocketManager.ts +9 -10
- package/src/players/MewsWsPlayer/index.ts +196 -154
- package/src/players/MewsWsPlayer/types.ts +21 -21
- package/src/players/MistPlayer.ts +46 -27
- package/src/players/MistWebRTCPlayer/index.ts +175 -129
- package/src/players/NativePlayer.ts +203 -143
- package/src/players/VideoJsPlayer.ts +200 -146
- package/src/players/WebCodecsPlayer/JitterBuffer.ts +6 -7
- package/src/players/WebCodecsPlayer/LatencyProfiles.ts +43 -43
- package/src/players/WebCodecsPlayer/RawChunkParser.ts +10 -10
- package/src/players/WebCodecsPlayer/SyncController.ts +46 -55
- package/src/players/WebCodecsPlayer/WebSocketController.ts +67 -69
- package/src/players/WebCodecsPlayer/index.ts +280 -220
- package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +12 -17
- package/src/players/WebCodecsPlayer/types.ts +81 -53
- package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +255 -192
- package/src/players/WebCodecsPlayer/worker/types.ts +33 -29
- package/src/players/index.ts +8 -8
- package/src/styles/animations.css +2 -1
- package/src/styles/player.css +182 -356
- package/src/styles/tailwind.css +473 -159
- package/src/types.ts +75 -33
- package/src/vanilla/FrameWorksPlayer.ts +34 -19
- package/src/vanilla/index.ts +7 -7
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { BasePlayer } from
|
|
2
|
-
import { checkProtocolMismatch, getBrowserInfo } from
|
|
3
|
-
import { translateCodec } from
|
|
4
|
-
import { LiveDurationProxy } from
|
|
5
|
-
import type {
|
|
1
|
+
import { BasePlayer } from "../core/PlayerInterface";
|
|
2
|
+
import { checkProtocolMismatch, getBrowserInfo } from "../core/detector";
|
|
3
|
+
import { translateCodec } from "../core/CodecUtils";
|
|
4
|
+
import { LiveDurationProxy } from "../core/LiveDurationProxy";
|
|
5
|
+
import type {
|
|
6
|
+
StreamSource,
|
|
7
|
+
StreamInfo,
|
|
8
|
+
PlayerOptions,
|
|
9
|
+
PlayerCapability,
|
|
10
|
+
} from "../core/PlayerInterface";
|
|
11
|
+
import type { HlsJsConfig } from "../types";
|
|
6
12
|
|
|
7
13
|
// Player implementation class
|
|
8
14
|
export class HlsJsPlayerImpl extends BasePlayer {
|
|
@@ -10,7 +16,7 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
10
16
|
name: "HLS.js Player",
|
|
11
17
|
shortname: "hlsjs",
|
|
12
18
|
priority: 3,
|
|
13
|
-
mimes: ["html5/application/vnd.apple.mpegurl", "html5/application/vnd.apple.mpegurl;version=7"]
|
|
19
|
+
mimes: ["html5/application/vnd.apple.mpegurl", "html5/application/vnd.apple.mpegurl;version=7"],
|
|
14
20
|
};
|
|
15
21
|
|
|
16
22
|
private hls: any = null;
|
|
@@ -23,7 +29,11 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
23
29
|
return this.capability.mimes.includes(mimetype);
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
isBrowserSupported(
|
|
32
|
+
isBrowserSupported(
|
|
33
|
+
mimetype: string,
|
|
34
|
+
source: StreamSource,
|
|
35
|
+
streamInfo: StreamInfo
|
|
36
|
+
): boolean | string[] {
|
|
27
37
|
// Check protocol mismatch
|
|
28
38
|
if (checkProtocolMismatch(source.url)) {
|
|
29
39
|
return false;
|
|
@@ -41,9 +51,9 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
41
51
|
// Check MediaSource support (required for HLS.js)
|
|
42
52
|
if (!browser.supportsMediaSource) {
|
|
43
53
|
// Fall back to native if available
|
|
44
|
-
const testVideo = document.createElement(
|
|
45
|
-
if (testVideo.canPlayType(
|
|
46
|
-
return [
|
|
54
|
+
const testVideo = document.createElement("video");
|
|
55
|
+
if (testVideo.canPlayType("application/vnd.apple.mpegurl")) {
|
|
56
|
+
return ["video", "audio"];
|
|
47
57
|
}
|
|
48
58
|
return false;
|
|
49
59
|
}
|
|
@@ -55,17 +65,17 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
55
65
|
// If no track info available yet, assume compatible (like upstream does)
|
|
56
66
|
// Track info comes async from MistServer - don't block on it
|
|
57
67
|
if (!streamInfo.meta.tracks || streamInfo.meta.tracks.length === 0) {
|
|
58
|
-
return [
|
|
68
|
+
return ["video", "audio"]; // Assume standard tracks until we know better
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
// Group tracks by type
|
|
62
72
|
for (const track of streamInfo.meta.tracks) {
|
|
63
|
-
if (track.type ===
|
|
64
|
-
if (track.codec ===
|
|
73
|
+
if (track.type === "meta") {
|
|
74
|
+
if (track.codec === "subtitle") {
|
|
65
75
|
// Check for WebVTT subtitle support
|
|
66
76
|
for (const src of streamInfo.source) {
|
|
67
|
-
if (src.type ===
|
|
68
|
-
playableTracks.push(
|
|
77
|
+
if (src.type === "html5/text/vtt") {
|
|
78
|
+
playableTracks.push("subtitle");
|
|
69
79
|
break;
|
|
70
80
|
}
|
|
71
81
|
}
|
|
@@ -81,7 +91,7 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
81
91
|
|
|
82
92
|
// HLS-incompatible audio codecs (even if browser MSE supports them in fMP4)
|
|
83
93
|
// HLS standard only supports: AAC, MP3, AC-3/E-AC-3
|
|
84
|
-
const HLS_INCOMPATIBLE_AUDIO = [
|
|
94
|
+
const HLS_INCOMPATIBLE_AUDIO = ["OPUS", "Opus", "opus", "VORBIS", "Vorbis", "FLAC"];
|
|
85
95
|
|
|
86
96
|
// Test codec support for video/audio tracks
|
|
87
97
|
for (const [trackType, tracks] of Object.entries(tracksByType)) {
|
|
@@ -89,14 +99,14 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
89
99
|
|
|
90
100
|
for (const track of tracks) {
|
|
91
101
|
// Explicit HLS codec filtering - OPUS doesn't work in HLS even if MSE supports it
|
|
92
|
-
if (trackType ===
|
|
102
|
+
if (trackType === "audio" && HLS_INCOMPATIBLE_AUDIO.includes(track.codec)) {
|
|
93
103
|
console.debug(`[HLS.js] Codec incompatible with HLS: ${track.codec}`);
|
|
94
104
|
continue;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
const codecString = translateCodec(track);
|
|
98
108
|
// Use correct container type for audio vs video tracks
|
|
99
|
-
const container = trackType ===
|
|
109
|
+
const container = trackType === "audio" ? "audio/mp4" : "video/mp4";
|
|
100
110
|
const mimeType = `${container};codecs="${codecString}"`;
|
|
101
111
|
|
|
102
112
|
if (MediaSource.isTypeSupported && MediaSource.isTypeSupported(mimeType)) {
|
|
@@ -115,17 +125,21 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
115
125
|
return playableTracks.length > 0 ? playableTracks : false;
|
|
116
126
|
}
|
|
117
127
|
|
|
118
|
-
async initialize(
|
|
119
|
-
|
|
128
|
+
async initialize(
|
|
129
|
+
container: HTMLElement,
|
|
130
|
+
source: StreamSource,
|
|
131
|
+
options: PlayerOptions
|
|
132
|
+
): Promise<HTMLVideoElement> {
|
|
133
|
+
console.log("[HLS.js] initialize() starting for", source.url.slice(0, 60) + "...");
|
|
120
134
|
this.destroyed = false;
|
|
121
135
|
this.container = container;
|
|
122
|
-
container.classList.add(
|
|
136
|
+
container.classList.add("fw-player-container");
|
|
123
137
|
|
|
124
138
|
// Create video element
|
|
125
|
-
const video = document.createElement(
|
|
126
|
-
video.classList.add(
|
|
127
|
-
video.setAttribute(
|
|
128
|
-
video.setAttribute(
|
|
139
|
+
const video = document.createElement("video");
|
|
140
|
+
video.classList.add("fw-player-video");
|
|
141
|
+
video.setAttribute("playsinline", "");
|
|
142
|
+
video.setAttribute("crossorigin", "anonymous");
|
|
129
143
|
|
|
130
144
|
// Apply options
|
|
131
145
|
if (options.autoplay) video.autoplay = true;
|
|
@@ -142,19 +156,42 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
142
156
|
|
|
143
157
|
try {
|
|
144
158
|
// Dynamic import of HLS.js
|
|
145
|
-
console.log(
|
|
146
|
-
const mod = await import(
|
|
159
|
+
console.log("[HLS.js] Dynamically importing hls.js module...");
|
|
160
|
+
const mod = await import("hls.js");
|
|
147
161
|
const Hls = (mod as any).default || (mod as any);
|
|
148
|
-
console.log(
|
|
162
|
+
console.log("[HLS.js] hls.js module imported, Hls.isSupported():", Hls.isSupported?.());
|
|
149
163
|
|
|
150
164
|
if (Hls.isSupported()) {
|
|
151
|
-
|
|
165
|
+
// Build optimized HLS.js config with user overrides
|
|
166
|
+
const hlsConfig: HlsJsConfig = {
|
|
167
|
+
// Worker disabled for lower latency (per HLS.js maintainer recommendation)
|
|
152
168
|
enableWorker: false,
|
|
169
|
+
|
|
170
|
+
// LL-HLS support
|
|
153
171
|
lowLatencyMode: true,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
172
|
+
|
|
173
|
+
// AGGRESSIVE: Assume 5 Mbps initially (not 500kbps default)
|
|
174
|
+
// This dramatically improves startup time by selecting appropriate quality faster
|
|
175
|
+
abrEwmaDefaultEstimate: 5_000_000,
|
|
176
|
+
|
|
177
|
+
// AGGRESSIVE: Minimal buffers for fastest startup
|
|
178
|
+
maxBufferLength: 6, // Reduced from 15 (just 2 segments @ 3s)
|
|
179
|
+
maxMaxBufferLength: 15, // Reduced from 60
|
|
180
|
+
backBufferLength: Infinity, // Let browser manage (per maintainer advice)
|
|
181
|
+
|
|
182
|
+
// Stay close to live edge but not too aggressive
|
|
183
|
+
liveSyncDuration: 4, // Target 4 seconds behind live edge
|
|
184
|
+
liveMaxLatencyDuration: 8, // Max 8 seconds before seeking to live
|
|
185
|
+
|
|
186
|
+
// Faster ABR adaptation for live
|
|
187
|
+
abrEwmaFastLive: 2.0, // Faster than default 3.0
|
|
188
|
+
abrEwmaSlowLive: 6.0, // Faster than default 9.0
|
|
189
|
+
|
|
190
|
+
// Allow user overrides
|
|
191
|
+
...options.hlsConfig,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
this.hls = new Hls(hlsConfig);
|
|
158
195
|
|
|
159
196
|
this.hls.attachMedia(video);
|
|
160
197
|
|
|
@@ -167,10 +204,12 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
167
204
|
if (data?.fatal) {
|
|
168
205
|
if (this.failureCount < 3) {
|
|
169
206
|
this.failureCount++;
|
|
170
|
-
try {
|
|
207
|
+
try {
|
|
208
|
+
this.hls.recoverMediaError();
|
|
209
|
+
} catch {}
|
|
171
210
|
} else {
|
|
172
|
-
const error = `HLS fatal error: ${data?.type ||
|
|
173
|
-
this.emit(
|
|
211
|
+
const error = `HLS fatal error: ${data?.type || "unknown"}`;
|
|
212
|
+
this.emit("error", error);
|
|
174
213
|
}
|
|
175
214
|
}
|
|
176
215
|
});
|
|
@@ -186,31 +225,34 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
186
225
|
constrainSeek: true,
|
|
187
226
|
liveOffset: 0,
|
|
188
227
|
});
|
|
189
|
-
console.debug(
|
|
228
|
+
console.debug("[HLS.js] LiveDurationProxy initialized for live stream");
|
|
190
229
|
}
|
|
191
230
|
|
|
192
231
|
if (options.autoplay) {
|
|
193
|
-
video.play().catch(e => console.warn(
|
|
232
|
+
video.play().catch((e) => console.warn("HLS autoplay failed:", e));
|
|
194
233
|
}
|
|
195
234
|
});
|
|
196
|
-
|
|
197
|
-
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
235
|
+
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
198
236
|
// Use native HLS support
|
|
199
237
|
video.src = source.url;
|
|
200
238
|
if (options.autoplay) {
|
|
201
|
-
video.play().catch(e => console.warn(
|
|
239
|
+
video.play().catch((e) => console.warn("Native HLS autoplay failed:", e));
|
|
202
240
|
}
|
|
203
241
|
} else {
|
|
204
|
-
throw new Error(
|
|
242
|
+
throw new Error("HLS not supported in this browser");
|
|
205
243
|
}
|
|
206
244
|
|
|
207
245
|
// Optional subtitle tracks helper from source extras
|
|
208
246
|
try {
|
|
209
|
-
const subs = (source as any).subtitles as Array<{
|
|
247
|
+
const subs = (source as any).subtitles as Array<{
|
|
248
|
+
label: string;
|
|
249
|
+
lang: string;
|
|
250
|
+
src: string;
|
|
251
|
+
}>;
|
|
210
252
|
if (Array.isArray(subs)) {
|
|
211
253
|
subs.forEach((s, idx) => {
|
|
212
|
-
const track = document.createElement(
|
|
213
|
-
track.kind =
|
|
254
|
+
const track = document.createElement("track");
|
|
255
|
+
track.kind = "subtitles";
|
|
214
256
|
track.label = s.label;
|
|
215
257
|
track.srclang = s.lang;
|
|
216
258
|
track.src = s.src;
|
|
@@ -220,17 +262,16 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
220
262
|
}
|
|
221
263
|
} catch {}
|
|
222
264
|
|
|
223
|
-
console.log(
|
|
265
|
+
console.log("[HLS.js] initialize() complete, returning video element");
|
|
224
266
|
return video;
|
|
225
|
-
|
|
226
267
|
} catch (error: any) {
|
|
227
|
-
this.emit(
|
|
268
|
+
this.emit("error", error.message || String(error));
|
|
228
269
|
throw error;
|
|
229
270
|
}
|
|
230
271
|
}
|
|
231
272
|
|
|
232
273
|
async destroy(): Promise<void> {
|
|
233
|
-
console.debug(
|
|
274
|
+
console.debug("[HLS.js] destroy() called");
|
|
234
275
|
this.destroyed = true;
|
|
235
276
|
|
|
236
277
|
if (this.liveDurationProxy) {
|
|
@@ -241,15 +282,17 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
241
282
|
if (this.hls) {
|
|
242
283
|
try {
|
|
243
284
|
this.hls.destroy();
|
|
244
|
-
console.debug(
|
|
285
|
+
console.debug("[HLS.js] hls.destroy() completed");
|
|
245
286
|
} catch (e) {
|
|
246
|
-
console.warn(
|
|
287
|
+
console.warn("[HLS.js] Error destroying:", e);
|
|
247
288
|
}
|
|
248
289
|
this.hls = null;
|
|
249
290
|
}
|
|
250
291
|
|
|
251
292
|
if (this.videoElement && this.container) {
|
|
252
|
-
try {
|
|
293
|
+
try {
|
|
294
|
+
this.container.removeChild(this.videoElement);
|
|
295
|
+
} catch {}
|
|
253
296
|
}
|
|
254
297
|
|
|
255
298
|
this.videoElement = null;
|
|
@@ -310,15 +353,19 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
310
353
|
if (!video) return;
|
|
311
354
|
|
|
312
355
|
// HLS.js provides liveSyncPosition for live streams - use that first
|
|
313
|
-
if (
|
|
314
|
-
|
|
356
|
+
if (
|
|
357
|
+
this.hls &&
|
|
358
|
+
typeof this.hls.liveSyncPosition === "number" &&
|
|
359
|
+
this.hls.liveSyncPosition > 0
|
|
360
|
+
) {
|
|
361
|
+
console.debug("[HLS.js] jumpToLive using liveSyncPosition:", this.hls.liveSyncPosition);
|
|
315
362
|
video.currentTime = this.hls.liveSyncPosition;
|
|
316
363
|
return;
|
|
317
364
|
}
|
|
318
365
|
|
|
319
366
|
// Fall back to LiveDurationProxy
|
|
320
367
|
if (this.liveDurationProxy && this.liveDurationProxy.isLive()) {
|
|
321
|
-
console.debug(
|
|
368
|
+
console.debug("[HLS.js] jumpToLive using LiveDurationProxy");
|
|
322
369
|
this.liveDurationProxy.jumpToLive();
|
|
323
370
|
return;
|
|
324
371
|
}
|
|
@@ -327,7 +374,7 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
327
374
|
if (video.seekable && video.seekable.length > 0) {
|
|
328
375
|
const liveEdge = video.seekable.end(video.seekable.length - 1);
|
|
329
376
|
if (isFinite(liveEdge) && liveEdge > 0) {
|
|
330
|
-
console.debug(
|
|
377
|
+
console.debug("[HLS.js] jumpToLive using seekable.end:", liveEdge);
|
|
331
378
|
video.currentTime = liveEdge;
|
|
332
379
|
}
|
|
333
380
|
}
|
|
@@ -343,7 +390,11 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
343
390
|
const start = video.seekable.start(0);
|
|
344
391
|
let end = video.seekable.end(video.seekable.length - 1);
|
|
345
392
|
|
|
346
|
-
if (
|
|
393
|
+
if (
|
|
394
|
+
this.liveDurationProxy?.isLive() &&
|
|
395
|
+
this.hls &&
|
|
396
|
+
typeof this.hls.liveSyncPosition === "number"
|
|
397
|
+
) {
|
|
347
398
|
const sync = this.hls.liveSyncPosition;
|
|
348
399
|
if (Number.isFinite(sync) && sync > 0) {
|
|
349
400
|
end = Math.min(end, sync);
|
|
@@ -362,7 +413,7 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
362
413
|
if (!video) return 0;
|
|
363
414
|
|
|
364
415
|
// HLS.js provides liveSyncPosition
|
|
365
|
-
if (this.hls && typeof this.hls.liveSyncPosition ===
|
|
416
|
+
if (this.hls && typeof this.hls.liveSyncPosition === "number") {
|
|
366
417
|
return Math.max(0, (this.hls.liveSyncPosition - video.currentTime) * 1000);
|
|
367
418
|
}
|
|
368
419
|
|
|
@@ -377,22 +428,37 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
377
428
|
// ============================================================================
|
|
378
429
|
// Quality API (Auto + levels)
|
|
379
430
|
// ============================================================================
|
|
380
|
-
getQualities(): Array<{
|
|
431
|
+
getQualities(): Array<{
|
|
432
|
+
id: string;
|
|
433
|
+
label: string;
|
|
434
|
+
bitrate?: number;
|
|
435
|
+
width?: number;
|
|
436
|
+
height?: number;
|
|
437
|
+
isAuto?: boolean;
|
|
438
|
+
active?: boolean;
|
|
439
|
+
}> {
|
|
381
440
|
const qualities: any[] = [];
|
|
382
441
|
const video = this.videoElement;
|
|
383
442
|
if (!this.hls || !video) return qualities;
|
|
384
443
|
const levels = this.hls.levels || [];
|
|
385
|
-
const auto = { id:
|
|
444
|
+
const auto = { id: "auto", label: "Auto", isAuto: true, active: this.hls.autoLevelEnabled };
|
|
386
445
|
qualities.push(auto);
|
|
387
446
|
levels.forEach((lvl: any, idx: number) => {
|
|
388
|
-
qualities.push({
|
|
447
|
+
qualities.push({
|
|
448
|
+
id: String(idx),
|
|
449
|
+
label: lvl.height ? `${lvl.height}p` : `${Math.round((lvl.bitrate || 0) / 1000)}kbps`,
|
|
450
|
+
bitrate: lvl.bitrate,
|
|
451
|
+
width: lvl.width,
|
|
452
|
+
height: lvl.height,
|
|
453
|
+
active: this.hls.currentLevel === idx,
|
|
454
|
+
});
|
|
389
455
|
});
|
|
390
456
|
return qualities;
|
|
391
457
|
}
|
|
392
458
|
|
|
393
459
|
selectQuality(id: string): void {
|
|
394
460
|
if (!this.hls) return;
|
|
395
|
-
if (id ===
|
|
461
|
+
if (id === "auto") {
|
|
396
462
|
this.hls.currentLevel = -1;
|
|
397
463
|
this.hls.autoLevelEnabled = true;
|
|
398
464
|
return;
|
|
@@ -412,7 +478,12 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
412
478
|
const out: any[] = [];
|
|
413
479
|
for (let i = 0; i < list.length; i++) {
|
|
414
480
|
const tt = list[i];
|
|
415
|
-
out.push({
|
|
481
|
+
out.push({
|
|
482
|
+
id: String(i),
|
|
483
|
+
label: tt.label || `CC ${i + 1}`,
|
|
484
|
+
lang: (tt as any).language,
|
|
485
|
+
active: tt.mode === "showing",
|
|
486
|
+
});
|
|
416
487
|
}
|
|
417
488
|
return out;
|
|
418
489
|
}
|
|
@@ -423,23 +494,27 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
423
494
|
const list = v.textTracks as TextTrackList;
|
|
424
495
|
for (let i = 0; i < list.length; i++) {
|
|
425
496
|
const tt = list[i];
|
|
426
|
-
if (id !== null && String(i) === id) tt.mode =
|
|
497
|
+
if (id !== null && String(i) === id) tt.mode = "showing";
|
|
498
|
+
else tt.mode = "disabled";
|
|
427
499
|
}
|
|
428
500
|
}
|
|
429
501
|
|
|
430
502
|
/**
|
|
431
503
|
* Get HLS.js-specific stats for accurate bitrate and bandwidth
|
|
432
504
|
*/
|
|
433
|
-
async getStats(): Promise<
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
505
|
+
async getStats(): Promise<
|
|
506
|
+
| {
|
|
507
|
+
type: "hls";
|
|
508
|
+
bandwidthEstimate: number;
|
|
509
|
+
currentLevel: number;
|
|
510
|
+
currentBitrate: number;
|
|
511
|
+
loadLevel: number;
|
|
512
|
+
levels: Array<{ bitrate: number; width: number; height: number }>;
|
|
513
|
+
buffered: number;
|
|
514
|
+
latency?: number;
|
|
515
|
+
}
|
|
516
|
+
| undefined
|
|
517
|
+
> {
|
|
443
518
|
if (!this.hls) return undefined;
|
|
444
519
|
|
|
445
520
|
const levels = (this.hls.levels || []).map((lvl: any) => ({
|
|
@@ -456,7 +531,10 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
456
531
|
const video = this.videoElement;
|
|
457
532
|
if (video && video.buffered.length > 0) {
|
|
458
533
|
for (let i = 0; i < video.buffered.length; i++) {
|
|
459
|
-
if (
|
|
534
|
+
if (
|
|
535
|
+
video.buffered.start(i) <= video.currentTime &&
|
|
536
|
+
video.buffered.end(i) > video.currentTime
|
|
537
|
+
) {
|
|
460
538
|
buffered = video.buffered.end(i) - video.currentTime;
|
|
461
539
|
break;
|
|
462
540
|
}
|
|
@@ -470,7 +548,7 @@ export class HlsJsPlayerImpl extends BasePlayer {
|
|
|
470
548
|
}
|
|
471
549
|
|
|
472
550
|
return {
|
|
473
|
-
type:
|
|
551
|
+
type: "hls",
|
|
474
552
|
bandwidthEstimate: this.hls.bandwidthEstimate || 0,
|
|
475
553
|
currentLevel,
|
|
476
554
|
currentBitrate: currentLevelData?.bitrate || 0,
|