2

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?

J H
  • 21
  • 1
  • Do you need audio? Because you could stream video via canvas element – IT goldman Jul 06 '23 at 09:22
  • You would have to convert the video in order to change the bitrate – kevinSpaceyIsKeyserSöze Jul 06 '23 at 09:47
  • @kevinSpaceyIsKeyserSöze Sorry, I don't really get what you mean. What does "convert" the video refer to? Does it mean converting to some format like hls? – J H Jul 07 '23 at 01:26
  • You can't just change the bitrate of a video without any form of processing. You need to convert the video. It's like having the same video file twice one which is 50 Mb and one which is 10 Mb. But you can do the conversion on the fly. Check out ffmpeg – kevinSpaceyIsKeyserSöze Jul 07 '23 at 06:45

0 Answers0