From d65861ad54cccba6a22a865d600f4b3d499c542b Mon Sep 17 00:00:00 2001 From: a-wing <1@233.email> Date: Tue, 26 Dec 2023 05:04:02 +0800 Subject: [PATCH] feat(webapp): add presentation --- webapp/components/layout.tsx | 55 ++++++++++++++---------- webapp/components/player/whep-player.tsx | 21 ++++++++- webapp/components/player/whip-player.tsx | 14 ++++++ webapp/store/atom.ts | 12 ++++++ 4 files changed, 77 insertions(+), 25 deletions(-) diff --git a/webapp/components/layout.tsx b/webapp/components/layout.tsx index ed26663..3a4632d 100644 --- a/webapp/components/layout.tsx +++ b/webapp/components/layout.tsx @@ -1,10 +1,17 @@ import { useEffect, useState } from 'react' import { useAtom } from 'jotai' import Member from './member' +import Player from './player/player' import WhipPlayer from './player/whip-player' import WhepPlayer from './player/whep-player' import DeviceBar from './device' -import { UserStatus, localStreamIdAtom, meetingJoinedAtom } from '../store/atom' +import { + UserStatus, + enabledPresentationAtom, + localStreamIdAtom, + meetingJoinedAtom, + presentationStreamAtom, +} from '../store/atom' import copy from 'copy-to-clipboard' import SvgDone from './svg/done' import SvgEnd from './svg/end' @@ -16,8 +23,10 @@ export default function Layout(props: { meetingId: string }) { const [localStreamId] = useAtom(localStreamIdAtom) const [remoteUserStatus, setRemoteUserStatus] = useState<{ [_: string]: UserStatus }>({}) - const [speaker, setSpeaker] = useState(null) - const [speakerId, setSpeakerId] = useState("") + //const [speaker, setSpeaker] = useState(null) + //const [speakerId, setSpeakerId] = useState("") + const [enabledPresentation] = useAtom(enabledPresentationAtom) + const [presentationStream] = useAtom(presentationStreamAtom) const refresh = async () => { let res = await fetch(location.origin + `/room/${props.meetingId}`) @@ -38,20 +47,18 @@ export default function Layout(props: { meetingId: string }) { setMeetingJoined(false) } - useEffect(() => { - let shareScreenId = "" - const setShareScreenId = (id: string) => shareScreenId = id - Object.keys(remoteUserStatus).map(i => remoteUserStatus[i].screen && setShareScreenId(i)) - - if (!shareScreenId) { - setSpeakerId("") - setSpeaker(null) - } else { - setSpeakerId(shareScreenId) - setSpeaker(remoteUserStatus[shareScreenId]) - } - - }, [remoteUserStatus]) + //useEffect(() => { + // let shareScreenId = "" + // const setShareScreenId = (id: string) => shareScreenId = id + // Object.keys(remoteUserStatus).map(i => remoteUserStatus[i].screen && setShareScreenId(i)) + // if (!shareScreenId) { + // setSpeakerId("") + // setSpeaker(null) + // } else { + // setSpeakerId(shareScreenId) + // setSpeaker(remoteUserStatus[shareScreenId]) + // } + //}, [remoteUserStatus]) useEffect(() => { const handle = setInterval(refresh, 3000) @@ -62,14 +69,16 @@ export default function Layout(props: { meetingId: string }) {
- {!speaker - ?
- - {Object.keys(remoteUserStatus).map(i => )} -
- : + { enabledPresentation + ? + : null } +
+ + {Object.keys(remoteUserStatus).map(i => )} +
+
diff --git a/webapp/components/player/whep-player.tsx b/webapp/components/player/whep-player.tsx index f5e3695..ec15b7f 100644 --- a/webapp/components/player/whep-player.tsx +++ b/webapp/components/player/whep-player.tsx @@ -1,7 +1,12 @@ import { useEffect, useRef, useState } from 'react' import Player from './player' -import { UserStream, UserStatus } from '../../store/atom' +import { + UserStream, + UserStatus, + presentationStreamAtom, +} from '../../store/atom' import { WHEPClient } from '@binbat/whip-whep/whep' +import { useAtom } from 'jotai' export default function WhepPlayer(props: { streamId: string, status: UserStatus, width: string }) { const refEnabled = useRef(false) @@ -11,6 +16,7 @@ export default function WhepPlayer(props: { streamId: string, status: UserStatus stream: new MediaStream, name: props.streamId, }) + const [presentationStream, setPresentationStream] = useAtom(presentationStreamAtom) const newPeerConnection = () => { const pc = new RTCPeerConnection() @@ -57,7 +63,18 @@ export default function WhepPlayer(props: { streamId: string, status: UserStatus if (props.status.state === "connected") { restart(props.streamId) } - }, [props.status.state, props.status.audio, props.status.video, props.status.screen]) + }, [props.status.state, props.status.audio, props.status.video]) + + useEffect(() => { + if (props.status.screen) { + setPresentationStream(userStream) + } else { + setPresentationStream({ + stream: new MediaStream, + name: presentationStream.name + }) + } + }, [props.status.screen]) return (
diff --git a/webapp/components/player/whip-player.tsx b/webapp/components/player/whip-player.tsx index 23f70b1..f0704a8 100644 --- a/webapp/components/player/whip-player.tsx +++ b/webapp/components/player/whip-player.tsx @@ -2,12 +2,15 @@ import { useEffect, useRef, useState } from 'react' import { useAtom } from 'jotai' import { localStreamAtom, + presentationStreamAtom, + localUserStatusAtom, currentDeviceAudioAtom, currentDeviceVideoAtom, } from '../../store/atom' import Player from './player' import { WHIPClient } from '@binbat/whip-whep/whip' +import { deviceScreen } from '../../lib/device' export default function WhipPlayer(props: { streamId: string, width: string }) { const refEnabled = useRef(false) @@ -19,6 +22,8 @@ export default function WhipPlayer(props: { streamId: string, width: string }) { const [currentDeviceAudio] = useAtom(currentDeviceAudioAtom) const [currentDeviceVideo] = useAtom(currentDeviceVideoAtom) + const [presentationStream, setPresentationStream] = useAtom(presentationStreamAtom) + const newPeerConnection = () => { const stream = localStream.stream if (stream) { @@ -108,6 +113,15 @@ export default function WhipPlayer(props: { streamId: string, width: string }) { }, [currentDeviceAudio]) useEffect(() => { + if (currentDeviceVideo === deviceScreen.deviceId) { + setPresentationStream(localStream) + } else { + setPresentationStream({ + stream: new MediaStream, + name: presentationStream.name + }) + } + const mediaStream = localStream.stream // If WebRTC is connected, switch track // NOTE: array video index is: 1 diff --git a/webapp/store/atom.ts b/webapp/store/atom.ts index 6cda82a..3ca6286 100644 --- a/webapp/store/atom.ts +++ b/webapp/store/atom.ts @@ -41,6 +41,15 @@ const localStreamAtom = atom({ }) localStreamAtom.debugLabel = 'localStream' +const presentationStreamAtom = atom({ + stream: new MediaStream, + name: "Presentation", +}) +presentationStreamAtom.debugLabel = 'presentationStream' + +const enabledPresentationAtom = atom(get => get(presentationStreamAtom).stream.getVideoTracks().length !== 0) +enabledPresentationAtom.debugLabel = 'enabledPresentation' + const enabledAudioAtom = atom(get => get(localStreamAtom).stream.getAudioTracks().length !== 0) enabledAudioAtom.debugLabel = 'enabledAudio' const enabledVideoAtom = atom(get => get(localStreamAtom).stream.getVideoTracks().length !== 0) @@ -73,11 +82,14 @@ export { remoteUsersStatusAtom, locationAtom, + presentationStreamAtom, + meetingIdAtom, meetingJoinedAtom, localStreamAtom, enabledAudioAtom, enabledVideoAtom, + enabledPresentationAtom, currentDeviceAudioAtom, currentDeviceVideoAtom, }