UNPKG

@videosdk.live/react-native-sdk

Version:

<h1 align="center"> <img src="https://cdn.videosdk.live/docs/images/react-native/banner.png" /><br/> </h1>

484 lines (452 loc) 11.9 kB
import React, { useEffect, useRef, useState, useMemo } from "react"; import { PermissionsAndroid, Platform } from "react-native"; import useFile from "./upload/useFile"; import useMediaDevice from "./useMediaDevice"; import { mediaDevices, registerGlobals, RTCView, MediaStream, MediaStreamTrack, permissions, VideoProcessor, ScreenShareManager, } from "@videosdk.live/react-native-webrtc"; import { useMeeting as OldUseMeeting, MeetingProvider as OldMeetingProvider, useParticipant as OldUseParticipant, MeetingConsumer, usePubSub, useConnection, Constants, createCameraVideoTrack, createMicrophoneAudioTrack, createScreenShareVideoTrack, useTranscription, useCharacter, useWhiteboard, useRealtimeStore, useStream, setLogLevel, useAgentParticipant, } from "@videosdk.live/react-sdk"; import InCallManager from "@videosdk.live/react-native-incallmanager"; import { version } from "./package.json"; import E2EEManager from "./e2ee/e2eeManager"; import useKeyProvider from "./e2ee/useKeyProvider"; let notificationObj = null; let e2eeManager = null; /**@deprecated */ const getAudioDeviceList = async () => { const devices = await InCallManager.getAudioDeviceList(); if (devices.length > 0) { return devices.includes("WIRED_HEADSET") || devices.includes("BLUETOOTH") ? devices.filter((d) => d !== "EARPIECE") : devices; } else { return null; } }; const switchAudioDevice = async (device) => { if (Platform.OS == "android") { await InCallManager.switchAudioDevice(device); } else { switch (device) { case "EARPIECE": InCallManager.setForceSpeakerphoneOn(false); break; case "SPEAKER_PHONE": InCallManager.setForceSpeakerphoneOn(true); break; case "BLUETOOTH": InCallManager.setForceSpeakerphoneOn(false); break; case "WIRED_HEADSET": InCallManager.setForceSpeakerphoneOn(false); break; default: InCallManager.setForceSpeakerphoneOn(true); } } }; const applyVideoProcessor = (name) => { VideoProcessor.applyVideoProcessor(name); }; const removeVideoProcessor = () => { VideoProcessor.removeVideoProcessor(); }; const initialize = ({ notification }) => { notificationObj = notification; InCallManager.start({ media: "video" }); }; const terminate = () => { VideoProcessor.removeVideoProcessor(); InCallManager.stop(); e2eeManager = null; if (ScreenShareManager.is_running) { ScreenShareManager.disableSystemAudio(); ScreenShareManager.disableScreenShare(); } }; const register = async () => { registerGlobals(); navigator.mediaDevices.getDisplayMedia = mediaDevices.getDisplayMedia.bind(mediaDevices); }; const RN_EXTRAS = [ "e2eeEnabled", "enableScreenShare", "disableScreenShare", "toggleScreenShare", ]; const RN_PARTICIPANT_EXTRAS = ["captureImage"]; function MeetingProvider(props) { const [deviceInfoConfig, setDeviceInfoConfig] = useState(null); const { keyProvider } = props; if (keyProvider) { e2eeManager = new E2EEManager(keyProvider); } useEffect(() => { InCallManager.getDeviceInfo().then((deviceData) => { const deviceInfo = { ...props, config: { ...props.config, deviceInfo: true }, deviceInfo: { sdkType: "react-native", sdkVersion: version, platform: Platform.OS, deviceUserAgent: deviceData, browserUserAgent: "null", }, }; delete deviceInfo.keyProvider; setDeviceInfoConfig(deviceInfo); }); }, []); useEffect(() => { return () => { terminate(); }; }, []); return deviceInfoConfig ? ( <OldMeetingProvider {...deviceInfoConfig}> <MeetingConsumer onMeetingJoined={() => { if (props.config.hasOwnProperty("notification")) { initialize(props.config); } else { initialize({ notification: { title: "Notification title", message: "Notification message", }, }); } }} onMeetingLeft={terminate} > {({}) => { return props.children; }} </MeetingConsumer> </OldMeetingProvider> ) : ( <></> ); } function useParticipant( participantId, { onStreamEnabled, onStreamDisabled, onMediaStatusChanged, onVideoQualityChanged, onStreamPaused, onStreamResumed, onE2EEStateChanged = () => {}, // default no-op function } = {}, ) { const data = OldUseParticipant(participantId, { onStreamEnabled, onStreamDisabled, onStreamPaused, onStreamResumed, onMediaStatusChanged, onVideoQualityChanged, onProducerAdded: (data) => { handleOnProducerAdded(data); }, onProducerRemoved: (data) => { handleOnProducerRemoved(data); }, onConsumerAdded: (data) => { handleOnConsumerAdded(data); }, onConsumerRemoved: (data) => { handleOnConsumerRemoved(data); }, }); const dataRef = useRef(data); useEffect(() => { dataRef.current = data; }, [data]); async function handleOnProducerAdded(data) { if (!e2eeManager) return; if (data.rtpSender._id && data.rtpSender._peerConnectionId) { let e2eCallback = (state) => { let stateinfo = { state: state, kind: data.rtpSender._track.kind, }; if (onE2EEStateChanged) { onE2EEStateChanged(stateinfo); } }; e2eeManager.setupE2EESender(data, e2eCallback); } } async function handleOnProducerRemoved(data) { if (!e2eeManager) return; e2eeManager.closeCryptor(data); } async function handleOnConsumerAdded(data) { if (!e2eeManager) return; if (data.rtpReceiver._id && data.rtpReceiver._peerConnectionId) { let e2eCallback = (state) => { let stateinfo = { state: state, kind: data.rtpReceiver._track.kind, }; onE2EEStateChanged(stateinfo); }; e2eeManager.setupE2EEReceiver(data, e2eCallback); } } async function handleOnConsumerRemoved(data) { if (!e2eeManager) return; e2eeManager.closeCryptor(data); } const captureImage = async ({ height, width } = {}) => { try { if (dataRef.current.isLocal) { if (dataRef.current.webcamStream == null) { throw new Error("Camera must be on to capture an image"); } const base64 = await new MediaStream([ dataRef.current.webcamStream?.track, ]).getBitMap({ height, width }); return base64; } else { throw new Error("You can only capture a image of LocalParticipant"); } } catch (err) { console.error("err on image capture", err); } }; const extrasRef = useRef({ captureImage }); extrasRef.current.captureImage = captureImage; return useMemo( () => new Proxy(data, { get(target, prop, receiver) { if (RN_PARTICIPANT_EXTRAS.includes(prop)) return extrasRef.current[prop]; return Reflect.get(target, prop, receiver); }, has(target, prop) { return ( RN_PARTICIPANT_EXTRAS.includes(prop) || Reflect.has(target, prop) ); }, ownKeys(target) { return [ ...new Set([...Reflect.ownKeys(target), ...RN_PARTICIPANT_EXTRAS]), ]; }, getOwnPropertyDescriptor() { return { configurable: true, enumerable: true, writable: false }; }, }), [data], ); } function useMeeting({ onParticipantJoined, onParticipantLeft, onSpeakerChanged, onPresenterChanged, onMainParticipantChanged, onEntryRequested, onEntryResponded, onRecordingStarted, onRecordingStopped, onChatMessage, onMeetingJoined, onMeetingLeft, onLiveStreamStarted, onLiveStreamStopped, onVideoStateChanged, onVideoSeeked, onWebcamRequested, onMicRequested, onPinStateChanged, onConnectionOpen, onConnetionClose, onSwitchMeeting, onError, onHlsStarted, onHlsStopped, onHlsStateChanged, onPausedAllStreams, onResumedAllStreams, onRecordingStateChanged, onLivestreamStateChanged, onMeetingStateChanged, onParticipantModeChanged, onQualityLimitation, onCharacterJoined, onCharacterLeft, onMediaRelayStarted, onMediaRelayStopped, onMediaRelayError, onMediaRelayRequestResponse, onMediaRelayRequestReceived, } = {}) { const meeting = OldUseMeeting({ onParticipantJoined, onParticipantLeft, onSpeakerChanged, onPresenterChanged, onMainParticipantChanged, onEntryRequested, onEntryResponded, onRecordingStarted, onRecordingStopped, onChatMessage, onMeetingJoined, onMeetingLeft, onLiveStreamStarted, onLiveStreamStopped, onVideoStateChanged, onVideoSeeked, onWebcamRequested, onMicRequested, onPinStateChanged, onConnectionOpen, onConnetionClose, onSwitchMeeting, onError, onHlsStarted, onHlsStopped, onHlsStateChanged, onPausedAllStreams, onResumedAllStreams, onRecordingStateChanged, onLivestreamStateChanged, onMeetingStateChanged, onParticipantModeChanged, onQualityLimitation, onCharacterJoined, onCharacterLeft, onMediaRelayStarted, onMediaRelayStopped, onMediaRelayError, onMediaRelayRequestResponse, onMediaRelayRequestReceived, }); const enableScreenShare = ({ customScreenShareTrack = undefined, enableAudio = false, } = {}) => { if (Platform.OS === "android") { const { title, message } = notificationObj; ScreenShareManager.configureNotification({ title: title ? title : "Provide notification title", message: message ? message : "Provide notification message", }); if (enableAudio && e2eeManager) { ScreenShareManager.enableSystemAudio(); } meeting.enableScreenShare(customScreenShareTrack); } else { meeting.enableScreenShare(customScreenShareTrack); } }; const disableScreenShare = () => { ScreenShareManager.disableSystemAudio(); ScreenShareManager.disableScreenShare(); meeting.disableScreenShare(); }; const toggleScreenShare = ({ customScreenShareTrack = undefined, enableAudio = false, } = {}) => { if (meeting.localScreenShareOn) { disableScreenShare(); } else { enableScreenShare({ customScreenShareTrack: customScreenShareTrack, enableAudio: enableAudio, }); } }; const extrasRef = useRef({}); extrasRef.current = { e2eeEnabled: e2eeManager || false, enableScreenShare, disableScreenShare, toggleScreenShare, }; return useMemo( () => new Proxy(meeting, { get(target, prop) { if (RN_EXTRAS.includes(prop)) return extrasRef.current[prop]; return target[prop]; }, has(target, prop) { return RN_EXTRAS.includes(prop) || prop in target; }, ownKeys(target) { return [...new Set([...Reflect.ownKeys(target), ...RN_EXTRAS])]; }, getOwnPropertyDescriptor() { return { configurable: true, enumerable: true, writable: false }; }, }), [meeting], ); } export { register, usePubSub, useConnection, useWhiteboard, Constants, MeetingConsumer, MeetingProvider, useParticipant, useFile, RTCView, MediaStream, MediaStreamTrack, permissions, mediaDevices, createCameraVideoTrack, createMicrophoneAudioTrack, createScreenShareVideoTrack, getAudioDeviceList, switchAudioDevice, useMediaDevice, useTranscription, useMeeting, useAgentParticipant, useCharacter, applyVideoProcessor, removeVideoProcessor, useKeyProvider, useRealtimeStore, useStream, setLogLevel, };