Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: isP2PDisabled #369

Merged
merged 35 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
633e59d
Add main & secondary stream configs
DimaDemchenko May 31, 2024
3e88f1d
Refactor dynamic configuration handling in Core.applyDynamicConfig
DimaDemchenko Jun 3, 2024
a0f457a
Refactored types
DimaDemchenko Jun 3, 2024
04f8bd2
Fix jsdoc in types
DimaDemchenko Jun 3, 2024
b8fa15d
Refactor mergeConfigs function to use Object.create(null)
DimaDemchenko Jun 3, 2024
4b6e2c1
Refactor mergeConfigs function to handle restricted properties
DimaDemchenko Jun 3, 2024
3f722b1
Fix types
DimaDemchenko Jun 3, 2024
4b6c204
Refactor core to use StreamConfig instead of CoreConfig
DimaDemchenko Jun 4, 2024
8f0a5cf
Refactor core configuration to use partial updates in HlsJsP2PEngine …
DimaDemchenko Jun 4, 2024
313b5ce
Refactor core configuration to use partial updates in HlsJsP2PEngine …
DimaDemchenko Jun 4, 2024
1bb3e8b
Refactor core configuration to use StreamConfig instead of CoreConfig
DimaDemchenko Jun 4, 2024
ca10710
Add tests for core utils
DimaDemchenko Jun 5, 2024
0efdaee
Update eslint config to ignore test folder
DimaDemchenko Jun 5, 2024
19dc3e8
Refactor core configuration to use DefinedCoreConfig
DimaDemchenko Jun 5, 2024
1d6f789
Refactor overrideConfig
DimaDemchenko Jun 5, 2024
cd4ae9c
Small improvements
DimaDemchenko Jun 5, 2024
930e723
Small improvements
DimaDemchenko Jun 5, 2024
73e4213
Add isP2PDisabled in static stream configuration
DimaDemchenko Jun 5, 2024
073de25
Refactor applyDynamicConfig method to handle changes in isP2PDisabled…
DimaDemchenko Jun 6, 2024
1052f50
Refactor Peer class connection event handling
DimaDemchenko Jun 6, 2024
70b057b
Refactor segment loading logic to improve P2P functionality
DimaDemchenko Jun 7, 2024
e9bc8da
Merge branch 'v1' into feat/p2pSwitcher
DimaDemchenko Jun 7, 2024
41f04e6
Refactor stream configuration handling in Core.applyDynamicConfig
DimaDemchenko Jun 7, 2024
776522b
Simplified dynamic configuration handling
DimaDemchenko Jun 10, 2024
67f7c83
Improved CoreConfig description in documentation
DimaDemchenko Jun 10, 2024
a4e22e8
Remove redundant comments from codebase
DimaDemchenko Jun 10, 2024
409a952
Refactor segment loading logic to improve P2P functionality
DimaDemchenko Jun 10, 2024
a790c57
Refactor naming for enhanced readability
DimaDemchenko Jun 10, 2024
d79bda3
Refactor segment loading logic to handle P2P functionality
DimaDemchenko Jun 10, 2024
e6c9295
Refactor error handling in RequestsContainer.destroy method
DimaDemchenko Jun 10, 2024
dcb2eac
Improved naming
DimaDemchenko Jun 11, 2024
7e0629a
Improvements in Core and RequestsContainer
DimaDemchenko Jun 11, 2024
0b4a4b2
Refactor Peer class connection event listeners
DimaDemchenko Jun 11, 2024
8bc30d6
Refactor error handling in catch block
DimaDemchenko Jun 11, 2024
5086513
Refactor Peer class connection event listeners and add missing event …
DimaDemchenko Jun 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 71 additions & 7 deletions packages/p2p-media-loader-core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class Core<TStream extends Stream = Stream> {
};

static readonly DEFAULT_STREAM_CONFIG: StreamConfig = {
isP2PDisabled: false,
simultaneousHttpDownloads: 3,
simultaneousP2PDownloads: 3,
highDemandTimeWindow: 15,
Expand Down Expand Up @@ -147,15 +148,25 @@ export class Core<TStream extends Stream = Stream> {
* core.applyDynamicConfig(dynamicConfig);
*/
applyDynamicConfig(dynamicConfig: DynamicCoreConfig) {
overrideConfig(this.commonCoreConfig, dynamicConfig);
overrideConfig(this.mainStreamConfig, dynamicConfig);
overrideConfig(this.secondaryStreamConfig, dynamicConfig);
const { mainStream, secondaryStream } = dynamicConfig;

this.overrideAllConfigs(dynamicConfig, mainStream, secondaryStream);

if (dynamicConfig.mainStream) {
overrideConfig(this.mainStreamConfig, dynamicConfig.mainStream);
if (
this.mainStreamConfig.isP2PDisabled &&
this.secondaryStreamConfig.isP2PDisabled
) {
this.destroyStreamLoader("main");
this.destroyStreamLoader("secondary");
return;
DimaDemchenko marked this conversation as resolved.
Show resolved Hide resolved
}
if (dynamicConfig.secondaryStream) {
overrideConfig(this.secondaryStreamConfig, dynamicConfig.secondaryStream);

if (this.mainStreamConfig.isP2PDisabled) {
this.destroyStreamLoader("main");
}

if (this.secondaryStreamConfig.isP2PDisabled) {
this.destroyStreamLoader("secondary");
}
}

Expand Down Expand Up @@ -282,6 +293,7 @@ export class Core<TStream extends Stream = Stream> {
}

const segment = this.identifySegment(segmentLocalId);

const loader = this.getStreamHybridLoader(segment);
void loader.loadSegment(segment, callbacks);
}
Expand Down Expand Up @@ -330,6 +342,30 @@ export class Core<TStream extends Stream = Stream> {
this.streamDetails.isLive = isLive;
}

isSegmentLoadableByP2PCore(segmentLocalId: string): boolean {
try {
const segment = this.identifySegment(segmentLocalId);

if (
segment.stream.type === "main" &&
this.mainStreamConfig.isP2PDisabled
) {
return false;
}

if (
segment.stream.type === "secondary" &&
this.secondaryStreamConfig.isP2PDisabled
) {
return false;
}

return true;
} catch (e) {
return false;
}
}

/**
* Cleans up resources used by the Core instance, including destroying any active stream loaders
* and clearing stored segments.
Expand Down Expand Up @@ -362,6 +398,34 @@ export class Core<TStream extends Stream = Stream> {
return segment;
}

private overrideAllConfigs(
dynamicConfig: DynamicCoreConfig,
mainStream?: Partial<StreamConfig>,
secondaryStream?: Partial<StreamConfig>,
) {
overrideConfig(this.commonCoreConfig, dynamicConfig);
overrideConfig(this.mainStreamConfig, dynamicConfig);
overrideConfig(this.secondaryStreamConfig, dynamicConfig);

if (mainStream) {
overrideConfig(this.mainStreamConfig, mainStream);
}

if (secondaryStream) {
overrideConfig(this.secondaryStreamConfig, secondaryStream);
}
}

private destroyStreamLoader(streamType: "main" | "secondary") {
if (streamType === "main") {
this.mainStreamLoader?.destroy();
this.mainStreamLoader = undefined;
} else {
this.secondaryStreamLoader?.destroy();
this.secondaryStreamLoader = undefined;
}
}

private getStreamHybridLoader(segment: SegmentWithStream) {
if (segment.stream.type === "main") {
this.mainStreamLoader ??= this.createNewHybridLoader(segment);
Expand Down
1 change: 1 addition & 0 deletions packages/p2p-media-loader-core/src/hybrid-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ export class HybridLoader {
clearInterval(this.storageCleanUpIntervalId);
clearInterval(this.randomHttpDownloadInterval);
this.storageCleanUpIntervalId = undefined;
this.engineRequest?.abort();
this.requests.destroy();
this.p2pLoaders.destroy();
}
Expand Down
12 changes: 7 additions & 5 deletions packages/p2p-media-loader-core/src/p2p/peer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ export class Peer {
eventTarget.getEventDispatcher("onPeerConnect")({
peerId: this.id,
});
connection.on("close", this.onPeerConnectionClosed);
connection.on("error", this.onConnectionError);

connection._channel.onerror = this.onConnectionError;
DimaDemchenko marked this conversation as resolved.
Show resolved Hide resolved
connection._channel.onclose = this.onPeerConnectionClosed;
}

get downloadingSegment(): SegmentWithStream | undefined {
Expand Down Expand Up @@ -301,10 +302,11 @@ export class Peer {
this.destroy();
};

private onConnectionError = (error: { code: string }) => {
this.logger(`peer connection error ${this.id} %O`, error);
private onConnectionError = (ev: Event) => {
this.logger(`peer connection error ${this.id} %O`, ev);

if (error.code === "ERR_DATA_CHANNEL") {
const event = ev as RTCErrorEvent;
if (event.error.errorDetail === "data-channel-failure") {
this.destroy();
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,12 @@ export class RequestsContainer {

destroy() {
for (const request of this.requests.values()) {
request.abortFromProcessQueue();
try {
DimaDemchenko marked this conversation as resolved.
Show resolved Hide resolved
request.abortFromProcessQueue();
} catch (error) {
// eslint-disable-next-line no-console
console.warn(error);
}
}
this.requests.clear();
}
Expand Down
29 changes: 27 additions & 2 deletions packages/p2p-media-loader-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export type DynamicStreamProperties =
| "httpErrorRetries"
| "p2pErrorRetries"
| "validateP2PSegment"
| "httpRequestSetup";
| "httpRequestSetup"
| "isP2PDisabled";

/**
* Represents a dynamically modifiable configuration, allowing updates to selected CoreConfig properties at runtime.
Expand Down Expand Up @@ -138,9 +139,23 @@ export type CommonCoreConfig = {
* Represents a set of configuration parameters that can be used to override or extend the
* default configuration settings for a specific stream (main or secondary).
*
* @example
* @example Configuration for basic video stream
*
* ```typescript
* const config: CoreConfig = {
* highDemandTimeWindow: 15,
* httpDownloadTimeWindow: 3000,
* p2pDownloadTimeWindow: 6000,
* swarmId: "custom swarm ID for video stream",
* cashedSegmentsCount: 1000,
* }
* ```
*
* @example Configuration for advanced video stream
*
* ```typescript
* const config: CoreConfig = {
* // Configuration for both streams
* highDemandTimeWindow: 20,
* httpDownloadTimeWindow: 3000,
* p2pDownloadTimeWindow: 6000,
Expand All @@ -164,6 +179,16 @@ export type CoreConfig = Partial<StreamConfig> &

/** Configuration options for the Core functionality, including network and processing parameters. */
export type StreamConfig = {
/**
* Indicates whether Peer-to-Peer (P2P) functionality is disabled for the stream.
* If set to true, P2P functionality is disabled for the stream.
*
* @default
* ```typescript
* isP2PDisabled: false
* ```
*/
isP2PDisabled: boolean;
/**
* 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.
Expand Down
8 changes: 7 additions & 1 deletion packages/p2p-media-loader-hlsjs/src/fragment-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@ export class FragmentLoaderBase implements Loader<FragmentLoaderContext> {
start,
end !== undefined ? end - 1 : undefined,
);

this.#segmentId = Utils.getSegmentLocalId(context.url, byteRange);
const isSegmentDownloadableByP2PCore =
this.#core.isSegmentLoadableByP2PCore(this.#segmentId);

if (!this.#core.hasSegment(this.#segmentId)) {
if (
!this.#core.hasSegment(this.#segmentId) ||
isSegmentDownloadableByP2PCore === false
) {
this.#defaultLoader = this.#createDefaultLoader();
this.#defaultLoader.stats = this.stats;
this.#defaultLoader?.load(context, config, callbacks);
Expand Down
8 changes: 7 additions & 1 deletion packages/p2p-media-loader-shaka/src/loading-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ export class Loader {
byteRangeString: string,
): LoadingHandlerResult {
const segmentId = Utils.getSegmentLocalId(segmentUrl, byteRangeString);
if (!this.core.hasSegment(segmentId)) {
const isSegmentDownloadableByP2PCore =
this.core.isSegmentLoadableByP2PCore(segmentId);

if (
!this.core.hasSegment(segmentId) ||
isSegmentDownloadableByP2PCore === false
) {
return this.defaultLoad() as LoadingHandlerResult;
}

Expand Down