大家好,我是杨成功。

之前介绍过 WebRTC,简单来说它是一个点对点的实时通讯技能,主要根据浏览器来完结音视频通讯。这项技能现在现已被广泛运用于实时视频通话,多人会议等场景。

不过 WebRTC 由于其过于优异的体现,其运用范围现已不限于 Web 端,移动 App 也基本完结了 WebRTC 的 API。在跨平台框架中,Flutter 和 React Native 都完结了对 WebRTC 的支持。

咱们以 App(React Native)为呼叫端,Web(React)为接纳端,分别介绍两头怎么进行视频通话。

接纳端 React 完结

React 运转在浏览器中,无需引证任何模块,能够直接运用 WebRTC API。下面分几个步骤,逐步介绍在 Web 端怎么获取、发送、接受长途视频流。

1. 获取本地摄像头流,并保存。

const stream = null;
const getMedia = async () => {
  let ori_stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
  });
  stream = ori_stream;
};

2. 创立 video 标签用于播映视频。

创立两个 video 标签,分别用于播映本地视频和长途视频。

const local_video = useRef();
const remote_video = useRef();
const Player = () => {
  return (
    <div>
      <video ref={local_video} autoPlay muted />;
      <video ref={remote_video} autoPlay muted />;
    </div>
  );
};
// stream 是上一步获取的本地视频流
if (local_video.current) {
  local_video.current.srcObject = stream;
}

3. 创立 RTC 衔接实例。

每一个运用 WebRTC 通讯的客户端,都要创立一个 RTCPeerConnection 衔接实例,该实例是真实负责通讯的角色。WebRTC 通讯的实质便是 RTCPeerConnection 实例之间的通讯。

// 1. 创立实例
let peer = new RTCPeerConnection();
// 2. 将本地视频流增加到实例中
stream.getTracks().forEach((track) => {
  peer.addTrack(track, stream);
});
// 3. 接纳长途视频流并播映
peer.ontrack = async (event) => {
  let [remoteStream] = event.streams;
  remote_video.current.srcObject = remoteStream;
};

实例创立之后,别忘记将上一步获取的摄像头流增加到实例中。

当两头衔接完全树立之后,在 peer.ontrack 之内就能接纳到对方的视频流了。

4. 衔接信令服务器,预备与 App 端通讯。

WebRTC 的通讯进程需求两个客户端实时进行数据交流。交流内容分为两大部分:

  • 交流 SDP(媒体信息)。
  • 交流 ICE(网络信息)。

因而咱们需求一个 WebSocket 服务器来衔接两个客户端进行传输数据,该服务器在 WebRTC 中被称为信令服务器

咱们现已根据 socket.io 建立了信令服务器,现在需求客户端衔接,办法如下。

(1)装置 socket.io-client:

$ yarn add socket.io-client

(2)衔接服务器,并监听音讯。

衔接服务器时带上验证信息(下面的用户 ID、用户名),方便在通讯时能够找到对方。

import { io } from 'socket.io-client';
const socket = null;
const socketInit = () => {
  let sock = io(`https://xxxx/webrtc`, {
    auth: {
      userid: '111',
      username: '我是接纳端',
      role: 'reader',
    },
  });
  sock.on('connect', () => {
    console.log('衔接成功');
  });
  socket = sock;
};
useEffect(() => {
  socketInit();
}, []);

通过上面 4 个步骤,咱们的预备工作现已做好了。结下来能够进行正式的通讯步骤了。

5. 接纳 offer,交流 SDP。

监听 offer 事情(呼叫端发来的 offer 数据),然后创立 answer 并发回呼叫端。

// 接纳 offer
socket.on('offer', (data) => {
  transMedia(data);
});
// 发送 answer
const transMedia = async (data: any) => {
  let offer = new RTCSessionDescription(data.offer);
  await peer.setRemoteDescription(offer);
  let answer = await peer.createAnswer();
socket.emit('answer', {
to: data.from, // 呼叫端 Socket ID
answer,
});
await peer.setLocalDescription(answer);
};

socket.emit('answer', { to: data.from, // 呼叫端 Socket ID answer, }); await peer.setLocalDescription(answer); };

6. 接纳 candidate,交流 ICE。

监听 candid 事情(呼叫端发来的 candidate 数据)并增加到本地 peer 实例,然后监听本地的 candidate 数据并发回给呼叫端。

上一步履行 peer.setLocalDescription() 之后,就会触发 peer.onicecandidate 事情。

// 接纳 candidate
socket.on('candid', (data) => {
let candid = new RTCIceCandidate(data.candid);
peer.addIceCandidate(candid);
});
// 发送 candidate
peer.onicecandidate = (event) => {
if (event.candidate) {
socket.emit('candid', {
to: data.from, // 呼叫端 Socket ID
candid: event.candidate,
});
}
};

至此,整个通讯进程就完结了。假如没有意外,此刻在第 3 步的 peer.ontrack 事情内拿到的对端视频流开端传输数据,咱们能够看到对端的视频画面了。

呼叫端 React Native 完结

在 React Native 端并不能直接运用 WebRTC API,咱们需求一个第三方模块 react-native-webrtc 来完结,它提供了和 Web 端几乎共同的 API。

走运的是,React Native 能够复用 Web 端的大多数逻辑性资源,socket.io-client 能够直接装置运用,和 Web 端完全共同。

不幸的是,App 开发少不了原生的环境装备、权限装备,这些比较繁琐,接下来介绍怎么完结吧。

1. 创立 React Native 项目。

创立项目之前需求装备开发环境,咱们以 Android 为例,详细的装备能够看这篇文章。

装备完结之后,直接通过 npx 指令创立项目,命名为 RnWebRTC

 npx react-native init RnWebRTC --template react-native-template-typescript</code></pre><blockquote>提示:假如不想运用 TypeScript,将 --template 选项和后边的内容去掉即可。</blockquote><p>我装置的最新版别如下:</p><ul><li>react: "18.1.0"</li><li>react-native: "0.70.6"</li></ul><p>创立之后,检查根目录的 <code>index.js</code><code>App.tsx</code> 两个文件,分别是进口文件和页面组件,咱们就在 App.tsx 这个组件中编写代码。</p><p>咱们以安卓为例,直接用手机数据线衔接电脑,翻开 USB 调试模式,然后运转以下指令:</p><pre><code class="sh"> yarn run android

履行该指令会装置 Gradle(安卓包办理)依赖,并编译打包 Android 运用。第一次运转耗时比较久,耐性等候打包完结后,手机上会提示装置该 App。

该指令在打包的一起,还会单独发动一个终端,运转 Metro 开发服务器。Metro 的作用是监 JS 代码修正,并打包成 js bundle 交给原生 App 去渲染。

单独发动 Metro 服务器,可运转 yarn run start

2. 装置 react-native-webrtc。

直接运转装置指令:

$ yarn add react-native-webrtc

装置之后并不能直接运用,需求在 android 文件夹中修正两处代码。

第一处:由于 react-native-webrtc 需求最低的安卓 SDK 版别为 24,而默许生成的最低版别是 21,所以修正 android/build.gradle 中的装备:

buildscript {
ext {
minSdkVersion = 24
}
}

第二处:webrtc 需求摄像头、麦克风等权限,所以咱们把权限先配齐喽。在 android/app/src/main/AndroidManifest.xml 中的 <application> 标签前增加如下装备:

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.audio.output" />
<uses-feature android:name="android.hardware.microphone" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.INTERNET" />

接着履行指令从头打包安卓:

 yarn run android</code></pre><p>现在就能够在 App.tsx 中导入和运用 WebRTC 的相关 API 了:</p><pre><code class="js">import {
  ScreenCapturePickerView,
  RTCPeerConnection,
  RTCIceCandidate,
  RTCSessionDescription,
  RTCView,
  MediaStream,
  MediaStreamTrack,
  mediaDevices,
} from 'react-native-webrtc';</code></pre><p><strong>3. 衔接信令服务器,预备与 Web 端通讯。</strong></p><p>由于 React Native 能够直接运用 <code>socket.io-client</code> 模块,所以这一步和 Web 端的代码共同。</p><pre><code class="sh"> yarn add socket.io-client

衔接信令服务器时,仅仅传递的验证信息不同:

import { io } from 'socket.io-client';
const socket = null;
const socketInit = () => {
let sock = io(https://xxxx/webrtc, {
auth: {
userid: '222',
username: '我是呼叫端',
role: 'sender',
},
});
sock.on('connect', () => {
console.log('衔接成功');
});
socket = sock;
};
useEffect(() => {
socketInit();
}, []);

4. 运用 RTCView 组件播映视频。

创立两个 RTCView 组件,分别用于播映本地视频和长途视频。

import { mediaDevices, RTCView } from 'react-native-webrtc';
var local_stream = null;
var remote_stream = null;
// 获取本地摄像头
const getMedia = async () => {
local_stream = await mediaDevices.getUserMedia({
audio: true,
video: true,
});
};
// 播映视频组件
const Player = () => {
return (
<View>
<RTCView style={{ height: 500 }} streamURL={local_stream.toURL()} />
<RTCView style={{ height: 500 }} streamURL={remote_stream.toURL()} />
</View>
);
};

5. 创立 RTC 衔接实例。

这一步与 Web 端基本共同,接纳到远端流直接赋值。

// 1. 创立实例
let peer = new RTCPeerConnection();
// 2. 将本地视频流增加到实例中
local_stream.getTracks().forEach((track) => {
peer.addTrack(track, local_stream);
});
// 3. 接纳长途视频流
peer.ontrack = async (event) => {
let [remoteStream] = event.streams;
remote_stream = remoteStream;
};

6. 创立 offer,开端交流 SDP。

App 端作为呼叫端,需求主动创立 offer 并发给接纳端,并监听接纳端发回的 answer:

// 发送 offer
const peerInit = async (socket_id) => {
let offer = await peer.createOffer();
peer.setLocalDescription(offer);
socket.emit('offer', {
to: socket_id, // 接纳端 Socket ID
offer: offer,
});
};
// 接纳 answer
socket.on('answer', (data) => {
let answer = new RTCSessionDescription(data.answer);
peer.setRemoteDescription(answer);
});

7. 监听 candidate,交流 ICE。

这一步依然与 Web 端共同,分别是发送本地的 candidate 和接纳长途的 candidate:

// 发送 candidate
peer.onicecandidate = (event) => {
if (event.candidate) {
socket.emit('candid', {
to: socket_id, // 接纳端 Socket ID
candid: event.candidate,
});
}
};
// 接纳 candidate
socket.on('candid', (data) => {
let candid = new RTCIceCandidate(data.candid);
peer.addIceCandidate(candid);
});

至此,App 端与 Web 端的视频通话流程现已完结,现在两头能够相互看到对方的视频画面了。

建立 socket.io 信令服务器

在 Web 和 App 两头视频通话时用到了信令服务器,该服务运用 socket.io 完结,代码并不杂乱,下面介绍下信令服务器怎么编写:

1. 创立项目,装置需求的依赖。

创立 socket-server 文件夹,并履行以下指令生成 package.json:

 npm init</code></pre><p>接着装置必要的模块:</p><pre><code> npm install koa socket.io

2. 创立进口文件,发动 socket 服务。

创立 app.js 文件,写入以下代码:

const Koa = require('koa');
const http = require('http');
const SocketIO = require('socket.io');
const SocketIoApi = require('./io.js');
const app = new Koa();
const server = http.createServer(app.callback());
const io = new SocketIO.Server(server, {
cors: { origin: '*' },
allowEIO3: true,
});
app.context.io = io; // 将socket实例存到全局
new SocketIoApi(io);
server.listen(9800, () => {
console.log(listen to http://localhost:9800);
});

server.listen(9800, () => { console.log(listen to http://localhost:9800); });

代码中运用 Koa 框架运转一个服务器,而且接入了 socket.io,一个基本的 WebSocket 服务器就写好了。接着将它运转起来:

$ node app.js

此刻 Koa 运转的 HTTP 服务器和 socket.io 运转的 WebSocket 服务器同享 9800 端口。

3. 创立 io.js,编写信令服务器逻辑。

上一步在进口文件 app.js 中引证了 io.js,该文件导出一个类,咱们来编写详细的逻辑:

// io.js
class IoServer {
constructor(io) {
this.io = io;
this.rtcio = io.of('/webrtc');
this.rtcio.on('connection', (socket) => {
this.rtcListen(socket);
});
}
rtcListen(socket) {
// 发送端|发送 offer
socket.on('offer', (json) => {
let { to, offer } = json;
let target = this.rtcio.sockets.get(to);
if (target) {
target.emit('offer', {
from: socket.id,
offer,
});
} else {
console.error('offer 接纳方未找到');
}
});
// 接纳端|发送 answer
socket.on('answer', (json) => {
let { to, answer } = json;
let target = this.rtcio.sockets.get(to);
// console.log(to, socket)
if (target) {
target.emit('answer', {
from: socket.id,
answer,
});
} else {
console.error('answer 接纳方未找到');
}
});
// 发送端|发送 candidate
socket.on('candid', (json) => {
let { to, candid } = json;
let target = this.rtcio.sockets.get(to);
// console.log(to, socket)
if (target) {
target.emit('candid', {
from: socket.id,
candid,
});
} else {
console.error('candid 接纳方未找到');
}
});
}
}
module.exports = IoServer;

module.exports = IoServer;

上面代码的逻辑中,当客户端衔接到服务器,就开端监听 offeranswercandid 三个事情。当有客户端发送音讯,这儿负责将音讯转发给另一个客户端。

两个客户端通过唯一的 Socket ID 找到对方。在 socket.io 中,每一次衔接都会产生一个唯一的 Socket Id。

4. 获取已衔接的接纳端列表。

由于每次改写浏览器 WebSocket 都要从头衔接,因而每次的 Socket ID 都不相同。为了在呼叫端准确找到在线的接纳端,咱们写一个获取接纳端列表的接口。

在进口文件中通过 app.context.io = io 将 SocketIO 实例全局存储到了 Koa 中,那么获取已衔接的接纳端办法如下:

app.get('/io-clients', async (ctx, next) => {
let { io } = ctx.app.context;
try {
let data = await io.of('/webrtc').fetchSockets();
let resarr = data
.map((row) => ({
id: row.id,
auth: row.handshake.auth,
data: row.data,
}))
.filter((row) => row.auth.role == 'reader');
ctx.body = { code: 200, data: resarr };
} catch (error) {
ctx.body = error.toString();
}
});

然后在呼叫端运用 GET 恳求 https://xxxx/io-clients 可拿到在线接纳端的 Socket ID 和其他信息,然后挑选一个接纳端建议衔接。

留意:在线上获取摄像头和屏幕时要求域名有必要是 https,不然无法获取。因而牢记给 Web 端和信令服务器都装备好 https 的域名,能够避免通讯时产生异常。

TURN 跨网络视频通讯

前面咱们完结了 App 端和 Web 端双端通讯,可是通讯成功有一个条件:两头有必要衔接同一个 WIFI

这是由于 WebRTC 的通讯是根据 IP 地址和端口来找到对方,假如两头衔接不同的 WIFI(不在同一个网段),或是 App 用流量 Web 端用 WIFI,那么两头的 IP 地址谁都不认识谁,自然无法树立通讯。

当两头不在一个局域网时,优先运用 STUN 服务器,将两头的本地 IP 转换为公网 IP 进行衔接。STUN 服务器咱们直接运用谷歌的就能够,可是由于防火墙等各种原因,实际测验 STUN 基本是连不通的。

当两头不能找到对方,无法直接树立衔接时,那么咱们就要运用兜底计划 ——— 用一个中继服务器转发数据,将媒体流数据转发给对方。该中继服务器被称为 TURN 服务器。

TURN 服务器由于要转发数据流,因而对带宽耗费比较大,需求咱们自己建立。现在有许多开源的 TURN 服务器计划,通过性能测验,我挑选运用 Go 语言开发的 pion/turn 框架。

1. 装置 Golang:

运用 pion/turn 的第一步是在你的服务器上装置 Golang。我运用的是 Linux Centos7 体系,运用 yum 指令装置最方便。

yum install -y epel-release # 增加 epel 源 yum install -y golang # 直接装置

装置之后运用如下指令检查版别,测验是否装置成功:

 go version
go version go1.17.7 linux/amd64</code></pre><p><strong>2. 运转 pion/turn:</strong></p><p>直接将 pion/turn 的源码拉下来:</p><pre><code class="sh"> git clone https://github.com/pion/turn ./pion-turn

源码中提供了许多案例能够直接运用,咱们运用 examples/turn-server/simple 这个目录下的代码:

cd ./pion-turn/examples/turn-server/simple go build

运用 go build 编译后,当前目录下会生成一个 simple 文件,该文件是可履行文件,运用该文件发动 TURN 服务器:

 ./simple -public-ip 123.45.67.89 -users ruidoc=123456</code></pre><p>上面指令中的 <code>123.45.67.89</code> 是你服务器的公网 IP,并装备一个用户名和暗码分别为 <code>ruidoc</code><code>123456</code>,这几项装备根据你的实际情况设置。</p><p>默许情况下该指令不会后台运转,咱们用下面的办法让它后台运转:</p><pre><code> nohup ./simple -public-ip 123.45.67.89 -users ruidoc=123456 &

此刻,该 TURN 服务现已在后台运转,并占用一个 3478 的 UDP 端口。咱们检查一下端口占用:

 netstat -nplu</code></pre><p></p><p>从上图能够看出,该服务现已在运转中。</p><p><strong>3. 装备安全组、检测 TURN 是否可用。</strong></p><p>上一步现已发动了 TURN 服务器,可是默许情况下从外部衔接不上(这儿是个坑),由于阿里云需求在安全组的入方向增加一条 UPD 3478 端口(其他云服务商也差不多),表示该端口答应从外部拜访。</p><p></p><p>安全组增加后,咱们就能够测验 TURN 服务器的连通性了。</p><p>翻开 <a href="https://link.segmentfault.com/?enc=X0YEiT5fxqodK9ejnvFGbQ%3D%3D.D%2BBRkGa3aEFOtB9%2BCutaLzkxtJg%2FetEp2BrC%2BBcz%2F2hTWa5cIwrTh85BGQqiIqZV52Y2VYARKuLErjGdnW8FrlzS52LX6rdzyQfMjjeRNUs%3D" rel="nofollow">Trickle ICE</a>,增加咱们的 TURN 服务器,格式为 <code>turn:123.45.67.89:3478</code>,然后输入上一步装备的用户名和暗码:</p><p></p><p>点击 Gather candidates 按钮,会列出以下信息。假如包括 <code>relay</code> 这一条,阐明咱们的 TURN 服务器建立成功,能够运用了。</p><p></p><p><strong>4. 客户端增加 ICE 装备。</strong></p><p>在 App 端和 Web 端创立 RTC 实例时,加入以下装备:</p><pre><code class="js">var turnConf = {
  iceServers: [
    {
      urls: 'stun:stun1.l.google.com:19302', // 免费的 STUN 服务器
    },
    {
      urls: 'turn:123.45.67.89:3478',
      username: 'ruidoc',
      credential: '123456',
    },
  ],
};
var peer = new RTCPeerConnection(turnConf);</code></pre><p>现在关闭手机 WIFI,运用流量与 Web 端建议衔接,不出意外能够正常通讯,可是推迟好像高了一些(究竟走中转必定没有直连快)。此刻再翻开手机 WIFI 从头衔接,发现推迟又变低了。</p><p>这便是 WebRTC 智能的地方。它会优先尝试直连,假如直连不成功,终究才会运用 TURN 转发。</p><h2>App 端屏幕同享</h2><p>视频通话一般都是同享摄像头,但是有的时候会有同享屏幕的需求。</p><p>在 Web 端同享屏幕很简单,将 <code>getUserMedia</code> 改成 <code>getDisplayMedia</code> 即可。但是在 App 端,或许由于隐私和安全问题,完结屏幕同享比较费劲。</p><p>安卓原生端运用 mediaProjection 完结同享屏幕。在 Android 10+ 之后,假如想正确同享屏幕,有必要要有一个继续存在的“前台进程”来保证屏幕同享的进程不被体系主动杀死。</p><p>假如没有装备前台进程,则屏幕流无法传输。下面咱们来介绍下在怎么 App 端同享屏幕。</p><p><strong>1. 装置 Notifee。</strong></p><p><a href="https://link.segmentfault.com/?enc=78lRGiRhGjfceVhPn3rCYg%3D%3D.M1qHGviU10RsF7IxFlvx3Wkic1GMCCCASwRqsj9r62xkAiADU77%2BiIheg%2FM8YK9V" rel="nofollow">Notifee</a> 是 React Native 完结告诉栏音讯的第三方库。咱们能够发动一个继续存在的音讯告诉作为前台进程,从而使屏幕流正常推送。</p><p>运用指令装置 Notifee:</p><pre><code> yarn add @notifee/react-native@5

留意这儿装置 Notifee 5.x 的版别,由于最新版的 7.x 需求 Android SDK 的 compileSdkVersion 为 33,而咱们创立的项目默许为 31,运用 5.x 不需求修正 SDK 的版别号。

2. 注册前台服务。

在进口文件 index.js 中,咱们运用 Notifee 注册一个前台服务:

import notifee from '@notifee/react-native';
notifee.registerForegroundService((notification) => {
return new Promise(() => {});
});

这儿只需求注册一下,不需求其他操作,因而比较简单。接着还需求在 Android 代码中注册一个 service,不然前台服务不生效。

翻开 android/app/src/main/AndroidManifest.xml 文件,在 <application> 标签内增加如下代码:

<service
android:name="app.notifee.core.ForegroundService"
android:foregroundServiceType="mediaProjection|camera|microphone" />

3. 创立前台告诉。

注册好前台服务之后,接着咱们在获取屏幕前创立前台告诉,代码如下:

import notifee from '@notifee/react-native';
const startNoti = async () => {
try {
let channelId = await notifee.createChannel({
id: 'default',
name: 'Default Channel',
});
await notifee.displayNotification({
title: '屏幕录制中...',
body: '在运用中手动关闭该告诉',
android: {
channelId,
asForegroundService: true, // 告诉作为前台服务,必填
},
});
} catch (err) {
console.error('前台服务发动异常:', err);
}
};

该办法会创立一个告诉音讯,详细 API 能够参阅 Notifee 文档。接着在捕获屏幕之前调用该办法即可:

const getMedia = async () => {
await startNoti();
let stream = await mediaDevices.getDisplayMedia();
};

终究通过测验,能够看到 App 端和 Web 端的屏幕都完结了同享。App 端作用如下:

Web 端作用如下:

摄像头与屏幕视频混流

在一些直播教育的场景中,呼叫端会一起同享两路视频 ——— 屏幕画面和摄像头画面。在咱们的经历中,一个 RTC 衔接实例中只能增加一条视频流。

假如要一起同享屏幕和摄像头,咱们首要想到的计划或许是在一个客户端创立两个 peer 实例,每个实例中增加一路视频流,建议两次 RTC 衔接。

事实上这种计划是低效的,增加杂乱度的一起也增加了资源的损耗。那么能不能在一个 peer 实例中增加两路视频呢?其实是能够的。

整体思路:在呼叫端将两条流合并为一条,在接纳端再将一条流拆分为两条。

1. 呼叫端组合流。

组合流其实很简单。由于流是由多个媒体轨迹组成,咱们只需求从屏幕和摄像头中拿到媒体轨迹,再将它们塞到一个自界说的流中,一条包括两个视频轨迹的流就组合好了。

var stream = new MediaStream();
const getMedia = async () => {
let camera_stream = await mediaDevices.getUserMedia();
let screen_stream = await mediaDevices.getDisplayMedia();
screen_stream.getVideoTracks().map((row) => stream.addTrack(row));
camera_stream.getVideoTracks().map((row) => stream.addTrack(row));
camera_stream.getAudioTracks().map((row) => stream.addTrack(row));
};

代码中为一个自界说媒体流增加了三条媒体轨迹,分别是屏幕视频、摄像头视频和摄像头音频。记住这个次序,在接纳端按照该次序拆流。

接着将这条媒体流增加到 peer 实例中,后边走正常的通讯逻辑即可:

stream.getTracks().forEach((track) => {
peer.addTrack(track, stream);
});

2. 接纳端拆解流。

接纳端在 ontrack 事情中拿到组合流,进行拆解:

peer.ontrack = async (event: any) => {
const [remoteStream] = event.streams;
let screen_stream = new MediaStream();
let camera_stream = new MediaStream();
remoteStream.getTracks().forEach((track, ind) => {
if (ind == 0) {
screen_stream.addTrack(track);
} else {
camera_stream.addTrack(track);
}
});
video1.srcObject = camera_stream; // 播映摄像头音视频
video2.srcObject = screen_stream; // 播映屏幕视频
};

这一步中,界说两条媒体流,然后将接纳到的混合流中的媒体轨迹拆分,分别增加到两条流中,这样屏幕流和摄像头流就拆开了,分别在两个 video 中播映即可。

总结

本篇比较体系的介绍了 App 端和 Web 端运用 WebRTC 通讯的全流程,能够看到整个流程还是比较杂乱的。尤其是咱们前端不拿手的地方,比如 TURN 建立和连通,App 端的各种版别权限问题,坑还是许多的。

假如有小伙伴在这个流程中遇到了问题,欢迎加我微信 ruidoc 拉你进入跨端与音视频评论群提问,或者关注我的大众号 程序员成功 检查更多文章。

再次感谢您的阅览~

本文来源:【WebRTC 跨端通讯】React + React Native 双端视频聊天、屏幕同享