速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

基于 Agora SDK 实现 Web 端的多人视频互动

  • 2020-02-26
  • 本文字数:9231 字

    阅读完需:约 30 分钟

基于 Agora SDK 实现 Web 端的多人视频互动

根据本文指导快速集成 Agora Web SDK 并在你自己的 app 里实现音视频互动直播。


本文会详细介绍如何建立一个简单的项目并使用 Agora Web SDK 实现基础的互动直播。我们建议你阅读本文以快速了解 Agora 的核心方法。


互动直播和实时通话的区别在于,直播频道的用户有角色之分。你可以将角色设置为主播,或者观众,其中主播可以收、发流,观众只能收流。


由于浏览器的安全策略对除 127.0.0.1 以外的 HTTP 地址作了限制,Agora Web SDK 仅支持 HTTPS 协议或者 http://localhosthttp://127.0.0.1),请勿使用 HTTP 协议部署你的项目。

Demo 体验

我们在 GitHub 上提供一个开源的基础视频互动直播示例项目,在开始开发之前你可以通过该示例项目体验互动直播效果。

前提条件

  1. 安装一款 Agora Web SDK 支持的浏览器,如下表所示:

  2. 获得一个有效的 Agora 账号。(免费注册)

  3. 如果你的网络环境部署了防火墙,请根据声网文档中心的「应用企业防火墙限制」打开相关端口。

设置开发环境

你需要准备一个自己的项目并且将 Agora Web SDK 集成到其中。

创建 Web 项目

如果你已经有一个 Web 项目了,跳过该节,直接阅读集成 SDK。


我们提供的示例代码使用了一些第三方的库文件来实现页面的样式和布局,你可以在 Github repo 中查找一下文件,或者使用其他方式实现。


  • common.css

  • jquery.min.js

  • materialize.min.js

  • 这个项目只需要用到一个 HTML 文件。


  1. 新建一个 HTML 文件。这里我们将文件命名为 index.html(以下简称项目文件)。

  2. 用一个代码编辑器(例如 VS Code)打开该文件。

  3. 复制以下代码,粘贴到项目文件中。

  4. 该步骤会为你的项目创建前端页面的 UI,你也可以自定义你的 UI。以下是示例代码:


<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta http-equiv="X-UA-Compatible" content="ie=edge">  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">  <title>Basic Live Broadcast</title>  <link rel="stylesheet" href="./assets/common.css" /></head><body class="agora-theme">  <div class="navbar-fixed">    <nav class="agora-navbar">      <div class="nav-wrapper agora-primary-bg valign-wrapper">        <h5 class="left-align">Basic Live Broadcast</h5>      </div>    </nav>  </div>  <form id="form" class="row col l12 s12">    <div class="row container col l12 s12">      <div class="col" style="min-width: 433px; max-width: 443px">        <div class="card" style="margin-top: 0px; margin-bottom: 0px;">          <div class="row card-content" style="margin-bottom: 0px;">              <div class="input-field">                <label for="appID" class="active">App ID</label>                <input type="text" placeholder="App ID" name="appID">              </div>              <div class="input-field">                <label for="channel" class="active">Channel</label>                <input type="text" placeholder="channel" name="channel">              </div>              <div class="input-field">                <label for="token" class="active">Token</label>                <input type="text" placeholder="token" name="token">              </div>              Host: <input id="role" type="checkbox" checked></input>              <div class="row" style="margin: 0">                <div class="col s12">                <button class="btn btn-raised btn-primary waves-effect waves-light" id="join">JOIN</button>                <button class="btn btn-raised btn-primary waves-effect waves-light" id="leave">LEAVE</button>                <button class="btn btn-raised btn-primary waves-effect waves-light" id="publish">PUBLISH</button>                <button class="btn btn-raised btn-primary waves-effect waves-light" id="unpublish">UNPUBLISH</button>                </div>              </div>          </div>        </div>      </div>      <div class="col s7">        <div class="video-grid" id="video">          <div class="video-view">            <div id="local_stream" class="video-placeholder"></div>            <div id="local_video_info" class="video-profile hide"></div>            <div id="video_autoplay_local" class="autoplay-fallback hide"></div>          </div>        </div>      </div>    </div>  </form><script src="vendor/jquery.min.js"></script><script src="vendor/materialize.min.js"></script></body></html>
复制代码

集成 SDK

选择如下任意一种方法获取 Agora Web SDK:


方法 1. 使用 npm 获取 SDK


使用该方法需要先安装 npm,详见 npm 快速入门。


  1. 运行安装命令


npm install agora-rtc-sdk
复制代码


  1. 在你的项目的 Javascript 代码中添加一行:


import AgoraRTC from 'agora-rtc-sdk'
复制代码


方法 2. 使用 CDN 方法获取 SDK


该方法无需下载安装包。在项目文件中,将以下代码添加到上一行:</p><pre><code>&lt;script&nbsp;src=&quot;https://cdn.agora.io/sdk/release/AgoraRTCSDK-3.0.0.js&quot;&gt;&lt;/script&gt;<br/></code></pre><p>方法 3. 从官网获取 SDK</p><ol><li>下载最新版 Agora Web SDK 软件包。</li><li>将下载下来的软件包中的 AgoraRTCSDK-3.0.0.js 文件保存到项目文件所在的目录下。</li><li>在项目文件中,将如下代码添加到<style>上一行:</li></ol><pre><code>&lt;script&nbsp;src=&quot;./AgoraRTCSDK-3.0.0.js&quot;&gt;&lt;/script&gt;<br/></code></pre><p>为方便起见,这里我们选择第二种方法,直接使用 CDN 链接。<br/>现在,我们已经将 Agora Web SDK 集成到项目中了。接下来我们要通过调用 Agora Web SDK 提供的核心 API 实现基础的互动直播功能。</p><h2>实现互动直播</h2><p>本节介绍如何使用 Agora Web SDK 实现互动直播。</p><p>在使用 Agora Web SDK 时,你会经常用到以下两种对象:</p><ul><li>Client 对象,代表一个本地客户端。Client 类的方法提供了音视频通话的主要功能,例如加入频道、发布音视频流等。</li><li>Stream 对象,代表本地和远端的音视频流。Stream 类的方法用于定义音视频流对象的行为,例如流的播放控制、音视频的编码配置等。调用 Stream 方法时,请注意区分本地流和远端流对象。<br/>下图展示了基础互动直播的 API 调用。注意图中的方法是对不同的对象调用的。</li></ul><p><img src="https://static001.infoq.cn/resource/image/94/47/94f50891c80418c172345becaedcf647.jpg" alt=""/></p><blockquote><p>本文只介绍 Agora Web SDK 最基础的方法和回调。完整的 API 方法和回调详见 Web API 参考。</p></blockquote><p>为方便起见,我们为下面要用到的示例代码定义了两个变量。此步骤不是必须的,你可以根据你的项目有其他的实现。</p><pre><code>//&nbsp;rtc&nbsp;object<br/>var&nbsp;rtc&nbsp;=&nbsp;{<br/>&nbsp;&nbsp;client:&nbsp;null,<br/>&nbsp;&nbsp;joined:&nbsp;false,<br/>&nbsp;&nbsp;published:&nbsp;false,<br/>&nbsp;&nbsp;localStream:&nbsp;null,<br/>&nbsp;&nbsp;remoteStreams:&nbsp;[],<br/>&nbsp;&nbsp;params:&nbsp;{}<br/>};<br/><br/>//&nbsp;Options&nbsp;for&nbsp;joining&nbsp;a&nbsp;channel<br/>var&nbsp;option&nbsp;=&nbsp;{<br/>&nbsp;&nbsp;appID:&nbsp;&quot;Your&nbsp;App&nbsp;ID&quot;,<br/>&nbsp;&nbsp;channel:&nbsp;&quot;Channel&nbsp;name&quot;,<br/>&nbsp;&nbsp;uid:&nbsp;null,<br/>&nbsp;&nbsp;token:&nbsp;&quot;Your&nbsp;token&quot;<br/>}<br/></code></pre><p>初始化客户端</p><p>加入频道前,我们需要先创建并初始化一个客户端对象。</p><pre><code>&nbsp;//&nbsp;Create&nbsp;a&nbsp;client<br/>&nbsp;rtc.client&nbsp;=&nbsp;AgoraRTC.createClient({mode:&nbsp;&quot;rtc&quot;,&nbsp;codec:&nbsp;&quot;h264&quot;});<br/><br/>&nbsp;//&nbsp;Initialize&nbsp;the&nbsp;client<br/>&nbsp;rtc.client.init(option.appID,&nbsp;function&nbsp;()&nbsp;{<br/>&nbsp;&nbsp;&nbsp;console.log(&quot;init&nbsp;success&quot;);<br/>&nbsp;&nbsp;&nbsp;},&nbsp;(err)&nbsp;=&gt;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;console.error(err);<br/>&nbsp;});<br/></code></pre><p>在 AgoraRTC.createClient 方法中,需注意 mode 和 codec 这两个参数的设置:</p><ul><li>mode 用于设置频道模式。一对一或多人通话中,建议设为 “rtc” ,使用通信模式;互动直播中,建议设为 “live”,使用直播模式。</li><li>codec 用于设置浏览器使用的编解码格式。如果你需要使用 Safari 12.1 及之前版本,将该参数设为 “h264”;如果你需要在手机上使用 Agora Web SDK,请参考移动端使用 Web SDK。<br/>你需要在该步骤中填入项目的 App ID。请参考如下步骤在控制台创建 Agora 项目并获取 App ID。</li></ul><ol><li>登录控制台,点击左侧导航栏的项目管理图标 。</li><li>点击创建,按照屏幕提示设置项目名,选择一种鉴权机制,然后点击提交。</li><li>在项目管理页面,你可以获取该项目的 App ID。<br/>设置用户角色</li></ol><p>直播频道有两种用户角色:主播和观众,其中默认的角色为观众。设置频道模式为直播后,你可以在 App 中参考如下步骤设置用户角色:</p><ol><li>让用户选择自己的角色是主播(“host”)还是观众(“audience”)。</li><li>调用 setClientRole 方法,传入用户选择的角色。</li></ol><pre><code>//&nbsp;The&nbsp;value&nbsp;of&nbsp;role&nbsp;can&nbsp;be&nbsp;&quot;host&quot;&nbsp;or&nbsp;&quot;audience&quot;.<br/>rtc.client.setClientRole(role);&nbsp;<br/></code></pre><p>直播频道内的用户,只能看到主播的画面、听到主播的声音。加入频道后,如果你想切换用户角色,也可以调用 setClientRole 方法。</p><p>加入频道</p><p>在 Client.init 的 onSuccess 回调中调用 Client.join 加入频道。</p><pre><code>&nbsp;//&nbsp;Join&nbsp;a&nbsp;channel<br/>&nbsp;rtc.client.join(option.token&nbsp;?&nbsp;option.token&nbsp;:&nbsp;null,&nbsp;option.channel,&nbsp;option.uid&nbsp;?&nbsp;+option.uid&nbsp;:&nbsp;null,&nbsp;function&nbsp;(uid)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;join&nbsp;channel:&nbsp;&quot;&nbsp;+&nbsp;option.channel&nbsp;+&nbsp;&quot;&nbsp;success,&nbsp;uid:&nbsp;&quot;&nbsp;+&nbsp;uid);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rtc.params.uid&nbsp;=&nbsp;uid;<br/>&nbsp;&nbsp;&nbsp;},&nbsp;function(err)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.error(&quot;client&nbsp;join&nbsp;failed&quot;,&nbsp;err)<br/>&nbsp;})<br/></code></pre><p>在 Client.join 中注意以下参数的设置:</p><ul><li>token: 该参数为可选。如果你的 Agora 项目开启了 App 证书,你需要在该参数中传入一个 Token,详见 使用 Token。</li><li>在测试环境,我们推荐使用控制台生成临时 Token,详见获取临时 Token。</li><li>在生产环境,我们推荐你在自己的服务端生成 Token,详见 生成 Token.</li><li>channel: 频道名,长度在 64 字节以内的字符串。</li><li>uid: 用户 ID,频道内每个用户的 UID 必须是唯一的。如果你将 uid 设为 null,Agora 会自动分配一个 UID 并在 onSuccess 回调中返回。<br/>更多的参数设置注意事项请参考 Client.join 接口中的参数描述。</li></ul><h3>发布本地流</h3><p>如果用户角色设置为主播,我们需要创建并发布本地流。</p><ol><li>在 Client.join 的 onSuccess 回调中调用 AgoraRTC.createStream 方法创建一个本地音视频流。<br/>在创建流时,通过设置 audio 和 video 参数来控制是否发布音频和视频。</li></ol><pre><code>//&nbsp;Create&nbsp;a&nbsp;local&nbsp;stream<br/>rtc.localStream&nbsp;=&nbsp;AgoraRTC.createStream({<br/>&nbsp;&nbsp;streamID:&nbsp;rtc.params.uid,<br/>&nbsp;&nbsp;audio:&nbsp;true,<br/>&nbsp;&nbsp;video:&nbsp;true,<br/>&nbsp;&nbsp;screen:&nbsp;false,<br/>})<br/>2.&nbsp;调用 &nbsp;Stream.init&nbsp;方法初始化创建的流。<br/>//&nbsp;Initialize&nbsp;the&nbsp;local&nbsp;stream<br/>rtc.localStream.init(function&nbsp;()&nbsp;{<br/>&nbsp;&nbsp;console.log(&quot;init&nbsp;local&nbsp;stream&nbsp;success&quot;);<br/>},&nbsp;function&nbsp;(err)&nbsp;{<br/>&nbsp;&nbsp;console.error(&quot;init&nbsp;local&nbsp;stream&nbsp;failed&nbsp;&quot;,&nbsp;err);<br/>})<br/></code></pre><p>在初始化流时,浏览器会跳出弹窗要求摄像头和麦克风权限,请确保授权。<br/>3. 在 Stream.init 的 onSuccess 回调中调用 Client.publish 方法,发布本地流。</p><pre><code>//&nbsp;Publish&nbsp;the&nbsp;local&nbsp;stream<br/>rtc.client.publish(rtc.localStream,&nbsp;function&nbsp;(err)&nbsp;{<br/>&nbsp;&nbsp;console.log(&quot;publish&nbsp;failed&quot;);<br/>&nbsp;&nbsp;console.error(err);<br/>})<br/></code></pre><h3>订阅远端流</h3><p>当远端流加入频道时,会触发 stream-added 事件,我们需要通过 Client.on 监听该事件并在回调中订阅新加入的远端流。</p><p>建议在创建客户端对象之后立即监听事件。</p><ol><li>监听 “stream-added” 事件,当有远端流加入时订阅该流。</li></ol><pre><code>rtc.client.on(&quot;stream-added&quot;,&nbsp;function&nbsp;(evt)&nbsp;{&nbsp;&nbsp;<br/>&nbsp;&nbsp;var&nbsp;remoteStream&nbsp;=&nbsp;evt.stream;<br/>&nbsp;&nbsp;var&nbsp;id&nbsp;=&nbsp;remoteStream.getId();<br/>&nbsp;&nbsp;if&nbsp;(id&nbsp;!==&nbsp;rtc.params.uid)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;rtc.client.subscribe(remoteStream,&nbsp;function&nbsp;(err)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(&quot;stream&nbsp;subscribe&nbsp;failed&quot;,&nbsp;err);<br/>&nbsp;&nbsp;&nbsp;&nbsp;})<br/>&nbsp;&nbsp;}<br/>&nbsp;&nbsp;console.log('stream-added&nbsp;remote-uid:&nbsp;',&nbsp;id);<br/>});<br/></code></pre><ol start="2"><li>监听 “stream-subscribed” 事件,订阅成功后播放远端流。</li></ol><pre><code>rtc.client.on(&quot;stream-subscribed&quot;,&nbsp;function&nbsp;(evt)&nbsp;{<br/>&nbsp;&nbsp;var&nbsp;remoteStream&nbsp;=&nbsp;evt.stream;<br/>&nbsp;&nbsp;var&nbsp;id&nbsp;=&nbsp;remoteStream.getId();<br/>&nbsp;&nbsp;//&nbsp;Add&nbsp;a&nbsp;view&nbsp;for&nbsp;the&nbsp;remote&nbsp;stream.<br/>&nbsp;&nbsp;addView(id);<br/>&nbsp;&nbsp;//&nbsp;Play&nbsp;the&nbsp;remote&nbsp;stream.<br/>&nbsp;&nbsp;remoteStream.play(&quot;remote_video_&quot;&nbsp;+&nbsp;id);<br/>&nbsp;&nbsp;console.log('stream-subscribed&nbsp;remote-uid:&nbsp;',&nbsp;id);<br/>})<br/></code></pre><p>受浏览器策略影响,在 Chrome 70+ 和 Safari 浏览器上,Stream.play 方法必须由用户手势触发,详情请在声网文档中心搜索、参考 Autoplay Policy Changes。<br/>3. 监听 “stream-removed” 事件,当远端流被移除时(例如远端用户调用了 Stream.unpublish), 停止播放该流并移除它的画面。</p><pre><code>rtc.client.on(&quot;stream-removed&quot;,&nbsp;function&nbsp;(evt)&nbsp;{<br/>&nbsp;&nbsp;var&nbsp;remoteStream&nbsp;=&nbsp;evt.stream;<br/>&nbsp;&nbsp;var&nbsp;id&nbsp;=&nbsp;remoteStream.getId();<br/>&nbsp;&nbsp;//&nbsp;Stop&nbsp;playing&nbsp;the&nbsp;remote&nbsp;stream.<br/>&nbsp;&nbsp;remoteStream.stop(&quot;remote_video_&quot;&nbsp;+&nbsp;id);<br/>&nbsp;&nbsp;//&nbsp;Remove&nbsp;the&nbsp;view&nbsp;of&nbsp;the&nbsp;remote&nbsp;stream.&nbsp;<br/>&nbsp;&nbsp;removeView(id);<br/>&nbsp;&nbsp;console.log('stream-removed&nbsp;remote-uid:&nbsp;',&nbsp;id);<br/>})<br/></code></pre><p>你需要自己实现 addView 和 removeView 的功能,可以参考以下示例代码。</p><pre><code>function&nbsp;addView&nbsp;(id,&nbsp;show)&nbsp;{<br/>&nbsp;&nbsp;if&nbsp;(!(&quot;#&quot;&nbsp;+&nbsp;id)[0])&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;(&quot;&lt;div/&gt;&quot;,&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;&quot;remote_video_panel_&quot;&nbsp;+&nbsp;id,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class:&nbsp;&quot;video-view&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;}).appendTo(&quot;#video&quot;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;(&quot;&lt;div/&gt;&quot;,&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;&quot;remote_video_&quot;&nbsp;+&nbsp;id,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class:&nbsp;&quot;video-placeholder&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;}).appendTo(&quot;#remote_video_panel_&quot;&nbsp;+&nbsp;id);<br/>&nbsp;&nbsp;&nbsp;&nbsp;(&quot;&lt;div/&gt;&quot;,&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;&quot;remote_video_info_&quot;&nbsp;+&nbsp;id,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class:&nbsp;&quot;video-profile&nbsp;&quot;&nbsp;+&nbsp;(show&nbsp;?&nbsp;&quot;&quot;&nbsp;:&nbsp;&nbsp;&quot;hide&quot;),<br/>&nbsp;&nbsp;&nbsp;&nbsp;}).appendTo(&quot;#remote_video_panel_&quot;&nbsp;+&nbsp;id);<br/>&nbsp;&nbsp;&nbsp;&nbsp;(&quot;&lt;div/&gt;&quot;,&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;&quot;video_autoplay_&quot;+&nbsp;id,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class:&nbsp;&quot;autoplay-fallback&nbsp;hide&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;}).appendTo(&quot;#remote_video_panel_&quot;&nbsp;+&nbsp;id);<br/>&nbsp;&nbsp;}<br/>}<br/>function&nbsp;removeView&nbsp;(id)&nbsp;{<br/>&nbsp;&nbsp;if&nbsp;((&quot;#remote_video_panel_&quot;&nbsp;+&nbsp;id)[0])&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;$(&quot;#remote_video_panel_&quot;+id).remove();<br/>&nbsp;&nbsp;}<br/>}<br/></code></pre><h3>离开频道</h3><p>调用 Client.leave 方法离开频道。</p><pre><code>//&nbsp;Leave&nbsp;the&nbsp;channel<br/>rtc.client.leave(function&nbsp;()&nbsp;{<br/>&nbsp;&nbsp;//&nbsp;Stop&nbsp;playing&nbsp;the&nbsp;local&nbsp;stream<br/>&nbsp;&nbsp;rtc.localStream.stop();<br/>&nbsp;&nbsp;//&nbsp;Close&nbsp;the&nbsp;local&nbsp;stream<br/>&nbsp;&nbsp;rtc.localStream.close();<br/>&nbsp;&nbsp;//&nbsp;Stop&nbsp;playing&nbsp;the&nbsp;remote&nbsp;streams&nbsp;and&nbsp;remove&nbsp;the&nbsp;views<br/>&nbsp;&nbsp;while&nbsp;(rtc.remoteStreams.length&nbsp;&gt;&nbsp;0)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;stream&nbsp;=&nbsp;rtc.remoteStreams.shift();<br/>&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;id&nbsp;=&nbsp;stream.getId();<br/>&nbsp;&nbsp;&nbsp;&nbsp;stream.stop();<br/>&nbsp;&nbsp;&nbsp;&nbsp;removeView(id);<br/>&nbsp;&nbsp;}<br/>&nbsp;&nbsp;console.log(&quot;client&nbsp;leaves&nbsp;channel&nbsp;success&quot;);<br/>},&nbsp;function&nbsp;(err)&nbsp;{<br/>&nbsp;&nbsp;console.log(&quot;channel&nbsp;leave&nbsp;failed&quot;);<br/>&nbsp;&nbsp;console.error(err);<br/>})<br/><br/></code></pre><h3>运行你的 app</h3><p>我们建议在本地 Web 服务器上测试你的 app。这里我们用 npm 的 live-server 设置一个本地服务器。</p><p>在本地服务器(localhost)运行 web app 仅作为测试用途。部署生产环境时,请确保使用 HTTPS 协议。</p><ol><li>安装 live-server。</li></ol><pre><code>npm&nbsp;i&nbsp;live-server&nbsp;-g<br/></code></pre><ol start="2"><li><p>在命令行中进入你的项目所在的目录。</p></li><li><p>运行 app。</p></li></ol><pre><code>live-server&nbsp;.<br/></code></pre><p>现在你的浏览器应该会自动打开你的 web app 页面。</p><ol start="4"><li><p>输入频道名,选择用户角色,点击 JOIN 开始通话。<br/>你可能需要给浏览器摄像头和麦克风权限。如果在创建本地流时打开了视频且选择主播角色,你现在应该可以看到自己的视频画面。</p></li><li><p>在浏览器中打开另一个页面,输入相同的 URL 地址。点击 JOIN 按钮。现在你应该可以看到两个视频画面。</p></li></ol><p>如果页面没有正常工作,可以打开浏览器的控制台查看错误信息进行排查。常见的错误信息包括:</p><ul><li><p>INVALID_VENDOR_KEY:App ID 错误,检查你填写的 App ID。</p></li><li><p>ERR_DYNAMIC_USE_STATIC_KE:你的 Agora 项目启用了 App 证书,需要在加入频道时填写 Token。</p></li><li><p>Media access:NotFoundError:检查你的摄像头和麦克风是否正常工作。</p></li><li><p>MEDIA_NOT_SUPPORT:请使用 HTTPS 协议 或者 localhost。</p></li></ul><p><strong>本文转载自 声网 Agora 公众号。</strong></p><p><strong>原文链接:</strong><a href="https://mp.weixin.qq.com/s/bK6bwE4rPXGFNx7sfAftrA">https://mp.weixin.qq.com/s/bK6bwE4rPXGFNx7sfAftrA</a></p>


2020-02-26 15:50940

评论

发布
暂无评论
发现更多内容

王者荣耀商城异地多活架构设计

dan629xy

Oceanbase 读写分离方案探索与优化

OceanBase 数据库

分布式数据库 读写分离 oceanbase

OceanBase数据库荣获领先科技成果“新技术”奖

OceanBase 数据库

oceanbase 数博会

[ Kitex 源码解读 ] 代码解读思路的总结

baiyutang

字节跳动 微服务 kitex 6月月更

模块七:王者荣耀商城异地多活架构

jiaoxn

「架构实战营」

迷宫问题java(DFS,回溯法,递归)

写代码两年半

DFS 迷宫 javase 回溯算法 6月月更

万物并作,吾以观复|OceanBase 政企行业实践

OceanBase 数据库

数据库 oceanbase

OceanBase 源码解读(十五):Location Cache 模块浅析

OceanBase 数据库

数据库 oceanbase 源码解读

【愚公系列】2022年06月 二十三种设计模式(二十)-状态模式(State Pattern)

愚公搬代码

6月月更

深入浅出 ODP(一):功能模块及特性详解

OceanBase 数据库

数据库 oceanbase

OceanBase 源码解读(十二):事务日志的提交和回放

OceanBase 数据库

oceanbase 源码解读

OceanBase 成为信通院首批可信开源社区、可信开源项目

OceanBase 数据库

开源 oceanbase 信通院

基于 RDMA 的分布式系统研究进展

OceanBase 数据库

RDMA

生态对对碰丨OceanBase如何与Prometheus与Grafana监控结合

OceanBase 数据库

Grafana Prometheus oceanbase

OceanBase 源码解读(十四):集群位置信息的存储与汇报

OceanBase 数据库

数据库 oceanbase 集群位置信息

[go] 后台管理数据权限控制实现 (无业务修改)

林逸民

富滇银行完成数字化升级|OceanBase数据库助力布局分布式架构中台

OceanBase 数据库

分布式数据库 oceanbase

Docker下RabbitMQ延时队列实战两部曲之二:细说开发

程序员欣宸

Java RabbitMQ RabbitMQ延时队列 6月月更

Fabric.js 上标和下标的使用偏方🔥

德育处主任

Web canvas 前端可视化 Fabric.js 6月月更

InfoQ 极客传媒 15 周年庆征文|分布式系统解决之道:目录、消息队列、事务系统及其他

No Silver Bullet

分布式系统 消息队列 6月月更 事务系统 InfoQ极客传媒15周年庆

创建一个springboot服务就是这么简单

乌龟哥哥

6月月更

模块7作业

KennyQ

生态对对碰丨详解 Flink CDC+OceanBase 全增量一体化数据集成方案

OceanBase 数据库

数据库 oceanbase Flink CDC

15 个用于创建漂亮图表的 JavaScript 库

devpoint

eCharts D3 plotly 图表库 6月月更

SpringBoot下用Kyro作为Redis序列化工具

程序员欣宸

Java 6月月更 Kyro

在 CloudIDE 里实现天猫精灵自定义技能的业务逻辑

汪子熙

人工智能 机器学习 机器人 Cloud 6月月更

内存数据库和磁盘数据库哪个更强?

OceanBase 数据库

内存数据库 oceanbase 磁盘数据库

聊聊 Sharding-Jdbc 的原理-初篇

Nick

MySQL 分库分表 中间件 ShardingJDBC 6月月更

产教融合加速数据库人才培养 | OceanBase数据库大赛10强诞生

OceanBase 数据库

数据库 oceanbase 产教融合

C#入门系列(三) -- 数据类型

陈言必行

C# 6月月更

Javascript中的垃圾回收

大熊G

JavaScript 前端 6月月更

基于 Agora SDK 实现 Web 端的多人视频互动_行业深度_声网_InfoQ精选文章