From 60b6ecc1d1ee60e16dc5757d4c131ba42c0eefed Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 23 May 2024 16:35:30 +0300 Subject: [PATCH 01/16] improvements --- packages/p2p-media-loader-core/src/types.ts | 96 +++++++++++++++------ 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index b2932713..7a09de57 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -68,11 +68,22 @@ export type StreamWithSegments = TStream & { }; /** - * Represents a stream with a unique local identifier, type, and index position. + * Represents a media stream with various defining characteristics. */ export type Stream = { + /** + * A unique identifier for the stream within the local system. + */ readonly localId: string; + + /** + * The type of stream, either "main" or "secondary". + */ readonly type: StreamType; + + /** + * The index position of the stream within a collection or sequence. + */ readonly index: number; }; @@ -93,7 +104,8 @@ export type DynamicCoreConfig = Partial< * Configuration options for the Core functionality, including network and processing parameters. */ export type CoreConfig = { - /** Time window to consider for high demand scenarios, in seconds. + /** + * Time window to consider for high demand scenarios, in seconds. * * @default * ```typescript @@ -102,7 +114,8 @@ export type CoreConfig = { */ highDemandTimeWindow: number; - /** Time window for HTTP downloads, in seconds. + /** + * Time window for HTTP downloads, in seconds. * Specifies amount of segments to be downloaded in advance through HTTP. * * @default @@ -112,9 +125,10 @@ export type CoreConfig = { */ httpDownloadTimeWindow: number; - /** Time window for P2P downloads, in seconds. - * Specifies amount of segments to be downloaded in advance through P2P. - * Should be greater than httpDownloadTimeWindow. + /** + * Time window for P2P downloads, in seconds. + * Specifies amount of segments to be downloaded in advance through P2P. + * Should be greater than httpDownloadTimeWindow. * * @default * ```typescript @@ -123,7 +137,8 @@ export type CoreConfig = { */ p2pDownloadTimeWindow: number; - /** Maximum number of simultaneous HTTP downloads allowed. + /** + * Maximum number of simultaneous HTTP downloads allowed. * * @default * ```typescript @@ -132,7 +147,8 @@ export type CoreConfig = { */ simultaneousHttpDownloads: number; - /** Maximum number of simultaneous P2P downloads allowed. + /** + * Maximum number of simultaneous P2P downloads allowed. * * @default * ```typescript @@ -141,7 +157,8 @@ export type CoreConfig = { */ simultaneousP2PDownloads: number; - /** Time after which a cached segment expires, in milliseconds. + /** + * Time after which a cached segment expires, in milliseconds. * * @default * ```typescript @@ -150,8 +167,10 @@ export type CoreConfig = { */ cachedSegmentExpiration: number; - /** Maximum number of segments to store in the cache. - * Has to be less then httpDownloadTimeWindow and p2pDownloadTimeWindow. + /** + * Maximum number of segments to store in the cache. + * Has to be less then httpDownloadTimeWindow and p2pDownloadTimeWindow. + * * @default * ```typescript * cachedSegmentsCount: 50 @@ -159,7 +178,8 @@ export type CoreConfig = { */ cachedSegmentsCount: number; - /** Maximum message size for WebRTC communications, in bytes. + /** + * Maximum message size for WebRTC communications, in bytes. * * @default * ```typescript @@ -168,7 +188,8 @@ export type CoreConfig = { */ webRtcMaxMessageSize: number; - /** Timeout for not receiving bytes from P2P, in milliseconds. + /** + * Timeout for not receiving bytes from P2P, in milliseconds. * * @default * ```typescript @@ -177,7 +198,8 @@ export type CoreConfig = { */ p2pNotReceivingBytesTimeoutMs: number; - /** Timeout for destroying the P2P loader if inactive, in milliseconds. + /** + * Timeout for destroying the P2P loader if inactive, in milliseconds. * * @default * ```typescript @@ -186,7 +208,8 @@ export type CoreConfig = { */ p2pInactiveLoaderDestroyTimeoutMs: number; - /** Timeout for not receiving bytes from HTTP downloads, in milliseconds. + /** + * Timeout for not receiving bytes from HTTP downloads, in milliseconds. * * @default * ```typescript @@ -195,7 +218,8 @@ export type CoreConfig = { */ httpNotReceivingBytesTimeoutMs: number; - /** Number of retries allowed after an HTTP error. + /** + * Number of retries allowed after an HTTP error. * * @default * ```typescript @@ -247,7 +271,8 @@ export type CoreConfig = { */ rtcConfig: RTCConfiguration; - /** Prefix to use for the WebTorrent client version in tracker communications. + /** + * Prefix to use for the WebTorrent client version in tracker communications. * * @default * ```typescript @@ -288,7 +313,11 @@ export type CoreConfig = { }; /** - * Specifies the source of a download, indicating whether it was from HTTP or P2P. + * Specifies the source of a download within a media streaming context. + * + * "http" - Indicates that the segment was downloaded using the HTTP protocol. + * + * "p2p"- Indicates that the segment was downloaded through a peer-to-peer network. */ export type DownloadSource = "http" | "p2p"; @@ -331,22 +360,34 @@ export type SegmentAbortDetails = { }; /** - * Represents the details about a loaded segment, including the length in bytes and the source of the download. - * @param {number} bytesLength - The length of the segment in bytes. - * @param {DownloadSource} downloadSource - The source of the download. + * Represents the details about a loaded segment in media streaming. */ export type SegmentLoadDetails = { + /** + * The length of the segment in bytes. + */ bytesLength: number; + + /** + * The source of the download, indicating whether the segment was retrieved over HTTP or from a + * peer-to-peer network. + */ downloadSource: DownloadSource; + + /** + * The unique identifier of the peer from which this segment was downloaded, if applicable. This field + * is optional and may be undefined if the segment was not sourced from a peer. + */ peerId: string | undefined; }; /** * Represents the details of a peer in a peer-to-peer network. - * - * @param {string} peerId - The unique identifier for a peer in the network. */ export type PeerDetails = { + /** + * The unique identifier for a peer in the network. + */ peerId: string; }; @@ -474,10 +515,17 @@ export class RequestError< } /** - * Represents the response from a segment request, including the data and measured bandwidth. + * Represents the response from a segment request in media streaming. */ export type SegmentResponse = { + /** + * The raw binary data of the media segment. + */ data: ArrayBuffer; + + /** + * The measured bandwidth during the segment's download. + */ bandwidth: number; }; From 08ffb402a419f5f7a8a8ac7caf76679f796348a4 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Tue, 28 May 2024 17:02:13 +0300 Subject: [PATCH 02/16] Fix segment abort issue --- .../p2p-media-loader-core/src/p2p/loader.ts | 2 +- .../src/p2p/peer-protocol.ts | 21 ++++++++++++------- .../p2p-media-loader-core/src/p2p/peer.ts | 1 + 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/p2p-media-loader-core/src/p2p/loader.ts b/packages/p2p-media-loader-core/src/p2p/loader.ts index cfaa9468..9486e860 100644 --- a/packages/p2p-media-loader-core/src/p2p/loader.ts +++ b/packages/p2p-media-loader-core/src/p2p/loader.ts @@ -135,7 +135,7 @@ export class P2PLoader { peer.sendSegmentAbsentCommand(segmentExternalId); return; } - void peer.uploadSegmentData( + await peer.uploadSegmentData( segment, byteFrom !== undefined ? segmentData.slice(byteFrom) : segmentData, ); diff --git a/packages/p2p-media-loader-core/src/p2p/peer-protocol.ts b/packages/p2p-media-loader-core/src/p2p/peer-protocol.ts index f934bbc6..cdb123f5 100644 --- a/packages/p2p-media-loader-core/src/p2p/peer-protocol.ts +++ b/packages/p2p-media-loader-core/src/p2p/peer-protocol.ts @@ -67,23 +67,29 @@ export class PeerProtocol { const { promise, resolve, reject } = Utils.getControlledPromise(); let isUploadingSegmentData = false; - this.uploadingContext = { + + const uploadingContext = { stopUploading: () => { isUploadingSegmentData = false; }, }; + this.uploadingContext = uploadingContext; + const sendChunk = () => { + if (!isUploadingSegmentData) { + reject(); + return; + } + while (channel.bufferedAmount <= channel.bufferedAmountLowThreshold) { const chunk = chunks.next().value; + if (!chunk) { resolve(); break; } - if (chunk && !isUploadingSegmentData) { - reject(); - break; - } + this.connection.send(chunk); this.onChunkUploaded(chunk.byteLength, this.connection.idUtf8); } @@ -94,10 +100,11 @@ export class PeerProtocol { isUploadingSegmentData = true; sendChunk(); await promise; - return promise; } finally { channel.removeEventListener("bufferedamountlow", sendChunk); - this.uploadingContext = undefined; + if (this.uploadingContext === uploadingContext) { + this.uploadingContext = undefined; + } } } diff --git a/packages/p2p-media-loader-core/src/p2p/peer.ts b/packages/p2p-media-loader-core/src/p2p/peer.ts index c9920d47..22c85eaf 100644 --- a/packages/p2p-media-loader-core/src/p2p/peer.ts +++ b/packages/p2p-media-loader-core/src/p2p/peer.ts @@ -92,6 +92,7 @@ export class Peer { case PeerCommandType.SegmentData: { if (!this.downloadingContext) break; + if (this.downloadingContext.isSegmentDataCommandReceived) break; const { request, controls } = this.downloadingContext; if (request.segment.externalId !== command.i) break; From 798cd0b1458188027ee71c1ee143db959f10d3af Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 11:23:36 +0300 Subject: [PATCH 03/16] Refactor requestProcessQueueCallback --- packages/p2p-media-loader-core/src/hybrid-loader.ts | 1 + packages/p2p-media-loader-core/src/p2p/loader.ts | 2 ++ packages/p2p-media-loader-core/src/p2p/loaders-container.ts | 2 ++ packages/p2p-media-loader-core/src/p2p/peer.ts | 2 ++ packages/p2p-media-loader-core/src/p2p/tracker-client.ts | 2 ++ 5 files changed, 9 insertions(+) diff --git a/packages/p2p-media-loader-core/src/hybrid-loader.ts b/packages/p2p-media-loader-core/src/hybrid-loader.ts index fff2f9c9..d4e58c2d 100644 --- a/packages/p2p-media-loader-core/src/hybrid-loader.ts +++ b/packages/p2p-media-loader-core/src/hybrid-loader.ts @@ -77,6 +77,7 @@ export class HybridLoader { this.segmentStorage, this.config, this.eventTarget, + this.requestProcessQueueMicrotask, ); this.logger = debug(`p2pml-core:hybrid-loader-${activeStream.type}`); diff --git a/packages/p2p-media-loader-core/src/p2p/loader.ts b/packages/p2p-media-loader-core/src/p2p/loader.ts index 9486e860..7d93c6c3 100644 --- a/packages/p2p-media-loader-core/src/p2p/loader.ts +++ b/packages/p2p-media-loader-core/src/p2p/loader.ts @@ -22,6 +22,7 @@ export class P2PLoader { private readonly requests: RequestsContainer, private readonly segmentStorage: SegmentsMemoryStorage, private readonly config: CoreConfig, + private readonly requestProcessQueueCallback: () => void, eventTarget: EventTarget, ) { const streamExternalId = StreamUtils.getStreamExternalId( @@ -39,6 +40,7 @@ export class P2PLoader { }, this.config, eventTarget, + this.requestProcessQueueCallback, ); this.segmentStorage.subscribeOnUpdate( diff --git a/packages/p2p-media-loader-core/src/p2p/loaders-container.ts b/packages/p2p-media-loader-core/src/p2p/loaders-container.ts index fe3cd5d7..ed989204 100644 --- a/packages/p2p-media-loader-core/src/p2p/loaders-container.ts +++ b/packages/p2p-media-loader-core/src/p2p/loaders-container.ts @@ -25,6 +25,7 @@ export class P2PLoadersContainer { private readonly segmentStorage: SegmentsMemoryStorage, private readonly config: CoreConfig, private readonly eventTarget: EventTarget, + private requestProcessQueueCallback: () => void, ) { this.changeCurrentLoader(stream); } @@ -39,6 +40,7 @@ export class P2PLoadersContainer { this.requests, this.segmentStorage, this.config, + this.requestProcessQueueCallback, this.eventTarget, ); const loggerInfo = LoggerUtils.getStreamString(stream); diff --git a/packages/p2p-media-loader-core/src/p2p/peer.ts b/packages/p2p-media-loader-core/src/p2p/peer.ts index 22c85eaf..ac96a0a2 100644 --- a/packages/p2p-media-loader-core/src/p2p/peer.ts +++ b/packages/p2p-media-loader-core/src/p2p/peer.ts @@ -43,6 +43,7 @@ export class Peer { private readonly connection: PeerConnection, private readonly eventHandlers: PeerEventHandlers, private readonly peerConfig: PeerConfig, + private readonly requestProcessQueueCallback: () => void, eventTarget: EventTarget, ) { this.onPeerClosed = eventTarget.getEventDispatcher("onPeerClose"); @@ -82,6 +83,7 @@ export class Peer { case PeerCommandType.SegmentsAnnouncement: this.loadedSegments = new Set(command.l); this.httpLoadingSegments = new Set(command.p); + this.requestProcessQueueCallback(); break; case PeerCommandType.SegmentRequest: diff --git a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts index d228c0ca..9fbe3017 100644 --- a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts +++ b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts @@ -32,6 +32,7 @@ export class P2PTrackerClient { private readonly eventHandlers: P2PTrackerClientEventHandlers, private readonly config: CoreConfig, private readonly eventTarget: EventTarget, + private readonly requestProcessQueueCallback: () => void, ) { const streamHash = PeerUtil.getStreamHash(streamId); this.streamShortId = LoggerUtils.getStreamString(stream); @@ -97,6 +98,7 @@ export class P2PTrackerClient { onSegmentRequested: this.eventHandlers.onSegmentRequested, }, this.config, + this.requestProcessQueueCallback, this.eventTarget, ); this.logger( From e05e173b8e8073fa2758324dd8845e577a224fbb Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 11:23:50 +0300 Subject: [PATCH 04/16] Refactor segment event details types --- packages/p2p-media-loader-core/src/types.ts | 56 +++++++++++++++------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index eca026d0..9cacf46e 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -323,40 +323,68 @@ export type CoreConfig = { export type DownloadSource = "http" | "p2p"; /** - * Represents details about a segment event, including the segment itself, the source of download, and an optional peer ID. - * @param {Segment} segment - The segment that the event is about. - * @param {DownloadSource} downloadSource - The source of the download. - * @param {string | undefined} peerId - The peer ID of the peer that the event is about, if applicable. + * Represents details about a segment event. */ export type SegmentStartDetails = { + /** + * The media segment related to the event. + */ segment: Segment; + + /** + * The origin of the segment download, such as from a server or a peer. + */ downloadSource: DownloadSource; + + /** + * The peer ID associated with the segment event, if the segment was downloaded from a peer. Undefined if not applicable. + */ peerId: string | undefined; }; /** - * Represents details about a segment error event with an error property to provide details about a segment download error. - * @param {RequestError} error - The error that occurred during the segment download. - * @param {Segment} segment - The segment that the event is about. - * @param {DownloadSource} downloadSource - The source of the download. - * @param {string | undefined} peerId - The peer ID of the peer that the event is about, if applicable. + * Represents details about a segment error event, providing contextual information about the error during a segment download. */ export type SegmentErrorDetails = { + /** + * The error that occurred during the segment download. + */ error: RequestError; + + /** + * The media segment related to the event. + */ segment: Segment; + + /** + * The origin of the segment download, such as from a server or a peer. + */ downloadSource: DownloadSource; + + /** + * The peer ID of the peer that the event is about, if applicable. Undefined if not applicable. + */ peerId: string | undefined; }; /** - * Represents details about a segment abort event, including the segment, the source of download, and an optional peer ID. - * @param {Segment} segment - The segment that the event is about. - * @param {DownloadSource | undefined} downloadSource - The source of the download. - * @param {string | undefined} peerId - The peer ID of the peer that the event is about, if applicable. + * Represents details about a segment abort event. This includes information about the segment, the source from where it was being downloaded, and an optional peer ID if the download involved peer-to-peer transfer. */ export type SegmentAbortDetails = { + /** + * The media segment related to the abort event. + */ segment: Segment; + + /** + * The source of the download, if it was specified; otherwise, undefined. + * This can be from a server or a peer, depending on the scenario. + */ downloadSource: DownloadSource | undefined; + + /** + * The peer ID of the peer involved in the event, if applicable. Undefined if not relevant or known. + */ peerId: string | undefined; }; @@ -365,7 +393,7 @@ export type SegmentAbortDetails = { */ export type SegmentLoadDetails = { /** - * The length of the segment in bytes. + * The length of the segment in bytes. */ bytesLength: number; From 9c2a21b0b1130e2a3dd3b31c8d0a0dabab21d423 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 12:28:27 +0300 Subject: [PATCH 05/16] Small improvements --- packages/p2p-media-loader-core/src/p2p/loader.ts | 2 +- packages/p2p-media-loader-core/src/p2p/peer.ts | 4 ++-- packages/p2p-media-loader-core/src/p2p/tracker-client.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/p2p-media-loader-core/src/p2p/loader.ts b/packages/p2p-media-loader-core/src/p2p/loader.ts index 7d93c6c3..7e974d65 100644 --- a/packages/p2p-media-loader-core/src/p2p/loader.ts +++ b/packages/p2p-media-loader-core/src/p2p/loader.ts @@ -37,10 +37,10 @@ export class P2PLoader { onPeerConnected: this.onPeerConnected, // eslint-disable-next-line @typescript-eslint/no-misused-promises onSegmentRequested: this.onSegmentRequested, + onSegmentsAnnouncement: this.requestProcessQueueCallback, }, this.config, eventTarget, - this.requestProcessQueueCallback, ); this.segmentStorage.subscribeOnUpdate( diff --git a/packages/p2p-media-loader-core/src/p2p/peer.ts b/packages/p2p-media-loader-core/src/p2p/peer.ts index ac96a0a2..8a2078e2 100644 --- a/packages/p2p-media-loader-core/src/p2p/peer.ts +++ b/packages/p2p-media-loader-core/src/p2p/peer.ts @@ -21,6 +21,7 @@ type PeerEventHandlers = { segmentId: number, byteFrom?: number, ) => void; + onSegmentsAnnouncement: () => void; }; export class Peer { @@ -43,7 +44,6 @@ export class Peer { private readonly connection: PeerConnection, private readonly eventHandlers: PeerEventHandlers, private readonly peerConfig: PeerConfig, - private readonly requestProcessQueueCallback: () => void, eventTarget: EventTarget, ) { this.onPeerClosed = eventTarget.getEventDispatcher("onPeerClose"); @@ -83,7 +83,7 @@ export class Peer { case PeerCommandType.SegmentsAnnouncement: this.loadedSegments = new Set(command.l); this.httpLoadingSegments = new Set(command.p); - this.requestProcessQueueCallback(); + this.eventHandlers.onSegmentsAnnouncement(); break; case PeerCommandType.SegmentRequest: diff --git a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts index 9fbe3017..44c09a1a 100644 --- a/packages/p2p-media-loader-core/src/p2p/tracker-client.ts +++ b/packages/p2p-media-loader-core/src/p2p/tracker-client.ts @@ -18,6 +18,7 @@ type PeerItem = { type P2PTrackerClientEventHandlers = { onPeerConnected: (peer: Peer) => void; onSegmentRequested: (peer: Peer, segmentExternalId: number) => void; + onSegmentsAnnouncement: () => void; }; export class P2PTrackerClient { @@ -32,7 +33,6 @@ export class P2PTrackerClient { private readonly eventHandlers: P2PTrackerClientEventHandlers, private readonly config: CoreConfig, private readonly eventTarget: EventTarget, - private readonly requestProcessQueueCallback: () => void, ) { const streamHash = PeerUtil.getStreamHash(streamId); this.streamShortId = LoggerUtils.getStreamString(stream); @@ -96,9 +96,9 @@ export class P2PTrackerClient { { onPeerClosed: this.onPeerClosed, onSegmentRequested: this.eventHandlers.onSegmentRequested, + onSegmentsAnnouncement: this.eventHandlers.onSegmentsAnnouncement, }, this.config, - this.requestProcessQueueCallback, this.eventTarget, ); this.logger( From f63b086bb8b972eb287f1a1e23c637644519e9f1 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 14:12:53 +0300 Subject: [PATCH 06/16] Fixed onSegmentAnnouncement --- packages/p2p-media-loader-core/src/hybrid-loader.ts | 4 +++- packages/p2p-media-loader-core/src/p2p/loader.ts | 8 ++++---- .../p2p-media-loader-core/src/p2p/loaders-container.ts | 8 ++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/p2p-media-loader-core/src/hybrid-loader.ts b/packages/p2p-media-loader-core/src/hybrid-loader.ts index d4e58c2d..08a62c34 100644 --- a/packages/p2p-media-loader-core/src/hybrid-loader.ts +++ b/packages/p2p-media-loader-core/src/hybrid-loader.ts @@ -77,7 +77,9 @@ export class HybridLoader { this.segmentStorage, this.config, this.eventTarget, - this.requestProcessQueueMicrotask, + () => { + this.requestProcessQueueMicrotask(); + }, ); this.logger = debug(`p2pml-core:hybrid-loader-${activeStream.type}`); diff --git a/packages/p2p-media-loader-core/src/p2p/loader.ts b/packages/p2p-media-loader-core/src/p2p/loader.ts index 7e974d65..a6236d87 100644 --- a/packages/p2p-media-loader-core/src/p2p/loader.ts +++ b/packages/p2p-media-loader-core/src/p2p/loader.ts @@ -22,8 +22,8 @@ export class P2PLoader { private readonly requests: RequestsContainer, private readonly segmentStorage: SegmentsMemoryStorage, private readonly config: CoreConfig, - private readonly requestProcessQueueCallback: () => void, - eventTarget: EventTarget, + private readonly eventTarget: EventTarget, + private readonly onSegmentAnnouncement: () => void, ) { const streamExternalId = StreamUtils.getStreamExternalId( this.config.swarmId ?? this.streamManifestUrl, @@ -37,10 +37,10 @@ export class P2PLoader { onPeerConnected: this.onPeerConnected, // eslint-disable-next-line @typescript-eslint/no-misused-promises onSegmentRequested: this.onSegmentRequested, - onSegmentsAnnouncement: this.requestProcessQueueCallback, + onSegmentsAnnouncement: this.onSegmentAnnouncement, }, this.config, - eventTarget, + this.eventTarget, ); this.segmentStorage.subscribeOnUpdate( diff --git a/packages/p2p-media-loader-core/src/p2p/loaders-container.ts b/packages/p2p-media-loader-core/src/p2p/loaders-container.ts index ed989204..968d18a7 100644 --- a/packages/p2p-media-loader-core/src/p2p/loaders-container.ts +++ b/packages/p2p-media-loader-core/src/p2p/loaders-container.ts @@ -25,7 +25,7 @@ export class P2PLoadersContainer { private readonly segmentStorage: SegmentsMemoryStorage, private readonly config: CoreConfig, private readonly eventTarget: EventTarget, - private requestProcessQueueCallback: () => void, + private onSegmentAnnouncement: () => void, ) { this.changeCurrentLoader(stream); } @@ -40,8 +40,12 @@ export class P2PLoadersContainer { this.requests, this.segmentStorage, this.config, - this.requestProcessQueueCallback, this.eventTarget, + () => { + if (this._currentLoaderItem.loader === loader) { + this.onSegmentAnnouncement(); + } + }, ); const loggerInfo = LoggerUtils.getStreamString(stream); this.logger(`created new loader: ${loggerInfo}`); From d78144e027d890bad902039cd3286fe88fe5b90e Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 15:26:45 +0300 Subject: [PATCH 07/16] Refactor core types docs --- packages/p2p-media-loader-core/src/types.ts | 241 +++++++------------- 1 file changed, 78 insertions(+), 163 deletions(-) diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index 9cacf46e..c14460a7 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -1,60 +1,36 @@ -/** - * Represents the types of streams available, either primary (main) or secondary. - */ +/** Represents the types of streams available, either primary (main) or secondary. */ export type StreamType = "main" | "secondary"; -/** - * Represents a range of bytes, used for specifying a segment of data to download. - */ +/** Represents a range of bytes, used for specifying a segment of data to download. */ export type ByteRange = { - /** - * The starting byte index of the range. - */ + /** The starting byte index of the range. */ start: number; - /** - * The ending byte index of the range. - */ + /** The ending byte index of the range. */ end: number; }; -/** - * Describes a media segment with its unique identifiers, location, and timing information. - */ +/** Describes a media segment with its unique identifiers, location, and timing information. */ export type Segment = { - /** - * A unique identifier for the segment within the local system. - */ + /** A unique identifier for the segment within the local system. */ readonly localId: string; - /** - * A unique identifier for the segment as recognized by external systems or servers. - */ + /** A unique identifier for the segment as recognized by external systems or servers. */ readonly externalId: number; - /** - * The URL from which the segment can be downloaded. - */ + /** The URL from which the segment can be downloaded. */ readonly url: string; - /** - * An optional property specifying the range of bytes that represent the segment. - */ + /** An optional property specifying the range of bytes that represent the segment. */ readonly byteRange?: ByteRange; - /** - * The start time of the segment in seconds, relative to the beginning of the stream. - */ + /** The start time of the segment in seconds, relative to the beginning of the stream. */ readonly startTime: number; - /** - * The end time of the segment in seconds, relative to the beginning of the stream. - */ + /** The end time of the segment in seconds, relative to the beginning of the stream. */ readonly endTime: number; }; -/** - * Extends a Segment with a reference to its associated stream. - */ +/** Extends a Segment with a reference to its associated stream. */ export type SegmentWithStream = Segment & { readonly stream: StreamWithSegments; }; @@ -67,29 +43,19 @@ export type StreamWithSegments = TStream & { readonly segments: Map>; }; -/** - * Represents a media stream with various defining characteristics. - */ +/** Represents a media stream with various defining characteristics. */ export type Stream = { - /** - * A unique identifier for the stream within the local system. - */ + /** Local ID of the stream: a string that consists of swarm ID, stream type, stream index. */ readonly localId: string; - /** - * The type of stream, either "main" or "secondary". - */ + /** Stream type. */ readonly type: StreamType; - /** - * The index position of the stream within a collection or sequence. - */ + /** Stream index in the manifest. */ readonly index: number; }; -/** - * Defines a subset of CoreConfig for dynamic updates, allowing selective modification of configuration properties. - */ +/** Defines a subset of CoreConfig for dynamic updates, allowing selective modification of configuration properties. */ export type DynamicCoreConfig = Partial< Pick< CoreConfig, @@ -101,12 +67,11 @@ export type DynamicCoreConfig = Partial< > >; -/** - * Configuration options for the Core functionality, including network and processing parameters. - */ +/** Configuration options for the Core functionality, including network and processing parameters. */ export type CoreConfig = { /** - * Time window to consider for high demand scenarios, in seconds. + * Defines the duration of the time window, in seconds, during which segments are pre-loaded to ensure smooth playback. + * This window helps prioritize the fetching of media segments that are imminent to playback. * * @default * ```typescript @@ -116,24 +81,30 @@ export type CoreConfig = { highDemandTimeWindow: number; /** - * Time window for HTTP downloads, in seconds. - * Specifies amount of segments to be downloaded in advance through HTTP. + * Defines the time window, in seconds, for HTTP segment downloads. This property specifies the duration + * over which media segments are pre-fetched using HTTP requests. + * + * For a better P2P ratio, it is recommended to set this `httpDownloadTimeWindow` to be lower than `p2pDownloadTimeWindow`. + * + * NOTE: This setting only takes effect if there is at least one peer connection and the connected peer + * does not have the requested segments available to share via P2P. * * @default * ```typescript - * httpDownloadTimeWindowMs: 45 + * httpDownloadTimeWindow: 3000 * ``` */ httpDownloadTimeWindow: number; /** - * Time window for P2P downloads, in seconds. - * Specifies amount of segments to be downloaded in advance through P2P. - * Should be greater than httpDownloadTimeWindow. + * Defines the time window, in seconds, dedicated to pre-fetching media segments via Peer-to-Peer (P2P) downloads. + * This duration determines how much content is downloaded in advance using P2P connections to ensure smooth playback and reduce reliance on HTTP downloads. + * + * For a better P2P ratio, it is recommended to set this time window to be greater than `httpDownloadTimeWindow` to maximize P2P usage. * * @default * ```typescript - * p2pDownloadTimeWindow: 45 + * p2pDownloadTimeWindow: 6000 * ``` */ p2pDownloadTimeWindow: number; @@ -174,7 +145,7 @@ export type CoreConfig = { * * @default * ```typescript - * cachedSegmentsCount: 50 + * cachedSegmentsCount: 1000 * ``` */ cachedSegmentsCount: number; @@ -229,7 +200,8 @@ export type CoreConfig = { */ httpErrorRetries: number; - /** Number of retries allowed after a P2P error. + /** + * Number of retries allowed after a P2P error. * * @default * ```typescript @@ -282,7 +254,8 @@ export type CoreConfig = { */ trackerClientVersionPrefix: string; - /** Optional unique identifier for the swarm, used to isolate peer pools by media stream. + /** + * Optional unique identifier for the swarm, used to isolate peer pools by media stream. * * @default * The master URL of the manifest is used as the swarmId. @@ -322,101 +295,60 @@ export type CoreConfig = { */ export type DownloadSource = "http" | "p2p"; -/** - * Represents details about a segment event. - */ +/** Represents details about a segment event. */ export type SegmentStartDetails = { - /** - * The media segment related to the event. - */ + /** The segment that the event is about. */ segment: Segment; - /** - * The origin of the segment download, such as from a server or a peer. - */ + /** The origin of the segment download. */ downloadSource: DownloadSource; - /** - * The peer ID associated with the segment event, if the segment was downloaded from a peer. Undefined if not applicable. - */ + /** The peer ID, if the segment is downloaded from a peer. */ peerId: string | undefined; }; -/** - * Represents details about a segment error event, providing contextual information about the error during a segment download. - */ +/** Represents details about a segment error event. */ export type SegmentErrorDetails = { - /** - * The error that occurred during the segment download. - */ + /** The error that occurred during the segment download. */ error: RequestError; - /** - * The media segment related to the event. - */ + /** The segment that the event is about. */ segment: Segment; - /** - * The origin of the segment download, such as from a server or a peer. - */ + /** The source of the download. */ downloadSource: DownloadSource; - /** - * The peer ID of the peer that the event is about, if applicable. Undefined if not applicable. - */ + /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; }; -/** - * Represents details about a segment abort event. This includes information about the segment, the source from where it was being downloaded, and an optional peer ID if the download involved peer-to-peer transfer. - */ +/** Represents details about a segment abort event. */ export type SegmentAbortDetails = { - /** - * The media segment related to the abort event. - */ + /** The segment that the event is about. */ segment: Segment; - /** - * The source of the download, if it was specified; otherwise, undefined. - * This can be from a server or a peer, depending on the scenario. - */ + /** The source of the download. */ downloadSource: DownloadSource | undefined; - /** - * The peer ID of the peer involved in the event, if applicable. Undefined if not relevant or known. - */ + /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; }; -/** - * Represents the details about a loaded segment in media streaming. - */ +/** Represents the details about a loaded segment. */ export type SegmentLoadDetails = { - /** - * The length of the segment in bytes. - */ + /** The length of the segment in bytes. */ bytesLength: number; - /** - * The source of the download, indicating whether the segment was retrieved over HTTP or from a - * peer-to-peer network. - */ + /** The source of the download. */ downloadSource: DownloadSource; - /** - * The unique identifier of the peer from which this segment was downloaded, if applicable. This field - * is optional and may be undefined if the segment was not sourced from a peer. - */ + /** The peer ID, if the segment was downloaded from a peer. */ peerId: string | undefined; }; -/** - * Represents the details of a peer in a peer-to-peer network. - */ +/** Represents the details of a peer in a peer-to-peer network. */ export type PeerDetails = { - /** - * The unique identifier for a peer in the network. - */ + /** The unique identifier for a peer in the network. */ peerId: string; }; @@ -428,51 +360,51 @@ export type CoreEventMap = { /** * Invoked when a segment is fully downloaded and available for use. * - * @param {SegmentLoadDetails} params - Contains information about the loaded segment. + * @param params - Contains information about the loaded segment. */ onSegmentLoaded: (params: SegmentLoadDetails) => void; /** * Triggered when an error occurs during the download of a segment. * - * @param {SegmentErrorDetails} params - Contains information about the errored segment. + * @param params - Contains information about the errored segment. */ onSegmentError: (params: SegmentErrorDetails) => void; /** * Called if the download of a segment is aborted before completion. * - * @param {SegmentAbortDetails} params - Contains information about the aborted segment. + * @param params - Contains information about the aborted segment. */ onSegmentAbort: (params: SegmentAbortDetails) => void; /** * Fired at the beginning of a segment download process. * - * @param {SegmentStartDetails} params - Provides details about the segment being downloaded. + * @param params - Provides details about the segment being downloaded. */ onSegmentStart: (params: SegmentStartDetails) => void; /** * Occurs when a new peer-to-peer connection is established. * - * @param {string} peerId - The unique identifier of the peer that has just connected. + * @param peerId - The unique identifier of the peer that has just connected. */ onPeerConnect: (params: PeerDetails) => void; /** * Triggered when an existing peer-to-peer connection is closed. * - * @param {string} peerId - The unique identifier of the peer whose connection has been closed. + * @param peerId - The unique identifier of the peer whose connection has been closed. */ onPeerClose: (params: PeerDetails) => void; /** * Invoked after a chunk of data from a segment has been successfully downloaded. * - * @param {number} bytesLength - The size of the downloaded chunk in bytes, offering a measure of the download progress. - * @param {DownloadSource} type - The source of the download. - * @param {string} [peerId] - The peer ID of the peer that the event is about, if applicable. + * @param bytesLength - The size of the downloaded chunk in bytes. + * @param type - The source of the download. + * @param peerId - The peer ID of the peer that the event is about, if applicable. */ onChunkDownloaded: ( bytesLength: number, @@ -483,28 +415,22 @@ export type CoreEventMap = { /** * Called when a chunk of data has been successfully uploaded to a peer. * - * @param {number} bytesLength - The length of the segment in bytes. - * @param {string} peerId - The peer ID of the peer that the event is about, if applicable. + * @param bytesLength - The length of the segment in bytes. + * @param peerId - The peer ID, if the segment was downloaded from a peer */ onChunkUploaded: (bytesLength: number, peerId: string) => void; }; -/** - * Defines the types of errors that can occur during a request abortion process. - */ +/** Defines the types of errors that can occur during a request abortion process. */ export type RequestAbortErrorType = "abort" | "bytes-receiving-timeout"; -/** - * Defines the types of errors specific to HTTP requests. - */ +/** Defines the types of errors specific to HTTP requests. */ export type HttpRequestErrorType = | "http-error" | "http-bytes-mismatch" | "http-unexpected-status-code"; -/** - * Defines the types of errors specific to peer-to-peer requests. - */ +/** Defines the types of errors specific to peer-to-peer requests. */ export type PeerRequestErrorType = | "peer-response-bytes-length-mismatch" | "peer-protocol-violation" @@ -512,9 +438,7 @@ export type PeerRequestErrorType = | "peer-closed" | "p2p-segment-validation-failed"; -/** - * Enumerates all possible request error types, including HTTP and peer-related errors. - */ +/** Enumerates all possible request error types, including HTTP and peer-related errors. */ export type RequestErrorType = | RequestAbortErrorType | PeerRequestErrorType @@ -527,6 +451,7 @@ export type RequestErrorType = export class RequestError< T extends RequestErrorType = RequestErrorType, > extends Error { + /** Error timestamp. */ readonly timestamp: number; /** @@ -543,24 +468,16 @@ export class RequestError< } } -/** - * Represents the response from a segment request in media streaming. - */ +/** Represents the response from a segment request, including the data and measured bandwidth. */ export type SegmentResponse = { - /** - * The raw binary data of the media segment. - */ + /** Segment data as an ArrayBuffer. */ data: ArrayBuffer; - /** - * The measured bandwidth during the segment's download. - */ + /** Measured bandwidth for the segment download, in bytes per second. */ bandwidth: number; }; -/** - * Custom error class for errors that occur during core network requests. - */ +/** Custom error class for errors that occur during core network requests. */ export class CoreRequestError extends Error { /** * Constructs a new CoreRequestError. @@ -571,9 +488,7 @@ export class CoreRequestError extends Error { } } -/** - * Callbacks for handling the success or failure of an engine operation. - */ +/** Callbacks for handling the success or failure of an engine operation. */ export type EngineCallbacks = { /** * Called when the operation is successful. From e7cda7c35b28a18362dba2658fcfb7f3b77ae73c Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 15:40:42 +0300 Subject: [PATCH 08/16] Refactor engine types --- packages/p2p-media-loader-hlsjs/src/engine.ts | 20 +++++++----------- packages/p2p-media-loader-shaka/src/engine.ts | 21 +++++++------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/p2p-media-loader-hlsjs/src/engine.ts b/packages/p2p-media-loader-hlsjs/src/engine.ts index 33f4efce..64c85a5e 100644 --- a/packages/p2p-media-loader-hlsjs/src/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/engine.ts @@ -19,26 +19,23 @@ import { } from "p2p-media-loader-core"; import { injectMixin } from "./engine-static"; -/** - * Represents the complete configuration for HlsJsP2PEngine. - */ +/** Represents the complete configuration for HlsJsP2PEngine. */ export type HlsJsP2PEngineConfig = { + /** Core configuration */ core: CoreConfig; }; -/** - * Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. - */ +/** Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. */ export type PartialHlsJsP2PEngineConfig = Partial< Omit > & { + /** Partial core configuration */ core?: Partial; }; -/** - * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. - */ +/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ export type DynamicHlsJsP2PEngineConfig = { + /** Dynamic core configuration */ core?: DynamicCoreConfig; }; @@ -47,6 +44,7 @@ export type DynamicHlsJsP2PEngineConfig = { * @template HlsType The base HLS type that is being extended. */ export type HlsWithP2PInstance = HlsType & { + /** HlsJsP2PEngine instance */ readonly p2pEngine: HlsJsP2PEngine; }; @@ -339,9 +337,7 @@ export class HlsJsP2PEngine { private destroyCore = () => this.core.destroy(); - /** - * Clean up and release all resources. Unregisters all event handlers. - */ + /** Clean up and release all resources. Unregisters all event handlers. */ destroy = () => { this.destroyCore(); this.updateHlsEventsHandlers("unregister"); diff --git a/packages/p2p-media-loader-shaka/src/engine.ts b/packages/p2p-media-loader-shaka/src/engine.ts index 4647c614..381796aa 100644 --- a/packages/p2p-media-loader-shaka/src/engine.ts +++ b/packages/p2p-media-loader-shaka/src/engine.ts @@ -20,26 +20,23 @@ import { DynamicCoreConfig, } from "p2p-media-loader-core"; -/** - * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. - */ +/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ export type DynamicShakaP2PEngineConfig = { + /** Dynamic core configuration */ core?: DynamicCoreConfig; }; -/** - * Represents the complete configuration for ShakaP2PEngine. - */ +/** Represents the complete configuration for ShakaP2PEngine. */ export type ShakaP2PEngineConfig = { + /** Core configuration */ core: CoreConfig; }; -/** - * Allows for partial configuration settings for the Shaka P2P Engine. - */ +/** Allows for partial configuration settings for the Shaka P2P Engine. */ export type PartialShakaEngineConfig = Partial< Omit > & { + /** Partial core configuration */ core?: Partial; }; @@ -272,9 +269,7 @@ export class ShakaP2PEngine { this.core.updatePlayback(media.currentTime, media.playbackRate); }; - /** - * Clean up and release all resources. Unregisters all event handlers. - */ + /** Clean up and release all resources. Unregister all event handlers. */ destroy() { this.destroyCurrentStreamContext(); this.updatePlayerEventHandlers("unregister"); @@ -343,7 +338,7 @@ export class ShakaP2PEngine { } /** - * Unregisters plugins related to P2P functionality from the Shaka Player. + * Unregister plugins related to P2P functionality from the Shaka Player. * * @param {Shaka} [shaka=window.shaka] - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. */ From 015bab94590480c89cac3a6c65468232e3ef7777 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 15:49:22 +0300 Subject: [PATCH 09/16] Revert "Refactor engine types" This reverts commit e7cda7c35b28a18362dba2658fcfb7f3b77ae73c. --- packages/p2p-media-loader-hlsjs/src/engine.ts | 20 +++++++++++------- packages/p2p-media-loader-shaka/src/engine.ts | 21 ++++++++++++------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/p2p-media-loader-hlsjs/src/engine.ts b/packages/p2p-media-loader-hlsjs/src/engine.ts index 64c85a5e..33f4efce 100644 --- a/packages/p2p-media-loader-hlsjs/src/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/engine.ts @@ -19,23 +19,26 @@ import { } from "p2p-media-loader-core"; import { injectMixin } from "./engine-static"; -/** Represents the complete configuration for HlsJsP2PEngine. */ +/** + * Represents the complete configuration for HlsJsP2PEngine. + */ export type HlsJsP2PEngineConfig = { - /** Core configuration */ core: CoreConfig; }; -/** Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. */ +/** + * Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. + */ export type PartialHlsJsP2PEngineConfig = Partial< Omit > & { - /** Partial core configuration */ core?: Partial; }; -/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ +/** + * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. + */ export type DynamicHlsJsP2PEngineConfig = { - /** Dynamic core configuration */ core?: DynamicCoreConfig; }; @@ -44,7 +47,6 @@ export type DynamicHlsJsP2PEngineConfig = { * @template HlsType The base HLS type that is being extended. */ export type HlsWithP2PInstance = HlsType & { - /** HlsJsP2PEngine instance */ readonly p2pEngine: HlsJsP2PEngine; }; @@ -337,7 +339,9 @@ export class HlsJsP2PEngine { private destroyCore = () => this.core.destroy(); - /** Clean up and release all resources. Unregisters all event handlers. */ + /** + * Clean up and release all resources. Unregisters all event handlers. + */ destroy = () => { this.destroyCore(); this.updateHlsEventsHandlers("unregister"); diff --git a/packages/p2p-media-loader-shaka/src/engine.ts b/packages/p2p-media-loader-shaka/src/engine.ts index 381796aa..4647c614 100644 --- a/packages/p2p-media-loader-shaka/src/engine.ts +++ b/packages/p2p-media-loader-shaka/src/engine.ts @@ -20,23 +20,26 @@ import { DynamicCoreConfig, } from "p2p-media-loader-core"; -/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ +/** + * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. + */ export type DynamicShakaP2PEngineConfig = { - /** Dynamic core configuration */ core?: DynamicCoreConfig; }; -/** Represents the complete configuration for ShakaP2PEngine. */ +/** + * Represents the complete configuration for ShakaP2PEngine. + */ export type ShakaP2PEngineConfig = { - /** Core configuration */ core: CoreConfig; }; -/** Allows for partial configuration settings for the Shaka P2P Engine. */ +/** + * Allows for partial configuration settings for the Shaka P2P Engine. + */ export type PartialShakaEngineConfig = Partial< Omit > & { - /** Partial core configuration */ core?: Partial; }; @@ -269,7 +272,9 @@ export class ShakaP2PEngine { this.core.updatePlayback(media.currentTime, media.playbackRate); }; - /** Clean up and release all resources. Unregister all event handlers. */ + /** + * Clean up and release all resources. Unregisters all event handlers. + */ destroy() { this.destroyCurrentStreamContext(); this.updatePlayerEventHandlers("unregister"); @@ -338,7 +343,7 @@ export class ShakaP2PEngine { } /** - * Unregister plugins related to P2P functionality from the Shaka Player. + * Unregisters plugins related to P2P functionality from the Shaka Player. * * @param {Shaka} [shaka=window.shaka] - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. */ From 80a68fe77c69986534041e0cbb41b0500de81c32 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Wed, 29 May 2024 16:01:49 +0300 Subject: [PATCH 10/16] Refactor engine types --- .../src/hybrid-loader.ts | 4 +-- packages/p2p-media-loader-hlsjs/src/engine.ts | 20 ++++++--------- packages/p2p-media-loader-shaka/src/engine.ts | 25 ++++++++----------- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/packages/p2p-media-loader-core/src/hybrid-loader.ts b/packages/p2p-media-loader-core/src/hybrid-loader.ts index 08a62c34..d4e58c2d 100644 --- a/packages/p2p-media-loader-core/src/hybrid-loader.ts +++ b/packages/p2p-media-loader-core/src/hybrid-loader.ts @@ -77,9 +77,7 @@ export class HybridLoader { this.segmentStorage, this.config, this.eventTarget, - () => { - this.requestProcessQueueMicrotask(); - }, + this.requestProcessQueueMicrotask, ); this.logger = debug(`p2pml-core:hybrid-loader-${activeStream.type}`); diff --git a/packages/p2p-media-loader-hlsjs/src/engine.ts b/packages/p2p-media-loader-hlsjs/src/engine.ts index 33f4efce..8ca9a88a 100644 --- a/packages/p2p-media-loader-hlsjs/src/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/engine.ts @@ -19,26 +19,23 @@ import { } from "p2p-media-loader-core"; import { injectMixin } from "./engine-static"; -/** - * Represents the complete configuration for HlsJsP2PEngine. - */ +/** Represents the complete configuration for HlsJsP2PEngine. */ export type HlsJsP2PEngineConfig = { + /** Core config */ core: CoreConfig; }; -/** - * Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. - */ +/** Allows for partial configuration of HlsJsP2PEngine, useful for providing overrides or partial updates. */ export type PartialHlsJsP2PEngineConfig = Partial< Omit > & { + /** Partial core config */ core?: Partial; }; -/** - * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. - */ +/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ export type DynamicHlsJsP2PEngineConfig = { + /** Dynamic core config */ core?: DynamicCoreConfig; }; @@ -47,6 +44,7 @@ export type DynamicHlsJsP2PEngineConfig = { * @template HlsType The base HLS type that is being extended. */ export type HlsWithP2PInstance = HlsType & { + /** HlsJsP2PEngine instance */ readonly p2pEngine: HlsJsP2PEngine; }; @@ -339,9 +337,7 @@ export class HlsJsP2PEngine { private destroyCore = () => this.core.destroy(); - /** - * Clean up and release all resources. Unregisters all event handlers. - */ + /** Clean up and release all resources. Unregister all event handlers. */ destroy = () => { this.destroyCore(); this.updateHlsEventsHandlers("unregister"); diff --git a/packages/p2p-media-loader-shaka/src/engine.ts b/packages/p2p-media-loader-shaka/src/engine.ts index 4647c614..312dff37 100644 --- a/packages/p2p-media-loader-shaka/src/engine.ts +++ b/packages/p2p-media-loader-shaka/src/engine.ts @@ -20,26 +20,23 @@ import { DynamicCoreConfig, } from "p2p-media-loader-core"; -/** - * Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. - */ +/** Type for specifying dynamic configuration options that can be changed at runtime for the P2P engine's core. */ export type DynamicShakaP2PEngineConfig = { + /** Dynamic core config */ core?: DynamicCoreConfig; }; -/** - * Represents the complete configuration for ShakaP2PEngine. - */ +/** Represents the complete configuration for ShakaP2PEngine. */ export type ShakaP2PEngineConfig = { + /** Core config */ core: CoreConfig; }; -/** - * Allows for partial configuration settings for the Shaka P2P Engine. - */ +/** Allows for partial configuration settings for the Shaka P2P Engine. */ export type PartialShakaEngineConfig = Partial< Omit > & { + /** Partial core config */ core?: Partial; }; @@ -272,9 +269,7 @@ export class ShakaP2PEngine { this.core.updatePlayback(media.currentTime, media.playbackRate); }; - /** - * Clean up and release all resources. Unregisters all event handlers. - */ + /** Clean up and release all resources. Unregister all event handlers. */ destroy() { this.destroyCurrentStreamContext(); this.updatePlayerEventHandlers("unregister"); @@ -333,7 +328,7 @@ export class ShakaP2PEngine { * Registers plugins related to P2P functionality into the Shaka Player. * Plugins must be registered before initializing the player to ensure proper integration. * - * @param {Shaka} [shaka=window.shaka] - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. + * @param shaka - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. */ static registerPlugins(shaka = window.shaka) { validateShaka(shaka); @@ -343,9 +338,9 @@ export class ShakaP2PEngine { } /** - * Unregisters plugins related to P2P functionality from the Shaka Player. + * Unregister plugins related to P2P functionality from the Shaka Player. * - * @param {Shaka} [shaka=window.shaka] - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. + * @param shaka - The Shaka Player library. Defaults to the global Shaka Player instance if not provided. */ static unregisterPlugins(shaka = window.shaka) { validateShaka(shaka); From b7ed75eec4963852290b7358a55aac058c92ce38 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 09:10:01 +0300 Subject: [PATCH 11/16] Removed question in FAQ --- FAQ.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/FAQ.md b/FAQ.md index a4f99246..d4d88e06 100644 --- a/FAQ.md +++ b/FAQ.md @@ -33,10 +33,6 @@ But they support a limited number of peers and can reject connections or even go That is why they can't be used in production environments. Consider running your personal tracker or buy resources from a tracker provider to go stable. -## How to achieve better P2P ratio for live streams & VOD streams? - -Our current default configuration efficiently supports both Live and Video On Demand (VOD) streams. This setup is optimized to provide a robust P2P sharing ratio, enhancing performance without the need for custom settings. - ## What are the requirements to share a stream over P2P? The requirements to share a stream over P2P are: From 7eae90cd9211a46dd18d832ed6c9b59f1d698a3a Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 09:14:20 +0300 Subject: [PATCH 12/16] Removed "ts-essentials" from dependencies --- packages/p2p-media-loader-core/package.json | 3 +-- packages/p2p-media-loader-hlsjs/package.json | 3 +-- packages/p2p-media-loader-shaka/package.json | 3 +-- pnpm-lock.yaml | 21 -------------------- 4 files changed, 3 insertions(+), 27 deletions(-) diff --git a/packages/p2p-media-loader-core/package.json b/packages/p2p-media-loader-core/package.json index 37d5dec2..92445fcd 100644 --- a/packages/p2p-media-loader-core/package.json +++ b/packages/p2p-media-loader-core/package.json @@ -53,8 +53,7 @@ "dependencies": { "bittorrent-tracker": "^11.0.2", "debug": "^4.3.4", - "nano-md5": "^1.0.5", - "ts-essentials": "^9.4.1" + "nano-md5": "^1.0.5" }, "devDependencies": { "vite-plugin-node-polyfills": "^0.21.0" diff --git a/packages/p2p-media-loader-hlsjs/package.json b/packages/p2p-media-loader-hlsjs/package.json index c44ea1c6..17fe2975 100644 --- a/packages/p2p-media-loader-hlsjs/package.json +++ b/packages/p2p-media-loader-hlsjs/package.json @@ -51,7 +51,6 @@ }, "dependencies": { "hls.js": "^1.5.7", - "p2p-media-loader-core": "workspace:*", - "ts-essentials": "^9.4.1" + "p2p-media-loader-core": "workspace:*" } } diff --git a/packages/p2p-media-loader-shaka/package.json b/packages/p2p-media-loader-shaka/package.json index 4e43b390..21ad1a5b 100644 --- a/packages/p2p-media-loader-shaka/package.json +++ b/packages/p2p-media-loader-shaka/package.json @@ -52,7 +52,6 @@ }, "dependencies": { "p2p-media-loader-core": "workspace:*", - "shaka-player": "^4.7.11", - "ts-essentials": "^9.4.1" + "shaka-player": "^4.7.11" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de404e37..f16cdf85 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,9 +103,6 @@ importers: nano-md5: specifier: ^1.0.5 version: 1.0.5 - ts-essentials: - specifier: ^9.4.1 - version: 9.4.1(typescript@5.4.2) devDependencies: vite-plugin-node-polyfills: specifier: ^0.21.0 @@ -186,9 +183,6 @@ importers: p2p-media-loader-core: specifier: workspace:* version: link:../p2p-media-loader-core - ts-essentials: - specifier: ^9.4.1 - version: 9.4.1(typescript@5.4.2) packages/p2p-media-loader-shaka: dependencies: @@ -198,9 +192,6 @@ importers: shaka-player: specifier: ^4.7.11 version: 4.7.11 - ts-essentials: - specifier: ^9.4.1 - version: 9.4.1(typescript@5.4.2) packages: @@ -2535,14 +2526,6 @@ packages: peerDependencies: typescript: '>=4.2.0' - ts-essentials@9.4.1: - resolution: {integrity: sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==} - peerDependencies: - typescript: '>=4.1.0' - peerDependenciesMeta: - typescript: - optional: true - tty-browserify@0.0.1: resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} @@ -5464,10 +5447,6 @@ snapshots: dependencies: typescript: 5.4.2 - ts-essentials@9.4.1(typescript@5.4.2): - optionalDependencies: - typescript: 5.4.2 - tty-browserify@0.0.1: {} type-check@0.4.0: From 0f0426f6c50b728ef34ea972edb8ea50e1a478a2 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 11:05:57 +0300 Subject: [PATCH 13/16] Refactor segment storage expiration logic --- packages/p2p-media-loader-core/src/core.ts | 4 +- .../src/hybrid-loader.ts | 6 ++- .../src/segments-storage.ts | 45 +++++++++++++------ packages/p2p-media-loader-core/src/types.ts | 8 ++-- packages/p2p-media-loader-hlsjs/src/engine.ts | 3 +- packages/p2p-media-loader-shaka/src/engine.ts | 3 +- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/packages/p2p-media-loader-core/src/core.ts b/packages/p2p-media-loader-core/src/core.ts index 63c6041a..0535d3ae 100644 --- a/packages/p2p-media-loader-core/src/core.ts +++ b/packages/p2p-media-loader-core/src/core.ts @@ -24,8 +24,8 @@ export class Core { highDemandTimeWindow: 15, httpDownloadTimeWindow: 3000, p2pDownloadTimeWindow: 6000, - cachedSegmentExpiration: 120 * 1000, - cachedSegmentsCount: 1000, + cachedSegmentExpiration: 0, + cachedSegmentsCount: 0, webRtcMaxMessageSize: 64 * 1024 - 1, p2pNotReceivingBytesTimeoutMs: 1000, p2pInactiveLoaderDestroyTimeoutMs: 30 * 1000, diff --git a/packages/p2p-media-loader-core/src/hybrid-loader.ts b/packages/p2p-media-loader-core/src/hybrid-loader.ts index d4e58c2d..bdaac2f3 100644 --- a/packages/p2p-media-loader-core/src/hybrid-loader.ts +++ b/packages/p2p-media-loader-core/src/hybrid-loader.ts @@ -185,7 +185,11 @@ export class HybridLoader { this.engineRequest = undefined; } this.requests.remove(request); - void this.segmentStorage.storeSegment(request.segment, request.data); + void this.segmentStorage.storeSegment( + request.segment, + request.data, + this.streamDetails.isLive, + ); break; case "failed": diff --git a/packages/p2p-media-loader-core/src/segments-storage.ts b/packages/p2p-media-loader-core/src/segments-storage.ts index fb76c7a0..d1fbbcb1 100644 --- a/packages/p2p-media-loader-core/src/segments-storage.ts +++ b/packages/p2p-media-loader-core/src/segments-storage.ts @@ -17,6 +17,7 @@ type StorageItem = { segment: SegmentWithStream; data: ArrayBuffer; lastAccessed: number; + isSegmentLive: boolean; }; type StorageEventHandlers = { @@ -61,12 +62,17 @@ export class SegmentsMemoryStorage { } // eslint-disable-next-line @typescript-eslint/require-await - async storeSegment(segment: SegmentWithStream, data: ArrayBuffer) { + async storeSegment( + segment: SegmentWithStream, + data: ArrayBuffer, + isStreamLive: boolean, + ) { const id = getStorageItemId(segment); this.cache.set(id, { segment, data, lastAccessed: performance.now(), + isSegmentLive: isStreamLive, }); this.logger(`add segment: ${id}`); this.dispatchStorageUpdatedEvent(segment.stream); @@ -108,11 +114,22 @@ export class SegmentsMemoryStorage { // Delete old segments const now = performance.now(); + const defaultLiveExpiration = 1000 * 60 * 20; for (const entry of this.cache.entries()) { const [itemId, item] = entry; const { lastAccessed, segment } = item; - if (now - lastAccessed > this.storageConfig.cachedSegmentExpiration) { + + let expirationThreshold; + if (this.storageConfig.cachedSegmentExpiration > 0) { + expirationThreshold = this.storageConfig.cachedSegmentExpiration * 1000; + } else if (item.isSegmentLive) { + expirationThreshold = defaultLiveExpiration; + } else { + continue; + } + + if (now - lastAccessed > expirationThreshold) { if (!this.isSegmentLocked(segment)) { itemsToDelete.push(itemId); streamsOfChangedItems.add(segment.stream); @@ -123,17 +140,19 @@ export class SegmentsMemoryStorage { } // Delete segments over cached count - let countOverhead = - remainingItems.length - this.storageConfig.cachedSegmentsCount; - if (countOverhead > 0) { - remainingItems.sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed); - - for (const [itemId, { segment }] of remainingItems) { - if (!this.isSegmentLocked(segment)) { - itemsToDelete.push(itemId); - streamsOfChangedItems.add(segment.stream); - countOverhead--; - if (countOverhead === 0) break; + if (this.storageConfig.cachedSegmentsCount > 0) { + let countOverhead = + remainingItems.length - this.storageConfig.cachedSegmentsCount; + if (countOverhead > 0) { + remainingItems.sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed); + + for (const [itemId, { segment }] of remainingItems) { + if (!this.isSegmentLocked(segment)) { + itemsToDelete.push(itemId); + streamsOfChangedItems.add(segment.stream); + countOverhead--; + if (countOverhead === 0) break; + } } } } diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index c14460a7..fdbbfa22 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -130,11 +130,12 @@ export type CoreConfig = { simultaneousP2PDownloads: number; /** - * Time after which a cached segment expires, in milliseconds. + * Time after which a cached segment expires, in seconds. + * If set to 0, the cacheSegmentExpiration is disabled for VOD streams, and a default value (20 minutes) is used for live streams. * * @default * ```typescript - * cachedSegmentExpiration: 120 * 1000 + * cachedSegmentExpiration: 0 * ``` */ cachedSegmentExpiration: number; @@ -142,10 +143,11 @@ export type CoreConfig = { /** * Maximum number of segments to store in the cache. * Has to be less then httpDownloadTimeWindow and p2pDownloadTimeWindow. + * If set to 0, the cache is unlimited. * * @default * ```typescript - * cachedSegmentsCount: 1000 + * cachedSegmentsCount: 0 * ``` */ cachedSegmentsCount: number; diff --git a/packages/p2p-media-loader-hlsjs/src/engine.ts b/packages/p2p-media-loader-hlsjs/src/engine.ts index 8ca9a88a..89e098c0 100644 --- a/packages/p2p-media-loader-hlsjs/src/engine.ts +++ b/packages/p2p-media-loader-hlsjs/src/engine.ts @@ -74,8 +74,7 @@ export type HlsWithP2PConfig unknown> = * core: { * highDemandTimeWindow: 30, // 30 seconds * simultaneousHttpDownloads: 3, - * cachedSegmentsCount: 50, - * webRtcMaxMessageSize: 262144, // 256 KB + * webRtcMaxMessageSize: 64 * 1024, // 64 KB * p2pNotReceivingBytesTimeoutMs: 10000, // 10 seconds * p2pInactiveLoaderDestroyTimeoutMs: 15000, // 15 seconds * httpNotReceivingBytesTimeoutMs: 8000, // 8 seconds diff --git a/packages/p2p-media-loader-shaka/src/engine.ts b/packages/p2p-media-loader-shaka/src/engine.ts index 312dff37..aba08f96 100644 --- a/packages/p2p-media-loader-shaka/src/engine.ts +++ b/packages/p2p-media-loader-shaka/src/engine.ts @@ -57,8 +57,7 @@ const LIVE_EDGE_DELAY = 25; * core: { * highDemandTimeWindow: 30, // 30 seconds * simultaneousHttpDownloads: 3, - * cachedSegmentsCount: 50, - * webRtcMaxMessageSize: 262144, // 256 KB + * webRtcMaxMessageSize: 64 * 1024, // 64 KB * p2pNotReceivingBytesTimeoutMs: 10000, // 10 seconds * p2pInactiveLoaderDestroyTimeoutMs: 15000, // 15 seconds * httpNotReceivingBytesTimeoutMs: 8000, // 8 seconds From f2633b05ba021e6524fe68d9a5c108b081db53e7 Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 11:19:29 +0300 Subject: [PATCH 14/16] Fix typedoc props sorting --- packages/p2p-media-loader-core/src/types.ts | 15 ++++++++------- packages/p2p-media-loader-core/typedoc.json | 3 ++- packages/p2p-media-loader-hlsjs/typedoc.json | 3 ++- packages/p2p-media-loader-shaka/typedoc.json | 3 ++- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index fdbbfa22..2d822e1f 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -55,15 +55,16 @@ export type Stream = { readonly index: number; }; -/** Defines a subset of CoreConfig for dynamic updates, allowing selective modification of configuration properties. */ +/** Represents a dynamically modifiable configuration, allowing updates to selected CoreConfig properties at runtime. */ export type DynamicCoreConfig = Partial< - Pick< + Omit< CoreConfig, - | "simultaneousP2PDownloads" - | "simultaneousHttpDownloads" - | "cachedSegmentsCount" - | "httpDownloadTimeWindow" - | "p2pDownloadTimeWindow" + | "announceTrackers" + | "rtcConfig" + | "trackerClientVersionPrefix" + | "swarmId" + | "validateP2PSegment" + | "httpRequestSetup" > >; diff --git a/packages/p2p-media-loader-core/typedoc.json b/packages/p2p-media-loader-core/typedoc.json index e838eba9..3a79cd64 100644 --- a/packages/p2p-media-loader-core/typedoc.json +++ b/packages/p2p-media-loader-core/typedoc.json @@ -2,5 +2,6 @@ "entryPoints": ["src/index.ts"], "excludeExternals": true, "excludePrivate": true, - "readme": "none" + "readme": "none", + "sort": ["source-order"] } diff --git a/packages/p2p-media-loader-hlsjs/typedoc.json b/packages/p2p-media-loader-hlsjs/typedoc.json index e838eba9..3a79cd64 100644 --- a/packages/p2p-media-loader-hlsjs/typedoc.json +++ b/packages/p2p-media-loader-hlsjs/typedoc.json @@ -2,5 +2,6 @@ "entryPoints": ["src/index.ts"], "excludeExternals": true, "excludePrivate": true, - "readme": "none" + "readme": "none", + "sort": ["source-order"] } diff --git a/packages/p2p-media-loader-shaka/typedoc.json b/packages/p2p-media-loader-shaka/typedoc.json index e838eba9..3a79cd64 100644 --- a/packages/p2p-media-loader-shaka/typedoc.json +++ b/packages/p2p-media-loader-shaka/typedoc.json @@ -2,5 +2,6 @@ "entryPoints": ["src/index.ts"], "excludeExternals": true, "excludePrivate": true, - "readme": "none" + "readme": "none", + "sort": ["source-order"] } From 0918158b94084222c8fec2be19bd6ee1f93499ff Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 15:00:33 +0300 Subject: [PATCH 15/16] Updated cachedSegmentExpiration to undefined for VOD streams and use default value for live streams --- packages/p2p-media-loader-core/src/core.ts | 2 +- .../src/segments-storage.ts | 26 +++++++------------ packages/p2p-media-loader-core/src/types.ts | 6 ++--- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/p2p-media-loader-core/src/core.ts b/packages/p2p-media-loader-core/src/core.ts index 0535d3ae..f3f0b2f7 100644 --- a/packages/p2p-media-loader-core/src/core.ts +++ b/packages/p2p-media-loader-core/src/core.ts @@ -24,7 +24,7 @@ export class Core { highDemandTimeWindow: 15, httpDownloadTimeWindow: 3000, p2pDownloadTimeWindow: 6000, - cachedSegmentExpiration: 0, + cachedSegmentExpiration: undefined, cachedSegmentsCount: 0, webRtcMaxMessageSize: 64 * 1024 - 1, p2pNotReceivingBytesTimeoutMs: 1000, diff --git a/packages/p2p-media-loader-core/src/segments-storage.ts b/packages/p2p-media-loader-core/src/segments-storage.ts index d1fbbcb1..6dd378b4 100644 --- a/packages/p2p-media-loader-core/src/segments-storage.ts +++ b/packages/p2p-media-loader-core/src/segments-storage.ts @@ -17,7 +17,6 @@ type StorageItem = { segment: SegmentWithStream; data: ArrayBuffer; lastAccessed: number; - isSegmentLive: boolean; }; type StorageEventHandlers = { @@ -65,18 +64,17 @@ export class SegmentsMemoryStorage { async storeSegment( segment: SegmentWithStream, data: ArrayBuffer, - isStreamLive: boolean, + isLiveStream: boolean, ) { const id = getStorageItemId(segment); this.cache.set(id, { segment, data, lastAccessed: performance.now(), - isSegmentLive: isStreamLive, }); this.logger(`add segment: ${id}`); this.dispatchStorageUpdatedEvent(segment.stream); - void this.clear(); + void this.clear(isLiveStream); } // eslint-disable-next-line @typescript-eslint/require-await @@ -107,29 +105,25 @@ export class SegmentsMemoryStorage { } // eslint-disable-next-line @typescript-eslint/require-await - private async clear(): Promise { + private async clear(isLiveStream: boolean): Promise { + const cacheSegmentExpiration = + (this.storageConfig.cachedSegmentExpiration ?? + (isLiveStream ? 60 * 20 : 0)) * 1000; + + if (cacheSegmentExpiration === 0) return false; + const itemsToDelete: string[] = []; const remainingItems: [string, StorageItem][] = []; const streamsOfChangedItems = new Set(); // Delete old segments const now = performance.now(); - const defaultLiveExpiration = 1000 * 60 * 20; for (const entry of this.cache.entries()) { const [itemId, item] = entry; const { lastAccessed, segment } = item; - let expirationThreshold; - if (this.storageConfig.cachedSegmentExpiration > 0) { - expirationThreshold = this.storageConfig.cachedSegmentExpiration * 1000; - } else if (item.isSegmentLive) { - expirationThreshold = defaultLiveExpiration; - } else { - continue; - } - - if (now - lastAccessed > expirationThreshold) { + if (now - lastAccessed > cacheSegmentExpiration) { if (!this.isSegmentLocked(segment)) { itemsToDelete.push(itemId); streamsOfChangedItems.add(segment.stream); diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index 2d822e1f..7589b3fb 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -132,14 +132,14 @@ export type CoreConfig = { /** * Time after which a cached segment expires, in seconds. - * If set to 0, the cacheSegmentExpiration is disabled for VOD streams, and a default value (20 minutes) is used for live streams. + * If set to undefined, the cacheSegmentExpiration is disabled for VOD streams, and a default value (20 minutes) is used for live streams. * * @default * ```typescript - * cachedSegmentExpiration: 0 + * cachedSegmentExpiration: undefined * ``` */ - cachedSegmentExpiration: number; + cachedSegmentExpiration: number | undefined; /** * Maximum number of segments to store in the cache. From d047aafceb980c74daadcb90d263b054a73c475e Mon Sep 17 00:00:00 2001 From: DimaDemchenko Date: Thu, 30 May 2024 16:43:39 +0300 Subject: [PATCH 16/16] Small adjustments --- packages/p2p-media-loader-core/src/core.ts | 1 - .../src/segments-storage.ts | 4 +++- packages/p2p-media-loader-core/src/types.ts | 23 ++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/p2p-media-loader-core/src/core.ts b/packages/p2p-media-loader-core/src/core.ts index f3f0b2f7..2f5c26af 100644 --- a/packages/p2p-media-loader-core/src/core.ts +++ b/packages/p2p-media-loader-core/src/core.ts @@ -24,7 +24,6 @@ export class Core { highDemandTimeWindow: 15, httpDownloadTimeWindow: 3000, p2pDownloadTimeWindow: 6000, - cachedSegmentExpiration: undefined, cachedSegmentsCount: 0, webRtcMaxMessageSize: 64 * 1024 - 1, p2pNotReceivingBytesTimeoutMs: 1000, diff --git a/packages/p2p-media-loader-core/src/segments-storage.ts b/packages/p2p-media-loader-core/src/segments-storage.ts index 6dd378b4..88a9cbe4 100644 --- a/packages/p2p-media-loader-core/src/segments-storage.ts +++ b/packages/p2p-media-loader-core/src/segments-storage.ts @@ -23,6 +23,8 @@ type StorageEventHandlers = { [key in `onStorageUpdated-${string}`]: (steam: Stream) => void; }; +const DEFAULT_LIVE_CACHED_SEGMENT_EXPIRATION = 1200; + export class SegmentsMemoryStorage { private cache = new Map(); private _isInitialized = false; @@ -108,7 +110,7 @@ export class SegmentsMemoryStorage { private async clear(isLiveStream: boolean): Promise { const cacheSegmentExpiration = (this.storageConfig.cachedSegmentExpiration ?? - (isLiveStream ? 60 * 20 : 0)) * 1000; + (isLiveStream ? DEFAULT_LIVE_CACHED_SEGMENT_EXPIRATION : 0)) * 1000; if (cacheSegmentExpiration === 0) return false; diff --git a/packages/p2p-media-loader-core/src/types.ts b/packages/p2p-media-loader-core/src/types.ts index 7589b3fb..35157acf 100644 --- a/packages/p2p-media-loader-core/src/types.ts +++ b/packages/p2p-media-loader-core/src/types.ts @@ -57,14 +57,21 @@ export type Stream = { /** Represents a dynamically modifiable configuration, allowing updates to selected CoreConfig properties at runtime. */ export type DynamicCoreConfig = Partial< - Omit< + Pick< CoreConfig, - | "announceTrackers" - | "rtcConfig" - | "trackerClientVersionPrefix" - | "swarmId" - | "validateP2PSegment" - | "httpRequestSetup" + | "highDemandTimeWindow" + | "httpDownloadTimeWindow" + | "p2pDownloadTimeWindow" + | "simultaneousHttpDownloads" + | "simultaneousP2PDownloads" + | "cachedSegmentExpiration" + | "cachedSegmentsCount" + | "webRtcMaxMessageSize" + | "p2pNotReceivingBytesTimeoutMs" + | "p2pInactiveLoaderDestroyTimeoutMs" + | "httpNotReceivingBytesTimeoutMs" + | "httpErrorRetries" + | "p2pErrorRetries" > >; @@ -139,7 +146,7 @@ export type CoreConfig = { * cachedSegmentExpiration: undefined * ``` */ - cachedSegmentExpiration: number | undefined; + cachedSegmentExpiration?: number; /** * Maximum number of segments to store in the cache.