UNPKG

@videosdk.live/react-native-sdk

Version:

<h1 align="center"> <img src="https://static.videosdk.live/videosdk_logo_website_black.png"/><br/> <p align="center"> Video SDK React Native App <br/> <a href="https://videosdk.live/">videosdk.live</a> </p> </h1>

439 lines (409 loc) 11 kB
import React, { useEffect, useRef, useState } 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, SystemAudioShare } 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, } from "@videosdk.live/react-sdk"; import ReactNativeForegroundService from "@videosdk.live/react-native-foreground-service"; 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; function generateTaskName(length) { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } /**@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 foregroundServiceInitialize = async () => { const { title, message } = notificationObj; const id = Math.floor(Math.random() * 1000); await ReactNativeForegroundService.start({ id, title: title ? title : "Provide notification title", message: message ? message : "Provide notification message", }); }; const foregroundServiceTerminate = async () => { if (ReactNativeForegroundService.is_running()) { await ReactNativeForegroundService.stopAll(); } }; const initialize = ({ notification }) => { notificationObj = notification; InCallManager.start({ media: "video" }); }; const terminate = () => { VideoProcessor.removeVideoProcessor(); InCallManager.stop(); e2eeManager = null; foregroundServiceTerminate(); }; const register = async () => { registerGlobals(); navigator.mediaDevices.getDisplayMedia = mediaDevices.getDisplayMedia.bind(mediaDevices); }; function MeetingProvider(props) { const [deviceInfoConfig, setDeviceInfoConfig] = useState(null); const { keyProvider } = props; if (keyProvider) { e2eeManager = new E2EEManager(keyProvider); } useEffect(() => { Platform.OS === "android" && requestBluetoothConnectPermission(); 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(); }; }, []); const requestBluetoothConnectPermission = async () => { try { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, { title: "This Permission will hep you to communicate over Bluetooth Peripherals", buttonNegative: "Cancel", buttonPositive: "OK", } ); if (granted === PermissionsAndroid.RESULTS.GRANTED) { console.log("Bluetooth Connect Permission Granted"); } else { console.log("Bluetooth Connect Permission Denied"); } } catch (err) { console.warn(err); } }; 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, onE2EEStateChanged = () => {}, // default no-op function } = {} ) { const data = OldUseParticipant(participantId, { onStreamEnabled, onStreamDisabled, 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); } }; return { ...data, captureImage, }; } 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, } = {}) { 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, }); const enableScreenShare = (customScreenShareTrack = undefined) => { if (Platform.OS === "android") { if (!ReactNativeForegroundService.is_running()) { ReactNativeForegroundService.register({ taskName: generateTaskName(10), }); foregroundServiceInitialize(); } meeting.enableScreenShare(customScreenShareTrack); } else { meeting.enableScreenShare(customScreenShareTrack); } }; const disableScreenShare = () => { foregroundServiceTerminate(); meeting.disableScreenShare(); SystemAudioShare.stopSystemAudioShare() }; const toggleScreenShare = (customScreenShareTrack = undefined) => { if (meeting.localScreenShareOn) { disableScreenShare(); } else { enableScreenShare(customScreenShareTrack); } }; return { ...meeting, e2eeEnabled: e2eeManager || false, enableScreenShare, disableScreenShare, toggleScreenShare, }; } export { register, usePubSub, useConnection, useWhiteboard, Constants, MeetingConsumer, MeetingProvider, useParticipant, useFile, RTCView, MediaStream, MediaStreamTrack, permissions, mediaDevices, ReactNativeForegroundService, createCameraVideoTrack, createMicrophoneAudioTrack, createScreenShareVideoTrack, getAudioDeviceList, switchAudioDevice, useMediaDevice, useTranscription, useMeeting, useCharacter, applyVideoProcessor, removeVideoProcessor, useKeyProvider, };