p2p-media-loader-hlsjs
Version:
P2P Media Loader hls.js integration
295 lines (294 loc) • 9.2 kB
JavaScript
import { Core as e, Core as t, CoreRequestError as n, debug as r, generateStreamShortId as i } from "p2p-media-loader-core";
//#region src/utils.ts
function a(e, t) {
return t ? `${e}|${t.start}-${t.end}` : e;
}
function o(e, t) {
if (e !== void 0 && t !== void 0 && e <= t) return {
start: e,
end: t
};
}
//#endregion
//#region src/fragment-loader.ts
var s = 10, c = class {
context;
config;
stats;
#e;
#t;
#n;
#r;
#i;
#a;
constructor(e, t) {
this.#r = t, this.#t = () => new e.loader(e), this.stats = {
aborted: !1,
chunkCount: 0,
loading: {
start: 0,
first: 0,
end: 0
},
buffering: {
start: 0,
first: 0,
end: 0
},
parsing: {
start: 0,
end: 0
},
total: 1,
loaded: 1,
bwEstimate: 0,
retry: 0
};
}
load(e, t, r) {
this.context = e, this.config = t, this.#e = r;
let { stats: i } = this, { rangeStart: s, rangeEnd: c } = e, u = o(s, c === void 0 ? void 0 : c - 1);
this.#a = a(e.url, u);
let d = this.#r.isSegmentLoadable(this.#a);
if (!this.#r.hasSegment(this.#a) || !d) {
this.#n = this.#t(), this.#n.stats = this.stats, this.#n.load(e, t, r);
return;
}
this.#r.loadSegment(this.#a, {
onSuccess: (t) => {
if (!this.#e) return;
this.#i = t;
let n = this.#i.data.byteLength;
i.loading = l(this.#i.bandwidth, n, performance.now()), i.total = n, i.loaded = n;
let r = this.#i.data.slice(0);
this.#e.onProgress && this.#e.onProgress(this.stats, e, r, void 0), this.#e.onSuccess({
data: r,
url: e.url
}, this.stats, e, void 0);
},
onError: (e) => {
e instanceof n && e.type === "aborted" && this.stats.aborted || this.#o(e);
}
});
}
#o(e) {
let t = {
code: 0,
text: ""
};
(e instanceof n && e.type === "failed" || e instanceof Error) && (t.text = e.message), this.#e?.onError(t, this.context, null, this.stats);
}
#s() {
!this.#i && this.#a && (this.stats.aborted = !0, this.#r.abortSegmentLoading(this.#a));
}
abort() {
this.#n ? this.#n.abort() : (this.#s(), this.#e?.onAbort?.(this.stats, this.context, {}));
}
destroy() {
this.#n ? this.#n.destroy() : (this.stats.aborted || this.#s(), this.#e = null, this.config = null);
}
};
function l(e, t, n) {
let r = e > 0 ? t * 8e3 / e : 0, i = Math.max(0, n - r);
return {
start: Math.max(0, i - s),
first: i,
end: n
};
}
//#endregion
//#region src/playlist-loader.ts
var u = class {
#e;
context;
stats;
constructor(e) {
this.#e = new e.loader(e), this.stats = this.#e.stats, this.context = this.#e.context;
}
load(e, t, n) {
this.#e.load(e, t, n);
}
abort() {
this.#e.abort();
}
destroy() {
this.#e.destroy();
}
}, d = class {
core;
constructor(e) {
this.core = e;
}
processMainManifest(e) {
let { levels: t, audioTracks: n } = e;
for (let e of t) {
let { url: t, bitrate: n, maxBitrate: r, videoCodec: a, width: o, height: s } = e, c = r ?? n, l = c === 0, u = e.attrs["FRAME-RATE"], d = e.attrs["VIDEO-RANGE"], f = i({
bitrate: c,
codecs: l ? void 0 : a,
width: l ? void 0 : o,
height: l ? void 0 : s,
frameRate: l ? void 0 : u,
videoRange: l ? void 0 : d
});
this.core.addStreamIfNoneExists({
runtimeId: Array.isArray(t) ? t[0] : t,
type: "main",
index: f
});
}
for (let e of n) {
let { url: t, audioCodec: n, lang: r, channels: a, name: o } = e, s = i({
bitrate: 0,
codecs: n,
language: r,
channels: a,
name: o
});
this.core.addStreamIfNoneExists({
runtimeId: Array.isArray(t) ? t[0] : t,
type: "secondary",
index: s
});
}
}
updatePlaylist(e) {
let { details: { url: t, fragments: n, live: r } } = e, i = this.core.getStream(t);
if (!i) return;
let s = new Set(i.segments.keys()), c = [];
n.forEach((e, t) => {
let { url: n, byteRange: l, sn: u, start: d, end: f } = e, [p, m] = l, h = o(p, m === void 0 ? void 0 : m - 1), g = a(n, h);
s.delete(g), !i.segments.has(g) && c.push({
runtimeId: g,
url: n,
externalId: r ? u : t,
byteRange: h,
startTime: d,
endTime: f
});
}), !(!c.length && !s.size) && this.core.updateStream(t, c, s.values());
}
};
//#endregion
//#region src/engine-static.ts
function f(e) {
return class extends e {
#e;
get p2pEngine() {
return this.#e;
}
constructor(...e) {
let { p2p: t, ...n } = e[0] ?? {}, r = new m(t);
super({
...n,
...r.getConfigForHlsJs()
}), r.bindHls(this), this.#e = r, t?.onHlsJsCreated?.(this);
}
};
}
//#endregion
//#region src/engine.ts
var p = 120, m = class {
core;
segmentManager;
hlsInstanceGetter;
currentHlsInstance;
debug = r("p2pml-hlsjs:engine");
static injectMixin(e) {
return f(e);
}
constructor(e) {
this.core = new t(e?.core), this.segmentManager = new d(this.core);
}
addEventListener(e, t) {
this.core.addEventListener(e, t);
}
removeEventListener(e, t) {
this.core.removeEventListener(e, t);
}
getConfigForHlsJs() {
return {
fLoader: this.createFragmentLoaderClass(),
pLoader: this.createPlaylistLoaderClass()
};
}
getConfig() {
return { core: this.core.getConfig() };
}
applyDynamicConfig(e) {
e.core && this.core.applyDynamicConfig(e.core);
}
bindHls(e) {
this.hlsInstanceGetter = typeof e == "function" ? e : () => e;
}
initHlsEvents() {
let e = this.hlsInstanceGetter?.();
this.currentHlsInstance !== e && (this.currentHlsInstance && this.destroy(), this.currentHlsInstance = e, this.updateHlsEventsHandlers("register"), this.updateMediaElementEventHandlers("register"));
}
updateHlsEventsHandlers(e) {
let t = this.currentHlsInstance;
if (!t) return;
let n = e === "register" ? "on" : "off";
t[n]("hlsManifestLoaded", this.handleManifestLoaded), t[n]("hlsLevelSwitching", this.handleLevelSwitching), t[n]("hlsLevelUpdated", this.handleLevelUpdated), t[n]("hlsAudioTrackLoaded", this.handleLevelUpdated), t[n]("hlsDestroying", this.destroy), t[n]("hlsMediaAttaching", this.destroyCore), t[n]("hlsManifestLoading", this.destroyCore), t[n]("hlsMediaDetached", this.handleMediaDetached), t[n]("hlsMediaAttached", this.handleMediaAttached);
}
updateMediaElementEventHandlers = (e) => {
let t = this.currentHlsInstance?.media;
if (!t) return;
let n = e === "register" ? "addEventListener" : "removeEventListener";
t[n]("timeupdate", this.handlePlaybackUpdate), t[n]("seeking", this.handlePlaybackUpdate), t[n]("ratechange", this.handlePlaybackUpdate);
};
handleManifestLoaded = (e, t) => {
let n = t.networkDetails;
n instanceof XMLHttpRequest ? this.core.setManifestResponseUrl(n.responseURL) : n instanceof Response && this.core.setManifestResponseUrl(n.url), this.segmentManager.processMainManifest(t);
};
handleLevelSwitching = (e, t) => {
t.bitrate && this.core.setActiveLevelBitrate(t.bitrate);
};
handleLevelUpdated = (e, t) => {
this.currentHlsInstance && t.details.fragments[0].type === "main" && t.details.fragments.length > 4 && (t.details.live && !this.currentHlsInstance.userConfig.liveSyncDuration && !this.currentHlsInstance.userConfig.liveSyncDurationCount && this.updateLiveSyncDurationCount(t), !this.currentHlsInstance.userConfig.maxBufferLength && !this.currentHlsInstance.userConfig.maxMaxBufferLength && this.updateMaxBufferLength(t.details.targetduration)), this.core.setIsLive(t.details.live), this.segmentManager.updatePlaylist(t);
};
updateLiveSyncDurationCount(e) {
let t = e.details.targetduration, n = Math.floor(p / t), r = Math.min(e.details.fragments.length - 1, n);
this.currentHlsInstance && this.currentHlsInstance.config.liveSyncDurationCount !== r && (this.debug(`Setting liveSyncDurationCount to ${r}`), this.currentHlsInstance.config.liveSyncDurationCount = r);
}
updateMaxBufferLength(e) {
if (!this.currentHlsInstance) return;
let t = this.core.getConfig(), n = Math.max(t.mainStream.highDemandTimeWindow, t.secondaryStream.highDemandTimeWindow), r = Math.max(e * 2, n);
this.currentHlsInstance.config.maxBufferLength > r && (this.debug(`Setting maxBufferLength to ${r}`), this.currentHlsInstance.config.maxBufferLength = r), this.currentHlsInstance.config.maxMaxBufferLength > r && (this.debug(`Setting maxMaxBufferLength to ${r}`), this.currentHlsInstance.config.maxMaxBufferLength = r);
}
handleMediaAttached = () => {
this.updateMediaElementEventHandlers("register");
};
handleMediaDetached = () => {
this.updateMediaElementEventHandlers("unregister");
};
handlePlaybackUpdate = (e) => {
let t = e.target;
this.core.updatePlayback(t.currentTime, t.playbackRate);
};
destroyCore = () => this.core.destroy();
destroy = () => {
this.destroyCore(), this.updateHlsEventsHandlers("unregister"), this.updateMediaElementEventHandlers("unregister"), this.currentHlsInstance = void 0;
};
createFragmentLoaderClass() {
let { core: e } = this, t = this;
return class extends c {
constructor(t) {
super(t, e);
}
static getEngine() {
return t;
}
};
}
createPlaylistLoaderClass() {
let e = this;
return class extends u {
constructor(t) {
super(t), e.initHlsEvents();
}
};
}
};
//#endregion
export { e as Core, m as HlsJsP2PEngine };
//# sourceMappingURL=p2p-media-loader-hlsjs.es.min.js.map