一个 HTML 页面中的一对一视频通话示例

举报
搞前端的半夏 发表于 2022/06/30 21:45:58 2022/06/30
【摘要】 前言WebRTC 是一项实时通讯技术!它允许我们在浏览器之间点对点的建立连接,实现音频,视频的的传输!本文主要是通过完成在一个HTML页面中一对一视频通话的小例子,来简单的介绍如何在浏览器中,使用WebRTC! 基本流程在正式的编码之前,我们需要先了解WebRTC 建立连接的一个流程!假设我们有用户 A 和用户 B。我们需要一个可以建立连接的方法-RTCPeerConnection`RTC...

前言

WebRTC 是一项实时通讯技术!它允许我们在浏览器之间点对点的建立连接,实现音频,视频的的传输!

本文主要是通过完成在一个HTML页面中一对一视频通话的小例子,来简单的介绍如何在浏览器中,使用WebRTC!

基本流程

在正式的编码之前,我们需要先了解WebRTC 建立连接的一个流程!

假设我们有用户 A 和用户 B。我们需要一个可以建立连接的方法-RTCPeerConnection`

RTCPeerConnection 接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。

const pc1 = new RTCPeerConnection(null);

然后用户还需要去从媒体设备中捕获音频和视频。这个我们可以通过navigator完成。

const stream = await navigator.mediaDevices.getUserMedia({ video: true });

现在我们本地的音频和视频。我们是不是就可以两端之间交换了呢?答案是不可以!这是因为在音视频通讯场景下,两端之间所支持的音视频接码,传输,都需要先通知对方!这就是媒体协商!媒体协商就是两台计算机交换媒体参数并达成公示的过程!例如:两个用户交换各自支持的编码器列表,并找到两者共同支持的部分,最后选择其中一个作为使用的编码器!

在WebRTC中我们使用offer-answser模型来完成这一过程!

const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);

// now offer will be sent to pc2 using a signaling server

await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);

// now answer will be sent to pc1 using a signaling server

await pc1.setRemoteDescription(answer);

在双方达成公式之后,我们就可以在它们之前建议P2P连接!

这一过程由候选交换模型完成!候选对象实际上是一个包含 ip/port 和其他地址信息的对象。每个用户通常有不止一个候选人。这些候选者将由信令服务器相互发送。然后每个用户将尝试使用他们收到的远程候选信息建立 p2p 连接。这个过程可以通过下面的代码来完成。

pc1.onicecandidate = async e => {
  if (!e.candidate) {
    return;
  }
  try {
    await pc2.addIceCandidate(e.candidate);
  } catch (error) {
    console.log("pc2 add pc1 candidate error", error);
  }
}

在RTCPeerConnection 对象中提供一个回调函数来监控 p2p 连接。

pc1.onconnectionstatechange = () => {
  console.log(pc1.connectionState)
}

最后,在p2p连接建立之后,我们可以从回调中获取媒体数据!

pc2.ontrack = (e) => {
  remoteVideo.srcObject = new MediaStream([e.track]);
}

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    video {
      width: 400px;
    }
  </style>
</head>

<body>
  <button id="start">start</button>

  <p>local video</p>
  <video autoplay id="local-video"></video>

  <p>remote video</p>
  <video autoplay id="remote-video"></video>

  <script>
    const localVideo = document.getElementById("local-video")
    const remoteVideo = document.getElementById("remote-video")

    // pc1 act as local pc
    const pc1 = new RTCPeerConnection(null);

    // pc2 act as remote pc
    const pc2 = new RTCPeerConnection(null);

    start.onclick = async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        localVideo.srcObject = stream;

        const videoTrack = stream.getVideoTracks()[0];
        pc1.addTrack(videoTrack);
      } catch (error) {
        console.log("get local stream error", error);
      }
    }

    pc1.onnegotiationneeded = async () => {
      try {
        const offer = await pc1.createOffer();
        await pc1.setLocalDescription(offer);

        // now offer will be sent to pc2 using a signaling server

        await pc2.setRemoteDescription(offer);
        const answer = await pc2.createAnswer();
        await pc2.setLocalDescription(answer);

        // now answer will be sent to pc1 using a signaling server

        await pc1.setRemoteDescription(answer);

      } catch (error) {
        console.log("offer answer error", error);
      }
    }

    pc1.onconnectionstatechange = () => {
      console.log(pc1.connectionState)
    }

    pc1.onicecandidate = async e => {
      if (!e.candidate) {
        return;
      }
      try {
        await pc2.addIceCandidate(e.candidate);
      } catch (error) {
        console.log("pc2 add pc1 candidate error", error);
      }
    }

    pc2.onicecandidate = async e => {
      if (!e.candidate) {
        return;
      }
      try {
        await pc1.addIceCandidate(e.candidate);
      } catch (error) {
        console.log("pc1 add pc2 candidate error", error);
      }
    }

    pc2.ontrack = (e) => {
      remoteVideo.srcObject = new MediaStream([e.track]);
    }

  </script>
</body>

</html>

image-20220525231847119

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。