webrtc 通话流程

  1. ClientA 和 ClientB 都链接上信令服务器(websocket)
  2. ClientA 获取本地视频流,并通过(websocket)发送会话描述信息(offer sdp)
  3. ClientB 收到信令(offer sdp)后回复(answer sdp)
  4. ClientA 接收到回复信令后开始链接,协商通信密钥,完成视频传输。

 

值得注意的是,webrtc 是客户端与客户端直接链接,不需要服务端参与。信令服务器仅在建立链接阶段交换数据。

基础环境

首先搭建一个基础环境方便开发,我使用的是尤大最新开发的 vite。

<span class="token function">yarn</span> create @vitejs/app webrtc --template vue <span class="token operator">&&</span> <span class="token function">cd</span> webrtc <span class="token operator">&&</span> <span class="token function">yarn</span> <span class="token operator">&&</span> <span class="token function">yarn</span> dev

安装 webrtc 浏览器兼容适配器:

<span class="token function">yarn</span> <span class="token function">add</span> webrtc-adapter

一个小的 demo 就不引入不必要的扩展包了,自己动手实现一个简单的路由器。调整 App.vue 文件:

<span class="token operator"><</span>script<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> h<span class="token punctuation">,</span> computed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span>
<span class="token keyword">import</span> routes <span class="token keyword">from</span> <span class="token string">"./routes"</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> currentRoute <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> window<span class="token punctuation">.</span>location<span class="token punctuation">.</span>pathname<span class="token punctuation">)</span>
    <span class="token keyword">const</span> currentComponent <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> routes<span class="token punctuation">[</span>currentRoute<span class="token punctuation">.</span>value<span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">)</span>

    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">h</span><span class="token punctuation">(</span>currentComponent<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>

新建 src/routes.js 文件:

<span class="token keyword">import</span> Home <span class="token keyword">from</span> <span class="token string">"./page/Server.vue"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Client <span class="token keyword">from</span> <span class="token string">"./page/Client.vue"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
    <span class="token string">'/'</span><span class="token punctuation">:</span> Home<span class="token punctuation">,</span>
    <span class="token string">'/client'</span><span class="token punctuation">:</span> Client
<span class="token punctuation">}</span>

这样就可以分开编写客户端和服务端代码了。由于这只是个简单的 demo ,所以我这样使用,生成环境开发还是建议大家使用 vue-router

正文

首先创建 src/page/Server.vue 组件,引入 webrtc 适配器:

<template>
    <div class="server">
        <video id="localVideo" autoplay playsinline muted></video>
    </div>
</template>

<script>
import "webrtc-adapter"
export default {
    name: "Server"
}
</script>

<style scoped>
video {
    width: 80%;
    height: 80%;
    background: #000;
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto; 
} 
</style>

1. 获取本地视频流

这一步既可以录制屏幕内容也可以读取用户摄像头。

<span class="token operator"><</span>template<span class="token operator">></span>
  <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"server"</span><span class="token operator">></span>
    <span class="token operator"><</span>video id<span class="token operator">=</span><span class="token string">"localVideo"</span> autoplay playsinline muted<span class="token operator">></span><span class="token operator"><</span><span class="token operator">/</span>video<span class="token operator">></span>
  <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span>
<span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span>

<span class="token operator"><</span>script<span class="token operator">></span>
<span class="token keyword">import</span> <span class="token string">"webrtc-adapter"</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> onMounted <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"vue"</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span>
  name<span class="token punctuation">:</span> <span class="token string">"Home"</span><span class="token punctuation">,</span>
  <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
     <span class="token keyword">const</span> <span class="token function-variable function">startLive</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
       <span class="token keyword">const</span> localVideo <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"localVideo"</span><span class="token punctuation">)</span>
       <span class="token keyword">let</span> stream <span class="token operator">=</span> <span class="token keyword">await</span> navigator<span class="token punctuation">.</span>mediaDevices<span class="token punctuation">.</span><span class="token function">getDisplayMedia</span><span class="token punctuation">(</span><span class="token punctuation">{</span> video<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> audio<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
       localVideo<span class="token punctuation">.</span>srcObject <span class="token operator">=</span> stream
     <span class="token punctuation">}</span>

     <span class="token function">onMounted</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">startLive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

     <span class="token keyword">return</span> <span class="token punctuation">{</span>  <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span>

这里需要注意的是一定要在 dom 节点加载完毕之后再执行,否则会获取不到 video 节点

2. 信令服务器

想要与客户端建立链接,必须有信令服务器端介入,帮助客户端之间进行通讯。其实信令服务器原理很简单,不用做任何处理(业务逻辑除外),只需要把客户端发送的数据原样转发给目标客户端即可。我这里采用 GoLang 实现:

<span class="token keyword">package</span> main

<span class="token keyword">import</span> <span class="token punctuation">(</span>
    <span class="token string">"github.com/gogf/gf/frame/g"</span>
    <span class="token string">"github.com/gogf/gf/net/ghttp"</span>
    <span class="token string">"github.com/gogf/gf/os/glog"</span>
<span class="token punctuation">)</span>

<span class="token keyword">var</span> <span class="token punctuation">(</span>
    Server <span class="token operator">*</span>ghttp<span class="token punctuation">.</span>WebSocket
    Client <span class="token operator">*</span>ghttp<span class="token punctuation">.</span>WebSocket
<span class="token punctuation">)</span>

<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token punctuation">{</span>
    s <span class="token operator">:=</span> g<span class="token punctuation">.</span><span class="token function">Server</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    s<span class="token punctuation">.</span><span class="token function">BindHandler</span><span class="token punctuation">(</span><span class="token string">"/server"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>r <span class="token operator">*</span>ghttp<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        ws<span class="token punctuation">,</span> err <span class="token operator">:=</span> r<span class="token punctuation">.</span><span class="token function">WebSocket</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
            glog<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
            r<span class="token punctuation">.</span><span class="token function">Exit</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

        Server <span class="token operator">=</span> ws

        <span class="token keyword">for</span> <span class="token punctuation">{</span>
            msgType<span class="token punctuation">,</span> msg<span class="token punctuation">,</span> err <span class="token operator">:=</span> ws<span class="token punctuation">.</span><span class="token function">ReadMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">if</span> err <span class="token operator">=</span> Client<span class="token punctuation">.</span><span class="token function">WriteMessage</span><span class="token punctuation">(</span>msgType<span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    s<span class="token punctuation">.</span><span class="token function">BindHandler</span><span class="token punctuation">(</span><span class="token string">"/client"</span><span class="token punctuation">,</span> <span class="token keyword">func</span><span class="token punctuation">(</span>r <span class="token operator">*</span>ghttp<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        ws<span class="token punctuation">,</span> err <span class="token operator">:=</span> r<span class="token punctuation">.</span><span class="token function">WebSocket</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
            glog<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
            r<span class="token punctuation">.</span><span class="token function">Exit</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

        Client <span class="token operator">=</span> ws

        <span class="token keyword">for</span> <span class="token punctuation">{</span>
            msgType<span class="token punctuation">,</span> msg<span class="token punctuation">,</span> err <span class="token operator">:=</span> ws<span class="token punctuation">.</span><span class="token function">ReadMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">if</span> err <span class="token operator">=</span> Server<span class="token punctuation">.</span><span class="token function">WriteMessage</span><span class="token punctuation">(</span>msgType<span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
                <span class="token keyword">return</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>

    s<span class="token punctuation">.</span><span class="token function">SetPort</span><span class="token punctuation">(</span><span class="token number">8199</span><span class="token punctuation">)</span>
    s<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

3. 建立链接

链接信令服务器并且创建 PeerConnection 实例 (参数为 null,忽略 iceserver,仅局域网下通讯);

<span class="token keyword">let</span> ws <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WebSocket</span><span class="token punctuation">(</span><span class="token string">"ws://127.0.0.1:8199/server"</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> peer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RTCPeerConnection</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span>

<span class="token comment">// 将媒体轨道添加到轨道集</span>
stream<span class="token punctuation">.</span><span class="token function">getTracks</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">track</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  peer<span class="token punctuation">.</span><span class="token function">addTrack</span><span class="token punctuation">(</span>track<span class="token punctuation">,</span> stream<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

然后通过 websocket 发送会话描述信息(offer sdp):

<span class="token keyword">const</span> offer <span class="token operator">=</span> <span class="token keyword">await</span> peer<span class="token punctuation">.</span><span class="token function">createOffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">await</span> peer<span class="token punctuation">.</span><span class="token function">setLocalDescription</span><span class="token punctuation">(</span>offer<span class="token punctuation">)</span>
ws<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>offer<span class="token punctuation">)</span><span class="token punctuation">)</span>

4. 客户端接收

客户端同样链接信令服务器并创建 PeerConnection 实例。

<span class="token keyword">let</span> ws <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WebSocket</span><span class="token punctuation">(</span><span class="token string">"ws://127.0.0.1:8199/client"</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> peer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RTCPeerConnection</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span>

监听信令服务器消息:

ws<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> type<span class="token punctuation">,</span> sdp<span class="token punctuation">,</span> iceCandidate <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"offer"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">answer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RTCSessionDescription</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">,</span> sdp <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"offer_ice"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    peer<span class="token punctuation">.</span><span class="token function">addIceCandidate</span><span class="token punctuation">(</span>iceCandidate<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

收到服务器链接请求后,需要回复消息:

<span class="token keyword">const</span> <span class="token function-variable function">answer</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token parameter">sdp</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">await</span> peer<span class="token punctuation">.</span><span class="token function">setRemoteDescription</span><span class="token punctuation">(</span>sdp<span class="token punctuation">)</span>

  <span class="token keyword">const</span> answer <span class="token operator">=</span> <span class="token keyword">await</span> peer<span class="token punctuation">.</span><span class="token function">createAnswer</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  ws<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">)</span>

  <span class="token keyword">await</span> peer<span class="token punctuation">.</span><span class="token function">setLocalDescription</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span> 
<span class="token punctuation">}</span>

5. 服务端监听响应

客户端收到链接请求并回复,服务端同时也要监听客户端的回复:

ws<span class="token punctuation">.</span><span class="token function-variable function">onmessage</span> <span class="token operator">=</span> <span class="token parameter">e</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> type<span class="token punctuation">,</span> sdp<span class="token punctuation">,</span> iceCandidate <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"answer"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    peer<span class="token punctuation">.</span><span class="token function">setRemoteDescription</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RTCSessionDescription</span><span class="token punctuation">(</span><span class="token punctuation">{</span> type<span class="token punctuation">,</span> sdp <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>type <span class="token operator">===</span> <span class="token string">"answer_ice"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    peer<span class="token punctuation">.</span><span class="token function">addIceCandidate</span><span class="token punctuation">(</span>iceCandidate<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

6. 获取远程视频流

当调用 setLocalDescription 之后,RTC 链接就开始搜集候选人,而我们只需要监听事件,通过信令服务器传递候选人信息即可。

<span class="token comment">// 服务端</span>
peer<span class="token punctuation">.</span><span class="token function-variable function">onicecandidate</span> <span class="token operator">=</span> <span class="token parameter">e</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>candidate<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"搜集并发送候选人"</span><span class="token punctuation">)</span>
    ws<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>
      <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        type<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`offer_ice`</span></span><span class="token punctuation">,</span>
        iceCandidate<span class="token punctuation">:</span> e<span class="token punctuation">.</span>candidate<span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"候选人收集完成!"</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 客户端</span>
peer<span class="token punctuation">.</span><span class="token function-variable function">onicecandidate</span> <span class="token operator">=</span> <span class="token parameter">e</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>candidate<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"搜集并发送候选人"</span><span class="token punctuation">)</span>
    ws<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>
      <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        type<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`answer_ice`</span></span><span class="token punctuation">,</span>
        iceCandidate<span class="token punctuation">:</span> e<span class="token punctuation">.</span>candidate<span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"候选人收集完成!"</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

候选人搜集完成之后,服务端和客户端就正式勾搭上了,开始通信协商密钥,建立一条最优的链接方式。这个时候只需要监听 ontrack 事件获取视频流即可:

peer<span class="token punctuation">.</span><span class="token function-variable function">ontrack</span> <span class="token operator">=</span> <span class="token parameter">e</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>e <span class="token operator">&&</span> e<span class="token punctuation">.</span>streams<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"收到对方音频/视频流数据..."</span><span class="token punctuation">)</span>
    <span class="token keyword">let</span> localVideo <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"localVideo"</span><span class="token punctuation">)</span>
    localVideo<span class="token punctuation">.</span>srcObject <span class="token operator">=</span> e<span class="token punctuation">.</span>streams<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

结语

RTCPeerConnection 链接是双向的,这里我为了演示方便,仅做了单向视频传输,强行区分了服务端与客户端。客户端收到链接请求的同时也可以获取本地视频流传递到服务端,形成双向视频传输。同时为了方便演示信令服务器也是最简单的方式实现,客户端比需先链接才能进行通讯。

完整代码已上传 github.com/zxdstyle/webrtc-demo

 

文章摘自https://learnku.com/articles/55045

作者:zxdstyle


不知天在水,清梦压星河