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

Live stream delay. #318

Merged
merged 11 commits into from
Dec 27, 2023
39 changes: 15 additions & 24 deletions p2p-media-loader-demo/index.html
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite + React + TS</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/hls.js@latest/dist/hls.min.js"></script>
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/clappr@latest"
></script>
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/gh/clappr/clappr-level-selector-plugin@latest/dist/level-selector.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/npm/shaka-player@latest/dist/shaka-player.compiled.min.js"
></script>
<script
type="text/javascript"
src="https://cdn.jsdelivr.net/gh/clappr/dash-shaka-playback@latest/dist/dash-shaka-playback.external.js"
></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/clappr@latest"></script>
<script type="text/javascript"
src="https://cdn.jsdelivr.net/gh/clappr/clappr-level-selector-plugin@latest/dist/level-selector.min.js"></script>
<script type="text/javascript"
src="https://cdn.jsdelivr.net/npm/shaka-player@~4.6.0/dist/shaka-player.compiled.min.js"></script>
<script type="text/javascript"
src="https://cdn.jsdelivr.net/gh/clappr/dash-shaka-playback@latest/dist/dash-shaka-playback.external.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
30 changes: 15 additions & 15 deletions p2p-media-loader-demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,27 @@ const streamUrls = {
hlsBigBunnyBuck: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
hlsByteRangeVideo:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8",
hlsBasicExample:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
hlsAdvancedVideo:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/adv_dv_atmos/main.m3u8",
hlsAdvancedVideo2:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_adv_example_hevc/master.m3u8",
hlsAdvancedVideo3:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8",
hlsAdvancedVideo4:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8",
hlsBasicExample:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
hlsLive1:
"https://fcc3ddae59ed.us-west-2.playback.live-video.net/api/video/v1/us-west-2.893648527354.channel.DmumNckWFTqz.m3u8",
hlsLive2:
"https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8",
live2OnlyLevel4:
hlsLive2Level4Only:
"https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/level_4.m3u8",
audioOnly:
hlsAudioOnly:
"https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/a1/prog_index.m3u8",
bigBunnyBuckDash: "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd",
dashLiveBigBunnyBuck:
"https://livesim.dashif.org/livesim/testpic_2s/Manifest.mpd",
dashVODBigBunnyBuck:
"https://dash.akamaized.net/dash264/TestCases/5b/nomor/6.mpd",
dashLiveHokey:
"https://d24rwxnt7vw9qb.cloudfront.net/v1/dash/e6d234965645b411ad572802b6c9d5a10799c9c1/All_Reference_Streams/4577dca5f8a44756875ab5cc913cd1f1/index.mpd",
};

function App() {
Expand Down Expand Up @@ -141,10 +139,7 @@ function App() {
type: "customHls",
customType: {
customHls: (video: HTMLVideoElement) => {
const hls = new window.Hls({
...engine.getConfig(),
liveSyncDurationCount: 7,
});
const hls = new window.Hls(engine.getConfig());
engine.setHls(hls);
hls.loadSource(video.src);
hls.attachMedia(video);
Expand Down Expand Up @@ -197,7 +192,7 @@ function App() {
shakaPlayer.addEventListener("error", (event: { code: number }) => {
onError(event);
});
engine.initShakaPlayer(shakaPlayer);
engine.configureAndInitShakaPlayer(shakaPlayer);
shakaPlayer.load(src).catch(onError);

shakaInstance.current = shakaPlayer;
Expand All @@ -220,7 +215,7 @@ function App() {
player.addEventListener("error", (event: { detail: { code: unknown } }) => {
onError(event.detail);
});
engine.initShakaPlayer(player);
engine.configureAndInitShakaPlayer(player);
player.load(url).catch(onError);
shakaInstance.current = player;
setPlayerToWindow(player);
Expand All @@ -235,7 +230,7 @@ function App() {
source: url,
plugins: [window.DashShakaPlayback, window.LevelSelector],
shakaOnBeforeLoad: (shakaPlayerInstance: any) => {
engine.initShakaPlayer(shakaPlayerInstance);
engine.configureAndInitShakaPlayer(shakaPlayerInstance);
},
});
setPlayerToWindow(clapprPlayer);
Expand Down Expand Up @@ -302,6 +297,10 @@ function App() {
}
};

const createInNewTab = () => {
window.open(window.location.href, "_blank");
};

return (
<div style={{ width: 1000, margin: "auto" }}>
<div style={{ textAlign: "center" }}>
Expand Down Expand Up @@ -338,6 +337,7 @@ function App() {
<button onClick={loadStreamWithExistingInstance}>
Load stream with existing hls/shaka instance
</button>
<button onClick={createInNewTab}>Create in new tab</button>
</div>
</div>
<div style={{ display: "flex", justifyContent: "center" }}>
Expand Down
7 changes: 6 additions & 1 deletion packages/p2p-media-loader-core/src/hybrid-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,11 @@ export class HybridLoader {
if (statuses.isHighDemand) {
if (request?.type === "http" && request.status === "loading") continue;

const isP2PLoadingRequest =
request?.status === "loading" && request.type === "p2p";

if (this.requests.executingHttpCount < simultaneousHttpDownloads) {
if (isP2PLoadingRequest) request.abortFromEngine();
void this.loadThroughHttp(segment);
continue;
}
Expand All @@ -196,11 +200,12 @@ export class HybridLoader {
this.abortLastHttpLoadingInQueueAfterItem(queue, segment) &&
this.requests.executingHttpCount < simultaneousHttpDownloads
) {
if (isP2PLoadingRequest) request.abortFromEngine();
void this.loadThroughHttp(segment);
continue;
}

if (request?.type === "p2p" && request.status === "loading") continue;
if (isP2PLoadingRequest) continue;

if (this.requests.executingP2PCount < simultaneousP2PDownloads) {
void this.loadThroughP2P(segment);
Expand Down
4 changes: 3 additions & 1 deletion packages/p2p-media-loader-core/src/p2p/tracker-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ export class P2PTrackerClient {
},
this.settings
);
this.logger(`connected with peer: ${peerItem.peer.id}`);
this.logger(
`connected with peer: ${peerItem.peer.id} ${this.streamShortId}`
);
this.eventHandlers.onPeerConnected(peerItem.peer);
});
};
Expand Down
5 changes: 5 additions & 0 deletions packages/p2p-media-loader-core/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ export class Request {
abortFromProcessQueue() {
this.throwErrorIfNotLoadingStatus();
this._status = "aborted";
this.logger(
`${this.currentAttempt?.type} ${this.segment.externalId} aborted`
);
this._abortRequestCallback?.("abort");
this._abortRequestCallback = undefined;
this.currentAttempt = undefined;
Expand All @@ -205,6 +208,7 @@ export class Request {
this._status = "failed";
const error = new RequestError("bytes-receiving-timeout");
this._abortRequestCallback?.(error.type);
this.logger(`${this.type} ${this.segment.externalId} failed ${error.type}`);

this.currentAttempt.error = error;
this._failedAttempts.push(this.currentAttempt);
Expand All @@ -217,6 +221,7 @@ export class Request {
if (!this.currentAttempt) return;

this._status = "failed";
this.logger(`${this.type} ${this.segment.externalId} failed ${error.type}`);
this.currentAttempt.error = error;
this._failedAttempts.push(this.currentAttempt);
this.notReceivingBytesTimeout.clear();
Expand Down
7 changes: 0 additions & 7 deletions packages/p2p-media-loader-core/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Segment, Stream } from "../types";
import { QueueItem } from "./queue";
import { SegmentPlaybackStatuses } from "./stream";

export function getStreamString(stream: Stream) {
Expand All @@ -21,9 +20,3 @@ export function getSegmentPlaybackStatusesString(
if (isP2PDownloadable) return "p2p-window";
return "-";
}

export function getQueueItemString(item: QueueItem) {
const { segment, statuses } = item;
const statusString = getSegmentPlaybackStatusesString(statuses);
return `${segment.externalId} ${statusString}`;
}
16 changes: 12 additions & 4 deletions packages/p2p-media-loader-core/src/utils/stream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,25 @@ export function getSegmentPlaybackStatuses(
} = timeWindowsSettings;

return {
isHighDemand: isInTimeWindow(segment, playback, highDemandTimeWindow),
isHttpDownloadable: isInTimeWindow(
isHighDemand: isSegmentInTimeWindow(
segment,
playback,
highDemandTimeWindow
),
isHttpDownloadable: isSegmentInTimeWindow(
segment,
playback,
httpDownloadTimeWindow
),
isP2PDownloadable: isInTimeWindow(segment, playback, p2pDownloadTimeWindow),
isP2PDownloadable: isSegmentInTimeWindow(
segment,
playback,
p2pDownloadTimeWindow
),
};
}

function isInTimeWindow(
function isSegmentInTimeWindow(
segment: Segment,
playback: Playback,
timeWindowLength: number
Expand Down
6 changes: 4 additions & 2 deletions packages/p2p-media-loader-hlsjs/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { PlaylistLoaderBase } from "./playlist-loader";
import { SegmentManager } from "./segment-mananger";
import { Core, CoreEventHandlers } from "p2p-media-loader-core";

const LIVE_EDGE_DELAY = 25;

export class Engine {
private readonly core: Core;
private readonly segmentManager: SegmentManager;
Expand All @@ -23,12 +25,12 @@ export class Engine {

public getConfig(): Pick<
HlsConfig,
"fLoader" | "pLoader" | "liveSyncDurationCount"
"fLoader" | "pLoader" | "liveSyncDuration"
> {
return {
liveSyncDurationCount: 7,
fLoader: this.createFragmentLoaderClass(),
pLoader: this.createPlaylistLoaderClass(),
liveSyncDuration: LIVE_EDGE_DELAY,
};
}

Expand Down
20 changes: 11 additions & 9 deletions packages/p2p-media-loader-shaka/src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
import { Loader } from "./loading-handler";
import { Core, CoreEventHandlers } from "p2p-media-loader-core";

const LIVE_EDGE_DELAY = 25;

export class Engine {
private player?: shaka.Player;
private readonly shaka: Shaka;
Expand All @@ -29,20 +31,24 @@ export class Engine {
this.segmentManager = new SegmentManager(this.streamInfo, this.core);
}

initShakaPlayer(player: shaka.Player) {
configureAndInitShakaPlayer(player: shaka.Player) {
if (this.player === player) return;
if (this.player) this.destroy();
this.player = player;
this.player.configure("manifest.defaultPresentationDelay", LIVE_EDGE_DELAY);
this.player.configure(
"manifest.dash.ignoreSuggestedPresentationDelay",
true
);
this.updatePlayerEventHandlers("register");
}

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

if (!this.player) return;
const networkingEngine =
this.player.getNetworkingEngine() as HookedNetworkingEngine | null;
player.getNetworkingEngine() as HookedNetworkingEngine | null;
if (networkingEngine) {
if (type === "register") {
const p2pml: P2PMLShakaData = {
Expand Down Expand Up @@ -78,6 +84,7 @@ export class Engine {
this.destroyCurrentStreamContext();
this.updateMediaElementEventHandlers("unregister");
};

private destroyCurrentStreamContext = () => {
this.streamInfo.protocol = undefined;
this.streamInfo.manifestResponseUrl = undefined;
Expand Down Expand Up @@ -140,12 +147,7 @@ export class Engine {
const { p2pml } = request;
if (!p2pml) return shaka.net.HttpFetchPlugin.parse(...args);

const loader = new Loader(
p2pml.shaka,
p2pml.core,
p2pml.streamInfo,
p2pml.segmentManager
);
const loader = new Loader(p2pml.shaka, p2pml.core, p2pml.streamInfo);
return loader.load(...args);
};
NetworkingEngine.registerScheme("http", handleLoading);
Expand Down
4 changes: 1 addition & 3 deletions packages/p2p-media-loader-shaka/src/loading-handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as Utils from "./stream-utils";
import { SegmentManager } from "./segment-manager";
import { StreamInfo } from "./types";
import { Shaka, Stream } from "./types";
import {
Expand All @@ -19,8 +18,7 @@ export class Loader {
constructor(
private readonly shaka: Shaka,
private readonly core: Core<Stream>,
readonly streamInfo: StreamInfo,
private readonly segmentManager: SegmentManager
readonly streamInfo: StreamInfo
) {}

private defaultLoad() {
Expand Down
Loading