实时音视频

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

    实现视频通话

    最近更新时间: 2022-07-14 15:44:44

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

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

    下文代码中的 ROOMTOKEN_1ROOMTOKEN_2 表示生成的两个RoomToken。

    我们将会编写 1 个独立的 html 页面,主要包括采集本地的摄像头/麦克风数据并将其发布与订阅发布流并将其展示在页面上的功能。

    您可以从 这里 查看这个 Demo 最终的成品代码,但是还是建议您在按照步骤阅读完之后再参阅。

    引入 SDK 并准备页面

    如果一切准备就绪,让我们开始吧,首先创建一个 oneOnOne.html 文件。

    <!DOCTYPE html>
    <html>
        <head>
        <meta charset="utf-8" />
        <title>One on One Sample</title>
        <script src="https://docs.qnsdk.com/qnweb-rtc.js"></script>
        <style>
          #localtracks, #remotetracks {
            width: 320px;
            height: 240px;
            background: #000;
          }
        </style>
        </head>
        <body>
            <label>请输入 RoomToken 加入房间开始连麦</label>
            <input id="roomtoken" type="text" />
            <button onclick="joinRoom()">加入房间</button>
            <p>本地视频</p>
            <div id="localtracks"></div>
            <p>远端视频</p>
            <div id="remotetracks"></div>
        </body>
    </html>
    

    https://docs.qnsdk.com/qnweb-rtc.js 该链接仅供测试,正式环境需要自行准备 JS 文件或自行 NPM 下载。JS SDK 文件 放在我们的 Github 上,只需要访问我们的 Github 页面,然后替换一下自己的 js 文件即可。

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

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

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

    加入房间

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

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

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

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

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

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

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

    编辑 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();
      const roomTokenInput = document.getElementById("roomtoken");
      const roomToken = roomTokenInput.value;
      // 这里替换成刚刚生成的 RoomToken
      await client.join(roomToken);
      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.isAudio()) continue;
        // 调用 Track 对象的 play 方法在这个元素下播放视频轨
        localTrack.play(localElement, {
            mirror: true
        });
    }
    

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

    订阅远端发布的音视频轨

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

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

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

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

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

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

    // 这里的参数 client 是指刚刚初始化的 QNRTCClient 对象
    async function subscribe(client, tracks) {
      // 传入 Track 对象数组调用订阅方法发起订阅,异步返回成功订阅的 Track 对象。
      const remoteTracks = await client.subscribe(tracks);
    
      // 选择页面上的一个元素作为父元素,播放远端的音视频轨
      const remoteElement = document.getElementById("remotetracks");
      // 遍历返回的远端 Track,调用 play 方法完成在页面上的播放
      for (const remoteTrack of [...remoteTracks.videoTracks, ...remoteTracks.audioTracks]) {
          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 方法监听相应的事件并给出处理函数即可
    }
    

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

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

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

    接下来...

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

    • SDK 使用中每个功能的细节都可以通过左侧的功能列表去查看
    • 如果您想了解 SDK 的架构以及所暴露的模块细节,可以阅读 API 文档

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

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