Commit 35323313 authored by Thisara Kavinda's avatar Thisara Kavinda

feat: introduced features

parent c0383c4d
...@@ -8,23 +8,44 @@ import { ...@@ -8,23 +8,44 @@ import {
IconButton, IconButton,
TextField, TextField,
Typography, Typography,
Popover,
} from '@mui/material' } from '@mui/material'
import ChevronRightIcon from '@mui/icons-material/ChevronRight' import ChevronRightIcon from '@mui/icons-material/ChevronRight'
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown' import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
import { useTheme, type Theme } from '@mui/material/styles' import { useTheme, type Theme } from '@mui/material/styles'
import { useState } from 'react' import { useState } from 'react'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import lips from '../../Assests/Images/lips.png'
import sign from '../../Assests/Images/hello.png'
import avatar from '../../Assests/Images/gamer.png'
import { IFeatureTypes } from '../../Services/types/FeatureTypes'
interface Props { interface Props {
setJoined: (x: boolean) => void setJoined: (x: boolean) => void
currentFeature: IFeatureTypes | null
setCurrentFeature: (x: IFeatureTypes | null) => void
} }
const Lobby = ({ setJoined }: Props) => { const Lobby = ({ setJoined, currentFeature, setCurrentFeature }: Props) => {
const theme: Theme = useTheme() const theme: Theme = useTheme()
const navigate = useNavigate() const navigate = useNavigate()
const [roomName, setRoomName] = useState('') const [roomName, setRoomName] = useState('')
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
const [toolTipLabel, setToolTipLabel] = useState<string>('')
const handlePopoverOpen = (event: React.MouseEvent<HTMLElement>, label: string) => {
setToolTipLabel(label)
setAnchorEl(event.currentTarget)
}
const handlePopoverClose = () => {
setAnchorEl(null)
}
const open = Boolean(anchorEl)
const handleJoinRoom = () => { const handleJoinRoom = () => {
navigate(`/${roomName}`) navigate(`/${roomName}`)
setJoined(true) setJoined(true)
...@@ -36,6 +57,10 @@ const Lobby = ({ setJoined }: Props) => { ...@@ -36,6 +57,10 @@ const Lobby = ({ setJoined }: Props) => {
setJoined(true) setJoined(true)
} }
const handleSelectFeature = (feature: IFeatureTypes) => {
setCurrentFeature(currentFeature === feature ? null : feature)
}
return ( return (
<Box <Box
sx={{ sx={{
...@@ -157,7 +182,80 @@ const Lobby = ({ setJoined }: Props) => { ...@@ -157,7 +182,80 @@ const Lobby = ({ setJoined }: Props) => {
</Typography> </Typography>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Box>sdsd</Box> <Box
sx={{
display: 'flex',
justifyContent: 'space-between',
}}
>
<IconButton
sx={{
width: '30%',
cursor: 'pointer',
borderRadius: '15px',
padding: '5px',
'&:hover': { border: '2px solid black' },
border: currentFeature === IFeatureTypes.LIP_READING ? '2px solid black' : '',
}}
onMouseEnter={(event) => handlePopoverOpen(event, 'Lip reading feature')}
onMouseLeave={handlePopoverClose}
onClick={() => handleSelectFeature(IFeatureTypes.LIP_READING)}
>
<img src={lips} alt='lips' style={{ width: '80px' }} />
</IconButton>
<Popover
id='mouse-over-popover'
sx={{
pointerEvents: 'none',
}}
open={open}
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
onClose={handlePopoverClose}
disableRestoreFocus
>
<Typography sx={{ p: 1 }}>{toolTipLabel}</Typography>
</Popover>
<IconButton
sx={{
width: '30%',
cursor: 'pointer',
borderRadius: '15px',
padding: '5px',
'&:hover': { border: '2px solid black' },
border:
currentFeature === IFeatureTypes.SIGN_LANGUAGE ? '2px solid black' : '',
}}
onMouseEnter={(event) => handlePopoverOpen(event, 'Sign translation feature')}
onMouseLeave={handlePopoverClose}
onClick={() => handleSelectFeature(IFeatureTypes.SIGN_LANGUAGE)}
>
<img src={sign} alt='lips' style={{ width: '80px' }} />
</IconButton>
<IconButton
sx={{
width: '30%',
cursor: 'pointer',
borderRadius: '15px',
padding: '5px',
'&:hover': { border: '2px solid black' },
border:
currentFeature === IFeatureTypes.LIP_SYNCHING ? '2px solid black' : '',
}}
onMouseEnter={(event) => handlePopoverOpen(event, 'Lip synching feature')}
onMouseLeave={handlePopoverClose}
onClick={() => handleSelectFeature(IFeatureTypes.LIP_SYNCHING)}
>
<img src={avatar} alt='lips' style={{ width: '80px' }} />
</IconButton>
</Box>
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
</Box> </Box>
......
...@@ -11,14 +11,23 @@ import type { ...@@ -11,14 +11,23 @@ import type {
} from 'agora-rtc-sdk-ng' } from 'agora-rtc-sdk-ng'
import ControlCenter from '../ControlCenter/ControlCenter' import ControlCenter from '../ControlCenter/ControlCenter'
import { socketService } from '../../Services/SocketService/socketService' import { socketService } from '../../Services/SocketService/socketService'
import { IFeatureTypes } from '../../Services/types/FeatureTypes'
interface Props { interface Props {
setJoined: (x: boolean) => void setJoined: (x: boolean) => void
localCameraTrack: ICameraVideoTrack | null localCameraTrack: ICameraVideoTrack | null
localMicrophoneTrack: IMicrophoneAudioTrack | null localMicrophoneTrack: IMicrophoneAudioTrack | null
currentFeature: IFeatureTypes | null
setCurrentFeature: (x: IFeatureTypes | null) => void
} }
const MeetingLayout = ({ setJoined, localCameraTrack, localMicrophoneTrack }: Props) => { const MeetingLayout = ({
setJoined,
localCameraTrack,
localMicrophoneTrack,
currentFeature,
setCurrentFeature,
}: Props) => {
const theme: Theme = useTheme() const theme: Theme = useTheme()
const agoraEngine = useRTCClient() const agoraEngine = useRTCClient()
...@@ -28,12 +37,48 @@ const MeetingLayout = ({ setJoined, localCameraTrack, localMicrophoneTrack }: Pr ...@@ -28,12 +37,48 @@ const MeetingLayout = ({ setJoined, localCameraTrack, localMicrophoneTrack }: Pr
const gridContainerRef = useRef<HTMLDivElement>(null) const gridContainerRef = useRef<HTMLDivElement>(null)
const [gridContainerHeight, setGridContainerHeight] = useState(0) const [gridContainerHeight, setGridContainerHeight] = useState(0)
const videoRef = useRef<HTMLVideoElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
const [totalUsers, setTotalUsers] = useState(0) const [totalUsers, setTotalUsers] = useState(0)
const [isPinned, setIsPinned] = useState(false) const [isPinned, setIsPinned] = useState(false)
const [pinnedUser, setPinnedUser] = useState<IAgoraRTCRemoteUser | null>(null) const [pinnedUser, setPinnedUser] = useState<IAgoraRTCRemoteUser | null>(null)
const [isCaptionEnable, setIsCaptionEnable] = useState(false) const [isCaptionEnable, setIsCaptionEnable] = useState(false)
const [caption, setCaption] = useState('') const [caption, setCaption] = useState('')
useEffect(() => {
if (localCameraTrack && videoRef.current) {
const stream = localCameraTrack.getMediaStreamTrack()
videoRef.current.srcObject = new MediaStream([stream])
videoRef.current.play().catch((err) => console.error('Video play failed', err))
}
}, [localCameraTrack])
const captureFrame = () => {
if (videoRef.current && canvasRef.current) {
const context = canvasRef.current.getContext('2d')
if (context && videoRef.current.videoWidth && videoRef.current.videoHeight) {
canvasRef.current.width = videoRef.current.videoWidth
canvasRef.current.height = videoRef.current.videoHeight
context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height)
// You can now access the image data from the canvas
const frame = canvasRef.current.toDataURL('image/jpeg')
if (frame && currentFeature === IFeatureTypes.LIP_READING) {
socketService.sendFrames('test', frame)
}
}
}
requestAnimationFrame(captureFrame) // Recursively capture the next frame
}
useEffect(() => {
if (currentFeature != null) {
captureFrame()
}
}, [])
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
socketService.getCaption(setCaption) socketService.getCaption(setCaption)
...@@ -82,6 +127,8 @@ const MeetingLayout = ({ setJoined, localCameraTrack, localMicrophoneTrack }: Pr ...@@ -82,6 +127,8 @@ const MeetingLayout = ({ setJoined, localCameraTrack, localMicrophoneTrack }: Pr
}} }}
ref={gridContainerRef} ref={gridContainerRef}
> >
<video ref={videoRef} style={{ display: 'none' }}></video>
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas>
{isPinned && pinnedUser ? ( {isPinned && pinnedUser ? (
<PinnedGrid <PinnedGrid
totalUsers={totalUsers} totalUsers={totalUsers}
......
...@@ -5,7 +5,6 @@ import { useRemoteUsers } from 'agora-rtc-react' ...@@ -5,7 +5,6 @@ import { useRemoteUsers } from 'agora-rtc-react'
import { useTheme, type Theme } from '@mui/material/styles' import { useTheme, type Theme } from '@mui/material/styles'
import type { IAgoraRTCRemoteUser, ICameraVideoTrack } from 'agora-rtc-sdk-ng' import type { IAgoraRTCRemoteUser, ICameraVideoTrack } from 'agora-rtc-sdk-ng'
import UserCard from '../UserCard/UserCard' import UserCard from '../UserCard/UserCard'
import { socketService } from '../../Services/SocketService/socketService'
interface Props { interface Props {
totalUsers: number totalUsers: number
...@@ -31,23 +30,12 @@ const SymmetricGrid = ({ ...@@ -31,23 +30,12 @@ const SymmetricGrid = ({
const remoteUsers = useRemoteUsers() const remoteUsers = useRemoteUsers()
const videoRef = useRef<HTMLVideoElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
const [itemOffset, setItemOffset] = useState(12) const [itemOffset, setItemOffset] = useState(12)
const [itemHeight, setItemHeight] = useState(230) const [itemHeight, setItemHeight] = useState(230)
const [numOfItems, setNumOfItems] = useState(1) const [numOfItems, setNumOfItems] = useState(1)
const maxUsersDisplay = isLg ? 12 : isMd ? 9 : isSm ? 4 : 2 const maxUsersDisplay = isLg ? 12 : isMd ? 9 : isSm ? 4 : 2
useEffect(() => {
if (localCameraTrack && videoRef.current) {
const stream = localCameraTrack.getMediaStreamTrack()
videoRef.current.srcObject = new MediaStream([stream])
videoRef.current.play().catch((err) => console.error('Video play failed', err))
}
}, [localCameraTrack])
useEffect(() => { useEffect(() => {
setNumOfItems(totalUsers > maxUsersDisplay ? maxUsersDisplay : totalUsers) setNumOfItems(totalUsers > maxUsersDisplay ? maxUsersDisplay : totalUsers)
}, [totalUsers]) }, [totalUsers])
...@@ -138,29 +126,6 @@ const SymmetricGrid = ({ ...@@ -138,29 +126,6 @@ const SymmetricGrid = ({
} }
}, [totalUsers, isSm, isMd, isLg, gridContainerHeight, numOfItems]) }, [totalUsers, isSm, isMd, isLg, gridContainerHeight, numOfItems])
const captureFrame = () => {
if (videoRef.current && canvasRef.current) {
const context = canvasRef.current.getContext('2d')
if (context && videoRef.current.videoWidth && videoRef.current.videoHeight) {
canvasRef.current.width = videoRef.current.videoWidth
canvasRef.current.height = videoRef.current.videoHeight
context.drawImage(videoRef.current, 0, 0, canvasRef.current.width, canvasRef.current.height)
// You can now access the image data from the canvas
const frame = canvasRef.current.toDataURL('image/jpeg')
if (frame) {
socketService.sendFrames('test', frame)
}
}
}
requestAnimationFrame(captureFrame) // Recursively capture the next frame
}
useEffect(() => {
captureFrame()
}, [])
return ( return (
<Box sx={{ display: 'flex', width: '100%' }}> <Box sx={{ display: 'flex', width: '100%' }}>
<Grid container spacing={3} width={'100%'} padding={0}> <Grid container spacing={3} width={'100%'} padding={0}>
...@@ -172,8 +137,6 @@ const SymmetricGrid = ({ ...@@ -172,8 +137,6 @@ const SymmetricGrid = ({
setPinnedUser={setPinnedUser} setPinnedUser={setPinnedUser}
localCameraTrack={localCameraTrack} localCameraTrack={localCameraTrack}
/> />
<video ref={videoRef} style={{ display: 'none' }}></video>
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas>
{/* <Box {/* <Box
sx={{ sx={{
......
...@@ -4,21 +4,31 @@ import { AgoraRTCProvider, useRTCClient } from 'agora-rtc-react' ...@@ -4,21 +4,31 @@ import { AgoraRTCProvider, useRTCClient } from 'agora-rtc-react'
import { AgoraManager } from '../../Services/AgoraManager/AgoraManager' import { AgoraManager } from '../../Services/AgoraManager/AgoraManager'
import Lobby from '../../Components/Lobby/Lobby' import Lobby from '../../Components/Lobby/Lobby'
import { Box } from '@mui/material' import { Box } from '@mui/material'
import { type IFeatureTypes } from '../../Services/types/FeatureTypes'
const VideoCall = () => { const VideoCall = () => {
const agoraEngine = useRTCClient(AgoraRTC.createClient({ codec: 'vp8', mode: 'rtc' })) const agoraEngine = useRTCClient(AgoraRTC.createClient({ codec: 'vp8', mode: 'rtc' }))
const [joined, setJoined] = useState(false) const [joined, setJoined] = useState(false)
const [currentFeature, setCurrentFeature] = useState<IFeatureTypes | null>(null)
return ( return (
<Box sx={{ height: '100vh', width: '100vw', overflow: 'hidden' }}> <Box sx={{ height: '100vh', width: '100vw', overflow: 'hidden' }}>
{joined ? ( {joined ? (
<AgoraRTCProvider client={agoraEngine}> <AgoraRTCProvider client={agoraEngine}>
<AgoraManager setJoined={setJoined}> <AgoraManager
setJoined={setJoined}
currentFeature={currentFeature}
setCurrentFeature={setCurrentFeature}
>
<div>asas</div> <div>asas</div>
</AgoraManager> </AgoraManager>
</AgoraRTCProvider> </AgoraRTCProvider>
) : ( ) : (
<Lobby setJoined={setJoined} /> <Lobby
setJoined={setJoined}
currentFeature={currentFeature}
setCurrentFeature={setCurrentFeature}
/>
)} )}
</Box> </Box>
) )
......
...@@ -7,6 +7,7 @@ import MeetingLoading from '../../Components/MeetingLoading/MeetingLoading' ...@@ -7,6 +7,7 @@ import MeetingLoading from '../../Components/MeetingLoading/MeetingLoading'
import MeetingLayout from '../../Components/MeetingLayout/MeetingLayout' import MeetingLayout from '../../Components/MeetingLayout/MeetingLayout'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { socketService } from '../SocketService/socketService' import { socketService } from '../SocketService/socketService'
import { type IFeatureTypes } from '../types/FeatureTypes'
interface AgoraContextType { interface AgoraContextType {
localCameraTrack: ICameraVideoTrack | null localCameraTrack: ICameraVideoTrack | null
...@@ -34,9 +35,13 @@ export const useAgoraContext = () => { ...@@ -34,9 +35,13 @@ export const useAgoraContext = () => {
export const AgoraManager = ({ export const AgoraManager = ({
setJoined, setJoined,
currentFeature,
setCurrentFeature,
children, children,
}: { }: {
setJoined: (x: boolean) => void setJoined: (x: boolean) => void
currentFeature: IFeatureTypes | null
setCurrentFeature: (x: IFeatureTypes | null) => void
children: React.ReactNode children: React.ReactNode
}) => { }) => {
const theme: Theme = useTheme() const theme: Theme = useTheme()
...@@ -100,6 +105,8 @@ export const AgoraManager = ({ ...@@ -100,6 +105,8 @@ export const AgoraManager = ({
setJoined={setJoined} setJoined={setJoined}
localCameraTrack={localCameraTrack} localCameraTrack={localCameraTrack}
localMicrophoneTrack={localMicrophoneTrack} localMicrophoneTrack={localMicrophoneTrack}
currentFeature={currentFeature}
setCurrentFeature={setCurrentFeature}
/> />
</AgoraProvider> </AgoraProvider>
) : joinError ? ( ) : joinError ? (
......
...@@ -32,7 +32,18 @@ class SocketService { ...@@ -32,7 +32,18 @@ class SocketService {
} }
public sendFrames(room: string, frame: any): void { public sendFrames(room: string, frame: any): void {
this?.socket?.emit('frame', { room: room, frame: frame }) this?.socket?.emit('calibrate_lips', { room, frame })
}
public calibrateLips(room: string, frame: any): void {
this?.socket?.emit('calibrate_lips', { room, frame })
}
public getCalibrateLipsStatus() {
this?.socket?.on('calibrate_lips', (res) => {
console.log('calibrate_lips', res)
return res
})
} }
} }
......
export enum IFeatureTypes {
LIP_READING = 'LIP_READING',
SIGN_LANGUAGE = 'SIGN_LANGUAGE',
LIP_SYNCHING = 'LIP_SYNCHING',
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment