UNPKG

media-stream-player

Version:

Player built on top of media-stream-library

1,539 lines (1,500 loc) 76.1 kB
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