I am exploring webRTC and trying to build a zoom alike web application. I have successfully sharing screen and showing it my device but I could show the shared screen to the peer networks. I want to share my screen with everyone who are connected to the same room. I don't know how to do that. Can anyone help me with the issue?
Below is the JavaScript code:
Script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '8000'
})
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
let videoStream
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
videoStream = stream
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
})
socket.on('user-disconnected', userId => {
if (peers[userId]) peers[userId].close()
})
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
//====================================== Front-end styling logics =================================
const audio = document.getElementById('audio')
const audio_mute = document.getElementById('audio-mute')
const video = document.getElementById('video')
const video_mute = document.getElementById('video-mute')
const screen_share = document.getElementById('screen-share')
const record = document.getElementById('record')
const record_stop = document.getElementById('record-stop')
const leave_btn = document.getElementById('leave-btn')
const message_view_box = document.getElementById('message-view-box')
audio.addEventListener('click', function () {
const track = videoStream.getAudioTracks()[0].enabled
console.log(videoStream.getAudioTracks());
if (track) {
videoStream.getAudioTracks()[0].enabled = false
}
else {
videoStream.getAudioTracks()[0].enabled = true
}
audio.style.display = 'none';
audio_mute.style.display = 'inline-block';
})
audio_mute.addEventListener('click', function () {
const track = videoStream.getAudioTracks()[0].enabled
console.log(videoStream.getAudioTracks());
if (track) {
videoStream.getAudioTracks()[0].enabled = false
}
else {
videoStream.getAudioTracks()[0].enabled = true
}
audio_mute.style.display = 'none';
audio.style.display = 'inline-block';
})
video.addEventListener('click', function () {
const track = videoStream.getVideoTracks()[0].enabled
console.log(videoStream.getVideoTracks()[0].enabled);
if (track) {
videoStream.getVideoTracks()[0].enabled = false
}
else {
videoStream.getVideoTracks()[0].enabled = true
}
video.style.display = 'none';
video_mute.style.display = 'inline-block';
})
video_mute.addEventListener('click', function () {
const track = videoStream.getVideoTracks()[0].enabled
console.log(videoStream.getVideoTracks()[0].enabled);
if (track) {
videoStream.getVideoTracks()[0].enabled = false
}
else {
videoStream.getVideoTracks()[0].enabled = true
}
video_mute.style.display = 'none';
video.style.display = 'inline-block';
})
// ============================= Chat box logics ===============================
let chat_box = document.getElementById('chat_box');
let chat_box_input = document.getElementById('chat_box_input');
let send_icon = document.getElementById('send-icon');
chat_box.addEventListener('submit', function (e) {
e.preventDefault()
// console.log(e.target.chat_box_input.value);
if (chat_box_input.value) {
socket.emit('chat message', chat_box_input.value);
chat_box_input.value = '';
}
// e.target.chat_box_input.value = ''
})
socket.on('chat message', function (msg) {
const item = document.createElement('li');
item.textContent = msg;
message_view_box.appendChild(item);
message_view_box.scrollTop = message_view_box.scrollHeight - message_view_box.clientHeight;
});
// =============================================================================
//================================== Screen share logics =======================
const videoElem = document.getElementById('screen')
screen_share.addEventListener('click',async function () {
startCapture();
})
// Options for getDisplayMedia()
const displayMediaOptions = {
video: {
cursor: "always"
},
audio: true
};
async function startCapture() {
try {
videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// const videoStreamTrack = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// call.peerConnection.getSenders()[1].replaceTrack(videoStreamTrack)
dumpOptionsInfo();
} catch (err) {
console.error(`Error: ${err}`);
}
}
function dumpOptionsInfo() {
const videoTrack = videoElem.srcObject.getVideoTracks()[0];
}
//==============================================================================
//==============================================================================
record.addEventListener('click', async function () {
record.style.display = 'none';
record_stop.style.display = 'inline-block';
let stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true
})
//needed for better browser support
const mime = MediaRecorder.isTypeSupported("video/webm; codecs=vp9")
? "video/webm; codecs=vp9"
: "video/webm"
let mediaRecorder = new MediaRecorder(stream, {
mimeType: mime
})
let chunks = []
mediaRecorder.addEventListener('dataavailable', function(e) {
chunks.push(e.data)
})
mediaRecorder.addEventListener('stop', function(){
let blob = new Blob(chunks, {
type: chunks[0].type
})
let url = URL.createObjectURL(blob)
// let video = document.querySelector("video")
// video.src = url
let a = document.createElement('a')
a.href = url
a.download = 'video.webm'
a.click()
})
//we have to start the recorder manually
mediaRecorder.start()
})
record_stop.addEventListener('click', function () {
record_stop.style.display = 'none';
record.style.display = 'inline-block';
mediaRecorder.stop();
})
//==============================================================================
screen sharing functionality below
//================================== Screen share logics =======================
const videoElem = document.getElementById('screen')
screen_share.addEventListener('click',async function () {
startCapture();
})
// Options for getDisplayMedia()
const displayMediaOptions = {
video: {
cursor: "always"
},
audio: true
};
async function startCapture() {
try {
videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// const videoStreamTrack = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
// call.peerConnection.getSenders()[1].replaceTrack(videoStreamTrack)
dumpOptionsInfo();
} catch (err) {
console.error(`Error: ${err}`);
}
}
function dumpOptionsInfo() {
const videoTrack = videoElem.srcObject.getVideoTracks()[0];
}
//==============================================================================
Related
I have a Raspberry Pi running a Node express server with webRTC. The Raspberry Pi is connected to a web cam. I'm able to view the index.html page and see the video stream, but then it randomly freezes or black out completely. I've tried researching the issue but most solutions refer to changing settings on components that are not part of what I'm using. Help is sincerely appreciated as I've been banging my head for the past few days.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Stream</title>
<meta charset="UTF-8" />
<link href="/styles.css" rel="stylesheet" />
</head>
<body>
<video playsinline autoplay muted></video>
<button id="enable-audio">Enable audio</button>
<script src="/socket.io/socket.io.js"></script>
<script src="/watch.js"></script>
</body>
</html>
broadcast.js
// wip 21 January 2022
const peerConnections = {};
const config = {
iceServers: [
{
"urls": "stun:stun.l.google.com:19302",
},
// {
// "urls": "turn:TURN_IP?transport=tcp",
// "username": "TURN_USERNAME",
// "credential": "TURN_CREDENTIALS"
// }
]
};
const socket = io.connect(window.location.origin);
socket.on("answer", (id, description) => {
peerConnections[id].setRemoteDescription(description);
});
socket.on("watcher", id => {
const peerConnection = new RTCPeerConnection(config);
peerConnections[id] = peerConnection;
let stream = videoElement.srcObject;
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
peerConnection
.createOffer()
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("offer", id, peerConnection.localDescription);
});
});
socket.on("candidate", (id, candidate) => {
peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on("disconnectPeer", id => {
peerConnections[id].close();
delete peerConnections[id];
});
window.onunload = window.onbeforeunload = () => {
socket.close();
};
// Get camera and microphone
const videoElement = document.querySelector("video");
const audioSelect = document.querySelector("select#audioSource");
const videoSelect = document.querySelector("select#videoSource");
audioSelect.onchange = getStream;
videoSelect.onchange = getStream;
getStream()
.then(getDevices)
.then(gotDevices);
function getDevices() {
return navigator.mediaDevices.enumerateDevices();
}
function gotDevices(deviceInfos) {
window.deviceInfos = deviceInfos;
for (const deviceInfo of deviceInfos) {
const option = document.createElement("option");
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === "audioinput") {
option.text = deviceInfo.label || `Microphone ${audioSelect.length + 1}`;
audioSelect.appendChild(option);
} else if (deviceInfo.kind === "videoinput") {
option.text = deviceInfo.label || `Camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
}
}
}
function getStream() {
if (window.stream) {
window.stream.getTracks().forEach(track => {
track.stop();
});
}
const audioSource = audioSelect.value;
const videoSource = videoSelect.value;
const constraints = {
audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
video: { deviceId: videoSource ? { exact: videoSource } : undefined }
};
return navigator.mediaDevices
.getUserMedia(constraints)
.then(gotStream)
.catch(handleError);
}
function gotStream(stream) {
window.stream = stream;
audioSelect.selectedIndex = [...audioSelect.options].findIndex(
option => option.text === stream.getAudioTracks()[0].label
);
videoSelect.selectedIndex = [...videoSelect.options].findIndex(
option => option.text === stream.getVideoTracks()[0].label
);
videoElement.srcObject = stream;
socket.emit("broadcaster");
}
function handleError(error) {
console.error("Error: ", error);
}
watch.js
let peerConnection;
const config = {
iceServers: [
{
"urls": "stun:stun.l.google.com:19302",
},
// {
// "urls": "turn:TURN_IP?transport=tcp",
// "username": "TURN_USERNAME",
// "credential": "TURN_CREDENTIALS"
// }
]
};
const socket = io.connect(window.location.origin);
const video = document.querySelector("video");
const enableAudioButton = document.querySelector("#enable-audio");
enableAudioButton.addEventListener("click", enableAudio)
socket.on("offer", (id, description) => {
peerConnection = new RTCPeerConnection(config);
peerConnection
.setRemoteDescription(description)
.then(() => peerConnection.createAnswer())
.then(sdp => peerConnection.setLocalDescription(sdp))
.then(() => {
socket.emit("answer", id, peerConnection.localDescription);
});
peerConnection.ontrack = event => {
video.srcObject = event.streams[0];
};
peerConnection.onicecandidate = event => {
if (event.candidate) {
socket.emit("candidate", id, event.candidate);
}
};
});
socket.on("candidate", (id, candidate) => {
peerConnection
.addIceCandidate(new RTCIceCandidate(candidate))
.catch(e => console.error(e));
});
socket.on("connect", () => {
socket.emit("watcher");
});
socket.on("broadcaster", () => {
socket.emit("watcher");
});
window.onunload = window.onbeforeunload = () => {
socket.close();
peerConnection.close();
};
function enableAudio() {
console.log("Enabling audio")
video.muted = false;
}
I was following Fireships' Electron tutorial to build a desktop capturer.
One thing I know is, as of now there is a huge difference between the version I used and his.
The only problem I am having is during the instantiation of the MediaRecorder class.
The class is not identified at all.
Is there a way I can fix it?
Render.js - Source Code
// Buttons
const videoElement = document.querySelector('video');
const startBtn = document.getElementById('startBtn');
startBtn.onclick = e => {
mediaRecorder.start();
startBtn.classList.add('is-danger');
startBtn.innerText = 'Recording';
};
const stopBtn = document.getElementById('stopBtn');
stopBtn.onclick = e => {
mediaRecorder.stop();
startBtn.classList.remove('is-danger');
startBtn.innerText = 'Start';
};
const videoSelectBtn = document.getElementById('videoSelectBtn');
videoSelectBtn.onclick = getVideoSources;
const { desktopCapturer, remote } = require('electron');
const { dialog, Menu } = remote;
// Get the available video sources
async function getVideoSources() {
const inputSources = await desktopCapturer.getSources({
types: ['window', 'screen']
});
const videoOptionsMenu = Menu.buildFromTemplate(
inputSources.map(source => {
return {
label: source.name,
click: () => selectSource(source)
};
})
);
videoOptionsMenu.popup();
}
let mediaRecorder; //MediaRecorder instance to capture footage
const recordedChunks = [];
// Change the videoSources window to record
async function selectSource(source) {
videoSelectBtn.innerText = source.name;
const constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id
}
}
};
// Create a Stream
const stream = await navigator.mediaDevices.getUserMedia(constraints);
//Preview the source in a video element
videoElement.srcObject = stream;
videoElement.play();
// Create the Media Recorder
const options = { mimeType: 'video/webm; codecs=vp9' };
mediaRecorder = new MediaRecorder(stream, options);
// Register Event Handlers
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.onStop = handleStop;
}
// Captures allrecorded chunks
function handleDataAvailable(e) {
console.log('video data available')
recordedChunks.push(e.data);
}
const { writeFile } = require('fs');
//Saves the video file on stop
async function handleStop(e) {
const blob = new Blob(recordedChunks,{
type: 'video/webm; codecs=vp9'
});
const buffer = Buffer.from(await blob.arrayBuffer());
const { filePath } = await dialog.showSaveDialog({
buttonLabel: 'Save Video',
defaultPath: `vid -${Date.now()}.webm`
});
console.log(filePath);
writeFile(filePath, buffer, () => console.log('Video Saved Successfully!'));
}
Web Preferences - Index.js
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
}
Try this in the render.js file, using electron": "10.2.0
const { desktopCapturer, remote, dialog } = require('electron');
const { writeFile } = require('fs');
const { Menu } = remote;
//Buttons
const videoElement = document.querySelector('video');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const videoSelectBtn = document.getElementById('videoSelectBtn');
videoSelectBtn.onclick = getVideoSources();
//Get all available video sources
async function getVideoSources() {
const inputSources = await desktopCapturer.getSources({
types: ['window', 'screen'],
});
const videoOptionsMenu = Menu.buildFromTemplate(
inputSources.map((source) => {
return {
label: source.name,
click: () => selectSource(source),
};
})
);
videoOptionsMenu.popup();
}
let mediaRecorder; //Mediarecorder instance to capture footage
const recordedChunks = [];
async function selectSource(source) {
videoSelectBtn.innerText = source.name;
const constraints = {
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id,
},
},
};
//Create a stream
const stream = await navigator.mediaDevices.getUserMedia(constraints);
//Preview the source in a video element
videoElement.srcObject = stream;
videoElement.play();
//Create the Media Recorder
const options = { mimeType: 'video/webm; codecs=vp9' };
mediaRecorder = new mediaRecorder(stream, options);
//Register Event Handlers
mediaRecorder.ondataavailable = handleAvailableData;
mediaRecorder.onstop = handleStop;
}
async function handleAvailableData(e) {
console.log('Video data available');
recordedChunks.push(e.data);
}
//Save video on stop
async function handleStop(e) {
const blob = new Blob(recordedChunks, {
type: 'video/webm; codecs=vp9',
});
const buffer = Buffer.from(await blob.arrayBuffer());
const { filePath } = await dialog.showSaveDialog({
buttonLabel: 'Save Video',
defaultPath: `vid-${Date.now()}.webm`
})
console.log(filePath);
writeFile(filePath, buffer, () => console.log('Saved Successfully'))
}
I have been trying to create a video group call application. It follows a mesh topology and the steps I follow are these:
When a client connects. A room is created if it is the first client or it broadcasts its socket id to all other clients in that room
When a client receives the socket id it sends it an offer and the cycle starts. A similar flow is followed for the other clients.
The problem is when I simply create data channels, it works fine but as soon as I start adding media Tracks, things break and even the data channel doesn't get connected. I'll attach the code below
import { io } from 'socket.io-client'
class RTConnection {
constructor() {
this.config = {
iceServers: [
{
urls: ["stun:stun.l.google.com:19302"]
}
]
}
this.connections = {}
this.videoElements = {}
this.ICEqueue = {}
}
init = async (roomId, videoElemRef) => {
this.roomId = roomId
this.videoElemRef = videoElemRef.current
let video = document.createElement('VIDEO')
this.myVideo = video
video.onloadedmetadata = e => video.play()
this.videoElemRef.appendChild(video)
const stream = await navigator.mediaDevices.getUserMedia({video: true, audio: false})
video.srcObject = stream
this.socket = io()
this.socket.emit("join-room", roomId)
this.socket.on('new-member', id => {
this.createOffer(id)
})
this.socket.on("offer", (id, offer) => {
this.acceptOffer(id, offer)
})
this.socket.on("new-ice-candidate", (id, candidate) => {
this.handleIceCandidate(id, candidate)
})
this.socket.on("answer", (id, answer) => {
this.acceptAnswer(id, answer)
})
}
acceptAnswer = async (id, answer) => {
const peerConnection = this.connections[id]
await peerConnection.setRemoteDescription(answer)
console.log("Answer Accepted")
}
handleIceCandidate = async (id, candidate) => {
if(!candidate) {
return
}
const peerConnection = this.connections[id]
if(peerConnection.remoteDescription) {
console.log("Setting Ice")
console.log(candidate)
await peerConnection.addIceCandidate(new RTCIceCandidate(candidate))
} else {
if(id in this.ICEqueue) {
console.log("Storing Ice Candidates")
console.log(id)
this.ICEqueue[id].push(candidate)
} else {
this.ICEqueue[id] = []
}
}
}
hydrateIceCandidates = async (id) => {
const peerConnection = this.connections[id]
console.log("Hydrating Ice")
for(let i in this.ICEqueue[id]) {
const candidate = this.ICEqueue[id][i]
await peerConnection.addIceCandidate(candidate)
}
}
acceptOffer = async (id, offer) => {
const peerConnection = new RTCPeerConnection()
peerConnection.ondatachannel = e => {
console.log("Data Channel Received")
peerConnection.dc = e.channel
peerConnection.dc.onopen = e => console.log("Connected!!!!")
}
this.connections[id] = peerConnection
peerConnection.onicecandidate = e => {
this.socket.emit("new-ice-candidate", e.candidate)
}
peerConnection.ontrack = (e) => {
console.log("Received a track");
console.log(e.streams)
const video = document.createElement('VIDEO')
this.videoElemRef.appendChild(video)
video.onloadeddata = ev => {console.log("Meta Data Loaded") ; video.play()}
video.srcObject = e.streams[0]
}
const stream = this.myVideo.srcObject
console.log("Private Stream")
console.log(stream)
//await stream.getTracks().forEach(async track => peerConnection.addTrack(track, stream))
await peerConnection.setRemoteDescription(offer)
console.log("Remote Description Set")
const ans = await peerConnection.createAnswer()
console.log("Answer Created")
await peerConnection.setLocalDescription(ans)
console.log("Local Description Set")
await this.hydrateIceCandidates(id)
this.socket.emit("answer", id, ans)
}
createOffer = async (id) => {
const peerConnection = new RTCPeerConnection()
const stream = this.myVideo.srcObject
//stream.getTracks().forEach(track => peerConnection.addTrack(track, stream))
peerConnection.dc = peerConnection.createDataChannel("channel")
peerConnection.dc.onopen = e => {
console.log("Connected!!!")
}
this.connections[id] = peerConnection
peerConnection.ontrack = (e) => {
console.log("Received a track");
console.log(e.streams)
const video = document.createElement('VIDEO')
console.log("Video Element")
console.log(video)
this.videoElemRef.appendChild(video)
video.onloadedmetadata = ev => video.play()
video.srcObject = e.streams[0]
}
peerConnection.onicecandidate = (e) => {
this.socket.emit("new-ice-candidate", e.candidate)
}
const offer = await peerConnection.createOffer()
await peerConnection.setLocalDescription(offer)
console.log(peerConnection.localDescription)
this.socket.emit("offer", id, offer)
}
stopStream = (stream, elem) => {
stream.getTracks().forEach(track => {
track.stop()
})
this.videoElemRef.current.removeChild(elem)
}
cleanMedia = () => {
this.stopStream(this.videoEl.srcObject, this.videoEl)
for(let id in this.videoElements) {
this.stopStream(this.videoElements[id].srcObject, this.videoElements[id])
delete this.videoElements[id]
}
delete this.videoElements
}
leaveCall = () => {
for(let id in this.connections) {
this.connections[id].close()
delete this.connections[id]
}
delete this.connections
this.cleanMedia()
this.socket.disconnect()
}
}
export default RTConnection
Server-Side Code
const express = require('express')
const fs = require('fs')
const app = express()
const path = require('path')
const v4 = require('uuid').v4
const options = {
key: fs.readFileSync("key.pem"),
cert: fs.readFileSync("cert.pem")
}
const server = require('http').createServer(app)
const io = require('socket.io')(server)
const port = process.env.port || '5000'
app.set('view engine', 'ejs')
app.use("/",express.static(path.resolve(__dirname + '/public')))
app.get('/',(req, res) => {
res.render('home')
})
app.get('/room', (req,res) => {
const roomId = v4()
res.redirect(`/room/${roomId}`)
})
app.get('/room/:roomId',(req, res) => {
res.render('room', {roomId: req.params.roomId})
})
server.listen(port, () => {
console.log(`Starting server on ${port}`)
})
io.on("connection", socket => {
socket.on('join-room', (roomId) => {
socket.join(roomId)
socket.to(roomId).emit("new-member", socket.id)
socket.on("new-ice-candidate", (candidate) => {
socket.to(roomId).emit("new-ice-candidate", socket.id, candidate)
})
socket.on("offer", (id, offer) => {
socket.to(id).emit("offer", socket.id, offer)
})
socket.on("answer", (id, answer) => {
socket.to(id).emit("answer", socket.id, answer)
})
socket.on("disconnecting", () => {
for(let room of socket.rooms) {
socket.to(room).emit("leaveCall", socket.id)
}
})
})
})
When I add getUserMedia, it is working properly but when I change getUserMedia to getDisplayMedia, it is showing screen share in my window but it is not streaming to another device.
Here is my script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
path: '/peerjs',
host: '/',
port: '443'
})
let myVideoStream;
const myVideo = document.createElement('video')
myVideo.muted = true;
const peers = {}
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
myVideoStream = stream;
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
})
// input value
let text = $("input");
// when press enter send message
$('html').keydown(function (e) {
if (e.which == 13 && text.val().length !== 0) {
socket.emit('message', text.val());
text.val('')
}
});
socket.on("createMessage", message => {
$("ul").append(`<li class="message"><b>user</b><br/>${message}</li>`);
scrollToBottom()
})
})
socket.on('user-disconnected', userId => {
if (peers[userId]) peers[userId].close()
})
myPeer.on('open', id => {
socket.emit('join-room', ROOM_ID, id)
})
function connectToNewUser(userId, stream) {
const call = myPeer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideoStream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
peers[userId] = call
}
function addVideoStream(video, stream) {
video.srcObject = stream
video.addEventListener('loadedmetadata', () => {
video.play()
})
videoGrid.append(video)
}
const scrollToBottom = () => {
var d = $('.main__chat_window');
d.scrollTop(d.prop("scrollHeight"));
}
const muteUnmute = () => {
const enabled = myVideoStream.getAudioTracks()[0].enabled;
if (enabled) {
myVideoStream.getAudioTracks()[0].enabled = false;
setUnmuteButton();
} else {
setMuteButton();
myVideoStream.getAudioTracks()[0].enabled = true;
}
}
const playStop = () => {
console.log('object')
let enabled = myVideoStream.getVideoTracks()[0].enabled;
if (enabled) {
myVideoStream.getVideoTracks()[0].enabled = false;
setPlayVideo()
} else {
setStopVideo()
myVideoStream.getVideoTracks()[0].enabled = true;
}
}
const shareScreen = async () => {
let captureStream = null
let userId=1233
try {
captureStream = await navigator.mediaDevices.getDisplayMedia()
connectToNewUser(userId, captureStream)
} catch (err) {
console.error("Error: " + err)
}
//
};
const setMuteButton = () => {
const html = `
<i class="fas fa-microphone"></i>
<span>Mute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setUnmuteButton = () => {
const html = `
<i class="unmute fas fa-microphone-slash"></i>
<span>Unmute</span>
`
document.querySelector('.main__mute_button').innerHTML = html;
}
const setStopVideo = () => {
const html = `
<i class="fas fa-video"></i>
<span>Stop Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
const setPlayVideo = () => {
const html = `
<i class="stop fas fa-video-slash"></i>
<span>Play Video</span>
`
document.querySelector('.main__video_button').innerHTML = html;
}
Here is my github fork github code
Everything is working fine with heroku too which is encrypted with ssl certificate.
Here is my heroku link
Replace this
const myPeer = new Peer(undefined, {
path: '/peerjs',
host: '/',
port: '443'
})
With this
var peer = new Peer();
I'm following this website to make a video call in react.
I slightly changed the code in connection.js for my pure javascript project, but other files are same as the tutorial.
Here is my connection.js
import openSocket from 'socket.io-client';
import Peer from 'peerjs';
let socketInstance = null;
let peers = {};
const initializePeerConnection = () => {
return new Peer('', {
host: 'localhost',
port: 9000,
secure: false //secure=true is https and wss. secure=false is http and ws
});
//return new Peer()
}
const initializeSocketConnection = () => {
const socket = openSocket('http://localhost:5000');
return socket
}
class Connection {
videoContainer = {};
message = [];
settings;
streaming = false;
myPeer;
socket;
myID = '';
constructor(settings) {
this.settings = settings;
this.myPeer = initializePeerConnection();
this.socket = initializeSocketConnection();
this.initializeSocketEvents();
this.initializePeersEvents();
}
initializeSocketEvents = () => {
this.socket.on('connect', () => {
console.log('socket connected');
});
this.socket.on('user-disconnected', (userID) => {
console.log('user disconnected-- closing peers', userID);
peers[userID] && peers[userID].close();
this.removeVideo(userID);
});
this.socket.on('disconnect', () => {
console.log('socket disconnected --');
});
this.socket.on('error', (err) => {
console.log('socket error --', err);
});
}
initializePeersEvents = () => {
this.myPeer.on('open', (id) => {
this.myID = id;
const roomID = window.location.pathname.split('/')[2];
const userData = {
userID: id, roomID
}
console.log('mydata::', userData)
this.socket.emit('join-room', userData);
this.setNavigatorToStream();
});
this.myPeer.on('error', (err) => {
console.log('peer connection error', err);
this.myPeer.reconnect();
})
}
setNavigatorToStream = () => {
this.getVideoAudioStream().then((stream) => {
if (stream) {
this.streaming = true;
this.createVideo({ id: this.myID, stream });
this.setPeersListeners(stream);
this.newUserConnection(stream);
}
})
}
getVideoAudioStream = (video=true, audio=true) => {
let quality = this.settings.params?.quality;
if (quality) quality = parseInt(quality);
const myNavigator = navigator.mediaDevices.getUserMedia ||
navigator.mediaDevices.webkitGetUserMedia ||
navigator.mediaDevices.mozGetUserMedia ||
navigator.mediaDevices.msGetUserMedia;
return myNavigator({
video: video ? {
frameRate: quality ? quality : 12,
noiseSuppression: true,
width: {min: 640, ideal: 1280, max: 1920},
height: {min: 480, ideal: 720, max: 1080}
} : false,
audio: audio,
});
}
createVideo = (createObj) => {
console.log('video created')
if (!this.videoContainer[createObj.id]) {
this.videoContainer[createObj.id] = {
...createObj,
};
const roomContainer = document.getElementById('room-container');
const videoContainer = document.createElement('div');
const video = document.createElement('video');
video.srcObject = this.videoContainer[createObj.id].stream;
video.id = createObj.id;
video.autoplay = true;
if (this.myID === createObj.id) {
video.muted = true;
video.className = 'my_video'
}
videoContainer.appendChild(video)
roomContainer.append(videoContainer);
} else {
const video = document.getElementById(createObj.id)
video.srcObject = createObj.stream
}
}
setPeersListeners = (stream) => {
//stream is my stream
this.myPeer.on('call', function(call) {
console.log('answered!!')
call.answer(stream);
call.on('stream', (userVideoStream) => {
console.log('user stream data', userVideoStream)
this.createVideo({ id: call.metadata.id, stream: userVideoStream });
});
call.on('close', () => {
console.log('closing peers listeners', call.metadata.id);
this.removeVideo(call.metadata.id);
});
call.on('error', () => {
console.log('peer error ------');
this.removeVideo(call.metadata.id);
});
peers[call.metadata.id] = call;
});
}
newUserConnection = (stream) => {
//stream is my stream
this.socket.on('new-user-connect', (userData) => {
this.connectToNewUser(userData, stream);
});
}
connectToNewUser(userData, stream) {
console.log('connectTonewUser is called')
const { userID } = userData;
const call = this.myPeer.call(userID, stream, { metadata: { id: this.myID }} );
console.log('your data:: new user connected', userData)
call.on('stream', (userVideoStream) => {
//userVideoStream is your stream
console.log('your stream::', userVideoStream)
this.createVideo({ id: userID, stream: userVideoStream, userData });
});
call.on('close', () => {
console.log('closing new user', userID);
this.removeVideo(userID);
});
call.on('error', () => {
console.log('peer error ------')
this.removeVideo(userID);
})
peers[userID] = call;
}
removeVideo = (id) => {
delete this.videoContainer[id];
const video = document.getElementById(id);
if (video) video.remove();
}
destroyConnection = () => {
const myMediaTracks = this.videoContainer[this.myID]?.stream.getTracks();
myMediaTracks?.forEach((track) => {
track.stop();
})
socketInstance?.socket.disconnect();
this.myPeer.destroy();
}
}
export default function createSocketConnectionInstance(settings={}) {
return socketInstance = new Connection(settings);
}
It is confirmed that both two clients are connected to peer.js and the sockets are also connected.
However, after I called in connectToNewUser, it seems to not answer in setPeersListeners.
I think the shape of video call function is quite same as peerjs doc
Do you have any ideas why it doesn't work?
Thank you :)
It worked when I removed all arguments somehow.
used to be
const initializePeerConnection = () => {
return new Peer('', {
host: 'localhost',
port: 9000,
secure: false //secure=true is https and wss. secure=false is http and ws
});
}
and in terminal
peerjs -p 9000
👇
this is working correctly
const initializePeerConnection = () => {
return new Peer()
}
you don't have to exectute peerjs in terminal