From 9f0b419f434b103b83e4330fbab489fd2dc74277 Mon Sep 17 00:00:00 2001 From: Tristan Yang Date: Sun, 23 Apr 2023 09:56:05 +0800 Subject: [PATCH] feat: video call --- src/assets/icons/camera.off.svg | 4 ++ src/assets/icons/camera.svg | 3 + src/assets/icons/share.screen.svg | 3 + src/common/component/Voice.tsx | 50 ++++++++++------ src/routes/chat/RTCWidget.tsx | 44 ++++++++------ src/routes/chat/VoiceChat/VoiceManagement.tsx | 57 ++++++++++++------- 6 files changed, 107 insertions(+), 54 deletions(-) create mode 100644 src/assets/icons/camera.off.svg create mode 100644 src/assets/icons/camera.svg create mode 100644 src/assets/icons/share.screen.svg diff --git a/src/assets/icons/camera.off.svg b/src/assets/icons/camera.off.svg new file mode 100644 index 00000000..9452484e --- /dev/null +++ b/src/assets/icons/camera.off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/camera.svg b/src/assets/icons/camera.svg new file mode 100644 index 00000000..6572494d --- /dev/null +++ b/src/assets/icons/camera.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/share.screen.svg b/src/assets/icons/share.screen.svg new file mode 100644 index 00000000..27696d4b --- /dev/null +++ b/src/assets/icons/share.screen.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/component/Voice.tsx b/src/common/component/Voice.tsx index 827f21a5..dc9ecfcb 100644 --- a/src/common/component/Voice.tsx +++ b/src/common/component/Voice.tsx @@ -44,14 +44,16 @@ const Voice = () => { if (mediaType == "audio") { // 播放远端音频 user.audioTrack?.play(); - // const playing= user.audioTrack?.isPlaying; - // const level = user.audioTrack?.getVolumeLevel(); - // if (level === 0) { - // // 远端静音 - // dispatch(updateVoicingMember({ uid: +user.uid, info: { muted: true } })); - // } window.VOICE_TRACK_MAP[+user.uid] = user.audioTrack; } + if (mediaType == "video") { + const id = `CAMERA_${user.uid}`; + console.log("video container id", id); + + // 播放远端视频 + user.videoTrack?.play(id); + window.VOICE_TRACK_MAP[+user.uid] = user.videoTrack; + } agoraEngine.on("user-unpublished", (user) => { // 远端用户取消了音频(muted) dispatch(updateVoicingMember({ uid: +user.uid, info: { muted: true } })); @@ -102,8 +104,6 @@ const Voice = () => { default: break; } - // const { downlinkNetworkQuality } = qlt; - // dispatch(updateVoicingNetworkQuality(downlinkNetworkQuality)); }); }); // 有新用户加入 @@ -127,13 +127,6 @@ const Voice = () => { return () => { window.removeEventListener("beforeunload", handlePageUnload, { capture: true }); - // if (window.VOICE_CLIENT && localTrack) { - // localTrack.close(); - // localTrack = null; - // window.VOICE_CLIENT.leave(); - // dispatch(updateVoicingInfo(null)); - // } - // window.VOICE_CLIENT=null }; }, []); @@ -148,9 +141,9 @@ type VoiceProps = { const audioJoin = new Audio(AudioJoin); const useVoice = ({ id, context = "channel" }: VoiceProps) => { const dispatch = useDispatch(); - const { voicingInfo } = useAppSelector(store => { + const { voicingInfo, loginUid } = useAppSelector(store => { return { - // loginUid: store.authData.user?.uid, + loginUid: store.authData.user?.uid, voicingInfo: store.voice.voicing }; }); @@ -197,6 +190,25 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { } // setJoining(false); }; + const openCamera = async () => { + if (localTrack) { + localTrack.close(); + localTrack = null; + } + localTrack = await AgoraRTC.createCameraVideoTrack(); + await window.VOICE_CLIENT?.publish(localTrack); + const id = `CAMERA_${loginUid}`; + localTrack?.play(id); + }; + const closeCamera = async () => { + if (localTrack) { + localTrack.close(); + localTrack = null; + } + localTrack = await AgoraRTC.createMicrophoneAudioTrack(); + await window.VOICE_CLIENT?.publish(localTrack); + }; + const leave = async () => { if (window.VOICE_CLIENT && localTrack) { localTrack.close(); @@ -250,7 +262,9 @@ const useVoice = ({ id, context = "channel" }: VoiceProps) => { joining: voicingInfo ? voicingInfo.joining : undefined, joinedAtThisContext, joined: !!voicingInfo, - joinVoice + joinVoice, + openCamera, + closeCamera }; }; export { useVoice }; diff --git a/src/routes/chat/RTCWidget.tsx b/src/routes/chat/RTCWidget.tsx index 685800fe..74bf7e43 100644 --- a/src/routes/chat/RTCWidget.tsx +++ b/src/routes/chat/RTCWidget.tsx @@ -8,6 +8,9 @@ import IconMicOff from '../../assets/icons/mic.off.svg'; import IconCallOff from '../../assets/icons/headphone.svg'; import IconSoundOn from '../../assets/icons/sound.on.svg'; import IconSoundOff from '../../assets/icons/sound.off.svg'; +import IconCameraOff from '../../assets/icons/camera.off.svg'; +import IconCamera from '../../assets/icons/camera.svg'; +import IconScreen from '../../assets/icons/share.screen.svg'; import { useVoice } from '../../common/component/Voice'; import Signal from '../../common/component/Signal'; import Tooltip from '../../common/component/Tooltip'; @@ -19,7 +22,7 @@ type Props = { const RTCWidget = ({ id, context = "channel" }: Props) => { const { t } = useTranslation("chat"); - const { leave, voicingInfo, setMute, setDeafen, joining = true } = useVoice({ context, id }); + const { leave, voicingInfo, setMute, setDeafen, joining = true, openCamera } = useVoice({ context, id }); const { loginUser, channelData, userData } = useAppSelector(store => { return { userData: store.users.byId, @@ -28,25 +31,36 @@ const RTCWidget = ({ id, context = "channel" }: Props) => { }; }); if (!loginUser || !voicingInfo || joining) return null; - const name = voicingInfo.context == "channel" ? channelData[voicingInfo.id]?.name : userData[voicingInfo.id]?.name; - if (!name) return null; + // const name = voicingInfo.context == "channel" ? channelData[voicingInfo.id]?.name : userData[voicingInfo.id]?.name; + // if (!name) return null; const isReConnecting = voicingInfo.connectionState == "RECONNECTING"; return (
- {/* {voicingInfo && */} -
-
- -
- {isReConnecting ? `Voice Reconnecting...` : `Voice Connected`} - {voicingInfo.context == "channel" ? "Channel" : "DM"} / {voicingInfo.context == "channel" ? channelData[voicingInfo.id].name : userData[voicingInfo.id].name} +
+
+ +
+ +
+ {isReConnecting ? `Voice Reconnecting...` : `Voice Connected`} + {voicingInfo.context == "channel" ? "Channel" : "DM"} / {voicingInfo.context == "channel" ? channelData[voicingInfo.id].name : userData[voicingInfo.id].name} +
+ + + +
+
+ +
- - -
- {/* } */}
@@ -55,7 +69,6 @@ const RTCWidget = ({ id, context = "channel" }: Props) => { #{loginUser.uid}
- {/* {voicingInfo && */}
{voicingInfo.deafen ? : } @@ -67,7 +80,6 @@ const RTCWidget = ({ id, context = "channel" }: Props) => { }
- {/* } */}
); diff --git a/src/routes/chat/VoiceChat/VoiceManagement.tsx b/src/routes/chat/VoiceChat/VoiceManagement.tsx index 2abce270..b2cd2d20 100644 --- a/src/routes/chat/VoiceChat/VoiceManagement.tsx +++ b/src/routes/chat/VoiceChat/VoiceManagement.tsx @@ -9,6 +9,9 @@ import IconHeadphone from '../../../assets/icons/sound.on.svg'; import IconHeadphoneOff from '../../../assets/icons/sound.off.svg'; import IconMic from '../../../assets/icons/mic.on.svg'; import IconMicOff from '../../../assets/icons/mic.off.svg'; +import IconCameraOff from '../../../assets/icons/camera.off.svg'; +import IconCamera from '../../../assets/icons/camera.svg'; +import IconScreen from '../../../assets/icons/share.screen.svg'; import StyledButton from '../../../common/component/styled/Button'; import IconCallOff from '../../../assets/icons/call.off.svg'; import Tooltip from '../../../common/component/Tooltip'; @@ -55,29 +58,32 @@ const VoiceManagement = ({ info, setMute, setDeafen, leave }: Props) => { if (!curr) return null; const { muted, speakingVolume = 0 } = membersData[uid]; const speaking = speakingVolume > 50; - return
  • -
    -
    - {speaking &&
    } - + return
  • +
    +
    +
    + {speaking &&
    } + +
    + + {curr?.name} + +
    +
    + {/* {deafen ? : } */} + {muted ? : }
    - - {curr?.name} -
    -
    - {/* {deafen ? : } */} - {muted ? : } +
    + {/* camera placeholder */}
    - {/* */} - {/* {userData[uid]?.name} */}
  • ; })} @@ -95,6 +101,17 @@ const VoiceManagement = ({ info, setMute, setDeafen, leave }: Props) => { + +
  • + {muted ? : } + +
  • +
    + +
  • + +
  • +