Skip to content

Commit

Permalink
Specific configuration for each separate shaka player instance. (#307)
Browse files Browse the repository at this point in the history
* Add ability to create multiple shaka p2p-players despite the global shared library configuration.

* Remove unnecessary p2pml assignment.
  • Loading branch information
i-zolotarenko authored Sep 26, 2023
1 parent 945e1dc commit 96c9882
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 65 deletions.
52 changes: 36 additions & 16 deletions packages/p2p-media-loader-shaka/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import {
} from "./manifest-parser-decorator";
import { SegmentManager } from "./segment-manager";
import Debug from "debug";
import { StreamInfo, StreamProtocol, Shaka, Stream } from "./types";
import {
StreamInfo,
Shaka,
Stream,
HookedNetworkingEngine,
HookedRequest,
P2PMLShakaData,
} from "./types";
import { LoadingHandler } from "./loading-handler";
import { decorateMethod } from "./utils";
import { Core } from "p2p-media-loader-core";

export class Engine {
private readonly shaka: Shaka;
private player!: shaka.Player;
private readonly streamInfo: StreamInfo = {};
private readonly core = new Core<Stream>();
private readonly segmentManager = new SegmentManager(
Expand All @@ -26,7 +32,21 @@ export class Engine {
}

initShakaPlayer(player: shaka.Player) {
this.player = player;
const networkingEngine =
player.getNetworkingEngine() as HookedNetworkingEngine | null;
if (networkingEngine) {
const p2pml: P2PMLShakaData = {
shaka: this.shaka,
core: this.core,
streamInfo: this.streamInfo,
segmentManager: this.segmentManager,
};
networkingEngine.p2pml = p2pml;
networkingEngine.registerRequestFilter((requestType, request) => {
(request as HookedRequest).p2pml = p2pml;
});
}

this.initializeNetworkingEngine();
this.registerParsers();

Expand Down Expand Up @@ -64,13 +84,8 @@ export class Engine {
}

private registerParsers() {
const setProtocol = (protocol: StreamProtocol) => {
this.streamInfo.protocol = protocol;
};
const hlsParserFactory = () =>
new HlsManifestParser(this.shaka, this.segmentManager, setProtocol);
const dashParserFactory = () =>
new DashManifestParser(this.shaka, this.segmentManager, setProtocol);
const hlsParserFactory = () => new HlsManifestParser(this.shaka);
const dashParserFactory = () => new DashManifestParser(this.shaka);
this.shaka.media.ManifestParser.registerParserByExtension(
"mpd",
dashParserFactory
Expand All @@ -95,14 +110,19 @@ export class Engine {

private initializeNetworkingEngine() {
const handleLoading: shaka.extern.SchemePlugin = (...args) => {
const loadingHandler = new LoadingHandler({
shaka: this.shaka,
streamInfo: this.streamInfo,
segmentManager: this.segmentManager,
core: this.core,
});
const request = args[1] as HookedRequest;
const { p2pml } = request;
if (!p2pml) return this.shaka.net.HttpFetchPlugin.parse(...args);

const loadingHandler = new LoadingHandler(
p2pml.shaka,
p2pml.core,
p2pml.streamInfo,
p2pml.segmentManager
);
return loadingHandler.handleLoading(...args);
};

this.shaka.net.NetworkingEngine.registerScheme("http", handleLoading);
this.shaka.net.NetworkingEngine.registerScheme("https", handleLoading);
}
Expand Down
26 changes: 6 additions & 20 deletions packages/p2p-media-loader-shaka/src/loading-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,14 @@ type Response = shaka.extern.Response;
type LoadingHandlerResult = shaka.extern.IAbortableOperation<Response>;

export class LoadingHandler implements LoadingHandlerInterface {
private readonly shaka: Shaka;
private readonly segmentManager: SegmentManager;
private readonly core: Core<Stream>;
readonly streamInfo: StreamInfo;
private loadArgs!: LoadingHandlerParams;

constructor({
shaka,
streamInfo,
core,
segmentManager,
}: {
shaka: Shaka;
streamInfo: Readonly<StreamInfo>;
core: Core<Stream>;
segmentManager: SegmentManager;
}) {
this.shaka = shaka;
this.streamInfo = streamInfo;
this.core = core;
this.segmentManager = segmentManager;
}
constructor(
private readonly shaka: Shaka,
private readonly core: Core<Stream>,
readonly streamInfo: StreamInfo,
private readonly segmentManager: SegmentManager
) {}

private defaultLoad() {
const fetchPlugin = this.shaka.net.HttpFetchPlugin;
Expand Down
62 changes: 34 additions & 28 deletions packages/p2p-media-loader-shaka/src/manifest-parser-decorator.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
import { SegmentManager } from "./segment-manager";
import Debug from "debug";
import { HookedStream, StreamProtocol, Shaka } from "./types";
import {
HookedStream,
Shaka,
HookedNetworkingEngine,
P2PMLShakaData,
} from "./types";
import { StreamType } from "p2p-media-loader-core";
import * as Utils from "./stream-utils";

export class ManifestParserDecorator implements shaka.extern.ManifestParser {
private readonly originalManifestParser: shaka.extern.ManifestParser;
private readonly segmentManager: SegmentManager;
private readonly debug = Debug("p2pml-shaka:manifest-parser");
private readonly isHLS: boolean;
private readonly isDash: boolean;
private segmentManager?: SegmentManager;

constructor(
originalManifestParser: shaka.extern.ManifestParser,
segmentManager: SegmentManager,
protocol: StreamProtocol
shaka: Readonly<Shaka>,
private readonly originalManifestParser: shaka.extern.ManifestParser
) {
this.originalManifestParser = originalManifestParser;
this.segmentManager = segmentManager;

this.isHLS = protocol === "hls";
this.isDash = protocol === "dash";
this.isHLS = this.originalManifestParser instanceof shaka.hls.HlsParser;
this.isDash = this.originalManifestParser instanceof shaka.dash.DashParser;
}

configure(config: shaka.extern.ManifestConfiguration) {
return this.originalManifestParser.configure(config);
}

private setP2PMediaLoaderData(p2pml?: P2PMLShakaData) {
if (!p2pml) return;
this.segmentManager = p2pml.segmentManager;
p2pml.streamInfo.protocol = this.isHLS ? "hls" : "dash";
}

async start(
uri: string,
playerInterface: shaka.extern.ManifestParser.PlayerInterface
): Promise<shaka.extern.Manifest> {
const { p2pml } =
playerInterface.networkingEngine as HookedNetworkingEngine;
this.setP2PMediaLoaderData(p2pml);
const manifest = await this.originalManifestParser.start(
uri,
playerInterface
);
if (!p2pml) return manifest;

if (this.isHLS) {
const success = this.retrieveStreamMediaSequenceTimeMaps(
manifest.variants
Expand Down Expand Up @@ -62,15 +73,17 @@ export class ManifestParserDecorator implements shaka.extern.ManifestParser {
}

private processStreams(variants: shaka.extern.Variant[]) {
const processedStreams = new Set<number>();
const { segmentManager } = this;
if (!segmentManager) return;

const processedStreams = new Set<number>();
const processStream = (
stream: shaka.extern.Stream,
type: StreamType,
order: number
) => {
if (this.isDash) this.hookSegmentIndex(stream);
this.segmentManager.setStream(stream as HookedStream, type, order);
segmentManager.setStream(stream as HookedStream, type, order);
processedStreams.add(stream.id);
return true;
};
Expand All @@ -90,6 +103,9 @@ export class ManifestParserDecorator implements shaka.extern.ManifestParser {
}

private hookSegmentIndex(stream: shaka.extern.Stream): void {
const { segmentManager } = this;
if (!segmentManager) return;

const createSegmentIndexOriginal = stream.createSegmentIndex;
stream.createSegmentIndex = async () => {
const result = await createSegmentIndexOriginal.call(stream);
Expand Down Expand Up @@ -130,7 +146,7 @@ export class ManifestParserDecorator implements shaka.extern.ManifestParser {
stream,
this.isHLS
);
this.segmentManager.updateStreamSegments(streamLocalId, references);
segmentManager.updateStreamSegments(streamLocalId, references);
this.debug(`Stream ${streamLocalId} is updated`);
prevFirstItemReference = firstItemReference;
prevLastItemReference = lastItemReference;
Expand Down Expand Up @@ -234,23 +250,13 @@ export class ManifestParserDecorator implements shaka.extern.ManifestParser {
}

export class HlsManifestParser extends ManifestParserDecorator {
public constructor(
shaka: Shaka,
segmentManager: SegmentManager,
setProtocol: (protocol: StreamProtocol) => void
) {
super(new shaka.hls.HlsParser(), segmentManager, "hls");
setProtocol("hls");
public constructor(shaka: Shaka) {
super(shaka, new shaka.hls.HlsParser());
}
}

export class DashManifestParser extends ManifestParserDecorator {
public constructor(
shaka: Shaka,
segmentsManager: SegmentManager,
setProtocol: (protocol: StreamProtocol) => void
) {
super(new shaka.dash.DashParser(), segmentsManager, "dash");
setProtocol("dash");
public constructor(shaka: Shaka) {
super(shaka, new shaka.dash.DashParser());
}
}
18 changes: 17 additions & 1 deletion packages/p2p-media-loader-shaka/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Stream as CoreStream } from "p2p-media-loader-core";
import type { Stream as CoreStream, Core } from "p2p-media-loader-core";
import { SegmentManager } from "./segment-manager";

export type StreamProtocol = "hls" | "dash";

Expand All @@ -17,3 +18,18 @@ export type Stream = CoreStream & {
};

export type Shaka = typeof window.shaka;

type P2PMLShakaData = {
core: Core<Stream>;
shaka: Shaka;
streamInfo: StreamInfo;
segmentManager: SegmentManager;
};

export type HookedRequest = shaka.extern.Request & {
p2pml?: P2PMLShakaData;
};

export type HookedNetworkingEngine = shaka.net.NetworkingEngine & {
p2pml?: P2PMLShakaData;
};

0 comments on commit 96c9882

Please sign in to comment.