UNPKG

react-video-call

Version:

ReactVideoCall is a simple yet powerful WebRTC-based video call component for React. It uses Firebase for signaling and supports both desktop and mobile views with minimal setup.

246 lines 11.3 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { FirebaseWrapper } from "./firebaseLib"; export class WebRTCManager { constructor(firebaseConfig, localVidEle, remortVidEle, RTCConfiguration) { this.ref = new Date().toISOString(); this.fireBaseDb = new FirebaseWrapper(firebaseConfig); this.localVidEle = localVidEle; this.remortVidEle = remortVidEle; this.peerConnection = new RTCPeerConnection(RTCConfiguration); this.peerConnection.ontrack = (event) => { this.onRemortTrack(event); }; this.peerConnection.onconnectionstatechange = (event) => { console.debug("connectionState", this.peerConnection.connectionState); if (this.onStateChange) { this.onStateChange(this.peerConnection.connectionState, event); } }; const videoElement = localVidEle.current; let isDragging = false; let offsetX, offsetY; // Handle start of dragging (both mouse and touch) function startDrag(e) { isDragging = true; let clientX; let clientY; if (e instanceof TouchEvent) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } offsetX = clientX - videoElement.offsetLeft; offsetY = clientY - videoElement.offsetTop; videoElement.style.cursor = 'grabbing'; } // Handle dragging (both mouse and touch) function drag(e) { if (!isDragging) return; let clientX; let clientY; if (e instanceof TouchEvent) { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; } else { clientX = e.clientX; clientY = e.clientY; } // Calculate the new position let newX = clientX - offsetX; let newY = clientY - offsetY; // Keep the video within the container boundaries if (!videoElement.parentElement) return; const rect = videoElement.parentElement.getBoundingClientRect(); const videoRect = videoElement.getBoundingClientRect(); if (newX < 0) newX = 0; if (newY < 0) newY = 0; if (newX + videoRect.width > rect.width) newX = rect.width - videoRect.width; if (newY + videoRect.height > rect.height) newY = rect.height - videoRect.height; // Update the position videoElement.style.left = `${newX}px`; videoElement.style.top = `${newY}px`; } // Handle end of dragging (both mouse and touch) function endDrag() { isDragging = false; videoElement.style.cursor = 'grab'; } // Mouse events videoElement.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', endDrag); // Touch events videoElement.addEventListener('touchstart', startDrag); document.addEventListener('touchmove', drag); document.addEventListener('touchend', endDrag); } joinOrStartRoom(name) { return __awaiter(this, void 0, void 0, function* () { this.peerConnection.onicecandidate = (event) => __awaiter(this, void 0, void 0, function* () { //Event that fires off when a new answer ICE candidate is created if (event.candidate) { yield this.fireBaseDb.updateRoom(this.roomRole === "owner" ? "offer" : "answer", JSON.stringify(this.peerConnection.localDescription)); } }); this.roomUnSub = yield this.fireBaseDb.joinRoom(name, (data) => __awaiter(this, void 0, void 0, function* () { var _a; if (!this.roomRole && (!data || (!data.create && !data.offer))) { console.debug("As Owner creating offer."); this.roomRole = "owner"; const offer = yield this.peerConnection.createOffer(); yield this.peerConnection.setLocalDescription(offer); yield this.fireBaseDb.createRoom(name, this.ref); } else if ((this.roomRole === "owner" && data && (data === null || data === void 0 ? void 0 : data.create) && ((_a = data === null || data === void 0 ? void 0 : data.create) === null || _a === void 0 ? void 0 : _a.ref) !== this.ref) || (!this.roomRole && (data && data.offer && !data.completed2))) { if (this.roomRole === "owner") console.debug("Got racecondition and moving to joiner"); console.debug("Joiner creating answer"); this.roomRole = "joiner"; yield this.peerConnection.setRemoteDescription(JSON.parse(data.offer)); const answer = yield this.peerConnection.createAnswer(); yield this.peerConnection.setLocalDescription(answer); yield this.fireBaseDb.updateRoom("completed2", true); } else if (this.roomRole === "owner" && data && data.answer && !data.completed1) { console.debug("OWNER GOT Answer", this.ref); this.peerConnection.setRemoteDescription(JSON.parse(data.answer)); yield this.fireBaseDb.updateRoom("completed1", true); } })); }); } onRemortTrack(event) { if (event.track.kind === "video") { return; } console.log("onRemortTrack"); if (this.remoteStream) { this.remoteStream.getTracks().forEach(track => { track.stop(); }); } const newstrema = new MediaStream(); event.streams[0].getTracks().forEach((track) => { newstrema.addTrack(track); }); this.remoteStream = newstrema; this.remortVidEle.current.srcObject = this.remoteStream; // this.remortVidEle.current.volume = 0; this.remortVidEle.current.play(); } getDevices() { return __awaiter(this, void 0, void 0, function* () { if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) { console.debug("Media devices not supported in this browser."); return { audioDevices: [], videoDevices: [] }; } yield navigator.mediaDevices.getUserMedia({ video: true, audio: true }); const deviceInfos = yield navigator.mediaDevices.enumerateDevices(); const audioDevices = [], videoDevices = []; for (const deviceInfo of deviceInfos) { if (deviceInfo.kind === "audioinput") { audioDevices.push({ value: deviceInfo.deviceId, label: deviceInfo.label, data: deviceInfo }); } else if (deviceInfo.kind === "videoinput") { videoDevices.push({ value: deviceInfo.deviceId, label: deviceInfo.label, data: deviceInfo }); } } return { audioDevices, videoDevices }; }); } selectDevice(camera, mic) { return __awaiter(this, void 0, void 0, function* () { if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) { console.debug("Media devices not supported in this browser."); return; } const constraints = { audio: mic ? { deviceId: mic ? { exact: mic } : undefined } : true, video: camera ? { deviceId: camera ? { exact: camera } : undefined, facingMode: "user" } : true }; navigator.mediaDevices.getUserMedia(constraints). then((stream) => { console.debug("Setting local Devices."); if (this.localStream) { this.localStream.getTracks().forEach(track => { track.stop(); }); } this.localStream = stream; if (this.localStream) { this.localStream.getTracks().forEach((track) => { if (this.localStream) { console.debug("added local track"); this.peerConnection.addTrack(track, this.localStream); } }); this.localVidEle.current.srcObject = this.localStream; this.localVidEle.current.volume = 0; this.localVidEle.current.play(); this.localVidEle.current.style.transform = "scaleX(" + "-1" + ")"; } }).catch((err) => { console.debug("Could not load stream", err); }); }); } disconnectAll() { if (this.roomUnSub) { this.roomUnSub(); } this.peerConnection.close(); if (this.localStream) { // Stop all media tracks this.localStream.getTracks().forEach((track) => track.stop()); this.localStream = undefined; } if (this.remoteStream) { // Stop all media tracks this.remoteStream.getTracks().forEach((track) => track.stop()); this.remoteStream = undefined; } if (this.peerConnection) { this.peerConnection.onicecandidate = null; this.peerConnection.ontrack = null; this.peerConnection.oniceconnectionstatechange = null; this.peerConnection.onnegotiationneeded = null; this.peerConnection.onsignalingstatechange = null; this.peerConnection.onconnectionstatechange = null; } this.fireBaseDb.leaveRoom(); console.debug('Connection Closed...'); } muteMic(isMiute) { var _a; (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks().forEach(track => track.enabled = !isMiute); console.debug("Microphone is muted"); } } //# sourceMappingURL=webrtcManager.js.map