I'm now making a broadcasting web using socket.io and webRTC. I can successly broadcast the video stream from the broadcaster to the viewers. However, I want to limit the bandwidth of the viewers/broadcasters so that if more viewers/broadcaster join the server, the quality of the video can be reduced to allow more users. It seems that I cannot change the videoWidth and videoHeight as they are read-only so I cannot directly change the resolution of the video. So I wonder if I can setBitrate to limit the performance of the video.
server.js
const express = require("express");
const app = express();
let broadcaster;
const port = 4000;
const http = require("http");
const server = http.createServer(app);
const io = require("socket.io")(server);
app.use(express.static(__dirname + "/public"));
io.sockets.on("error", e => console.log(e));
io.sockets.on("connection", socket => {
socket.on("broadcaster", () => {
broadcaster = socket.id;
socket.broadcast.emit("broadcaster");
});
socket.on("watcher", () => {
socket.to(broadcaster).emit("watcher", socket.id);
});
socket.on("offer", (id, message) => {
socket.to(id).emit("offer", socket.id, message);
});
socket.on("answer", (id, message) => {
socket.to(id).emit("answer", socket.id, message);
});
socket.on("candidate", (id, message) => {
socket.to(id).emit("candidate", socket.id, message);
});
socket.on("disconnect", () => {
if (socket.id === broadcaster) {
socket.broadcast.emit("disconnectServer");
}
socket.broadcast.emit("disconnectPeer", socket.id);
});
});
server.listen(port, () => console.log(`Server is running on port ${port}`));
broadcast.js
const peerConnections = {};
const config = {
iceServers: [
{
"urls": "stun:stun.l.google.com:19302"
}
],
sdpSemantics: "plan-b"
};
function setBitrate(sdp, bitrate) {
return sdp.replace(/a=mid:(audio|video)\r\n/g, `a=mid:$1\r\nb=AS:${bitrate}\r\n`);
}
const socket = io.connect(window.location.origin);
const video = document.querySelector("video");
// Media contrains
function getStream() {
if (window.stream) {
window.stream.getTracks().forEach(track =>{
track.stop();
});
}
const constraints = {
video: true,
audio: true,
width: {exact:320},
height: {exact:240},
aspectRatio: 16/9,
frameRate: {exact: 30},
videoBitrate: 100000
};
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
const videoTrack = stream.getVideoTracks()[0];
const capabilities = videoTrack.getCapabilities();
console.log(capabilities.width.max); // maximum supported width
console.log(capabilities.height.max); // maximum supported height
})
.catch(error => {
// handle the error
});
video.addEventListener("loadedmetadata", function(){
console.log("VideoWidth: " + video.videoWidth + " VideoHeight: " + video.videoHeight);
})
return navigator.mediaDevices
.getUserMedia(constraints)
.then(gotStream)
.catch(handleError);
}
function gotStream(stream){
window.stream = stream;
video.srcObject = stream;
socket.emit("broadcaster");
}
function handleError(error) {
console.error("Error: ", error);
}
function setdefaultscreen(){
video.srcObject = null;
video.style.backgroundColor = "black";
}
socket.on("watcher", id => {
console.log("viewerConnect!!");
const peerConnection = new RTCPeerConnection(config);
peerConnections[id] = peerConnection;
let stream = video.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 => {
sdp.sdp = setBitrate(sdp.sdp, 5);
return peerConnection.setLocalDescription(sdp)})
.then(() => {
socket.emit("offer", id, peerConnection.localDescription);
});
});
socket.on("answer", (id, description) => {
peerConnections[id].setRemoteDescription(description);
});
socket.on("candidate", (id, candidate) => {
peerConnections[id].addIceCandidate(new RTCIceCandidate(candidate));
});
socket.on("disconnect" ,() =>{
socket.emit("disconnect");
console.log("disconnect from server");
if (peerConnection) {
peerConnection.close();
}
setdefaultscreen();
})
socket.on("disconnectPeer", id => {
console.log("disconnect peer from client");
peerConnections[id].close();
delete peerConnections[id];
});
window.onunload = window.onbeforeunload = () => {
socket.close();
};
getStream();
viewer.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"
// }
],
sdpSemantics: "plan-b",
};
function setBitrate(sdp, bitrate) {
return sdp.replace(/a=mid:(audio|video)\r\n/g, `a=mid:$1\r\nb=AS:${bitrate}\r\n`);
}
const socket = io.connect(window.location.origin);
const video = document.querySelector("video");
socket.on("offer", (id, description) => {
peerConnection = new RTCPeerConnection(config);
peerConnection
.setRemoteDescription(description)
.then(() => peerConnection.createAnswer())
.then(sdp => {
sdp.sdp = setBitrate(sdp.sdp, 5);
return 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");
});
function setdefaultscreen(){
video.srcObject = null;
video.style.backgroundColor = "black";
}
socket.on("disconnectServer", () => {
console.log("disconnect from server");
setdefaultscreen();
});
window.onunload = window.onbeforeunload = () => {
socket.close();
peerConnection.close();
setdefaultscreen();
};
now I setBitrate to 5 and the video should be hardly playable. But I found that no matter what value I set the video is still playable and its quality doesn't change. I use Google Chrome to open the web. Is the problem related to the browser?