使用 React 和 Express 以及 Socket.io 和点对点连接创建视频通话应用程序

在本文中,我们将使用 Socket.io 和 Peer-to-Peer 库在 React 和 Express 之间为视频通话应用程序建立连接。

设置后端

1. 安装必要的软件包

npm init -y 
npm install express socket.io cors

2. 导入库

const express = require(“express”);
const http = require(“http”);
const { Server } = require(“socket.io”);
const cors = require(“cors”);

3. 快速设置

const app = express (); 
app.use (cors());

4. HTTP 服务器连接

const server = http.createServer(app);

5. Socket.io 和 CORS 设置

const io = new  Server (server, { 
  cors : { 
    origin : 'http://localhost:3000' , 
    methods : [ 'GET' , 'POST' ], 
  }, 
});

根据您的前端调整原点。

6. 端口配置

const PORT = 5000;

7. 用户连接

io.on('connection', (socket) => {
  console.log('A user connected');
});

用于监听新套接字连接的事件,每次有新用户进入时在控制台上打印消息。

8. 加入视频通话房间

socket.on('join room', (roomID) => {
  if (rooms.has(roomID)) {
    rooms.get(roomID).push(socket.id);
  } else {
    rooms.set(roomID, [socket.id]);
  }

  const otherUsers = rooms.get(roomID).filter(id => id !== socket.id);
  socket.emit('all users', otherUsers);
});

此代码监听“加入房间”事件。如果指定房间存在,则将套接字 ID 添加到该房间,否则创建一个新房间。此外,将该房间 ID 中存在的所有用户发送到客户端。

9. 发送信号以进行 WebRTC 连接

socket.on('sending signal', (payload) => {
  io.to(payload.userToSignal).emit('user joined', { signal: payload.signal, callerID: payload.callerID });
});

此代码监听来自客户端的“发送信号”事件,并向 userToSignal 标识的指定用户发出“用户加入”事件。

userToSignal — 已经在房间中的用户的 ID。

callerID — 进入房间的用户的ID。

10. 返回 WebRTC 连接的信号

socket.on('returning signal', (payload) => {
  io.to(payload.callerID).emit('receiving returned signal', { signal: payload.signal, id: socket.id });
});

此代码监听来自客户端的“returning signal”事件,并向callerID标识的指定用户发出“receiving returned signal”事件。

11. 用户断线

socket.on('disconnect', () => {
  console.log('A user disconnected');
  rooms.forEach((value, key) => {
    if (value.includes(socket.id)) {
      rooms.set(key, value.filter(id => id !== socket.id));
      if (rooms.get(key).length === 0) {
        rooms.delete(key);
      }
    }
  });
  socket.broadcast.emit('user left', socket.id);
});

此代码监听“disconnect”事件,并在每次用户断开连接时在控制台上打印消息。它从指定的房间中删除套接字 ID,如果房间为空,则删除该房间。此外,通过发出“user left”事件通知房间中的所有用户。

12. 服务器初始化

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

设置前端

1. 安装库

npm install react react-dom socket.io-client simple-peer react-icons react-router-dom

2. 导入库

import React, { useState, useEffect, useRef } from 'react';
import { FaCamera, FaUserPlus, FaMicrophone, FaMicrophoneSlash } from 'react-icons/fa';
import io from 'socket.io-client';
import Peer from 'simple-peer';
import { useNavigate } from 'react-router-dom';

3. 视频通话的状态和参考

const [roomId,setRoomId] = useState(''); 
const [peers,setPeers] = useState([]); 
const [isMicOn,setIsMicOn] = useState(true); 
const [isCameraOn,setIsCameraOn] = useState(true); 
const socketRef = useRef(); 
const userVideoRef = useRef(); 
const peersRef = useRef([]); 
const streamRef = useRef(); 
const navigation = useNavigate();

4. 套接字连接和流处理

useEffect(() => {
    socketRef.current = io.connect('http://localhost:5000');

    navigator.mediaDevices.getUserMedia({ video: true, audio: true })
        .then(stream => {
            streamRef.current = stream;
            if (userVideoRef.current) {
                userVideoRef.current.srcObject = stream;
            }

            socketRef.current.emit('join room', roomId);

            socketRef.current.on('all users', users => {
                const peers = [];
                users.forEach(userId => {
                    const peer = createPeer(userId, socketRef.current.id, stream);
                    peersRef.current.push({
                        peerID: userId,
                        peer,
                    });
                    peers.push(peer);
                });
                setPeers(peers);
            });

            socketRef.current.on('user joined', payload => {
                const peer = addPeer(payload.signal, payload.callerID, stream);
                peersRef.current.push({
                    peerID: payload.callerID,
                    peer,
                });
                setPeers(users => [...users, peer]);
            });

            socketRef.current.on('receiving returned signal', payload => {
                const item = peersRef.current.find(p => p.peerID === payload.id);
                item.peer.signal(payload.signal);
            });
        })
        .catch(err => {
            console.error("Error accessing media devices:", err);
        });

    return () => {
        if (socketRef.current) {
            socketRef.current.disconnect();
        }
        if (streamRef.current) {
            streamRef.current.getTracks().forEach(track => track.stop());
        }
    };
}, [roomId]);

根据您的后端调整 URL。

每当 roomId 发生变化时,此 useEffect 钩子就会运行。它连接到 WebSocket 服务器并设置事件侦听器,用于管理参与者并访问用户的设备以获取视频和音频流。它会监听有关用户加入或离开的所有通知。

5. 切换麦克风

const toggleMic = () => {
    const audioTracks = streamRef.current.getAudioTracks();
    audioTracks.forEach(track => {
        track.enabled = !track.enabled;
    });
    setIsMicOn(prev => !prev);
};

6. 切换摄像头

const toggleCamera = () => {
    const videoTracks = streamRef.current.getVideoTracks();
    videoTracks.forEach(track => {
        track.enabled = !track.enabled;
    });
    setIsCameraOn(prev => !prev);
};

7. 创建对等体

const createPeer = (userToSignal, callerID, stream) => {
    const peer = new Peer({
        initiator: true,
        trickle: false,
        stream,
    });

    peer.on('signal', signal => {
        socketRef.current.emit('sending signal', { userToSignal, callerID, signal });
    });

    return peer;
};

通过设置事件监听器来通知服务器发出“发送信号”,为用户建立新的 WebRTC 对等连接,从而发起呼叫。

8. 添加 Peer

const addPeer = (incomingSignal, callerID, stream) => {
    const peer = new Peer({
        initiator: false,
        trickle: false,
        stream,
    });

    peer.on('signal', signal => {
        socketRef.current.emit('returning signal', { signal, callerID });
    });

    peer.signal(incomingSignal);

    return peer;
};

通过设置事件监听器来通知服务器发出“returning signal”,为接听电话的用户建立新的 WebRTC 对等连接。

9. 处理房间创建

const  handleRoomCreate = ( ) => { 
    const newRoomId = Math . random (). toString ( 36 ). substring ( 7 ); 
    setRoomId (newRoomId); 
};

为用户和其他参与者生成唯一的代码以加入。

10. 处理房间加入

const  handleRoomJoin = ( e ) => { 
    e.preventDefault ( ); 
};

11. 显示用户和参与者的视频

return (
    <div>
        <div>
            <video playsInline muted ref={userVideoRef} autoPlay/>
            <div>You</div>
        </div>
        {peers.length > 0 ? (
            peers.map((peer, index) => (
                <Video key={index} peer={peer} />
            ))
        ) : (
            <div>
                <p>Waiting for a participant...</p>
            </div>
        )}
    </div>
);

此代码为用户和房间中的其他参与者呈现视频元素。

12. 参与者的视频组件

const Video = ({ peer }) => {
    const ref = useRef();

    useEffect(() => {
        peer.on('stream', stream => {
            if (ref.current) {
                ref.current.srcObject = stream;
            }
        });
    }, [peer]);

    return (
        <div>
            <video playsInline autoPlay ref={ref} />
            <div>Participant</div>
        </div>
    );
};

13. 房间操作按钮

<div>
    <button onClick={toggleMic}>
        {isMicOn ? <FaMicrophone/> : <FaMicrophoneSlash/>}
        {isMicOn ? "Mute" : "Unmute"}
    </button>
    <button onClick={toggleCamera}>
        {isCameraOn ? <FaCamera className/> : <FaCamera/>}
        {isCameraOn ? "Turn Off Camera" : "Turn On Camera"}
    </button>
</div>

通过以上步骤,您将创建视频通话应用程序。此外,还可以集成白板和聊天选项,使其成为更强大的应用程序。

了解更多信息,可以参考此链接:https://github.com/JainSneha6/SyncSpace。作者:Sneha Jain

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/yinshipin/52834.html

(0)

相关推荐

发表回复

登录后才能评论