From 75a804abbde481fb3b78e3c1a079feb6b9a22aca Mon Sep 17 00:00:00 2001 From: Jeremy Kahn Date: Sun, 13 Nov 2022 17:11:09 -0600 Subject: [PATCH] feat: [closes #67] Screen sharing (#68) * feat: [#67] stand up useRoomScreenShare hook * feat: [#67] stand up RoomScreenShareControls * feat: [#67] display screen share streams * fix: [#67] don't flip screen share preview * feat: don't display screen share controls in unsupported environments * fix: [#67] always remove media streams for exiting peers --- package-lock.json | 13 +- package.json | 2 +- src/components/Room/PeerVideo.tsx | 12 +- src/components/Room/Room.tsx | 2 + .../Room/RoomScreenShareControls.tsx | 58 +++++++ src/components/Room/RoomVideoControls.tsx | 2 +- src/components/Room/RoomVideoDisplay.tsx | 63 +++++-- src/components/Room/useRoom.ts | 31 +++- src/components/Room/useRoomScreenShare.ts | 159 ++++++++++++++++++ src/components/Room/useRoomVideo.ts | 30 +++- src/components/Shell/Shell.tsx | 9 +- src/contexts/RoomContext.ts | 8 + src/contexts/ShellContext.ts | 6 +- src/models/chat.ts | 11 ++ src/models/network.ts | 1 + src/services/PeerRoom/PeerRoom.ts | 6 +- src/utils.ts | 8 + 17 files changed, 377 insertions(+), 44 deletions(-) create mode 100644 src/components/Room/RoomScreenShareControls.tsx create mode 100644 src/components/Room/useRoomScreenShare.ts diff --git a/package-lock.json b/package-lock.json index 37903c2..36156d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "react-syntax-highlighter": "^15.5.0", "remark-gfm": "^3.0.1", "sass": "^1.54.3", - "trystero": "^0.11.4", + "trystero": "github:jeremyckahn/trystero#bugfix/stream-metadata-type", "typeface-public-sans": "^1.1.13", "typeface-roboto": "^1.1.13", "typescript": "^4.7.4", @@ -22977,9 +22977,9 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, "node_modules/trystero": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/trystero/-/trystero-0.11.4.tgz", - "integrity": "sha512-x5SkPXlodoNhs1o2TLjarABfBLPtuR2iKovk3mtboCCr349eclpGrK3JPjs4Rp+coAMqC+l/ZcC39GCR9VCvlQ==", + "version": "0.11.6", + "resolved": "git+ssh://git@github.com/jeremyckahn/trystero.git#b270de4b79b2fef1ca550187b6c0fbe72d4e8118", + "license": "MIT", "dependencies": { "firebase": "^9.6.5", "ipfs-core": "0.9.0", @@ -41257,9 +41257,8 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, "trystero": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/trystero/-/trystero-0.11.4.tgz", - "integrity": "sha512-x5SkPXlodoNhs1o2TLjarABfBLPtuR2iKovk3mtboCCr349eclpGrK3JPjs4Rp+coAMqC+l/ZcC39GCR9VCvlQ==", + "version": "git+ssh://git@github.com/jeremyckahn/trystero.git#b270de4b79b2fef1ca550187b6c0fbe72d4e8118", + "from": "trystero@github:jeremyckahn/trystero#bugfix/stream-metadata-type", "requires": { "firebase": "^9.6.5", "ipfs-core": "0.9.0", diff --git a/package.json b/package.json index 956d447..658550e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "react-syntax-highlighter": "^15.5.0", "remark-gfm": "^3.0.1", "sass": "^1.54.3", - "trystero": "^0.11.4", + "trystero": "github:jeremyckahn/trystero#bugfix/stream-metadata-type", "typeface-public-sans": "^1.1.13", "typeface-roboto": "^1.1.13", "typescript": "^4.7.4", diff --git a/src/components/Room/PeerVideo.tsx b/src/components/Room/PeerVideo.tsx index 7099e09..e8ae900 100644 --- a/src/components/Room/PeerVideo.tsx +++ b/src/components/Room/PeerVideo.tsx @@ -4,8 +4,8 @@ import Paper from '@mui/material/Paper' import { PeerNameDisplay } from 'components/PeerNameDisplay' interface PeerVideoProps { - isSelf?: boolean - numberOfPeers: number + isSelfVideo?: boolean + numberOfVideos: number userId: string videoStream: MediaStream } @@ -18,8 +18,8 @@ const nextPerfectSquare = (base: number) => { } export const PeerVideo = ({ - isSelf, - numberOfPeers, + isSelfVideo, + numberOfVideos, userId, videoStream, }: PeerVideoProps) => { @@ -33,7 +33,7 @@ export const PeerVideo = ({ video.srcObject = videoStream }, [videoRef, videoStream]) - const sizePercent = 100 / Math.sqrt(nextPerfectSquare(numberOfPeers - 1)) + const sizePercent = 100 / Math.sqrt(nextPerfectSquare(numberOfVideos - 1)) return ( + diff --git a/src/components/Room/RoomScreenShareControls.tsx b/src/components/Room/RoomScreenShareControls.tsx new file mode 100644 index 0000000..54e8117 --- /dev/null +++ b/src/components/Room/RoomScreenShareControls.tsx @@ -0,0 +1,58 @@ +import Box from '@mui/material/Box' +import ScreenShare from '@mui/icons-material/ScreenShare' +import StopScreenShare from '@mui/icons-material/StopScreenShare' +import Fab from '@mui/material/Fab' +import Tooltip from '@mui/material/Tooltip' + +import { PeerRoom } from 'services/PeerRoom/PeerRoom' + +import { useRoomScreenShare } from './useRoomScreenShare' + +export interface RoomVideoControlsProps { + peerRoom: PeerRoom +} + +export function RoomScreenShareControls({ peerRoom }: RoomVideoControlsProps) { + const { isSharingScreen, handleScreenShareStart, handleScreenShareStop } = + useRoomScreenShare({ + peerRoom, + }) + + const handleToggleScreenShareButtonClick = () => { + if (isSharingScreen) { + handleScreenShareStop() + } else { + handleScreenShareStart() + } + } + + if (!window.navigator?.mediaDevices?.getDisplayMedia) { + return <> + } + + return ( + + + + {isSharingScreen ? : } + + + + ) +} diff --git a/src/components/Room/RoomVideoControls.tsx b/src/components/Room/RoomVideoControls.tsx index 613beae..2ad612e 100644 --- a/src/components/Room/RoomVideoControls.tsx +++ b/src/components/Room/RoomVideoControls.tsx @@ -66,7 +66,7 @@ export function RoomVideoControls({ peerRoom }: RoomVideoControlsProps) { {isCameraEnabled ? : } diff --git a/src/components/Room/RoomVideoDisplay.tsx b/src/components/Room/RoomVideoDisplay.tsx index ebc77d0..b1c0a59 100644 --- a/src/components/Room/RoomVideoDisplay.tsx +++ b/src/components/Room/RoomVideoDisplay.tsx @@ -1,4 +1,4 @@ -import { useContext } from 'react' +import { Fragment, useContext } from 'react' import Paper from '@mui/material/Paper' import { RoomContext } from 'contexts/RoomContext' @@ -7,7 +7,11 @@ import { Peer } from 'models/chat' import { PeerVideo } from './PeerVideo' -type PeerWithVideo = { peer: Peer; videoStream: MediaStream } +type PeerWithVideo = { + peer: Peer + videoStream?: MediaStream + screenStream?: MediaStream +} export interface RoomVideoDisplayProps { userId: string @@ -18,15 +22,23 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => { const roomContext = useContext(RoomContext) const { peerList } = shellContext - const { peerVideoStreams, selfVideoStream } = roomContext + const { + peerVideoStreams, + selfVideoStream, + peerScreenStreams, + selfScreenStream, + } = roomContext const peersWithVideo: PeerWithVideo[] = peerList.reduce( (acc: PeerWithVideo[], peer: Peer) => { const videoStream = peerVideoStreams[peer.peerId] - if (videoStream) { + const screenStream = peerScreenStreams[peer.peerId] + + if (videoStream || screenStream) { acc.push({ peer, videoStream, + screenStream, }) } @@ -35,7 +47,15 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => { [] ) - const numberOfPeers = (selfVideoStream ? 1 : 0) + peersWithVideo.length + const numberOfVideos = + (selfVideoStream ? 1 : 0) + + (selfScreenStream ? 1 : 0) + + peersWithVideo.reduce((sum, peerWithVideo) => { + if (peerWithVideo.videoStream) sum++ + if (peerWithVideo.screenStream) sum++ + + return sum + }, 0) return ( { alignContent: 'center', alignItems: 'center', display: 'flex', - flexDirection: numberOfPeers === 1 ? 'column' : 'row', + flexDirection: numberOfVideos === 1 ? 'column' : 'row', flexGrow: 1, flexWrap: 'wrap', justifyContent: 'center', @@ -56,19 +76,36 @@ export const RoomVideoDisplay = ({ userId }: RoomVideoDisplayProps) => { > {selfVideoStream && ( )} - {peersWithVideo.map(peerWithVideo => ( + {selfScreenStream && ( + )} + {peersWithVideo.map(peerWithVideo => ( + + {peerWithVideo.videoStream && ( + + )} + {peerWithVideo.screenStream && ( + + )} + ))} ) diff --git a/src/components/Room/useRoom.ts b/src/components/Room/useRoom.ts index 50d99a3..6eed633 100644 --- a/src/components/Room/useRoom.ts +++ b/src/components/Room/useRoom.ts @@ -13,6 +13,7 @@ import { ReceivedMessage, UnsentMessage, VideoState, + ScreenShareState, isMessageReceived, } from 'models/chat' import { getPeerName } from 'components/PeerNameDisplay' @@ -62,14 +63,34 @@ export function useRoom( Record >({}) + const [selfScreenStream, setSelfScreenStream] = useState( + null + ) + const [peerScreenStreams, setPeerScreenStreams] = useState< + Record + >({}) + const roomContextValue = useMemo( () => ({ selfVideoStream, setSelfVideoStream, peerVideoStreams, setPeerVideoStreams, + selfScreenStream, + setSelfScreenStream, + peerScreenStreams, + setPeerScreenStreams, }), - [selfVideoStream, setSelfVideoStream, peerVideoStreams, setPeerVideoStreams] + [ + selfVideoStream, + setSelfVideoStream, + peerVideoStreams, + setPeerVideoStreams, + selfScreenStream, + setSelfScreenStream, + peerScreenStreams, + setPeerScreenStreams, + ] ) useEffect(() => { @@ -131,6 +152,7 @@ export function useRoom( userId, audioState: AudioState.STOPPED, videoState: VideoState.STOPPED, + screenShareState: ScreenShareState.NOT_SHARING, }, ]) } else { @@ -216,8 +238,11 @@ export function useRoom( } }) - const showVideoDisplay = - selfVideoStream || Object.values(peerVideoStreams).length > 0 + const showVideoDisplay = Boolean( + selfVideoStream || + selfScreenStream || + Object.values({ ...peerVideoStreams, ...peerScreenStreams }).length > 0 + ) return { isMessageSending, diff --git a/src/components/Room/useRoomScreenShare.ts b/src/components/Room/useRoomScreenShare.ts new file mode 100644 index 0000000..64e781e --- /dev/null +++ b/src/components/Room/useRoomScreenShare.ts @@ -0,0 +1,159 @@ +import { useContext, useEffect, useCallback, useState } from 'react' + +import { isRecord } from 'utils' +import { RoomContext } from 'contexts/RoomContext' +import { ShellContext } from 'contexts/ShellContext' +import { PeerActions } from 'models/network' +import { ScreenShareState, Peer, VideoStreamType } from 'models/chat' +import { PeerRoom, PeerHookType, PeerStreamType } from 'services/PeerRoom' + +import { usePeerRoomAction } from './usePeerRoomAction' + +interface UseRoomScreenShareConfig { + peerRoom: PeerRoom +} + +export function useRoomScreenShare({ peerRoom }: UseRoomScreenShareConfig) { + const shellContext = useContext(ShellContext) + const roomContext = useContext(RoomContext) + const [isSharingScreen, setIsSharingScreen] = useState(false) + + const { peerList, setPeerList, setScreenState } = shellContext + + const { + peerScreenStreams, + selfScreenStream, + setPeerScreenStreams, + setSelfScreenStream, + } = roomContext + + const [sendScreenShare, receiveScreenShare] = + usePeerRoomAction(peerRoom, PeerActions.SCREEN_SHARE) + + receiveScreenShare((screenState, peerId) => { + const newPeerList = peerList.map(peer => { + const newPeer: Peer = { ...peer } + + if (peer.peerId === peerId) { + newPeer.screenShareState = screenState + + if (screenState === ScreenShareState.NOT_SHARING) { + deletePeerScreen(peerId) + } + } + + return newPeer + }) + + setPeerList(newPeerList) + }) + + peerRoom.onPeerStream(PeerStreamType.SCREEN, (stream, peerId, metadata) => { + const isScreenShareStream = + isRecord(metadata) && + 'type' in metadata && + metadata.type === VideoStreamType.SCREEN_SHARE + + if (!isScreenShareStream) return + + setPeerScreenStreams({ + ...peerScreenStreams, + [peerId]: stream, + }) + }) + + const cleanupScreenStream = useCallback(() => { + if (!selfScreenStream) return + + for (const screenStreamTrack of selfScreenStream.getTracks()) { + screenStreamTrack.stop() + selfScreenStream.removeTrack(screenStreamTrack) + } + }, [selfScreenStream]) + + const handleScreenShareStart = async () => { + if (selfScreenStream) return + + const displayMedia = await window.navigator.mediaDevices.getDisplayMedia({ + audio: true, + video: true, + }) + + peerRoom.addStream(displayMedia, null, { + type: VideoStreamType.SCREEN_SHARE, + }) + setSelfScreenStream(displayMedia) + sendScreenShare(ScreenShareState.SHARING) + setScreenState(ScreenShareState.SHARING) + setIsSharingScreen(true) + } + + const handleScreenShareStop = () => { + if (!selfScreenStream) return + + cleanupScreenStream() + peerRoom.removeStream(selfScreenStream, peerRoom.getPeers()) + sendScreenShare(ScreenShareState.NOT_SHARING) + setScreenState(ScreenShareState.NOT_SHARING) + setSelfScreenStream(null) + setIsSharingScreen(false) + } + + useEffect(() => { + return () => { + cleanupScreenStream() + } + }, [cleanupScreenStream]) + + useEffect(() => { + return () => { + if (selfScreenStream) { + setSelfScreenStream(null) + setScreenState(ScreenShareState.NOT_SHARING) + } + } + }, [selfScreenStream, setSelfScreenStream, setScreenState]) + + useEffect(() => { + return () => { + setPeerScreenStreams({}) + } + }, [setPeerScreenStreams]) + + const deletePeerScreen = (peerId: string) => { + const newPeerScreens = { ...peerScreenStreams } + delete newPeerScreens[peerId] + setPeerScreenStreams(newPeerScreens) + } + + const handleScreenForNewPeer = (peerId: string) => { + if (selfScreenStream) { + peerRoom.addStream(selfScreenStream, peerId, { + type: VideoStreamType.SCREEN_SHARE, + }) + } + } + + const handleScreenForLeavingPeer = (peerId: string) => { + if (selfScreenStream) { + peerRoom.removeStream(selfScreenStream, peerId) + } + + deletePeerScreen(peerId) + } + + peerRoom.onPeerJoin(PeerHookType.SCREEN, (peerId: string) => { + handleScreenForNewPeer(peerId) + }) + + peerRoom.onPeerLeave(PeerHookType.SCREEN, (peerId: string) => { + handleScreenForLeavingPeer(peerId) + }) + + return { + handleScreenShareStart, + handleScreenShareStop, + isSharingScreen, + setIsSharingScreen, + } +} diff --git a/src/components/Room/useRoomVideo.ts b/src/components/Room/useRoomVideo.ts index 252381d..145e103 100644 --- a/src/components/Room/useRoomVideo.ts +++ b/src/components/Room/useRoomVideo.ts @@ -3,9 +3,11 @@ import { useContext, useEffect, useCallback, useState } from 'react' import { RoomContext } from 'contexts/RoomContext' import { ShellContext } from 'contexts/ShellContext' import { PeerActions } from 'models/network' -import { VideoState, Peer } from 'models/chat' +import { VideoState, Peer, VideoStreamType } from 'models/chat' import { PeerRoom, PeerHookType, PeerStreamType } from 'services/PeerRoom' +import { isRecord } from 'utils' + import { usePeerRoomAction } from './usePeerRoomAction' interface UseRoomVideoConfig { @@ -60,7 +62,9 @@ export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { }, }) - peerRoom.addStream(newSelfStream) + peerRoom.addStream(newSelfStream, null, { + type: VideoStreamType.WEBCAM, + }) setSelfVideoStream(newSelfStream) setSelfVideoStream(newSelfStream) @@ -91,10 +95,13 @@ export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { setPeerList(newPeerList) }) - peerRoom.onPeerStream(PeerStreamType.VIDEO, (stream, peerId) => { - const videoTracks = stream.getVideoTracks() + peerRoom.onPeerStream(PeerStreamType.VIDEO, (stream, peerId, metadata) => { + const isWebcamStream = + isRecord(metadata) && + 'type' in metadata && + metadata.type === VideoStreamType.WEBCAM - if (videoTracks.length === 0) return + if (!isWebcamStream) return setPeerVideoStreams({ ...peerVideoStreams, @@ -122,7 +129,9 @@ export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { : true, }) - peerRoom.addStream(newSelfStream) + peerRoom.addStream(newSelfStream, null, { + type: VideoStreamType.WEBCAM, + }) sendVideoChange(VideoState.PLAYING) setVideoState(VideoState.PLAYING) setSelfVideoStream(newSelfStream) @@ -191,7 +200,7 @@ export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { }, }) - peerRoom.addStream(newSelfStream) + peerRoom.addStream(newSelfStream, null, { type: VideoStreamType.WEBCAM }) setSelfVideoStream(newSelfStream) } @@ -203,15 +212,18 @@ export function useRoomVideo({ peerRoom }: UseRoomVideoConfig) { const handleVideoForNewPeer = (peerId: string) => { if (selfVideoStream) { - peerRoom.addStream(selfVideoStream, peerId) + peerRoom.addStream(selfVideoStream, peerId, { + type: VideoStreamType.WEBCAM, + }) } } const handleVideoForLeavingPeer = (peerId: string) => { if (selfVideoStream) { peerRoom.removeStream(selfVideoStream, peerId) - deletePeerVideo(peerId) } + + deletePeerVideo(peerId) } peerRoom.onPeerJoin(PeerHookType.VIDEO, (peerId: string) => { diff --git a/src/components/Shell/Shell.tsx b/src/components/Shell/Shell.tsx index ea617c0..c06447e 100644 --- a/src/components/Shell/Shell.tsx +++ b/src/components/Shell/Shell.tsx @@ -15,7 +15,7 @@ import { AlertColor } from '@mui/material/Alert' import { ShellContext } from 'contexts/ShellContext' import { SettingsContext } from 'contexts/SettingsContext' import { AlertOptions } from 'models/shell' -import { AudioState, VideoState, Peer } from 'models/chat' +import { AudioState, ScreenShareState, VideoState, Peer } from 'models/chat' import { ErrorBoundary } from 'components/ErrorBoundary' import { Drawer } from './Drawer' @@ -46,6 +46,9 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => { const [tabHasFocus, setTabHasFocus] = useState(true) const [audioState, setAudioState] = useState(AudioState.STOPPED) const [videoState, setVideoState] = useState(VideoState.STOPPED) + const [screenState, setScreenState] = useState( + ScreenShareState.NOT_SHARING + ) const showAlert = useCallback< (message: string, options?: AlertOptions) => void @@ -72,6 +75,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => { setAudioState, videoState, setVideoState, + screenState, + setScreenState, }), [ isPeerListOpen, @@ -87,6 +92,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => { setAudioState, videoState, setVideoState, + screenState, + setScreenState, ] ) diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 6261025..01597ef 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -5,6 +5,10 @@ interface RoomContextProps { setSelfVideoStream: Dispatch> peerVideoStreams: Record setPeerVideoStreams: Dispatch>> + selfScreenStream: MediaStream | null + setSelfScreenStream: Dispatch> + peerScreenStreams: Record + setPeerScreenStreams: Dispatch>> } export const RoomContext = createContext({ @@ -12,4 +16,8 @@ export const RoomContext = createContext({ setSelfVideoStream: () => {}, peerVideoStreams: {}, setPeerVideoStreams: () => {}, + selfScreenStream: null, + setSelfScreenStream: () => {}, + peerScreenStreams: {}, + setPeerScreenStreams: () => {}, }) diff --git a/src/contexts/ShellContext.ts b/src/contexts/ShellContext.ts index 46e3b49..da6fd11 100644 --- a/src/contexts/ShellContext.ts +++ b/src/contexts/ShellContext.ts @@ -1,7 +1,7 @@ import { createContext, Dispatch, SetStateAction } from 'react' import { AlertOptions } from 'models/shell' -import { AudioState, VideoState, Peer } from 'models/chat' +import { AudioState, ScreenShareState, VideoState, Peer } from 'models/chat' interface ShellContextProps { numberOfPeers: number @@ -18,6 +18,8 @@ interface ShellContextProps { setAudioState: Dispatch> videoState: VideoState setVideoState: Dispatch> + screenState: ScreenShareState + setScreenState: Dispatch> } export const ShellContext = createContext({ @@ -35,4 +37,6 @@ export const ShellContext = createContext({ setAudioState: () => {}, videoState: VideoState.STOPPED, setVideoState: () => {}, + screenState: ScreenShareState.NOT_SHARING, + setScreenState: () => {}, }) diff --git a/src/models/chat.ts b/src/models/chat.ts index a0812ab..ee24600 100644 --- a/src/models/chat.ts +++ b/src/models/chat.ts @@ -15,11 +15,22 @@ export enum VideoState { STOPPED = 'STOPPED', } +export enum VideoStreamType { + WEBCAM = 'WEBCAM', + SCREEN_SHARE = 'SCREEN_SHARE', +} + +export enum ScreenShareState { + SHARING = 'SHARING', + NOT_SHARING = 'NOT_SHARING', +} + export interface Peer { peerId: string userId: string audioState: AudioState videoState: VideoState + screenShareState: ScreenShareState } export interface ReceivedMessage extends UnsentMessage { diff --git a/src/models/network.ts b/src/models/network.ts index 1ce0f99..feff862 100644 --- a/src/models/network.ts +++ b/src/models/network.ts @@ -5,4 +5,5 @@ export enum PeerActions { PEER_NAME = 'PEER_NAME', AUDIO_CHANGE = 'AUDIO_CHANGE', VIDEO_CHANGE = 'VIDEO_CHANGE', + SCREEN_SHARE = 'SCREEN_SHARE', } diff --git a/src/services/PeerRoom/PeerRoom.ts b/src/services/PeerRoom/PeerRoom.ts index a7c157a..7027b8f 100644 --- a/src/services/PeerRoom/PeerRoom.ts +++ b/src/services/PeerRoom/PeerRoom.ts @@ -5,11 +5,13 @@ export enum PeerHookType { NEW_PEER = 'NEW_PEER', AUDIO = 'AUDIO', VIDEO = 'VIDEO', + SCREEN = 'SCREEN', } export enum PeerStreamType { AUDIO = 'AUDIO', VIDEO = 'VIDEO', + SCREEN = 'SCREEN', } export class PeerRoom { @@ -107,8 +109,8 @@ export class PeerRoom { return this.room.makeAction(namespace) } - addStream: Room['addStream'] = stream => { - return this.room.addStream(stream) + addStream: Room['addStream'] = (...args) => { + return this.room.addStream(...args) } removeStream: Room['removeStream'] = (stream, targetPeers) => { diff --git a/src/utils.ts b/src/utils.ts index fb9adf6..938bc98 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,3 +2,11 @@ export const sleep = (milliseconds: number): Promise => new Promise(res => { setTimeout(res, milliseconds) }) + +export const isRecord = (variable: any): variable is Record => { + return ( + typeof variable === 'object' && + !Array.isArray(variable) && + variable !== null + ) +}