-I want to make a server that can receive data sent from the client. The client can be the person broadcasting the live video or the person who wants to watch the live video. Connect the clients together using licenseID. and the server can have many connections.
-For example: there is a client A who sends a licenseID of 1 and wants to play live video, and client B sends a licenseID of 2 and also wants to play a video….. then there is a client C who wants to watch client A’s live video and will transmit a licenseID of 1 to watch live, and can exit. Transmit licenseID as 2 to watch live video of client B. Server code is independent for deployment.
Currently the client cannot watch live video from the broadcaster.
Code server
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const cors = require("cors");
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
const port = 4000;
// Map to store active broadcasters by licenseID
const broadcasters = new Map();
app.use(cors());
// Socket.io connection handler
io.on("connection", socket => {
console.log(`Socket connected: ${socket.id}`);
// Handle broadcaster registration
socket.on("broadcaster", (licenseID) => {
broadcasters.set(licenseID, socket.id);
console.log(`Broadcaster registered: LicenseID ${licenseID}`);
});
// Handle watcher request to watch broadcast
socket.on("watcher", (licenseID) => {
const broadcasterSocketId = broadcasters.get(licenseID);
if (broadcasterSocketId) {
socket.to(broadcasterSocketId).emit("watcher", socket.id);
console.log(`Watcher connected to broadcaster ${licenseID}`);
} else {
socket.emit("error", "No broadcaster available for this licenseID");
}
});
// Handle WebRTC signaling
socket.on("offer", (to, message) => {
socket.to(to).emit("offer", socket.id, message);
});
socket.on("answer", (to, message) => {
socket.to(to).emit("answer", socket.id, message);
});
socket.on("candidate", (to, message) => {
socket.to(to).emit("candidate", socket.id, message);
});
// Handle disconnection
socket.on("disconnect", () => {
console.log(`Socket disconnected: ${socket.id}`);
// Clean up broadcasters map if needed
broadcasters.forEach((value, key) => {
if (value === socket.id) {
broadcasters.delete(key);
}
});
});
});
// Serve static files (e.g., HTML, JS, CSS)
app.use(express.static(__dirname + "/public"));
// Start the server
server.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
Code broadcaster
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Broadcaster</title>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io.connect('http://localhost:4000');
let peerConnection;
const licenseID = 1;
const config = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
socket.emit('broadcaster', licenseID);
// Start video streaming
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => {
const localVideo = document.getElementById('localVideo');
localVideo.srcObject = stream;
peerConnection = new RTCPeerConnection(config);
stream.getTracks().forEach(track => peerConnection.addTrack(track, stream));
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit('candidate', licenseID, { candidate: event.candidate, to: 'watcher_id' });
}
};
peerConnection.createOffer()
.then(offer => peerConnection.setLocalDescription(offer))
.then(() => {
socket.emit('offer', 'watcher_id', { description: peerConnection.localDescription });
})
.catch(error => {
console.error('Error creating offer:', error);
});
socket.on('answer', (from, message) => {
peerConnection.setRemoteDescription(new RTCSessionDescription(message.description))
.catch(error => {
console.error('Error setting remote description:', error);
});
});
socket.on('candidate', (from, message) => {
peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate))
.catch(error => {
console.error('Error adding ICE candidate:', error);
});
});
})
.catch(error => {
console.error('Error accessing media devices: ', error);
});
</script>
</head>
<body>
<h1>Broadcaster</h1>
<video id="localVideo" autoplay muted></video>
</body>
</html>
Code viewer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Watcher</title>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io.connect('http://localhost:4000');
let peerConnection;
const licenseID = 1;
const config = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
socket.emit('watcher', licenseID);
socket.on('offer', async (from, message) => {
console.log("Received offer from broadcaster:", from, message);
peerConnection = new RTCPeerConnection(config);
peerConnection.ontrack = (event) => {
const remoteVideo = document.getElementById('remoteVideo');
remoteVideo.srcObject = event.streams[0];
};
peerConnection.onicecandidate = (event) => {
if (event.candidate) {
socket.emit('candidate', 'broadcaster_id', { candidate: event.candidate, to: from });
}
};
try {
await peerConnection.setRemoteDescription(new RTCSessionDescription(message.description));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
socket.emit('answer', from, { description: peerConnection.localDescription });
} catch (e) {
console.error("Error establishing connection:", e);
}
});
socket.on('candidate', (from, message) => {
console.log("Received ICE candidate from broadcaster:", from, message);
if (peerConnection) {
peerConnection.addIceCandidate(new RTCIceCandidate(message.candidate));
}
});
</script>
</head>
<body>
<h1>Watcher</h1>
<video id="remoteVideo" autoplay controls></video>
</body>
</html>
3