Skip to content

Commit

Permalink
refactor(webapp): device bar
Browse files Browse the repository at this point in the history
  • Loading branch information
a-wing committed Jan 14, 2024
1 parent aa5e9b3 commit d8dceb1
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 236 deletions.
110 changes: 23 additions & 87 deletions webapp/components/device.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import useWhipClient from "./use/whip"
import { useEffect, useState } from 'react'
import { useAtom } from 'jotai'
import {
Device,
deviceNone,
deviceScreen,
asyncGetAudioStream,
asyncGetVideoStream,
} from '../lib/device'
import {
localStreamAtom,
enabledAudioAtom,
enabledVideoAtom,
localUserStatusAtom,
currentDeviceAudioAtom,
currentDeviceVideoAtom,
localStreamIdAtom,
} from '../store/atom'

import Loading from './svg/loading'
Expand All @@ -25,17 +19,22 @@ export default function DeviceBar() {
const [permissionAudio, setPermissionAudio] = useState("...")
const [permissionVideo, setPermissionVideo] = useState("...")

const [localStream, setLocalStream] = useAtom(localStreamAtom)
const [localUserStatus, setLocalUserStatus] = useAtom(localUserStatusAtom)
const [localStreamId] = useAtom(localStreamIdAtom)

const [loadingAudio, setLoadingAudio] = useState(false)
const [loadingVideo, setLoadingVideo] = useState(false)
const [loadingScreen, setLoadingScreen] = useState(false)

const [enabledAudio] = useAtom(enabledAudioAtom)
const [enabledVideo] = useAtom(enabledVideoAtom)
const [currentDeviceAudio, setCurrentDeviceAudio] = useAtom(currentDeviceAudioAtom)
const [currentDeviceVideo, setCurrentDeviceVideo] = useAtom(currentDeviceVideoAtom)
const {
userStatus,
currentDeviceAudio,
currentDeviceVideo,
setCurrentDeviceAudio,
setCurrentDeviceVideo,
toggleEnableAudio,
toggleEnableVideo,
} = useWhipClient(localStreamId)

const [deviceAudio, setDeviceAudio] = useState<Device[]>([deviceNone])
const [deviceVideo, setDeviceVideo] = useState<Device[]>([deviceNone])

Expand Down Expand Up @@ -70,16 +69,12 @@ export default function DeviceBar() {

if (currentDeviceAudio === deviceNone.deviceId) {
let device = audios[0]
if (device) {
setCurrentDeviceAudio(device.deviceId)
}
if (device) await setCurrentDeviceAudio(device.deviceId)
}

if (currentDeviceVideo === deviceNone.deviceId) {
let device = videos[0]
if (device) {
setCurrentDeviceVideo(device.deviceId)
}
if (device) await setCurrentDeviceVideo(device.deviceId)
}

setDeviceAudio([...audios])
Expand All @@ -88,6 +83,7 @@ export default function DeviceBar() {

const init = async () => {
try {
(await navigator.mediaDevices.getUserMedia({ video: true, audio: true })).getTracks().map(track => track.stop())
// NOTE:
// In some device have problem:
// - Android Web Browser
Expand All @@ -99,89 +95,29 @@ export default function DeviceBar() {

useEffect(() => {
init()
}, [localStream])
}, [])

useEffect(() => {
// Reference: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/devicechange_event
navigator.mediaDevices.addEventListener("devicechange", updateDeviceList)
return () => { navigator.mediaDevices.removeEventListener("devicechange", updateDeviceList) }
}, [])

const toggleEnableAudio = async () => {
if (enabledAudio) {
onChangedDeviceAudio(deviceNone.deviceId)
} else {
onChangedDeviceAudio(currentDeviceAudio)
}
}

const onChangedDeviceAudio = async (current: string) => {
setLoadingAudio(true)
// Closed old tracks
const stream = localStream.stream
stream.getAudioTracks().map(track => {
track.stop()
stream.removeTrack(track)
})

const mediaStream = await asyncGetAudioStream(current)

const videoTracks = localStream.stream.getVideoTracks()
const audioTracks = mediaStream.getAudioTracks()

setLocalStream({
stream: new MediaStream([...audioTracks, ...videoTracks]),
name: "Me",
})

setLocalUserStatus({
...localUserStatus,
audio: current === deviceNone.deviceId ? false : true,
})

current === deviceNone.deviceId ? null : setCurrentDeviceAudio(current)
await setCurrentDeviceAudio(current)
setLoadingAudio(false)
}

const toggleEnableVideo = async () => {
if (enabledVideo) {
onChangedDeviceVideo(deviceNone.deviceId)
} else {
onChangedDeviceVideo(currentDeviceVideo)
}
}

const onChangedDeviceVideo = async (current: string) => {
setLoadingVideo(true)
// Closed old tracks
const stream = localStream.stream
stream.getVideoTracks().map(track => {
track.stop()
stream.removeTrack(track)
})

const mediaStream = await asyncGetVideoStream(current)
const audioTracks = localStream.stream.getAudioTracks()
const videoTracks = mediaStream.getVideoTracks()

setLocalStream({
stream: new MediaStream([...audioTracks, ...videoTracks]),
name: "Me",
})

setLocalUserStatus({
...localUserStatus,
video: current === deviceNone.deviceId ? false : true,
screen: current === deviceScreen.deviceId ? true : false,
})

current === deviceNone.deviceId ? null : setCurrentDeviceVideo(current)
await setCurrentDeviceVideo(current)
setLoadingVideo(false)
}

const toggleEnableScreen = async () => {
setLoadingScreen(true)
if (localUserStatus.screen) {
if (userStatus.screen) {
await onChangedDeviceVideo(deviceNone.deviceId)
} else {
await onChangedDeviceVideo(deviceScreen.deviceId)
Expand All @@ -206,7 +142,7 @@ export default function DeviceBar() {
? <div></div>
: <div className='bg-orange-500 shadow-sm w-1 h-1 p-1 rounded-full' style={{ position: 'relative', right: '7px' }}></div>
}
{enabledAudio
{userStatus.audio
? <div></div>
: <div className='w-8 h-1 bg-red-500 rounded-full rotate-45'
style={{
Expand Down Expand Up @@ -241,7 +177,7 @@ export default function DeviceBar() {
? <div></div>
: <div className='bg-orange-500 shadow-sm w-1 h-1 p-1 rounded-full' style={{ position: 'relative', right: '7px' }}></div>
}
{enabledVideo
{userStatus.video
? <div></div>
: <div className='w-8 h-1 bg-red-500 rounded-full rotate-45'
style={{
Expand All @@ -268,7 +204,7 @@ export default function DeviceBar() {
<center>
{loadingScreen
? <Loading />
: localUserStatus.screen ? <SvgPresentCancel /> : <SvgPresentToAll />
: userStatus.screen ? <SvgPresentCancel /> : <SvgPresentToAll />
}
</center>
</button>
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function Layout(props: { meetingId: string }) {
<div></div>

{ enabledPresentation
? <Player user={presentationStream} muted={true} width="auto" display="auto" />
? <Player stream={presentationStream.stream} muted={true} width="auto" display="auto" />
: null
}

Expand Down
23 changes: 11 additions & 12 deletions webapp/components/player/player.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,49 @@
import { useEffect, useRef, useState } from 'react'
import { UserStream } from '../../store/atom'
import WaveSurfer from 'wavesurfer.js'
import RecordPlugin from 'wavesurfer.js/dist/plugins/record'
import { isWechat } from '../../lib/util'

function AudioWave(props: { user: UserStream }) {
function AudioWave(props: { stream: MediaStream }) {
const refWave = useRef<HTMLDivElement>(null)

useEffect(() => {
if (refWave.current && !!props.user.stream?.getAudioTracks().length) {
if (refWave.current && !!props.stream?.getAudioTracks().length) {
const wavesurfer = WaveSurfer.create({
container: refWave.current,
waveColor: 'rgb(200, 100, 0)',
progressColor: 'rgb(100, 50, 0)',
})

const record = wavesurfer.registerPlugin(RecordPlugin.create())
const { onDestroy, onEnd } = record.renderMicStream(props.user.stream)
const { onDestroy, onEnd } = record.renderMicStream(props.stream)

return () => {
onDestroy()
onEnd()
wavesurfer.destroy()
}
}
}, [refWave.current, props.user.stream])
}, [refWave.current, props.stream])

return <div ref={refWave}></div>
}

export default function Player(props: { user: UserStream, muted: boolean, width: string, display: string }) {
export default function Player(props: { stream: MediaStream, muted: boolean, width: string, display: string }) {
const refVideo = useRef<HTMLVideoElement>(null)
const [showAudio, setShowAudio] = useState(false)

useEffect(() => {
if (props.user.stream?.getAudioTracks().length !== 0 && props.user.stream?.getVideoTracks().length === 0) {
if (props.stream?.getAudioTracks().length !== 0 && props.stream?.getVideoTracks().length === 0) {
setShowAudio(true)
} else {
setShowAudio(false)
}

}, [props.user.stream])
}, [props.stream])

useEffect(() => {
if (refVideo.current) {
refVideo.current.srcObject = props.user.stream
refVideo.current.srcObject = props.stream

// NOTE: About Autoplay
// Reference: https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
Expand All @@ -54,7 +53,7 @@ export default function Player(props: { user: UserStream, muted: boolean, width:
// https://developers.weixin.qq.com/community/develop/doc/0006a61de48ab0165f99e1dcd51c00
if (isWechat()) refVideo.current.play()
}
}, [refVideo, props.user.stream]);
}, [refVideo, props.stream])

// NOTE: iOS can't display video
// https://webkit.org/blog/6784/new-video-policies-for-ios/
Expand All @@ -67,10 +66,10 @@ export default function Player(props: { user: UserStream, muted: boolean, width:
controls={false}
muted={props.muted}
ref={refVideo}
style={!!props.user.stream?.getVideoTracks().length ? { width: props.width } : { height: '0px' }}
style={!!props.stream?.getVideoTracks().length ? { width: props.width } : { height: '0px' }}
/>
{props.display === "full" || showAudio
? <AudioWave user={props.user} />
? <AudioWave stream={props.stream} />
: null
}
</center>
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/player/whep-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default function WhepPlayer(props: { streamId: string, status: UserStatus
<center>
{ loading
? <div className='m-xl'><SvgProgress/></div>
: <Player user={userStream} muted={false} width={props.width} display="auto" />
: <Player stream={userStream.stream} muted={false} width={props.width} display="auto" />
}
</center>

Expand Down
31 changes: 2 additions & 29 deletions webapp/components/player/whip-player.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,12 @@
import { useEffect } from "react"
import useWhipClient from "../use/whip"
import Player from './player'
import { useAtom } from 'jotai'
import {
localStreamAtom,
localUserStatusAtom,

currentDeviceAudioAtom,
currentDeviceVideoAtom,
} from '../../store/atom'

export default function WhipPlayer(props: { streamId: string, width: string }) {
// TODO: Need fix
// - name
// - audio
// - video
const [localUserStatus] = useAtom(localUserStatusAtom)
const [localStream] = useAtom(localStreamAtom)
const { userStatus, setCurrentDeviceAudio, setCurrentDeviceVideo, restart } = useWhipClient(localUserStatus.name, props.streamId, localStream.stream)

const [currentDeviceAudio] = useAtom(currentDeviceAudioAtom)
const [currentDeviceVideo] = useAtom(currentDeviceVideoAtom)

useEffect(() => {
setCurrentDeviceAudio(currentDeviceAudio)
}, [currentDeviceAudio])

useEffect(() => {
setCurrentDeviceVideo(currentDeviceVideo)
}, [currentDeviceVideo])

const { stream, userStatus, restart } = useWhipClient(props.streamId)
return (
<div className='flex flex-col'>
<center>
<Player user={localStream} muted={true} width={props.width} display="auto" />
<Player stream={stream} muted={true} width={props.width} display="auto" />
</center>

<details className='text-white mx-2 text-sm font-border' style={{
Expand Down
Loading

0 comments on commit d8dceb1

Please sign in to comment.