Files
v10/dist/dev/dom/hls-js/live.js
T

125 lines
4.2 KiB
JavaScript

import { MediaStreamTypes } from "../../core/types.js";
import Hls from "hls.js";
//#region src/dom/hls-js/live.ts
function HlsJsMediaLiveMixin(BaseClass) {
class HlsJsMediaLive extends BaseClass {
#targetLiveWindow = NaN;
#liveEdgeStartOffset;
#seekToLiveAbort = null;
#seekToLivePending = false;
constructor(...args) {
super(...args);
const { engine } = this;
engine?.on(Hls.Events.MANIFEST_LOADING, () => {
this.#reset();
this.#armSeekToLive();
});
engine?.on(Hls.Events.MEDIA_ATTACHED, () => this.#armSeekToLive());
engine?.on(Hls.Events.MEDIA_DETACHED, () => this.#disarmSeekToLive());
engine?.on(Hls.Events.DESTROYING, () => {
this.#reset();
this.#disarmSeekToLive();
});
engine?.on(Hls.Events.LEVEL_LOADED, (_event, data) => {
this.#derive(data.details);
if (this.#seekToLivePending) this.#trySeekToLive();
});
}
get targetLiveWindow() {
return this.#targetLiveWindow;
}
get liveEdgeStart() {
if (this.#liveEdgeStartOffset === void 0) return NaN;
const { target } = this;
if (!target) return NaN;
const { seekable } = target;
if (!seekable.length) return NaN;
return seekable.end(seekable.length - 1) - this.#liveEdgeStartOffset;
}
#derive(details) {
if (!details.live) return this.#reset();
const info = getStreamInfoFromHlsjsLevelDetails(details);
this.#liveEdgeStartOffset = info.liveEdgeStartOffset;
this.#updateConfig(info);
this.#setTargetLiveWindow(info.targetLiveWindow);
}
#reset() {
this.#liveEdgeStartOffset = void 0;
this.#setTargetLiveWindow(NaN);
}
#updateConfig({ streamType, lowLatency }) {
if (streamType === MediaStreamTypes.LIVE) {
const hls = this.engine;
if (!hls) return;
if (lowLatency) {
hls.config.backBufferLength = hls.userConfig.backBufferLength ?? 4;
hls.config.maxFragLookUpTolerance = hls.userConfig.maxFragLookUpTolerance ?? .001;
hls.config.abrBandWidthUpFactor = hls.userConfig.abrBandWidthUpFactor ?? hls.config.abrBandWidthFactor;
} else hls.config.backBufferLength = hls.userConfig.backBufferLength ?? 8;
}
}
#setTargetLiveWindow(value) {
if (Object.is(this.#targetLiveWindow, value)) return;
this.#targetLiveWindow = value;
this.dispatchEvent(new Event("targetlivewindowchange"));
}
/**
* Arm a one-shot seek-to-live on the first user-initiated `play`. Skipped
* when `autoplay` is set, since hls.js positions at the live edge during
* its own startup sequence and a programmatic seek would race that.
*/
#armSeekToLive() {
this.#disarmSeekToLive();
const target = this.target;
if (!target || target.autoplay) return;
this.#seekToLiveAbort = new AbortController();
target.addEventListener("play", () => {
this.#seekToLivePending = true;
this.#trySeekToLive();
}, {
signal: this.#seekToLiveAbort.signal,
once: true
});
}
#disarmSeekToLive() {
this.#seekToLiveAbort?.abort();
this.#seekToLiveAbort = null;
this.#seekToLivePending = false;
}
#trySeekToLive() {
const target = this.target;
if (!target) return;
const { liveEdgeStart } = this;
if (!Number.isFinite(liveEdgeStart)) return;
if (target.currentTime < liveEdgeStart) target.currentTime = liveEdgeStart;
this.#seekToLivePending = false;
}
}
return HlsJsMediaLive;
}
const getStreamInfoFromHlsjsLevelDetails = (levelDetails) => {
const playlistType = levelDetails.type;
const streamType = toStreamTypeFromPlaylistType(playlistType);
const targetLiveWindow = toTargetLiveWindowFromPlaylistType(playlistType);
const lowLatency = !!levelDetails.partList?.length;
let liveEdgeStartOffset;
if (streamType === MediaStreamTypes.LIVE) liveEdgeStartOffset = lowLatency ? levelDetails.partHoldBack || levelDetails.partTarget * 2 : levelDetails.holdBack || levelDetails.targetduration * 3;
return {
streamType,
targetLiveWindow,
liveEdgeStartOffset,
lowLatency
};
};
const toStreamTypeFromPlaylistType = (playlistType) => {
return playlistType === "VOD" ? MediaStreamTypes.ON_DEMAND : MediaStreamTypes.LIVE;
};
const toTargetLiveWindowFromPlaylistType = (playlistType) => {
if (playlistType === "EVENT") return Number.POSITIVE_INFINITY;
if (playlistType === "VOD") return NaN;
return 0;
};
//#endregion
export { HlsJsMediaLiveMixin };
//# sourceMappingURL=live.js.map