diff --git a/package.json b/package.json index ffd23c2..471ea67 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3": "^1.0.1", + "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5": "^0.0.12", "@livekit/components-react": "^2.0.3", "@livekit/components-styles": "^1.0.10", "@uidotdev/usehooks": "^2.4.1", @@ -20,6 +21,7 @@ "i18next-browser-languagedetector": "^7.2.0", "ky": "^1.1.3", "livekit-client": "^1.13.2", + "peerjs": "^1.5.4", "react": "^18.2.0", "react-calendar": "^4.3.0", "react-countdown": "^2.3.5", @@ -38,12 +40,14 @@ "socket.io-client": "^4.7.4", "ua-parser-js": "^1.0.35", "use-clipboard-copy": "^0.2.0", + "use-is-audio-active": "^1.0.0", "usehooks-ts": "^3.0.1", "uuid": "^9.0.1", "zustand": "^4.3.9" }, "devDependencies": { "@types/node": "^20.11.17", + "@types/peerjs": "^1.1.0", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@types/react-input-mask": "^3.0.2", diff --git a/src/App.tsx b/src/App.tsx index 873fe4f..f84dec8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,11 +31,12 @@ function App() { state.setIsOpen, ]); const [loading, setLoading] = useState(false); - const [countdownTimer, setCountdownTimer] = useState(15); + const [countdownTimer, setCountdownTimer] = useState(10); const { t, i18n } = useTranslation(); const build = searchParams.get("build") || null; const type = searchParams.get("type") || "demo"; const endAt = searchParams.get("endAt"); + const [streamUrl, setStreamUrl] = useState(); function toastError(text: string) { toast.error(text, { @@ -95,13 +96,11 @@ function App() { .json(); if (response.stream) { + setStreamUrl(`/stream/${response.stream}`); + setInterval(() => { setCountdownTimer((prev) => prev - 1); }, 1000); - - setTimeout(() => { - navigate(`/stream/${response.stream}`); - }, 15000); } else if (response.error) { toastError(response.error); setLoading(false); @@ -117,6 +116,12 @@ function App() { } } + useEffect(() => { + if (countdownTimer > 0 || !streamUrl) return; + + navigate(streamUrl); + }, [countdownTimer]); + useEffect(() => { getLang(); diff --git a/src/StreamPage.tsx b/src/StreamPage.tsx index 560490a..a1735a6 100644 --- a/src/StreamPage.tsx +++ b/src/StreamPage.tsx @@ -28,7 +28,6 @@ import "react-toastify/dist/ReactToastify.css"; import AlertIcon from "./components/icons/AlertIcon"; import useSocketStore from "./stores/useSocketStore"; import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react"; -import ToggleMic from "./components/ToggleMic"; import Chat from "./components/Chat"; // import AFKTimerModal from "./components/modals/AFKTimerModal"; import { differenceInMilliseconds, format, parseISO } from "date-fns"; @@ -380,13 +379,12 @@ function StreamPage() { */} -
diff --git a/src/components/PixelStreamingWrapper2.tsx b/src/components/PixelStreamingWrapper2.tsx new file mode 100644 index 0000000..a7acc38 --- /dev/null +++ b/src/components/PixelStreamingWrapper2.tsx @@ -0,0 +1,91 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +/* eslint-disable no-empty */ + +import { useEffect, useRef, useState } from "react"; +import { + Config, + AllSettings, + PixelStreaming, +} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5"; + +export interface PixelStreamingWrapperProps { + initialSettings?: Partial; +} + +export const PixelStreamingWrapper2 = ({ + initialSettings, +}: PixelStreamingWrapperProps) => { + // A reference to parent div element that the Pixel Streaming library attaches into: + const videoParent = useRef(null); + + // Pixel streaming library instance is stored into this state variable after initialization: + const [pixelStreaming, setPixelStreaming] = useState(); + + // A boolean state variable that determines if the Click to play overlay is shown: + const [clickToPlayVisible, setClickToPlayVisible] = useState(false); + + // Run on component mount: + useEffect(() => { + if (videoParent.current) { + // Attach Pixel Streaming library to videoParent element: + const config = new Config({ initialSettings }); + const streaming = new PixelStreaming(config, { + videoElementParent: videoParent.current, + }); + + // register a playStreamRejected handler to show Click to play overlay if needed: + streaming.addEventListener("playStreamRejected", () => { + setClickToPlayVisible(true); + }); + + // Save the library instance into component state so that it can be accessed later: + setPixelStreaming(streaming); + + // Clean up on component unmount: + return () => { + try { + streaming.disconnect(); + } catch {} + }; + } + }, []); + + return ( +
+
+ {clickToPlayVisible && ( +
{ + pixelStreaming?.play(); + setClickToPlayVisible(false); + }} + > +
Click to play
+
+ )} +
+ ); +}; diff --git a/src/components/SidebarTab4.tsx b/src/components/SidebarTab4.tsx index b5eb15e..fc245c8 100644 --- a/src/components/SidebarTab4.tsx +++ b/src/components/SidebarTab4.tsx @@ -10,7 +10,6 @@ import { Trans } from "react-i18next"; import i18n from "../i18n"; import { useState } from "react"; import LoaderIcon from "./icons/LoaderIcon"; -import api from "../utils/api"; function SidebarTab4() { const { @@ -28,17 +27,6 @@ function SidebarTab4() { const [isLoading, setIsLoading] = useState(false); - async function sendInvite(email: string, link: string) { - try { - const reuslt: any = await api - .post("sendInvite", { json: { email, link } }) - .json(); - console.log("reuslt", reuslt); - } catch (error) { - console.log({ error: (error as Error).message }); - } - } - async function handleClickSignUp() { if (!selectedTime || !selectedDay) { return; @@ -63,7 +51,6 @@ function SidebarTab4() { }) .json(); - sendInvite(email, result.url); setUrl(result.url); setCurrentTab(currentTab + 1); setIsLoading(false); diff --git a/src/components/ToastContainer.tsx b/src/components/ToastContainer.tsx deleted file mode 100644 index 425f869..0000000 --- a/src/components/ToastContainer.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function ToastContainer() { - return
; -} - -export default ToastContainer; diff --git a/src/components/ToggleMic.tsx b/src/components/ToggleMic.tsx deleted file mode 100644 index f7c811d..0000000 --- a/src/components/ToggleMic.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable react-hooks/exhaustive-deps */ -import { useRoomContext } from "@livekit/components-react"; -import { Track } from "livekit-client"; -import { useEffect, useState } from "react"; -import MicroOnIcon from "./icons/MicroOnIcon"; -import MicroOffIcon from "./icons/MicroOffIcon"; - -function ToggleMic({ socket, handleUpdate }: any) { - const room = useRoomContext(); - const [muted, setMuted] = useState(true); - - function toggleMic(value: boolean) { - const audioTrack = room.localParticipant.getTrack(Track.Source.Microphone); - - if (!audioTrack) return; - - if (value) { - audioTrack.mute(); - } else { - audioTrack.unmute(); - } - - setMuted(!audioTrack.isMuted); - } - - useEffect(() => { - if (!socket) return; - - socket.on("update", (socketId: string, data: { [key: string]: any }) => { - if ( - socket.id === socketId && - Object.prototype.hasOwnProperty.call(data, "muted") - ) { - toggleMic(data.muted); - } - }); - }, [socket]); - - useEffect(() => { - // room.on(RoomEvent.TrackMuted, (_, participant) => { - // console.log(participant); - // }); - - // room.on(RoomEvent.TrackUnmuted, (_, participant) => { - // console.log(participant); - // }); - - room.on("localTrackPublished", () => { - room.localParticipant.getTrack(Track.Source.Microphone)?.mute(); - }); - }, []); - - return ( - - ); -} - -export default ToggleMic; diff --git a/src/components/Video.tsx b/src/components/Video.tsx new file mode 100644 index 0000000..9165194 --- /dev/null +++ b/src/components/Video.tsx @@ -0,0 +1,35 @@ +import { useEffect, useRef } from "react"; +import useIsAudioActive from "use-is-audio-active"; + +interface Props { + mediaStream: MediaStream | null; + muted: boolean; +} + +function Video({ mediaStream, muted }: Props) { + const remoteVideoRef = useRef(null); + const isSpeaking = useIsAudioActive({ source: mediaStream }); + + useEffect(() => { + if (!remoteVideoRef.current) return; + + remoteVideoRef.current.srcObject = mediaStream; + remoteVideoRef.current.onloadedmetadata = () => { + remoteVideoRef.current?.play(); + }; + }, [mediaStream]); + + return ( + + ); +} + +export default Video; diff --git a/src/components/icons/CameraOffIcon.tsx b/src/components/icons/CameraOffIcon.tsx new file mode 100644 index 0000000..27e7670 --- /dev/null +++ b/src/components/icons/CameraOffIcon.tsx @@ -0,0 +1,20 @@ +function CameraOffIcon() { + return ( + + + + ); +} + +export default CameraOffIcon; diff --git a/src/components/icons/CameraOnIcon.tsx b/src/components/icons/CameraOnIcon.tsx new file mode 100644 index 0000000..cfc8fc9 --- /dev/null +++ b/src/components/icons/CameraOnIcon.tsx @@ -0,0 +1,20 @@ +function CameraOnIcon() { + return ( + + + + ); +} + +export default CameraOnIcon; diff --git a/src/main.tsx b/src/main.tsx index 7991f38..f26d1a6 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,7 @@ import App from "./App"; import HistoryPage from "./HistoryPage"; import ScheduledPage from "./ScheduledPage"; import StreamPage2 from "./pages/StreamPage2"; -// import StreamPage from "./StreamPage"; +// import StreamPage3 from "./pages/StreamPage3"; const router = createBrowserRouter([ { diff --git a/src/pages/StreamPage2.tsx b/src/pages/StreamPage2.tsx index e0364ec..6eaa087 100644 --- a/src/pages/StreamPage2.tsx +++ b/src/pages/StreamPage2.tsx @@ -11,7 +11,7 @@ import { Transition } from "react-transition-group"; import Button from "../components/ui/Button"; // import CloseIcon from "../components/icons/CloseIcon"; import HandOffIcon from "../components/icons/HandOffIcon"; -import MicroOffIcon from "../components/icons/MicroOffIcon"; +// import MicroOffIcon from "../components/icons/MicroOffIcon"; import PersonsIcon from "../components/icons/PersonsIcon"; import MicroOnIcon from "../components/icons/MicroOnIcon"; // import MoreIcon from "../components/icons/MoreIcon"; @@ -41,15 +41,6 @@ import DesktopIcon from "../components/icons/DesktopIcon"; import { format } from "date-fns"; import IUser from "../types/IUser"; import IMessage from "../types/IMessage"; -import ky from "ky"; -import { - RemoteParticipant, - RemoteTrack, - RemoteTrackPublication, - Room, - RoomEvent, - Track, -} from "livekit-client"; import InfoIcon from "../components/icons/InfoIcon"; import Rotate64Icon from "../components/icons/Rotate64Icon"; import { useClipboard } from "use-clipboard-copy"; @@ -57,6 +48,13 @@ import QRCode from "react-qr-code"; import Star12Icon from "../components/icons/Star12Icon"; import { Trans, useTranslation } from "react-i18next"; import Countdown from "react-countdown"; +import useStreamStore from "../stores/useStreamStore"; +import Peer from "peerjs"; +import Video from "../components/Video"; +import MicroOffIcon from "../components/icons/MicroOffIcon"; +import useIsAudioActive from "use-is-audio-active"; +import CameraOffIcon from "../components/icons/CameraOffIcon"; +import CameraOnIcon from "../components/icons/CameraOnIcon"; const renderer = ({ minutes, seconds }: any) => { return ( @@ -74,7 +72,7 @@ function StreamPage2() { const [socket, setSocket] = useState(); const [wsUrl, setWsUrl] = useState(); const [isEnded, setIsEnded] = useState(false); - const [name, setName] = useState(""); + const { name, setName } = useStreamStore(); const [userId] = useState(uuidv4()); const [step, setStep] = useState(1); const nameRef = useRef(null!); @@ -86,8 +84,6 @@ function StreamPage2() { const [isShowChat, setIsShowChat] = useState(false); const [isShowUsers, setIsShowUsers] = useState(false); const [isShowInviteModal, setIsShowInviteModal] = useState(false); - const [liveKitRoom, setLiveKitRoom] = useState(); - const [isMicEnabled, setIsMicEnabled] = useState(false); const [isVideoInitialized, setIsVideoInitialized] = useState(false); const [messageText, setMessageText] = useState(""); const [messages, setMessages] = useState([]); @@ -98,6 +94,21 @@ function StreamPage2() { const link = window.location.origin + window.location.pathname; const [anyNewMessages, setAnyNewMessages] = useState(false); const [endAt, setEndAt] = useState(); + const [peerId, setPeerId] = useState(""); + const videoRef = useRef(null); + // const [users, setUsers] = useState([]); + const peerInstance = useRef(); + const mediaStreamInstance = useRef(null); + // const [isEnabledVideo, setIsEnabledVideo] = useState(true); + // const [isEnabledAudio, setIsEnabledAudio] = useState(true); + const [permission, setPermission] = useState(); + const [remoteStreams, setRemoteStreams] = useState([]); + // const [errorMessage, setErrorMessage] = useState(""); + const [isVideoEnabled, setIsVideoEnabled] = useState(false); + const [isAudioEnabled, setIsAudioEnabled] = useState(false); + const isSpeaking = useIsAudioActive({ + source: mediaStreamInstance.current, + }); async function getLang() { const { countryCode, error }: { countryCode: string; error: string } = @@ -125,17 +136,6 @@ function StreamPage2() { }); } - function handleTrackSubscribed( - track: RemoteTrack, - publication: RemoteTrackPublication, - participant: RemoteParticipant - ) { - console.log(track, publication, participant); - - const element = track.attach(); - parentElementRef.current?.append(element); - } - async function sendMessage(e: FormEvent) { e.preventDefault(); @@ -204,6 +204,7 @@ function StreamPage2() { name: name, device: isMobile ? "mobile" : "desktop", isAdmin: searchParams.has("admin", true), + peerId, }, }, }) @@ -224,6 +225,7 @@ function StreamPage2() { name: i18n.language === "ru" ? "Гость" : "Guest", device: isMobile ? "mobile" : "desktop", isAdmin: searchParams.has("admin", true), + peerId, }, }, }) @@ -232,78 +234,73 @@ function StreamPage2() { setStep(2); } - async function mute() { - const result = await liveKitRoom?.localParticipant - ?.getTrack(Track.Source.Microphone) - ?.mute(); - - if (!result) return; - - setIsMicEnabled(false); - } - - async function unmute() { - const result = await liveKitRoom?.localParticipant - ?.getTrack(Track.Source.Microphone) - ?.unmute(); - - if (!result) return; - - setIsMicEnabled(true); - } - - async function allowMic() { - if (!liveKitRoom) return; - + async function getPermission() { try { - const result = await liveKitRoom.localParticipant.setMicrophoneEnabled( - true - ); - console.log("result", result); + const mediaStream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: true, + }); - setIsMicEnabled( - !liveKitRoom?.localParticipant?.getTrack(Track.Source.Microphone) - ?.isMuted - ); - setStep(3); + mediaStream.getAudioTracks().forEach((track) => { + track.enabled = !track.enabled; + }); + + videoRef.current!.srcObject = mediaStream; + videoRef.current!.onloadedmetadata = async () => { + try { + await videoRef.current?.play(); + } catch (error) { + // toast.error((error as Error).message); + } + }; + + mediaStreamInstance.current = mediaStream; + + setIsVideoEnabled(true); + setPermission(true); } catch (error) { - if (error instanceof Error) { - if (error.message === "Requested device not found") { - toast.warn( - "Устройство ввода не найдено. Подключите микрофон или нажмите кнопку «Пропустить»", - { - icon: , - position: "top-center", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - } - ); - } + const mediaStream = new MediaStream(); - if (error.message === "Permission denied") { - toast.warn( - "Вы заблокировали доступ к микрофону в настройках. Разрешите доступ в настройках вашего браузера и попробуйте снова.", - { - icon: , - position: "top-center", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: "light", - transition: Bounce, - } - ); + mediaStreamInstance.current = mediaStream; + + videoRef.current!.srcObject = mediaStream; + videoRef.current!.onloadedmetadata = async () => { + try { + await videoRef.current?.play(); + } catch (error) { + // toast.error((error as Error).message); } - } + }; + + const errorMessage = (error as Error).message; + console.log("errorMessage", errorMessage); + + setPermission(false); + } + + setStep(3); + } + + async function startCalls() { + const options = { + constraints: { + offerToReceiveVideo: true, + offerToReceiveAudio: true, + }, + }; + + for (const user of users) { + if (user.peerId === peerId) continue; + + const call = peerInstance.current!.call( + user.peerId, + mediaStreamInstance.current!, + options as any + ); + + call.on("stream", (remoteStream) => { + setRemoteStreams((prev) => [...prev, { peerId, remoteStream }]); + }); } } @@ -311,6 +308,24 @@ function StreamPage2() { setStep(3); } + function toggleVideo() { + mediaStreamInstance.current!.getVideoTracks().forEach((track) => { + track.enabled = !track.enabled; + + if (!permission) return; + setIsVideoEnabled(track.enabled); + }); + } + + function toggleAudio() { + mediaStreamInstance.current!.getAudioTracks().forEach((track) => { + track.enabled = !track.enabled; + + if (!permission) return; + setIsAudioEnabled(track.enabled); + }); + } + function transferControl(userId: string) { socket?.emit("transferControl", userId); } @@ -336,32 +351,46 @@ function StreamPage2() { socket?.emit("kickUser", userId); } - async function getLiveKitToken() { - const { token }: any = await ky - .get( - `https://coord.graff.tech/getToken?roomName=${params.id}&participantName=${userId}` - ) - .json(); - - return token; - } - - async function liveKitConnect() { - const liveKitWsUrl = "wss://livekit.stream.graff.tech"; - const liveKitToken = await getLiveKitToken(); - - const room = new Room(); - room.on(RoomEvent.TrackSubscribed, handleTrackSubscribed); - await room.connect(liveKitWsUrl, liveKitToken); - - setLiveKitRoom(room); - } - useEffect(() => { + const peer = new Peer(); + + // Как только соединение с сервером PeerJS установлено, запускается событие "open" + peer.on("open", (id) => { + setPeerId(id); + }); + + // Обработка входящего вызова + peer.on("call", (call) => { + call.answer(mediaStreamInstance.current!); + call.on("stream", function (remoteStream) { + setRemoteStreams((prev) => [...prev, { peerId, remoteStream }]); + }); + }); + + peerInstance.current = peer; + getLang(); getWsUrl(); }, []); + useEffect(() => { + if (!remoteStreams.length) return; + + setRemoteStreams((prev) => + prev.filter( + (obj, idx, arr) => idx === arr.findIndex((t) => t.peerId === obj.peerId) + ) + ); + + console.log("remoteStreams", remoteStreams); + }, [remoteStreams.length]); + + useEffect(() => { + if (permission === undefined) return; + + startCalls(); + }, [permission]); + useEffect(() => { document.title = t("title"); }, [i18n.language]); @@ -369,7 +398,7 @@ function StreamPage2() { useEffect(() => { if (!name) return; - setName(() => name.trim()); + setName(name.trim()); }, [name]); useEffect(() => { @@ -397,6 +426,11 @@ function StreamPage2() { socket.on("update", (roomUsers: IUser[]) => { setUsers(roomUsers); + // for (const user of roomUsers) { + // setRemoteStreams( + // remoteStreams.filter(({ peerId }) => peerId === user.peerId) + // ); + // } }); socket.on("message", (message: IMessage) => { @@ -444,24 +478,8 @@ function StreamPage2() { window.location.reload(); } }); - - liveKitConnect(); }, [socket]); - useEffect(() => { - console.log(liveKitRoom); - - if (!liveKitRoom) return; - - liveKitRoom.on(RoomEvent.TrackMuted, (_, participant) => { - console.log(participant); - }); - - liveKitRoom.on(RoomEvent.TrackUnmuted, (_, participant) => { - console.log(participant); - }); - }, [liveKitRoom]); - useEffect(() => { if (!isMobile) return; if (isShowUsers) { @@ -535,12 +553,17 @@ function StreamPage2() { )} -
@@ -678,9 +701,9 @@ function StreamPage2() {
@@ -746,6 +769,31 @@ function StreamPage2() { } >
)} + +
+
{(isShowUsers || isShowChat) && ( @@ -1074,46 +1122,31 @@ function StreamPage2() {

- Разрешите использование микрофона + Разрешите использование камеры и микрофона

- Выключить микрофон можно в любой момент + Выключить камеру и микрофон можно в любой момент

- {liveKitRoom ? ( -
- - -
- ) : ( -
- - - - Подключение к голосовому серверу - - -
- )} +
+ + +
)} diff --git a/src/pages/StreamPage3.tsx b/src/pages/StreamPage3.tsx new file mode 100644 index 0000000..9e79202 --- /dev/null +++ b/src/pages/StreamPage3.tsx @@ -0,0 +1,58 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable react-hooks/exhaustive-deps */ +import { useEffect, useState } from "react"; +import { PixelStreamingWrapper2 } from "../components/PixelStreamingWrapper2"; +import api from "../utils/api"; +import { useParams } from "react-router-dom"; + +function StreamPage3() { + const params = useParams(); + const [WSUrl, setWSUrl] = useState(""); + + async function getActiveSession() { + const activeSession: any = await api + .get(`activeSessions/${params.id}`) + .json(); + + // if (activeSession?.endAt) { + // setEndAt(activeSession.endAt); + // } + + return activeSession; + } + + async function getWSUrl() { + const activeSession = await getActiveSession(); + + if (!activeSession || activeSession.status === "error") { + return; + } + + setWSUrl( + `wss://${activeSession.location}.sess.stream.graff.tech/${activeSession.name}/${activeSession.cirrusPort}/` + ); + } + + useEffect(() => { + getWSUrl(); + }, []); + + return ( +
+ {WSUrl && ( + + )} +
+ ); +} + +export default StreamPage3; diff --git a/src/stores/useStreamStore.ts b/src/stores/useStreamStore.ts new file mode 100644 index 0000000..4c4e018 --- /dev/null +++ b/src/stores/useStreamStore.ts @@ -0,0 +1,26 @@ +import { create } from "zustand"; +import { devtools, persist } from "zustand/middleware"; + +interface State { + name: string; +} + +interface Actions { + setName: (name: string) => void; +} + +const useStreamStore = create()( + devtools( + persist( + (set) => ({ + name: "", + setName: (name) => set({ name }), + }), + { + name: "auth", + } + ) + ) +); + +export default useStreamStore; diff --git a/src/types/IActiveSession.ts b/src/types/IActiveSession.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/types/IUser.ts b/src/types/IUser.ts index f9fae11..b889ef5 100644 --- a/src/types/IUser.ts +++ b/src/types/IUser.ts @@ -5,6 +5,7 @@ interface IUser { isAdmin: boolean; isControlAllowed: boolean; isMicAllowed: boolean; + peerId: string; } export default IUser; diff --git a/yarn.lock b/yarn.lock index dede04c..f0b049e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,6 +43,13 @@ dependencies: sdp "^3.1.0" +"@epicgames-ps/lib-pixelstreamingfrontend-ue5.5@^0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@epicgames-ps/lib-pixelstreamingfrontend-ue5.5/-/lib-pixelstreamingfrontend-ue5.5-0.0.12.tgz#8840133405b811d12fd7ac79bec1b75db274b743" + integrity sha512-vLJTrjuxhG+TqDhTK+ETwEmN0jSorBMdKG5AH2C01jL6IRFI21ipxD8lQ9ySq9vnfjXmhh036pEtJwR5DlY6sQ== + dependencies: + sdp "^3.1.0" + "@esbuild/android-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" @@ -293,6 +300,11 @@ resolved "https://registry.yarnpkg.com/@livekit/components-styles/-/components-styles-1.0.10.tgz#41becdb7649629e586daea02b0b8bf4375f75ccf" integrity sha512-WCTtXnMAcZiXgo2N+1SmlcpPwltpGp8fW+x83P9OnHHTK3qrKEzfcfKi1z0xS8VMu/KKLgJm+yhAlCmZPme/RA== +"@msgpack/msgpack@^2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-2.8.0.tgz#4210deb771ee3912964f14a15ddfb5ff877e70b9" + integrity sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -437,6 +449,13 @@ dependencies: undici-types "~5.26.4" +"@types/peerjs@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/peerjs/-/peerjs-1.1.0.tgz#822962d78b26dc43c113fac0a8bf653e12851487" + integrity sha512-dVocsfYFg5QQuUB9OAxfrSvz4br4pyX+7M61ZJSRiYtE3NdayShk1p1Y8b9TmCj724TwHskVraeF7wyPl0rYcg== + dependencies: + peerjs "*" + "@types/prop-types@*": version "15.7.11" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" @@ -1095,6 +1114,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -1849,6 +1873,21 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +peerjs-js-binarypack@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/peerjs-js-binarypack/-/peerjs-js-binarypack-2.1.0.tgz#f0fc822d3cb54ab1022f4bd580308475e8f77b70" + integrity sha512-YIwCC+pTzp3Bi8jPI9UFKO0t0SLo6xALnHkiNt/iUFmUUZG0fEEmEyFKvjsDKweiFitzHRyhuh6NvyJZ4nNxMg== + +peerjs@*, peerjs@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/peerjs/-/peerjs-1.5.4.tgz#bcf933406d07fad9b2a34ae2e8215ba3f1878672" + integrity sha512-yFsoLMnurJKlQbx6kVSBpOp+AlNldY1JQS2BrSsHLKCZnq6t7saHleuHM5svuLNbQkUJXHLF3sKOJB1K0xulOw== + dependencies: + "@msgpack/msgpack" "^2.8.0" + eventemitter3 "^4.0.7" + peerjs-js-binarypack "^2.1.0" + webrtc-adapter "^9.0.0" + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -2471,6 +2510,11 @@ use-clipboard-copy@^0.2.0: dependencies: clipboard-copy "^3.0.0" +use-is-audio-active@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/use-is-audio-active/-/use-is-audio-active-1.0.0.tgz#4e0fcb99c9d75782da3f2e2cf7f7da00bf5da397" + integrity sha512-8GZaxCe7DaBi1VeDDiHY9+aO1vCb0OCQb6+Gwf3j46Nxo/sITcgPmKKt0TiJVgbDlbEVP4RWu9krpadA57V6Ag== + use-sync-external-store@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" @@ -2530,6 +2574,13 @@ webrtc-adapter@^8.1.1: dependencies: sdp "^3.2.0" +webrtc-adapter@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-9.0.1.tgz#d4efa22ca9604cb2c8cdb9e492815ba37acfa0b2" + integrity sha512-1AQO+d4ElfVSXyzNVTOewgGT/tAomwwztX/6e3totvyyzXPvXIIuUUjAmyZGbKBKbZOXauuJooZm3g6IuFuiNQ== + dependencies: + sdp "^3.2.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"