实时音视频

  • 实时音视频 > 使用指南 > Web >最佳实践

    最佳实践

    最近更新时间:2021-12-10 16:25:27

    房间对象

    在使用 SDK 的第一步,应当是创建一个 QNRTCClient 对象,之后所有关于房间的连接、发布订阅等操作,均通过该对象完成。

    const mClient = QNRTC.createClient();
    

    这里要注意的点是,同一个页面,该方法仅允许执行一次。即 QNRTCClient 对象应当只创建一个,当作单例对象使用。

    监听远端用户

    不管是先加入房间,还是后加入房间,均通过监听事件获取远端用户信息。这样的逻辑就要求,一定要在加入房间之前,注册好事件监听,这样才可以在加入房间后,立刻收到房间内已有用户的信息。

    const mClient = QNRTC.createClient()
    
    // 监听远端用户加入房间
    mClient.on("user-joined", (userID, userData) => {
      // ...
    })
    
    // 监听远端用户发布 track
    mClient.on("user-published", (userID, tracks) => {
      // ...
    })
    
    // 注册好前面的事件监听后,再加入房间
    mClient.join("your room token")
      .then(() => {
        console.log("join room success")
      })
    

    断网重连处理

    监控房间连接状态的用法,可以参考文档 房间管理。这里需要注意的点是,断网重连的处理。

    默认情况下,当发生网络异常等情况,SDK 会进入自动重连状态,此时房间连接状态会变为 RECONNECTING,表示重连中。之后就会两种可能:

    第一种,此后如果网络状态恢复正常,则 SDK 会完成自动重连,房间连接状态会变为 RECONNECTED,表示重连成功。重连成功后,SDK 会自动恢复原有的发布和订阅状态。也就是说,如果在重连之前有发布过 tracks 或者订阅过 tracks,重连成功后,SDK 会自动重新发布/订阅这些 tracks。

    第二种,如果 SDK 自动重连后,仍然无法正常连接网络,则 SDK 会自动退出房间,此时房间连接状态变为 DISCONNECTED,表示断开连接。在断开连接状态时,回调函数中会带有第二个参数 QNConnectionDisconnectedInfo,用来表示断开的原因。此时应当根据断开原因不同,做不同的逻辑处理。具体见如下代码。

    mClient.on('connection-state-changed', function(connectionState, info) {
      console.log('connection-state-changed', connectionState);
    
      // 当进入连接断开状态
      if (connectionState === QNConnectionState.DISCONNECTED) {
    
        // 监控断开原因
        switch(info.reason) {
    
          // 当异常断开时
          case QNConnectionDisconnectedReason.ERROR:
            break;
    
          // 当被踢出房间时
          case QNConnectionDisconnectedReason.KICKED_OUT:
            break;
    
          // 当调用接口,主动离开房间时
          case QNConnectionDisconnectedReason.LEAVE:
            break;
    
        }
    });
    

    此时,最需要关注的是异常断开的情况。一般处理模式有两种:

    • 通知用户:展示 UI,通知用户网络异常,用户可选择调整网络后重试
    • 自动重连:可以在收到异常断开的通知后,主动调用重新加入房间的方法,主动再次重试

    切换前后摄像头

    手机浏览器中,一般会有切换前后摄像头的需求。web 中切换前后摄像头无法一步完成,需要按照如下逻辑。

    <body>
      <div id="container"></div>
      <button id="switchCamera">切换摄像头</button>
    
      <script>
        const switchCameraBtn = document.getElementById("switchCamera")
        const playContainer = document.getElementById("container")
    
        let facingMode = "user"
        let cameraTrack;
    
        switchCameraBtn.onclick = async () => {
    
          // facingMode 参数用于指定采集前置摄像头还是后置摄像头
          facingMode = facingMode === "user" ? "environment" : "user"
    
          // 之前采集的 camera track 必须销毁
          // 注意,如果该 camera track 发布过,就也要先取消发布
          if (cameraTrack) {
            // unpublish
            // ...
    
            cameraTrack.destroy()
            cameraTrack = undefined;
          }
    
          // 重新采集指定的摄像头
          cameraTrack = await QNRTC.createCameraVideoTrack({ facingMode })
    
          // 再次播放
          cameraTrack.play(playContainer);
        }
      </script>
    
    </body>
    

    local track 断流处理

    本地采集的 local track,某些情况下可能会发生断流,一般常见的有下面几种情况:

    • 摄像头被拔出
    • 麦克风被拔出
    • 用户主动停止了屏幕共享
    • 媒体设备故障

    当发生断流时,视频 track 会黑屏,音频 track 会没有声音。此时如果该 track 已发布,SDK 会自动取消发布该 track,并触发 QNLocalTrack 对象上的 ended 事件,可以通过监听该事件,来处理这种情况。

    const track = await QNRTC.createScreenVideoTrack({ screenVideoTag: "screen-share" })
    
    track.on("ended", () => {
      console.log(`track: ${track.tag} ended!`)
    
      // 后续逻辑
      // 1. 通知用户
      // 2. 自动重新采集
      // 3. 其他等...
    })
    

    仅需关注 local track 的 ended 情况,remote track 的 ended 由远端用户自行处理,每个本地用户处理自己的 local track 即可。

    自动播放失败处理

    浏览器中为了安全和用户体验等方面考虑,对媒体数据的自动播放做了限制:在没有用户交互操作的情况下,不允许媒体播放。也就是说,在没有用户交互的情况下,如果直接调用 play 方法播放 track,一定概率会被浏览器阻止,导致播放失败。

    针对这种情况,一般处理策略有两种。

    第一种,始终保证在用户交互之后,调用 play 方法播放。例如,在一个 button 的点击回调函数中调用。

    const playBtn = document.getElementById("playBtn")
    const playContainer = document.getElementById("container")
    
    // create track 
    // ...
    
    playBtn.onclick = () => {
      // 这里,点击回调函数中调用
      track.play(playContainer)
    }
    

    第二种,可以选择在自动播放失败时,引导用户交互,在用户的交互中,再次播放。

    <body>
      <div id="container"></div>
      <button id="resumeBtn" disabled>恢复播放</button>
    
      <script>
        const resumeBtn = document.getElementById("resumeBtn")
        const playContainer = document.getElementById("container");
    
        // 用来存储播放失败的 tracks
        let toBeResumedTracks = []
    
        // 引导用户点击的 button,点击后再次执行播放
        resumeBtn.onclick = () => {
          for (const track of toBeResumedTracks) {
            track.play(playContainer)
          }
          // 将数组清空
          toBeResumedTracks = []
          // 隐藏重新播放按钮
          resumeBtn.disabled = true
        }
    
        const mClient = QNRTC.createClient()
    
        mClient.on("user-published", (userID, tracks) => {
          mClient.subscribe(tracks)
            .then(({ videoTracks, audioTracks }) => {
              const tracks = [...videoTracks, ...audioTracks]
    
              for (const track of tracks) {
                // 播放 track
                track.play(playContainer).catch(e => {
                  console.log("play fail", track)
    
                  // 如果播放失败,push 到待恢复数组中
                  toBeResumedTracks.push(track)
    
                  // 显示按钮,引导用户点击
                  resumeBtn.disabled = false
                })
              }
            })
        })
    
        mClient.join("your room token")
          .then(() => {
            console.log("join room success")
          })
      </script>
    </body>
    
    以上内容是否对您有帮助?
  • Qvm free helper
    Close