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: event emitter #340

Merged
merged 5 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,14 @@ function App() {
};

if (!hlsEngine.current) {
hlsEngine.current = new HlsJsEngine({ onSegmentLoaded });
hlsEngine.current = new HlsJsEngine();
hlsEngine.current.addEventListener("onSegmentLoaded", onSegmentLoaded);
}

if (!shakaEngine.current) {
ShakaEngine.setGlobalSettings();
shakaEngine.current = new ShakaEngine(window.shaka, { onSegmentLoaded });
shakaEngine.current = new ShakaEngine(window.shaka);
shakaEngine.current.addEventListener("onSegmentLoaded", onSegmentLoaded);
}

useEffect(() => {
Expand All @@ -127,6 +129,7 @@ function App() {
const hls = new window.Hls({
...engine.getConfig(),
});

engine.setHls(hls);
hls.attachMedia(videoRef.current);
hls.loadSource(url);
Expand Down
22 changes: 19 additions & 3 deletions packages/p2p-media-loader-core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import {
Segment,
Settings,
SegmentBase,
CoreEventHandlers,
BandwidthCalculators,
StreamDetails,
CoreEventMap,
} from "./types";
import * as StreamUtils from "./utils/stream";
import { BandwidthCalculator } from "./bandwidth-calculator";
import { EngineCallbacks } from "./requests/engine-request";
import { SegmentsMemoryStorage } from "./segments-storage";
import { EventEmitter } from "./utils/event-emitter";

export class Core<TStream extends Stream = Stream> {
private readonly eventEmitter = new EventEmitter<CoreEventMap>();
private manifestResponseUrl?: string;
private readonly streams = new Map<string, StreamWithSegments<TStream>>();
private readonly settings: Settings = {
Expand Down Expand Up @@ -44,7 +46,21 @@ export class Core<TStream extends Stream = Stream> {
activeLevelBitrate: 0,
};

constructor(private readonly eventHandlers?: CoreEventHandlers) {}
constructor() {}

addEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.eventEmitter.addEventListener(eventName, listener);
}

removeEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.eventEmitter.removeEventListener(eventName, listener);
}

setManifestResponseUrl(url: string): void {
this.manifestResponseUrl = url.split("?")[0];
Expand Down Expand Up @@ -170,7 +186,7 @@ export class Core<TStream extends Stream = Stream> {
this.settings,
this.bandwidthCalculators,
this.segmentStorage,
this.eventHandlers,
this.eventEmitter,
);
};

Expand Down
10 changes: 7 additions & 3 deletions packages/p2p-media-loader-core/src/hybrid-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { HttpRequestExecutor } from "./http-loader";
import { SegmentsMemoryStorage } from "./segments-storage";
import {
Settings,
CoreEventHandlers,
Playback,
BandwidthCalculators,
StreamDetails,
CoreEventMap,
} from "./types";
import { P2PLoadersContainer } from "./p2p/loaders-container";
import { RequestsContainer } from "./requests/request-container";
Expand All @@ -17,6 +17,7 @@ import * as StreamUtils from "./utils/stream";
import * as Utils from "./utils/utils";
import debug from "debug";
import { QueueItem } from "./utils/queue";
import { EventEmitter } from "./utils/event-emitter";

const FAILED_ATTEMPTS_CLEAR_INTERVAL = 60000;

Expand All @@ -32,6 +33,7 @@ export class HybridLoader {
private lastQueueProcessingTimeStamp?: number;
private randomHttpDownloadInterval?: number;
private isProcessQueueMicrotaskCreated = false;
private readonly onSegmentLoaded: CoreEventMap["onSegmentLoaded"];

constructor(
private streamManifestUrl: string,
Expand All @@ -40,7 +42,7 @@ export class HybridLoader {
private readonly settings: Settings,
private readonly bandwidthCalculators: BandwidthCalculators,
private readonly segmentStorage: SegmentsMemoryStorage,
private readonly eventHandlers?: Pick<CoreEventHandlers, "onSegmentLoaded">,
eventEmitter: EventEmitter<CoreEventMap>,
) {
const activeStream = this.lastRequestedSegment.stream;
this.playback = { position: this.lastRequestedSegment.startTime, rate: 1 };
Expand All @@ -52,6 +54,8 @@ export class HybridLoader {
this.settings,
);

this.onSegmentLoaded = eventEmitter.getEventDispatcher("onSegmentLoaded");

if (!this.segmentStorage.isInitialized) {
throw new Error("Segment storage is not initialized.");
}
Expand Down Expand Up @@ -167,7 +171,7 @@ export class HybridLoader {
}
this.requests.remove(request);
void this.segmentStorage.storeSegment(request.segment, request.data);
this.eventHandlers?.onSegmentLoaded?.(request.data.byteLength, type);
this.onSegmentLoaded(request.data.byteLength, type);
break;

case "failed":
Expand Down
4 changes: 2 additions & 2 deletions packages/p2p-media-loader-core/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export type Settings = {
validateP2PSegment?: (url: string, byteRange?: ByteRange) => Promise<boolean>;
};

export type CoreEventHandlers = {
onSegmentLoaded?: (byteLength: number, type: RequestAttempt["type"]) => void;
export type CoreEventMap = {
onSegmentLoaded: (byteLength: number, type: RequestAttempt["type"]) => void;
};

export type Playback = {
Expand Down
61 changes: 61 additions & 0 deletions packages/p2p-media-loader-core/src/utils/event-emitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
export class EventEmitter<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
EventTypesMap extends { [key: string]: (...args: any[]) => unknown },
> {
private events = new Map<
keyof EventTypesMap,
EventTypesMap[keyof EventTypesMap][]
>();

public dispatchEvent<K extends keyof EventTypesMap>(
eventName: K,
...args: Parameters<EventTypesMap[K]>
) {
const listeners = this.events.get(eventName);
if (!listeners) return;
for (const listener of listeners) {
listener(...args);
}
}

public getEventDispatcher<K extends keyof EventTypesMap>(eventName: K) {
let listeners = this.events.get(eventName);
if (!listeners) {
listeners = [];
this.events.set(eventName, listeners);
}

const definedListeners = listeners;

return (...args: Parameters<EventTypesMap[K]>) => {
for (const listener of definedListeners) {
listener(...args);
}
};
}

public addEventListener<K extends keyof EventTypesMap>(
eventName: K,
listener: EventTypesMap[K],
) {
const listeners = this.events.get(eventName);
if (!listeners) {
this.events.set(eventName, [listener]);
} else {
listeners.push(listener);
}
}

public removeEventListener<K extends keyof EventTypesMap>(
eventName: K,
listener: EventTypesMap[K],
) {
const listeners = this.events.get(eventName);
if (listeners) {
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
}
}
}
20 changes: 17 additions & 3 deletions packages/p2p-media-loader-hlsjs/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,33 @@ import type { HlsConfig, Events } from "hls.js";
import { FragmentLoaderBase } from "./fragment-loader";
import { PlaylistLoaderBase } from "./playlist-loader";
import { SegmentManager } from "./segment-mananger";
import { Core, CoreEventHandlers } from "p2p-media-loader-core";
import { Core, CoreEventMap } from "p2p-media-loader-core";

export class Engine {
private readonly core: Core;
private readonly segmentManager: SegmentManager;
private hlsInstanceGetter?: () => Hls;
private currentHlsInstance?: Hls;

constructor(eventHandlers?: CoreEventHandlers) {
this.core = new Core(eventHandlers);
constructor() {
this.core = new Core();
this.segmentManager = new SegmentManager(this.core);
}

public addEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.core.addEventListener(eventName, listener);
}

public removeEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.core.removeEventListener(eventName, listener);
}

public getConfig(): Partial<HlsConfig> {
return {
fLoader: this.createFragmentLoaderClass(),
Expand Down
20 changes: 17 additions & 3 deletions packages/p2p-media-loader-shaka/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
P2PMLShakaData,
} from "./types";
import { Loader } from "./loading-handler";
import { Core, CoreEventHandlers } from "p2p-media-loader-core";
import { Core, CoreEventMap } from "p2p-media-loader-core";

const LIVE_EDGE_DELAY = 25;

Expand All @@ -25,9 +25,9 @@ export class Engine {
private readonly segmentManager: SegmentManager;
private requestFilter?: shaka.extern.RequestFilter;

constructor(shaka?: unknown, eventHandlers?: CoreEventHandlers) {
constructor(shaka?: unknown) {
this.shaka = (shaka as Shaka | undefined) ?? window.shaka;
this.core = new Core(eventHandlers);
this.core = new Core();
this.segmentManager = new SegmentManager(this.streamInfo, this.core);
}

Expand All @@ -44,6 +44,20 @@ export class Engine {
this.updatePlayerEventHandlers("register");
}

public addEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.core.addEventListener(eventName, listener);
}

public removeEventListener<K extends keyof CoreEventMap>(
eventName: K,
listener: CoreEventMap[K],
) {
this.core.removeEventListener(eventName, listener);
}

private updatePlayerEventHandlers = (type: "register" | "unregister") => {
const { player } = this;
if (!player) return;
Expand Down