@cdn365/p2p-media-loader-hlsjs 1.0.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.
@@ -0,0 +1,202 @@
1
+ import { CoreRequestError as E, debug as M, Core as C } from "p2p-media-loader-core";
2
+ function H(r, t) {
3
+ return t ? `${r}|${t.start}-${t.end}` : r;
4
+ }
5
+ function L(r, t) {
6
+ if (r !== void 0 && t !== void 0 && r <= t) return { start: r, end: t };
7
+ }
8
+ class I {
9
+ context;
10
+ config;
11
+ stats;
12
+ #t;
13
+ #a;
14
+ #e;
15
+ #i;
16
+ #s;
17
+ #n;
18
+ constructor(t, e) {
19
+ this.#i = e, this.#a = () => new t.loader(t), 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 };
20
+ }
21
+ load(t, e, s) {
22
+ this.context = t, this.config = e, this.#t = s;
23
+ const { stats: i } = this, { rangeStart: a, rangeEnd: n } = t, o = L(a, n !== void 0 ? n - 1 : void 0);
24
+ this.#n = H(t.url, o);
25
+ const u = this.#i.isSegmentLoadable(this.#n);
26
+ if (!this.#i.hasSegment(this.#n) || !u) return this.#e = this.#a(), this.#e.stats = this.stats, void this.#e.load(t, e, s);
27
+ this.#i.loadSegment(this.#n, { onSuccess: (d) => {
28
+ this.#s = d;
29
+ const h = this.#s.data.byteLength;
30
+ i.loading = (function(g, f, c) {
31
+ const p = 8e3 * f / g, l = c - p;
32
+ return { start: l - 10, first: l, end: c };
33
+ })(this.#s.bandwidth, h, performance.now()), i.total = h, i.loaded = h, s.onProgress && s.onProgress(this.stats, t, this.#s.data, void 0), s.onSuccess({ data: this.#s.data, url: t.url }, this.stats, t, void 0);
34
+ }, onError: (d) => {
35
+ d instanceof E && d.type === "aborted" && this.stats.aborted || this.#o(d);
36
+ } });
37
+ }
38
+ #o(t) {
39
+ const e = { code: 0, text: "" };
40
+ (t instanceof E && t.type === "failed" || t instanceof Error) && (e.text = t.message), this.#t?.onError(e, this.context, null, this.stats);
41
+ }
42
+ #r() {
43
+ !this.#s && this.#n && (this.stats.aborted = !0, this.#i.abortSegmentLoading(this.#n));
44
+ }
45
+ abort() {
46
+ this.#e ? this.#e.abort() : (this.#r(), this.#t?.onAbort?.(this.stats, this.context, {}));
47
+ }
48
+ destroy() {
49
+ this.#e ? this.#e.destroy() : (this.stats.aborted || this.#r(), this.#t = null, this.config = null);
50
+ }
51
+ }
52
+ class S {
53
+ #t;
54
+ context;
55
+ stats;
56
+ constructor(t) {
57
+ this.#t = new t.loader(t), this.stats = this.#t.stats, this.context = this.#t.context;
58
+ }
59
+ load(t, e, s) {
60
+ this.#t.load(t, e, s);
61
+ }
62
+ abort() {
63
+ this.#t.abort();
64
+ }
65
+ destroy() {
66
+ this.#t.destroy();
67
+ }
68
+ }
69
+ class x {
70
+ core;
71
+ constructor(t) {
72
+ this.core = t;
73
+ }
74
+ processMainManifest(t) {
75
+ const { levels: e, audioTracks: s } = t;
76
+ for (const [i, a] of e.entries()) {
77
+ const { url: n } = a;
78
+ this.core.addStreamIfNoneExists({ runtimeId: Array.isArray(n) ? n[0] : n, type: "main", index: i });
79
+ }
80
+ for (const [i, a] of s.entries()) {
81
+ const { url: n } = a;
82
+ this.core.addStreamIfNoneExists({ runtimeId: Array.isArray(n) ? n[0] : n, type: "secondary", index: i });
83
+ }
84
+ }
85
+ updatePlaylist(t) {
86
+ const { details: { url: e, fragments: s, live: i } } = t, a = this.core.getStream(e);
87
+ if (!a) return;
88
+ const n = new Set(a.segments.keys()), o = [];
89
+ s.forEach((u, d) => {
90
+ const { url: h, byteRange: g, sn: f, start: c, end: p } = u, [l, v] = g, m = L(l, v !== void 0 ? v - 1 : void 0), y = H(h, m);
91
+ n.delete(y), a.segments.has(y) || o.push({ runtimeId: y, url: h, externalId: i ? f : d, byteRange: m, startTime: c, endTime: p });
92
+ }), (o.length || n.size) && this.core.updateStream(e, o, n.values());
93
+ }
94
+ }
95
+ class b {
96
+ core;
97
+ segmentManager;
98
+ hlsInstanceGetter;
99
+ currentHlsInstance;
100
+ debug = M("p2pml-hlsjs:engine");
101
+ static injectMixin(t) {
102
+ return e = t, class extends e {
103
+ #t;
104
+ get p2pEngine() {
105
+ return this.#t;
106
+ }
107
+ constructor(...s) {
108
+ const i = s[0], { p2p: a, ...n } = i ?? {}, o = new b(a);
109
+ super({ ...n, ...o.getConfigForHlsJs() }), o.bindHls(this), this.#t = o, a?.onHlsJsCreated?.(this);
110
+ }
111
+ };
112
+ var e;
113
+ }
114
+ constructor(t) {
115
+ this.core = new C(t?.core), this.segmentManager = new x(this.core);
116
+ }
117
+ addEventListener(t, e) {
118
+ this.core.addEventListener(t, e);
119
+ }
120
+ removeEventListener(t, e) {
121
+ this.core.removeEventListener(t, e);
122
+ }
123
+ getConfigForHlsJs() {
124
+ return { fLoader: this.createFragmentLoaderClass(), pLoader: this.createPlaylistLoaderClass() };
125
+ }
126
+ getConfig() {
127
+ return { core: this.core.getConfig() };
128
+ }
129
+ applyDynamicConfig(t) {
130
+ t.core && this.core.applyDynamicConfig(t.core);
131
+ }
132
+ bindHls(t) {
133
+ this.hlsInstanceGetter = typeof t == "function" ? t : () => t;
134
+ }
135
+ initHlsEvents() {
136
+ const t = this.hlsInstanceGetter?.();
137
+ this.currentHlsInstance !== t && (this.currentHlsInstance && this.destroy(), this.currentHlsInstance = t, this.updateHlsEventsHandlers("register"), this.updateMediaElementEventHandlers("register"));
138
+ }
139
+ updateHlsEventsHandlers(t) {
140
+ const e = this.currentHlsInstance;
141
+ if (!e) return;
142
+ const s = t === "register" ? "on" : "off";
143
+ e[s]("hlsManifestLoaded", this.handleManifestLoaded), e[s]("hlsLevelSwitching", this.handleLevelSwitching), e[s]("hlsLevelUpdated", this.handleLevelUpdated), e[s]("hlsAudioTrackLoaded", this.handleLevelUpdated), e[s]("hlsDestroying", this.destroy), e[s]("hlsMediaAttaching", this.destroyCore), e[s]("hlsManifestLoading", this.destroyCore), e[s]("hlsMediaDetached", this.handleMediaDetached), e[s]("hlsMediaAttached", this.handleMediaAttached);
144
+ }
145
+ updateMediaElementEventHandlers = (t) => {
146
+ const e = this.currentHlsInstance?.media;
147
+ if (!e) return;
148
+ const s = t === "register" ? "addEventListener" : "removeEventListener";
149
+ e[s]("timeupdate", this.handlePlaybackUpdate), e[s]("seeking", this.handlePlaybackUpdate), e[s]("ratechange", this.handlePlaybackUpdate);
150
+ };
151
+ handleManifestLoaded = (t, e) => {
152
+ const s = e.networkDetails;
153
+ s instanceof XMLHttpRequest ? this.core.setManifestResponseUrl(s.responseURL) : s instanceof Response && this.core.setManifestResponseUrl(s.url), this.segmentManager.processMainManifest(e);
154
+ };
155
+ handleLevelSwitching = (t, e) => {
156
+ e.bitrate && this.core.setActiveLevelBitrate(e.bitrate);
157
+ };
158
+ handleLevelUpdated = (t, e) => {
159
+ this.currentHlsInstance && e.details.live && e.details.fragments[0].type === "main" && !this.currentHlsInstance.userConfig.liveSyncDuration && !this.currentHlsInstance.userConfig.liveSyncDurationCount && e.details.fragments.length > 4 && this.updateLiveSyncDurationCount(e), this.core.setIsLive(e.details.live), this.segmentManager.updatePlaylist(e);
160
+ };
161
+ updateLiveSyncDurationCount(t) {
162
+ const e = t.details.targetduration, s = Math.floor(120 / e), i = Math.min(t.details.fragments.length - 1, s);
163
+ this.currentHlsInstance && this.currentHlsInstance.config.liveSyncDurationCount !== i && (this.debug(`Setting liveSyncDurationCount to ${i}`), this.currentHlsInstance.config.liveSyncDurationCount = i);
164
+ }
165
+ handleMediaAttached = () => {
166
+ this.updateMediaElementEventHandlers("register");
167
+ };
168
+ handleMediaDetached = () => {
169
+ this.updateMediaElementEventHandlers("unregister");
170
+ };
171
+ handlePlaybackUpdate = (t) => {
172
+ const e = t.target;
173
+ this.core.updatePlayback(e.currentTime, e.playbackRate);
174
+ };
175
+ destroyCore = () => this.core.destroy();
176
+ destroy = () => {
177
+ this.destroyCore(), this.updateHlsEventsHandlers("unregister"), this.updateMediaElementEventHandlers("unregister"), this.currentHlsInstance = void 0;
178
+ };
179
+ createFragmentLoaderClass() {
180
+ const { core: t } = this, e = this;
181
+ return class extends I {
182
+ constructor(s) {
183
+ super(s, t);
184
+ }
185
+ static getEngine() {
186
+ return e;
187
+ }
188
+ };
189
+ }
190
+ createPlaylistLoaderClass() {
191
+ const t = this;
192
+ return class extends S {
193
+ constructor(e) {
194
+ super(e), t.initHlsEvents();
195
+ }
196
+ };
197
+ }
198
+ }
199
+ export {
200
+ b as HlsJsP2PEngine
201
+ };
202
+ //# sourceMappingURL=p2p-media-loader-hlsjs.es.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"p2p-media-loader-hlsjs.es.min.js","sources":["../src/utils.ts","../src/fragment-loader.ts","../src/playlist-loader.ts","../src/segment-mananger.ts","../src/engine.ts","../src/engine-static.ts"],"sourcesContent":["import { ByteRange } from \"p2p-media-loader-core\";\n\nexport function getSegmentRuntimeId(\n segmentRequestUrl: string,\n byteRange?: ByteRange,\n) {\n if (!byteRange) return segmentRequestUrl;\n return `${segmentRequestUrl}|${byteRange.start}-${byteRange.end}`;\n}\n\nexport function getByteRange(\n rangeStart: number | undefined,\n rangeEnd: number | undefined,\n): ByteRange | undefined {\n if (\n rangeStart !== undefined &&\n rangeEnd !== undefined &&\n rangeStart <= rangeEnd\n ) {\n return { start: rangeStart, end: rangeEnd };\n }\n}\n","import type {\n FragmentLoaderContext,\n HlsConfig,\n Loader,\n LoaderCallbacks,\n LoaderConfiguration,\n LoaderContext,\n LoaderStats,\n} from \"hls.js\";\nimport * as Utils from \"./utils.js\";\nimport { Core, SegmentResponse, CoreRequestError } from \"p2p-media-loader-core\";\n\nconst DEFAULT_DOWNLOAD_LATENCY = 10;\n\nexport class FragmentLoaderBase implements Loader<FragmentLoaderContext> {\n context!: FragmentLoaderContext;\n config!: LoaderConfiguration | null;\n stats: LoaderStats;\n #callbacks!: LoaderCallbacks<FragmentLoaderContext> | null;\n #createDefaultLoader: () => Loader<LoaderContext>;\n #defaultLoader?: Loader<LoaderContext>;\n #core: Core;\n #response?: SegmentResponse;\n #segmentId?: string;\n\n constructor(config: HlsConfig, core: Core) {\n this.#core = core;\n this.#createDefaultLoader = () => new config.loader(config);\n this.stats = {\n aborted: false,\n chunkCount: 0,\n loading: { start: 0, first: 0, end: 0 },\n buffering: { start: 0, first: 0, end: 0 },\n parsing: { start: 0, end: 0 },\n // set total and loaded to 1 to prevent hls.js\n // on progress loading monitoring in AbrController\n total: 1,\n loaded: 1,\n bwEstimate: 0,\n retry: 0,\n };\n }\n\n load(\n context: FragmentLoaderContext,\n config: LoaderConfiguration,\n callbacks: LoaderCallbacks<LoaderContext>,\n ) {\n this.context = context;\n this.config = config;\n this.#callbacks = callbacks;\n const { stats } = this;\n\n const { rangeStart: start, rangeEnd: end } = context;\n const byteRange = Utils.getByteRange(\n start,\n end !== undefined ? end - 1 : undefined,\n );\n\n this.#segmentId = Utils.getSegmentRuntimeId(context.url, byteRange);\n const isSegmentDownloadableByP2PCore = this.#core.isSegmentLoadable(\n this.#segmentId,\n );\n\n if (\n !this.#core.hasSegment(this.#segmentId) ||\n !isSegmentDownloadableByP2PCore\n ) {\n this.#defaultLoader = this.#createDefaultLoader();\n this.#defaultLoader.stats = this.stats;\n this.#defaultLoader.load(context, config, callbacks);\n return;\n }\n\n const onSuccess = (response: SegmentResponse) => {\n this.#response = response;\n const loadedBytes = this.#response.data.byteLength;\n stats.loading = getLoadingStat(\n this.#response.bandwidth,\n loadedBytes,\n performance.now(),\n );\n stats.total = loadedBytes;\n stats.loaded = loadedBytes;\n\n if (callbacks.onProgress) {\n callbacks.onProgress(\n this.stats,\n context,\n this.#response.data,\n undefined,\n );\n }\n callbacks.onSuccess(\n { data: this.#response.data, url: context.url },\n this.stats,\n context,\n undefined,\n );\n };\n\n const onError = (error: unknown) => {\n if (\n error instanceof CoreRequestError &&\n error.type === \"aborted\" &&\n this.stats.aborted\n ) {\n return;\n }\n this.#handleError(error);\n };\n\n void this.#core.loadSegment(this.#segmentId, { onSuccess, onError });\n }\n\n #handleError(thrownError: unknown) {\n const error = { code: 0, text: \"\" };\n if (\n thrownError instanceof CoreRequestError &&\n thrownError.type === \"failed\"\n ) {\n // error.code = thrownError.code;\n error.text = thrownError.message;\n } else if (thrownError instanceof Error) {\n error.text = thrownError.message;\n }\n this.#callbacks?.onError(error, this.context, null, this.stats);\n }\n\n #abortInternal() {\n if (!this.#response && this.#segmentId) {\n this.stats.aborted = true;\n this.#core.abortSegmentLoading(this.#segmentId);\n }\n }\n\n abort() {\n if (this.#defaultLoader) {\n this.#defaultLoader.abort();\n } else {\n this.#abortInternal();\n this.#callbacks?.onAbort?.(this.stats, this.context, {});\n }\n }\n\n destroy() {\n if (this.#defaultLoader) {\n this.#defaultLoader.destroy();\n } else {\n if (!this.stats.aborted) this.#abortInternal();\n this.#callbacks = null;\n this.config = null;\n }\n }\n}\n\nfunction getLoadingStat(\n targetBitrate: number,\n loadedBytes: number,\n loadingEndTime: number,\n) {\n const timeForLoading = (loadedBytes * 8000) / targetBitrate;\n const first = loadingEndTime - timeForLoading;\n const start = first - DEFAULT_DOWNLOAD_LATENCY;\n\n return { start, first, end: loadingEndTime };\n}\n","import {\n HlsConfig,\n Loader,\n LoaderCallbacks,\n LoaderConfiguration,\n LoaderContext,\n LoaderStats,\n PlaylistLoaderContext,\n} from \"hls.js\";\n\nexport class PlaylistLoaderBase implements Loader<PlaylistLoaderContext> {\n #defaultLoader: Loader<LoaderContext>;\n context: PlaylistLoaderContext;\n stats: LoaderStats;\n\n constructor(config: HlsConfig) {\n this.#defaultLoader = new config.loader(config);\n this.stats = this.#defaultLoader.stats;\n this.context = this.#defaultLoader.context as PlaylistLoaderContext;\n }\n\n load(\n context: LoaderContext,\n config: LoaderConfiguration,\n callbacks: LoaderCallbacks<LoaderContext>,\n ) {\n this.#defaultLoader.load(context, config, callbacks);\n }\n\n abort() {\n this.#defaultLoader.abort();\n }\n\n destroy() {\n this.#defaultLoader.destroy();\n }\n}\n","import * as Utils from \"./utils.js\";\nimport type {\n ManifestLoadedData,\n LevelUpdatedData,\n AudioTrackLoadedData,\n} from \"hls.js\";\nimport { Core, Segment } from \"p2p-media-loader-core\";\n\nexport class SegmentManager {\n core: Core;\n\n constructor(core: Core) {\n this.core = core;\n }\n\n processMainManifest(data: ManifestLoadedData) {\n const { levels, audioTracks } = data;\n // in the case of audio only stream it is stored in levels\n\n for (const [index, level] of levels.entries()) {\n const { url } = level;\n this.core.addStreamIfNoneExists({\n runtimeId: Array.isArray(url) ? (url as string[])[0] : url,\n type: \"main\",\n index,\n });\n }\n\n for (const [index, track] of audioTracks.entries()) {\n const { url } = track;\n this.core.addStreamIfNoneExists({\n runtimeId: Array.isArray(url) ? (url as string[])[0] : url,\n type: \"secondary\",\n index,\n });\n }\n }\n\n updatePlaylist(data: LevelUpdatedData | AudioTrackLoadedData) {\n const {\n details: { url, fragments, live },\n } = data;\n\n const playlist = this.core.getStream(url);\n if (!playlist) return;\n\n const segmentToRemoveIds = new Set(playlist.segments.keys());\n const newSegments: Segment[] = [];\n fragments.forEach((fragment, index) => {\n const {\n url: responseUrl,\n byteRange: fragByteRange,\n sn,\n start: startTime,\n end: endTime,\n } = fragment;\n\n const [start, end] = fragByteRange;\n const byteRange = Utils.getByteRange(\n start,\n end !== undefined ? end - 1 : undefined,\n );\n const runtimeId = Utils.getSegmentRuntimeId(responseUrl, byteRange);\n segmentToRemoveIds.delete(runtimeId);\n\n if (playlist.segments.has(runtimeId)) return;\n newSegments.push({\n runtimeId,\n url: responseUrl,\n externalId: live ? sn : index,\n byteRange,\n startTime,\n endTime,\n });\n });\n\n if (!newSegments.length && !segmentToRemoveIds.size) return;\n this.core.updateStream(url, newSegments, segmentToRemoveIds.values());\n }\n}\n","import type Hls from \"hls.js\";\nimport type {\n AudioTrackLoadedData,\n LevelUpdatedData,\n ManifestLoadedData,\n LevelSwitchingData,\n PlaylistLevelType,\n HlsConfig,\n Events,\n} from \"hls.js\";\nimport { FragmentLoaderBase } from \"./fragment-loader.js\";\nimport { PlaylistLoaderBase } from \"./playlist-loader.js\";\nimport { SegmentManager } from \"./segment-mananger.js\";\nimport {\n CoreConfig,\n Core,\n CoreEventMap,\n DynamicCoreConfig,\n debug,\n DefinedCoreConfig,\n} from \"p2p-media-loader-core\";\nimport { injectMixin } from \"./engine-static.js\";\n\n/** Represents the complete configuration for HlsJsP2PEngine. */\nexport type HlsJsP2PEngineConfig = {\n /** Complete core configuration settings. */\n core: DefinedCoreConfig;\n};\n\n/** Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. */\nexport type PartialHlsJsP2PEngineConfig = Partial<\n Omit<HlsJsP2PEngineConfig, \"core\">\n> & {\n /** Partial core config */\n core?: Partial<CoreConfig>;\n};\n\n/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */\nexport type DynamicHlsJsP2PEngineConfig = {\n /** Dynamic core config */\n core?: DynamicCoreConfig;\n};\n\n/**\n * Extends a generic HLS type to include the P2P engine, integrating P2P capabilities directly into the HLS instance.\n * @template HlsType The base HLS type that is being extended.\n */\nexport type HlsWithP2PInstance<HlsType> = HlsType & {\n /** HlsJsP2PEngine instance */\n readonly p2pEngine: HlsJsP2PEngine;\n};\n\n/**\n * Configuration type for HLS instances that includes P2P settings, augmenting standard HLS configuration with P2P capabilities.\n * @template HlsType A constructor type that produces an HLS instance.\n */\nexport type HlsWithP2PConfig<HlsType extends abstract new () => unknown> =\n ConstructorParameters<HlsType>[0] & {\n p2p?: PartialHlsJsP2PEngineConfig & {\n onHlsJsCreated?: (hls: HlsWithP2PInstance<HlsType>) => void;\n };\n };\n\nconst MAX_LIVE_SYNC_DURATION = 120;\n\n/**\n * Represents a P2P (peer-to-peer) engine for HLS (HTTP Live Streaming) to enhance media streaming efficiency.\n * This class integrates P2P technologies into HLS.js, enabling the distribution of media segments via a peer network\n * alongside traditional HTTP fetching. It reduces server bandwidth costs and improves scalability by sharing the load\n * across multiple clients.\n *\n * The engine manages core functionalities such as segment fetching, segment management, peer connection management,\n * and event handling related to the P2P and HLS processes.\n *\n * @example\n * // Creating an instance of HlsJsP2PEngine with custom configuration\n * const hlsP2PEngine = new HlsJsP2PEngine({\n * core: {\n * highDemandTimeWindow: 30, // 30 seconds\n * simultaneousHttpDownloads: 3,\n * webRtcMaxMessageSize: 64 * 1024, // 64 KB\n * p2pNotReceivingBytesTimeoutMs: 10000, // 10 seconds\n * p2pInactiveLoaderDestroyTimeoutMs: 15000, // 15 seconds\n * httpNotReceivingBytesTimeoutMs: 8000, // 8 seconds\n * httpErrorRetries: 2,\n * p2pErrorRetries: 2,\n * announceTrackers: [\"wss://personal.tracker.com\"],\n * rtcConfig: {\n * iceServers: [{ urls: \"stun:personal.stun.com\" }]\n * },\n * swarmId: \"example-swarm-id\"\n * }\n * });\n *\n */\nexport class HlsJsP2PEngine {\n private readonly core: Core;\n private readonly segmentManager: SegmentManager;\n private hlsInstanceGetter?: () => Hls;\n private currentHlsInstance?: Hls;\n private readonly debug = debug(\"p2pml-hlsjs:engine\");\n\n /**\n * Enhances a given Hls.js class by injecting additional P2P (peer-to-peer) functionalities.\n *\n * @returns {HlsWithP2PInstance} - The enhanced Hls.js class with P2P functionalities.\n *\n * @example\n * const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hls);\n *\n * const hls = new HlsWithP2P({\n * // Hls.js configuration\n * startLevel: 0, // Example of Hls.js config parameter\n * p2p: {\n * core: {\n * // P2P core configuration\n * },\n * onHlsJsCreated(hls) {\n * // Do something with the Hls.js instance\n * },\n * },\n * });\n */\n static injectMixin(hls: typeof Hls) {\n return injectMixin(hls);\n }\n\n /**\n * Constructs an instance of HlsJsP2PEngine.\n * @param config Optional configuration for P2P engine setup.\n */\n constructor(config?: PartialHlsJsP2PEngineConfig) {\n this.core = new Core(config?.core);\n this.segmentManager = new SegmentManager(this.core);\n }\n\n /**\n * Adds an event listener for the specified event.\n * @param eventName The name of the event to listen for.\n * @param listener The callback function to be invoked when the event is triggered.\n *\n * @example\n * // Listening for a segment being successfully loaded\n * p2pEngine.addEventListener('onSegmentLoaded', (details) => {\n * console.log('Segment Loaded:', details);\n * });\n *\n * @example\n * // Handling segment load errors\n * p2pEngine.addEventListener('onSegmentError', (errorDetails) => {\n * console.error('Error loading segment:', errorDetails);\n * });\n *\n * @example\n * // Tracking data downloaded from peers\n * p2pEngine.addEventListener('onChunkDownloaded', (bytesLength, downloadSource, peerId) => {\n * console.log(`Downloaded ${bytesLength} bytes from ${downloadSource} ${peerId ? 'from peer ' + peerId : 'from server'}`);\n * });\n */\n addEventListener<K extends keyof CoreEventMap>(\n eventName: K,\n listener: CoreEventMap[K],\n ) {\n this.core.addEventListener(eventName, listener);\n }\n\n /**\n * Removes an event listener for the specified event.\n * @param eventName The name of the event.\n * @param listener The callback function that was previously added.\n */\n removeEventListener<K extends keyof CoreEventMap>(\n eventName: K,\n listener: CoreEventMap[K],\n ) {\n this.core.removeEventListener(eventName, listener);\n }\n\n /**\n * provides the Hls.js P2P specific configuration for Hls.js loaders.\n * @returns An object with fragment loader (fLoader) and playlist loader (pLoader).\n */\n getConfigForHlsJs(): { fLoader: unknown; pLoader: unknown } {\n return {\n fLoader: this.createFragmentLoaderClass(),\n pLoader: this.createPlaylistLoaderClass(),\n };\n }\n\n /**\n * Returns the configuration of the HLS.js P2P engine.\n * @returns A readonly version of the HlsJsP2PEngineConfig.\n */\n getConfig(): HlsJsP2PEngineConfig {\n return { core: this.core.getConfig() };\n }\n\n /**\n * Applies dynamic configuration updates to the P2P engine.\n * @param dynamicConfig Configuration changes to apply.\n *\n * @example\n * // Assuming `hlsP2PEngine` is an instance of HlsJsP2PEngine\n *\n * const newDynamicConfig = {\n * core: {\n * // Increase the number of cached segments to 1000\n * cachedSegmentsCount: 1000,\n * // 50 minutes of segments will be downloaded further through HTTP connections if P2P fails\n * httpDownloadTimeWindow: 3000,\n * // 100 minutes of segments will be downloaded further through P2P connections\n * p2pDownloadTimeWindow: 6000,\n * };\n *\n * hlsP2PEngine.applyDynamicConfig(newDynamicConfig);\n */\n applyDynamicConfig(dynamicConfig: DynamicHlsJsP2PEngineConfig) {\n if (dynamicConfig.core) this.core.applyDynamicConfig(dynamicConfig.core);\n }\n\n /**\n * Sets the HLS instance for handling media.\n * @param hls The HLS instance or a function that returns an HLS instance.\n */\n bindHls<T = unknown>(hls: T | (() => T)) {\n this.hlsInstanceGetter =\n typeof hls === \"function\" ? (hls as () => Hls) : () => hls as Hls;\n }\n\n private initHlsEvents() {\n const hlsInstance = this.hlsInstanceGetter?.();\n if (this.currentHlsInstance === hlsInstance) return;\n if (this.currentHlsInstance) this.destroy();\n this.currentHlsInstance = hlsInstance;\n this.updateHlsEventsHandlers(\"register\");\n this.updateMediaElementEventHandlers(\"register\");\n }\n\n private updateHlsEventsHandlers(type: \"register\" | \"unregister\") {\n const hls = this.currentHlsInstance;\n if (!hls) return;\n const method = type === \"register\" ? \"on\" : \"off\";\n\n hls[method](\n \"hlsManifestLoaded\" as Events.MANIFEST_LOADED,\n this.handleManifestLoaded,\n );\n hls[method](\n \"hlsLevelSwitching\" as Events.LEVEL_SWITCHING,\n this.handleLevelSwitching,\n );\n hls[method](\n \"hlsLevelUpdated\" as Events.LEVEL_UPDATED,\n this.handleLevelUpdated,\n );\n hls[method](\n \"hlsAudioTrackLoaded\" as Events.AUDIO_TRACK_LOADED,\n this.handleLevelUpdated,\n );\n hls[method](\"hlsDestroying\" as Events.DESTROYING, this.destroy);\n hls[method](\n \"hlsMediaAttaching\" as Events.MEDIA_ATTACHING,\n this.destroyCore,\n );\n hls[method](\n \"hlsManifestLoading\" as Events.MANIFEST_LOADING,\n this.destroyCore,\n );\n hls[method](\n \"hlsMediaDetached\" as Events.MEDIA_DETACHED,\n this.handleMediaDetached,\n );\n hls[method](\n \"hlsMediaAttached\" as Events.MEDIA_ATTACHED,\n this.handleMediaAttached,\n );\n }\n\n private updateMediaElementEventHandlers = (\n type: \"register\" | \"unregister\",\n ) => {\n const media = this.currentHlsInstance?.media;\n if (!media) return;\n const method =\n type === \"register\" ? \"addEventListener\" : \"removeEventListener\";\n media[method](\"timeupdate\", this.handlePlaybackUpdate);\n media[method](\"seeking\", this.handlePlaybackUpdate);\n media[method](\"ratechange\", this.handlePlaybackUpdate);\n };\n\n private handleManifestLoaded = (event: string, data: ManifestLoadedData) => {\n // eslint-disable-next-line prefer-destructuring\n const networkDetails: unknown = data.networkDetails;\n if (networkDetails instanceof XMLHttpRequest) {\n this.core.setManifestResponseUrl(networkDetails.responseURL);\n } else if (networkDetails instanceof Response) {\n this.core.setManifestResponseUrl(networkDetails.url);\n }\n this.segmentManager.processMainManifest(data);\n };\n\n private handleLevelSwitching = (event: string, data: LevelSwitchingData) => {\n if (data.bitrate) this.core.setActiveLevelBitrate(data.bitrate);\n };\n\n private handleLevelUpdated = (\n event: string,\n data: LevelUpdatedData | AudioTrackLoadedData,\n ) => {\n if (\n this.currentHlsInstance &&\n data.details.live &&\n data.details.fragments[0].type === (\"main\" as PlaylistLevelType) &&\n !this.currentHlsInstance.userConfig.liveSyncDuration &&\n !this.currentHlsInstance.userConfig.liveSyncDurationCount &&\n data.details.fragments.length > 4\n ) {\n this.updateLiveSyncDurationCount(data);\n }\n\n this.core.setIsLive(data.details.live);\n this.segmentManager.updatePlaylist(data);\n };\n\n private updateLiveSyncDurationCount(\n data: LevelUpdatedData | AudioTrackLoadedData,\n ) {\n const fragmentDuration = data.details.targetduration;\n\n const maxLiveSyncCount = Math.floor(\n MAX_LIVE_SYNC_DURATION / fragmentDuration,\n );\n const newLiveSyncDurationCount = Math.min(\n data.details.fragments.length - 1,\n maxLiveSyncCount,\n );\n\n if (\n this.currentHlsInstance &&\n this.currentHlsInstance.config.liveSyncDurationCount !==\n newLiveSyncDurationCount\n ) {\n this.debug(\n `Setting liveSyncDurationCount to ${newLiveSyncDurationCount}`,\n );\n this.currentHlsInstance.config.liveSyncDurationCount =\n newLiveSyncDurationCount;\n }\n }\n\n private handleMediaAttached = () => {\n this.updateMediaElementEventHandlers(\"register\");\n };\n\n private handleMediaDetached = () => {\n this.updateMediaElementEventHandlers(\"unregister\");\n };\n\n private handlePlaybackUpdate = (event: Event) => {\n const media = event.target as HTMLMediaElement;\n this.core.updatePlayback(media.currentTime, media.playbackRate);\n };\n\n private destroyCore = () => this.core.destroy();\n\n /** Clean up and release all resources. Unregister all event handlers. */\n destroy = () => {\n this.destroyCore();\n this.updateHlsEventsHandlers(\"unregister\");\n this.updateMediaElementEventHandlers(\"unregister\");\n this.currentHlsInstance = undefined;\n };\n\n private createFragmentLoaderClass() {\n const { core } = this;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const engine = this;\n\n return class FragmentLoader extends FragmentLoaderBase {\n constructor(config: HlsConfig) {\n super(config, core);\n }\n\n static getEngine() {\n return engine;\n }\n };\n }\n\n private createPlaylistLoaderClass() {\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const engine = this;\n return class PlaylistLoader extends PlaylistLoaderBase {\n constructor(config: HlsConfig) {\n super(config);\n engine.initHlsEvents();\n }\n };\n }\n}\n","import {\n HlsJsP2PEngine,\n PartialHlsJsP2PEngineConfig,\n HlsWithP2PInstance,\n HlsWithP2PConfig,\n} from \"./engine.js\";\n\nexport function injectMixin<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n HlsJsConstructor extends new (...args: any[]) => any,\n>(HlsJsClass: HlsJsConstructor) {\n return class HlsJsWithP2PClass extends HlsJsClass {\n #p2pEngine: HlsJsP2PEngine;\n\n get p2pEngine() {\n return this.#p2pEngine;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n constructor(...args: any[]) {\n const config = args[0] as\n | ({\n p2p?: PartialHlsJsP2PEngineConfig & {\n onHlsJsCreated?: (hls: InstanceType<HlsJsConstructor>) => void;\n };\n } & Record<string, unknown>)\n | undefined;\n\n const { p2p, ...hlsJsConfig } = config ?? {};\n\n const p2pEngine = new HlsJsP2PEngine(p2p);\n\n super({ ...hlsJsConfig, ...p2pEngine.getConfigForHlsJs() });\n\n p2pEngine.bindHls(this);\n\n this.#p2pEngine = p2pEngine;\n p2p?.onHlsJsCreated?.(this as InstanceType<HlsJsConstructor>);\n }\n } as new (\n config?: HlsWithP2PConfig<HlsJsConstructor>,\n ) => HlsWithP2PInstance<InstanceType<HlsJsConstructor>>;\n}\n"],"names":["getSegmentRuntimeId","segmentRequestUrl","byteRange","start","end","getByteRange","rangeStart","rangeEnd","FragmentLoaderBase","context","config","stats","callbacks","createDefaultLoader","defaultLoader","core","response","segmentId","this","loader","aborted","chunkCount","loading","first","buffering","parsing","total","loaded","bwEstimate","retry","Utils.getByteRange","Utils.getSegmentRuntimeId","url","isSegmentDownloadableByP2PCore","isSegmentLoadable","hasSegment","load","loadSegment","onSuccess","loadedBytes","data","byteLength","targetBitrate","loadingEndTime","timeForLoading","bandwidth","performance","now","onProgress","onError","error","CoreRequestError","type","#i","thrownError","code","text","Error","message","#o","abortInternal","abortSegmentLoading","abort","onAbort","destroy","PlaylistLoaderBase","SegmentManager","levels","audioTracks","index","level","entries","addStreamIfNoneExists","runtimeId","Array","isArray","track","details","fragments","live","playlist","getStream","segmentToRemoveIds","Set","segments","keys","newSegments","forEach","fragment","responseUrl","fragByteRange","sn","startTime","endTime","delete","has","push","externalId","length","size","updateStream","values","HlsJsP2PEngine","segmentManager","hlsInstanceGetter","currentHlsInstance","debug","hls","HlsJsClass","p2pEngine","args","p2p","hlsJsConfig","super","getConfigForHlsJs","bindHls","onHlsJsCreated","Core","eventName","listener","addEventListener","removeEventListener","fLoader","createFragmentLoaderClass","pLoader","createPlaylistLoaderClass","getConfig","dynamicConfig","applyDynamicConfig","initHlsEvents","hlsInstance","updateHlsEventsHandlers","updateMediaElementEventHandlers","method","handleManifestLoaded","handleLevelSwitching","handleLevelUpdated","destroyCore","handleMediaDetached","handleMediaAttached","media","handlePlaybackUpdate","event","networkDetails","XMLHttpRequest","setManifestResponseUrl","responseURL","Response","processMainManifest","bitrate","setActiveLevelBitrate","userConfig","liveSyncDuration","liveSyncDurationCount","updateLiveSyncDurationCount","setIsLive","updatePlaylist","fragmentDuration","targetduration","maxLiveSyncCount","Math","floor","newLiveSyncDurationCount","min","target","updatePlayback","currentTime","playbackRate","engine","getEngine"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { HlsWithP2PInstance, HlsWithP2PConfig } from "./engine.js";
2
+ export declare function injectMixin<HlsJsConstructor extends new (...args: any[]) => any>(HlsJsClass: HlsJsConstructor): new (config?: HlsWithP2PConfig<HlsJsConstructor>) => HlsWithP2PInstance<InstanceType<HlsJsConstructor>>;
@@ -0,0 +1,34 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ import { HlsJsP2PEngine, } from "./engine.js";
13
+ export function injectMixin(HlsJsClass) {
14
+ var _HlsJsWithP2PClass_p2pEngine, _a;
15
+ return _a = class HlsJsWithP2PClass extends HlsJsClass {
16
+ get p2pEngine() {
17
+ return __classPrivateFieldGet(this, _HlsJsWithP2PClass_p2pEngine, "f");
18
+ }
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ constructor(...args) {
21
+ const config = args[0];
22
+ const { p2p, ...hlsJsConfig } = config ?? {};
23
+ const p2pEngine = new HlsJsP2PEngine(p2p);
24
+ super({ ...hlsJsConfig, ...p2pEngine.getConfigForHlsJs() });
25
+ _HlsJsWithP2PClass_p2pEngine.set(this, void 0);
26
+ p2pEngine.bindHls(this);
27
+ __classPrivateFieldSet(this, _HlsJsWithP2PClass_p2pEngine, p2pEngine, "f");
28
+ p2p?.onHlsJsCreated?.(this);
29
+ }
30
+ },
31
+ _HlsJsWithP2PClass_p2pEngine = new WeakMap(),
32
+ _a;
33
+ }
34
+ //# sourceMappingURL=engine-static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine-static.js","sourceRoot":"","sources":["../src/engine-static.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,EACL,cAAc,GAIf,MAAM,aAAa,CAAC;AAErB,MAAM,UAAU,WAAW,CAGzB,UAA4B;;IAC5B,OAAO,KAAA,MAAM,iBAAkB,SAAQ,UAAU;YAG/C,IAAI,SAAS;gBACX,OAAO,uBAAA,IAAI,oCAAW,CAAC;YACzB,CAAC;YAED,8DAA8D;YAC9D,YAAY,GAAG,IAAW;gBACxB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAMR,CAAC;gBAEd,MAAM,EAAE,GAAG,EAAE,GAAG,WAAW,EAAE,GAAG,MAAM,IAAI,EAAE,CAAC;gBAE7C,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC;gBAE1C,KAAK,CAAC,EAAE,GAAG,WAAW,EAAE,GAAG,SAAS,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBApB9D,+CAA2B;gBAsBzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAExB,uBAAA,IAAI,gCAAc,SAAS,MAAA,CAAC;gBAC5B,GAAG,EAAE,cAAc,EAAE,CAAC,IAAsC,CAAC,CAAC;YAChE,CAAC;SACF;;UAEsD,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,189 @@
1
+ import type Hls from "hls.js";
2
+ import type { HlsConfig } from "hls.js";
3
+ import { CoreConfig, CoreEventMap, DynamicCoreConfig, DefinedCoreConfig } from "p2p-media-loader-core";
4
+ /** Represents the complete configuration for HlsJsP2PEngine. */
5
+ export type HlsJsP2PEngineConfig = {
6
+ /** Complete core configuration settings. */
7
+ core: DefinedCoreConfig;
8
+ };
9
+ /** Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. */
10
+ export type PartialHlsJsP2PEngineConfig = Partial<Omit<HlsJsP2PEngineConfig, "core">> & {
11
+ /** Partial core config */
12
+ core?: Partial<CoreConfig>;
13
+ };
14
+ /** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */
15
+ export type DynamicHlsJsP2PEngineConfig = {
16
+ /** Dynamic core config */
17
+ core?: DynamicCoreConfig;
18
+ };
19
+ /**
20
+ * Extends a generic HLS type to include the P2P engine, integrating P2P capabilities directly into the HLS instance.
21
+ * @template HlsType The base HLS type that is being extended.
22
+ */
23
+ export type HlsWithP2PInstance<HlsType> = HlsType & {
24
+ /** HlsJsP2PEngine instance */
25
+ readonly p2pEngine: HlsJsP2PEngine;
26
+ };
27
+ /**
28
+ * Configuration type for HLS instances that includes P2P settings, augmenting standard HLS configuration with P2P capabilities.
29
+ * @template HlsType A constructor type that produces an HLS instance.
30
+ */
31
+ export type HlsWithP2PConfig<HlsType extends abstract new () => unknown> = ConstructorParameters<HlsType>[0] & {
32
+ p2p?: PartialHlsJsP2PEngineConfig & {
33
+ onHlsJsCreated?: (hls: HlsWithP2PInstance<HlsType>) => void;
34
+ };
35
+ };
36
+ /**
37
+ * Represents a P2P (peer-to-peer) engine for HLS (HTTP Live Streaming) to enhance media streaming efficiency.
38
+ * This class integrates P2P technologies into HLS.js, enabling the distribution of media segments via a peer network
39
+ * alongside traditional HTTP fetching. It reduces server bandwidth costs and improves scalability by sharing the load
40
+ * across multiple clients.
41
+ *
42
+ * The engine manages core functionalities such as segment fetching, segment management, peer connection management,
43
+ * and event handling related to the P2P and HLS processes.
44
+ *
45
+ * @example
46
+ * // Creating an instance of HlsJsP2PEngine with custom configuration
47
+ * const hlsP2PEngine = new HlsJsP2PEngine({
48
+ * core: {
49
+ * highDemandTimeWindow: 30, // 30 seconds
50
+ * simultaneousHttpDownloads: 3,
51
+ * webRtcMaxMessageSize: 64 * 1024, // 64 KB
52
+ * p2pNotReceivingBytesTimeoutMs: 10000, // 10 seconds
53
+ * p2pInactiveLoaderDestroyTimeoutMs: 15000, // 15 seconds
54
+ * httpNotReceivingBytesTimeoutMs: 8000, // 8 seconds
55
+ * httpErrorRetries: 2,
56
+ * p2pErrorRetries: 2,
57
+ * announceTrackers: ["wss://personal.tracker.com"],
58
+ * rtcConfig: {
59
+ * iceServers: [{ urls: "stun:personal.stun.com" }]
60
+ * },
61
+ * swarmId: "example-swarm-id"
62
+ * }
63
+ * });
64
+ *
65
+ */
66
+ export declare class HlsJsP2PEngine {
67
+ private readonly core;
68
+ private readonly segmentManager;
69
+ private hlsInstanceGetter?;
70
+ private currentHlsInstance?;
71
+ private readonly debug;
72
+ /**
73
+ * Enhances a given Hls.js class by injecting additional P2P (peer-to-peer) functionalities.
74
+ *
75
+ * @returns {HlsWithP2PInstance} - The enhanced Hls.js class with P2P functionalities.
76
+ *
77
+ * @example
78
+ * const HlsWithP2P = HlsJsP2PEngine.injectMixin(Hls);
79
+ *
80
+ * const hls = new HlsWithP2P({
81
+ * // Hls.js configuration
82
+ * startLevel: 0, // Example of Hls.js config parameter
83
+ * p2p: {
84
+ * core: {
85
+ * // P2P core configuration
86
+ * },
87
+ * onHlsJsCreated(hls) {
88
+ * // Do something with the Hls.js instance
89
+ * },
90
+ * },
91
+ * });
92
+ */
93
+ static injectMixin(hls: typeof Hls): new (config?: (Partial<HlsConfig> & {
94
+ p2p?: (Partial<Omit<HlsJsP2PEngineConfig, "core">> & {
95
+ /** Partial core config */
96
+ core?: Partial<CoreConfig>;
97
+ } & {
98
+ onHlsJsCreated?: ((hls: HlsWithP2PInstance<typeof Hls>) => void) | undefined;
99
+ }) | undefined;
100
+ }) | undefined) => HlsWithP2PInstance<Hls>;
101
+ /**
102
+ * Constructs an instance of HlsJsP2PEngine.
103
+ * @param config Optional configuration for P2P engine setup.
104
+ */
105
+ constructor(config?: PartialHlsJsP2PEngineConfig);
106
+ /**
107
+ * Adds an event listener for the specified event.
108
+ * @param eventName The name of the event to listen for.
109
+ * @param listener The callback function to be invoked when the event is triggered.
110
+ *
111
+ * @example
112
+ * // Listening for a segment being successfully loaded
113
+ * p2pEngine.addEventListener('onSegmentLoaded', (details) => {
114
+ * console.log('Segment Loaded:', details);
115
+ * });
116
+ *
117
+ * @example
118
+ * // Handling segment load errors
119
+ * p2pEngine.addEventListener('onSegmentError', (errorDetails) => {
120
+ * console.error('Error loading segment:', errorDetails);
121
+ * });
122
+ *
123
+ * @example
124
+ * // Tracking data downloaded from peers
125
+ * p2pEngine.addEventListener('onChunkDownloaded', (bytesLength, downloadSource, peerId) => {
126
+ * console.log(`Downloaded ${bytesLength} bytes from ${downloadSource} ${peerId ? 'from peer ' + peerId : 'from server'}`);
127
+ * });
128
+ */
129
+ addEventListener<K extends keyof CoreEventMap>(eventName: K, listener: CoreEventMap[K]): void;
130
+ /**
131
+ * Removes an event listener for the specified event.
132
+ * @param eventName The name of the event.
133
+ * @param listener The callback function that was previously added.
134
+ */
135
+ removeEventListener<K extends keyof CoreEventMap>(eventName: K, listener: CoreEventMap[K]): void;
136
+ /**
137
+ * provides the Hls.js P2P specific configuration for Hls.js loaders.
138
+ * @returns An object with fragment loader (fLoader) and playlist loader (pLoader).
139
+ */
140
+ getConfigForHlsJs(): {
141
+ fLoader: unknown;
142
+ pLoader: unknown;
143
+ };
144
+ /**
145
+ * Returns the configuration of the HLS.js P2P engine.
146
+ * @returns A readonly version of the HlsJsP2PEngineConfig.
147
+ */
148
+ getConfig(): HlsJsP2PEngineConfig;
149
+ /**
150
+ * Applies dynamic configuration updates to the P2P engine.
151
+ * @param dynamicConfig Configuration changes to apply.
152
+ *
153
+ * @example
154
+ * // Assuming `hlsP2PEngine` is an instance of HlsJsP2PEngine
155
+ *
156
+ * const newDynamicConfig = {
157
+ * core: {
158
+ * // Increase the number of cached segments to 1000
159
+ * cachedSegmentsCount: 1000,
160
+ * // 50 minutes of segments will be downloaded further through HTTP connections if P2P fails
161
+ * httpDownloadTimeWindow: 3000,
162
+ * // 100 minutes of segments will be downloaded further through P2P connections
163
+ * p2pDownloadTimeWindow: 6000,
164
+ * };
165
+ *
166
+ * hlsP2PEngine.applyDynamicConfig(newDynamicConfig);
167
+ */
168
+ applyDynamicConfig(dynamicConfig: DynamicHlsJsP2PEngineConfig): void;
169
+ /**
170
+ * Sets the HLS instance for handling media.
171
+ * @param hls The HLS instance or a function that returns an HLS instance.
172
+ */
173
+ bindHls<T = unknown>(hls: T | (() => T)): void;
174
+ private initHlsEvents;
175
+ private updateHlsEventsHandlers;
176
+ private updateMediaElementEventHandlers;
177
+ private handleManifestLoaded;
178
+ private handleLevelSwitching;
179
+ private handleLevelUpdated;
180
+ private updateLiveSyncDurationCount;
181
+ private handleMediaAttached;
182
+ private handleMediaDetached;
183
+ private handlePlaybackUpdate;
184
+ private destroyCore;
185
+ /** Clean up and release all resources. Unregister all event handlers. */
186
+ destroy: () => void;
187
+ private createFragmentLoaderClass;
188
+ private createPlaylistLoaderClass;
189
+ }