skip to Main Content

There are my idea steps that I try to implement:

  • callee start a live and wait to a new caller join
  • caller join and then send offer and establish peer-to-peer following WebRTC workflow normally
    Actual: I try to log and not see any ice candidate triggered

server.js

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const path = require('path');
const cors = require('cors');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
    cors: {
        origin: '*', // You can specify specific origins instead of '*' for better security
        methods: ['GET', 'POST']
    }
});

app.use(cors());
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.get('/lives/:id', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'callee.html'));
});

app.get('/view/:id', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'caller.html'));
});

io.on('connection', (socket) => {
    console.log('New client connected:', socket.id);

    socket.on('start-lives', ({ role, roomId }) => {
        socket.data = { role, roomId };
        socket.join(roomId);
        console.log("Owner connected");
    });

    socket.on('join', (data) => {
        socket.join(data.roomId);
        socket.to(data.roomId).emit('join', socket.id);
        console.log(`Client ${socket.id} joined room ${data.roomId}`);
    });

    socket.on('offer', (data) => {
        socket.to(data.roomId).emit('offer', data.sdp);
    });

    socket.on('answer', (data) => {
        console.log("transfer:answer");
        socket.to(data.roomId).emit('answer', data.sdp);
    });

    socket.on('candidate', (data) => {
        socket.to(data.roomId).emit('candidate', data.candidate);
    });

    socket.on('disconnect', () => {
        console.log('Client disconnected:', socket.data);
    });
});

server.listen(8080, () => {
    console.log('Server listening on port 8080');
});

callee.js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Livestream</title>
    <style>
        #lives-container {
            width: 50vw;
        }

        #lives-video {
            background: url('https://marketplace.canva.com/EAEyZPDsh-4/1/0/1600w/canva-dark-purple-futuristic-stream-starting-soon-twitch-background-9h5ocxbndVU.jpg') no-repeat center;
            width: 100%;
        }

        #btn-start-lives {
            display: block;
            margin-left: auto;
        }
    </style>
</head>

<body>
    <h1>Livestream</h1>
    <section id="lives-container">
        <video id="lives-video" autoplay playsinline></video>
        <button id="btn-start-lives">Start</button>
        <button id="btn-end-lives">End</button>
    </section>

    <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
    <script>
        const socket = io('http://localhost:8080');
        const livesVideo = document.getElementById('lives-video');
        const btnStartLives = document.getElementById('btn-start-lives');
        const btnEndLives = document.getElementById('btn-end-lives');
        const roomId = window.location.pathname.split('/').pop();
        let stream;
        let pc = new RTCPeerConnection();

        pc.onicecandidate = event => {
            console.log("send:candidate");
            if (event.candidate) {
                socket.emit('candidate', { roomId, candidate: event.candidate });
            }
        }

        socket.on('offer', async (sdp) => {
            console.log("received:offer");
            stream.getTracks().forEach(track => pc.addTrack(track, stream));
            await pc.setRemoteDescription(new RTCSessionDescription({ type: 'offer', sdp }));
            const answer = await pc.createAnswer();
            await pc.setLocalDescription(answer);
            console.log("send:answer");
            socket.emit('answer', { roomId, sdp: answer.sdp });
        });

        socket.on('candidate', async (candidate) => {
            console.log("received:candidate");
            try {
                await pc.addIceCandidate(new RTCIceCandidate(candidate));
            } catch (e) {
                console.error('Error adding received ice candidate', e);
            }
        });

        btnStartLives.addEventListener("click", async function () {
            stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
            livesVideo.srcObject = stream;
            livesVideo.onloadedmetadata = () => {
                livesVideo.play();
            };
            socket.emit('start-lives', { role: 'owner', roomId });
        });

        btnEndLives.addEventListener("click", async function () {
            stream.getTracks().forEach(track => track.stop());
            pc.close();
        });
    </script>
</body>

</html>

caller.js

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Livestream viewer</title>
</head>

<body>
    <section>
        <video id="remote-video"></video>
        <button id="btn-approval">See lives</button>
    </section>

    <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
    <script>
        const socket = io('http://localhost:8080');
        const remoteVideo = document.getElementById('remote-video');
        const btnApproval = document.getElementById('btn-approval');
        const roomId = window.location.pathname.split('/').pop();
        let pc = new RTCPeerConnection();

        remoteVideo.onloadedmetadata = () => {
            remoteVideo.play();
        }

        pc.onicecandidate = event => {
            console.log("send:candidate");
            if (event.candidate) {
                socket.emit('candidate', { roomId, candidate: event.candidate });
            }
        }

        pc.ontrack = event => {
            console.log("trigger:ontrack");
            remoteVideo.srcObject = event.streams[0];
        }
        
        btnApproval.addEventListener("click", async function () {
            // let stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
            // stream.getTracks().forEach(track => pc.addTrack(track, stream));
            socket.emit('join', { roomId });
            const offer = await pc.createOffer();
            await pc.setLocalDescription(offer);
            console.log("send:offer");
            socket.emit('offer', { roomId, sdp: offer.sdp });
            this.remove();
        });

        socket.on('answer', async (sdp) => {
            console.log("received:answer");
            await pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp }));
        });

        socket.on('candidate', async (candidate) => {
            console.log("received:candidate");
            try {
                await pc.addIceCandidate(new RTCIceCandidate(candidate));
            } catch (e) {
                console.error('Error adding received ice candidate', e);
            }
        });
    </script>
</body>

</html>

I try to log and not see any ice candidate triggered, my expected is the ice candidate gathering should be triggered because the setLocalDescription() is called

2

Answers


  1. The API you are using may be outdated.

    I followed the web page below to build my app.

    https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation

    Login or Signup to reply.
  2. I don’t see any stun server list in order to pick up a list of ice candidate

    kind of code like the below
    
    const iceServers = [
    { urls: "stun:stun.l.google.com:19302" },
    { urls: "stun:stun.l.google.com:5349" },
    { urls: "stun:stun1.l.google.com:3478" },
    { urls: "stun:stun1.l.google.com:5349" },
    { urls: "stun:stun2.l.google.com:19302" },
    { urls: "stun:stun2.l.google.com:5349" },
    { urls: "stun:stun3.l.google.com:3478" },
    { urls: "stun:stun3.l.google.com:5349" },
    { urls: "stun:stun4.l.google.com:19302" },
    { urls: "stun:stun4.l.google.com:5349" }
    

    ];

    my free demo about webrtc

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search