@scarlett-player/hls 1.11.0 → 1.12.0
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 +75 -1
- package/dist/{chunk-NVPSLRJY.js → chunk-47MU7IVX.js} +237 -49
- package/dist/index.cjs +239 -49
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1
- package/dist/light.cjs +239 -49
- package/dist/light.d.cts +2 -2
- package/dist/light.d.ts +2 -2
- package/dist/light.js +5 -1
- package/dist/live-metrics-CoYkWhl5.d.cts +388 -0
- package/dist/live-metrics-CoYkWhl5.d.ts +388 -0
- package/package.json +2 -2
- package/dist/types-DnvcTuSn.d.cts +0 -143
- package/dist/types-DnvcTuSn.d.ts +0 -143
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
DEFAULT_TARGET_LATENCY: () => DEFAULT_TARGET_LATENCY,
|
|
34
|
+
computeLiveMetrics: () => computeLiveMetrics,
|
|
33
35
|
createHLSPlugin: () => createHLSPlugin,
|
|
34
36
|
default: () => index_default,
|
|
35
37
|
sanitizeUrl: () => import_core.sanitizeUrl
|
|
@@ -167,6 +169,107 @@ function getInitialBandwidthEstimate(overrideBps) {
|
|
|
167
169
|
// src/sanitize-url.ts
|
|
168
170
|
var import_core = require("@scarlett-player/core");
|
|
169
171
|
|
|
172
|
+
// src/live-metrics.ts
|
|
173
|
+
var DEFAULT_TARGET_LATENCY = 3;
|
|
174
|
+
var MIN_EDGE_TOLERANCE = 1.5;
|
|
175
|
+
var NATIVE_EDGE_TOLERANCE = 7;
|
|
176
|
+
var EPSILON = 0.05;
|
|
177
|
+
function finite(value) {
|
|
178
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
179
|
+
}
|
|
180
|
+
function rangeFromDetails(details) {
|
|
181
|
+
if (!details) return null;
|
|
182
|
+
const start = finite(details.fragmentStart) ?? finite(details.fragments?.[0]?.start) ?? 0;
|
|
183
|
+
const total = finite(details.totalduration);
|
|
184
|
+
const end = finite(details.edge) ?? (total === null ? null : start + total);
|
|
185
|
+
if (end === null) return null;
|
|
186
|
+
return { start, end };
|
|
187
|
+
}
|
|
188
|
+
function rangeFromMedia(media) {
|
|
189
|
+
const seekable = media?.seekable;
|
|
190
|
+
if (!seekable || seekable.length === 0) return null;
|
|
191
|
+
const start = seekable.start(0);
|
|
192
|
+
const end = seekable.end(seekable.length - 1);
|
|
193
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
194
|
+
return { start, end };
|
|
195
|
+
}
|
|
196
|
+
function targetFromDetails(details) {
|
|
197
|
+
if (!details) return null;
|
|
198
|
+
const targetduration = finite(details.targetduration);
|
|
199
|
+
return finite(details.partHoldBack) ?? finite(details.holdBack) ?? (targetduration !== null ? targetduration * 3 : null);
|
|
200
|
+
}
|
|
201
|
+
function edgeTolerance(details, targetLatency) {
|
|
202
|
+
const targetduration = finite(details?.targetduration);
|
|
203
|
+
const half = targetduration !== null ? targetduration / 2 : targetLatency / 2;
|
|
204
|
+
return Math.max(MIN_EDGE_TOLERANCE, finite(details?.partTarget) ?? half);
|
|
205
|
+
}
|
|
206
|
+
function computeLiveMetrics(source) {
|
|
207
|
+
if (source.kind === "hls") {
|
|
208
|
+
const { hls, details } = source;
|
|
209
|
+
const media2 = hls.media;
|
|
210
|
+
const seekableRange2 = rangeFromDetails(details) ?? rangeFromMedia(media2);
|
|
211
|
+
const targetLatency2 = finite(hls.targetLatency) ?? targetFromDetails(details) ?? DEFAULT_TARGET_LATENCY;
|
|
212
|
+
const latency2 = finite(hls.latency) ?? (seekableRange2 && media2 ? Math.max(0, seekableRange2.end - media2.currentTime) : null);
|
|
213
|
+
if (latency2 === null && seekableRange2 === null) return null;
|
|
214
|
+
const resolvedLatency = latency2 ?? 0;
|
|
215
|
+
return {
|
|
216
|
+
latency: resolvedLatency,
|
|
217
|
+
targetLatency: targetLatency2,
|
|
218
|
+
atEdge: resolvedLatency <= targetLatency2 + edgeTolerance(details, targetLatency2),
|
|
219
|
+
seekableRange: seekableRange2,
|
|
220
|
+
// Effective LL, not requested LL: the manifest has to carry parts or
|
|
221
|
+
// advertise blocking reloads, AND the host has to have asked for it.
|
|
222
|
+
lowLatency: source.lowLatencyRequested !== false && (!!details?.partList?.length || details?.canBlockReload === true)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const { media } = source;
|
|
226
|
+
const seekableRange = rangeFromMedia(media);
|
|
227
|
+
if (!seekableRange) return null;
|
|
228
|
+
const targetLatency = source.targetLatency ?? DEFAULT_TARGET_LATENCY;
|
|
229
|
+
const tolerance = source.targetLatency === void 0 ? NATIVE_EDGE_TOLERANCE : Math.max(MIN_EDGE_TOLERANCE, source.targetLatency / 2);
|
|
230
|
+
const latency = Math.max(0, seekableRange.end - media.currentTime);
|
|
231
|
+
return {
|
|
232
|
+
latency,
|
|
233
|
+
targetLatency,
|
|
234
|
+
atEdge: latency <= targetLatency + tolerance,
|
|
235
|
+
seekableRange,
|
|
236
|
+
lowLatency: source.lowLatency === true
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function applyLiveMetrics(api, metrics) {
|
|
240
|
+
if (!metrics) return;
|
|
241
|
+
const previousLatency = api.getState("liveLatency");
|
|
242
|
+
if (Math.abs(previousLatency - metrics.latency) > EPSILON) {
|
|
243
|
+
api.setState("liveLatency", metrics.latency);
|
|
244
|
+
api.emit("live:latency", { latency: metrics.latency });
|
|
245
|
+
}
|
|
246
|
+
if (api.getState("liveEdge") !== metrics.atEdge) {
|
|
247
|
+
api.setState("liveEdge", metrics.atEdge);
|
|
248
|
+
api.emit("live:edgechange", { atEdge: metrics.atEdge });
|
|
249
|
+
}
|
|
250
|
+
const range = metrics.seekableRange;
|
|
251
|
+
if (range) {
|
|
252
|
+
const previous = api.getState("seekableRange");
|
|
253
|
+
if (!previous || Math.abs(previous.start - range.start) > EPSILON || Math.abs(previous.end - range.end) > EPSILON) {
|
|
254
|
+
api.setState("seekableRange", { start: range.start, end: range.end });
|
|
255
|
+
api.emit("live:seekablerange", { start: range.start, end: range.end });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (api.getState("lowLatencyMode") !== metrics.lowLatency) {
|
|
259
|
+
api.setState("lowLatencyMode", metrics.lowLatency);
|
|
260
|
+
api.emit("live:lowlatency", { enabled: metrics.lowLatency });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function resetLiveMetrics(api) {
|
|
264
|
+
if (api.getState("lowLatencyMode")) {
|
|
265
|
+
api.setState("lowLatencyMode", false);
|
|
266
|
+
api.emit("live:lowlatency", { enabled: false });
|
|
267
|
+
}
|
|
268
|
+
api.setState("liveLatency", 0);
|
|
269
|
+
api.setState("liveEdge", false);
|
|
270
|
+
api.setState("seekableRange", null);
|
|
271
|
+
}
|
|
272
|
+
|
|
170
273
|
// src/event-map.ts
|
|
171
274
|
var HLS_ERROR_TYPES = {
|
|
172
275
|
NETWORK_ERROR: "networkError",
|
|
@@ -304,16 +407,18 @@ function setupHlsEventHandlers(hls, api, callbacks) {
|
|
|
304
407
|
if (data.details?.live !== void 0) {
|
|
305
408
|
api.setState("live", data.details.live);
|
|
306
409
|
if (data.details.live) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
410
|
+
callbacks.onLevelDetails?.(data.details);
|
|
411
|
+
applyLiveMetrics(
|
|
412
|
+
api,
|
|
413
|
+
computeLiveMetrics({
|
|
414
|
+
kind: "hls",
|
|
415
|
+
hls,
|
|
416
|
+
details: data.details,
|
|
417
|
+
lowLatencyRequested: callbacks.isLowLatencyRequested?.() ?? true
|
|
418
|
+
})
|
|
419
|
+
);
|
|
420
|
+
} else {
|
|
421
|
+
resetLiveMetrics(api);
|
|
317
422
|
}
|
|
318
423
|
callbacks.onLiveUpdate?.();
|
|
319
424
|
}
|
|
@@ -353,7 +458,7 @@ function setupHlsEventHandlers(hls, api, callbacks) {
|
|
|
353
458
|
api.setState("currentAudioTrack", null);
|
|
354
459
|
};
|
|
355
460
|
}
|
|
356
|
-
function setupVideoEventHandlers(video, api) {
|
|
461
|
+
function setupVideoEventHandlers(video, api, getLiveMetrics) {
|
|
357
462
|
const handlers = [];
|
|
358
463
|
const addHandler = (event, handler) => {
|
|
359
464
|
video.addEventListener(event, handler);
|
|
@@ -390,15 +495,11 @@ function setupVideoEventHandlers(video, api) {
|
|
|
390
495
|
addHandler("timeupdate", () => {
|
|
391
496
|
api.setState("currentTime", video.currentTime);
|
|
392
497
|
api.emit("playback:timeupdate", { currentTime: video.currentTime });
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const latency = Math.max(0, end - video.currentTime);
|
|
399
|
-
api.setState("liveEdge", latency < 10);
|
|
400
|
-
api.setState("liveLatency", latency);
|
|
401
|
-
}
|
|
498
|
+
if (api.getState("live") || !Number.isFinite(video.duration)) {
|
|
499
|
+
applyLiveMetrics(
|
|
500
|
+
api,
|
|
501
|
+
getLiveMetrics ? getLiveMetrics() : computeLiveMetrics({ kind: "media", media: video })
|
|
502
|
+
);
|
|
402
503
|
}
|
|
403
504
|
});
|
|
404
505
|
addHandler("durationchange", () => {
|
|
@@ -546,7 +647,7 @@ function createValidatingPlaylistLoader(Hls) {
|
|
|
546
647
|
}
|
|
547
648
|
|
|
548
649
|
// src/version.ts
|
|
549
|
-
var PKG_VERSION = true ? "1.
|
|
650
|
+
var PKG_VERSION = true ? "1.12.0" : "0.0.0-dev";
|
|
550
651
|
|
|
551
652
|
// src/create-hls-plugin.ts
|
|
552
653
|
var DEFAULT_CONFIG = {
|
|
@@ -574,6 +675,7 @@ var DEFAULT_CONFIG = {
|
|
|
574
675
|
// Never index a malformed live playlist refresh blindly
|
|
575
676
|
validatePlaylists: true
|
|
576
677
|
};
|
|
678
|
+
var LL_CATCH_UP_PLAYBACK_RATE = 1.1;
|
|
577
679
|
var MANIFEST_PHASE_ERRORS = [
|
|
578
680
|
"manifestLoadError",
|
|
579
681
|
"manifestLoadTimeOut",
|
|
@@ -589,6 +691,9 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
589
691
|
let cleanupHlsEvents = null;
|
|
590
692
|
let cleanupVideoEvents = null;
|
|
591
693
|
let isAutoQuality = true;
|
|
694
|
+
let lastLevelDetails = null;
|
|
695
|
+
let lastLiveMetrics = null;
|
|
696
|
+
let lastKnownTargetLatency = null;
|
|
592
697
|
let loadSession = 0;
|
|
593
698
|
let abortPendingLoad = null;
|
|
594
699
|
let networkRetryCount = 0;
|
|
@@ -611,6 +716,24 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
611
716
|
let stallWatchdogTimer = null;
|
|
612
717
|
let lastStallCheckTime = 0;
|
|
613
718
|
let lastStallCheckPosition = 0;
|
|
719
|
+
const readLiveMetrics = () => {
|
|
720
|
+
const metrics = hls && !isNative ? computeLiveMetrics({
|
|
721
|
+
kind: "hls",
|
|
722
|
+
hls,
|
|
723
|
+
details: lastLevelDetails,
|
|
724
|
+
lowLatencyRequested: mergedConfig.lowLatencyMode === true
|
|
725
|
+
}) : video ? computeLiveMetrics({
|
|
726
|
+
kind: "media",
|
|
727
|
+
media: video,
|
|
728
|
+
targetLatency: lastKnownTargetLatency ?? void 0,
|
|
729
|
+
lowLatency: lastLiveMetrics?.lowLatency
|
|
730
|
+
}) : null;
|
|
731
|
+
if (metrics) {
|
|
732
|
+
lastLiveMetrics = metrics;
|
|
733
|
+
if (hls && !isNative) lastKnownTargetLatency = metrics.targetLatency;
|
|
734
|
+
}
|
|
735
|
+
return metrics;
|
|
736
|
+
};
|
|
614
737
|
const applyPoster = () => {
|
|
615
738
|
if (!video) return;
|
|
616
739
|
video.poster = api?.getState("poster") || "";
|
|
@@ -660,6 +783,10 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
660
783
|
mediaRetryCount = 0;
|
|
661
784
|
errorCount = 0;
|
|
662
785
|
errorWindowStart = 0;
|
|
786
|
+
lastLevelDetails = null;
|
|
787
|
+
lastLiveMetrics = null;
|
|
788
|
+
lastKnownTargetLatency = null;
|
|
789
|
+
if (api) resetLiveMetrics(api);
|
|
663
790
|
};
|
|
664
791
|
const buildHlsConfig = () => {
|
|
665
792
|
const config2 = buildBaseHlsConfig();
|
|
@@ -671,27 +798,58 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
671
798
|
}
|
|
672
799
|
return config2;
|
|
673
800
|
};
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
801
|
+
const buildLiveHlsConfig = () => {
|
|
802
|
+
const live = {};
|
|
803
|
+
const set = (key, value) => {
|
|
804
|
+
if (value !== void 0) live[key] = value;
|
|
805
|
+
};
|
|
806
|
+
const syncDuration = mergedConfig.liveSyncDuration;
|
|
807
|
+
const syncCount = mergedConfig.liveSyncDurationCount;
|
|
808
|
+
const maxDuration = mergedConfig.liveMaxLatencyDuration;
|
|
809
|
+
const maxCount = mergedConfig.liveMaxLatencyDurationCount;
|
|
810
|
+
const mixed = (syncDuration !== void 0 || maxDuration !== void 0) && (syncCount !== void 0 || maxCount !== void 0);
|
|
811
|
+
const dropCount = mixed && syncDuration !== void 0;
|
|
812
|
+
const dropDuration = mixed && !dropCount;
|
|
813
|
+
if (mixed) {
|
|
814
|
+
api?.logger.warn(
|
|
815
|
+
`Ignoring ${dropCount ? "liveSyncDurationCount/liveMaxLatencyDurationCount" : "liveSyncDuration/liveMaxLatencyDuration"}: hls.js rejects a config mixing seconds-based and count-based live latency options`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
set("liveSyncDuration", dropDuration ? void 0 : syncDuration);
|
|
819
|
+
set("liveSyncDurationCount", dropCount ? void 0 : syncCount);
|
|
820
|
+
set("liveMaxLatencyDuration", dropDuration ? void 0 : maxDuration);
|
|
821
|
+
set("liveMaxLatencyDurationCount", dropCount ? void 0 : maxCount);
|
|
822
|
+
set("liveDurationInfinity", mergedConfig.liveDurationInfinity);
|
|
823
|
+
set(
|
|
824
|
+
"maxLiveSyncPlaybackRate",
|
|
825
|
+
mergedConfig.maxLiveSyncPlaybackRate ?? (mergedConfig.lowLatencyMode === true ? LL_CATCH_UP_PLAYBACK_RATE : void 0)
|
|
826
|
+
);
|
|
827
|
+
return live;
|
|
828
|
+
};
|
|
829
|
+
const buildBaseHlsConfig = () => {
|
|
830
|
+
return {
|
|
831
|
+
debug: mergedConfig.debug,
|
|
832
|
+
autoStartLoad: mergedConfig.autoStartLoad,
|
|
833
|
+
startPosition: mergedConfig.startPosition,
|
|
834
|
+
startLevel: -1,
|
|
835
|
+
// Auto quality selection (ABR)
|
|
836
|
+
abrEwmaDefaultEstimate: getInitialBandwidthEstimate(mergedConfig.initialBandwidthEstimate),
|
|
837
|
+
lowLatencyMode: mergedConfig.lowLatencyMode,
|
|
838
|
+
maxBufferLength: mergedConfig.maxBufferLength,
|
|
839
|
+
maxMaxBufferLength: mergedConfig.maxMaxBufferLength,
|
|
840
|
+
backBufferLength: mergedConfig.backBufferLength,
|
|
841
|
+
enableWorker: mergedConfig.enableWorker,
|
|
842
|
+
capLevelToPlayerSize: mergedConfig.capLevelToPlayerSize,
|
|
843
|
+
// Minimize hls.js internal retries - we handle retries ourselves
|
|
844
|
+
fragLoadingMaxRetry: 1,
|
|
845
|
+
manifestLoadingMaxRetry: 1,
|
|
846
|
+
levelLoadingMaxRetry: 1,
|
|
847
|
+
fragLoadingRetryDelay: 500,
|
|
848
|
+
manifestLoadingRetryDelay: 500,
|
|
849
|
+
levelLoadingRetryDelay: 500,
|
|
850
|
+
...buildLiveHlsConfig()
|
|
851
|
+
};
|
|
852
|
+
};
|
|
695
853
|
const getRetryDelay = (retryCount) => {
|
|
696
854
|
const baseDelay = mergedConfig.retryDelayMs ?? 1e3;
|
|
697
855
|
const backoffFactor = mergedConfig.retryBackoffFactor ?? 2;
|
|
@@ -888,7 +1046,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
888
1046
|
const videoEl = getOrCreateVideo();
|
|
889
1047
|
isNative = true;
|
|
890
1048
|
if (api) {
|
|
891
|
-
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api);
|
|
1049
|
+
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
|
|
892
1050
|
}
|
|
893
1051
|
return new Promise((resolve, reject) => {
|
|
894
1052
|
let watchdog = null;
|
|
@@ -981,7 +1139,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
981
1139
|
isNative = false;
|
|
982
1140
|
hls = loader.createHlsInstance(buildHlsConfig());
|
|
983
1141
|
if (api) {
|
|
984
|
-
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api);
|
|
1142
|
+
cleanupVideoEvents = setupVideoEventHandlers(videoEl, api, readLiveMetrics);
|
|
985
1143
|
}
|
|
986
1144
|
return new Promise((resolve, reject) => {
|
|
987
1145
|
if (!hls || !api) {
|
|
@@ -1023,6 +1181,12 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1023
1181
|
},
|
|
1024
1182
|
onLevelSwitched: () => {
|
|
1025
1183
|
},
|
|
1184
|
+
onLevelDetails: (details) => {
|
|
1185
|
+
if (session !== loadSession) return;
|
|
1186
|
+
lastLevelDetails = details;
|
|
1187
|
+
readLiveMetrics();
|
|
1188
|
+
},
|
|
1189
|
+
isLowLatencyRequested: () => mergedConfig.lowLatencyMode === true,
|
|
1026
1190
|
onError: (error) => {
|
|
1027
1191
|
if (session !== loadSession) return;
|
|
1028
1192
|
const terminal = handleHlsError(error);
|
|
@@ -1460,25 +1624,45 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1460
1624
|
isNativeHLS() {
|
|
1461
1625
|
return isNative;
|
|
1462
1626
|
},
|
|
1627
|
+
/**
|
|
1628
|
+
* Report the live state of the stream.
|
|
1629
|
+
*
|
|
1630
|
+
* `latency` and `targetLatency` come from hls.js on the MSE path, where
|
|
1631
|
+
* they are measured against `EXT-X-PROGRAM-DATE-TIME` drift when the
|
|
1632
|
+
* manifest carries it. On the native path there is no latency API, so both
|
|
1633
|
+
* are approximations: latency is the distance to `seekable.end`, and the
|
|
1634
|
+
* target is whatever an earlier hls.js session on this source measured
|
|
1635
|
+
* (an AirPlay handoff) before it falls back to 3 seconds. Parking a viewer
|
|
1636
|
+
* of a 2-second-target stream 3 seconds back was the previous behaviour,
|
|
1637
|
+
* and it is a full target latency of drift.
|
|
1638
|
+
*
|
|
1639
|
+
* @returns Live info, or null for VOD and before a pipeline exists
|
|
1640
|
+
*/
|
|
1463
1641
|
getLiveInfo() {
|
|
1464
1642
|
const live = api?.getState("live") || false;
|
|
1465
1643
|
if (!live) return null;
|
|
1644
|
+
const metrics = readLiveMetrics();
|
|
1466
1645
|
if (isNative) {
|
|
1646
|
+
const targetLatency2 = metrics?.targetLatency ?? DEFAULT_TARGET_LATENCY;
|
|
1647
|
+
const seekableEnd = video?.seekable?.length ? video.seekable.end(video.seekable.length - 1) : void 0;
|
|
1467
1648
|
return {
|
|
1468
1649
|
isLive: true,
|
|
1469
|
-
latency: 0,
|
|
1470
|
-
targetLatency:
|
|
1650
|
+
latency: metrics?.latency ?? 0,
|
|
1651
|
+
targetLatency: targetLatency2,
|
|
1471
1652
|
drift: 0,
|
|
1472
|
-
liveSyncPosition:
|
|
1653
|
+
liveSyncPosition: seekableEnd !== void 0 ? Math.max(0, seekableEnd - targetLatency2) : void 0,
|
|
1654
|
+
lowLatency: metrics?.lowLatency ?? false
|
|
1473
1655
|
};
|
|
1474
1656
|
}
|
|
1475
1657
|
if (!hls) return null;
|
|
1658
|
+
const targetLatency = hls.targetLatency || metrics?.targetLatency || DEFAULT_TARGET_LATENCY;
|
|
1476
1659
|
return {
|
|
1477
1660
|
isLive: true,
|
|
1478
1661
|
latency: hls.latency || 0,
|
|
1479
|
-
targetLatency
|
|
1662
|
+
targetLatency,
|
|
1480
1663
|
drift: hls.drift || 0,
|
|
1481
|
-
liveSyncPosition: hls.liveSyncPosition ?? (video?.seekable?.length ? Math.max(0, video.seekable.end(video.seekable.length - 1) -
|
|
1664
|
+
liveSyncPosition: hls.liveSyncPosition ?? (video?.seekable?.length ? Math.max(0, video.seekable.end(video.seekable.length - 1) - targetLatency) : void 0),
|
|
1665
|
+
lowLatency: metrics?.lowLatency ?? false
|
|
1482
1666
|
};
|
|
1483
1667
|
},
|
|
1484
1668
|
/**
|
|
@@ -1503,10 +1687,12 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1503
1687
|
const wasPlaying = api?.getState("playing") || false;
|
|
1504
1688
|
const currentTime = video?.currentTime || 0;
|
|
1505
1689
|
const savedSrc = currentSrc;
|
|
1690
|
+
const savedTargetLatency = lastKnownTargetLatency;
|
|
1506
1691
|
const session = ++loadSession;
|
|
1507
1692
|
cancelReconnect();
|
|
1508
1693
|
cleanup(new Error("HLS load cancelled: switching to native HLS"));
|
|
1509
1694
|
currentSrc = savedSrc;
|
|
1695
|
+
lastKnownTargetLatency = savedTargetLatency;
|
|
1510
1696
|
await loadNative(savedSrc);
|
|
1511
1697
|
if (session !== loadSession) return;
|
|
1512
1698
|
if (video && currentTime > 0) {
|
|
@@ -1542,10 +1728,12 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1542
1728
|
const wasPlaying = api?.getState("playing") || false;
|
|
1543
1729
|
const currentTime = video?.currentTime || 0;
|
|
1544
1730
|
const savedSrc = currentSrc;
|
|
1731
|
+
const savedTargetLatency = lastKnownTargetLatency;
|
|
1545
1732
|
const session = ++loadSession;
|
|
1546
1733
|
cancelReconnect();
|
|
1547
1734
|
cleanup(new Error("HLS load cancelled: switching to hls.js"));
|
|
1548
1735
|
currentSrc = savedSrc;
|
|
1736
|
+
lastKnownTargetLatency = savedTargetLatency;
|
|
1549
1737
|
await loadWithHlsJs(savedSrc);
|
|
1550
1738
|
if (session !== loadSession) return;
|
|
1551
1739
|
if (video && currentTime > 0) {
|
|
@@ -1580,6 +1768,8 @@ function createHLSPlugin(config) {
|
|
|
1580
1768
|
var index_default = createHLSPlugin;
|
|
1581
1769
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1582
1770
|
0 && (module.exports = {
|
|
1771
|
+
DEFAULT_TARGET_LATENCY,
|
|
1772
|
+
computeLiveMetrics,
|
|
1583
1773
|
createHLSPlugin,
|
|
1584
1774
|
sanitizeUrl
|
|
1585
1775
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HLSPluginConfig, I as IHLSPlugin } from './
|
|
2
|
-
export { b as HLSError, c as HLSLiveInfo, a as HLSQualityLevel } from './
|
|
1
|
+
import { H as HLSPluginConfig, I as IHLSPlugin } from './live-metrics-CoYkWhl5.cjs';
|
|
2
|
+
export { D as DEFAULT_TARGET_LATENCY, b as HLSError, c as HLSLiveInfo, a as HLSQualityLevel, f as HlsLevelDetails, L as LiveMetrics, e as LiveMetricsSource, d as computeLiveMetrics } from './live-metrics-CoYkWhl5.cjs';
|
|
3
3
|
export { sanitizeUrl } from '@scarlett-player/core';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HLSPluginConfig, I as IHLSPlugin } from './
|
|
2
|
-
export { b as HLSError, c as HLSLiveInfo, a as HLSQualityLevel } from './
|
|
1
|
+
import { H as HLSPluginConfig, I as IHLSPlugin } from './live-metrics-CoYkWhl5.js';
|
|
2
|
+
export { D as DEFAULT_TARGET_LATENCY, b as HLSError, c as HLSLiveInfo, a as HLSQualityLevel, f as HlsLevelDetails, L as LiveMetrics, e as LiveMetricsSource, d as computeLiveMetrics } from './live-metrics-CoYkWhl5.js';
|
|
3
3
|
export { sanitizeUrl } from '@scarlett-player/core';
|
|
4
4
|
|
|
5
5
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_TARGET_LATENCY,
|
|
2
3
|
__export,
|
|
4
|
+
computeLiveMetrics,
|
|
3
5
|
createHLSPluginWith,
|
|
4
6
|
sanitizeUrl
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-47MU7IVX.js";
|
|
6
8
|
|
|
7
9
|
// src/hls-loader.ts
|
|
8
10
|
var hls_loader_exports = {};
|
|
@@ -85,6 +87,8 @@ function createHLSPlugin(config) {
|
|
|
85
87
|
}
|
|
86
88
|
var index_default = createHLSPlugin;
|
|
87
89
|
export {
|
|
90
|
+
DEFAULT_TARGET_LATENCY,
|
|
91
|
+
computeLiveMetrics,
|
|
88
92
|
createHLSPlugin,
|
|
89
93
|
index_default as default,
|
|
90
94
|
sanitizeUrl
|