media-stream-player
Version:
Player built on top of media-stream-library
1,539 lines (1,500 loc) • 76.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/MediaStreamPlayer.tsx
import React22, { useEffect as useEffect12, useMemo as useMemo3, useState as useState12 } from "react";
import { createRoot } from "react-dom/client";
// src/BasicPlayer.tsx
import React18, {
forwardRef,
useCallback as useCallback4,
useEffect as useEffect9,
useLayoutEffect,
useMemo,
useRef as useRef7,
useState as useState8
} from "react";
// src/Container.tsx
import React from "react";
import styled from "styled-components";
var DEFAULT_ASPECT_RATIO = 16 / 9;
var getHeightPct = (aspectRatio) => {
if (aspectRatio === 0) {
throw new Error("Cannot handle aspect ratio 0");
}
return 100 / aspectRatio;
};
var ContainerBody = styled.div.attrs(
({ aspectRatio }) => {
return { style: { paddingTop: `${getHeightPct(aspectRatio)}%` } };
}
)`
width: 100%;
background: black;
position: relative;
`;
var Layer = styled.div`
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
`;
var Container = ({
aspectRatio = DEFAULT_ASPECT_RATIO,
children
}) => /* @__PURE__ */ React.createElement(ContainerBody, { aspectRatio }, children);
// src/Controls.tsx
import React12, { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
import { DateTime, Duration } from "luxon";
import styled5 from "styled-components";
// src/Settings.tsx
import React3, { useCallback, useRef, useState } from "react";
import styled3 from "styled-components";
// src/components/Switch.tsx
import React2 from "react";
import styled2 from "styled-components";
var Container2 = styled2.label`
position: relative;
display: inline-block;
width: 28px;
height: 16px;
`;
var Input = styled2.input`
opacity: 0;
width: 0;
height: 0;
`;
var Slider = styled2.span`
border-radius: 16px;
cursor: pointer;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #ccc;
transition: 0.4s;
&:before {
border-radius: 50%;
content: '';
position: absolute;
height: 12px;
width: 12px;
left: 2px;
bottom: 2px;
background-color: white;
transition: 0.4s;
}
${Input}:checked + & {
background-color: #2196f3;
}
${Input}:checked + &:before {
transform: translateX(12px);
}
${Input}:focus + & {
box-shadow: 0 0 1px #2196f3;
}
`;
var Switch = (props) => {
return /* @__PURE__ */ React2.createElement(Container2, null, /* @__PURE__ */ React2.createElement(Input, __spreadValues({ type: "checkbox" }, props)), /* @__PURE__ */ React2.createElement(Slider, null));
};
// src/Settings.tsx
var SettingsMenu = styled3.div`
font-family: sans-serif;
display: flex;
flex-direction: column;
position: absolute;
bottom: 32px;
right: 0;
background: rgb(0, 0, 0, 0.66);
padding: 8px 16px;
margin-bottom: 16px;
margin-right: 8px;
&:after {
content: '';
width: 10px;
height: 10px;
transform: rotate(45deg);
position: absolute;
bottom: -5px;
right: 12px;
background: rgb(0, 0, 0, 0.66);
}
`;
var SettingsItem = styled3.div`
display: flex;
flex-direction: row;
color: white;
height: 24px;
width: 320px;
align-items: center;
justify-content: space-between;
margin: 4px 0;
`;
var Settings = ({
parameters,
format,
onFormat,
onVapix,
showStatsOverlay,
toggleStats
}) => {
const [textString, setTextString] = useState(parameters["textstring"]);
const textStringTimeout = useRef();
const changeParam = useCallback(
(e) => {
const { name, value } = e.target;
switch (name) {
case "textstring":
setTextString(value);
clearTimeout(textStringTimeout.current);
textStringTimeout.current = window.setTimeout(() => {
onVapix(name, value);
}, 300);
break;
case "text":
onVapix(name, value ? "1" : "0");
break;
default:
console.warn("internal error");
}
},
[onVapix]
);
const changeStatsOverlay = useCallback(
(e) => toggleStats(e.target.checked),
[toggleStats]
);
const changeFormat = useCallback(
(e) => onFormat(e.target.value),
[onFormat]
);
const changeResolution = useCallback(
(e) => onVapix("resolution", e.target.value),
[onVapix]
);
const changeRotation = useCallback(
(e) => onVapix("rotation", e.target.value),
[onVapix]
);
const changeCompression = useCallback(
(e) => onVapix("compression", e.target.value),
[onVapix]
);
return /* @__PURE__ */ React3.createElement(SettingsMenu, null, /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Format"), /* @__PURE__ */ React3.createElement("select", { onChange: changeFormat, defaultValue: format }, /* @__PURE__ */ React3.createElement("option", { value: "RTP_H264" }, "H.264 (RTP over WS)"), /* @__PURE__ */ React3.createElement("option", { value: "MP4_H264" }, "H.264 (MP4 over HTTP)"), /* @__PURE__ */ React3.createElement("option", { value: "RTP_JPEG" }, "Motion JPEG"), /* @__PURE__ */ React3.createElement("option", { value: "JPEG" }, "Still image"))), /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Resolution"), /* @__PURE__ */ React3.createElement("select", { value: parameters["resolution"], onChange: changeResolution }, /* @__PURE__ */ React3.createElement("option", { value: "" }, "default"), /* @__PURE__ */ React3.createElement("option", { value: "1920x1080" }, "1920 x 1080 (FHD)"), /* @__PURE__ */ React3.createElement("option", { value: "1280x720" }, "1280 x 720 (HD)"), /* @__PURE__ */ React3.createElement("option", { value: "800x600" }, "800 x 600 (VGA)"))), /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Rotation"), /* @__PURE__ */ React3.createElement("select", { value: parameters["rotation"], onChange: changeRotation }, /* @__PURE__ */ React3.createElement("option", { value: "0" }, "0"), /* @__PURE__ */ React3.createElement("option", { value: "90" }, "90"), /* @__PURE__ */ React3.createElement("option", { value: "180" }, "180"), /* @__PURE__ */ React3.createElement("option", { value: "270" }, "270"))), /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Compression"), /* @__PURE__ */ React3.createElement("select", { value: parameters["compression"], onChange: changeCompression }, /* @__PURE__ */ React3.createElement("option", { value: "" }, "default"), /* @__PURE__ */ React3.createElement("option", { value: "0" }, "0"), /* @__PURE__ */ React3.createElement("option", { value: "10" }, "10"), /* @__PURE__ */ React3.createElement("option", { value: "20" }, "20"), /* @__PURE__ */ React3.createElement("option", { value: "30" }, "30"), /* @__PURE__ */ React3.createElement("option", { value: "40" }, "40"), /* @__PURE__ */ React3.createElement("option", { value: "50" }, "50"), /* @__PURE__ */ React3.createElement("option", { value: "60" }, "60"), /* @__PURE__ */ React3.createElement("option", { value: "70" }, "70"), /* @__PURE__ */ React3.createElement("option", { value: "80" }, "80"), /* @__PURE__ */ React3.createElement("option", { value: "90" }, "90"), /* @__PURE__ */ React3.createElement("option", { value: "100" }, "100"))), /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Text overlay"), /* @__PURE__ */ React3.createElement("input", { name: "textstring", value: textString, onChange: changeParam }), /* @__PURE__ */ React3.createElement(
Switch,
{
name: "text",
checked: parameters["text"] === "1",
onChange: changeParam
}
)), /* @__PURE__ */ React3.createElement(SettingsItem, null, /* @__PURE__ */ React3.createElement("div", null, "Stats overlay"), /* @__PURE__ */ React3.createElement(Switch, { checked: showStatsOverlay, onChange: changeStatsOverlay })));
};
// src/components/Button.tsx
import styled4 from "styled-components";
var Button = styled4.button`
background: transparent;
fill: white;
border: none;
box-sizing: border-box;
padding: 0;
margin: 0;
line-height: 0;
:focus {
outline: none;
}
`;
// src/hooks/useUserActive.ts
import { useEffect, useState as useState2 } from "react";
var DEFAULT_TIMEOUT = 3e3;
var useUserActive = (ref, duration = DEFAULT_TIMEOUT) => {
const [userActive, setUserActive] = useState2(false);
const startUserActive = () => setUserActive(true);
const stopUserActive = () => setUserActive(false);
useEffect(() => {
if (userActive) {
const timer = setTimeout(stopUserActive, duration);
return () => {
clearTimeout(timer);
};
}
});
useEffect(() => {
const el = ref.current;
if (el === null) {
return;
}
el.addEventListener("pointermove", startUserActive);
if (userActive) {
el.addEventListener("pointerleave", stopUserActive);
}
return () => {
el.removeEventListener("pointermove", startUserActive);
if (userActive) {
el.removeEventListener("pointerleave", stopUserActive);
}
};
}, [userActive, ref]);
return userActive;
};
// src/img/CogWheel.tsx
import React4 from "react";
var CogWheel = ({ title }) => {
return /* @__PURE__ */ React4.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "20",
height: "20",
viewBox: "0 0 20 20"
},
title !== void 0 ? /* @__PURE__ */ React4.createElement("title", null, title) : null,
/* @__PURE__ */ React4.createElement("path", { fill: "none", d: "M0 0h20v20H0V0z" }),
/* @__PURE__ */ React4.createElement("path", { d: "M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z" })
);
};
// src/img/Pause.tsx
import React5 from "react";
var Pause = ({ title }) => {
return /* @__PURE__ */ React5.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
},
title !== void 0 ? /* @__PURE__ */ React5.createElement("title", null, title) : null,
/* @__PURE__ */ React5.createElement("path", { d: "M6 19h4V5H6v14zm8-14v14h4V5h-4z" }),
/* @__PURE__ */ React5.createElement("path", { d: "M0 0h24v24H0z", fill: "none" })
);
};
// src/img/Play.tsx
import React6 from "react";
var Play = ({ title }) => {
return /* @__PURE__ */ React6.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
},
title !== void 0 ? /* @__PURE__ */ React6.createElement("title", null, title) : null,
/* @__PURE__ */ React6.createElement("path", { d: "M8 5v14l11-7z" }),
/* @__PURE__ */ React6.createElement("path", { d: "M0 0h24v24H0z", fill: "none" })
);
};
// src/img/Refresh.tsx
import React7 from "react";
var Refresh = ({ title }) => {
return /* @__PURE__ */ React7.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
height: "24",
viewBox: "0 0 24 24",
width: "24"
},
title !== void 0 ? /* @__PURE__ */ React7.createElement("title", null, title) : null,
/* @__PURE__ */ React7.createElement("path", { d: "M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" }),
/* @__PURE__ */ React7.createElement("path", { d: "M0 0h24v24H0z", fill: "none" })
);
};
// src/img/Screenshot.tsx
import React8 from "react";
var Screenshot = ({ title }) => {
return /* @__PURE__ */ React8.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
},
title !== void 0 ? /* @__PURE__ */ React8.createElement("title", null, title) : null,
/* @__PURE__ */ React8.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }),
/* @__PURE__ */ React8.createElement("circle", { cx: "12", cy: "12", r: "3.2" }),
/* @__PURE__ */ React8.createElement("path", { d: "M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" })
);
};
// src/img/Spinner.tsx
import React9 from "react";
var Spinner = () => {
return /* @__PURE__ */ React9.createElement(
"svg",
{
width: "38",
height: "38",
viewBox: "0 0 38 38",
xmlns: "http://www.w3.org/2000/svg"
},
/* @__PURE__ */ React9.createElement("defs", null, /* @__PURE__ */ React9.createElement("linearGradient", { x1: "8.042%", y1: "0%", x2: "65.682%", y2: "23.865%", id: "a" }, /* @__PURE__ */ React9.createElement("stop", { stopColor: "#fff", stopOpacity: "0", offset: "0%" }), /* @__PURE__ */ React9.createElement("stop", { stopColor: "#fff", stopOpacity: ".631", offset: "63.146%" }), /* @__PURE__ */ React9.createElement("stop", { stopColor: "#fff", offset: "100%" }))),
/* @__PURE__ */ React9.createElement("g", { fill: "none", fillRule: "evenodd" }, /* @__PURE__ */ React9.createElement("g", { transform: "translate(1 1)" }, /* @__PURE__ */ React9.createElement(
"path",
{
d: "M36 18c0-9.94-8.06-18-18-18",
id: "Oval-2",
stroke: "url(#a)",
strokeWidth: "2"
},
/* @__PURE__ */ React9.createElement(
"animateTransform",
{
attributeName: "transform",
type: "rotate",
from: "0 18 18",
to: "360 18 18",
dur: "0.9s",
repeatCount: "indefinite"
}
)
), /* @__PURE__ */ React9.createElement("circle", { fill: "#fff", cx: "36", cy: "18", r: "1" }, /* @__PURE__ */ React9.createElement(
"animateTransform",
{
attributeName: "transform",
type: "rotate",
from: "0 18 18",
to: "360 18 18",
dur: "0.9s",
repeatCount: "indefinite"
}
))))
);
};
// src/img/Stop.tsx
import React10 from "react";
var Stop = ({ title }) => {
return /* @__PURE__ */ React10.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
},
title !== void 0 ? /* @__PURE__ */ React10.createElement("title", null, title) : null,
/* @__PURE__ */ React10.createElement("path", { d: "M0 0h24v24H0z", fill: "none" }),
/* @__PURE__ */ React10.createElement("path", { d: "M6 6h12v12H6z" })
);
};
// src/img/StreamStats.tsx
import React11 from "react";
var StreamStats = ({ title }) => {
return /* @__PURE__ */ React11.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24" }, title !== void 0 ? /* @__PURE__ */ React11.createElement("title", null, title) : null, /* @__PURE__ */ React11.createElement("path", { d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z" }));
};
// src/Controls.tsx
function isHTMLMediaElement(el) {
return el.buffered !== void 0;
}
var ControlArea = styled5.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
opacity: ${({ visible }) => visible ? 1 : 0};
transition: opacity 0.3s ease-in-out;
`;
var ControlBar = styled5.div`
width: 100%;
height: 32px;
background: rgb(0, 0, 0, 0.66);
display: flex;
align-items: center;
padding: 0 16px;
box-sizing: border-box;
`;
var VolumeContainer = styled5.div`
margin-left: 8px;
`;
var Progress = styled5.div`
flex-grow: 2;
padding: 0 32px;
display: flex;
align-items: center;
`;
var ProgressBarContainer = styled5.div`
margin: 0;
width: 100%;
height: 24px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
`;
var ProgressBar = styled5.div`
background-color: rgba(255, 255, 255, 0.1);
height: 1px;
position: relative;
width: 100%;
${ProgressBarContainer}:hover > & {
height: 3px;
}
`;
var ProgressBarPlayed = styled5.div.attrs(
({ fraction }) => {
return {
style: { transform: `scaleX(${fraction})` }
};
}
)`
background-color: rgb(240, 180, 0);
height: 100%;
position: absolute;
top: 0;
transform: scaleX(0);
transform-origin: 0 0;
width: 100%;
`;
var ProgressBarBuffered = styled5.div.attrs(
({ fraction }) => {
return {
style: { transform: `scaleX(${fraction})` }
};
}
)`
background-color: rgba(255, 255, 255, 0.2);
height: 100%;
position: absolute;
top: 0;
transform: scaleX(0);
transform-origin: 0 0;
width: 100%;
`;
var ProgressTimestamp = styled5.div.attrs(
({ left }) => {
return {
style: { left: `${left}px` }
};
}
)`
background-color: rgb(56, 55, 51);
border-radius: 3px;
bottom: 200%;
color: #fff;
font-size: 9px;
padding: 5px;
position: absolute;
text-align: center;
`;
var ProgressIndicator = styled5.div`
color: rgb(240, 180, 0);
padding-left: 24px;
font-size: 10px;
white-space: nowrap;
`;
var Controls = ({
play,
videoProperties,
duration,
startTime,
src,
parameters,
onPlay,
onStop,
onRefresh,
onSeek,
onScreenshot,
onFormat,
onVapix,
labels,
showStatsOverlay,
toggleStats,
format,
volume,
setVolume
}) => {
const controlArea = useRef2(null);
const userActive = useUserActive(controlArea);
const [settings, setSettings] = useState3(false);
const toggleSettings = useCallback2(
() => setSettings((currentSettings) => !currentSettings),
[setSettings]
);
const onVolumeChange = useCallback2(
(e) => {
if (setVolume !== void 0) {
setVolume(parseFloat(e.target.value));
}
},
[setVolume]
);
const [totalDuration, setTotalDuration] = useState3(duration);
const __mediaTimeline = useRef2({
startDateTime: startTime !== void 0 ? DateTime.fromISO(startTime) : void 0
});
const [progress, setProgress] = useState3({
playedFraction: 0,
bufferedFraction: 0,
counter: ""
});
useEffect2(() => {
var _a;
if (videoProperties === void 0) {
return;
}
const { el, pipeline, range } = videoProperties;
if (el === null || pipeline === void 0) {
return;
}
const [start = 0, end = duration] = range != null ? range : [0, duration];
const __duration = (_a = duration != null ? duration : end) != null ? _a : Infinity;
setTotalDuration(__duration);
const updateProgress = () => {
const played = start + pipeline.currentTime;
const buffered = isHTMLMediaElement(el) && el.buffered.length > 0 ? start + el.buffered.end(el.buffered.length - 1) : played;
const total = __duration === Infinity ? buffered : __duration;
const counter = `${Duration.fromMillis(played * 1e3).toFormat(
"h:mm:ss"
)} / ${Duration.fromMillis(total * 1e3).toFormat("h:mm:ss")}`;
setProgress({
playedFraction: played / total,
bufferedFraction: buffered / total,
counter
});
};
updateProgress();
if (isHTMLMediaElement(el)) {
el.addEventListener("ended", updateProgress);
el.addEventListener("progress", updateProgress);
el.addEventListener("timeupdate", updateProgress);
return () => {
el.removeEventListener("timeupdate", updateProgress);
el.removeEventListener("progress", updateProgress);
el.removeEventListener("ended", updateProgress);
};
}
const progressInterval = setInterval(updateProgress, 1e3);
return () => {
clearInterval(progressInterval);
};
}, [videoProperties, duration, startTime, setTotalDuration]);
const seek = useCallback2(
(e) => {
if (totalDuration === void 0) {
return;
}
const { left, width } = e.currentTarget.getBoundingClientRect();
const fraction = (e.pageX - left) / width;
onSeek(fraction * totalDuration);
},
[totalDuration, onSeek]
);
const [timestamp, setTimestamp] = useState3({ left: 0, label: "" });
const __progressBarContainerRef = useRef2(null);
useEffect2(() => {
if (startTime !== void 0) {
__mediaTimeline.current.startDateTime = DateTime.fromISO(startTime);
}
const el = __progressBarContainerRef.current;
if (el === null || totalDuration === void 0) {
return;
}
const { left, width } = el.getBoundingClientRect();
const showTimestamp = (e) => {
const offset = e.pageX - left;
const offsetMillis = offset / width * totalDuration * 1e3;
setTimestamp({
left: offset,
label: __mediaTimeline.current.startDateTime !== void 0 ? __mediaTimeline.current.startDateTime.plus(offsetMillis).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS) : Duration.fromMillis(offsetMillis).toFormat("h:mm:ss")
});
};
const start = () => {
el.addEventListener("pointermove", showTimestamp);
};
const stop = () => {
setTimestamp({ left: 0, label: "" });
el.removeEventListener("pointermove", showTimestamp);
};
el.addEventListener("pointerover", start);
el.addEventListener("pointerout", stop);
return () => {
el.removeEventListener("pointerout", stop);
el.removeEventListener("pointerover", start);
};
}, [startTime, totalDuration]);
return /* @__PURE__ */ React12.createElement(
ControlArea,
{
ref: controlArea,
visible: play !== true || settings || userActive
},
/* @__PURE__ */ React12.createElement(ControlBar, null, /* @__PURE__ */ React12.createElement(Button, { onClick: onPlay }, play === true ? /* @__PURE__ */ React12.createElement(Pause, { title: labels == null ? void 0 : labels.pause }) : /* @__PURE__ */ React12.createElement(Play, { title: labels == null ? void 0 : labels.play })), src !== void 0 && /* @__PURE__ */ React12.createElement(Button, { onClick: onStop }, /* @__PURE__ */ React12.createElement(Stop, { title: labels == null ? void 0 : labels.stop })), src !== void 0 && /* @__PURE__ */ React12.createElement(Button, { onClick: onRefresh }, /* @__PURE__ */ React12.createElement(Refresh, { title: labels == null ? void 0 : labels.refresh })), src !== void 0 && /* @__PURE__ */ React12.createElement(Button, { onClick: onScreenshot }, /* @__PURE__ */ React12.createElement(Screenshot, { title: labels == null ? void 0 : labels.screenshot })), volume !== void 0 ? /* @__PURE__ */ React12.createElement(VolumeContainer, { title: labels == null ? void 0 : labels.volume }, /* @__PURE__ */ React12.createElement(
"input",
{
type: "range",
min: "0",
max: "1",
step: "0.05",
onChange: onVolumeChange,
value: volume != null ? volume : 0
}
)) : null, /* @__PURE__ */ React12.createElement(Progress, null, /* @__PURE__ */ React12.createElement(ProgressBarContainer, { onClick: seek, ref: __progressBarContainerRef }, /* @__PURE__ */ React12.createElement(ProgressBar, null, /* @__PURE__ */ React12.createElement(ProgressBarPlayed, { fraction: progress.playedFraction }), /* @__PURE__ */ React12.createElement(ProgressBarBuffered, { fraction: progress.bufferedFraction }), timestamp.left !== 0 ? /* @__PURE__ */ React12.createElement(ProgressTimestamp, { left: timestamp.left }, timestamp.label) : null)), /* @__PURE__ */ React12.createElement(ProgressIndicator, null, totalDuration === Infinity ? "\u2219 LIVE" : progress.counter)), /* @__PURE__ */ React12.createElement(Button, { onClick: toggleSettings }, /* @__PURE__ */ React12.createElement(CogWheel, { title: labels == null ? void 0 : labels.settings }))),
settings && /* @__PURE__ */ React12.createElement(
Settings,
{
parameters,
format,
onFormat,
onVapix,
showStatsOverlay,
toggleStats
}
)
);
};
// src/PlaybackArea.tsx
import React17 from "react";
import debug5 from "debug";
// src/HttpMp4Video.tsx
import React13, { useEffect as useEffect5, useRef as useRef3, useState as useState5 } from "react";
import debug from "debug";
import { pipelines } from "media-stream-library";
import styled6 from "styled-components";
// src/constants.ts
var FORMAT_SUPPORTS_AUDIO = {
RTP_H264: true,
RTP_JPEG: false,
MP4_H264: true,
JPEG: false,
MJPEG: false
};
// src/hooks/useEventState.ts
import { useCallback as useCallback3, useEffect as useEffect3, useState as useState4 } from "react";
var useEventState = (ref, eventName) => {
const [eventState, setEventState] = useState4(false);
const setEventStateTrue = useCallback3(() => setEventState(true), []);
const setEventStateFalse = useCallback3(() => setEventState(false), []);
useEffect3(() => {
const el = ref.current;
if (!eventState && el !== null) {
el.addEventListener(eventName, setEventStateTrue);
return () => {
el.removeEventListener(eventName, setEventStateTrue);
};
}
}, [eventState, eventName, ref, setEventStateTrue]);
return [eventState, setEventStateFalse];
};
// src/hooks/useVideoDebug.ts
import { useEffect as useEffect4 } from "react";
var useVideoDebug = (videoEl, debugLog6) => {
useEffect4(() => {
if (videoEl === null) {
return;
}
const onUpdate = () => {
try {
const currentTime = videoEl.currentTime;
const bufferedEnd = videoEl.buffered.end(videoEl.buffered.length - 1);
debugLog6("%o", {
delay: bufferedEnd - currentTime,
currentTime,
bufferedEnd
});
} catch (err) {
debugLog6("%o", err);
}
};
videoEl.addEventListener("timeupdate", onUpdate);
videoEl.addEventListener("progress", onUpdate);
return () => {
videoEl.removeEventListener("timeupdate", onUpdate);
videoEl.removeEventListener("progress", onUpdate);
};
}, [debugLog6, videoEl]);
};
// src/types.ts
var Format = /* @__PURE__ */ ((Format2) => {
Format2["RTP_H264"] = "RTP_H264";
Format2["RTP_JPEG"] = "RTP_JPEG";
Format2["JPEG"] = "JPEG";
Format2["MJPEG"] = "MJPEG";
Format2["MP4_H264"] = "MP4_H264";
return Format2;
})(Format || {});
// src/HttpMp4Video.tsx
var debugLog = debug("msp:http-mp4-video");
var VideoNative = styled6.video`
max-height: 100%;
object-fit: contain;
width: 100%;
`;
var HttpMp4Video = ({
forwardedRef,
play = false,
src,
autoPlay = true,
muted = true,
onPlaying,
onEnded,
metadataHandler
}) => {
let videoRef = useRef3(null);
if (typeof forwardedRef === "function") {
forwardedRef(videoRef.current);
} else if (forwardedRef) {
videoRef = forwardedRef;
}
const [canplay, unsetCanplay] = useEventState(videoRef, "canplay");
const [playing, unsetPlaying] = useEventState(videoRef, "playing");
const [pipeline, setPipeline] = useState5(
null
);
const [fetching, setFetching] = useState5(false);
const __onPlayingRef = useRef3(onPlaying);
__onPlayingRef.current = onPlaying;
const __onEndedRef = useRef3(onEnded);
__onEndedRef.current = onEnded;
const __sensorTmRef = useRef3();
useVideoDebug(videoRef.current, debugLog);
useEffect5(() => {
const videoEl = videoRef.current;
if (videoEl === null) {
return;
}
if (play && canplay === true && playing === false) {
debugLog("play");
videoEl.play().catch((err) => {
console.error("VideoElement error: ", err.message);
});
const { videoHeight, videoWidth } = videoEl;
debugLog("%o", {
videoHeight,
videoWidth
});
} else if (!play && playing === true) {
debugLog("pause");
videoEl.pause();
unsetPlaying();
} else if (play && playing === true) {
if (__onPlayingRef.current !== void 0) {
__onPlayingRef.current({
el: videoEl,
width: videoEl.videoWidth,
height: videoEl.videoHeight,
sensorTm: __sensorTmRef.current,
formatSupportsAudio: FORMAT_SUPPORTS_AUDIO["MP4_H264" /* MP4_H264 */]
// TODO: no volume, need to expose tracks?
// TODO: no pipeline, can we even get stats?
});
}
}
}, [play, canplay, playing, unsetPlaying, pipeline]);
const __metadataHandlerRef = useRef3(metadataHandler);
__metadataHandlerRef.current = metadataHandler;
useEffect5(() => {
const videoEl = videoRef.current;
if (src !== void 0 && src.length > 0 && videoEl !== null) {
const endedCallback = () => {
var _a;
(_a = __onEndedRef.current) == null ? void 0 : _a.call(__onEndedRef);
};
debugLog("create pipeline", src);
const newPipeline = new pipelines.HttpMsePipeline({
http: { uri: src },
mediaElement: videoEl
});
setPipeline(newPipeline);
newPipeline.onServerClose = endedCallback;
return () => {
debugLog("close pipeline and clear video");
newPipeline.close();
videoEl.src = "";
setPipeline(null);
setFetching(false);
unsetCanplay();
unsetPlaying();
};
}
}, [src, unsetCanplay, unsetPlaying]);
useEffect5(() => {
if (play && pipeline && !fetching) {
pipeline.onHeaders = (headers) => {
var _a;
__sensorTmRef.current = parseTransformHeader(
(_a = headers.get("video-sensor-transform")) != null ? _a : headers.get("video-metadata-transform")
);
};
pipeline.http.play();
debugLog("initiated data fetching");
setFetching(true);
}
}, [play, pipeline, fetching]);
return /* @__PURE__ */ React13.createElement(VideoNative, { autoPlay, muted, ref: videoRef });
};
var parseTransformHeader = (value) => {
if (value === void 0 || value === null) {
return void 0;
}
return value.split(";").map((row) => row.split(",").map(Number));
};
// src/StillImage.tsx
import React14, { useEffect as useEffect6, useRef as useRef4 } from "react";
import debug2 from "debug";
import styled7 from "styled-components";
var debugLog2 = debug2("msp:still-image");
var ImageNative = styled7.img`
max-height: 100%;
object-fit: contain;
width: 100%;
`;
var cachebust = 0;
var StillImage = ({
forwardedRef,
play = false,
onPlaying,
src
}) => {
let imgRef = useRef4(null);
if (typeof forwardedRef === "function") {
forwardedRef(imgRef.current);
} else if (forwardedRef) {
imgRef = forwardedRef;
}
const [loaded, unsetLoaded] = useEventState(imgRef, "load");
useEffect6(() => {
const imgEl = imgRef.current;
if (imgEl === null) {
return;
}
if (play && src !== void 0) {
imgEl.src = `${src}&cachebust=${cachebust++}`;
return () => {
imgEl.src = "";
unsetLoaded();
};
}
}, [play, src, unsetLoaded]);
const __onPlayingRef = useRef4(onPlaying);
__onPlayingRef.current = onPlaying;
useEffect6(() => {
const el = imgRef.current;
if (loaded && el !== null && __onPlayingRef.current !== void 0) {
__onPlayingRef.current({
el,
width: el.naturalWidth,
height: el.naturalHeight,
formatSupportsAudio: FORMAT_SUPPORTS_AUDIO["JPEG" /* JPEG */]
});
}
}, [loaded]);
debugLog2("render image", loaded);
return /* @__PURE__ */ React14.createElement(ImageNative, { ref: imgRef });
};
// src/WsRtspCanvas.tsx
import React15, { useEffect as useEffect7, useRef as useRef5, useState as useState6 } from "react";
import debug3 from "debug";
import {
isRtcpBye,
pipelines as pipelines2,
utils
} from "media-stream-library";
import styled8 from "styled-components";
var debugLog3 = debug3("msp:ws-rtsp-video");
var CanvasNative = styled8.canvas`
max-height: 100%;
object-fit: contain;
width: 100%;
`;
var WsRtspCanvas = ({
forwardedRef,
play = true,
ws = "",
rtsp = "",
onPlaying,
onEnded,
onSdp,
onRtcp,
offset = 0,
autoRetry = false
}) => {
let canvasRef = useRef5(null);
if (typeof forwardedRef === "function") {
forwardedRef(canvasRef.current);
} else if (forwardedRef) {
canvasRef = forwardedRef;
}
const [pipeline, setPipeline] = useState6(null);
const [fetching, setFetching] = useState6(false);
const __offsetRef = useRef5(offset);
const __rangeRef = useRef5([offset, void 0]);
const timeout = useRef5(void 0);
useEffect7(() => {
if (pipeline === null) {
return;
}
timeout.current = window.setInterval(() => {
const { currentTime } = pipeline;
debugLog3("%o", { currentTime });
}, 1e3);
return () => window.clearTimeout(timeout.current);
}, [pipeline]);
useEffect7(() => {
__offsetRef.current = offset;
const canvas = canvasRef.current;
if (ws && rtsp && canvas) {
debugLog3("create pipeline");
const newPipeline = new pipelines2.Html5CanvasPipeline({
ws: { uri: ws },
rtsp: { uri: rtsp },
mediaElement: canvas
});
if (autoRetry) {
utils.addRTSPRetry(newPipeline.rtsp);
}
setPipeline(newPipeline);
return () => {
debugLog3("destroy pipeline");
newPipeline.pause();
newPipeline.close();
setPipeline(null);
setFetching(false);
debugLog3("canvas cleared");
};
}
}, [ws, rtsp, offset, autoRetry]);
const __onPlayingRef = useRef5(onPlaying);
__onPlayingRef.current = onPlaying;
const __onEndedRef = useRef5(onEnded);
__onEndedRef.current = onEnded;
const __onSdpRef = useRef5(onSdp);
__onSdpRef.current = onSdp;
const __onRtcpRef = useRef5(onRtcp);
__onRtcpRef.current = onRtcp;
const __sensorTmRef = useRef5();
useEffect7(() => {
if (play && pipeline && !fetching) {
pipeline.ready.then(() => {
debugLog3("fetch");
pipeline.onSdp = (sdp) => {
var _a;
const videoMedia = sdp.media.find((m) => {
return m.type === "video";
});
if (videoMedia !== void 0) {
__sensorTmRef.current = (_a = videoMedia["x-sensor-transform"]) != null ? _a : videoMedia["transform"];
}
if (__onSdpRef.current !== void 0) {
__onSdpRef.current(sdp);
}
};
pipeline.rtsp.onRtcp = (rtcp) => {
var _a, _b;
(_a = __onRtcpRef.current) == null ? void 0 : _a.call(__onRtcpRef, rtcp);
if (isRtcpBye(rtcp)) {
(_b = __onEndedRef.current) == null ? void 0 : _b.call(__onEndedRef);
}
};
pipeline.rtsp.onPlay = (range) => {
if (range !== void 0) {
__rangeRef.current = [
parseFloat(range[0]) || 0,
parseFloat(range[1]) || void 0
];
}
};
pipeline.rtsp.play(__offsetRef.current);
setFetching(true);
}).catch(console.error);
} else if (play && pipeline !== null) {
debugLog3("play");
pipeline.play();
pipeline.onCanplay = () => {
if (canvasRef.current !== null && __onPlayingRef.current !== void 0) {
__onPlayingRef.current({
el: canvasRef.current,
width: canvasRef.current.width,
height: canvasRef.current.height,
formatSupportsAudio: FORMAT_SUPPORTS_AUDIO["RTP_JPEG" /* RTP_JPEG */],
range: __rangeRef.current,
sensorTm: __sensorTmRef.current
});
}
};
} else if (!play && pipeline) {
debugLog3("pause");
pipeline.pause();
}
}, [play, pipeline, fetching]);
return /* @__PURE__ */ React15.createElement(CanvasNative, { ref: canvasRef });
};
// src/WsRtspVideo.tsx
import React16, { useEffect as useEffect8, useRef as useRef6, useState as useState7 } from "react";
import debug4 from "debug";
import {
isRtcpBye as isRtcpBye2,
pipelines as pipelines4,
utils as utils3
} from "media-stream-library";
import styled9 from "styled-components";
// src/metadata.ts
import {
MessageType,
components,
utils as utils2
} from "media-stream-library";
var attachMetadataHandler = (pipeline, { parser, cb }) => {
const scheduler = new utils2.Scheduler(pipeline, cb, 30);
const xmlParser = new DOMParser();
const xmlMessageHandler = (msg) => {
const xmlDocument = xmlParser.parseFromString(
msg.data.toString(),
"text/xml"
);
const newMsg = parser(__spreadProps(__spreadValues({}, msg), { xmlDocument }));
if (msg.ntpTimestamp !== void 0) {
scheduler.run(newMsg);
}
};
const onvifDepay = new components.ONVIFDepay();
const onvifHandlerPipe = components.Tube.fromHandlers((msg) => {
if (msg.type === MessageType.XML) {
xmlMessageHandler(msg);
}
}, void 0);
pipeline.insertAfter(pipeline.rtsp, onvifDepay);
pipeline.insertAfter(onvifDepay, onvifHandlerPipe);
pipeline.onSync = (ntpPresentationTime) => scheduler.init(ntpPresentationTime);
return scheduler;
};
// src/WsRtspVideo.tsx
var debugLog4 = debug4("msp:ws-rtsp-video");
var VideoNative2 = styled9.video`
max-height: 100%;
object-fit: contain;
width: 100%;
`;
var WsRtspVideo = ({
forwardedRef,
play = false,
ws,
rtsp,
autoPlay = true,
muted = true,
onPlaying,
onEnded,
onSdp,
onRtcp,
metadataHandler,
offset = 0,
autoRetry = false
}) => {
let videoRef = useRef6(null);
if (typeof forwardedRef === "function") {
forwardedRef(videoRef.current);
} else if (forwardedRef) {
videoRef = forwardedRef;
}
const [canplay, unsetCanplay] = useEventState(videoRef, "canplay");
const [playing, unsetPlaying] = useEventState(videoRef, "playing");
const [pipeline, setPipeline] = useState7(
null
);
const [fetching, setFetching] = useState7(false);
const __offsetRef = useRef6(offset);
const __rangeRef = useRef6([offset, void 0]);
const __onPlayingRef = useRef6(onPlaying);
__onPlayingRef.current = onPlaying;
const __onEndedRef = useRef6(onEnded);
__onEndedRef.current = onEnded;
const __sensorTmRef = useRef6();
useVideoDebug(videoRef.current, debugLog4);
useEffect8(() => {
var _a;
const videoEl = videoRef.current;
if (videoEl === null) {
return;
}
if (play && canplay === true && playing === false) {
debugLog4("play");
videoEl.play().catch((err) => {
console.error("VideoElement error: ", err.message);
});
const { videoHeight, videoWidth } = videoEl;
debugLog4("%o", {
videoHeight,
videoWidth
});
} else if (!play && playing === true) {
debugLog4("pause");
videoEl.pause();
unsetPlaying();
} else if (play && playing === true) {
if (__onPlayingRef.current !== void 0) {
__onPlayingRef.current({
el: videoEl,
pipeline: pipeline != null ? pipeline : void 0,
width: videoEl.videoWidth,
height: videoEl.videoHeight,
formatSupportsAudio: FORMAT_SUPPORTS_AUDIO["RTP_H264" /* RTP_H264 */],
volume: ((_a = pipeline == null ? void 0 : pipeline.tracks) == null ? void 0 : _a.find((track) => track.type === "audio")) ? videoEl.volume : void 0,
range: __rangeRef.current,
sensorTm: __sensorTmRef.current
});
}
}
}, [play, canplay, playing, unsetPlaying, pipeline]);
const __metadataHandlerRef = useRef6(metadataHandler);
__metadataHandlerRef.current = metadataHandler;
useEffect8(() => {
const videoEl = videoRef.current;
__offsetRef.current = offset;
if (ws !== void 0 && ws.length > 0 && rtsp !== void 0 && rtsp.length > 0 && videoEl !== null) {
debugLog4("create pipeline", ws, rtsp);
const newPipeline = new pipelines4.Html5VideoPipeline({
ws: { uri: ws },
rtsp: { uri: rtsp },
mediaElement: videoEl
});
if (autoRetry) {
utils3.addRTSPRetry(newPipeline.rtsp);
}
setPipeline(newPipeline);
let scheduler;
if (__metadataHandlerRef.current !== void 0) {
scheduler = attachMetadataHandler(
newPipeline,
__metadataHandlerRef.current
);
}
return () => {
debugLog4("close pipeline and clear video");
newPipeline.close();
videoEl.src = "";
scheduler == null ? void 0 : scheduler.reset();
setPipeline(null);
setFetching(false);
unsetCanplay();
unsetPlaying();
};
}
}, [ws, rtsp, offset, unsetCanplay, unsetPlaying, autoRetry]);
const __onSdpRef = useRef6(onSdp);
__onSdpRef.current = onSdp;
const __onRtcpRef = useRef6(onRtcp);
__onRtcpRef.current = onRtcp;
useEffect8(() => {
if (play && pipeline && !fetching) {
pipeline.ready.then(() => {
pipeline.onSdp = (sdp) => {
var _a;
const videoMedia = sdp.media.find((m) => {
return m.type === "video";
});
if (videoMedia !== void 0) {
__sensorTmRef.current = (_a = videoMedia["x-sensor-transform"]) != null ? _a : videoMedia["transform"];
}
if (__onSdpRef.current !== void 0) {
__onSdpRef.current(sdp);
}
};
pipeline.rtsp.onRtcp = (rtcp) => {
var _a, _b;
(_a = __onRtcpRef.current) == null ? void 0 : _a.call(__onRtcpRef, rtcp);
if (isRtcpBye2(rtcp)) {
(_b = __onEndedRef.current) == null ? void 0 : _b.call(__onEndedRef);
}
};
pipeline.rtsp.onPlay = (range) => {
if (range !== void 0) {
__rangeRef.current = [
parseFloat(range[0]) || 0,
parseFloat(range[1]) || void 0
];
}
};
pipeline.rtsp.play(__offsetRef.current);
}).catch((err) => {
console.error(err);
});
debugLog4("initiated data fetching");
setFetching(true);
}
}, [play, pipeline, fetching]);
return /* @__PURE__ */ React16.createElement(VideoNative2, { autoPlay, muted, ref: videoRef });
};
// src/PlaybackArea.tsx
var debugLog5 = debug5("msp:api");
var AxisApi = /* @__PURE__ */ ((AxisApi2) => {
AxisApi2["AXIS_IMAGE_CGI"] = "AXIS_IMAGE_CGI";
AxisApi2["AXIS_MEDIA_AMP"] = "AXIS_MEDIA_AMP";
AxisApi2["AXIS_MEDIA_CGI"] = "AXIS_MEDIA_CGI";
AxisApi2["AXIS_MJPEG_CGI"] = "AXIS_MJPEG_CGI";
return AxisApi2;
})(AxisApi || {});
var Protocol = /* @__PURE__ */ ((Protocol2) => {
Protocol2["HTTP"] = "http:";
Protocol2["HTTPS"] = "https:";
Protocol2["WS"] = "ws:";
Protocol2["WSS"] = "wss:";
return Protocol2;
})(Protocol || {});
var FORMAT_API = {
RTP_H264: "AXIS_MEDIA_AMP" /* AXIS_MEDIA_AMP */,
RTP_JPEG: "AXIS_MEDIA_AMP" /* AXIS_MEDIA_AMP */,
MJPEG: "AXIS_MJPEG_CGI" /* AXIS_MJPEG_CGI */,
MP4_H264: "AXIS_MEDIA_CGI" /* AXIS_MEDIA_CGI */,
JPEG: "AXIS_IMAGE_CGI" /* AXIS_IMAGE_CGI */
};
var wsUri = (protocol, host) => {
return host.length !== 0 ? `${protocol}//${host}/rtsp-over-websocket` : "";
};
var rtspUri = (host, searchParams2) => {
return host.length !== 0 ? `rtsp://${host}/axis-media/media.amp?${searchParams2}` : "";
};
var mediaUri = (protocol, host, searchParams2) => {
return host.length !== 0 ? `${protocol}//${host}/axis-cgi/media.cgi?${searchParams2}` : "";
};
var imgUri = (protocol, host, searchParams2) => {
return host.length !== 0 ? `${protocol}//${host}/axis-cgi/jpg/image.cgi?${searchParams2}` : "";
};
var mjpegUri = (protocol, host, searchParams2) => {
return host.length !== 0 ? `${protocol}//${host}/mjpg/video.mjpg?${searchParams2}` : "";
};
var PARAMETERS = {
["AXIS_IMAGE_CGI" /* AXIS_IMAGE_CGI */]: [
"resolution",
"camera",
"compression",
"rotation",
"palette",
"squarepixel",
"timestamp"
],
["AXIS_MEDIA_AMP" /* AXIS_MEDIA_AMP */]: [
"camera",
"resolution",
"h264profile",
"streamprofile",
"recordingid",
"audio",
"compression",
"colorlevel",
"color",
"palette",
"clock",
"date",
"text",
"textstring",
"textcolor",
"textbackgroundcolor",
"rotation",
"textpos",
"overlayimage",
"overlaypos",
"duration",
"nbrofframes",
"fps",
"pull",
"event",
"timestamp",
"videocodec"
],
["AXIS_MEDIA_CGI" /* AXIS_MEDIA_CGI */]: [
"container",
"camera",
"resolution",
"h264profile",
"streamprofile",
"recordingid",
"audio",
"compression",
"colorlevel",
"color",
"palette",
"clock",
"date",
"text",
"textstring",
"textcolor",
"textbackgroundcolor",
"rotation",
"textpos",
"overlayimage",
"overlaypos",
"duration",
"nbrofframes",
"fps",
"pull",
"event",
"timestamp",
"videocodec"
],
["AXIS_MJPEG_CGI" /* AXIS_MJPEG_CGI */]: [
"camera",
"resolution",
"streamprofile",
"compression",
"colorlevel",
"color",
"palette",
"clock",
"date",
"text",
"textstring",
"textcolor",
"textbackgroundcolor",
"rotation",
"textpos",
"overlayimage",
"overlaypos",
"duration",
"nbrofframes",
"fps",
"timestamp"
]
};
var searchParams = (api, parameters = {}) => {
const parameterList = PARAMETERS[api];
return Object.entries(parameters).map(([key, value]) => {
if (!parameterList.includes(key)) {
debugLog5(`undocumented VAPIX parameter ${key}`);
}
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}).join("&");
};
var PlaybackArea = ({
forwardedRef,
host,
format,
parameters = {},
play,
offset,
refresh,
onPlaying,
onEnded,
onSdp,
onRtcp,
metadataHandler,
secure = window.location.protocol === "https:" /* HTTPS */,
autoRetry = false
}) => {
const timestamp = refresh.toString();
if (format === "RTP_H264" /* RTP_H264 */) {
const ws = wsUri(secure ? "wss:" /* WSS */ : "ws:" /* WS */, host);
const rtsp = rtspUri(
host,
searchParams(FORMAT_API[format], __spreadProps(__spreadValues({}, parameters), {
timestamp,
videocodec: "h264"
}))
);
return /* @__PURE__ */ React17.createElement(
WsRtspVideo,
__spreadValues({
key: refresh,
forwardedRef
}, {
ws,
rtsp,
play,
offset,
onPlaying,
onEnded,
onSdp,
onRtcp,
metadataHandler,
autoRetry
})
);
}
if (format === "RTP_JPEG" /* RTP_JPEG */) {
const ws = wsUri(secure ? "wss:" /* WSS */ : "ws:" /* WS */, host);
const rtsp = rtspUri(
host,
searchParams(FORMAT_API[format], __spreadProps(__spreadValues({}, parameters), {
timestamp,
videocodec: "jpeg"
}))
);
return /* @__PURE__ */ React17.createElement(
WsRtspCanvas,
__spreadValues({
key: refresh,
forwardedRef
}, { ws, rtsp, play, offset, onPlaying, onEnded, onSdp, onRtcp })
);
}
if (format === "JPEG" /* JPEG */) {
const src = imgUri(
secure ? "https:" /* HTTPS */ : "http:" /* HTTP */,
host,
searchParams(FORMAT_API[format], __spreadProps(__spreadValues({}, parameters), {
timestamp
}))
);
return /* @__PURE__ */ React17.createElement(
StillImage,
__spreadValues({
key: refresh,
forwardedRef
}, { src, play, onPlaying })
);
}
if (format === "MJPEG" /* MJPEG */) {
const src = mjpegUri(
secure ? "https:" /* HTTPS */ : "http:" /* HTTP */,
host,
searchParams(FORMAT_API[format], __spreadProps(__spreadValues({}, parameters), {
timestamp
}))
);
return /* @__PURE__ */ React17.createElement(
StillImage,
__spreadValues({
key: refresh,
forwardedRef
}, { src, play, onPlaying })
);
}
if (format === "MP4_H264" /* MP4_H264 */) {
const src = mediaUri(
secure ? "https:" /* HTTPS */ : "http:" /* HTTP */,
host,
searchParams(FORMAT_API[format], __spreadProps(__spreadValues({}, parameters), {
timestamp,
videocodec: "h264",
container: "mp4"
}))
);
return /* @__PURE__ */ React17.createElement(
HttpMp4Video,
__spreadValues({
key: refresh,
forwardedRef
}, { src, play, onPlaying, onEnded })
);
}
console.warn(`Error: unknown format: ${format},
please use one of ${[
"JPEG" /* JPEG */,
"MJPEG" /* MJPEG */,
"MP4_H264" /* MP4_H264 */,
"RTP_H264" /* RTP_H264 */,
"RTP_JPEG" /* RTP_JPEG */
].join(", ")}`);
return null;
};
// src/components/Limiter.tsx
import styled10 from "styled-components";
var