i'm trying to make a zoom/google meet clone.. and i'm using RecordRTC to record the screen and then send it the Node Server via socket.io ...sometimes i get data , and sometimes i don't,..
however i tried to do the same code with websocket ... i didn't get any problem .. always work ... that even made me wonder more,
Please help Me figure the problem where and why ... thank you..
Server Side [Node] :
const express = require('express');
const chalk = require('chalk');
const socketio = require('socket.io')
require('dotenv').config();
const PORT = process.env.PORT || 5000;
const app = express();
app.use(express.static(__dirname + '/public'))
const server = app.listen(PORT, () => {
console.log(chalk.yellowBright.inverse.bold(`Server is Running on PORT ${PORT}`))
})
function writeToDisk(dataURL, fileName) {
var fileExtension = fileName.split('.').pop(),
fileRootNameWithBase = './uploads/' + fileName,
filePath = fileRootNameWithBase,
fileID = 2,
fileBuffer;
// #todo return the new filename to client
while (fs.existsSync(filePath)) {
filePath = fileRootNameWithBase + '(' + fileID + ').' + fileExtension;
fileID += 1;
}
dataURL = dataURL.split(',').pop();
fileBuffer = new Buffer(dataURL, 'base64');
fs.writeFileSync(filePath, fileBuffer);
console.log('filePath', filePath);
}
const io = socketio(server)
io.on('connect', (socket) => {
console.log("Client Has Been Connected")
socket.emit('messageFromServer', { text:'You Are Connected To The Server!'})
socket.on('fromClient',(data)=>{
console.log(chalk.red.bold(data))
if (data.data.video) {
console.log(chalk.red.bold("Video Found"))
writeToDisk(data.data.video.dataURL, fileName + '.webm');
}
})
})
Client Side [Javascript]
var recordButton = document.getElementById('start-recording');
var stopButton = document.getElementById('stop-recording');
var local_video = document.querySelector("#local-video")
const socketio = io('http://localhost:3000/')
console.log('Hello World')
socketio.on('connect', () => {
console.log(socketio.id)
})
function invokeGetDisplayMedia(success, error) {
var displaymediastreamconstraints = {
video: {
displaySurface: 'monitor', // monitor, window, application, browser
logicalSurface: true,
cursor: 'always' // never, always, motion
}
};
displaymediastreamconstraints = {
video: true
};
if (navigator.mediaDevices.getDisplayMedia) {
navigator.mediaDevices.getDisplayMedia(displaymediastreamconstraints).then(success).catch(error);
}
else {
navigator.getDisplayMedia(displaymediastreamconstraints).then(success).catch(error);
}
}
function captureScreen(callback) {
invokeGetDisplayMedia(function (screen) {
callback(screen);
}, function (error) {
console.error(error);
alert('Unable to capture your screen. Please check console logs.\n' + error);
});
}
function startRecording() {
captureScreen(function (stream) {
mediaStream = stream;
local_video.srcObject = stream;
var videoOnlyStream = new MediaStream();
stream.getVideoTracks().forEach(function (track) {
videoOnlyStream.addTrack(track);
});
recordVideo = RecordRTC(videoOnlyStream, {
type: 'video/webm',
canvas: {
width: 1280,
height: 720
},
mandatory: {
// chromeMediaSource: 'screen',
minWidth: 1280,
minHeight: 720,
maxWidth: 1920,
maxHeight: 1080,
minAspectRatio: 1.77
},
recorderType: !!navigator.mozGetUserMedia ? MediaStreamRecorder : WhammyRecorder
});
recordVideo.startRecording();
stopButton.disabled = false;
});
}
function stopRecording() {
recordButton.disabled = false;
stopButton.disabled = true;
// stop video recorder
recordVideo.stopRecording(function () {
recordVideo.getDataURL(function (videoDataURL) {
var files = {
video: {
type: recordVideo.getBlob().type || 'video/webm',
dataURL: videoDataURL
}
};
const data = JSON.stringify(files)
console.log(data)
socketio.emit('fromClient', { "message": "Sent from client!", "data": data });
console.log('EMIT: fromClient');
if (mediaStream) mediaStream.stop();
});
});
}
recordButton.onclick = function () {
recordButton.disabled = true;
startRecording();
}
stopButton.onclick = function () {
stopButton.disabled = true;
stopRecording();
}
HTML :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RealTime Record</title>
</head>
<body>
<center>
<h1>Testing Recording</h1>
</center>
<center>
<div class="record-action">
<button id="start-recording">Start Recording</button>
<button id="stop-recording" disabled>Stop Recording</button>
<button id="fromClient">From Client</button>
</div>
<video id="local-video" autoplay style="border: 1px solid rgb(15, 158, 238);"></video>
</center>
<script src="RecordRTC.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="client.js"></script>
</body>
</html>
Related
I would like to record microphone input from the client and then when he stops, send the data to the server and then output the recorded audio to a specific folder.
So far I have for the recording on the client I have followed this
mediaRecorder.onstop = function(e) {
console.log("recorder stopped");
const blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });
chunks = [];
const formData = new FormData();
formData.append('audio-file', blob);
return fetch('http://localhost:3000/notes', {
method: 'POST',
body: formData
});
}
console.log(blob) on the client returns an object
Blob { size: 35412, type: "audio/ogg; codecs=opus" }
On the server side I use Node.js
app.post("/notes",function(req,res){
console.log(req);
});
The server receives formData but the body is empty {}
I have also tried XMLHttpRequest with the same result.
I've played about with this type of project before. I created a simple form that allows you to record from the microphone, then upload to the server.
Sound files will be saved in ./sound_files
Just run the node server like
node server.js
And go to localhost:3000 to view the page.
Node code (server.js)
const express = require('express');
const multer = require('multer');
const storage = multer.diskStorage(
{
destination: './sound_files/',
filename: function (req, file, cb ) {
cb( null, file.originalname);
}
}
);
const upload = multer( { storage: storage } );
const app = express();
const port = 3000;
app.use(express.static('./'));
app.post("/notes", upload.single("audio_data"), function(req,res){
res.status(200).send("ok");
});
app.listen(port, () => {
console.log(`Express server listening on port: ${port}...`);
});
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Speech to text test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/cerulean/bootstrap.min.css">
</head>
<body style="padding:50px;">
<h1>Speech to text test</h1>
<div id="controls">
<button id="recordButton">Record</button>
<button id="transcribeButton" disabled>Stop and upload to server</button>
</div>
<div id="output"></div>
<script src="https://cdn.rawgit.com/mattdiamond/Recorderjs/08e7abd9/dist/recorder.js"></script>
<script>
let rec = null;
let audioStream = null;
const recordButton = document.getElementById("recordButton");
const transcribeButton = document.getElementById("transcribeButton");
recordButton.addEventListener("click", startRecording);
transcribeButton.addEventListener("click", transcribeText);
function startRecording() {
let constraints = { audio: true, video:false }
recordButton.disabled = true;
transcribeButton.disabled = false;
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
const audioContext = new window.AudioContext();
audioStream = stream;
const input = audioContext.createMediaStreamSource(stream);
rec = new Recorder(input, { numChannels: 1 })
rec.record()
document.getElementById("output").innerHTML = "Recording started..."
}).catch(function(err) {
recordButton.disabled = false;
transcribeButton.disabled = true;
});
}
function transcribeText() {
document.getElementById("output").innerHTML = "Converting audio to text..."
transcribeButton.disabled = true;
recordButton.disabled = false;
rec.stop();
audioStream.getAudioTracks()[0].stop();
rec.exportWAV(uploadSoundData);
}
function uploadSoundData(blob) {
const filename = "sound-file-" + new Date().getTime() + ".wav";
const formData = new FormData();
formData.append("audio_data", blob, filename);
fetch('http://localhost:3000/notes', {
method: 'POST',
body: formData
}).then(async result => {
document.getElementById("output").innerHTML = await result.text();
}).catch(error => {
document.getElementById("output").innerHTML = "An error occurred: " + error;
})
}
</script>
</body>
</html>
I am trying to create an app that allow users to create a videochat event room (by inserting it into the backend's database) and then let other users that have an account on the website to join it. At the moment, the login part is not created, but it is not a problem.
The backend is done in Spring Boot RestAPI (and runs on 8080) and the frontend in nodejs (and runs on 3000). The Peer To Peer system is done using an nodejs server and Peer.js API (and runs on 3001).
The main question is the following:
When the user clicks on an event fetched from the DB, if it is the first one, it becomes host. If not, then it becomes a simple user. When a user enters the room, the host have to refresh the page(like to reconnect to the room) and so does the user, in order to be both connected. Why is so? I will give you the files codes bellow.
The second one: Why this system is not working for Safari and, if it works how to solve it?
server.js:
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4 } = require('uuid')
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.get('/', (req, res) => {
var http = require("http")
// BELOW IT IS THE BACKEND CONNECTION. TO TEST YOUR CODE, YOU NEED AN ARRAY THAT HAVE ARRAYS WITH sessionID AND name PARAMS LIKE SO: [{"sessionID":"1231", "name":"event"},{...}].
http.get("http://localhost:8080/events", (resp) => {
let data = "";
resp.on("data", (chunk) => {
data += chunk;
});
resp.on("end", () => {
console.log(data);
res.render('index', {events: data})
});
})
.on("error", (err) => {
console.log("Error: " + err.message);
});
})
app.get('/join', (req, res) => {
res.render('join')
})
app.get('/event', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
io.on('connection', socket => {
socket.on('join-room', (roomId, userId) => {
console.log("User connected: " + userId)
socket.join(roomId)
socket.to(roomId).broadcast.emit('user-connected', userId)
socket.on('disconnect', () => {
socket.to(roomId).broadcast.emit('user-disconnected', userId)
})
})
})
server.listen(3000)
script.js:
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, { // user id
host: '/', // path to event
port: '3001' // post
})
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(stream => {
addVideoStream(myVideo, stream)
myPeer.on('call', call => {
call.answer(stream) // HOST SEE OTHERS
const video = document.createElement('video')
call.on('stream', userVideoStream => { // OTHERS SEE HOST
addVideoStream(video, userVideoStream)
})
})
socket.on('user-connected', userId => {
connectToNewUser(userId, stream)
console.log(peers);
})
})
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)
}
room.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Template</title>
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js" defer></script>
<script src="/socket.io/socket.io.js" defer></script>
<script src="script.js" defer></script>
<style>
#video-grid{
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: 300px;
}
video{
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<div id="video-grid">
</div>
</body>
</html>
index.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>index</title>
<script>
var raw = "<%= events %>"
raw = raw.replaceAll(""", "\"") // DONE BECAUSE WHEN PASSING THE PARAM, INSTEAD OF " IT IS THE " ENTITY
var events = JSON.parse(raw)
window.onload = (event) => {
var table = document.querySelectorAll("#events")[0];
table.innerHTML = '';
for (let index = 0; index < events.length; index++) {
table.innerHTML += "<tr><td><a href='/" + events[index].sessionID + "'>" + events[index].name + "</a></td></tr>"
}
};
</script>
</head>
<body>
<h1>Create Event</h1>
<table id="events">
</table>
</body>
</html>
using these codes i can see my video but user's video is not apearing and showing this warning "DevTools failed to load SourceMap: Could not load content for https://unpkg.com/peerjs.min.js.map: HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE"
my codes are given below. if anyone finds a solution please let me know. thank you.
root/server.js:
const express = require('express')
const app = express()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const { v4: uuidV4 } = require('uuid')
app.set('view engine', 'ejs')
app.use(express.static('public'))
app.get('/', (req, res) => {
res.redirect(`/${uuidV4()}`)
})
app.get('/:room', (req, res) => {
res.render('room', { roomId: req.params.room })
})
io.on('connection', socket => {
socket.on('join-room', (roomId, userId) => {
socket.join(roomId)
socket.to(roomId).broadcast.emit('user-connected', userId)
socket.on('disconnect', () => {
socket.to(roomId).broadcast.emit('user-disconnected', userId)
})
})
})
server.listen(3000)
root/public/script.js:
const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
host: '/',
port: '3001'
})
const myVideo = document.createElement('video')
myVideo.muted = true
const peers = {}
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(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)
}
root/views/room.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<!-- <script defer src="https://unpkg.com/peerjs#1.2.0/dist/peerjs.min.js"></script> -->
<script defer src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
<script src="/socket.io/socket.io.js" defer></script>
<script src="script.js" defer></script>
<title>Document</title>
<style>
#video-grid {
display: grid;
grid-template-columns: repeat(auto-fill, 300px);
grid-auto-rows: 300px;
}
video {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<div id="video-grid"></div>
</body>
</html>
Go to Settings/preferences/sources from Inspect element on Chrome and uncheck "Enabled Javascript source maps". Then clean the console and refresh the page.
I'm trying to follow this tutorial:
https://www.tutorialspoint.com/electron/electron_audio_and_video_capturing.htm
The first part of the tutorial worked fine... I can stream av from my pc camera and mic... into my electron app. But now I'm trying to do is stream audio and video from a specific application running on my windows desktop via the desktopCapturer object.
Problem
I'm not getting any errors. But the electron app's video html tag is not showing the stream from myappthatstreamsAV.
Code
I changed my index.html code to look like this: (just changed stuff inside the tag)
<!DOCTYPE html>
<html>
<head>
<meta charset = "UTF-8">
<title>Audio and Video</title>
</head>
<body>
<video autoplay></video>
<script type = "text/javascript">
var desktopCapturer = require('electron').desktopCapturer;
desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
if (error) throw error
desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
if (error) throw error
for (let i = 0; i < sources.length; ++i) {
if (sources[i].name === 'myappthatstreamsAV') {
navigator.webkitGetUserMedia({
audio: true,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sources[i].id,
minWidth: 1280,
maxWidth: 1280,
minHeight: 720,
maxHeight: 720
}
}
}, handleStream, handleError)
return
}
}
})
function handleStream (stream) {
document.querySelector('video').src = URL.createObjectURL(stream)
}
function handleError (e) {
console.log(e)
}
</script>
</body>
</html>
and the index.js looks like this:
const {app, BrowserWindow} = require('electron')
const url = require('url')
const path = require('path')
let win
// Set the path where recordings will be saved
app.setPath("userData", __dirname + "/saved_recordings")
function createWindow() {
win = new BrowserWindow({width: 800, height: 600,
webPreferences: {
nodeIntegration: true
}
})
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
}
app.on('ready', createWindow)
What I've tried so far:
I added some debug statements like this:
<script type = "text/javascript">
var desktopCapturer = require('electron').desktopCapturer;
console.log("1")
console.log(desktopCapturer)
desktopCapturer.getSources({types: ['window', 'screen']}, (error, sources) => {
console.log("2")
if (error) throw error
for (let i = 0; i < sources.length; ++i) {
console.log((sources[i].name));
console.log("3")
and basically, it executes only the first two console.logs:
console.log("1")
console.log(desktopCapturer)
It never gets to 2 or 3.
Changed my code to look like this:
var desktopCapturer = require('electron').desktopCapturer;
console.log("are you here?")
console.log(desktopCapturer)
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
for (const source of sources) {
if (source.name === 'mystreamApp') {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: source.id,
minWidth: 1280,
maxWidth: 1280,
minHeight: 720,
maxHeight: 720
}
}
})
handleStream(stream)
} catch (e) {
handleError(e)
}
return
}
}
})
function handleStream (stream) {
const video = document.querySelector('video')
video.srcObject = stream
video.onloadedmetadata = (e) => video.play()
}
function handleError (e) {
console.log(e)
}
and now I see the video stream.
Audio is still not working. But i'll open another question for that.
I have tried to create zoom clone using nodejs i have successfully displayed my video in the web page and created id for each person joining the link but while displaying the joined person in the main page is not working.
I'm use peerjs library to call the user and display him in the web page.
server.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const {v4:uuidv4} = require('uuid');
const { ExpressPeerServer} = require('peer');
const peerServer = ExpressPeerServer(server, {
debug:true
});
const { response } = require('express');
app.use('/peerjs',peerServer);
app.set('view engine','ejs');
app.use(express.static('public'))
app.get('/', (req, res) => {
res.redirect(`/${uuidv4()}`);
})
app.get('/:room',(req, res)=>{
res.render('room',{ roomId:req.params.room })
})
io.on('connection', socket => {
socket.on('join-room' , (roomId,userId) => {
socket.join(roomId);
socket.to(roomId).broadcast.emit('user-connected',userId);
})
})
server.listen(3035);
room.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zoom CLone</title>
<script src="https://unpkg.com/peerjs#1.3.1/dist/peerjs.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
const ROOM_ID = "<%= roomId %>"
</script>
<style>
#video-grid{
display: flex;
justify-content: center;
}
video{
height: 300px;
width: 400px;
object-fit: cover;
}
</style>
</head>
<body>
<div id="video-grid"></div>
<script src="script.js"></script>
</body>
</html>
script.js
const socket = io('/')
const videoGrid = document.getElementById('video-grid');
const myVideo=document.createElement('video');
myVideo.muted = true;
var peer = new Peer(undefined, {
host:'/',
port:'3031'
})
// const peer = new Peer(undefined, {
// path: '/peerjs',
// host: '/',
// port: '3001'
// })
peer.on('open',id => {
socket.emit('join-room', ROOM_ID, id);
})
let myVideoStream
navigator.mediaDevices.getUserMedia({
video:true,
audio:true
}).then(stream => {
myVideoStream=stream;
addVideostream(myVideo,stream)
peer.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);
})
})
const connectToNewUser = (userId, stream) => {
const call = peer.call(userId, stream)
const video = document.createElement('video')
call.on('stream', userVideoStream => {
addVideostream(video, userVideoStream)
})
call.on('close', () => {
video.remove()
})
}
const addVideostream = (video,stream) =>{
video.srcObject = stream;
video.addEventListener('loadedmetadata',() => {
video.play();
})
videoGrid.append(video);
}
there is also no error in the code, I also checked in the console.
Only problem is the person connected with the link video is not displayed on the main(Host) page.
I was working on the same project and I figured that the reason why when the user joins the channel, his/her video wont be showing on the host screen is because of audio: true present in the getUserMedia in script.js.
navigator.mediaDevices.getUserMedia({
video:true,
audio:true
})
So if you change it to false, that problem will be solved and the second person's video also will be seen there but then another problem will arise, which is that you wont be able to communicate via audio. I haven't figured out the solution to this, but if anyone else figure it out, then do tell me.
Your code looks fine it has no error but in your script.js you need to include
path : "/peerjs"
and port number should be same on which server is listening therefor it should be 3035 instead 3031, your code should look like this:
var peer = new Peer(undefined, {
path: "/peerjs",
host: "/",
port: "3035",
});
Also try removing this lines on your connectToNewUser function
call.on('close', () => {
video.remove()
})
And sometimes it happens due to poor internet connection so you need to keep refreshing your browser to see the changes on live video.
hope this help. For more info on peerjs library check its official website.
I have created similar project where I have same code with extra features you can refer to my git repository to compare your code.
change
peer port:"3031"
to
port:"443"
or something else
then when you deploy it to online server it will work fine
I may be late but i confronted the same problem
try changing
socket.on('user-connected',(userId) => {
connectToNewUser(userId, stream);
})
})
with
socket.on('user-connected', userId => {
// user is joining`
setTimeout(() => {
// user joined
connectToNewUser(userId, stream)
}, 1000)
})
})
that worked for me