在 React Native 中使用 Peerjs 进行多个视频通话(n 个用户)

Posted

技术标签:

【中文标题】在 React Native 中使用 Peerjs 进行多个视频通话(n 个用户)【英文标题】:Multiple video call (n users) using Peerjs in React Native 【发布时间】:2021-12-24 19:13:38 【问题描述】:

我有一个应用程序,我试图让视频聊天在 React Native 中工作。

使用过 react-native-webrtc 和 react-native-peerjs 等软件包。

使用 Node Js 创建对等 js 服务器。

一对一的视频通话与 react native Peerjs 配合得很好。但是,现在我希望超过 2 个用户连接到 n 个用户。

是否可以将一对一视频通话转换为多个视频通话。请告诉我如何使用 Peer js 和 web rtc 实现多个视频通话。

这是我的一对一视频通话代码:

初始化 webrtc 和 PeerJS:

const initialize = async () => 
const isFrontCamera = true;
const devices = await mediaDevices.enumerateDevices();

const facing = isFrontCamera ? 'front' : 'environment';
const videoSourceId = devices.find(
  (device: any) => device.kind === 'videoinput' && device.facing === facing,
);
const facingMode = isFrontCamera ? 'user' : 'environment';
const constraints: MediaStreamConstraints = 
  audio: true,
  video: 
    mandatory: 
      minWidth: 1280,
      minHeight: 720,
      minFrameRate: 30,
    ,
    facingMode,
    optional: videoSourceId ? [ sourceId: videoSourceId ] : [],
  ,
;

const newStream = await mediaDevices.getUserMedia(constraints);

setLocalStream(newStream as MediaStream);

console.log("************ Started ************");
// const io = socketio(SERVER_URL);
// io.connect();

console.log(SERVER_URL);
const io = socketio.connect(SERVER_URL, 
  reconnection: true,
  autoConnect: true,
  reconnectionDelay: 500,
  jsonp: false,
  reconnectionAttempts: Infinity,
  // transports: ['websocket']
);

io.on('connect', () => 
  console.log("----------- Socket Connected -----------");
  setSocket(io);
  io.emit('register', username);
);

io.on('users-change', (users: User[]) => 
  console.log("----------- New User - " + JSON.stringify(users) + " -----------");
  setUsers(users);
);

io.on('accepted-call', (user: User) => 
  setRemoteUser(user);
);

io.on('rejected-call', (user: User) => 
  setRemoteUser(null);
  setActiveCall(null);
  Alert.alert('Your call request rejected by ' + user?.username);
  navigate('Users');
);

io.on('not-available', (username: string) => 
  setRemoteUser(null);
  setActiveCall(null);
  Alert.alert(username + ' is not available right now');
  navigate('Users');
);

const peerServer = new Peer(undefined, 
  host: PEER_SERVER_HOST,
  path: PEER_SERVER_PATH,
  secure: false,
  port: PEER_SERVER_PORT,
  config: 
    iceServers: [
      
        urls: [
          'stun:stun1.l.google.com:19302',
          'stun:stun2.l.google.com:19302',
        ],
      ,
    ],
  ,
);

peerServer.on('error', (err: Error) =>
  console.log('Peer server error', err),
);

peerServer.on('open', (peerId: string) => 
  setPeerServer(peerServer);
  setPeerId(peerId);
  io.emit('set-peer-id', peerId);
);

io.on('call', (user: User) => 
  peerServer.on('call', (call: any) => 
    //Alert.alert("PeerServer Call");
    setRemoteUser(user);
    Alert.alert(
      'New Call',
      'You have a new call from ' + user?.username,
      [
        
          text: 'Reject',
          onPress: () => 
            io.emit('reject-call', user?.username);
            setRemoteUser(null);
            setActiveCall(null);
          ,
          style: 'cancel',
        ,
        
          text: 'Accept',
          onPress: () => 
            io.emit('accept-call', user?.username);
            call.answer(newStream);
            setActiveCall(call);
            navigate('Call');
          ,
        ,
      ],
       cancelable: false ,
    );

    call.on('stream', (stream: MediaStream) => 
      setRemoteStream(stream);
    );

    call.on('close', () => 
      closeCall();
    );

    call.on('error', () =>  );
  );
);

;

当一个用户呼叫另一个用户时:

const call = (user: User) => 
if (!peerServer || !socket) 
  Alert.alert('Peer server or socket connection not found');
  return;


if (!user.peerId) 
  Alert.alert('User not connected to peer server');
  return;


socket.emit('call', user.username);

setRemoteUser(user);

try 
  const call = peerServer.call(user.peerId, localStream);

  call.on(
    'stream',
    (stream: MediaStream) => 
      setActiveCall(call);
      setRemoteStream(stream);
    ,
    (err: Error) => 
      console.error('Failed to get call stream', err);
    ,
  );
 catch (error) 
  console.log('Calling error', error);

;

现在,我应该如何从下面的代码中调用多个用户以及如何处理多个流。

const call = peerServer.call(user.peerId, localStream);

【问题讨论】:

【参考方案1】:

是否可以将一对一视频通话转换为多个视频通话

在点对点架构中,无法将一对一视频通话“转换”为“多个”。在具有 n 参与者的 p2p 架构中,每个参与者将与其余的 n-1 其他参与者具有单独的一对一连接。

我可能误解了你的问题,但如果你问是否可以为每个参与者建立n-1 连接,那么答案是肯定的。以下是我的实现方式:

    每当有新参与者加入会话时,提取他们的同伴信息。这是 peer.js 库提供的peerId。 接下来,让其他参与者知道这个新用户的存在。为此,您将与房间中的其他参与者共享此新参与者的姓名 peerID 和任何其他元数据。这可以通过您使用 socket.io 实现的信号逻辑来完成。

现在,您有两个选择:

    新参与者可以发起与房间内其他人的一对一对等连接,或者, 其余参与者可以发起与新参与者的一对一连接。

我个人更喜欢第一个。所以继续这个过程:

    通过 socket.io 使用相同的信号逻辑,其余参与者将通过提供他们自己的对等信息和其他元数据来让新用户知道他们的存在。 一旦新参与者获得每个人的对等信息,使用call.on('stream', callback) 发起新的对等连接并开始广播他们的视频。 在接收端,当接收到呼叫和流时,您将在 react-native 中创建一个新的视频元素,并将接收到的媒体流绑定到该元素。这意味着,每个参与者都将拥有n-1 视频元素,用于流式传输n-1 其他参与者的媒体。接收者也开始向呼叫发起者广播他们自己的视频。

Here's a tutorial 展示了如何使用 vanilla javascript 以及github repository with source code 来完成此操作。


现在,回答下一个问题:

请告诉我如何使用 Peer js 和 webrtc 实现多个视频通话。

这取决于参与者的数量、他们所处的地理位置、浏览器/设备限制、设备计算能力和网络带宽。因此,涉及多个因素,因此很难给出任何具体数字。

浏览器可以为可能的最大连接数设置自己的上限,androidios 可能还有其他值。在 chrome 上,最大理论限制是 500。如果您正在为 Android 开发,您可能需要查看here。但我无法找到有关这方面的太多信息。

大多数涉及 WebRTC 的实际应用程序不依赖于网格架构。常见的实现涉及使用 SFU,它接收多个媒体流并转发它们。稍微复杂一点的技术是 MCU 架构,它将来自多个参与者的所有这些媒体流合并为一个,并将该单个流发送给其他参与者。

我在这里详细讨论一下:

https://egen.solutions/articles/how-to-build-your-own-clubhouse-part-2/#architectures-scaling-and-costs

这是一个很好的article,它解释了 SFU 和 MCU 之间的区别。

【讨论】:

SFU合并流,它们转发。这就是他们的区别。您可能想更新您的文章。 @PhilippHancke 啊,谢谢指出错误!我会更新文章

以上是关于在 React Native 中使用 Peerjs 进行多个视频通话(n 个用户)的主要内容,如果未能解决你的问题,请参考以下文章

如何从外部主机连接到 peerjs 服务器

使用 React-Native-Router-Flux 在 React Native 中嵌套场景

在带有 wix/react-native-navigation 的模态中使用 react-native-gesture-handler (RNGH)

尝试在 create-react-native-app 项目 (expo) 中使用 react-native-fs

React-native:如何在 React-native 中使用(和翻译)带有 jsx 的 typescript .tsx 文件?

如何在 React Native 中使用 React Native Video 显示多个视频? [关闭]