让我们构建一个视频会议应用程序
大家好👋,
在本文中,我们将了解如何构建视频会议应用程序。
先决条件:WebRTC
 基础知识
为了实现这一点,我们将使用以下库:
设置服务器:
touch app.js
yarn add express socket.io
const express = require('express');
const http = require('http');
const app = express();
const server = http.createServer(app);
const io = require('socket.io')(server);
const users = {};
const socketRoomMap = {};
io.on('connection', (socket) => {
  socket.on('join-room', (roomId, userDetails) => {
    // adding all user to a room so that we can broadcast messages
    socket.join(roomId);
    // adding map users to room
    if (users[roomId]) {
      users[roomId].push({ socketId: socket.id, ...userDetails });
    } else {
      users[roomId] = [{ socketId: socket.id, ...userDetails }];
    }
    // adding map of socketid to room
    socketRoomMap[socket.id] = roomId;
    const usersInThisRoom = users[roomId].filter(
      (user) => user.socketId !== socket.id
    );
    /* once a new user has joined sending the details of 
    users who are already present in room. */
    socket.emit('users-present-in-room', usersInThisRoom);
  });
  socket.on('initiate-signal', (payload) => {
    const roomId = socketRoomMap[socket.id];
    let room = users[roomId];
    let name = '';
    if (room) {
      const user = room.find((user) => user.socketId === socket.id);
      name = user.name;
    }
    /* once a peer wants to initiate signal, 
    To old user sending the user details along with signal */
    io.to(payload.userToSignal).emit('user-joined', {
      signal: payload.signal,
      callerId: payload.callerId,
      name,
    });
  });
  /* once the peer acknowledge signal sending the 
  acknowledgement back so that it can stream peer to peer. */
  socket.on('ack-signal', (payload) => {
    io.to(payload.callerId).emit('signal-accepted', {
      signal: payload.signal,
      id: socket.id,
    });
  });
  socket.on('disconnect', () => {
    const roomId = socketRoomMap[socket.id];
    let room = users[roomId];
    if (room) {
      room = room.filter((user) => user.socketId !== socket.id);
      users[roomId] = room;
    }
    // on disconnect sending to all users that user has disconnected
    socket.to(roomId).broadcast.emit('user-disconnected', socket.id);
  });
});
server.listen(3001);
这里我们使用socket在多个peer之间传输用户详细信息和webrtc信号。
设置客户端:
npx create-react-app webrtc-video-call-react
cd webrtc-video-call-react
yarn add socket.io-client simple-peer chance
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Home from './Home';
import Room from './Room';
const App = () => (
  <div className='App'>
    <BrowserRouter>
      <Switch>
        <Route path='/' exact component={Home} />
        <Route path='/room/:roomId' component={Room} />
      </Switch>
    </BrowserRouter>
  </div>
);
export default App;
这里有两条路由:/home 和 /room/:roomId。在 /home 中,我们将提供创建房间或加入房间的选项。在 /room/:roomId 中,我们将渲染来自多个用户的视频流。
import React, { useState } from 'react';
import * as Chance from 'chance';
const chance = new Chance();
const Home = ({ history }) => {
  const [roomId, setRoomId] = useState('');
  return (
    <div style={{ marginTop: 10, marginLeft: 10 }}>
      <input
        type='text'
        value={roomId}
        onChange={(e) => setRoomId(e.target.value)}
      ></input>
      <button
        type='button'
        onClick={() => {
          if (!roomId) {
            alert('RoomId is required');
            return;
          }
          history.push(`/room/${roomId}`);
        }}
      >
        Join Room
      </button>
      <button
        type='button'
        onClick={() => {
          const id = chance.guid();
          history.push(`/room/${id}`);
        }}
      >
        Create Room
      </button>
    </div>
  );
};
export default Home;
import React, { useEffect, useRef, useState } from 'react';
import io from 'socket.io-client';
import Peer from 'simple-peer';
import * as Chance from 'chance';
import Video from './Video';
const chance = new Chance();
const Room = (props) => {
  const [userDetails, setUserDetails] = useState({
    id: chance.guid(),
    name: chance.name(),
  });
  const [peers, setPeers] = useState([]);
  const socketRef = useRef();
  const refVideo = useRef();
  const peersRef = useRef([]);
  const roomId = props.match.params.roomId;
  useEffect(() => {
    navigator.mediaDevices
      .getUserMedia({ video: true, audio: true })
      .then((stream) => {
        refVideo.current.srcObject = stream;
        socketRef.current = io.connect('http://localhost:3001');
        // sending the user details and roomid to join in the room
        socketRef.current.emit('join-room', roomId, userDetails);
        socketRef.current.on('users-present-in-room', (users) => {
          const peers = [];
          // To all users who are already in the room initiating a peer connection
          users.forEach((user) => {
            const peer = createPeer(
              user.socketId,
              socketRef.current.id,
              stream
            );
            peersRef.current.push({
              peerId: user.socketId,
              peer,
              name: user.name,
            });
            peers.push({
              peerId: user.socketId,
              peerObj: peer,
            });
          });
          setPeers(peers);
        });
        // once the users initiate signal we will call add peer
        // to acknowledge the signal and send the stream
        socketRef.current.on('user-joined', (payload) => {
          const peer = addPeer(payload.signal, payload.callerId, stream);
          peersRef.current.push({
            peerId: payload.callerId,
            peer,
            name: payload.name,
          });
          setPeers((users) => [
            ...users,
            { peerId: payload.callerId, peerObj: peer },
          ]);
        });
        // once the signal is accepted calling the signal with signal
        // from other user so that stream can flow between peers
        socketRef.current.on('signal-accepted', (payload) => {
          const item = peersRef.current.find((p) => p.peerId === payload.id);
          item.peer.signal(payload.signal);
        });
        // if some user is disconnected removing his references.
        socketRef.current.on('user-disconnected', (payload) => {
          const item = peersRef.current.find((p) => p.peerId === payload);
          if (item) {
            item.peer.destroy();
            peersRef.current = peersRef.current.filter(
              (p) => p.peerId !== payload
            );
          }
          setPeers((users) => users.filter((p) => p.peerId !== payload));
        });
      });
  }, []);
  function createPeer(userToSignal, callerId, stream) {
    const peer = new Peer({
      initiator: true,
      trickle: false,
      stream,
    });
    peer.on('signal', (signal) => {
      socketRef.current.emit('initiate-signal', {
        userToSignal,
        callerId,
        signal,
      });
    });
    return peer;
  }
  function addPeer(incomingSignal, callerId, stream) {
    const peer = new Peer({
      initiator: false,
      trickle: false,
      stream,
    });
    peer.on('signal', (signal) => {
      socketRef.current.emit('ack-signal', { signal, callerId });
    });
    peer.signal(incomingSignal);
    return peer;
  }
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap' }}>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <video muted ref={refVideo} autoPlay playsInline />
        <span>{userDetails.name}</span>
      </div>
      {peers.map((peer, index) => {
        return (
          <Video
            key={peersRef.current[index].peerId}
            peer={peer.peerObj}
            name={peersRef.current[index].name}
          />
        );
      })}
    </div>
  );
};
export default Room;
该房间组件将负责连接对等点,在我们的套接字服务器和简单对等点的帮助下发送和接收信号。
启动服务器、客户端并在多个设备中打开 URL 以查看其运行情况。
请随意分叉并使用源代码。
如果您觉得有趣,请点赞并分享。
文章来源:https://dev.to/kannndev/let-s-build-a-video-conferencing-app-pp 后端开发教程 - Java、Spring Boot 实战 - msg200.com
            后端开发教程 - Java、Spring Boot 实战 - msg200.com
          