p2p-media-loader-core
Version:
P2P Media Loader core functionality
106 lines • 4.82 kB
JavaScript
import { sha1 } from "./hash.js";
const PEER_PROTOCOL_VERSION = "v2";
/**
* Generates a stable, unique string ID for a stream based on its properties.
* Uses a SHA-1 hash to avoid collisions and encodes the result to standard Base64.
*/
export function generateStreamShortId({ bitrate, codecs, width, height, language, channels, name, frameRate, videoRange, }) {
const normalizedCodecs = codecs
? codecs
.split(",")
.map((c) => {
c = c.trim().toLowerCase();
// Normalize decimal RFC 4281 avc1 codecs to hex (e.g., avc1.66.30 -> avc1.42001e)
const parts = c.split(".");
if (parts.length === 3 &&
(parts[0] === "avc1" || parts[0] === "avc")) {
const profile = parseInt(parts[1], 10);
const level = parseInt(parts[2], 10);
if (!isNaN(profile) &&
!isNaN(level) &&
parts[1] === profile.toString() &&
parts[2] === level.toString()) {
const profileHex = `00${profile.toString(16)}`.slice(-2);
const levelHex = `00${level.toString(16)}`.slice(-2);
c = `${parts[0]}.${profileHex}00${levelHex}`;
}
}
return c;
})
.sort()
.join(",")
: "";
const normalizedLanguage = language && language !== "und" ? language.slice(0, 2).toLowerCase() : "";
const normalizedChannels = channels ? channels.toString().split("/")[0] : "";
const normalizedName = name ? name.toLowerCase().trim() : "";
// Normalize frame rates to eliminate trailing zeros (e.g. "30.000" -> "30")
const normalizedFrameRate = frameRate && !isNaN(Number(frameRate)) ? Number(frameRate).toString() : "";
const normalizedVideoRange = videoRange
? videoRange.toUpperCase().trim()
: "";
const str = `${bitrate !== null && bitrate !== void 0 ? bitrate : 0}-${normalizedCodecs}-${width !== null && width !== void 0 ? width : ""}-${height !== null && height !== void 0 ? height : ""}-${normalizedLanguage}-${normalizedChannels}-${normalizedName}-${normalizedFrameRate}-${normalizedVideoRange}`;
return btoa(sha1(str));
}
export function getStreamSwarmId(swarmId, stream) {
return `${PEER_PROTOCOL_VERSION}-${swarmId}-${getStreamId(stream)}`;
}
export function getSegmentFromStreamsMap(streams, segmentRuntimeId) {
for (const stream of streams.values()) {
const segment = stream.segments.get(segmentRuntimeId);
if (segment)
return segment;
}
}
export function getSegmentFromStreamByExternalId(stream, segmentExternalId) {
for (const segment of stream.segments.values()) {
if (segment.externalId === segmentExternalId)
return segment;
}
}
export function getStreamId(stream) {
return `${stream.type}-${stream.index}`;
}
export function getSegmentAvgDuration(stream) {
const { segments } = stream;
let sumDuration = 0;
const { size } = segments;
if (size === 0)
return 0;
for (const segment of segments.values()) {
const duration = segment.endTime - segment.startTime;
sumDuration += duration;
}
return sumDuration / size;
}
function calculateTimeWindows(timeWindowsConfig, availableMemoryInPercent) {
const { highDemandTimeWindow, httpDownloadTimeWindow, p2pDownloadTimeWindow, } = timeWindowsConfig;
const result = {
highDemandTimeWindow,
httpDownloadTimeWindow,
p2pDownloadTimeWindow,
};
if (availableMemoryInPercent <= 5) {
result.httpDownloadTimeWindow = 0;
result.p2pDownloadTimeWindow = 0;
}
else if (availableMemoryInPercent <= 10) {
result.p2pDownloadTimeWindow = result.httpDownloadTimeWindow;
}
return result;
}
export function getSegmentPlaybackStatuses(segment, playback, timeWindowsConfig, currentP2PLoader, availableMemoryPercent) {
const { highDemandTimeWindow, httpDownloadTimeWindow, p2pDownloadTimeWindow, } = calculateTimeWindows(timeWindowsConfig, availableMemoryPercent);
return {
isHighDemand: isSegmentInTimeWindow(segment, playback, highDemandTimeWindow),
isHttpDownloadable: isSegmentInTimeWindow(segment, playback, httpDownloadTimeWindow),
isP2PDownloadable: isSegmentInTimeWindow(segment, playback, p2pDownloadTimeWindow) &&
currentP2PLoader.isSegmentLoadingOrLoadedBySomeone(segment),
};
}
function isSegmentInTimeWindow(segment, playback, timeWindowLength) {
const { startTime, endTime } = segment;
const { position, rate } = playback;
const rightMargin = position + timeWindowLength * rate;
return !(rightMargin < startTime || position > endTime);
}
//# sourceMappingURL=stream.js.map