1.1.5 • Published 1 year ago
react-vcall v1.1.5
react-vcall
VCall ReactJS Library
Install
npm install --save react-vcall
Usage
import React, { useState } from 'react'
import { UserMediaStream, useVCall, UserRequestJoinResponse } from 'react-vcall'
import VideoBox from './VideoBox';
interface RoomStatus {
is_joined: boolean
is_muted: boolean
is_show_camera: boolean
is_recording: boolean
is_share_screen: boolean
}
const my_user_id = `${new Date().getTime()}`;
const App = () => {
const [room_status, setRoomStatus] = useState<RoomStatus>({
is_joined: false,
is_muted: true,
is_show_camera: false,
is_recording: false,
is_share_screen: false
});
const { streams, action } = useVCall({
user_id: my_user_id,
room: `3f014cb2-e8ca-4589-a0b7-b909d683289e`, // this room should be fetched from API
name: 'sample-name',
is_muted: room_status.is_muted,
is_show_camera: room_status.is_show_camera,
is_share_screen: room_status.is_share_screen,
}, {
onRecordingStarted,
onRecordingEnded,
onWaitingRoom,
onUserRequestToJoinRoom,
onReady,
onSharingScreenStarted,
onSharingScreenEnded
});
function onSharingScreenStarted() {
setRoomStatus({ ...room_status, is_share_screen: true });
}
function onSharingScreenEnded() {
setRoomStatus({ ...room_status, is_share_screen: false });
}
function onRecordingStarted() {
setRoomStatus({ ...room_status, is_recording: true });
}
function onRecordingEnded() {
setRoomStatus({ ...room_status, is_recording: false });
}
function onUserRequestToJoinRoom(data: UserRequestJoinResponse, accept: () => void, reject: () => void) {
setTimeout(() => {
accept();
}, 3000);
}
function onWaitingRoom() {
//
}
function onReady() {
//
}
return <div>
<div> { my_user_id } </div>
<div>
<button
disabled={room_status.is_share_screen}
onClick={() => !room_status.is_share_screen && action.startShareScreen() }>
Start Share Screen
</button>
<button
disabled={!room_status.is_share_screen}
onClick={() => room_status.is_share_screen && action.stopShareScreen() }>
Stop Share Screen
</button>
</div>
<div>
<button
disabled={room_status.is_show_camera}
onClick={() => {
!room_status.is_show_camera && action.showCamera();
setRoomStatus({ ...room_status, is_show_camera: true });
}}>
Show Camera
</button>
<button
disabled={!room_status.is_show_camera}
onClick={() => {
room_status.is_show_camera && action.hideCamera();
setRoomStatus({ ...room_status, is_show_camera: false });
}}>
Hide Camera
</button>
</div>
<div>
<button
onClick={() => {
room_status.is_muted && action.startMic();
setRoomStatus({ ...room_status, is_muted: false });
}}
disabled={!room_status.is_muted}>
Start Microphone
</button>
<button
onClick={() => {
!room_status.is_muted && action.stopMic();
setRoomStatus({ ...room_status, is_muted: true });
}}
disabled={room_status.is_muted}>
Stop Microphone
</button>
</div>
<div>
<button
onClick={() => !room_status.is_recording && action.startRecording()}
disabled={room_status.is_recording}>
Start Recording
</button>
<button
onClick={() => room_status.is_recording && action.stopRecording()}
disabled={!room_status.is_recording}>
Stop Recording
</button>
</div>
<div>
{
streams.map((ums: UserMediaStream, i: number) => (
<div key={i}>
<VideoBox
isLocal={my_user_id === ums.user_id}
userId={ums.user_id}
stream={ums.ms}
type={ums.manifest.is_sharing_screen_user ? 'display' : 'camera'}
muted={ums.manifest.is_muted}
showCamera={ums.manifest.is_show_camera}
audioAnalyser={ums.setAudioAnalyser} />
</div>
))
}
</div>
</div>
}
export default App
// VideoBox.tsx
import React, { useState } from "react";
import { useEffect, useRef } from "react";
interface VideoBoxProps {
isLocal: boolean
userId: string
stream?: MediaStream
muted?: boolean
type: 'camera' | 'display'
showCamera?: boolean
audioAnalyser(interval: number, callback: (value: number) => void): void
}
export default function VideoBox(props: VideoBoxProps) {
const ref = useRef(null);
const [sound_level, setSoundLevel] = useState<number>(50);
useEffect(() => {
if (!props.stream) {
return;
}
(ref.current as any).srcObject = props.stream;
props.audioAnalyser(50, setSoundLevel);
}, [props.stream]);
useEffect(() => {
props.audioAnalyser(50, setSoundLevel);
}, [props.muted]);
return (
<div style={{ position: 'relative', width: 300, height: 200, border: 'solid 1px #AAA' }}>
<div style={{ position: 'absolute', right: 0, top: 0, background: '#EEE', border: 'solid 1px #AAA' }}>
<div>
{ props.muted ? 'Sound Off' : 'Sound On' }
</div>
<div>
{ props.showCamera ? 'Camera On' : 'Camera Off' }
</div>
</div>
<div style={{ position: 'absolute', bottom: 0, right: 0, background: '#CCC' }}>
<div>
User ID: { props.userId } ({ props.type })
</div>
v: { props.stream?.getVideoTracks().length }({ (props.stream?.getVideoTracks() ?? []).map(x => `${x.kind}-${x.label}`).join(', ') }), a: { props.stream?.getAudioTracks().length }
</div>
{ !props.muted && <div style={{ position: 'absolute', bottom: 0, left: 0, background: '#9C9', width: 12, height: 100 * (sound_level - 128) / 128 }} /> }
<video
style={{ width: '100%', height: '100%', background: '#EEE' }}
ref={ref}
muted={(props.isLocal ? true : props.muted)}
autoPlay
playsInline />
</div>
);
}
License
MIT © Graf-Research