Skip to content

Instantly share code, notes, and snippets.

@clemos
Last active November 7, 2021 00:29
Show Gist options
  • Save clemos/9dbd351972dcf6ac4d89a8449511989e to your computer and use it in GitHub Desktop.
Save clemos/9dbd351972dcf6ac4d89a8449511989e to your computer and use it in GitHub Desktop.
quick and dirty nodejs webrtc server
var pc = new RTCPeerConnection();
var localVideo = document.getElementById('local');
var remoteVideo = document.getElementById('remote');
var isBroadcaster = document.location.hash.indexOf('broadcast') > -1;
var watcherId;
function broadcast(stream) {
localVideo.src = URL.createObjectURL(stream);
pc.addStream(stream);
connect();
}
function post(endpoint, payload, cb) {
var x = new XMLHttpRequest();
x.open('POST',endpoint);
x.onreadystatechange = function(e){
console.log('ready',x);
if(x.readyState==4){
console.log('got answer',x.responseText);
cb(x.responseText);
}
};
x.send(payload);
}
function connect(){
var ch = pc.createDataChannel('test');
pc.ondatachannel = function(e){
console.log('got remote datachannel', e.channel);
}
pc.onaddstream = function(e){
console.log('got remote stream', e.stream);
remoteVideo.src = URL.createObjectURL(e.stream);
}
function sendOffer(){
console.log('sending offer',pc.localDescription);
var payload = JSON.stringify(pc.localDescription);
var endpoint = '/broadcast';
post(endpoint,payload,function(data){
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(data)));
});
}
function sendAnswer(){
console.log('sending answer', watcherId,pc.localDescription);
var payload = JSON.stringify({
session:pc.localDescription,
watcherId:watcherId
});
var endpoint = '/answer';
post(endpoint,payload,function(){
console.log('done sending answer');
});
}
function getOffer(){
console.log('retrieving offer');
post('/watch','', function(json){
var data = JSON.parse(json);
watcherId = data.watcherId;
pc.setRemoteDescription(new RTCSessionDescription(data.session))
.then(()=>{
console.log('success setting remote description');
});
pc.onaddstream = (e)=>{
console.log('GOT REMOTE STREAM !!',e);
remoteVideo.src = URL.createObjectURL(e.stream);
}
pc.createAnswer().then((answer)=>{
console.log('got answer', answer);
pc.setLocalDescription(new RTCSessionDescription(answer));
});
});
}
pc.onicecandidate = function(e){
console.log('got ice candidate',e);
if(e.candidate==null){
if( isBroadcaster ) {
sendOffer();
} else {
sendAnswer();
}
}
}
if( isBroadcaster ) {
pc.createOffer().then((offer)=>{
pc.setLocalDescription(offer);
console.log('got offer', offer);
});
} else {
getOffer();
}
}
var br;
if( isBroadcaster ) {
navigator.getUserMedia({video:true, audio:true},(stream)=>{
br = stream;
broadcast(stream);
}, (err)=>{
console.error('error getting user media',err);
});
}else{
connect();
}
var http = require('http');
var fs = require('fs');
var webrtc = require('./index.js');
const PORT = 3000;
function index(req,res){
res.writeHead(200, {
"content-type":"text/html"
});
res.write('<html><head></head><body><video id="local" autoplay></video><video id="remote" autoplay></video><script src="/client.js"></script></body></html>');
res.end();
}
function client(req,res){
fs.createReadStream('client.js').pipe(res);
}
function readRequest(req, cb) {
var chunks = [];
req.on('data',(data)=>{
chunks.push(data);
});
req.on('end',()=>{
cb(Buffer.concat(chunks).toString());
});
}
function createConnection(cb){
var pc = new webrtc.RTCPeerConnection({});
pc.addEventListener('icecandidate', (e)=>{
console.log('got ice candidate',e);
if( e.candidate == null ) {
cb();
}
});
pc.addEventListener('datachannel', (e)=>{
console.log('got remote datachannel',e);
e.channel.addEventListener('message', function(m){
console.log('got message on remote channel',m.data);
});
});
var ch = pc.createDataChannel('test');
ch.addEventListener('message',function(m){
console.log('got message on local datachannel', m);
});
return pc;
}
var broadcast = null;
function doBroadcast(req,res){
console.log('do broadcast');
var pc;
readRequest(req,(data)=>{
var offer = JSON.parse(data);
var remote = new webrtc.RTCSessionDescription(offer);
pc = createConnection(()=>{
res.writeHead(200,{"content-type":"text/json"});
res.write(JSON.stringify(pc.localDescription));
res.end();
});
pc.addEventListener('addstream', (e)=>{
console.log('got broadcast stream');
broadcast = e.stream;
});
pc.setRemoteDescription(remote,function(){
console.log('success setting remote description');
}, function(err){
console.log('error setting remote description');
});
pc.createAnswer().then((answer)=>{
var local = new webrtc.RTCSessionDescription(answer);
pc.setLocalDescription(local).then(()=>{
console.log('success setting local description');
});
});
});
}
var watchers = [];
function doWatch(req,res){
if( broadcast == null ) {
res.writeHead(404,{"content-type":"text/json"});
res.write('{"error":"no broadcast stream"}');
res.end();
return;
}
console.log('DO WATCH');
var pc;
var watcherId = watchers.length;
pc = createConnection(()=>{
res.writeHead(200,{"content-type":"text/json"});
var data = {
session: pc.localDescription,
watcherId: watcherId
};
console.log('sending data to peer', data);
res.write(JSON.stringify(data));
res.end();
});
console.log('adding PC to watchers list', watcherId);
watchers.push(pc);
pc.addStream(broadcast);
pc.createOffer().then((offer)=>{
var local = new webrtc.RTCSessionDescription(offer);
pc.setLocalDescription(local).then(()=>{
console.log('success setting local description');
});
});
}
function doAnswer(req,res){
readRequest(req,(json)=>{
var data = JSON.parse(json);
console.log('got answer ',data);
var watcherId = data.watcherId;
console.log('picking watcher', watcherId, "out of", watchers.length);
var pc = watchers[watcherId];
pc.setRemoteDescription(new webrtc.RTCSessionDescription(data.session)).then(function(){
console.log('success setting session description for watcher #' + watcherId );
res.end();
});
});
}
const server = http.createServer(function(req,res){
console.log('got req ', req.url);
switch(req.url){
case '/':
return index(req,res);
case '/client.js':
return client(req,res);
case '/broadcast':
return doBroadcast(req,res);
case '/watch':
return doWatch(req,res);
case '/answer':
return doAnswer(req,res);
default:
res.writeHead(404,{});
res.end();
}
});
server.listen(PORT);
@rockyhuber
Copy link

Hi Clément, this is great!

What does the var webrtc = require('./index.js'); file refer to?

I know it was a long time ago, though your help would be great.

@clemos
Copy link
Author

clemos commented Nov 6, 2021

Hi

What does the var webrtc = require('./index.js'); file refer to?

It was very probably referring to this webrtc implementation for node.js : aisouard/node-webrtc#5

Unfortunately, this project is pretty much dead. But 4 years later, there probably exists a better webrtc package for node.js somewhere 🤷

Just curious: what are you working on ?

Thanks 🙏

@rockyhuber
Copy link

rockyhuber commented Nov 7, 2021

Hi, thank you for your reply.

I will continue look around for a more updated webrtc nodejs implementation.

A new type of video conferencing platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment