实时音视频

  • 实时音视频 > 快速入门 > Web >实现视频通话

    实现视频通话

    最近更新时间:2021-11-15 10:59:24

    如果您已经完成了上文中提到的开发准备,现在就让我们开始编写一个基本的实时音视频应用吧。这个应用会展示 SDK 基本的连麦功能,包括 加入房间,采集,发布,订阅 等过程。但是在实际的连麦应用开发过程中,是需要后端介入的(用于给用户鉴权并生成 RoomToken),在这里为了减少开发的时间,请您先预先生成 2 个 RoomToken(要求同一个 APPID,同一个 RoomName,不同的 UserName)。

    如果您还不知道如何生成 RoomToken,请先阅读 七牛实时音视频云接入指南

    准备好之后,请将下文代码中的 ROOMTOKEN_1ROOMTOKEN_2 替换成您生成的 RoomToken。

    我们将会编写 2 个独立的 html 页面,其中第一个页面负责采集本地的摄像头/麦克风数据并将其发布,第二个页面负责订阅刚刚发布的这个流并将其展示在页面上。

    引入 SDK 并准备页面

    如果一切准备就绪,让我们开始吧,首先准备一个空文件夹作为我们项目的根目录。参考上文 引入 SDK 中的方式,下载 qnweb-rtc.js 的最新代码到我们项目的目录下。

    然后分别创建 publish.htmlsubscribe.html 2 个文件,作为我们的 2 个页面。

    <!-- publish.html -->
    <!DOCTYPE html>
    <html>
        <head>
        <meta charset="utf-8" />
        <title>Publish Page</title>
        </head>
        <body>
        <p>本地音视频轨</p>
        <div id="localtracks" style="width: 640px;"></div>
        <button onclick="joinRoom()">加入房间</button>
        <!-- 这里引入我们的 SDK -->
        <script src="./qnweb-rtc.js"></script>
        <script>
          // 确认引入成功
          console.log("current version", QNRTC.VERSION);
        </script>
        </body>
    </html>
    
    <!-- subscribe.html -->
    <!DOCTYPE html>
    <html>
        <head>
        <meta charset="utf-8" />
        <title>Subscribe Page</title>
        </head>
        <body>
        <p>远端音视频轨</p>
        <div id="remotetracks" style="width: 640px;"></div>
        <button onclick="joinRoom()">加入房间</button>
        <!-- 同样,这里引入我们的 SDK -->
        <script src="./qnweb-rtc.js"></script>
        <script>
          // 确认引入成功
          console.log("current version", QNRTC.VERSION);
        </script>
        </body>
    </html>
    

    可以看到 2 个页面的基本结构都是一样的,一个用来加入房间的按钮和一个准备用来播放媒体流的空元素。在正式开发过程中我们可以根据需要将订阅和发布放到一个页面上,这里为了阐述方便采用了这种模式。

    然后在项目的目录下起一个 http 服务,打开 Chrome 通过 localhost 或者 127.0.0.1 访问刚刚那 2 个页面(因为屏幕和摄像头采集只能在 localhost 或者 https 下完成), 观察到控制台打印出了 sdk 的版本信息说明这一步完成了。

    如果您不知道如何在本地起一个 http 服务,推荐使用基于 nodejs 的 http-server。可以参考这篇文章

    注意!Chrome 只允许 localhost 或者 https 的网络页面访问媒体设备。请在之后的开发和上线中注意这一点

    加入房间

    上文提到过,SDK 所有的功能都是从 RoomToken 开始的,所以加入房间只需要将 RoomToken 作为参数传给 SDK 就可以了。编辑 publish.html<script> 中添加如下代码。

    // 确认引入成功
    console.log("current version", QNRTC.VERSION);
    // 这里采用的是 async/await 的异步方案,您也可以根据需要或者习惯替换成 Promise 的写法
    async function joinRoom() {
      // 创建QNRTCClient对象
      const client = QNRTC.createClient();
      // 这里替换成刚刚生成的 RoomToken
      await client.join(ROOMTOKEN_1);
      console.log("joinRoom success!");
    }
    

    同样,在 subscribe.html 中也添加一样的代码,不过注意要把 ROOMTOKEN_1 替换成 ROOMTOKEN_2

    再次访问这 2 个页面,分别点击加入房间按钮,看到控制台打印出 joinRoom success! 表示这一步完成。

    采集并发布本地的音视频轨

    接下来,我们的任务是在 publish.html 下采集本地的媒体数据并把它发布到房间中,供下文的 subscribe.html 去订阅。在编写代码之前,请先确认您的电脑已经连接了摄像头和麦克风设备(支持 USB 设备)。

    如果没有摄像头或者没有麦克风(缺少其中一个),请稍微修改下文中的采集参数关闭音频或者视频采集

    这里我们将要采集 2 个音视频轨,分别是 摄像头视频轨麦克风音频轨

    编辑 publish.html 中的 <script>,添加如下代码。

    // 增加一个函数 publish,用于采集并发布自己的媒体流
    // 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象
    async function publish(client) {
        // 同时采集麦克风音频和摄像头视频轨道。
        // 这个函数会返回一组audio track 与 video track
    
        const localTracks = await QNRTC.createMicrophoneAndCameraTracks();
        console.log("my local tracks", localTracks);
        // 将刚刚的 Track 列表发布到房间中
        await client.publish(localTracks);
        console.log("publish success!");
    }
    

    这个函数很简单,总共就 2 个语句,我们现在仔细分析一下这 2 行代码分别做了什么。

    第一句,调用了 QNRTC 提供的静态方法 createMicrophoneAndCameraTracks ,从名字我们可以看出,这个方法负责采集本地的麦克风频轨与摄像头轨道,并将其包装成 SDK 的 Track 对象并以列表的形式返回给我我们。

    第二句,调用了 QNRTCClient 提供的方法 publish ,将本地 Track 发布至远端。

    然后在刚刚 joinRoom 的函数末尾调用我们刚刚编写的 publish

    async function joinRoom() {
      // 创建QNRTCClient对象
      const client = QNRTC.createClient();
      // 这里替换成刚刚生成的 RoomToken
      await client.join(ROOMTOKEN_1);
      console.log("joinRoom success!");
    
      await publish(client);
    }
    

    现在访问页面,加入房间后 Chrome 应该会申请媒体设备的权限,同意之后观察控制台打印出 publish success! 表示发布成功。

    如果您有需要想要看到自己刚刚采集的视频画面,可以在 publish 函数的末尾添加如下代码

    ...
    ...
    await client.publish(localTracks);
    console.log("publish success!");
    
    // 在这里添加
    // 获取页面上的一个元素作为播放画面的父元素
    const localElement = document.getElementById("localtracks");
    // 遍历本地采集的 Track 对象
    for (const localTrack of localTracks) {
        console.log(localTrack)
      // 如果这是麦克风采集的音频 Track,我们就不播放它。
      if (localTrack.track.info.kind === "audio") continue;
      // 调用 Track 对象的 play 方法在这个元素下播放视频轨
      localTrack.play(localElement);
    }
    

    下一步,我们将在 subscribe.html 中完成订阅的代码,实现对我们刚刚发布的音视频轨道的订阅。

    订阅远端发布的音视频轨

    从上文发布的过程中我们可以知道,一个用户可以发布多个 Track,而在实际需求中我们不一定会把他所有发布的 Track 都订阅(可能只订阅音频之类的),所以订阅的目标是以 Track 为单位的。

    那么,我怎么知道房间中有哪些 Track,这些 Track 又分别对应哪些用户呢?这就是我们接下来讲述的重点。

    我们将这些 Track 的信息称为 TrackInfo, TrackInfo 会包含除了媒体数据以外所有和这个 Track 相关的信息。所以订阅操作的本质就是通过读取 TrackInfo 然后返回包含了媒体数据的 Track 对象。

    我们可以通过 SDK 的 user-published 事件获取当前房间的tracks列表:

    需要在join房间之前监听对应事件,join 加入房间成功后,user-published 会返回当前所有房间内已发布 Track 用户 userID 以及对应 TrackInfo

    进入房间后,当一个远端用户发布了自己的 Track,此时在我们这边也会触发 user-published 事件,事件会带上远端用户的 userID 以及所发布的 TrackInfo

    具体让我们通过代码来理解,在 subscribe.html<script> 中添加函数 subscribeautoSubscribe, subscribe 用于传入 TrackInfo 完成订阅操作并播放订阅流,autoSubscribe 用于实时检测房间的 TrackInfo 变化并在合适的时机调用 subscribe

    // 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象
    async function subscribe(client, trackInfoList) {
      // 通过传入 trackInfoList 调用订阅方法发起订阅,成功会返回相应的 Track 对象,也就是远端的 Track 列表了
      const remoteTracks = await client.subscribe(trackInfoList);
    
      // 选择页面上的一个元素作为父元素,播放远端的音视频轨
      const remoteElement = document.getElementById("remotetracks");
      // 遍历返回的远端 Track,调用 play 方法完成在页面上的播放
      for (const remoteTrack of remoteTracks.videoTracks) {
          remoteTrack.play(remoteElement);
      }
    }
    

    在一次批量订阅中,如果想区分返回的 Track(比如播放屏幕分享的 Track 时给更大的界面),和上文一样,通过 tag 来区分。这里的 tag 来自于远端采集它的时候所指定的 tag。

    代码不多,总共就两步。先是发起订阅拿到返回的 Track 对象,之后调用 Track 的 play 方法在页面上播放。

    下面我们实现 autoSubscribeTrackInfo 传给 subscribe

    // 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象, 同上
    function autoSubscribe(client) {
        // 添加事件监听,当房间中出现新的 Track 时就会触发,参数是 trackInfo 列表
        client.on("user-published", (userId,tracks) => {
            console.log("user-published!", userId,tracks);
            subscribe(client, tracks)
            .then(() => console.log("subscribe success!"))
            .catch(e => console.error("subscribe error", e));
        });
        // 就是这样,就像监听 DOM 事件一样通过 on 方法监听相应的事件并给出处理函数即可
    }
    

    最后,让我们在 subscribe.htmljoinRoom 中调用 autoSubscribe 即可

    async function joinRoom() {
      // 创建QNRTCClient对象
      const client = QNRTC.createClient();
      // 需要先监听对应事件再加入房间
      autoSubscribe(client);
      // 这里替换成刚刚生成的 RoomToken
      await client.join(ROOMTOKEN_2);
      console.log("joinRoom success!");
    }
    

    好啦,这个时候再访问这 2 个页面,分别进入房间中,在 subscribe.html 中看到你自己和屏幕的画面就代表成功啦。您成功完成了一个最简单的一对一连麦应用(而且还支持单用户多流)。

    接下来...

    经过上面的步骤之后,相信您已经对一次连麦互动的流程以及 SDK 的使用有了大概的了解。如果您已经准备正式开始接入我们的 SDK,下面一些建议可以让您更加熟悉 SDK。

    • 这个 Demo 是用 2 个页面完成的,一个页面负责发布一个负责订阅。尝试将它整合成一个页面(比如通过按钮来选择以哪个用户的身份加入房间)
    • SDK 使用中每个功能的细节都可以通过左侧的功能列表去查看
    • 如果您想了解 SDK 的架构以及所暴露的模块细节,可以阅读右上角的 API 文档

    如果您在使用过程中遇到什么疑问,或者遇到了 BUG,请在 Github 的 issue 列表提问,我们会尽快为您解答。

    以上内容是否对您有帮助?
  • Qvm free helper
    Close